diff options
345 files changed, 8972 insertions, 2584 deletions
diff --git a/MULTIUSER_OWNERS b/MULTIUSER_OWNERS new file mode 100644 index 000000000000..fbc611a39d7d --- /dev/null +++ b/MULTIUSER_OWNERS @@ -0,0 +1,4 @@ +# OWNERS of Multiuser related files +bookatz@google.com +omakoto@google.com +yamasani@google.com diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt index ae9e7ff8de2f..ae32fba443f6 100644 --- a/apex/appsearch/framework/api/current.txt +++ b/apex/appsearch/framework/api/current.txt @@ -100,6 +100,7 @@ package android.app.appsearch { method public long getCreationTimestampMillis(); method public static int getMaxIndexedProperties(); method @NonNull public String getNamespace(); + method @Nullable public Object getProperty(@NonNull String); method public boolean getPropertyBoolean(@NonNull String); method @Nullable public boolean[] getPropertyBooleanArray(@NonNull String); method @Nullable public byte[] getPropertyBytes(@NonNull String); @@ -148,6 +149,12 @@ package android.app.appsearch { method @NonNull public android.app.appsearch.GetByUriRequest.Builder setNamespace(@NonNull String); } + public class PackageIdentifier { + ctor public PackageIdentifier(@NonNull String, @NonNull byte[]); + method @NonNull public String getPackageName(); + method @NonNull public byte[] getSha256Certificate(); + } + public final class PutDocumentsRequest { method @NonNull public java.util.List<android.app.appsearch.GenericDocument> getDocuments(); } @@ -233,6 +240,8 @@ package android.app.appsearch { public final class SetSchemaRequest { method @NonNull public java.util.Set<android.app.appsearch.AppSearchSchema> getSchemas(); + method @NonNull public java.util.Set<java.lang.String> getSchemasNotVisibleToSystemUi(); + method @NonNull public java.util.Map<java.lang.String,java.util.Set<android.app.appsearch.PackageIdentifier>> getSchemasVisibleToPackages(); method public boolean isForceOverride(); } @@ -242,6 +251,17 @@ package android.app.appsearch { method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchema(@NonNull java.util.Collection<android.app.appsearch.AppSearchSchema>); method @NonNull public android.app.appsearch.SetSchemaRequest build(); method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setForceOverride(boolean); + method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForPackage(@NonNull String, boolean, @NonNull android.app.appsearch.PackageIdentifier); + method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForSystemUi(@NonNull String, boolean); + } + +} + +package android.app.appsearch.exceptions { + + public class AppSearchException extends java.lang.Exception { + method public int getResultCode(); + method @NonNull public <T> android.app.appsearch.AppSearchResult<T> toAppSearchResult(); } } diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java index 85207f7ed9d7..11e7fab2b7d9 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java @@ -20,7 +20,6 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; -import android.app.appsearch.exceptions.AppSearchException; import android.app.appsearch.util.BundleUtil; import android.os.Bundle; import android.util.Log; @@ -92,9 +91,7 @@ public class GenericDocument { /** Contains {@link GenericDocument} basic information (uri, schemaType etc). */ @NonNull final Bundle mBundle; - /** - * Contains all properties in {@link GenericDocument} to support getting properties via keys. - */ + /** Contains all properties in {@link GenericDocument} to support getting properties via keys */ @NonNull private final Bundle mProperties; @NonNull private final String mUri; @@ -202,6 +199,24 @@ public class GenericDocument { } /** + * Retrieves the property value with the given key as {@link Object}. + * + * @param key The key to look for. + * @return The entry with the given key as an object or {@code null} if there is no such key. + */ + @Nullable + public Object getProperty(@NonNull String key) { + Preconditions.checkNotNull(key); + Object property = mProperties.get(key); + if (property instanceof ArrayList) { + return getPropertyBytesArray(key); + } else if (property instanceof Bundle[]) { + return getPropertyDocumentArray(key); + } + return property; + } + + /** * Retrieves a {@link String} value by key. * * @param key The key to look for. diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java b/apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java index 8b20c0903ce1..43be442bd4dc 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java @@ -23,10 +23,7 @@ import com.android.internal.util.Preconditions; import java.util.Arrays; import java.util.Objects; -/** - * This class represents a uniquely identifiable package. - * @hide - */ +/** This class represents a uniquely identifiable package. */ public class PackageIdentifier { private final String mPackageName; private final byte[] mSha256Certificate; diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java index 1c360a65a041..b9503eed153c 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java @@ -18,7 +18,6 @@ package android.app.appsearch; import android.annotation.NonNull; import android.annotation.SuppressLint; -import android.app.appsearch.exceptions.AppSearchException; import com.android.internal.util.Preconditions; diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java index 5ffa7c94087c..eb0b7324a117 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java @@ -49,31 +49,20 @@ public final class SearchResult { /** @hide */ public static final String MATCHES_FIELD = "matches"; - @NonNull private final Bundle mBundle; + /** @hide */ + public static final String PACKAGE_NAME_FIELD = "packageName"; - @NonNull private final Bundle mDocumentBundle; + @NonNull private final Bundle mBundle; /** Cache of the inflated document. Comes from inflating mDocumentBundle at first use. */ @Nullable private GenericDocument mDocument; - /** - * Contains a list of MatchInfo bundles that matched the request. - * - * <p>Only populated when requested in both {@link SearchSpec.Builder#setSnippetCount} and - * {@link SearchSpec.Builder#setSnippetCountPerProperty}. - * - * @see #getMatches() - */ - @NonNull private final List<Bundle> mMatchBundles; - /** Cache of the inflated matches. Comes from inflating mMatchBundles at first use. */ @Nullable private List<MatchInfo> mMatches; /** @hide */ public SearchResult(@NonNull Bundle bundle) { mBundle = Preconditions.checkNotNull(bundle); - mDocumentBundle = Preconditions.checkNotNull(bundle.getBundle(DOCUMENT_FIELD)); - mMatchBundles = Preconditions.checkNotNull(bundle.getParcelableArrayList(MATCHES_FIELD)); } /** @hide */ @@ -90,7 +79,9 @@ public final class SearchResult { @NonNull public GenericDocument getDocument() { if (mDocument == null) { - mDocument = new GenericDocument(mDocumentBundle); + mDocument = + new GenericDocument( + Preconditions.checkNotNull(mBundle.getBundle(DOCUMENT_FIELD))); } return mDocument; } @@ -106,9 +97,11 @@ public final class SearchResult { @NonNull public List<MatchInfo> getMatches() { if (mMatches == null) { - mMatches = new ArrayList<>(mMatchBundles.size()); - for (int i = 0; i < mMatchBundles.size(); i++) { - MatchInfo matchInfo = new MatchInfo(getDocument(), mMatchBundles.get(i)); + List<Bundle> matchBundles = + Preconditions.checkNotNull(mBundle.getParcelableArrayList(MATCHES_FIELD)); + mMatches = new ArrayList<>(matchBundles.size()); + for (int i = 0; i < matchBundles.size(); i++) { + MatchInfo matchInfo = new MatchInfo(getDocument(), matchBundles.get(i)); mMatches.add(matchInfo); } } @@ -116,6 +109,17 @@ public final class SearchResult { } /** + * Contains the package name that stored the {@link GenericDocument}. + * + * @return Package name that stored the document + * @hide + */ + @NonNull + public String getPackageName() { + return Preconditions.checkNotNull(mBundle.getString(PACKAGE_NAME_FIELD)); + } + + /** * 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. diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java index 400b6303d12e..c3f0d8ab53e3 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java @@ -19,8 +19,6 @@ package android.app.appsearch; 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 android.util.ArrayMap; diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java index ad3ee0587f61..e9c4cb4e9e34 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java @@ -59,7 +59,6 @@ public final class SetSchemaRequest { /** * Returns the set of schema types that have opted out of being visible on system UI surfaces. - * @hide */ @NonNull public Set<String> getSchemasNotVisibleToSystemUi() { @@ -72,7 +71,6 @@ public final class SetSchemaRequest { * certificate. * * <p>This method is inefficient to call repeatedly. - * @hide */ @NonNull public Map<String, Set<PackageIdentifier>> getSchemasVisibleToPackages() { @@ -141,7 +139,6 @@ public final class SetSchemaRequest { * * @param schemaType The schema type to set visibility on. * @param visible Whether the {@code schemaType} will be visible or not. - * @hide */ // Merged list available from getSchemasNotVisibleToSystemUi @SuppressLint("MissingGetterMatchingBuilder") @@ -165,7 +162,6 @@ public final class SetSchemaRequest { * @param schemaType The schema type to set visibility on. * @param visible Whether the {@code schemaType} will be visible or not. * @param packageIdentifier Represents the package that will be granted visibility. - * @hide */ // Merged list available from getSchemasVisibleToPackages @SuppressLint("MissingGetterMatchingBuilder") diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java index 704f180bffc4..b1a33a478a47 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java @@ -25,8 +25,6 @@ import android.app.appsearch.AppSearchResult; * * <p>These exceptions can be converted into a failed {@link AppSearchResult} for propagating to the * client. - * - * @hide */ public class AppSearchException extends Exception { private final @AppSearchResult.ResultCode int mResultCode; 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 47a81eb37c7d..a2126b19d31e 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 @@ -37,6 +37,7 @@ import com.android.server.appsearch.external.localstorage.converter.SearchResult import com.android.server.appsearch.external.localstorage.converter.SearchSpecToProtoConverter; import com.google.android.icing.IcingSearchEngine; +import com.google.android.icing.proto.DeleteByQueryResultProto; import com.google.android.icing.proto.DeleteResultProto; import com.google.android.icing.proto.DocumentProto; import com.google.android.icing.proto.GetAllNamespacesResultProto; @@ -609,7 +610,7 @@ public final class AppSearchImpl { SearchSpecProto searchSpecProto = SearchSpecToProtoConverter.toSearchSpecProto(searchSpec); SearchSpecProto.Builder searchSpecBuilder = searchSpecProto.toBuilder().setQuery(queryExpression); - DeleteResultProto deleteResultProto; + DeleteByQueryResultProto deleteResultProto; mReadWriteLock.writeLock().lock(); try { // Only rewrite SearchSpec for non empty prefixes. @@ -797,11 +798,27 @@ public final class AppSearchImpl { * Removes any prefixes from types and namespaces mentioned anywhere in {@code documentBuilder}. * * @param documentBuilder The document to mutate + * @return Prefix name that was removed from the document. + * @throws AppSearchException if there are unexpected database prefixing errors. */ + @NonNull @VisibleForTesting - static void removePrefixesFromDocument(@NonNull DocumentProto.Builder documentBuilder) + static String removePrefixesFromDocument(@NonNull DocumentProto.Builder documentBuilder) throws AppSearchException { // Rewrite the type name and namespace to remove the prefix. + String schemaPrefix = getPrefix(documentBuilder.getSchema()); + String namespacePrefix = getPrefix(documentBuilder.getNamespace()); + + if (!schemaPrefix.equals(namespacePrefix)) { + throw new AppSearchException( + AppSearchResult.RESULT_INTERNAL_ERROR, + "Found unexpected" + + " multiple prefix names in document: " + + schemaPrefix + + ", " + + namespacePrefix); + } + documentBuilder.setSchema(removePrefix(documentBuilder.getSchema())); documentBuilder.setNamespace(removePrefix(documentBuilder.getNamespace())); @@ -816,12 +833,22 @@ public final class AppSearchImpl { for (int documentIdx = 0; documentIdx < documentCount; documentIdx++) { DocumentProto.Builder derivedDocumentBuilder = propertyBuilder.getDocumentValues(documentIdx).toBuilder(); - removePrefixesFromDocument(derivedDocumentBuilder); + String nestedPrefix = removePrefixesFromDocument(derivedDocumentBuilder); + if (!nestedPrefix.equals(schemaPrefix)) { + throw new AppSearchException( + AppSearchResult.RESULT_INTERNAL_ERROR, + "Found unexpected multiple prefix names in document: " + + schemaPrefix + + ", " + + nestedPrefix); + } propertyBuilder.setDocumentValues(documentIdx, derivedDocumentBuilder); } documentBuilder.setProperties(propertyIdx, propertyBuilder); } } + + return schemaPrefix; } /** @@ -929,6 +956,25 @@ public final class AppSearchImpl { return packageName + PACKAGE_DELIMITER + databaseName + DATABASE_DELIMITER; } + /** + * Returns the package name that's contained within the {@code prefix}. + * + * @param prefix Prefix string that contains the package name inside of it. The package name + * must be in the front of the string, and separated from the rest of the string by the + * {@link #PACKAGE_DELIMITER}. + * @return Valid package name. + */ + @NonNull + private static String getPackageName(@NonNull String prefix) { + int delimiterIndex = prefix.indexOf(PACKAGE_DELIMITER); + if (delimiterIndex == -1) { + // This should never happen if we construct our prefixes properly + Log.wtf(TAG, "Malformed prefix doesn't contain package name: " + prefix); + return ""; + } + return prefix.substring(0, delimiterIndex); + } + @NonNull private static String removePrefix(@NonNull String prefixedString) throws AppSearchException { // The prefix is made up of the package, then the database. So we only need to find the @@ -949,7 +995,7 @@ public final class AppSearchImpl { if (databaseDelimiterIndex == -1) { throw new AppSearchException( AppSearchResult.RESULT_UNKNOWN_ERROR, - "The databaseName prefixed value doesn't contains a valid database name."); + "The databaseName prefixed value doesn't contain a valid database name."); } // Add 1 to include the char size of the DATABASE_DELIMITER @@ -1034,20 +1080,24 @@ public final class AppSearchImpl { } /** Remove the rewritten schema types from any result documents. */ - private static SearchResultPage rewriteSearchResultProto( - @NonNull SearchResultProto searchResultProto) throws AppSearchException { + @NonNull + @VisibleForTesting + static SearchResultPage rewriteSearchResultProto(@NonNull SearchResultProto searchResultProto) + throws AppSearchException { + // Parallel array of package names for each document search result. + List<String> packageNames = new ArrayList<>(searchResultProto.getResultsCount()); + SearchResultProto.Builder resultsBuilder = searchResultProto.toBuilder(); for (int i = 0; i < searchResultProto.getResultsCount(); i++) { - if (searchResultProto.getResults(i).hasDocument()) { - SearchResultProto.ResultProto.Builder resultBuilder = - searchResultProto.getResults(i).toBuilder(); - DocumentProto.Builder documentBuilder = resultBuilder.getDocument().toBuilder(); - removePrefixesFromDocument(documentBuilder); - resultBuilder.setDocument(documentBuilder); - resultsBuilder.setResults(i, resultBuilder); - } + SearchResultProto.ResultProto.Builder resultBuilder = + searchResultProto.getResults(i).toBuilder(); + DocumentProto.Builder documentBuilder = resultBuilder.getDocument().toBuilder(); + String prefix = removePrefixesFromDocument(documentBuilder); + packageNames.add(getPackageName(prefix)); + resultBuilder.setDocument(documentBuilder); + resultsBuilder.setResults(i, resultBuilder); } - return SearchResultToProtoConverter.toSearchResultPage(resultsBuilder); + return SearchResultToProtoConverter.toSearchResultPage(resultsBuilder, packageNames); } @GuardedBy("mReadWriteLock") 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 5474cd04287c..a2386eccc256 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 @@ -54,40 +54,43 @@ public final class GenericDocumentToProtoConverter { for (int i = 0; i < keys.size(); i++) { String name = keys.get(i); PropertyProto.Builder propertyProto = PropertyProto.newBuilder().setName(name); - String[] stringValues = document.getPropertyStringArray(name); - long[] longValues = document.getPropertyLongArray(name); - double[] doubleValues = document.getPropertyDoubleArray(name); - boolean[] booleanValues = document.getPropertyBooleanArray(name); - byte[][] bytesValues = document.getPropertyBytesArray(name); - GenericDocument[] documentValues = document.getPropertyDocumentArray(name); - if (stringValues != null) { + Object property = document.getProperty(name); + if (property instanceof String[]) { + String[] stringValues = (String[]) property; for (int j = 0; j < stringValues.length; j++) { propertyProto.addStringValues(stringValues[j]); } - } else if (longValues != null) { + } else if (property instanceof long[]) { + long[] longValues = (long[]) property; for (int j = 0; j < longValues.length; j++) { propertyProto.addInt64Values(longValues[j]); } - } else if (doubleValues != null) { + } else if (property instanceof double[]) { + double[] doubleValues = (double[]) property; for (int j = 0; j < doubleValues.length; j++) { propertyProto.addDoubleValues(doubleValues[j]); } - } else if (booleanValues != null) { + } else if (property instanceof boolean[]) { + boolean[] booleanValues = (boolean[]) property; for (int j = 0; j < booleanValues.length; j++) { propertyProto.addBooleanValues(booleanValues[j]); } - } else if (bytesValues != null) { + } else if (property instanceof byte[][]) { + byte[][] bytesValues = (byte[][]) property; for (int j = 0; j < bytesValues.length; j++) { propertyProto.addBytesValues(ByteString.copyFrom(bytesValues[j])); } - } else if (documentValues != null) { + } else if (property instanceof GenericDocument[]) { + GenericDocument[] documentValues = (GenericDocument[]) property; for (int j = 0; j < documentValues.length; j++) { DocumentProto proto = toDocumentProto(documentValues[j]); propertyProto.addDocumentValues(proto); } } else { throw new IllegalStateException( - "Property \"" + name + "\" has unsupported value type"); + String.format( + "Property \"%s\" has unsupported value type %s", + name, property.getClass().toString())); } mProtoBuilder.addProperties(propertyProto); } 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 4d107a970abd..ccd567d1c945 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 @@ -22,12 +22,15 @@ import android.app.appsearch.SearchResult; import android.app.appsearch.SearchResultPage; import android.os.Bundle; +import com.android.internal.util.Preconditions; + import com.google.android.icing.proto.SearchResultProto; import com.google.android.icing.proto.SearchResultProtoOrBuilder; import com.google.android.icing.proto.SnippetMatchProto; import com.google.android.icing.proto.SnippetProto; import java.util.ArrayList; +import java.util.List; /** * Translates a {@link SearchResultProto} into {@link SearchResult}s. @@ -37,27 +40,45 @@ import java.util.ArrayList; public class SearchResultToProtoConverter { private SearchResultToProtoConverter() {} - /** Translate a {@link SearchResultProto} into {@link SearchResultPage}. */ + /** + * Translate a {@link SearchResultProto} into {@link SearchResultPage}. + * + * @param proto The {@link SearchResultProto} containing results. + * @param packageNames A parallel array of package names. The package name at index 'i' of this + * list should be the package that indexed the document at index 'i' of proto.getResults(i). + * @return {@link SearchResultPage} of results. + */ @NonNull - public static SearchResultPage toSearchResultPage(@NonNull SearchResultProtoOrBuilder proto) { + public static SearchResultPage toSearchResultPage( + @NonNull SearchResultProtoOrBuilder proto, @NonNull List<String> packageNames) { + Preconditions.checkArgument( + proto.getResultsCount() == packageNames.size(), + "Size of " + "results does not match the number of package names."); Bundle bundle = new Bundle(); bundle.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, proto.getNextPageToken()); ArrayList<Bundle> resultBundles = new ArrayList<>(proto.getResultsCount()); for (int i = 0; i < proto.getResultsCount(); i++) { - resultBundles.add(toSearchResultBundle(proto.getResults(i))); + resultBundles.add(toSearchResultBundle(proto.getResults(i), packageNames.get(i))); } bundle.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles); return new SearchResultPage(bundle); } - /** Translate a {@link SearchResultProto.ResultProto} into {@link SearchResult}. */ + /** + * Translate a {@link SearchResultProto.ResultProto} into {@link SearchResult}. + * + * @param proto The proto to be converted. + * @param packageName The package name associated with the document in {@code proto}. + * @return A {@link SearchResult} bundle. + */ @NonNull private static Bundle toSearchResultBundle( - @NonNull SearchResultProto.ResultProtoOrBuilder proto) { + @NonNull SearchResultProto.ResultProtoOrBuilder proto, @NonNull String packageName) { Bundle bundle = new Bundle(); GenericDocument document = GenericDocumentToProtoConverter.toGenericDocument(proto.getDocument()); bundle.putBundle(SearchResult.DOCUMENT_FIELD, document.getBundle()); + bundle.putString(SearchResult.PACKAGE_NAME_FIELD, packageName); ArrayList<Bundle> matchList = new ArrayList<>(); if (proto.hasSnippet()) { diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt index 2b1ec08e1d8e..73f64dc500ae 100644 --- a/apex/appsearch/synced_jetpack_changeid.txt +++ b/apex/appsearch/synced_jetpack_changeid.txt @@ -1 +1 @@ -I596ad1269b4d3a4f26db67f5d970aeaa3bf94a9d +I8b7425b3f87153547d1c8f5b560be5a54c9be97e diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java index 9e22bf6ddbbf..6859747286a8 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java @@ -46,7 +46,7 @@ public class GlobalSearchSessionShimImpl implements GlobalSearchSessionShim { private final ExecutorService mExecutor; @NonNull - public static ListenableFuture<GlobalSearchSessionShimImpl> createGlobalSearchSession() { + public static ListenableFuture<GlobalSearchSessionShim> createGlobalSearchSession() { Context context = ApplicationProvider.getApplicationContext(); AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class); SettableFuture<AppSearchResult<GlobalSearchSession>> future = SettableFuture.create(); diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java index 8383df4dd0cd..e439c5ab3947 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 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. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.app.appsearch; import android.annotation.NonNull; @@ -26,7 +27,7 @@ import java.util.Set; * Represents a connection to an AppSearch storage system where {@link GenericDocument}s can be * placed and queried. * - * All implementations of this interface must be thread safe. + * <p>All implementations of this interface must be thread safe. */ public interface AppSearchSessionShim { @@ -37,41 +38,42 @@ public interface AppSearchSessionShim { * 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. + * <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. + * <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 completing its - * future with an {@link androidx.appsearch.exceptions.AppSearchException} with a code of + * future with an {@link android.app.appsearch.exceptions.AppSearchException} with a code of * {@link AppSearchResult#RESULT_INVALID_SCHEMA} and a 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 completing its future with an - * {@link androidx.appsearch.exceptions.AppSearchException} 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. + * instead of completing its future with an {@link + * android.app.appsearch.exceptions.AppSearchException} 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. @@ -79,8 +81,8 @@ public interface AppSearchSessionShim { * <p>By default, documents are visible on platform surfaces. To opt out, call {@code * SetSchemaRequest.Builder#setPlatformSurfaceable} with {@code surfaceable} as false. Any * visibility settings apply only to the schemas that are included in the {@code request}. - * Visibility settings for a schema type do not apply or persist across - * {@link SetSchemaRequest}s. + * Visibility settings for a schema type do not apply or persist across {@link + * SetSchemaRequest}s. * * @param request The schema update request. * @return The pending result of performing this operation. @@ -107,10 +109,9 @@ public interface AppSearchSessionShim { * schema type previously registered via the {@link #setSchema} method. * * @param request {@link PutDocumentsRequest} containing documents to be indexed - * @return The 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. + * @return The 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. */ @NonNull ListenableFuture<AppSearchBatchResult<String, Void>> putDocuments( @@ -120,11 +121,11 @@ public interface AppSearchSessionShim { * Retrieves {@link GenericDocument}s by URI. * * @param request {@link GetByUriRequest} containing URIs to be retrieved. - * @return 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}. + * @return 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}. */ @NonNull ListenableFuture<AppSearchBatchResult<String, GenericDocument>> getByUri( @@ -134,42 +135,39 @@ public interface AppSearchSessionShim { * Searches a document based on a given query string. * * <p>Currently we support following features in the raw query format: + * * <ul> - * <li>AND - * <p>AND joins (e.g. “match documents that have both the terms ‘dog’ and - * ‘cat’”). - * Example: hello world matches documents that have both ‘hello’ and ‘world’ - * <li>OR - * <p>OR joins (e.g. “match documents that have either the term ‘dog’ or - * ‘cat’”). - * Example: dog OR puppy - * <li>Exclusion - * <p>Exclude a term (e.g. “match documents that do - * not have the term ‘dog’”). - * Example: -dog excludes the term ‘dog’ - * <li>Grouping terms - * <p>Allow for conceptual grouping of subqueries to enable hierarchical structures (e.g. - * “match documents that have either ‘dog’ or ‘puppy’, and either ‘cat’ or ‘kitten’”). - * Example: (dog puppy) (cat kitten) two one group containing two terms. - * <li>Property restricts - * <p> Specifies which properties of a document to specifically match terms in (e.g. - * “match documents where the ‘subject’ property contains ‘important’”). - * Example: subject:important matches documents with the term ‘important’ in the - * ‘subject’ property - * <li>Schema type restricts - * <p>This is similar to property restricts, but allows for restricts on top-level document - * fields, such as schema_type. Clients should be able to limit their query to documents of - * a certain schema_type (e.g. “match documents that are of the ‘Email’ schema_type”). - * Example: { schema_type_filters: “Email”, “Video”,query: “dog” } will match documents - * that contain the query term ‘dog’ and are of either the ‘Email’ schema type or the - * ‘Video’ schema type. + * <li>AND + * <p>AND joins (e.g. “match documents that have both the terms ‘dog’ and ‘cat’”). + * Example: hello world matches documents that have both ‘hello’ and ‘world’ + * <li>OR + * <p>OR joins (e.g. “match documents that have either the term ‘dog’ or ‘cat’”). Example: + * dog OR puppy + * <li>Exclusion + * <p>Exclude a term (e.g. “match documents that do not have the term ‘dog’”). Example: + * -dog excludes the term ‘dog’ + * <li>Grouping terms + * <p>Allow for conceptual grouping of subqueries to enable hierarchical structures (e.g. + * “match documents that have either ‘dog’ or ‘puppy’, and either ‘cat’ or ‘kitten’”). + * Example: (dog puppy) (cat kitten) two one group containing two terms. + * <li>Property restricts + * <p>Specifies which properties of a document to specifically match terms in (e.g. “match + * documents where the ‘subject’ property contains ‘important’”). Example: + * subject:important matches documents with the term ‘important’ in the ‘subject’ property + * <li>Schema type restricts + * <p>This is similar to property restricts, but allows for restricts on top-level + * document fields, such as schema_type. Clients should be able to limit their query to + * documents of a certain schema_type (e.g. “match documents that are of the ‘Email’ + * schema_type”). Example: { schema_type_filters: “Email”, “Video”,query: “dog” } will + * match documents that contain the query term ‘dog’ and are of either the ‘Email’ schema + * type or the ‘Video’ schema type. * </ul> * - * <p> This method is lightweight. The heavy work will be done in - * {@link SearchResults#getNextPage()}. + * <p>This method is lightweight. The heavy work will be done in {@link + * SearchResultsShim#getNextPage()}. * * @param queryExpression Query String to search. - * @param searchSpec Spec for setting filters, raw query etc. + * @param searchSpec Spec for setting filters, raw query etc. * @return The search result of performing this operation. */ @NonNull @@ -179,11 +177,10 @@ public interface AppSearchSessionShim { * Removes {@link GenericDocument}s from the index by URI. * * @param request Request containing URIs to be removed. - * @return 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}. + * @return 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}. */ @NonNull ListenableFuture<AppSearchBatchResult<String, Void>> removeByUri( @@ -191,18 +188,18 @@ public interface AppSearchSessionShim { /** * Removes {@link GenericDocument}s from the index by Query. Documents will be removed if they - * match the {@code queryExpression} in given namespaces and schemaTypes which is set via - * {@link SearchSpec.Builder#addNamespace} and {@link SearchSpec.Builder#addSchemaType}. + * match the {@code queryExpression} in given namespaces and schemaTypes which is set via {@link + * SearchSpec.Builder#addNamespace} and {@link SearchSpec.Builder#addSchemaType}. * - * <p> An empty {@code queryExpression} matches all documents. + * <p>An empty {@code queryExpression} matches all documents. * - * <p> An empty set of namespaces or schemaTypes matches all namespaces or schemaTypes in - * the current database. + * <p>An empty set of namespaces or schemaTypes matches all namespaces or schemaTypes in the + * current database. * * @param queryExpression Query String to search. - * @param searchSpec Spec containing schemaTypes, namespaces and query expression - * indicates how document will be removed. All specific about how to - * scoring, ordering, snippeting and resulting will be ignored. + * @param searchSpec Spec containing schemaTypes, namespaces and query expression indicates how + * document will be removed. All specific about how to scoring, ordering, snippeting and + * resulting will be ignored. * @return The pending result of performing this operation. */ @NonNull diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java index 33dc3795325b..2d09247dfbc9 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 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. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.app.appsearch; import android.annotation.NonNull; @@ -27,42 +28,39 @@ public interface GlobalSearchSessionShim { * Searches across all documents in the storage based on a given query string. * * <p>Currently we support following features in the raw query format: + * * <ul> - * <li>AND - * <p>AND joins (e.g. “match documents that have both the terms ‘dog’ and - * ‘cat’”). - * Example: hello world matches documents that have both ‘hello’ and ‘world’ - * <li>OR - * <p>OR joins (e.g. “match documents that have either the term ‘dog’ or - * ‘cat’”). - * Example: dog OR puppy - * <li>Exclusion - * <p>Exclude a term (e.g. “match documents that do - * not have the term ‘dog’”). - * Example: -dog excludes the term ‘dog’ - * <li>Grouping terms - * <p>Allow for conceptual grouping of subqueries to enable hierarchical structures (e.g. - * “match documents that have either ‘dog’ or ‘puppy’, and either ‘cat’ or ‘kitten’”). - * Example: (dog puppy) (cat kitten) two one group containing two terms. - * <li>Property restricts - * <p> Specifies which properties of a document to specifically match terms in (e.g. - * “match documents where the ‘subject’ property contains ‘important’”). - * Example: subject:important matches documents with the term ‘important’ in the - * ‘subject’ property - * <li>Schema type restricts - * <p>This is similar to property restricts, but allows for restricts on top-level document - * fields, such as schema_type. Clients should be able to limit their query to documents of - * a certain schema_type (e.g. “match documents that are of the ‘Email’ schema_type”). - * Example: { schema_type_filters: “Email”, “Video”,query: “dog” } will match documents - * that contain the query term ‘dog’ and are of either the ‘Email’ schema type or the - * ‘Video’ schema type. + * <li>AND + * <p>AND joins (e.g. “match documents that have both the terms ‘dog’ and ‘cat’”). + * Example: hello world matches documents that have both ‘hello’ and ‘world’ + * <li>OR + * <p>OR joins (e.g. “match documents that have either the term ‘dog’ or ‘cat’”). Example: + * dog OR puppy + * <li>Exclusion + * <p>Exclude a term (e.g. “match documents that do not have the term ‘dog’”). Example: + * -dog excludes the term ‘dog’ + * <li>Grouping terms + * <p>Allow for conceptual grouping of subqueries to enable hierarchical structures (e.g. + * “match documents that have either ‘dog’ or ‘puppy’, and either ‘cat’ or ‘kitten’”). + * Example: (dog puppy) (cat kitten) two one group containing two terms. + * <li>Property restricts + * <p>Specifies which properties of a document to specifically match terms in (e.g. “match + * documents where the ‘subject’ property contains ‘important’”). Example: + * subject:important matches documents with the term ‘important’ in the ‘subject’ property + * <li>Schema type restricts + * <p>This is similar to property restricts, but allows for restricts on top-level + * document fields, such as schema_type. Clients should be able to limit their query to + * documents of a certain schema_type (e.g. “match documents that are of the ‘Email’ + * schema_type”). Example: { schema_type_filters: “Email”, “Video”,query: “dog” } will + * match documents that contain the query term ‘dog’ and are of either the ‘Email’ schema + * type or the ‘Video’ schema type. * </ul> * - * <p> This method is lightweight. The heavy work will be done in - * {@link SearchResults#getNextPage}. + * <p>This method is lightweight. The heavy work will be done in {@link + * SearchResultsShim#getNextPage()}. * * @param queryExpression Query String to search. - * @param searchSpec Spec for setting filters, raw query etc. + * @param searchSpec Spec for setting filters, raw query etc. * @return The search result of performing this operation. */ @NonNull diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java index f387a1740386..328c65ca2727 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 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. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.app.appsearch; import android.annotation.NonNull; @@ -23,10 +24,10 @@ import java.io.Closeable; import java.util.List; /** - * SearchResults are a returned object from a query API. + * SearchResultsShim are a returned object from a query API. * - * <p>Each {@link SearchResult} contains a document and may contain other fields like snippets - * based on request. + * <p>Each {@link SearchResult} contains a document and may contain other fields like snippets based + * on request. * * <p>Should close this object after finish fetching results. * @@ -36,11 +37,10 @@ public interface SearchResultsShim extends Closeable { /** * Gets a whole page of {@link SearchResult}s. * - * <p>Re-call this method to get next page of {@link SearchResult}, until it returns an - * empty list. + * <p>Re-call this method to get next page of {@link SearchResult}, until it returns an empty + * list. * - * <p>The page size is set by - * {@link android.app.appsearch.SearchSpec.Builder#setResultCountPerPage}. + * <p>The page size is set by {@link SearchSpec.Builder#setResultCountPerPage}. * * @return The pending result of performing this operation. */ diff --git a/core/api/current.txt b/core/api/current.txt index 2ce0b35a218a..b6a15343deda 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -6925,6 +6925,7 @@ package android.app.admin { method @NonNull public java.util.List<java.lang.String> getDelegatedScopes(@Nullable android.content.ComponentName, @NonNull String); method public CharSequence getDeviceOwnerLockScreenInfo(); method public CharSequence getEndUserSessionMessage(@NonNull android.content.ComponentName); + method @NonNull public String getEnrollmentSpecificId(); method @Nullable public android.app.admin.FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(@Nullable android.content.ComponentName); method @Nullable public String getGlobalPrivateDnsHost(@NonNull android.content.ComponentName); method public int getGlobalPrivateDnsMode(@NonNull android.content.ComponentName); @@ -7075,6 +7076,7 @@ package android.app.admin { method @NonNull public java.util.List<java.lang.String> setMeteredDataDisabledPackages(@NonNull android.content.ComponentName, @NonNull java.util.List<java.lang.String>); method public void setNetworkLoggingEnabled(@Nullable android.content.ComponentName, boolean); method @Deprecated public void setOrganizationColor(@NonNull android.content.ComponentName, int); + method public void setOrganizationId(@NonNull String); method public void setOrganizationName(@NonNull android.content.ComponentName, @Nullable CharSequence); method public void setOverrideApnsEnabled(@NonNull android.content.ComponentName, boolean); method @NonNull public String[] setPackagesSuspended(@NonNull android.content.ComponentName, @NonNull String[], boolean); @@ -7135,7 +7137,7 @@ package android.app.admin { field public static final String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED"; field public static final String ACTION_PROFILE_OWNER_CHANGED = "android.app.action.PROFILE_OWNER_CHANGED"; field public static final String ACTION_PROVISIONING_SUCCESSFUL = "android.app.action.PROVISIONING_SUCCESSFUL"; - field public static final String ACTION_PROVISION_MANAGED_DEVICE = "android.app.action.PROVISION_MANAGED_DEVICE"; + field @Deprecated public static final String ACTION_PROVISION_MANAGED_DEVICE = "android.app.action.PROVISION_MANAGED_DEVICE"; field public static final String ACTION_PROVISION_MANAGED_PROFILE = "android.app.action.PROVISION_MANAGED_PROFILE"; field public static final String ACTION_SET_NEW_PARENT_PROFILE_PASSWORD = "android.app.action.SET_NEW_PARENT_PROFILE_PASSWORD"; field public static final String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD"; @@ -7186,7 +7188,7 @@ package android.app.admin { field public static final String EXTRA_PROVISIONING_SERIAL_NUMBER = "android.app.extra.PROVISIONING_SERIAL_NUMBER"; field public static final String EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS = "android.app.extra.PROVISIONING_SKIP_EDUCATION_SCREENS"; field public static final String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION"; - field public static final String EXTRA_PROVISIONING_SKIP_USER_CONSENT = "android.app.extra.PROVISIONING_SKIP_USER_CONSENT"; + field @Deprecated public static final String EXTRA_PROVISIONING_SKIP_USER_CONSENT = "android.app.extra.PROVISIONING_SKIP_USER_CONSENT"; field public static final String EXTRA_PROVISIONING_TIME_ZONE = "android.app.extra.PROVISIONING_TIME_ZONE"; field public static final String EXTRA_PROVISIONING_WIFI_ANONYMOUS_IDENTITY = "android.app.extra.PROVISIONING_WIFI_ANONYMOUS_IDENTITY"; field public static final String EXTRA_PROVISIONING_WIFI_CA_CERTIFICATE = "android.app.extra.PROVISIONING_WIFI_CA_CERTIFICATE"; @@ -7492,6 +7494,7 @@ package android.app.assist { method public int getMaxTextEms(); method public int getMaxTextLength(); method public int getMinTextEms(); + method @Nullable public String[] getOnReceiveContentMimeTypes(); method public int getScrollX(); method public int getScrollY(); method @Nullable public CharSequence getText(); @@ -7826,6 +7829,52 @@ package android.app.job { } +package android.app.people { + + public final class ConversationStatus implements android.os.Parcelable { + method public int describeContents(); + method public int getActivity(); + method public int getAvailability(); + method @Nullable public CharSequence getDescription(); + method public long getEndTimeMillis(); + method @Nullable public android.graphics.drawable.Icon getIcon(); + method @NonNull public String getId(); + method public long getStartTimeMillis(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int ACTIVITY_ANNIVERSARY = 2; // 0x2 + field public static final int ACTIVITY_BIRTHDAY = 1; // 0x1 + field public static final int ACTIVITY_GAME = 5; // 0x5 + field public static final int ACTIVITY_LOCATION = 6; // 0x6 + field public static final int ACTIVITY_MEDIA = 4; // 0x4 + field public static final int ACTIVITY_NEW_STORY = 3; // 0x3 + field public static final int ACTIVITY_OTHER = 0; // 0x0 + field public static final int ACTIVITY_UPCOMING_BIRTHDAY = 7; // 0x7 + field public static final int AVAILABILITY_AVAILABLE = 0; // 0x0 + field public static final int AVAILABILITY_BUSY = 1; // 0x1 + field public static final int AVAILABILITY_OFFLINE = 2; // 0x2 + field public static final int AVAILABILITY_UNKNOWN = -1; // 0xffffffff + field @NonNull public static final android.os.Parcelable.Creator<android.app.people.ConversationStatus> CREATOR; + } + + public static final class ConversationStatus.Builder { + ctor public ConversationStatus.Builder(@NonNull String, @NonNull int); + method @NonNull public android.app.people.ConversationStatus build(); + method @NonNull public android.app.people.ConversationStatus.Builder setAvailability(int); + method @NonNull public android.app.people.ConversationStatus.Builder setDescription(@Nullable CharSequence); + method @NonNull public android.app.people.ConversationStatus.Builder setEndTimeMillis(long); + method @NonNull public android.app.people.ConversationStatus.Builder setIcon(@Nullable android.graphics.drawable.Icon); + method @NonNull public android.app.people.ConversationStatus.Builder setStartTimeMillis(long); + } + + public final class PeopleManager { + method public void addOrUpdateStatus(@NonNull String, @NonNull android.app.people.ConversationStatus); + method public void clearStatus(@NonNull String, @NonNull String); + method public void clearStatuses(@NonNull String); + method @NonNull public java.util.List<android.app.people.ConversationStatus> getStatuses(@NonNull String); + } + +} + package android.app.role { public final class RoleManager { @@ -10315,6 +10364,7 @@ package android.content { field public static final String NFC_SERVICE = "nfc"; field public static final String NOTIFICATION_SERVICE = "notification"; field public static final String NSD_SERVICE = "servicediscovery"; + field public static final String PEOPLE_SERVICE = "people"; field public static final String POWER_SERVICE = "power"; field public static final String PRINT_SERVICE = "print"; field public static final int RECEIVER_VISIBLE_TO_INSTANT_APPS = 1; // 0x1 @@ -12327,6 +12377,7 @@ package android.content.pm { field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH = "android.hardware.touchscreen.multitouch"; field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT = "android.hardware.touchscreen.multitouch.distinct"; field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH_JAZZHAND = "android.hardware.touchscreen.multitouch.jazzhand"; + field public static final String FEATURE_TRANSLATION = "android.software.translation"; field public static final String FEATURE_USB_ACCESSORY = "android.hardware.usb.accessory"; field public static final String FEATURE_USB_HOST = "android.hardware.usb.host"; field public static final String FEATURE_VERIFIED_BOOT = "android.software.verified_boot"; @@ -31384,6 +31435,7 @@ package android.os { method @NonNull public android.os.VibrationEffect.Composition addPrimitive(int, @FloatRange(from=0.0f, to=1.0f) float, @IntRange(from=0) int); method @NonNull public android.os.VibrationEffect compose(); field public static final int PRIMITIVE_CLICK = 1; // 0x1 + field public static final int PRIMITIVE_LOW_TICK = 8; // 0x8 field public static final int PRIMITIVE_QUICK_FALL = 6; // 0x6 field public static final int PRIMITIVE_QUICK_RISE = 4; // 0x4 field public static final int PRIMITIVE_SLOW_RISE = 5; // 0x5 @@ -48717,6 +48769,7 @@ package android.view { method public void setMaxTextEms(int); method public void setMaxTextLength(int); method public void setMinTextEms(int); + method public void setOnReceiveContentMimeTypes(@Nullable String[]); method public abstract void setOpaque(boolean); method public abstract void setSelected(boolean); method public abstract void setText(CharSequence); @@ -51559,6 +51612,66 @@ package android.view.textservice { } +package android.view.translation { + + public final class TranslationManager { + method @Nullable @WorkerThread public android.view.translation.Translator createTranslator(@NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec); + method @NonNull @WorkerThread public java.util.List<java.lang.String> getSupportedLocales(); + } + + public final class TranslationRequest implements android.os.Parcelable { + ctor public TranslationRequest(@Nullable CharSequence); + method public int describeContents(); + method @Nullable public android.view.autofill.AutofillId getAutofillId(); + method @Nullable public CharSequence getTranslationText(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.TranslationRequest> CREATOR; + } + + public static final class TranslationRequest.Builder { + ctor public TranslationRequest.Builder(); + method @NonNull public android.view.translation.TranslationRequest build(); + method @NonNull public android.view.translation.TranslationRequest.Builder setAutofillId(@NonNull android.view.autofill.AutofillId); + method @NonNull public android.view.translation.TranslationRequest.Builder setTranslationText(@NonNull CharSequence); + } + + public final class TranslationResponse implements android.os.Parcelable { + method public int describeContents(); + method public int getTranslationStatus(); + method @NonNull public java.util.List<android.view.translation.TranslationRequest> getTranslations(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.TranslationResponse> CREATOR; + field public static final int TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE = 2; // 0x2 + field public static final int TRANSLATION_STATUS_SUCCESS = 0; // 0x0 + field public static final int TRANSLATION_STATUS_UNKNOWN_ERROR = 1; // 0x1 + } + + public static final class TranslationResponse.Builder { + ctor public TranslationResponse.Builder(int); + method @NonNull public android.view.translation.TranslationResponse.Builder addTranslations(@NonNull android.view.translation.TranslationRequest); + method @NonNull public android.view.translation.TranslationResponse build(); + method @NonNull public android.view.translation.TranslationResponse.Builder setTranslationStatus(int); + method @NonNull public android.view.translation.TranslationResponse.Builder setTranslations(@NonNull java.util.List<android.view.translation.TranslationRequest>); + } + + public final class TranslationSpec implements android.os.Parcelable { + ctor public TranslationSpec(@NonNull String, int); + method public int describeContents(); + method public int getDataFormat(); + method @NonNull public String getLanguage(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.TranslationSpec> CREATOR; + field public static final int DATA_FORMAT_TEXT = 1; // 0x1 + } + + public class Translator { + method public void destroy(); + method public boolean isDestroyed(); + method @Nullable @WorkerThread public android.view.translation.TranslationResponse translate(@NonNull android.view.translation.TranslationRequest); + } + +} + package android.webkit { public abstract class ClientCertRequest { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 9a15e770d47d..da79b67c245e 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -52,6 +52,7 @@ package android { field public static final String BIND_TELEPHONY_NETWORK_SERVICE = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE"; field public static final String BIND_TEXTCLASSIFIER_SERVICE = "android.permission.BIND_TEXTCLASSIFIER_SERVICE"; field public static final String BIND_TIME_ZONE_PROVIDER_SERVICE = "android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE"; + field public static final String BIND_TRANSLATION_SERVICE = "android.permission.BIND_TRANSLATION_SERVICE"; field public static final String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT"; field public static final String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE"; field public static final String BRICK = "android.permission.BRICK"; @@ -906,6 +907,7 @@ package android.app.admin { field public static final int STATE_USER_SETUP_FINALIZED = 3; // 0x3 field public static final int STATE_USER_SETUP_INCOMPLETE = 1; // 0x1 field public static final int STATE_USER_UNMANAGED = 0; // 0x0 + field public static final int SUPPORTED_MODES_DEVICE_OWNER = 4; // 0x4 field public static final int SUPPORTED_MODES_ORGANIZATION_AND_PERSONALLY_OWNED = 3; // 0x3 field public static final int SUPPORTED_MODES_ORGANIZATION_OWNED = 1; // 0x1 field public static final int SUPPORTED_MODES_PERSONALLY_OWNED = 2; // 0x2 @@ -1370,8 +1372,6 @@ package android.app.role { method @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHolders(@NonNull String); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle); - method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isApplicationVisibleForRole(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); - method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void removeOnRoleHoldersChangedListenerAsUser(@NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String); @@ -1942,6 +1942,7 @@ package android.content { field public static final String SYSTEM_CONFIG_SERVICE = "system_config"; field public static final String SYSTEM_UPDATE_SERVICE = "system_update"; field public static final String TETHERING_SERVICE = "tethering"; + field public static final String TRANSLATION_MANAGER_SERVICE = "transformer"; field public static final String VR_SERVICE = "vrmanager"; field public static final String WIFI_NL80211_SERVICE = "wifinl80211"; field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager"; @@ -8618,6 +8619,7 @@ package android.provider { field public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager"; field public static final String NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT = "activity_manager_native_boot"; field public static final String NAMESPACE_APP_COMPAT = "app_compat"; + field public static final String NAMESPACE_APP_HIBERNATION = "app_hibernation"; field public static final String NAMESPACE_ATTENTION_MANAGER_SERVICE = "attention_manager_service"; field public static final String NAMESPACE_AUTOFILL = "autofill"; field public static final String NAMESPACE_BATTERY_SAVER = "battery_saver"; @@ -9854,6 +9856,48 @@ package android.service.timezone { } +package android.service.translation { + + public final class TranslationRequest implements android.os.Parcelable { + ctor public TranslationRequest(int, @NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, @NonNull java.util.List<android.view.translation.TranslationRequest>); + method public int describeContents(); + method @NonNull public android.view.translation.TranslationSpec getDestSpec(); + method public int getRequestId(); + method @NonNull public android.view.translation.TranslationSpec getSourceSpec(); + method @NonNull public java.util.List<android.view.translation.TranslationRequest> getTranslationRequests(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.translation.TranslationRequest> CREATOR; + } + + public static final class TranslationRequest.Builder { + ctor public TranslationRequest.Builder(int, @NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, @NonNull java.util.List<android.view.translation.TranslationRequest>); + method @NonNull public android.service.translation.TranslationRequest.Builder addTranslationRequests(@NonNull android.view.translation.TranslationRequest); + method @NonNull public android.service.translation.TranslationRequest build(); + method @NonNull public android.service.translation.TranslationRequest.Builder setDestSpec(@NonNull android.view.translation.TranslationSpec); + method @NonNull public android.service.translation.TranslationRequest.Builder setRequestId(int); + method @NonNull public android.service.translation.TranslationRequest.Builder setSourceSpec(@NonNull android.view.translation.TranslationSpec); + method @NonNull public android.service.translation.TranslationRequest.Builder setTranslationRequests(@NonNull java.util.List<android.view.translation.TranslationRequest>); + } + + public abstract class TranslationService extends android.app.Service { + ctor public TranslationService(); + method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); + method public void onConnected(); + method public abstract void onCreateTranslationSession(@NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, int); + method public void onDisconnected(); + method public abstract void onFinishTranslationSession(int); + method public abstract void onTranslationRequest(@NonNull android.service.translation.TranslationRequest, int, @NonNull android.os.CancellationSignal, @NonNull android.service.translation.TranslationService.OnTranslationResultCallback); + field public static final String SERVICE_INTERFACE = "android.service.translation.TranslationService"; + field public static final String SERVICE_META_DATA = "android.translation_service"; + } + + public static interface TranslationService.OnTranslationResultCallback { + method public void onError(); + method public void onTranslationSuccess(@NonNull android.view.translation.TranslationResponse); + } + +} + package android.service.trust { public class TrustAgentService extends android.app.Service { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index f0a02978003e..c03461aa7742 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -18,6 +18,7 @@ package android { field public static final String MANAGE_CRATES = "android.permission.MANAGE_CRATES"; field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS"; field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS"; + field public static final String MANAGE_TOAST_RATE_LIMITING = "android.permission.MANAGE_TOAST_RATE_LIMITING"; field public static final String MODIFY_REFRESH_RATE_SWITCHING_TYPE = "android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE"; field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS"; field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK"; @@ -274,6 +275,7 @@ package android.app { method public boolean isNotificationPolicyAccessGrantedForPackage(@NonNull String); method public boolean matchesCallFilter(android.os.Bundle); method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean, boolean); + method @RequiresPermission(android.Manifest.permission.MANAGE_TOAST_RATE_LIMITING) public void setToastRateLimitingEnabled(boolean); method public void updateNotificationChannel(@NonNull String, int, @NonNull android.app.NotificationChannel); } @@ -447,6 +449,11 @@ package android.app.prediction { package android.app.role { + public class RoleControllerManager { + method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isApplicationVisibleForRole(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); + method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); + } + public final class RoleManager { method @Nullable public String getSmsRoleHolder(int); } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 55df824b1243..55b858e3c602 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -5151,12 +5151,6 @@ public class Activity extends ContextThemeWrapper * #checkSelfPermission(String)}. * </p> * <p> - * Calling this API for permissions already granted to your app would show UI - * to the user to decide whether the app can still hold these permissions. This - * can be useful if the way your app uses data guarded by the permissions - * changes significantly. - * </p> - * <p> * You cannot request a permission if your activity sets {@link * android.R.styleable#AndroidManifestActivity_noHistory noHistory} to * <code>true</code> because in this case the activity would not receive diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 697a377dd99f..bda2fa9b9622 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -228,4 +228,6 @@ interface INotificationManager NotificationListenerFilter getListenerFilter(in ComponentName cn, int userId); void setListenerFilter(in ComponentName cn, int userId, in NotificationListenerFilter nlf); + + void setToastRateLimitingEnabled(boolean enable); } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index e56274aa4fd2..5fbc94822d4e 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -102,6 +102,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -645,6 +646,11 @@ public class Notification implements Parcelable */ public static final int FLAG_IMMEDIATE_FGS_DISPLAY = 0x00002000; + private static final List<Class<? extends Style>> PLATFORM_STYLE_CLASSES = Arrays.asList( + BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class, + DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class, + MessagingStyle.class); + /** @hide */ @IntDef(flag = true, prefix = { "FLAG_" }, value = {FLAG_SHOW_LIGHTS, FLAG_ONGOING_EVENT, FLAG_INSISTENT, FLAG_ONLY_ALERT_ONCE, @@ -5099,7 +5105,7 @@ public class Notification implements Parcelable final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0); final int progress = ex.getInt(EXTRA_PROGRESS, 0); final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE); - if (p.hasProgress && (max != 0 || ind)) { + if (!p.mHideProgress && (max != 0 || ind)) { contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE); contentView.setProgressBar( R.id.progress, max, progress, ind); @@ -5427,7 +5433,7 @@ public class Notification implements Parcelable int N = nonContextualActions.size(); boolean emphazisedMode = mN.fullScreenIntent != null; big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode); - if (N > 0) { + if (N > 0 && !p.mHideActions) { big.setViewVisibility(R.id.actions_container, View.VISIBLE); big.setViewVisibility(R.id.actions, View.VISIBLE); big.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target, @@ -5520,6 +5526,71 @@ public class Notification implements Parcelable return createContentView(false /* increasedheight */ ); } + // This code is executed on behalf of other apps' notifications, sometimes even by 3p apps, + // a use case that is not supported by the Compat Framework library. Workarounds to resolve + // the change's state in NotificationManagerService were very complex. While it's possible + // apps can detect the change, it's most likely that the changes will simply result in + // visual regressions. + @SuppressWarnings("AndroidFrameworkCompatChange") + private boolean fullyCustomViewRequiresDecoration(boolean fromStyle) { + // Custom views which come from a platform style class are safe, and thus do not need to + // be wrapped. Any subclass of those styles has the opportunity to make arbitrary + // changes to the RemoteViews, and thus can't be trusted as a fully vetted view. + if (fromStyle && PLATFORM_STYLE_CLASSES.contains(mStyle.getClass())) { + return false; + } + final ContentResolver contentResolver = mContext.getContentResolver(); + final int decorationType = DevFlags.getFullyCustomViewNotifDecoration(contentResolver); + return decorationType != DevFlags.DECORATION_NONE + && (DevFlags.shouldBackportSNotifRules(contentResolver) + || mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S); + } + + private RemoteViews minimallyDecoratedContentView(@NonNull RemoteViews customContent) { + int decorationType = + DevFlags.getFullyCustomViewNotifDecoration(mContext.getContentResolver()); + StandardTemplateParams p = mParams.reset() + .viewType(StandardTemplateParams.VIEW_TYPE_NORMAL) + .decorationType(decorationType) + .fillTextsFrom(this); + TemplateBindResult result = new TemplateBindResult(); + RemoteViews standard = applyStandardTemplate(getBaseLayoutResource(), p, result); + buildCustomContentIntoTemplate(mContext, standard, customContent, + p, result, decorationType); + return standard; + } + + private RemoteViews minimallyDecoratedBigContentView(@NonNull RemoteViews customContent) { + int decorationType = + DevFlags.getFullyCustomViewNotifDecoration(mContext.getContentResolver()); + StandardTemplateParams p = mParams.reset() + .viewType(StandardTemplateParams.VIEW_TYPE_BIG) + .decorationType(decorationType) + .fillTextsFrom(this); + TemplateBindResult result = new TemplateBindResult(); + RemoteViews standard = applyStandardTemplateWithActions(getBigBaseLayoutResource(), + p, result); + buildCustomContentIntoTemplate(mContext, standard, customContent, + p, result, decorationType); + return standard; + } + + private RemoteViews minimallyDecoratedHeadsUpContentView( + @NonNull RemoteViews customContent) { + int decorationType = + DevFlags.getFullyCustomViewNotifDecoration(mContext.getContentResolver()); + StandardTemplateParams p = mParams.reset() + .viewType(StandardTemplateParams.VIEW_TYPE_HEADS_UP) + .decorationType(decorationType) + .fillTextsFrom(this); + TemplateBindResult result = new TemplateBindResult(); + RemoteViews standard = applyStandardTemplateWithActions(getHeadsUpBaseLayoutResource(), + p, result); + buildCustomContentIntoTemplate(mContext, standard, customContent, + p, result, decorationType); + return standard; + } + /** * Construct a RemoteViews for the smaller content view. * @@ -5532,11 +5603,13 @@ public class Notification implements Parcelable */ public RemoteViews createContentView(boolean increasedHeight) { if (mN.contentView != null && useExistingRemoteView()) { - return mN.contentView; + return fullyCustomViewRequiresDecoration(false /* fromStyle */) + ? minimallyDecoratedContentView(mN.contentView) : mN.contentView; } else if (mStyle != null) { final RemoteViews styleView = mStyle.makeContentView(increasedHeight); if (styleView != null) { - return styleView; + return fullyCustomViewRequiresDecoration(true /* fromStyle */) + ? minimallyDecoratedContentView(styleView) : styleView; } } StandardTemplateParams p = mParams.reset() @@ -5556,18 +5629,30 @@ public class Notification implements Parcelable public RemoteViews createBigContentView() { RemoteViews result = null; if (mN.bigContentView != null && useExistingRemoteView()) { - return mN.bigContentView; + return fullyCustomViewRequiresDecoration(false /* fromStyle */) + ? minimallyDecoratedBigContentView(mN.bigContentView) : mN.bigContentView; } if (mStyle != null) { result = mStyle.makeBigContentView(); hideLine1Text(result); + if (fullyCustomViewRequiresDecoration(true /* fromStyle */)) { + result = minimallyDecoratedBigContentView(result); + } } - if (result == null && bigContentViewRequired()) { - StandardTemplateParams p = mParams.reset() - .viewType(StandardTemplateParams.VIEW_TYPE_BIG) - .fillTextsFrom(this); - result = applyStandardTemplateWithActions(getBigBaseLayoutResource(), p, - null /* result */); + if (result == null) { + if (bigContentViewRequired()) { + StandardTemplateParams p = mParams.reset() + .viewType(StandardTemplateParams.VIEW_TYPE_BIG) + .fillTextsFrom(this); + result = applyStandardTemplateWithActions(getBigBaseLayoutResource(), p, + null /* result */); + } else if (DevFlags.shouldBackportSNotifRules(mContext.getContentResolver()) + && useExistingRemoteView() + && fullyCustomViewRequiresDecoration(false /* fromStyle */)) { + // This "backport" logic is a special case to handle the UNDO style of notif + // so that we can see what that will look like when the app targets S. + result = minimallyDecoratedBigContentView(mN.contentView); + } } makeHeaderExpanded(result); return result; @@ -5661,11 +5746,14 @@ public class Notification implements Parcelable */ public RemoteViews createHeadsUpContentView(boolean increasedHeight) { if (mN.headsUpContentView != null && useExistingRemoteView()) { - return mN.headsUpContentView; + return fullyCustomViewRequiresDecoration(false /* fromStyle */) + ? minimallyDecoratedHeadsUpContentView(mN.headsUpContentView) + : mN.headsUpContentView; } else if (mStyle != null) { final RemoteViews styleView = mStyle.makeHeadsUpContentView(increasedHeight); if (styleView != null) { - return styleView; + return fullyCustomViewRequiresDecoration(true /* fromStyle */) + ? minimallyDecoratedHeadsUpContentView(styleView) : styleView; } } else if (mActions.size() == 0) { return null; @@ -5677,8 +5765,7 @@ public class Notification implements Parcelable .viewType(StandardTemplateParams.VIEW_TYPE_HEADS_UP) .fillTextsFrom(this) .setMaxRemoteInputHistory(1); - return applyStandardTemplateWithActions(getHeadsUpBaseLayoutResource(), - p, + return applyStandardTemplateWithActions(getHeadsUpBaseLayoutResource(), p, null /* result */); } @@ -6596,11 +6683,7 @@ public class Notification implements Parcelable */ @SystemApi public static Class<? extends Style> getNotificationStyleClass(String templateClass) { - Class<? extends Style>[] classes = new Class[] { - BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class, - DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class, - MessagingStyle.class }; - for (Class<? extends Style> innerClass : classes) { + for (Class<? extends Style> innerClass : PLATFORM_STYLE_CLASSES) { if (templateClass.equals(innerClass.getName())) { return innerClass; } @@ -6608,6 +6691,56 @@ public class Notification implements Parcelable return null; } + private static void buildCustomContentIntoTemplate(@NonNull Context context, + @NonNull RemoteViews template, @Nullable RemoteViews customContent, + @NonNull StandardTemplateParams p, @NonNull TemplateBindResult result, + int decorationType) { + int childIndex = -1; + if (customContent != null) { + // Need to clone customContent before adding, because otherwise it can no longer be + // parceled independently of remoteViews. + customContent = customContent.clone(); + if (p.mHeaderless) { + if (decorationType <= DevFlags.DECORATION_PARTIAL) { + template.removeFromParent(R.id.notification_top_line); + } + if (decorationType != DevFlags.DECORATION_FULL_COMPATIBLE) { + // Change the max content size from 60dp (the compatible size) to 48dp + // (the constrained size). This is done by increasing the minimum margin + // (implemented as top/bottom margins) and decreasing the extra margin + // (implemented as the height of shrinkable top/bottom views in the column). + template.setViewLayoutMarginDimen( + R.id.notification_headerless_view_column, + RemoteViews.MARGIN_TOP, + R.dimen.notification_headerless_margin_constrained_minimum); + template.setViewLayoutMarginDimen( + R.id.notification_headerless_view_column, + RemoteViews.MARGIN_BOTTOM, + R.dimen.notification_headerless_margin_constrained_minimum); + template.setViewLayoutHeightDimen( + R.id.notification_headerless_margin_extra_top, + R.dimen.notification_headerless_margin_constrained_extra); + template.setViewLayoutHeightDimen( + R.id.notification_headerless_margin_extra_bottom, + R.dimen.notification_headerless_margin_constrained_extra); + } + } else { + // also update the end margin to account for the large icon or expander + Resources resources = context.getResources(); + result.mTitleMarginSet.applyToView(template, R.id.notification_main_column, + resources.getDimension(R.dimen.notification_content_margin_end) + / resources.getDisplayMetrics().density); + } + template.removeAllViewsExceptId(R.id.notification_main_column, R.id.progress); + template.addView(R.id.notification_main_column, customContent, 0 /* index */); + template.addFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED); + childIndex = 0; + } + template.setIntTag(R.id.notification_main_column, + com.android.internal.R.id.notification_custom_view_index_tag, + childIndex); + } + /** * An object that can apply a rich notification style to a {@link Notification.Builder} * object. @@ -7813,7 +7946,7 @@ public class Notification implements Parcelable StandardTemplateParams p = mBuilder.mParams.reset() .viewType(isCollapsed ? StandardTemplateParams.VIEW_TYPE_NORMAL : StandardTemplateParams.VIEW_TYPE_BIG) - .hasProgress(false) + .hideProgress(true) .title(conversationTitle) .text(null) .hideLargeIcon(hideRightIcons || isOneToOne) @@ -8628,7 +8761,8 @@ public class Notification implements Parcelable private RemoteViews makeMediaContentView() { StandardTemplateParams p = mBuilder.mParams.reset() .viewType(StandardTemplateParams.VIEW_TYPE_NORMAL) - .hasProgress(false).fillTextsFrom(mBuilder); + .hideProgress(true) + .fillTextsFrom(mBuilder); RemoteViews view = mBuilder.applyStandardTemplate( R.layout.notification_template_material_media, p, null /* result */); @@ -8677,7 +8811,8 @@ public class Notification implements Parcelable } StandardTemplateParams p = mBuilder.mParams.reset() .viewType(StandardTemplateParams.VIEW_TYPE_BIG) - .hasProgress(false).fillTextsFrom(mBuilder); + .hideProgress(true) + .fillTextsFrom(mBuilder); RemoteViews big = mBuilder.applyStandardTemplate( R.layout.notification_template_material_big_media, p , null /* result */); @@ -8771,29 +8906,39 @@ public class Notification implements Parcelable RemoteViews headsUpContentView = mBuilder.mN.headsUpContentView == null ? mBuilder.mN.contentView : mBuilder.mN.headsUpContentView; + if (headsUpContentView == null) { + return null; // no custom view; use the default behavior + } if (mBuilder.mActions.size() == 0) { return makeStandardTemplateWithCustomContent(headsUpContentView); } + int decorationType = getDecorationType(); TemplateBindResult result = new TemplateBindResult(); StandardTemplateParams p = mBuilder.mParams.reset() .viewType(StandardTemplateParams.VIEW_TYPE_HEADS_UP) - .hasCustomContent(headsUpContentView != null) + .decorationType(decorationType) .fillTextsFrom(mBuilder); RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions( mBuilder.getHeadsUpBaseLayoutResource(), p, result); - buildIntoRemoteViewContent(remoteViews, headsUpContentView, result, true); + buildCustomContentIntoTemplate(mBuilder.mContext, remoteViews, headsUpContentView, + p, result, decorationType); return remoteViews; } private RemoteViews makeStandardTemplateWithCustomContent(RemoteViews customContent) { + if (customContent == null) { + return null; // no custom view; use the default behavior + } + int decorationType = getDecorationType(); TemplateBindResult result = new TemplateBindResult(); StandardTemplateParams p = mBuilder.mParams.reset() .viewType(StandardTemplateParams.VIEW_TYPE_NORMAL) - .hasCustomContent(customContent != null) + .decorationType(decorationType) .fillTextsFrom(mBuilder); RemoteViews remoteViews = mBuilder.applyStandardTemplate( mBuilder.getBaseLayoutResource(), p, result); - buildIntoRemoteViewContent(remoteViews, customContent, result, true); + buildCustomContentIntoTemplate(mBuilder.mContext, remoteViews, customContent, + p, result, decorationType); return remoteViews; } @@ -8801,38 +8946,37 @@ public class Notification implements Parcelable RemoteViews bigContentView = mBuilder.mN.bigContentView == null ? mBuilder.mN.contentView : mBuilder.mN.bigContentView; + if (bigContentView == null) { + return null; // no custom view; use the default behavior + } + int decorationType = getDecorationType(); TemplateBindResult result = new TemplateBindResult(); StandardTemplateParams p = mBuilder.mParams.reset() .viewType(StandardTemplateParams.VIEW_TYPE_BIG) - .hasCustomContent(bigContentView != null) + .decorationType(decorationType) .fillTextsFrom(mBuilder); RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions( mBuilder.getBigBaseLayoutResource(), p, result); - buildIntoRemoteViewContent(remoteViews, bigContentView, result, false); + buildCustomContentIntoTemplate(mBuilder.mContext, remoteViews, bigContentView, + p, result, decorationType); return remoteViews; } - private void buildIntoRemoteViewContent(RemoteViews remoteViews, - RemoteViews customContent, TemplateBindResult result, boolean headerless) { - int childIndex = -1; - if (customContent != null) { - // Need to clone customContent before adding, because otherwise it can no longer be - // parceled independently of remoteViews. - customContent = customContent.clone(); - remoteViews.removeAllViewsExceptId(R.id.notification_main_column, R.id.progress); - remoteViews.addView(R.id.notification_main_column, customContent, 0 /* index */); - remoteViews.addFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED); - childIndex = 0; - } - remoteViews.setIntTag(R.id.notification_main_column, - com.android.internal.R.id.notification_custom_view_index_tag, - childIndex); - if (!headerless) { - // also update the end margin to account for the large icon or expander - Resources resources = mBuilder.mContext.getResources(); - result.mTitleMarginSet.applyToView(remoteViews, R.id.notification_main_column, - resources.getDimension(R.dimen.notification_content_margin_end) - / resources.getDisplayMetrics().density); + // This code is executed on behalf of other apps' notifications, sometimes even by 3p apps, + // a use case that is not supported by the Compat Framework library. Workarounds to resolve + // the change's state in NotificationManagerService were very complex. While it's possible + // apps can detect the change, it's most likely that the changes will simply result in + // visual regressions. + @SuppressWarnings("AndroidFrameworkCompatChange") + private int getDecorationType() { + ContentResolver contentResolver = mBuilder.mContext.getContentResolver(); + if (mBuilder.mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S + || DevFlags.shouldBackportSNotifRules(contentResolver)) { + return DevFlags.getDecoratedCustomViewNotifDecoration(contentResolver); + } else { + // For apps that don't target S, this decoration provides the closest behavior to R, + // but doesn't fit with the design guidelines for S. + return DevFlags.DECORATION_FULL_COMPATIBLE; } } @@ -11159,8 +11303,9 @@ public class Notification implements Parcelable int mViewType = VIEW_TYPE_UNSPECIFIED; boolean mHeaderless; - boolean mHasCustomContent; - boolean hasProgress = true; + boolean mHideTitle; + boolean mHideActions; + boolean mHideProgress; CharSequence title; CharSequence text; CharSequence headerTextSecondary; @@ -11173,8 +11318,9 @@ public class Notification implements Parcelable final StandardTemplateParams reset() { mViewType = VIEW_TYPE_UNSPECIFIED; mHeaderless = false; - mHasCustomContent = false; - hasProgress = true; + mHideTitle = false; + mHideActions = false; + mHideProgress = false; title = null; text = null; summaryText = null; @@ -11186,9 +11332,7 @@ public class Notification implements Parcelable } final boolean hasTitle() { - // We hide the title when the notification is a decorated custom view so that decorated - // custom views always have to include their own title. - return !TextUtils.isEmpty(title) && !mHasCustomContent; + return !TextUtils.isEmpty(title) && !mHideTitle; } final StandardTemplateParams viewType(int viewType) { @@ -11201,13 +11345,18 @@ public class Notification implements Parcelable return this; } - final StandardTemplateParams hasProgress(boolean hasProgress) { - this.hasProgress = hasProgress; + final StandardTemplateParams hideActions(boolean hideActions) { + this.mHideActions = hideActions; return this; } - final StandardTemplateParams hasCustomContent(boolean hasCustomContent) { - this.mHasCustomContent = hasCustomContent; + final StandardTemplateParams hideProgress(boolean hideProgress) { + this.mHideProgress = hideProgress; + return this; + } + + final StandardTemplateParams hideTitle(boolean hideTitle) { + this.mHideTitle = hideTitle; return this; } @@ -11263,6 +11412,16 @@ public class Notification implements Parcelable this.maxRemoteInputHistory = maxRemoteInputHistory; return this; } + + public StandardTemplateParams decorationType(int decorationType) { + boolean hideTitle = decorationType <= DevFlags.DECORATION_FULL_COMPATIBLE; + boolean hideOtherFields = decorationType <= DevFlags.DECORATION_MINIMAL; + hideTitle(hideTitle); + hideLargeIcon(hideOtherFields); + hideProgress(hideOtherFields); + hideActions(hideOtherFields); + return this; + } } /** @@ -11272,7 +11431,67 @@ public class Notification implements Parcelable * @hide */ public static class DevFlags { + + /** + * Notifications will not be decorated. The custom content will be shown as-is. + * + * <p>NOTE: This option is not available for notifications with DecoratedCustomViewStyle, + * as that API contract includes decorations that this does not provide. + */ + public static final int DECORATION_NONE = 0; + + /** + * Notifications will be minimally decorated with ONLY an icon and expander as follows: + * <li>A large icon is never shown. + * <li>A progress bar is never shown. + * <li>The expanded and heads up states do not show actions, even if provided. + * <li>The collapsed state gives the app's custom content 48dp of vertical space. + * <li>The collapsed state does NOT include the top line of views, + * like the alerted icon or work profile badge. + * + * <p>NOTE: This option is not available for notifications with DecoratedCustomViewStyle, + * as that API contract includes decorations that this does not provide. + */ + public static final int DECORATION_MINIMAL = 1; + + /** + * Notifications will be partially decorated with AT LEAST an icon and expander as follows: + * <li>A large icon is shown if provided. + * <li>A progress bar is shown if provided and enough space remains below the content. + * <li>Actions are shown in the expanded and heads up states. + * <li>The collapsed state gives the app's custom content 48dp of vertical space. + * <li>The collapsed state does NOT include the top line of views, + * like the alerted icon or work profile badge. + */ + public static final int DECORATION_PARTIAL = 2; + + /** + * Notifications will be fully decorated as follows: + * <li>A large icon is shown if provided. + * <li>A progress bar is shown if provided and enough space remains below the content. + * <li>Actions are shown in the expanded and heads up states. + * <li>The collapsed state gives the app's custom content 40dp of vertical space. + * <li>The collapsed state DOES include the top line of views, + * like the alerted icon or work profile badge. + * <li>The collapsed state's top line views will never show the title text. + */ + public static final int DECORATION_FULL_COMPATIBLE = 3; + + /** + * Notifications will be fully decorated as follows: + * <li>A large icon is shown if provided. + * <li>A progress bar is shown if provided and enough space remains below the content. + * <li>Actions are shown in the expanded and heads up states. + * <li>The collapsed state gives the app's custom content 20dp of vertical space. + * <li>The collapsed state DOES include the top line of views + * like the alerted icon or work profile badge. + * <li>The collapsed state's top line views will contain the title text if provided. + */ + public static final int DECORATION_FULL_CONSTRAINED = 4; + private static final boolean DEFAULT_BACKPORT_S_NOTIF_RULES = false; + private static final int DEFAULT_FULLY_CUSTOM_DECORATION = DECORATION_MINIMAL; + private static final int DEFAULT_DECORATED_DECORATION = DECORATION_PARTIAL; /** * Used by unit tests to force that this class returns its default values, which is required @@ -11292,5 +11511,37 @@ public class Notification implements Parcelable return Settings.Global.getInt(contentResolver, Settings.Global.BACKPORT_S_NOTIF_RULES, DEFAULT_BACKPORT_S_NOTIF_RULES ? 1 : 0) == 1; } + + /** + * @return the decoration type to be applied to notifications with fully custom view. + * @hide + */ + public static int getFullyCustomViewNotifDecoration( + @NonNull ContentResolver contentResolver) { + if (sForceDefaults) { + return DEFAULT_FULLY_CUSTOM_DECORATION; + } + final int decoration = Settings.Global.getInt(contentResolver, + Settings.Global.FULLY_CUSTOM_VIEW_NOTIF_DECORATION, + DEFAULT_FULLY_CUSTOM_DECORATION); + // clamp to a valid decoration value + return Math.max(DECORATION_NONE, Math.min(decoration, DECORATION_FULL_CONSTRAINED)); + } + + /** + * @return the decoration type to be applied to notifications with DecoratedCustomViewStyle. + * @hide + */ + public static int getDecoratedCustomViewNotifDecoration( + @NonNull ContentResolver contentResolver) { + if (sForceDefaults) { + return DEFAULT_DECORATED_DECORATION; + } + final int decoration = Settings.Global.getInt(contentResolver, + Settings.Global.DECORATED_CUSTOM_VIEW_NOTIF_DECORATION, + DEFAULT_DECORATED_DECORATION); + // clamp to a valid decoration value (and don't allow decoration to be NONE or MINIMAL) + return Math.max(DECORATION_PARTIAL, Math.min(decoration, DECORATION_FULL_CONSTRAINED)); + } } } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 58f382dc2c6c..6cce270bc5a3 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1730,6 +1730,23 @@ public class NotificationManager { } /** + * Controls whether toast rate limiting is enabled for the calling uid. + * + * @param enable true to enable toast rate limiting, false to disable it + * @hide + */ + @TestApi + @RequiresPermission(android.Manifest.permission.MANAGE_TOAST_RATE_LIMITING) + public void setToastRateLimitingEnabled(boolean enable) { + INotificationManager service = getService(); + try { + service.setToastRateLimitingEnabled(enable); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Notification policy configuration. Represents user-preferences for notification * filtering. */ diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS index 06ad9c99c301..c804e6427dce 100644 --- a/core/java/android/app/OWNERS +++ b/core/java/android/app/OWNERS @@ -2,6 +2,33 @@ # Remain no owner because multiple modules may touch this file. per-file ContextImpl.java = * +# ActivityManager +per-file ActivityManager* = file:/services/core/java/com/android/server/am/OWNERS +per-file ApplicationErrorReport* = file:/services/core/java/com/android/server/am/OWNERS +per-file ApplicationExitInfo* = file:/services/core/java/com/android/server/am/OWNERS +per-file Application.java = file:/services/core/java/com/android/server/am/OWNERS +per-file ApplicationLoaders.java = file:/services/core/java/com/android/server/am/OWNERS +per-file ApplicationThreadConstants.java = file:/services/core/java/com/android/server/am/OWNERS +per-file BroadcastOptions.java = file:/services/core/java/com/android/server/am/OWNERS +per-file ContentProviderHolder* = file:/services/core/java/com/android/server/am/OWNERS +per-file IActivityController.aidl = file:/services/core/java/com/android/server/am/OWNERS +per-file IActivityManager.aidl = file:/services/core/java/com/android/server/am/OWNERS +per-file IApplicationThread.aidl = file:/services/core/java/com/android/server/am/OWNERS +per-file IAppTraceRetriever.aidl = file:/services/core/java/com/android/server/am/OWNERS +per-file IInstrumentationWatcher.aidl = file:/services/core/java/com/android/server/am/OWNERS +per-file IntentService.aidl = file:/services/core/java/com/android/server/am/OWNERS +per-file IServiceConnection.aidl = file:/services/core/java/com/android/server/am/OWNERS +per-file IStopUserCallback.aidl = file:/services/core/java/com/android/server/am/OWNERS +per-file IUidObserver.aidl = file:/services/core/java/com/android/server/am/OWNERS +per-file LoadedApk.java = file:/services/core/java/com/android/server/am/OWNERS +per-file LocalActivityManager.java = file:/services/core/java/com/android/server/am/OWNERS +per-file PendingIntent* = file:/services/core/java/com/android/server/am/OWNERS +per-file *Process* = file:/services/core/java/com/android/server/am/OWNERS +per-file ProfilerInfo* = file:/services/core/java/com/android/server/am/OWNERS +per-file Service* = file:/services/core/java/com/android/server/am/OWNERS +per-file SystemServiceRegistry.java = file:/services/core/java/com/android/server/am/OWNERS +per-file *UserSwitchObserver* = file:/services/core/java/com/android/server/am/OWNERS + # ActivityThread per-file ActivityThread.java = file:/services/core/java/com/android/server/am/OWNERS per-file ActivityThread.java = file:/services/core/java/com/android/server/wm/OWNERS diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 2ce0e877aa9f..050d194c4094 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -29,7 +29,9 @@ import android.app.blob.BlobStoreManagerFrameworkInitializer; import android.app.contentsuggestions.ContentSuggestionsManager; import android.app.contentsuggestions.IContentSuggestionsManager; import android.app.job.JobSchedulerFrameworkInitializer; +import android.app.people.PeopleManager; import android.app.prediction.AppPredictionManager; +import android.app.role.RoleControllerManager; import android.app.role.RoleManager; import android.app.search.SearchUiManager; import android.app.slice.SliceManager; @@ -585,6 +587,13 @@ public final class SystemServiceRegistry { return new NsdManager(ctx.getOuterContext(), service); }}); + registerService(Context.PEOPLE_SERVICE, PeopleManager.class, + new CachedServiceFetcher<PeopleManager>() { + @Override + public PeopleManager createService(ContextImpl ctx) throws ServiceNotFoundException { + return new PeopleManager(ctx); + }}); + registerService(Context.POWER_SERVICE, PowerManager.class, new CachedServiceFetcher<PowerManager>() { @Override @@ -1290,6 +1299,14 @@ public final class SystemServiceRegistry { return new RoleManager(ctx.getOuterContext()); }}); + registerService(Context.ROLE_CONTROLLER_SERVICE, RoleControllerManager.class, + new CachedServiceFetcher<RoleControllerManager>() { + @Override + public RoleControllerManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + return new RoleControllerManager(ctx.getOuterContext()); + }}); + registerService(Context.DYNAMIC_SYSTEM_SERVICE, DynamicSystemManager.class, new CachedServiceFetcher<DynamicSystemManager>() { @Override diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 806cb496c4c3..97879b80ea06 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -39,7 +39,6 @@ import android.annotation.WorkerThread; import android.app.Activity; import android.app.IServiceConnection; import android.app.KeyguardManager; -import android.app.admin.DevicePolicyManager.DevicePolicyOperation; import android.app.admin.SecurityLog.SecurityEvent; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; @@ -306,7 +305,13 @@ public class DevicePolicyManager { * of the provisioning flow was successful, although this doesn't guarantee the full flow will * succeed. Conversely a result code of {@link android.app.Activity#RESULT_CANCELED} implies * that the user backed-out of provisioning, or some precondition for provisioning wasn't met. + * + * @deprecated admin apps must now implement activities with intent filters for the {@link + * #ACTION_GET_PROVISIONING_MODE} and {@link #ACTION_ADMIN_POLICY_COMPLIANCE} intent actions; + * using {@link #ACTION_PROVISION_MANAGED_DEVICE} to start provisioning will cause the + * provisioning to fail. */ + @Deprecated @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_PROVISION_MANAGED_DEVICE = "android.app.action.PROVISION_MANAGED_DEVICE"; @@ -598,6 +603,12 @@ public class DevicePolicyManager { * properties will be converted into a {@link android.os.PersistableBundle} and passed to the * management application after provisioning. * + * <p>Admin apps will receive this extra in their {@link #ACTION_GET_PROVISIONING_MODE} and + * {@link #ACTION_ADMIN_POLICY_COMPLIANCE} intent handlers. Additionally, {@link + * #ACTION_GET_PROVISIONING_MODE} may also return this extra which will then be sent over to + * {@link #ACTION_ADMIN_POLICY_COMPLIANCE}, alongside the original values that were passed to + * {@link #ACTION_GET_PROVISIONING_MODE}. + * * <p> * In both cases the application receives the data in * {@link DeviceAdminReceiver#onProfileProvisioningComplete} via an intent with the action @@ -655,7 +666,9 @@ public class DevicePolicyManager { * profile provisioning. If the account supplied is present in the primary user, it will be * copied, along with its credentials to the managed profile and removed from the primary user. * - * Use with {@link #ACTION_PROVISION_MANAGED_PROFILE}. + * Use with {@link #ACTION_PROVISION_MANAGED_PROFILE}, with managed account provisioning, or + * return as an extra to the intent result from the {@link #ACTION_GET_PROVISIONING_MODE} + * activity. */ public static final String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE @@ -669,8 +682,10 @@ public class DevicePolicyManager { * * <p> Defaults to {@code false} * - * <p> Use with {@link #ACTION_PROVISION_MANAGED_PROFILE} and - * {@link #EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE} + * <p> Use with {@link #ACTION_PROVISION_MANAGED_PROFILE} or set as an extra to the + * intent result of the {@link #ACTION_GET_PROVISIONING_MODE} activity. + * + * @see #EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE */ public static final String EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION = "android.app.extra.PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION"; @@ -697,8 +712,9 @@ public class DevicePolicyManager { * A Boolean extra that can be used by the mobile device management application to skip the * disabling of system apps during provisioning when set to {@code true}. * - * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} or an intent with action - * {@link #ACTION_PROVISION_MANAGED_DEVICE} that starts device owner provisioning. + * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC}, an intent with action + * {@link #ACTION_PROVISION_MANAGED_PROFILE} that starts profile owner provisioning or + * set as an extra to the intent result of the {@link #ACTION_GET_PROVISIONING_MODE} activity. */ public static final String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED"; @@ -1175,27 +1191,15 @@ public class DevicePolicyManager { "android.app.extra.PROVISIONING_DISCLAIMER_CONTENT"; /** - * A boolean extra indicating if user setup should be skipped, for when provisioning is started - * during setup-wizard. - * - * <p>If unspecified, defaults to {@code true} to match the behavior in - * {@link android.os.Build.VERSION_CODES#M} and earlier. - * - * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE} or - * {@link #ACTION_PROVISION_MANAGED_USER}. - * - * @hide - */ - public static final String EXTRA_PROVISIONING_SKIP_USER_SETUP = - "android.app.extra.PROVISIONING_SKIP_USER_SETUP"; - - /** * A boolean extra indicating if the user consent steps from the provisioning flow should be * skipped. If unspecified, defaults to {@code false}. * * It can only be used by an existing device owner trying to create a managed profile via * {@link #ACTION_PROVISION_MANAGED_PROFILE}. Otherwise it is ignored. + * + * @deprecated this extra is no longer relevant as device owners cannot create managed profiles */ + @Deprecated public static final String EXTRA_PROVISIONING_SKIP_USER_CONSENT = "android.app.extra.PROVISIONING_SKIP_USER_CONSENT"; @@ -1252,7 +1256,8 @@ public class DevicePolicyManager { @IntDef(prefix = { "SUPPORTED_MODES_" }, value = { SUPPORTED_MODES_ORGANIZATION_OWNED, SUPPORTED_MODES_PERSONALLY_OWNED, - SUPPORTED_MODES_ORGANIZATION_AND_PERSONALLY_OWNED + SUPPORTED_MODES_ORGANIZATION_AND_PERSONALLY_OWNED, + SUPPORTED_MODES_DEVICE_OWNER }) @Retention(RetentionPolicy.SOURCE) public @interface ProvisioningConfiguration {} @@ -1379,6 +1384,15 @@ public class DevicePolicyManager { public static final int SUPPORTED_MODES_ORGANIZATION_AND_PERSONALLY_OWNED = 3; /** + * A value for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that the only supported + * provisioning mode is device owner. + * + * @hide + */ + @SystemApi + public static final int SUPPORTED_MODES_DEVICE_OWNER = 4; + + /** * This MIME type is used for starting the device owner provisioning. * * <p>During device owner provisioning a device admin app is set as the owner of the device. @@ -2456,6 +2470,16 @@ public class DevicePolicyManager { * <p>The target activity may also return the account that needs to be migrated from primary * user to managed profile in case of a profile owner provisioning in * {@link #EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE} as result. + * + * <p>The target activity may also include the {@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE} + * extra in the intent result. The values of this {@link android.os.PersistableBundle} will be + * sent as an intent extra of the same name to the {@link #ACTION_ADMIN_POLICY_COMPLIANCE} + * activity, along with the values of the {@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE} extra + * that are already supplied to this activity. + * + * @see #EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION + * @see #EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED + * @see #ACTION_ADMIN_POLICY_COMPLIANCE */ public static final String ACTION_GET_PROVISIONING_MODE = "android.app.action.GET_PROVISIONING_MODE"; @@ -2504,6 +2528,7 @@ public class DevicePolicyManager { * @see #SUPPORTED_MODES_ORGANIZATION_OWNED * @see #SUPPORTED_MODES_PERSONALLY_OWNED * @see #SUPPORTED_MODES_ORGANIZATION_AND_PERSONALLY_OWNED + * @see #SUPPORTED_MODES_DEVICE_OWNER * @hide */ @SystemApi @@ -2545,6 +2570,11 @@ public class DevicePolicyManager { * provisioning flow (in which case this gets sent after {@link #ACTION_GET_PROVISIONING_MODE}), * or it could happen during provisioning finalization if the administrator supports * finalization during setup wizard. + * + * <p>Intents with this action may also be supplied with the {@link + * #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE} extra. + * + * @see #ACTION_GET_PROVISIONING_MODE */ public static final String ACTION_ADMIN_POLICY_COMPLIANCE = "android.app.action.ADMIN_POLICY_COMPLIANCE"; @@ -12783,4 +12813,66 @@ public class DevicePolicyManager { } } } + + /** + * Returns an enrollment-specific identifier of this device, which is guaranteed to be the same + * value for the same device, enrolled into the same organization by the same managing app. + * This identifier is high-entropy, useful for uniquely identifying individual devices within + * the same organisation. + * It is available both in a work profile and on a fully-managed device. + * The identifier would be consistent even if the work profile is removed and enrolled again + * (to the same organization), or the device is factory reset and re-enrolled. + + * Can only be called by the Profile Owner or Device Owner, if the + * {@link #setOrganizationId(String)} was previously called. + * If {@link #setOrganizationId(String)} was not called, then the returned value will be an + * empty string. + * + * @return A stable, enrollment-specific identifier. + * @throws SecurityException if the caller is not a profile owner or device owner. + */ + @NonNull public String getEnrollmentSpecificId() { + if (mService == null) { + return ""; + } + + try { + return mService.getEnrollmentSpecificId(); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Sets the Enterprise ID for the work profile or managed device. This is a requirement for + * generating an enrollment-specific ID for the device, see {@link #getEnrollmentSpecificId()}. + * + * It is recommended that the Enterprise ID is at least 6 characters long, and no more than + * 64 characters. + * + * @param enterpriseId An identifier of the organization this work profile or device is + * enrolled into. + */ + public void setOrganizationId(@NonNull String enterpriseId) { + setOrganizationIdForUser(mContext.getPackageName(), enterpriseId, myUserId()); + } + + /** + * Sets the Enterprise ID for the work profile or managed device. This is a requirement for + * generating an enrollment-specific ID for the device, see + * {@link #getEnrollmentSpecificId()}. + * + * @hide + */ + public void setOrganizationIdForUser(@NonNull String packageName, + @NonNull String enterpriseId, @UserIdInt int userId) { + if (mService == null) { + return; + } + try { + mService.setOrganizationIdForUser(packageName, enterpriseId, userId); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 4b87bb9cae6c..e81abfe5a409 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -489,4 +489,7 @@ interface IDevicePolicyManager { boolean canProfileOwnerResetPasswordWhenLocked(int userId); void setNextOperationSafety(int operation, boolean safe); + + String getEnrollmentSpecificId(); + void setOrganizationIdForUser(in String callerPackage, in String enterpriseId, int userId); } diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 0f7fac4eefa5..65a21640bb98 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -672,6 +672,7 @@ public class AssistStructure implements Parcelable { static final int FLAGS_CONTEXT_CLICKABLE = 0x00004000; static final int FLAGS_OPAQUE = 0x00008000; + static final int FLAGS_HAS_MIME_TYPES = 0x80000000; static final int FLAGS_HAS_MATRIX = 0x40000000; static final int FLAGS_HAS_ALPHA = 0x20000000; static final int FLAGS_HAS_ELEVATION = 0x10000000; @@ -715,6 +716,7 @@ public class AssistStructure implements Parcelable { String mWebDomain; Bundle mExtras; LocaleList mLocaleList; + String[] mOnReceiveContentMimeTypes; ViewNode[] mChildren; @@ -880,6 +882,9 @@ public class AssistStructure implements Parcelable { if ((flags&FLAGS_HAS_LOCALE_LIST) != 0) { mLocaleList = in.readParcelable(null); } + if ((flags & FLAGS_HAS_MIME_TYPES) != 0) { + mOnReceiveContentMimeTypes = in.readStringArray(); + } if ((flags&FLAGS_HAS_EXTRAS) != 0) { mExtras = in.readBundle(); } @@ -939,6 +944,9 @@ public class AssistStructure implements Parcelable { if (mLocaleList != null) { flags |= FLAGS_HAS_LOCALE_LIST; } + if (mOnReceiveContentMimeTypes != null) { + flags |= FLAGS_HAS_MIME_TYPES; + } if (mExtras != null) { flags |= FLAGS_HAS_EXTRAS; } @@ -1109,6 +1117,9 @@ public class AssistStructure implements Parcelable { if ((flags&FLAGS_HAS_LOCALE_LIST) != 0) { out.writeParcelable(mLocaleList, 0); } + if ((flags & FLAGS_HAS_MIME_TYPES) != 0) { + out.writeStringArray(mOnReceiveContentMimeTypes); + } if ((flags&FLAGS_HAS_EXTRAS) != 0) { out.writeBundle(mExtras); } @@ -1527,6 +1538,15 @@ public class AssistStructure implements Parcelable { } /** + * Returns the MIME types accepted by {@link View#performReceiveContent} for this view. See + * {@link View#getOnReceiveContentMimeTypes()} for details. + */ + @Nullable + public String[] getOnReceiveContentMimeTypes() { + return mOnReceiveContentMimeTypes; + } + + /** * Returns any text associated with the node that is displayed to the user, or null * if there is none. */ @@ -2146,6 +2166,11 @@ public class AssistStructure implements Parcelable { } @Override + public void setOnReceiveContentMimeTypes(@Nullable String[] mimeTypes) { + mNode.mOnReceiveContentMimeTypes = mimeTypes; + } + + @Override public void setInputType(int inputType) { mNode.mInputType = inputType; } @@ -2422,6 +2447,10 @@ public class AssistStructure implements Parcelable { if (localeList != null) { Log.i(TAG, prefix + " LocaleList: " + localeList); } + String[] mimeTypes = node.getOnReceiveContentMimeTypes(); + if (mimeTypes != null) { + Log.i(TAG, prefix + " MIME types: " + Arrays.toString(mimeTypes)); + } String hint = node.getHint(); if (hint != null) { Log.i(TAG, prefix + " Hint: " + hint); diff --git a/core/java/android/app/people/ConversationStatus.aidl b/core/java/android/app/people/ConversationStatus.aidl new file mode 100644 index 000000000000..acfe1356304c --- /dev/null +++ b/core/java/android/app/people/ConversationStatus.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2021, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; + +parcelable ConversationStatus;
\ No newline at end of file diff --git a/core/java/android/app/people/ConversationStatus.java b/core/java/android/app/people/ConversationStatus.java new file mode 100644 index 000000000000..d2a0255d572e --- /dev/null +++ b/core/java/android/app/people/ConversationStatus.java @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.drawable.Icon; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +public final class ConversationStatus implements Parcelable { + private static final String TAG = "ConversationStatus"; + + /** @hide */ + @IntDef(prefix = { "ACTIVITY_" }, value = { + ACTIVITY_OTHER, + ACTIVITY_BIRTHDAY, + ACTIVITY_ANNIVERSARY, + ACTIVITY_NEW_STORY, + ACTIVITY_MEDIA, + ACTIVITY_GAME, + ACTIVITY_LOCATION, + ACTIVITY_UPCOMING_BIRTHDAY + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ActivityType {} + + public static final int ACTIVITY_OTHER = 0; + public static final int ACTIVITY_BIRTHDAY = 1; + public static final int ACTIVITY_ANNIVERSARY = 2; + public static final int ACTIVITY_NEW_STORY = 3; + public static final int ACTIVITY_MEDIA = 4; + public static final int ACTIVITY_GAME = 5; + public static final int ACTIVITY_LOCATION = 6; + public static final int ACTIVITY_UPCOMING_BIRTHDAY = 7; + + /** @hide */ + @IntDef(prefix = { "AVAILABILITY_" }, value = { + AVAILABILITY_UNKNOWN, + AVAILABILITY_AVAILABLE, + AVAILABILITY_BUSY, + AVAILABILITY_OFFLINE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Availability {} + + public static final int AVAILABILITY_UNKNOWN = -1; + public static final int AVAILABILITY_AVAILABLE = 0; + public static final int AVAILABILITY_BUSY = 1; + public static final int AVAILABILITY_OFFLINE = 2; + + private final String mId; + private final int mActivity; + + private int mAvailability; + private CharSequence mDescription; + private Icon mIcon; + private long mStartTimeMs; + private long mEndTimeMs; + + private ConversationStatus(Builder b) { + mId = b.mId; + mActivity = b.mActivity; + mAvailability = b.mAvailability; + mDescription = b.mDescription; + mIcon = b.mIcon; + mStartTimeMs = b.mStartTimeMs; + mEndTimeMs = b.mEndTimeMs; + } + + private ConversationStatus(Parcel p) { + mId = p.readString(); + mActivity = p.readInt(); + mAvailability = p.readInt(); + mDescription = p.readCharSequence(); + mIcon = p.readParcelable(Icon.class.getClassLoader()); + mStartTimeMs = p.readLong(); + mEndTimeMs = p.readLong(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mId); + dest.writeInt(mActivity); + dest.writeInt(mAvailability); + dest.writeCharSequence(mDescription); + dest.writeParcelable(mIcon, flags); + dest.writeLong(mStartTimeMs); + dest.writeLong(mEndTimeMs); + } + + public @NonNull String getId() { + return mId; + } + + public @ActivityType int getActivity() { + return mActivity; + } + + public @Availability + int getAvailability() { + return mAvailability; + } + + public @Nullable + CharSequence getDescription() { + return mDescription; + } + + public @Nullable Icon getIcon() { + return mIcon; + } + + public long getStartTimeMillis() { + return mStartTimeMs; + } + + public long getEndTimeMillis() { + return mEndTimeMs; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ConversationStatus that = (ConversationStatus) o; + return mActivity == that.mActivity && + mAvailability == that.mAvailability && + mStartTimeMs == that.mStartTimeMs && + mEndTimeMs == that.mEndTimeMs && + mId.equals(that.mId) && + Objects.equals(mDescription, that.mDescription) && + Objects.equals(mIcon, that.mIcon); + } + + @Override + public int hashCode() { + return Objects.hash(mId, mActivity, mAvailability, mDescription, mIcon, mStartTimeMs, + mEndTimeMs); + } + + @Override + public String toString() { + return "ConversationStatus{" + + "mId='" + mId + '\'' + + ", mActivity=" + mActivity + + ", mAvailability=" + mAvailability + + ", mDescription=" + mDescription + + ", mIcon=" + mIcon + + ", mStartTimeMs=" + mStartTimeMs + + ", mEndTimeMs=" + mEndTimeMs + + '}'; + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Creator<ConversationStatus> CREATOR + = new Creator<ConversationStatus>() { + public ConversationStatus createFromParcel(Parcel source) { + return new ConversationStatus(source); + } + + public ConversationStatus[] newArray(int size) { + return new ConversationStatus[size]; + } + }; + + public static final class Builder { + final String mId; + final int mActivity; + int mAvailability = AVAILABILITY_UNKNOWN; + CharSequence mDescription; + Icon mIcon; + long mStartTimeMs = -1; + long mEndTimeMs = -1; + + /** + * Creates a new builder. + * + * @param id The unique id for this status + * @param activity The type of status + */ + public Builder(@NonNull String id, @ActivityType @NonNull int activity) { + mId = id; + mActivity = activity; + } + + + public @NonNull Builder setAvailability(@Availability int availability) { + mAvailability = availability; + return this; + } + + public @NonNull Builder setDescription(@Nullable CharSequence description) { + mDescription = description; + return this; + } + + public @NonNull Builder setIcon(@Nullable Icon icon) { + mIcon = icon; + return this; + } + + public @NonNull Builder setStartTimeMillis(long startTimeMs) { + mStartTimeMs = startTimeMs; + return this; + } + + public @NonNull Builder setEndTimeMillis(long endTimeMs) { + mEndTimeMs = endTimeMs; + return this; + } + + public @NonNull ConversationStatus build() { + return new ConversationStatus(this); + } + } +} diff --git a/core/java/android/app/people/IPeopleManager.aidl b/core/java/android/app/people/IPeopleManager.aidl index c547ef1e7e9b..0d12ed02f610 100644 --- a/core/java/android/app/people/IPeopleManager.aidl +++ b/core/java/android/app/people/IPeopleManager.aidl @@ -16,6 +16,7 @@ package android.app.people; +import android.app.people.ConversationStatus; import android.content.pm.ParceledListSlice; import android.net.Uri; import android.os.IBinder; @@ -45,4 +46,9 @@ interface IPeopleManager { * conversation can't be found or no interactions have been recorded, returns 0L. */ long getLastInteraction(in String packageName, int userId, in String shortcutId); + + void addOrUpdateStatus(in String packageName, int userId, in String conversationId, in ConversationStatus status); + void clearStatus(in String packageName, int userId, in String conversationId, in String statusId); + void clearStatuses(in String packageName, int userId, in String conversationId); + ParceledListSlice getStatuses(in String packageName, int userId, in String conversationId); } diff --git a/core/java/android/app/people/PeopleManager.java b/core/java/android/app/people/PeopleManager.java new file mode 100644 index 000000000000..de7ba622ae88 --- /dev/null +++ b/core/java/android/app/people/PeopleManager.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.annotation.SystemService; +import android.content.Context; +import android.content.pm.ParceledListSlice; +import android.content.pm.ShortcutInfo; +import android.os.RemoteException; +import android.os.ServiceManager; + +import com.android.internal.util.Preconditions; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * This class allows interaction with conversation and people data. + */ +@SystemService(Context.PEOPLE_SERVICE) +public final class PeopleManager { + + private static final String LOG_TAG = PeopleManager.class.getSimpleName(); + + @NonNull + private final Context mContext; + + @NonNull + private final IPeopleManager mService; + + /** + * @hide + */ + public PeopleManager(@NonNull Context context) throws ServiceManager.ServiceNotFoundException { + mContext = context; + mService = IPeopleManager.Stub.asInterface(ServiceManager.getServiceOrThrow( + Context.PEOPLE_SERVICE)); + } + + + /** + * Sets or updates a {@link ConversationStatus} for a conversation. + * + * <p>Statuses are meant to represent current information about the conversation. Like + * notifications, they are transient and are not persisted beyond a reboot, nor are they + * backed up and restored.</p> + * <p>If the provided conversation shortcut is not already pinned, or cached by the system, + * it will remain cached as long as the status is active.</p> + * + * @param conversationId the {@link ShortcutInfo#getId() id} of the shortcut backing the + * conversation that has an active status + * @param status the current status for the given conversation + * + * @return whether the role is available in the system + */ + public void addOrUpdateStatus(@NonNull String conversationId, + @NonNull ConversationStatus status) { + Preconditions.checkStringNotEmpty(conversationId); + Objects.requireNonNull(status); + try { + mService.addOrUpdateStatus( + mContext.getPackageName(), mContext.getUserId(), conversationId, status); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Unpublishes a given status from the given conversation. + * + * @param conversationId the {@link ShortcutInfo#getId() id} of the shortcut backing the + * conversation that has an active status + * @param statusId the {@link ConversationStatus#getId() id} of a published status for the given + * conversation + */ + public void clearStatus(@NonNull String conversationId, @NonNull String statusId) { + Preconditions.checkStringNotEmpty(conversationId); + Preconditions.checkStringNotEmpty(statusId); + try { + mService.clearStatus( + mContext.getPackageName(), mContext.getUserId(), conversationId, statusId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Removes all published statuses for the given conversation. + * + * @param conversationId the {@link ShortcutInfo#getId() id} of the shortcut backing the + * conversation that has one or more active statuses + */ + public void clearStatuses(@NonNull String conversationId) { + Preconditions.checkStringNotEmpty(conversationId); + try { + mService.clearStatuses( + mContext.getPackageName(), mContext.getUserId(), conversationId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns all of the currently published statuses for a given conversation. + * + * @param conversationId the {@link ShortcutInfo#getId() id} of the shortcut backing the + * conversation that has one or more active statuses + */ + public @NonNull List<ConversationStatus> getStatuses(@NonNull String conversationId) { + try { + final ParceledListSlice<ConversationStatus> parceledList + = mService.getStatuses( + mContext.getPackageName(), mContext.getUserId(), conversationId); + if (parceledList != null) { + return parceledList.getList(); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return new ArrayList<>(); + } +} diff --git a/core/java/android/app/role/RoleControllerManager.java b/core/java/android/app/role/RoleControllerManager.java index ba1f61290631..8dde2c55d7d3 100644 --- a/core/java/android/app/role/RoleControllerManager.java +++ b/core/java/android/app/role/RoleControllerManager.java @@ -20,6 +20,8 @@ import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SystemService; +import android.annotation.TestApi; import android.app.ActivityThread; import android.content.ComponentName; import android.content.Context; @@ -46,6 +48,8 @@ import java.util.function.Consumer; * * @hide */ +@SystemService(Context.ROLE_CONTROLLER_SERVICE) +@TestApi public class RoleControllerManager { private static final String LOG_TAG = RoleControllerManager.class.getSimpleName(); @@ -195,11 +199,32 @@ public class RoleControllerManager { } /** + * @see RoleControllerService#onIsApplicationQualifiedForRole(String, String) + * + * @deprecated Use {@link #isApplicationVisibleForRole(String, String, Executor, Consumer)} + * instead. + * + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) + public void isApplicationQualifiedForRole(@NonNull String roleName, @NonNull String packageName, + @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { + AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> { + AndroidFuture<Bundle> future = new AndroidFuture<>(); + service.isApplicationQualifiedForRole(roleName, packageName, + new RemoteCallback(future::complete)); + return future; + }); + propagateCallback(operation, "isApplicationQualifiedForRole", executor, callback); + } + + /** * @see RoleControllerService#onIsApplicationVisibleForRole(String, String) * * @hide */ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) + @TestApi public void isApplicationVisibleForRole(@NonNull String roleName, @NonNull String packageName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> { @@ -217,6 +242,7 @@ public class RoleControllerManager { * @hide */ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) + @TestApi public void isRoleVisible(@NonNull String roleName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> { diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java index 9ddd5bef5c6f..8b2e07b09701 100644 --- a/core/java/android/app/role/RoleManager.java +++ b/core/java/android/app/role/RoleManager.java @@ -174,9 +174,6 @@ public final class RoleManager { @NonNull private final Object mListenersLock = new Object(); - @NonNull - private final RoleControllerManager mRoleControllerManager; - /** * @hide */ @@ -184,7 +181,6 @@ public final class RoleManager { mContext = context; mService = IRoleManager.Stub.asInterface(ServiceManager.getServiceOrThrow( Context.ROLE_SERVICE)); - mRoleControllerManager = new RoleControllerManager(context); } /** @@ -680,44 +676,6 @@ public final class RoleManager { } } - /** - * Check whether a role should be visible to user. - * - * @param roleName name of the role to check for - * @param executor the executor to execute callback on - * @param callback the callback to receive whether the role should be visible to user - * - * @hide - */ - @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) - @SystemApi - public void isRoleVisible(@NonNull String roleName, - @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { - mRoleControllerManager.isRoleVisible(roleName, executor, callback); - } - - /** - * Check whether an application is visible for a role. - * - * While an application can be qualified for a role, it can still stay hidden from user (thus - * not visible). If an application is visible for a role, we may show things related to the role - * for it, e.g. showing an entry pointing to the role settings in its application info page. - * - * @param roleName the name of the role to check for - * @param packageName the package name of the application to check for - * @param executor the executor to execute callback on - * @param callback the callback to receive whether the application is visible for the role - * - * @hide - */ - @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) - @SystemApi - public void isApplicationVisibleForRole(@NonNull String roleName, @NonNull String packageName, - @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { - mRoleControllerManager.isApplicationVisibleForRole(roleName, packageName, executor, - callback); - } - private static class OnRoleHoldersChangedListenerDelegate extends IOnRoleHoldersChangedListener.Stub { diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java index 7a6ff79623af..381318b26dad 100644 --- a/core/java/android/bluetooth/BluetoothGatt.java +++ b/core/java/android/bluetooth/BluetoothGatt.java @@ -824,6 +824,25 @@ public final class BluetoothGatt implements BluetoothProfile { * error */ private boolean registerApp(BluetoothGattCallback callback, Handler handler) { + return registerApp(callback, handler, false); + } + + /** + * Register an application callback to start using GATT. + * + * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered} + * is used to notify success or failure if the function returns true. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @param eatt_support indicate to allow for eatt support + * @return If true, the callback will be called to notify success or failure, false on immediate + * error + * @hide + */ + private boolean registerApp(BluetoothGattCallback callback, Handler handler, + boolean eatt_support) { if (DBG) Log.d(TAG, "registerApp()"); if (mService == null) return false; @@ -833,7 +852,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); try { - mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback); + mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback, eatt_support); } catch (RemoteException e) { Log.e(TAG, "", e); return false; diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java index 13b1b4f93cf0..088b0169b631 100644 --- a/core/java/android/bluetooth/BluetoothGattServer.java +++ b/core/java/android/bluetooth/BluetoothGattServer.java @@ -443,6 +443,25 @@ public final class BluetoothGattServer implements BluetoothProfile { * error */ /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) { + return registerCallback(callback, false); + } + + /** + * Register an application callback to start using GattServer. + * + * <p>This is an asynchronous call. The callback is used to notify + * success or failure if the function returns true. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @param eatt_support indicates if server can use eatt + * @return true, the callback will be called to notify success or failure, false on immediate + * error + * @hide + */ + /*package*/ boolean registerCallback(BluetoothGattServerCallback callback, + boolean eatt_support) { if (DBG) Log.d(TAG, "registerCallback()"); if (mService == null) { Log.e(TAG, "GATT service not available"); @@ -459,7 +478,7 @@ public final class BluetoothGattServer implements BluetoothProfile { mCallback = callback; try { - mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback); + mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback, eatt_support); } catch (RemoteException e) { Log.e(TAG, "", e); mCallback = null; diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java index 3b4fe0a30b80..d5c1c3e2d61e 100644 --- a/core/java/android/bluetooth/BluetoothManager.java +++ b/core/java/android/bluetooth/BluetoothManager.java @@ -225,6 +225,24 @@ public final class BluetoothManager { * * @param context App context * @param callback GATT server callback handler that will receive asynchronous callbacks. + * @param eatt_support idicates if server should use eatt channel for notifications. + * @return BluetoothGattServer instance + * @hide + */ + public BluetoothGattServer openGattServer(Context context, + BluetoothGattServerCallback callback, boolean eatt_support) { + return (openGattServer(context, callback, BluetoothDevice.TRANSPORT_AUTO, eatt_support)); + } + + /** + * Open a GATT Server + * The callback is used to deliver results to Caller, such as connection status as well + * as the results of any other GATT server operations. + * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer + * to conduct GATT server operations. + * + * @param context App context + * @param callback GATT server callback handler that will receive asynchronous callbacks. * @param transport preferred transport for GATT connections to remote dual-mode devices {@link * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link * BluetoothDevice#TRANSPORT_LE} @@ -233,6 +251,27 @@ public final class BluetoothManager { */ public BluetoothGattServer openGattServer(Context context, BluetoothGattServerCallback callback, int transport) { + return (openGattServer(context, callback, transport, false)); + } + + /** + * Open a GATT Server + * The callback is used to deliver results to Caller, such as connection status as well + * as the results of any other GATT server operations. + * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer + * to conduct GATT server operations. + * + * @param context App context + * @param callback GATT server callback handler that will receive asynchronous callbacks. + * @param transport preferred transport for GATT connections to remote dual-mode devices {@link + * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link + * BluetoothDevice#TRANSPORT_LE} + * @param eatt_support idicates if server should use eatt channel for notifications. + * @return BluetoothGattServer instance + * @hide + */ + public BluetoothGattServer openGattServer(Context context, + BluetoothGattServerCallback callback, int transport, boolean eatt_support) { if (context == null || callback == null) { throw new IllegalArgumentException("null parameter: " + context + " " + callback); } @@ -248,7 +287,7 @@ public final class BluetoothManager { return null; } BluetoothGattServer mGattServer = new BluetoothGattServer(iGatt, transport); - Boolean regStatus = mGattServer.registerCallback(callback); + Boolean regStatus = mGattServer.registerCallback(callback, eatt_support); return regStatus ? mGattServer : null; } catch (RemoteException e) { Log.e(TAG, "", e); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 33f960732be9..5ccceca9a69d 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -40,6 +40,7 @@ import android.app.ActivityManager; import android.app.IApplicationThread; import android.app.IServiceConnection; import android.app.VrManager; +import android.app.people.PeopleManager; import android.app.time.TimeManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ApplicationInfo; @@ -4508,6 +4509,17 @@ public abstract class Context { public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture"; /** + * Official published name of the translation service. + * + * @hide + * @see #getSystemService(String) + */ + // TODO(b/176208267): change it back to translation before S release. + @SystemApi + @SuppressLint("ServiceName") + public static final String TRANSLATION_MANAGER_SERVICE = "transformer"; + + /** * Used for getting content selections and classifications for task snapshots. * * @hide @@ -4821,6 +4833,16 @@ public abstract class Context { public static final String ROLE_SERVICE = "role"; /** + * Official published name of the (internal) role controller service. + * + * @see #getSystemService(String) + * @see android.app.role.RoleControllerService + * + * @hide + */ + public static final String ROLE_CONTROLLER_SERVICE = "role_controller"; + + /** * Use with {@link #getSystemService(String)} to retrieve a * {@link android.hardware.camera2.CameraManager} for interacting with * camera devices. @@ -5301,10 +5323,10 @@ public abstract class Context { public static final String SMS_SERVICE = "sms"; /** - * Use with {@link #getSystemService(String)} to access people service. + * Use with {@link #getSystemService(String)} to access a {@link PeopleManager} to interact + * with your published conversations. * * @see #getSystemService(String) - * @hide */ public static final String PEOPLE_SERVICE = "people"; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index e074eab99a7c..17c4d25d82d7 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3406,6 +3406,14 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: + * The device supports translation of text-to-text in multiple languages via integration with + * the system {@link android.service.translation.TranslationService translation provider}. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_TRANSLATION = "android.software.translation"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: * The device implements headtracking suitable for a VR device. */ @SdkConstant(SdkConstantType.FEATURE) diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java index b3b2dd85a8d8..8497525f85b3 100644 --- a/core/java/android/hardware/SensorPrivacyManager.java +++ b/core/java/android/hardware/SensorPrivacyManager.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemService; -import android.annotation.UserIdInt; import android.content.Context; import android.os.IBinder; import android.os.RemoteException; @@ -164,12 +163,11 @@ public final class SensorPrivacyManager { * Registers a new listener to receive notification when the state of sensor privacy * changes. * - * @param userId the user's id * @param sensor the sensor to listen to changes to * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor * privacy changes. */ - public void addSensorPrivacyListener(@UserIdInt int userId, @IndividualSensor int sensor, + public void addSensorPrivacyListener(@IndividualSensor int sensor, final OnSensorPrivacyChangedListener listener) { synchronized (mListeners) { ISensorPrivacyListener iListener = mListeners.get(listener); @@ -184,7 +182,8 @@ public final class SensorPrivacyManager { } try { - mService.addIndividualSensorPrivacyListener(userId, sensor, iListener); + mService.addIndividualSensorPrivacyListener(mContext.getUserId(), sensor, + iListener); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -230,10 +229,9 @@ public final class SensorPrivacyManager { * * @return true if sensor privacy is currently enabled, false otherwise. */ - public boolean isIndividualSensorPrivacyEnabled(@UserIdInt int userId, - @IndividualSensor int sensor) { + public boolean isIndividualSensorPrivacyEnabled(@IndividualSensor int sensor) { try { - return mService.isIndividualSensorPrivacyEnabled(userId, sensor); + return mService.isIndividualSensorPrivacyEnabled(mContext.getUserId(), sensor); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -242,13 +240,14 @@ public final class SensorPrivacyManager { /** * Sets sensor privacy to the specified state for an individual sensor. * + * @param sensor the sensor which to change the state for * @param enable the state to which sensor privacy should be set. */ @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) - public void setIndividualSensorPrivacy(@UserIdInt int userId, @IndividualSensor int sensor, + public void setIndividualSensorPrivacy(@IndividualSensor int sensor, boolean enable) { try { - mService.setIndividualSensorPrivacy(userId, sensor, enable); + mService.setIndividualSensorPrivacy(mContext.getUserId(), sensor, enable); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -256,15 +255,17 @@ public final class SensorPrivacyManager { /** * Sets sensor privacy to the specified state for an individual sensor for the profile group of - * the given user. + * context's user. * + * @param sensor the sensor which to change the state for * @param enable the state to which sensor privacy should be set. */ @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) - public void setIndividualSensorPrivacyForProfileGroup(@UserIdInt int userId, - @IndividualSensor int sensor, boolean enable) { + public void setIndividualSensorPrivacyForProfileGroup(@IndividualSensor int sensor, + boolean enable) { try { - mService.setIndividualSensorPrivacyForProfileGroup(userId, sensor, enable); + mService.setIndividualSensorPrivacyForProfileGroup(mContext.getUserId(), sensor, + enable); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/face/OWNERS b/core/java/android/hardware/face/OWNERS index 33527f824827..be10df1099ed 100644 --- a/core/java/android/hardware/face/OWNERS +++ b/core/java/android/hardware/face/OWNERS @@ -1,3 +1,7 @@ # Bug component: 879035 +curtislb@google.com +ilyamaty@google.com jaggies@google.com +joshmccloskey@google.com +kchyn@google.com diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 36348b365158..8bfbad605420 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -442,6 +442,24 @@ public class NetworkPolicyManager { } /** + * Check that networking is blocked for the given uid. + * + * @param uid The target uid. + * @param meteredNetwork True if the network is metered. + * @return true if networking is blocked for the given uid according to current networking + * policies. + * + * @hide + */ + public boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork) { + try { + return mService.isUidNetworkingBlocked(uid, meteredNetwork); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Get multipath preference for the given network. */ public int getMultipathPreference(Network network) { diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index 6209718e8788..66b99b9ba319 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -40,6 +40,18 @@ import java.util.Set; */ public class NetworkRequest implements Parcelable { /** + * The first requestId value that will be allocated. + * @hide only used by ConnectivityService. + */ + public static final int FIRST_REQUEST_ID = 1; + + /** + * The requestId value that represents the absence of a request. + * @hide only used by ConnectivityService. + */ + public static final int REQUEST_ID_NONE = -1; + + /** * The {@link NetworkCapabilities} that define this request. * @hide */ diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java index bf8ac6e55d8a..ba29a15a91bb 100644 --- a/core/java/android/os/BatteryConsumer.java +++ b/core/java/android/os/BatteryConsumer.java @@ -39,6 +39,11 @@ public abstract class BatteryConsumer { POWER_COMPONENT_USAGE, POWER_COMPONENT_CPU, POWER_COMPONENT_BLUETOOTH, + POWER_COMPONENT_CAMERA, + POWER_COMPONENT_AUDIO, + POWER_COMPONENT_VIDEO, + POWER_COMPONENT_FLASHLIGHT, + POWER_COMPONENT_SYSTEM_SERVICES, }) @Retention(RetentionPolicy.SOURCE) public static @interface PowerComponent { @@ -47,8 +52,13 @@ public abstract class BatteryConsumer { public static final int POWER_COMPONENT_USAGE = 0; public static final int POWER_COMPONENT_CPU = 1; public static final int POWER_COMPONENT_BLUETOOTH = 2; + public static final int POWER_COMPONENT_CAMERA = 3; + public static final int POWER_COMPONENT_AUDIO = 4; + public static final int POWER_COMPONENT_VIDEO = 5; + public static final int POWER_COMPONENT_FLASHLIGHT = 6; + public static final int POWER_COMPONENT_SYSTEM_SERVICES = 7; - public static final int POWER_COMPONENT_COUNT = 3; + public static final int POWER_COMPONENT_COUNT = 8; public static final int FIRST_CUSTOM_POWER_COMPONENT_ID = 1000; public static final int LAST_CUSTOM_POWER_COMPONENT_ID = 9999; @@ -75,6 +85,8 @@ public abstract class BatteryConsumer { TIME_COMPONENT_CPU, TIME_COMPONENT_CPU_FOREGROUND, TIME_COMPONENT_BLUETOOTH, + TIME_COMPONENT_CAMERA, + TIME_COMPONENT_FLASHLIGHT, }) @Retention(RetentionPolicy.SOURCE) public static @interface TimeComponent { @@ -84,8 +96,12 @@ public abstract class BatteryConsumer { public static final int TIME_COMPONENT_CPU = 1; public static final int TIME_COMPONENT_CPU_FOREGROUND = 2; public static final int TIME_COMPONENT_BLUETOOTH = 3; + public static final int TIME_COMPONENT_CAMERA = 4; + public static final int TIME_COMPONENT_AUDIO = 5; + public static final int TIME_COMPONENT_VIDEO = 6; + public static final int TIME_COMPONENT_FLASHLIGHT = 7; - public static final int TIME_COMPONENT_COUNT = 4; + public static final int TIME_COMPONENT_COUNT = 8; public static final int FIRST_CUSTOM_TIME_COMPONENT_ID = 1000; public static final int LAST_CUSTOM_TIME_COMPONENT_ID = 9999; diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index b57418d751bc..c0b2ada7860c 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -1071,6 +1071,7 @@ public abstract class VibrationEffect implements Parcelable { PRIMITIVE_SLOW_RISE, PRIMITIVE_QUICK_FALL, PRIMITIVE_TICK, + PRIMITIVE_LOW_TICK, }) @Retention(RetentionPolicy.SOURCE) public @interface Primitive {} @@ -1116,6 +1117,12 @@ public abstract class VibrationEffect implements Parcelable { */ // Internally this maps to the HAL constant CompositePrimitive::LIGHT_TICK public static final int PRIMITIVE_TICK = 7; + /** + * This very short low frequency effect should produce a light crisp sensation + * intended to be used repetitively for dynamic feedback. + */ + // Internally this maps to the HAL constant CompositePrimitive::LOW_TICK + public static final int PRIMITIVE_LOW_TICK = 8; private ArrayList<PrimitiveEffect> mEffects = new ArrayList<>(); @@ -1194,7 +1201,7 @@ public abstract class VibrationEffect implements Parcelable { * */ static int checkPrimitive(int primitiveId) { - Preconditions.checkArgumentInRange(primitiveId, PRIMITIVE_NOOP, PRIMITIVE_TICK, + Preconditions.checkArgumentInRange(primitiveId, PRIMITIVE_NOOP, PRIMITIVE_LOW_TICK, "primitiveId"); return primitiveId; } @@ -1223,6 +1230,8 @@ public abstract class VibrationEffect implements Parcelable { return "PRIMITIVE_QUICK_FALL"; case PRIMITIVE_TICK: return "PRIMITIVE_TICK"; + case PRIMITIVE_LOW_TICK: + return "PRIMITIVE_LOW_TICK"; default: return Integer.toString(id); } diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java index 8ac1fc1f23c7..b5abe2a3e311 100644 --- a/core/java/android/os/storage/StorageVolume.java +++ b/core/java/android/os/storage/StorageVolume.java @@ -163,7 +163,7 @@ public final class StorageVolume implements Parcelable { mMaxFileSize = in.readLong(); mOwner = in.readParcelable(null); if (in.readInt() != 0) { - mUuid = StorageManager.convert(in.readString()); + mUuid = StorageManager.convert(in.readString8()); } else { mUuid = null; } diff --git a/core/java/android/permission/PermGroupUsage.java b/core/java/android/permission/PermGroupUsage.java index 3bee401dbd0d..7335e002e3b7 100644 --- a/core/java/android/permission/PermGroupUsage.java +++ b/core/java/android/permission/PermGroupUsage.java @@ -30,17 +30,19 @@ public final class PermGroupUsage { private final String mPackageName; private final int mUid; + private final long mLastAccess; private final String mPermGroupName; private final boolean mIsActive; private final boolean mIsPhoneCall; private final CharSequence mAttribution; PermGroupUsage(@NonNull String packageName, int uid, - @NonNull String permGroupName, boolean isActive, boolean isPhoneCall, + @NonNull String permGroupName, long lastAccess, boolean isActive, boolean isPhoneCall, @Nullable CharSequence attribution) { this.mPackageName = packageName; this.mUid = uid; this.mPermGroupName = permGroupName; + this.mLastAccess = lastAccess; this.mIsActive = isActive; this.mIsPhoneCall = isPhoneCall; this.mAttribution = attribution; @@ -58,6 +60,10 @@ public final class PermGroupUsage { return mPermGroupName; } + public long getLastAccess() { + return mLastAccess; + } + public boolean isActive() { return mIsActive; } diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java index 4868827d8700..00ba86732e55 100644 --- a/core/java/android/permission/PermissionUsageHelper.java +++ b/core/java/android/permission/PermissionUsageHelper.java @@ -187,7 +187,7 @@ public class PermissionUsageHelper { for (int usageNum = 0; usageNum < numUsages; usageNum++) { OpUsage usage = rawUsages.get(permGroup).get(usageNum); usages.add(new PermGroupUsage(usage.packageName, usage.uid, permGroup, - usage.isRunning, isPhone, + usage.lastAccessTime, usage.isRunning, isPhone, packagesWithAttributionLabels.get(usage.toPackageAttr()))); } } diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index ec4d81c10b5b..7a856e8a02b7 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -101,6 +101,13 @@ public final class DeviceConfig { public static final String NAMESPACE_APP_COMPAT = "app_compat"; /** + * Namespace for all app hibernation related features. + * @hide + */ + @SystemApi + public static final String NAMESPACE_APP_HIBERNATION = "app_hibernation"; + + /** * Namespace for app standby configurations. * * @hide diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 69ca28a38fb6..9db7ca0cad6b 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -14630,6 +14630,34 @@ public final class Settings { public static final String BACKPORT_S_NOTIF_RULES = "backport_s_notif_rules"; /** + * The decoration to put on fully custom views that target S. + * + * <p>Values are: + * <br>0: DECORATION_NONE: no decorations. + * <br>1: DECORATION_MINIMAL: most minimal template; just the icon and the expander. + * <br>2: DECORATION_PARTIAL: basic template without the top line. + * <br>3: DECORATION_FULL_COMPATIBLE: basic template with the top line; 40dp of height. + * <br>4: DECORATION_FULL_CONSTRAINED: basic template with the top line; 28dp of height. + * <p>See {@link android.app.Notification.DevFlags} for more details. + * @hide + */ + public static final String FULLY_CUSTOM_VIEW_NOTIF_DECORATION = + "fully_custom_view_notif_decoration"; + + /** + * The decoration to put on decorated custom views that target S. + * + * <p>Values are: + * <br>2: DECORATION_PARTIAL: basic template without the top line. + * <br>3: DECORATION_FULL_COMPATIBLE: basic template with the top line; 40dp of height. + * <br>4: DECORATION_FULL_CONSTRAINED: basic template with the top line; 28dp of height. + * <p>See {@link android.app.Notification.DevFlags} for more details. + * @hide + */ + public static final String DECORATED_CUSTOM_VIEW_NOTIF_DECORATION = + "decorated_custom_view_notif_decoration"; + + /** * Block untrusted touches mode. * * Can be one of: diff --git a/core/java/android/service/translation/ITranslationCallback.aidl b/core/java/android/service/translation/ITranslationCallback.aidl new file mode 100644 index 000000000000..333cb577f790 --- /dev/null +++ b/core/java/android/service/translation/ITranslationCallback.aidl @@ -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 android.service.translation; + +import android.view.translation.TranslationResponse; + +/** + * Interface to receive the result of a {@code TranslationRequest}. + * + * @hide + */ +oneway interface ITranslationCallback { + void onTranslationComplete(in TranslationResponse translationResponse); + void onError(); +} diff --git a/core/java/android/service/translation/ITranslationService.aidl b/core/java/android/service/translation/ITranslationService.aidl new file mode 100644 index 000000000000..6d6f2782ef4b --- /dev/null +++ b/core/java/android/service/translation/ITranslationService.aidl @@ -0,0 +1,38 @@ +/* + * 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.service.translation; + +import android.service.translation.TranslationRequest; +import android.service.translation.ITranslationCallback; +import android.view.translation.TranslationSpec; +import com.android.internal.os.IResultReceiver; + +/** + * System-wide on-device translation service. + * + * <p>Services requests to translate text between different languages. The primary use case for this + * service is automatic translation of text and web views, when the auto Translate feature is + * enabled. + * + * @hide + */ +oneway interface ITranslationService { + void onConnected(); + void onDisconnected(); + void onCreateTranslationSession(in TranslationSpec sourceSpec, in TranslationSpec destSpec, + int sessionId, in IResultReceiver receiver); +} diff --git a/core/java/android/service/translation/OWNERS b/core/java/android/service/translation/OWNERS new file mode 100644 index 000000000000..a1e663aa8ff7 --- /dev/null +++ b/core/java/android/service/translation/OWNERS @@ -0,0 +1,8 @@ +# Bug component: 994311 + +adamhe@google.com +augale@google.com +joannechung@google.com +lpeter@google.com +svetoslavganov@google.com +tymtsai@google.com diff --git a/core/java/android/service/translation/OnTranslationResultCallbackWrapper.java b/core/java/android/service/translation/OnTranslationResultCallbackWrapper.java new file mode 100644 index 000000000000..345c69c0935d --- /dev/null +++ b/core/java/android/service/translation/OnTranslationResultCallbackWrapper.java @@ -0,0 +1,92 @@ +/* + * 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.service.translation; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.DeadObjectException; +import android.os.RemoteException; +import android.util.Log; +import android.view.translation.TranslationResponse; + +import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Callback to receive the {@link TranslationResponse} on successful translation. + * + * @hide + */ +final class OnTranslationResultCallbackWrapper implements + TranslationService.OnTranslationResultCallback { + + private static final String TAG = "OnTranslationResultCallback"; + + private final @NonNull ITranslationCallback mCallback; + + private AtomicBoolean mCalled; + + /** + * @hide + */ + public OnTranslationResultCallbackWrapper(@NonNull ITranslationCallback callback) { + mCallback = Objects.requireNonNull(callback); + mCalled = new AtomicBoolean(); + } + + @Override + public void onTranslationSuccess(@Nullable TranslationResponse response) { + assertNotCalled(); + if (mCalled.getAndSet(true)) { + throw new IllegalStateException("Already called"); + } + + try { + mCallback.onTranslationComplete(response); + } catch (RemoteException e) { + if (e instanceof DeadObjectException) { + Log.w(TAG, "Process is dead, ignore."); + return; + } + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public void onError() { + assertNotCalled(); + if (mCalled.getAndSet(true)) { + throw new IllegalStateException("Already called"); + } + + try { + mCallback.onError(); + } catch (RemoteException e) { + if (e instanceof DeadObjectException) { + Log.w(TAG, "Process is dead, ignore."); + return; + } + throw e.rethrowAsRuntimeException(); + } + } + + private void assertNotCalled() { + if (mCalled.get()) { + throw new IllegalStateException("Already called"); + } + } +} diff --git a/core/java/android/service/translation/TranslationRequest.aidl b/core/java/android/service/translation/TranslationRequest.aidl new file mode 100644 index 000000000000..9a2d4157696e --- /dev/null +++ b/core/java/android/service/translation/TranslationRequest.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.service.translation; + +parcelable TranslationRequest; diff --git a/core/java/android/service/translation/TranslationRequest.java b/core/java/android/service/translation/TranslationRequest.java new file mode 100644 index 000000000000..b8afd7049a82 --- /dev/null +++ b/core/java/android/service/translation/TranslationRequest.java @@ -0,0 +1,281 @@ +/* + * 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.service.translation; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.translation.TranslationSpec; + +import com.android.internal.util.DataClass; + +import java.util.ArrayList; +import java.util.List; + +/** + * Internal translation request sent to the {@link android.service.translation.TranslationService} + * which contains the text to be translated. + * + * @hide + */ +@SystemApi +@DataClass(genConstructor = true, genBuilder = true, genToString = true) +public final class TranslationRequest implements Parcelable { + + private final int mRequestId; + @NonNull + private final TranslationSpec mSourceSpec; + @NonNull + private final TranslationSpec mDestSpec; + @NonNull + private final List<android.view.translation.TranslationRequest> mTranslationRequests; + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/translation/TranslationRequest.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + public TranslationRequest( + int requestId, + @NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec destSpec, + @NonNull List<android.view.translation.TranslationRequest> translationRequests) { + this.mRequestId = requestId; + this.mSourceSpec = sourceSpec; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mSourceSpec); + this.mDestSpec = destSpec; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mDestSpec); + this.mTranslationRequests = translationRequests; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTranslationRequests); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public int getRequestId() { + return mRequestId; + } + + @DataClass.Generated.Member + public @NonNull TranslationSpec getSourceSpec() { + return mSourceSpec; + } + + @DataClass.Generated.Member + public @NonNull TranslationSpec getDestSpec() { + return mDestSpec; + } + + @DataClass.Generated.Member + public @NonNull List<android.view.translation.TranslationRequest> getTranslationRequests() { + return mTranslationRequests; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "TranslationRequest { " + + "requestId = " + mRequestId + ", " + + "sourceSpec = " + mSourceSpec + ", " + + "destSpec = " + mDestSpec + ", " + + "translationRequests = " + mTranslationRequests + + " }"; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + dest.writeInt(mRequestId); + dest.writeTypedObject(mSourceSpec, flags); + dest.writeTypedObject(mDestSpec, flags); + dest.writeParcelableList(mTranslationRequests, flags); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ TranslationRequest(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + int requestId = in.readInt(); + TranslationSpec sourceSpec = (TranslationSpec) in.readTypedObject(TranslationSpec.CREATOR); + TranslationSpec destSpec = (TranslationSpec) in.readTypedObject(TranslationSpec.CREATOR); + List<android.view.translation.TranslationRequest> translationRequests = new ArrayList<>(); + in.readParcelableList(translationRequests, android.view.translation.TranslationRequest.class.getClassLoader()); + + this.mRequestId = requestId; + this.mSourceSpec = sourceSpec; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mSourceSpec); + this.mDestSpec = destSpec; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mDestSpec); + this.mTranslationRequests = translationRequests; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTranslationRequests); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<TranslationRequest> CREATOR + = new Parcelable.Creator<TranslationRequest>() { + @Override + public TranslationRequest[] newArray(int size) { + return new TranslationRequest[size]; + } + + @Override + public TranslationRequest createFromParcel(@NonNull Parcel in) { + return new TranslationRequest(in); + } + }; + + /** + * A builder for {@link TranslationRequest} + */ + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static final class Builder { + + private int mRequestId; + private @NonNull TranslationSpec mSourceSpec; + private @NonNull TranslationSpec mDestSpec; + private @NonNull List<android.view.translation.TranslationRequest> mTranslationRequests; + + private long mBuilderFieldsSet = 0L; + + public Builder( + int requestId, + @NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec destSpec, + @NonNull List<android.view.translation.TranslationRequest> translationRequests) { + mRequestId = requestId; + mSourceSpec = sourceSpec; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mSourceSpec); + mDestSpec = destSpec; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mDestSpec); + mTranslationRequests = translationRequests; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTranslationRequests); + } + + @DataClass.Generated.Member + public @NonNull Builder setRequestId(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1; + mRequestId = value; + return this; + } + + @DataClass.Generated.Member + public @NonNull Builder setSourceSpec(@NonNull TranslationSpec value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mSourceSpec = value; + return this; + } + + @DataClass.Generated.Member + public @NonNull Builder setDestSpec(@NonNull TranslationSpec value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; + mDestSpec = value; + return this; + } + + @DataClass.Generated.Member + public @NonNull Builder setTranslationRequests(@NonNull List<android.view.translation.TranslationRequest> value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x8; + mTranslationRequests = value; + return this; + } + + /** @see #setTranslationRequests */ + @DataClass.Generated.Member + public @NonNull Builder addTranslationRequests(@NonNull android.view.translation.TranslationRequest value) { + // You can refine this method's name by providing item's singular name, e.g.: + // @DataClass.PluralOf("item")) mItems = ... + + if (mTranslationRequests == null) setTranslationRequests(new ArrayList<>()); + mTranslationRequests.add(value); + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull TranslationRequest build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x10; // Mark builder used + + TranslationRequest o = new TranslationRequest( + mRequestId, + mSourceSpec, + mDestSpec, + mTranslationRequests); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x10) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } + + @DataClass.Generated( + time = 1609966181888L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/service/translation/TranslationRequest.java", + inputSignatures = "private final int mRequestId\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mSourceSpec\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mDestSpec\nprivate final @android.annotation.NonNull java.util.List<android.view.translation.TranslationRequest> mTranslationRequests\nclass TranslationRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=true, genBuilder=true, genToString=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/service/translation/TranslationService.java b/core/java/android/service/translation/TranslationService.java new file mode 100644 index 000000000000..b0288076376c --- /dev/null +++ b/core/java/android/service/translation/TranslationService.java @@ -0,0 +1,261 @@ +/* + * 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.service.translation; + +import static android.view.translation.Translator.EXTRA_SERVICE_BINDER; +import static android.view.translation.Translator.EXTRA_SESSION_ID; + +import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; + +import android.annotation.CallSuper; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.app.Service; +import android.content.Intent; +import android.os.BaseBundle; +import android.os.Bundle; +import android.os.CancellationSignal; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.util.Log; +import android.view.translation.ITranslationDirectManager; +import android.view.translation.TranslationManager; +import android.view.translation.TranslationResponse; +import android.view.translation.TranslationSpec; + +import com.android.internal.os.IResultReceiver; +import com.android.internal.util.SyncResultReceiver; + +/** + * Service for translating text. + * @hide + */ +@SystemApi +public abstract class TranslationService extends Service { + private static final String TAG = "TranslationService"; + + /** + * The {@link Intent} that must be declared as handled by the service. + * + * <p>To be supported, the service must also require the + * {@link android.Manifest.permission#BIND_TRANSLATION_SERVICE} permission so + * that other applications can not abuse it. + */ + public static final String SERVICE_INTERFACE = + "android.service.translation.TranslationService"; + + /** + * Name under which a TranslationService component publishes information about itself. + * + * <p>This meta-data should reference an XML resource containing a + * <code><{@link + * android.R.styleable#TranslationService translation-service}></code> tag. + * + * <p>Here's an example of how to use it on {@code AndroidManifest.xml}: + * TODO: fill in doc example (check CCService/AFService). + */ + public static final String SERVICE_META_DATA = "android.translation_service"; + + private Handler mHandler; + + /** + * Binder to receive calls from system server. + */ + private final ITranslationService mInterface = new ITranslationService.Stub() { + @Override + public void onConnected() { + mHandler.sendMessage(obtainMessage(TranslationService::onConnected, + TranslationService.this)); + } + + @Override + public void onDisconnected() { + mHandler.sendMessage(obtainMessage(TranslationService::onDisconnected, + TranslationService.this)); + } + + @Override + public void onCreateTranslationSession(TranslationSpec sourceSpec, TranslationSpec destSpec, + int sessionId, IResultReceiver receiver) throws RemoteException { + mHandler.sendMessage(obtainMessage(TranslationService::handleOnCreateTranslationSession, + TranslationService.this, sourceSpec, destSpec, sessionId, receiver)); + } + }; + + /** + * Interface definition for a callback to be invoked when the translation is compleled. + */ + public interface OnTranslationResultCallback { + /** + * Notifies the Android System that a translation request + * {@link TranslationService#onTranslationRequest(TranslationRequest, int, + * CancellationSignal, OnTranslationResultCallback)} was successfully fulfilled by the + * service. + * + * <p>This method should always be called, even if the service cannot fulfill the request + * (in which case it should be called with a TranslationResponse with + * {@link android.view.translation.TranslationResponse#TRANSLATION_STATUS_UNKNOWN_ERROR}, + * or {@link android.view.translation.TranslationResponse + * #TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE}). + * + * @param response translation response for the provided request infos. + * + * @throws IllegalStateException if this method was already called. + */ + void onTranslationSuccess(@NonNull TranslationResponse response); + + /** + * TODO: implement javadoc + */ + void onError(); + } + + /** + * Binder that receives calls from the app. + */ + private final ITranslationDirectManager mClientInterface = + new ITranslationDirectManager.Stub() { + // TODO: Implement cancellation signal + @NonNull + private final CancellationSignal mCancellationSignal = new CancellationSignal(); + + @Override + public void onTranslationRequest(TranslationRequest request, int sessionId, + ITranslationCallback callback, IResultReceiver receiver) + throws RemoteException { + // TODO(b/176464808): Currently, the API is used for both sync and async case. + // It may work now, but maybe two methods is more cleaner. To think how to + // define the APIs for these two cases. + final ITranslationCallback cb = callback != null + ? callback + : new ITranslationCallback.Stub() { + @Override + public void onTranslationComplete( + TranslationResponse translationResponse) + throws RemoteException { + receiver.send(0, + SyncResultReceiver.bundleFor(translationResponse)); + } + + @Override + public void onError() throws RemoteException { + //TODO: implement default error callback + } + }; + // TODO(b/176464808): make it a private member of client + final OnTranslationResultCallback translationResultCallback = + new OnTranslationResultCallbackWrapper(cb); + mHandler.sendMessage(obtainMessage(TranslationService::onTranslationRequest, + TranslationService.this, request, sessionId, mCancellationSignal, + translationResultCallback)); + } + + @Override + public void onFinishTranslationSession(int sessionId) throws RemoteException { + mHandler.sendMessage(obtainMessage( + TranslationService::onFinishTranslationSession, + TranslationService.this, sessionId)); + } + }; + + @CallSuper + @Override + public void onCreate() { + super.onCreate(); + mHandler = new Handler(Looper.getMainLooper(), null, true); + BaseBundle.setShouldDefuse(true); + } + + @Override + @Nullable + public final IBinder onBind(@NonNull Intent intent) { + if (SERVICE_INTERFACE.equals(intent.getAction())) { + return mInterface.asBinder(); + } + Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent); + return null; + } + + /** + * Called when the Android system connects to service. + * + * <p>You should generally do initialization here rather than in {@link #onCreate}. + */ + public void onConnected() { + } + + /** + * Called when the Android system disconnects from the service. + * + * <p> At this point this service may no longer be an active {@link TranslationService}. + * It should not make calls on {@link TranslationManager} that requires the caller to be + * the current service. + */ + public void onDisconnected() { + } + + /** + * TODO: fill in javadoc. + * + * @param sourceSpec + * @param destSpec + * @param sessionId + */ + // TODO(b/176464808): the session id won't be unique cross client/server process. Need to find + // solution to make it's safe. + public abstract void onCreateTranslationSession(@NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec destSpec, int sessionId); + + /** + * TODO: fill in javadoc. + * + * @param sessionId + */ + public abstract void onFinishTranslationSession(int sessionId); + + /** + * TODO: fill in javadoc. + * + * @param request + * @param sessionId + * @param callback + * @param cancellationSignal + */ + public abstract void onTranslationRequest(@NonNull TranslationRequest request, int sessionId, + @NonNull CancellationSignal cancellationSignal, + @NonNull OnTranslationResultCallback callback); + + // TODO(b/176464808): Need to handle client dying case + + // TODO(b/176464808): Need to handle the failure case. e.g. if the specs does not support + + private void handleOnCreateTranslationSession(@NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec destSpec, int sessionId, IResultReceiver resultReceiver) { + try { + final Bundle extras = new Bundle(); + extras.putBinder(EXTRA_SERVICE_BINDER, mClientInterface.asBinder()); + extras.putInt(EXTRA_SESSION_ID, sessionId); + resultReceiver.send(0, extras); + } catch (RemoteException e) { + Log.w(TAG, "RemoteException sending client interface: " + e); + } + onCreateTranslationSession(sourceSpec, destSpec, sessionId); + } +} diff --git a/core/java/android/service/translation/TranslationServiceInfo.java b/core/java/android/service/translation/TranslationServiceInfo.java new file mode 100644 index 000000000000..18cc29d12b5f --- /dev/null +++ b/core/java/android/service/translation/TranslationServiceInfo.java @@ -0,0 +1,173 @@ +/* + * 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.service.translation; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.AppGlobals; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ServiceInfo; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.os.RemoteException; +import android.util.AttributeSet; +import android.util.Log; +import android.util.Slog; +import android.util.Xml; + +import com.android.internal.R; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.PrintWriter; + +/** + * {@link ServiceInfo} and meta-data about an {@link TranslationService}. + * + * @hide + */ +public final class TranslationServiceInfo { + + private static final String TAG = "TranslationServiceInfo"; + private static final String XML_TAG_SERVICE = "translation-service"; + + @NonNull + private final ServiceInfo mServiceInfo; + + @Nullable + private final String mSettingsActivity; + + private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, boolean isTemp, + @UserIdInt int userId) throws PackageManager.NameNotFoundException { + int flags = PackageManager.GET_META_DATA; + if (!isTemp) { + flags |= PackageManager.MATCH_SYSTEM_ONLY; + } + + ServiceInfo si = null; + try { + si = AppGlobals.getPackageManager().getServiceInfo(comp, flags, userId); + } catch (RemoteException e) { + } + if (si == null) { + throw new NameNotFoundException("Could not get serviceInfo for " + + (isTemp ? " (temp)" : "(default system)") + + " " + comp.flattenToShortString()); + } + return si; + } + + @NonNull + public ServiceInfo getServiceInfo() { + return mServiceInfo; + } + + @Nullable + public String getSettingsActivity() { + return mSettingsActivity; + } + + public TranslationServiceInfo(@NonNull Context context, @NonNull ComponentName comp, + boolean isTemporaryService, @UserIdInt int userId) + throws PackageManager.NameNotFoundException { + this(context, getServiceInfoOrThrow(comp, isTemporaryService, userId)); + } + + private TranslationServiceInfo(@NonNull Context context, @NonNull ServiceInfo si) { + // Check for permission. + if (!Manifest.permission.BIND_TRANSLATION_SERVICE.equals(si.permission)) { + Slog.w(TAG, "TranslationServiceInfo from '" + si.packageName + + "' does not require permission " + + Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE); + throw new SecurityException("Service does not require permission " + + Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE); + } + + mServiceInfo = si; + + // Get the metadata, if declared. + // TODO: Try to find more easier way to do this. + final XmlResourceParser parser = si.loadXmlMetaData(context.getPackageManager(), + TranslationService.SERVICE_META_DATA); + if (parser == null) { + mSettingsActivity = null; + return; + } + + String settingsActivity = null; + + try { + final Resources resources = context.getPackageManager().getResourcesForApplication( + si.applicationInfo); + + int type = 0; + while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { + type = parser.next(); + } + + if (XML_TAG_SERVICE.equals(parser.getName())) { + final AttributeSet allAttributes = Xml.asAttributeSet(parser); + TypedArray afsAttributes = null; + try { + afsAttributes = resources.obtainAttributes(allAttributes, + com.android.internal.R.styleable.TranslationService); + settingsActivity = afsAttributes.getString( + R.styleable.ContentCaptureService_settingsActivity); + } finally { + if (afsAttributes != null) { + afsAttributes.recycle(); + } + } + } else { + Log.e(TAG, "Meta-data does not start with translation-service tag"); + } + } catch (PackageManager.NameNotFoundException | IOException | XmlPullParserException e) { + Log.e(TAG, "Error parsing auto fill service meta-data", e); + } + + mSettingsActivity = settingsActivity; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getClass().getSimpleName()); + builder.append("[").append(mServiceInfo); + builder.append(", settings:").append(mSettingsActivity); + return builder.toString(); + } + + /** + * Dumps the service information. + */ + public void dump(@NonNull String prefix, @NonNull PrintWriter pw) { + pw.print(prefix); + pw.print("Component: "); + pw.println(getServiceInfo().getComponentName()); + pw.print(prefix); + pw.print("Settings: "); + pw.println(mSettingsActivity); + } +} diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0f5321548f95..e8584dac90c8 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -8821,6 +8821,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, structure.setAutofillValue(getAutofillValue()); } structure.setImportantForAutofill(getImportantForAutofill()); + structure.setOnReceiveContentMimeTypes(getOnReceiveContentMimeTypes()); } int ignoredParentLeft = 0; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index d863ea5d2c01..0097b2e107a1 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -3150,6 +3150,8 @@ public final class ViewRootImpl implements ViewParent, mImeFocusController.onTraversal(hasWindowFocus, mWindowAttributes); + final boolean wasReportNextDraw = mReportNextDraw; + // Remember if we must report the next draw. if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { reportNextDraw(); @@ -3177,12 +3179,25 @@ public final class ViewRootImpl implements ViewParent, if (isViewVisible) { // Try again scheduleTraversals(); - } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) { - for (int i = 0; i < mPendingTransitions.size(); ++i) { - mPendingTransitions.get(i).endChangingAnimations(); + } else { + if (mPendingTransitions != null && mPendingTransitions.size() > 0) { + for (int i = 0; i < mPendingTransitions.size(); ++i) { + mPendingTransitions.get(i).endChangingAnimations(); + } + mPendingTransitions.clear(); + } + + // We may never draw since it's not visible. Report back that we're finished + // drawing. + if (!wasReportNextDraw && mReportNextDraw) { + mReportNextDraw = false; + pendingDrawFinished(); } - mPendingTransitions.clear(); } + + // We were unable to draw this traversal. Unset this flag since we'll block without + // ever being able to draw again + mNextDrawUseBlastSync = false; } if (mAttachInfo.mContentCaptureEvents != null) { diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java index 29ce231d5d87..f5aa97a88608 100644 --- a/core/java/android/view/ViewStructure.java +++ b/core/java/android/view/ViewStructure.java @@ -372,6 +372,14 @@ public abstract class ViewStructure { public void setImportantForAutofill(@AutofillImportance int mode) {} /** + * Sets the MIME types accepted by this view. See {@link View#getOnReceiveContentMimeTypes()}. + * + * <p>Should only be set when the node is used for Autofill or Content Capture purposes - it + * will be ignored when used for Assist. + */ + public void setOnReceiveContentMimeTypes(@Nullable String[] mimeTypes) {} + + /** * Sets the {@link android.text.InputType} bits of this node. * * @param inputType inputType bits as defined by {@link android.text.InputType}. diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java index e731d4b76fb9..4e9c229e03a0 100644 --- a/core/java/android/view/contentcapture/ViewNode.java +++ b/core/java/android/view/contentcapture/ViewNode.java @@ -81,6 +81,7 @@ public final class ViewNode extends AssistStructure.ViewNode { private static final long FLAGS_HAS_AUTOFILL_HINTS = 1L << 33; private static final long FLAGS_HAS_AUTOFILL_OPTIONS = 1L << 34; private static final long FLAGS_HAS_HINT_ID_ENTRY = 1L << 35; + private static final long FLAGS_HAS_MIME_TYPES = 1L << 36; /** Flags used to optimize what's written to the parcel */ private long mFlags; @@ -113,6 +114,7 @@ public final class ViewNode extends AssistStructure.ViewNode { private String[] mAutofillHints; private AutofillValue mAutofillValue; private CharSequence[] mAutofillOptions; + private String[] mOnReceiveContentMimeTypes; /** @hide */ public ViewNode() { @@ -169,6 +171,9 @@ public final class ViewNode extends AssistStructure.ViewNode { if ((nodeFlags & FLAGS_HAS_LOCALE_LIST) != 0) { mLocaleList = parcel.readParcelable(null); } + if ((nodeFlags & FLAGS_HAS_MIME_TYPES) != 0) { + mOnReceiveContentMimeTypes = parcel.readStringArray(); + } if ((nodeFlags & FLAGS_HAS_INPUT_TYPE) != 0) { mInputType = parcel.readInt(); } @@ -463,6 +468,12 @@ public final class ViewNode extends AssistStructure.ViewNode { return mAutofillOptions; } + @Override + @Nullable + public String[] getOnReceiveContentMimeTypes() { + return mOnReceiveContentMimeTypes; + } + @Nullable @Override public LocaleList getLocaleList() { @@ -508,6 +519,9 @@ public final class ViewNode extends AssistStructure.ViewNode { if (mLocaleList != null) { nodeFlags |= FLAGS_HAS_LOCALE_LIST; } + if (mOnReceiveContentMimeTypes != null) { + nodeFlags |= FLAGS_HAS_MIME_TYPES; + } if (mInputType != 0) { nodeFlags |= FLAGS_HAS_INPUT_TYPE; } @@ -584,6 +598,9 @@ public final class ViewNode extends AssistStructure.ViewNode { if ((nodeFlags & FLAGS_HAS_LOCALE_LIST) != 0) { parcel.writeParcelable(mLocaleList, 0); } + if ((nodeFlags & FLAGS_HAS_MIME_TYPES) != 0) { + parcel.writeStringArray(mOnReceiveContentMimeTypes); + } if ((nodeFlags & FLAGS_HAS_INPUT_TYPE) != 0) { parcel.writeInt(mInputType); } @@ -912,6 +929,11 @@ public final class ViewNode extends AssistStructure.ViewNode { } @Override + public void setOnReceiveContentMimeTypes(@Nullable String[] mimeTypes) { + mNode.mOnReceiveContentMimeTypes = mimeTypes; + } + + @Override public void setAutofillHints(String[] hints) { mNode.mAutofillHints = hints; } diff --git a/core/java/android/view/translation/ITranslationDirectManager.aidl b/core/java/android/view/translation/ITranslationDirectManager.aidl new file mode 100644 index 000000000000..358f99a5104b --- /dev/null +++ b/core/java/android/view/translation/ITranslationDirectManager.aidl @@ -0,0 +1,33 @@ +/* + * 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.translation; + +import android.service.translation.TranslationRequest; +import android.service.translation.ITranslationCallback; +import com.android.internal.os.IResultReceiver; + +/** + * Interface between an app (TranslationManager / Translator) and the remote TranslationService + * providing the TranslationService implementation. + * + * @hide + */ +oneway interface ITranslationDirectManager { + void onTranslationRequest(in TranslationRequest request, int sessionId, + in ITranslationCallback callback, in IResultReceiver receiver); + void onFinishTranslationSession(int sessionId); +} diff --git a/core/java/android/view/translation/ITranslationManager.aidl b/core/java/android/view/translation/ITranslationManager.aidl new file mode 100644 index 000000000000..73addf4d1894 --- /dev/null +++ b/core/java/android/view/translation/ITranslationManager.aidl @@ -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.translation; + +import android.service.translation.TranslationRequest; +import android.view.translation.TranslationSpec; +import com.android.internal.os.IResultReceiver; + +/** + * Mediator between apps being translated and translation service implementation. + * + * {@hide} + */ +oneway interface ITranslationManager { + void getSupportedLocales(in IResultReceiver receiver, int userId); + void onSessionCreated(in TranslationSpec sourceSpec, in TranslationSpec destSpec, + int sessionId, in IResultReceiver receiver, int userId); +} diff --git a/core/java/android/view/translation/OWNERS b/core/java/android/view/translation/OWNERS new file mode 100644 index 000000000000..a1e663aa8ff7 --- /dev/null +++ b/core/java/android/view/translation/OWNERS @@ -0,0 +1,8 @@ +# Bug component: 994311 + +adamhe@google.com +augale@google.com +joannechung@google.com +lpeter@google.com +svetoslavganov@google.com +tymtsai@google.com diff --git a/core/java/android/view/translation/TranslationData.aidl b/core/java/android/view/translation/TranslationData.aidl new file mode 100644 index 000000000000..40f21a6b3d4e --- /dev/null +++ b/core/java/android/view/translation/TranslationData.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.view.translation; + +parcelable TranslationData; diff --git a/core/java/android/view/translation/TranslationManager.java b/core/java/android/view/translation/TranslationManager.java new file mode 100644 index 000000000000..6554e1a1db54 --- /dev/null +++ b/core/java/android/view/translation/TranslationManager.java @@ -0,0 +1,201 @@ +/* + * 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.translation; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresFeature; +import android.annotation.SystemService; +import android.annotation.WorkerThread; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Handler; +import android.os.Looper; +import android.os.RemoteException; +import android.service.translation.TranslationService; +import android.util.ArrayMap; +import android.util.Log; +import android.util.Pair; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.SyncResultReceiver; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * The {@link TranslationManager} class provides ways for apps to integrate and use the + * translation framework. + * + * <p>The TranslationManager manages {@link Translator}s and help bridge client calls to + * the server {@link android.service.translation.TranslationService} </p> + */ +@SystemService(Context.TRANSLATION_MANAGER_SERVICE) +@RequiresFeature(PackageManager.FEATURE_TRANSLATION) +public final class TranslationManager { + + private static final String TAG = "TranslationManager"; + + /** + * Timeout for calls to system_server. + */ + static final int SYNC_CALLS_TIMEOUT_MS = 5000; + /** + * The result code from result receiver success. + * @hide + */ + public static final int STATUS_SYNC_CALL_SUCCESS = 1; + /** + * The result code from result receiver fail. + * @hide + */ + public static final int STATUS_SYNC_CALL_FAIL = 2; + + private static final Random ID_GENERATOR = new Random(); + private final Object mLock = new Object(); + + @NonNull + private final Context mContext; + + private final ITranslationManager mService; + + @Nullable + @GuardedBy("mLock") + private ITranslationDirectManager mDirectServiceBinder; + + @NonNull + @GuardedBy("mLock") + private final SparseArray<Translator> mTranslators = new SparseArray<>(); + + @NonNull + @GuardedBy("mLock") + private final ArrayMap<Pair<TranslationSpec, TranslationSpec>, Integer> mTranslatorIds = + new ArrayMap<>(); + + @NonNull + private final Handler mHandler; + + private static final AtomicInteger sAvailableRequestId = new AtomicInteger(1); + + /** + * @hide + */ + public TranslationManager(@NonNull Context context, ITranslationManager service) { + mContext = Objects.requireNonNull(context, "context cannot be null"); + mService = service; + + mHandler = Handler.createAsync(Looper.getMainLooper()); + } + + /** + * Create a Translator for translation. + * + * <p><strong>NOTE: </strong>Call on a worker thread. + * + * @param sourceSpec {@link TranslationSpec} for the data to be translated. + * @param destSpec {@link TranslationSpec} for the translated data. + * @return a {@link Translator} to be used for calling translation APIs. + */ + @Nullable + @WorkerThread + public Translator createTranslator(@NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec destSpec) { + Objects.requireNonNull(sourceSpec, "sourceSpec cannot be null"); + Objects.requireNonNull(sourceSpec, "destSpec cannot be null"); + + synchronized (mLock) { + // TODO(b/176464808): Disallow multiple Translator now, it will throw + // IllegalStateException. Need to discuss if we can allow multiple Translators. + final Pair<TranslationSpec, TranslationSpec> specs = new Pair<>(sourceSpec, destSpec); + if (mTranslatorIds.containsKey(specs)) { + return mTranslators.get(mTranslatorIds.get(specs)); + } + + int translatorId; + do { + translatorId = Math.abs(ID_GENERATOR.nextInt()); + } while (translatorId == 0 || mTranslators.indexOfKey(translatorId) >= 0); + + final Translator newTranslator = new Translator(mContext, sourceSpec, destSpec, + translatorId, this, mHandler, mService); + // Start the Translator session and wait for the result + newTranslator.start(); + try { + if (!newTranslator.isSessionCreated()) { + return null; + } + mTranslators.put(translatorId, newTranslator); + mTranslatorIds.put(specs, translatorId); + return newTranslator; + } catch (Translator.ServiceBinderReceiver.TimeoutException e) { + // TODO(b/176464808): maybe make SyncResultReceiver.TimeoutException constructor + // public and use it. + Log.e(TAG, "Timed out getting create session: " + e); + return null; + } + } + } + + /** + * Returns a list of locales supported by the {@link TranslationService}. + * + * <p><strong>NOTE: </strong>Call on a worker thread. + * + * TODO: Change to correct language/locale format + */ + @NonNull + @WorkerThread + public List<String> getSupportedLocales() { + try { + // TODO: implement it + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); + mService.getSupportedLocales(receiver, mContext.getUserId()); + int resutCode = receiver.getIntResult(); + if (resutCode != STATUS_SYNC_CALL_SUCCESS) { + return Collections.emptyList(); + } + return receiver.getParcelableResult(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } catch (SyncResultReceiver.TimeoutException e) { + Log.e(TAG, "Timed out getting supported locales: " + e); + return Collections.emptyList(); + } + } + + void removeTranslator(int id) { + synchronized (mLock) { + mTranslators.remove(id); + for (int i = 0; i < mTranslatorIds.size(); i++) { + if (mTranslatorIds.valueAt(i) == id) { + mTranslatorIds.removeAt(i); + break; + } + } + } + } + + AtomicInteger getAvailableRequestId() { + synchronized (mLock) { + return sAvailableRequestId; + } + } +} diff --git a/core/java/android/view/translation/TranslationRequest.aidl b/core/java/android/view/translation/TranslationRequest.aidl new file mode 100644 index 000000000000..c34bf3011462 --- /dev/null +++ b/core/java/android/view/translation/TranslationRequest.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.view.translation; + +parcelable TranslationRequest; diff --git a/core/java/android/view/translation/TranslationRequest.java b/core/java/android/view/translation/TranslationRequest.java new file mode 100644 index 000000000000..a5e3f758ba9f --- /dev/null +++ b/core/java/android/view/translation/TranslationRequest.java @@ -0,0 +1,215 @@ +/* + * 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.translation; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcelable; +import android.view.autofill.AutofillId; + +import com.android.internal.util.DataClass; + +/** + * Wrapper class for data to be translated by {@link android.service.translation.TranslationService} + */ +@DataClass(genToString = true, genBuilder = true) +public final class TranslationRequest implements Parcelable { + + @Nullable + private final AutofillId mAutofillId; + + @Nullable + private final CharSequence mTranslationText; + + public TranslationRequest(@Nullable CharSequence text) { + mAutofillId = null; + mTranslationText = text; + } + + private static CharSequence defaultTranslationText() { + return null; + } + + private static AutofillId defaultAutofillId() { + return null; + } + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/translation/TranslationRequest.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + /* package-private */ TranslationRequest( + @Nullable AutofillId autofillId, + @Nullable CharSequence translationText) { + this.mAutofillId = autofillId; + this.mTranslationText = translationText; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public @Nullable AutofillId getAutofillId() { + return mAutofillId; + } + + @DataClass.Generated.Member + public @Nullable CharSequence getTranslationText() { + return mTranslationText; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "TranslationRequest { " + + "autofillId = " + mAutofillId + ", " + + "translationText = " + mTranslationText + + " }"; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mAutofillId != null) flg |= 0x1; + if (mTranslationText != null) flg |= 0x2; + dest.writeByte(flg); + if (mAutofillId != null) dest.writeTypedObject(mAutofillId, flags); + if (mTranslationText != null) dest.writeCharSequence(mTranslationText); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ TranslationRequest(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + AutofillId autofillId = (flg & 0x1) == 0 ? null : (AutofillId) in.readTypedObject(AutofillId.CREATOR); + CharSequence translationText = (flg & 0x2) == 0 ? null : (CharSequence) in.readCharSequence(); + + this.mAutofillId = autofillId; + this.mTranslationText = translationText; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<TranslationRequest> CREATOR + = new Parcelable.Creator<TranslationRequest>() { + @Override + public TranslationRequest[] newArray(int size) { + return new TranslationRequest[size]; + } + + @Override + public TranslationRequest createFromParcel(@NonNull android.os.Parcel in) { + return new TranslationRequest(in); + } + }; + + /** + * A builder for {@link TranslationRequest} + */ + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static final class Builder { + + private @Nullable AutofillId mAutofillId; + private @Nullable CharSequence mTranslationText; + + private long mBuilderFieldsSet = 0L; + + public Builder() { + } + + @DataClass.Generated.Member + public @NonNull Builder setAutofillId(@NonNull AutofillId value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1; + mAutofillId = value; + return this; + } + + @DataClass.Generated.Member + public @NonNull Builder setTranslationText(@NonNull CharSequence value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mTranslationText = value; + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull TranslationRequest build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; // Mark builder used + + if ((mBuilderFieldsSet & 0x1) == 0) { + mAutofillId = defaultAutofillId(); + } + if ((mBuilderFieldsSet & 0x2) == 0) { + mTranslationText = defaultTranslationText(); + } + TranslationRequest o = new TranslationRequest( + mAutofillId, + mTranslationText); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x4) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } + + @DataClass.Generated( + time = 1610060189421L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/view/translation/TranslationRequest.java", + inputSignatures = "private final @android.annotation.Nullable android.view.autofill.AutofillId mAutofillId\nprivate final @android.annotation.Nullable java.lang.CharSequence mTranslationText\nprivate static java.lang.CharSequence defaultTranslationText()\nprivate static android.view.autofill.AutofillId defaultAutofillId()\nclass TranslationRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genBuilder=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/view/translation/TranslationResponse.aidl b/core/java/android/view/translation/TranslationResponse.aidl new file mode 100644 index 000000000000..e5350bb54dc2 --- /dev/null +++ b/core/java/android/view/translation/TranslationResponse.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.view.translation; + +parcelable TranslationResponse; diff --git a/core/java/android/view/translation/TranslationResponse.java b/core/java/android/view/translation/TranslationResponse.java new file mode 100644 index 000000000000..d29063fbd914 --- /dev/null +++ b/core/java/android/view/translation/TranslationResponse.java @@ -0,0 +1,311 @@ +/* + * 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.translation; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.service.translation.TranslationService; + +import com.android.internal.util.DataClass; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; + +/** + * Response from the {@link TranslationService}, which contains the translated result. + */ +@DataClass(genBuilder = true, genToString = true, genHiddenConstDefs = true) +public final class TranslationResponse implements Parcelable { + + /** + * The {@link TranslationService} was successful in translating. + */ + public static final int TRANSLATION_STATUS_SUCCESS = 0; + /** + * The {@link TranslationService} returned unknown translation result. + */ + public static final int TRANSLATION_STATUS_UNKNOWN_ERROR = 1; + /** + * The language of the request is not available to be translated. + */ + public static final int TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE = 2; + + /** + * The translation result status code. + */ + private final @TranslationStatus int mTranslationStatus; + /** + * The translation results. If there is no translation result, set it with an empty list. + */ + @NonNull + private List<TranslationRequest> mTranslations = new ArrayList(); + + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/translation/TranslationResponse.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** @hide */ + @IntDef(prefix = "TRANSLATION_STATUS_", value = { + TRANSLATION_STATUS_SUCCESS, + TRANSLATION_STATUS_UNKNOWN_ERROR, + TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE + }) + @Retention(RetentionPolicy.SOURCE) + @DataClass.Generated.Member + public @interface TranslationStatus {} + + /** @hide */ + @DataClass.Generated.Member + public static String translationStatusToString(@TranslationStatus int value) { + switch (value) { + case TRANSLATION_STATUS_SUCCESS: + return "TRANSLATION_STATUS_SUCCESS"; + case TRANSLATION_STATUS_UNKNOWN_ERROR: + return "TRANSLATION_STATUS_UNKNOWN_ERROR"; + case TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE: + return "TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE"; + default: return Integer.toHexString(value); + } + } + + @DataClass.Generated.Member + /* package-private */ TranslationResponse( + @TranslationStatus int translationStatus, + @NonNull List<TranslationRequest> translations) { + this.mTranslationStatus = translationStatus; + + if (!(mTranslationStatus == TRANSLATION_STATUS_SUCCESS) + && !(mTranslationStatus == TRANSLATION_STATUS_UNKNOWN_ERROR) + && !(mTranslationStatus == TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE)) { + throw new java.lang.IllegalArgumentException( + "translationStatus was " + mTranslationStatus + " but must be one of: " + + "TRANSLATION_STATUS_SUCCESS(" + TRANSLATION_STATUS_SUCCESS + "), " + + "TRANSLATION_STATUS_UNKNOWN_ERROR(" + TRANSLATION_STATUS_UNKNOWN_ERROR + "), " + + "TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE(" + TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE + ")"); + } + + this.mTranslations = translations; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTranslations); + + // onConstructed(); // You can define this method to get a callback + } + + /** + * The translation result status code. + */ + @DataClass.Generated.Member + public @TranslationStatus int getTranslationStatus() { + return mTranslationStatus; + } + + /** + * The translation results. If there is no translation result, set it with an empty list. + */ + @DataClass.Generated.Member + public @NonNull List<TranslationRequest> getTranslations() { + return mTranslations; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "TranslationResponse { " + + "translationStatus = " + translationStatusToString(mTranslationStatus) + ", " + + "translations = " + mTranslations + + " }"; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + dest.writeInt(mTranslationStatus); + dest.writeParcelableList(mTranslations, flags); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ TranslationResponse(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + int translationStatus = in.readInt(); + List<TranslationRequest> translations = new ArrayList<>(); + in.readParcelableList(translations, TranslationRequest.class.getClassLoader()); + + this.mTranslationStatus = translationStatus; + + if (!(mTranslationStatus == TRANSLATION_STATUS_SUCCESS) + && !(mTranslationStatus == TRANSLATION_STATUS_UNKNOWN_ERROR) + && !(mTranslationStatus == TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE)) { + throw new java.lang.IllegalArgumentException( + "translationStatus was " + mTranslationStatus + " but must be one of: " + + "TRANSLATION_STATUS_SUCCESS(" + TRANSLATION_STATUS_SUCCESS + "), " + + "TRANSLATION_STATUS_UNKNOWN_ERROR(" + TRANSLATION_STATUS_UNKNOWN_ERROR + "), " + + "TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE(" + TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE + ")"); + } + + this.mTranslations = translations; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTranslations); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<TranslationResponse> CREATOR + = new Parcelable.Creator<TranslationResponse>() { + @Override + public TranslationResponse[] newArray(int size) { + return new TranslationResponse[size]; + } + + @Override + public TranslationResponse createFromParcel(@NonNull Parcel in) { + return new TranslationResponse(in); + } + }; + + /** + * A builder for {@link TranslationResponse} + */ + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static final class Builder { + + private @TranslationStatus int mTranslationStatus; + private @NonNull List<TranslationRequest> mTranslations; + + private long mBuilderFieldsSet = 0L; + + /** + * Creates a new Builder. + * + * @param translationStatus + * The translation result status code. + */ + public Builder( + @TranslationStatus int translationStatus) { + mTranslationStatus = translationStatus; + + if (!(mTranslationStatus == TRANSLATION_STATUS_SUCCESS) + && !(mTranslationStatus == TRANSLATION_STATUS_UNKNOWN_ERROR) + && !(mTranslationStatus == TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE)) { + throw new java.lang.IllegalArgumentException( + "translationStatus was " + mTranslationStatus + " but must be one of: " + + "TRANSLATION_STATUS_SUCCESS(" + TRANSLATION_STATUS_SUCCESS + "), " + + "TRANSLATION_STATUS_UNKNOWN_ERROR(" + TRANSLATION_STATUS_UNKNOWN_ERROR + "), " + + "TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE(" + TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE + ")"); + } + + } + + /** + * The translation result status code. + */ + @DataClass.Generated.Member + public @NonNull Builder setTranslationStatus(@TranslationStatus int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1; + mTranslationStatus = value; + return this; + } + + /** + * The translation results. If there is no translation result, set it with an empty list. + */ + @DataClass.Generated.Member + public @NonNull Builder setTranslations(@NonNull List<TranslationRequest> value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mTranslations = value; + return this; + } + + /** @see #setTranslations */ + @DataClass.Generated.Member + public @NonNull Builder addTranslations(@NonNull TranslationRequest value) { + // You can refine this method's name by providing item's singular name, e.g.: + // @DataClass.PluralOf("item")) mItems = ... + + if (mTranslations == null) setTranslations(new ArrayList<>()); + mTranslations.add(value); + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull TranslationResponse build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; // Mark builder used + + if ((mBuilderFieldsSet & 0x2) == 0) { + mTranslations = new ArrayList(); + } + TranslationResponse o = new TranslationResponse( + mTranslationStatus, + mTranslations); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x4) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } + + @DataClass.Generated( + time = 1609973911361L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/view/translation/TranslationResponse.java", + inputSignatures = "public static final int TRANSLATION_STATUS_SUCCESS\npublic static final int TRANSLATION_STATUS_UNKNOWN_ERROR\npublic static final int TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE\nprivate final @android.view.translation.TranslationResponse.TranslationStatus int mTranslationStatus\nprivate @android.annotation.NonNull java.util.List<android.view.translation.TranslationRequest> mTranslations\nclass TranslationResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genHiddenConstDefs=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/view/translation/TranslationSpec.aidl b/core/java/android/view/translation/TranslationSpec.aidl new file mode 100644 index 000000000000..875d798370d4 --- /dev/null +++ b/core/java/android/view/translation/TranslationSpec.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.view.translation; + +parcelable TranslationSpec; diff --git a/core/java/android/view/translation/TranslationSpec.java b/core/java/android/view/translation/TranslationSpec.java new file mode 100644 index 000000000000..ab1bc477e0fd --- /dev/null +++ b/core/java/android/view/translation/TranslationSpec.java @@ -0,0 +1,189 @@ +/* + * 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.translation; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +/** + * Specs and additional info for the translation data. + * + * <p>This spec help specify information such as the language/locale for the translation, as well + * as the data format for the translation (text, audio, etc.)</p> + */ +@DataClass(genEqualsHashCode = true, genHiddenConstDefs = true) +public final class TranslationSpec implements Parcelable { + + /** Data format for translation is text. */ + public static final int DATA_FORMAT_TEXT = 1; + + /** @hide */ + @android.annotation.IntDef(prefix = "DATA_FORMAT_", value = { + DATA_FORMAT_TEXT + }) + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) + @DataClass.Generated.Member + public @interface DataFormat {} + + /** + * String representation of language codes e.g. "en", "es", etc. + */ + private final @NonNull String mLanguage; + + private final @DataFormat int mDataFormat; + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/translation/TranslationSpec.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Creates a new TranslationSpec. + * + * @param language + * String representation of language codes e.g. "en", "es", etc. + */ + @DataClass.Generated.Member + public TranslationSpec( + @NonNull String language, + @DataFormat int dataFormat) { + this.mLanguage = language; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mLanguage); + this.mDataFormat = dataFormat; + com.android.internal.util.AnnotationValidations.validate( + DataFormat.class, null, mDataFormat); + + // onConstructed(); // You can define this method to get a callback + } + + /** + * String representation of language codes e.g. "en", "es", etc. + */ + @DataClass.Generated.Member + public @NonNull String getLanguage() { + return mLanguage; + } + + @DataClass.Generated.Member + public @DataFormat int getDataFormat() { + return mDataFormat; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@android.annotation.Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(TranslationSpec other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + TranslationSpec that = (TranslationSpec) o; + //noinspection PointlessBooleanExpression + return true + && java.util.Objects.equals(mLanguage, that.mLanguage) + && mDataFormat == that.mDataFormat; + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + java.util.Objects.hashCode(mLanguage); + _hash = 31 * _hash + mDataFormat; + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + dest.writeString(mLanguage); + dest.writeInt(mDataFormat); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ TranslationSpec(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + String language = in.readString(); + int dataFormat = in.readInt(); + + this.mLanguage = language; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mLanguage); + this.mDataFormat = dataFormat; + com.android.internal.util.AnnotationValidations.validate( + DataFormat.class, null, mDataFormat); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<TranslationSpec> CREATOR + = new Parcelable.Creator<TranslationSpec>() { + @Override + public TranslationSpec[] newArray(int size) { + return new TranslationSpec[size]; + } + + @Override + public TranslationSpec createFromParcel(@NonNull Parcel in) { + return new TranslationSpec(in); + } + }; + + @DataClass.Generated( + time = 1609964630624L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/view/translation/TranslationSpec.java", + inputSignatures = "public static final int DATA_FORMAT_TEXT\nprivate final @android.annotation.NonNull java.lang.String mLanguage\nprivate final @android.view.translation.TranslationSpec.DataFormat int mDataFormat\nclass TranslationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genHiddenConstDefs=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/view/translation/Translator.java b/core/java/android/view/translation/Translator.java new file mode 100644 index 000000000000..675f32b19d17 --- /dev/null +++ b/core/java/android/view/translation/Translator.java @@ -0,0 +1,298 @@ +/* + * 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.translation; + +import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_FAIL; +import static android.view.translation.TranslationManager.SYNC_CALLS_TIMEOUT_MS; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.annotation.WorkerThread; +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.IResultReceiver; +import com.android.internal.util.SyncResultReceiver; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Objects; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * The {@link Translator} for translation, defined by a source and a dest {@link TranslationSpec}. + */ +@SuppressLint("NotCloseable") +public class Translator { + + private static final String TAG = "Translator"; + + // TODO: make this configurable and cross the Translation component + private static boolean sDEBUG = false; + + private final Object mLock = new Object(); + + private int mId; + + @NonNull + private final Context mContext; + + @NonNull + private final TranslationSpec mSourceSpec; + + @NonNull + private final TranslationSpec mDestSpec; + + @NonNull + private final TranslationManager mManager; + + @NonNull + private final Handler mHandler; + + /** + * Interface to the system_server binder object. + */ + private ITranslationManager mSystemServerBinder; + + /** + * Direct interface to the TranslationService binder object. + */ + @Nullable + private ITranslationDirectManager mDirectServiceBinder; + + @NonNull + private final ServiceBinderReceiver mServiceBinderReceiver; + + @GuardedBy("mLock") + private boolean mDestroyed; + + /** + * Name of the {@link IResultReceiver} extra used to pass the binder interface to Translator. + * @hide + */ + public static final String EXTRA_SERVICE_BINDER = "binder"; + /** + * Name of the extra used to pass the session id to Translator. + * @hide + */ + public static final String EXTRA_SESSION_ID = "sessionId"; + + static class ServiceBinderReceiver extends IResultReceiver.Stub { + private final WeakReference<Translator> mTranslator; + private final CountDownLatch mLatch = new CountDownLatch(1); + private int mSessionId; + + ServiceBinderReceiver(Translator translator) { + mTranslator = new WeakReference<>(translator); + } + + int getSessionStateResult() throws TimeoutException { + try { + if (!mLatch.await(SYNC_CALLS_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { + throw new TimeoutException( + "Session not created in " + SYNC_CALLS_TIMEOUT_MS + "ms"); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new TimeoutException("Session not created because interrupted"); + } + return mSessionId; + } + + @Override + public void send(int resultCode, Bundle resultData) { + if (resultCode == STATUS_SYNC_CALL_FAIL) { + mLatch.countDown(); + return; + } + mSessionId = resultData.getInt(EXTRA_SESSION_ID); + final Translator translator = mTranslator.get(); + if (translator == null) { + Log.w(TAG, "received result after session is finished"); + return; + } + final IBinder binder; + if (resultData != null) { + binder = resultData.getBinder(EXTRA_SERVICE_BINDER); + if (binder == null) { + Log.wtf(TAG, "No " + EXTRA_SERVICE_BINDER + " extra result"); + return; + } + } else { + binder = null; + } + translator.setServiceBinder(binder); + mLatch.countDown(); + } + + // TODO(b/176464808): maybe make SyncResultReceiver.TimeoutException constructor public + // and use it. + static final class TimeoutException extends Exception { + private TimeoutException(String msg) { + super(msg); + } + } + } + + /** + * Create the Translator. + * + * @hide + */ + public Translator(@NonNull Context context, + @NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec destSpec, int sessionId, + @NonNull TranslationManager translationManager, @NonNull Handler handler, + @Nullable ITranslationManager systemServerBinder) { + mContext = context; + mSourceSpec = sourceSpec; + mDestSpec = destSpec; + mId = sessionId; + mManager = translationManager; + mHandler = handler; + mSystemServerBinder = systemServerBinder; + mServiceBinderReceiver = new ServiceBinderReceiver(this); + } + + /** + * Starts this Translator session. + */ + void start() { + try { + mSystemServerBinder.onSessionCreated(mSourceSpec, mDestSpec, mId, + mServiceBinderReceiver, mContext.getUserId()); + } catch (RemoteException e) { + Log.w(TAG, "RemoteException calling startSession(): " + e); + } + } + + /** + * Wait this Translator session created. + * + * @return {@code true} if the session is created successfully. + */ + boolean isSessionCreated() throws ServiceBinderReceiver.TimeoutException { + int receivedId = mServiceBinderReceiver.getSessionStateResult(); + return receivedId > 0; + } + + private int getNextRequestId() { + // Get from manager to keep the request id unique to different Translators + return mManager.getAvailableRequestId().getAndIncrement(); + } + + private void setServiceBinder(@Nullable IBinder binder) { + synchronized (mLock) { + if (mDirectServiceBinder != null) { + return; + } + if (binder != null) { + mDirectServiceBinder = ITranslationDirectManager.Stub.asInterface(binder); + } + } + } + + /** @hide */ + public int getTranslatorId() { + return mId; + } + + /** + * Requests a translation for the provided {@link TranslationRequest} using the Translator's + * source spec and destination spec. + * + * <p><strong>NOTE: </strong>Call on a worker thread. + * + * @param request {@link TranslationRequest} request to be translated. + * + * @return {@link TranslationRequest} containing translated request, + * or null if translation could not be done. + * @throws IllegalStateException if this TextClassification session was destroyed when calls + */ + @Nullable + @WorkerThread + public TranslationResponse translate(@NonNull TranslationRequest request) { + Objects.requireNonNull(request, "Translation request cannot be null"); + if (isDestroyed()) { + // TODO(b/176464808): Disallow multiple Translator now, it will throw + // IllegalStateException. Need to discuss if we can allow multiple Translators. + throw new IllegalStateException( + "This translator has been destroyed"); + } + final ArrayList<TranslationRequest> requests = new ArrayList<>(); + requests.add(request); + final android.service.translation.TranslationRequest internalRequest = + new android.service.translation.TranslationRequest + .Builder(getNextRequestId(), mSourceSpec, mDestSpec, requests) + .build(); + + TranslationResponse response = null; + try { + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); + mDirectServiceBinder.onTranslationRequest(internalRequest, mId, null, receiver); + + response = receiver.getParcelableResult(); + } catch (RemoteException e) { + Log.w(TAG, "RemoteException calling requestTranslate(): " + e); + } catch (SyncResultReceiver.TimeoutException e) { + Log.e(TAG, "Timed out calling requestTranslate: " + e); + } + if (sDEBUG) { + Log.v(TAG, "Receive translation response: " + response); + } + return response; + } + + /** + * Destroy this Translator. + */ + public void destroy() { + synchronized (mLock) { + if (mDestroyed) { + return; + } + mDestroyed = true; + try { + mDirectServiceBinder.onFinishTranslationSession(mId); + } catch (RemoteException e) { + Log.w(TAG, "RemoteException calling onSessionFinished"); + } + mDirectServiceBinder = null; + mManager.removeTranslator(mId); + } + } + + /** + * Returns whether or not this Translator has been destroyed. + * + * @see #destroy() + */ + public boolean isDestroyed() { + synchronized (mLock) { + return mDestroyed; + } + } + + // TODO: add methods for UI-toolkit case. +} diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 0025d1e2a853..26dd5e309fa7 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -99,6 +99,7 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; +import android.view.OnReceiveContentListener; import android.view.SubMenu; import android.view.View; import android.view.View.DragShadowBuilder; @@ -588,8 +589,18 @@ public class Editor { mUndoOwner = mUndoManager.getOwner(UNDO_OWNER_TAG, this); } - @VisibleForTesting - public @NonNull TextViewOnReceiveContentListener getDefaultOnReceiveContentListener() { + /** + * Returns the default handler for receiving content in an editable {@link TextView}. This + * listener impl is used to encapsulate the default behavior but it is not part of the public + * API. If an app wants to execute the default platform behavior for receiving content, it + * should call {@link View#onReceiveContent}. Alternatively, if an app implements a custom + * listener for receiving content and wants to delegate some of the content to be handled by + * the platform, it should return the corresponding content from its listener. See + * {@link View#setOnReceiveContentListener} and {@link OnReceiveContentListener} for more info. + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + @NonNull + public TextViewOnReceiveContentListener getDefaultOnReceiveContentListener() { return mDefaultOnReceiveContentListener; } @@ -6613,6 +6624,9 @@ public class Editor { } updateSelection(event); + if (mTextView.hasSelection() && mEndHandle != null) { + mEndHandle.updateMagnifier(event); + } break; case MotionEvent.ACTION_UP: @@ -6623,6 +6637,9 @@ public class Editor { break; } updateSelection(event); + if (mEndHandle != null) { + mEndHandle.dismissMagnifier(); + } // No longer dragging to select text, let the parent intercept events. mTextView.getParent().requestDisallowInterceptTouchEvent(false); diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 220a31c12f4e..8dafc5db6178 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -75,6 +75,8 @@ import android.view.RemotableViewMethod; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.MarginLayoutParams; +import android.view.ViewManager; +import android.view.ViewParent; import android.view.ViewStub; import android.widget.AdapterView.OnItemClickListener; @@ -177,6 +179,7 @@ public class RemoteViews implements Parcelable, Filter { private static final int OVERRIDE_TEXT_COLORS_TAG = 20; private static final int SET_RIPPLE_DRAWABLE_COLOR_TAG = 21; private static final int SET_INT_TAG_TAG = 22; + private static final int REMOVE_FROM_PARENT_ACTION_TAG = 23; /** @hide **/ @IntDef(prefix = "MARGIN_", value = { @@ -1831,6 +1834,75 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Action to remove a view from its parent. + */ + private class RemoveFromParentAction extends Action { + + RemoveFromParentAction(@IdRes int viewId) { + this.viewId = viewId; + } + + RemoveFromParentAction(Parcel parcel) { + viewId = parcel.readInt(); + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(viewId); + } + + @Override + public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + final View target = root.findViewById(viewId); + + if (target == null || target == root) { + return; + } + + ViewParent parent = target.getParent(); + if (parent instanceof ViewManager) { + ((ViewManager) parent).removeView(target); + } + } + + @Override + public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) { + // In the async implementation, update the view tree so that subsequent calls to + // findViewById return the correct view. + root.createTree(); + ViewTree target = root.findViewTreeById(viewId); + + if (target == null || target == root) { + return ACTION_NOOP; + } + + ViewTree parent = root.findViewTreeParentOf(target); + if (parent == null || !(parent.mRoot instanceof ViewManager)) { + return ACTION_NOOP; + } + final ViewManager parentVg = (ViewManager) parent.mRoot; + + parent.mChildren.remove(target); + return new RuntimeAction() { + @Override + public void apply(View root, ViewGroup rootParent, OnClickHandler handler) + throws ActionException { + parentVg.removeView(target.mRoot); + } + }; + } + + @Override + public int getActionTag() { + return REMOVE_FROM_PARENT_ACTION_TAG; + } + + @Override + public int mergeBehavior() { + return MERGE_APPEND; + } + } + + /** * Helper action to set compound drawables on a TextView. Supports relative * (s/t/e/b) or cardinal (l/t/r/b) arrangement. */ @@ -2537,6 +2609,8 @@ public class RemoteViews implements Parcelable, Filter { return new SetRippleDrawableColor(parcel); case SET_INT_TAG_TAG: return new SetIntTagAction(parcel); + case REMOVE_FROM_PARENT_ACTION_TAG: + return new RemoveFromParentAction(parcel); default: throw new ActionException("Tag " + tag + " not found"); } @@ -2675,6 +2749,18 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Removes the {@link View} specified by the {@code viewId} from its parent {@link ViewManager}. + * This will do nothing if the viewId specifies the root view of this RemoteViews. + * + * @param viewId The id of the {@link View} to remove from its parent. + * + * @hide + */ + public void removeFromParent(@IdRes int viewId) { + addAction(new RemoveFromParentAction(viewId)); + } + + /** * Equivalent to calling {@link AdapterViewAnimator#showNext()} * * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()} @@ -4025,6 +4111,22 @@ public class RemoteViews implements Parcelable, Filter { return null; } + public ViewTree findViewTreeParentOf(ViewTree child) { + if (mChildren == null) { + return null; + } + for (ViewTree tree : mChildren) { + if (tree == child) { + return this; + } + ViewTree result = tree.findViewTreeParentOf(child); + if (result != null) { + return result; + } + } + return null; + } + public void replaceView(View v) { mRoot = v; mChildren = null; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 1599f2bdef2e..b8660255acc1 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -11717,6 +11717,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } } + String[] mimeTypes = getOnReceiveContentMimeTypes(); + if (mimeTypes == null && mEditor != null) { + // If the app hasn't set a listener for receiving content on this view (ie, + // getOnReceiveContentMimeTypes() returns null), check if it implements the + // keyboard image API and, if possible, use those MIME types as fallback. + // This fallback is only in place for autofill, not other mechanisms for + // inserting content. See AUTOFILL_NON_TEXT_REQUIRES_ON_RECEIVE_CONTENT_LISTENER + // in TextViewOnReceiveContentListener for more info. + mimeTypes = mEditor.getDefaultOnReceiveContentListener() + .getFallbackMimeTypesForAutofill(this); + } + structure.setOnReceiveContentMimeTypes(mimeTypes); } if (!isPassword || viewFor == VIEW_STRUCTURE_FOR_AUTOFILL diff --git a/core/java/android/widget/TextViewOnReceiveContentListener.java b/core/java/android/widget/TextViewOnReceiveContentListener.java index 8cef1061c423..91fac5511807 100644 --- a/core/java/android/widget/TextViewOnReceiveContentListener.java +++ b/core/java/android/widget/TextViewOnReceiveContentListener.java @@ -60,7 +60,7 @@ import java.util.Arrays; * * @hide */ -@VisibleForTesting +@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public final class TextViewOnReceiveContentListener implements OnReceiveContentListener { private static final String LOG_TAG = "ReceiveContent"; @@ -261,10 +261,17 @@ public final class TextViewOnReceiveContentListener implements OnReceiveContentL mInputConnectionInfo = null; } - /** @hide */ - @VisibleForTesting + /** + * Returns the MIME types accepted by {@link View#performReceiveContent} for the given view, + * <strong>for autofill purposes</strong>. This will be non-null only if fallback to the + * keyboard image API {@link #isUsageOfImeCommitContentEnabled is enabled} and the view has an + * {@link InputConnection} with {@link EditorInfo#contentMimeTypes} populated. + * + * @hide + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) @Nullable - public String[] getEditorInfoMimeTypes(@NonNull TextView view) { + public String[] getFallbackMimeTypesForAutofill(@NonNull TextView view) { if (!isUsageOfImeCommitContentEnabled(view)) { return null; } diff --git a/core/java/com/android/internal/inputmethod/Completable.java b/core/java/com/android/internal/inputmethod/Completable.java index b82ba8132889..d6a466316ed4 100644 --- a/core/java/com/android/internal/inputmethod/Completable.java +++ b/core/java/com/android/internal/inputmethod/Completable.java @@ -117,8 +117,8 @@ public final class Completable { } /** - * @return {@link true} if {@link #onComplete()} gets called and {@link #mState} is - * {@link CompletionState#COMPLETED_WITH_VALUE} . + * @return {@code true} if {@link #onComplete()} gets called and {@link #mState} is + * {@link CompletionState#COMPLETED_WITH_VALUE}. */ @AnyThread public boolean hasValue() { @@ -232,13 +232,25 @@ public final class Completable { } /** - * Blocks the calling thread until this object becomes ready to return the value. + * Blocks the calling thread until this object becomes ready to return the value, even if + * {@link InterruptedException} is thrown. */ @AnyThread public void await() { - try { - mLatch.await(); - } catch (InterruptedException ignored) { } + boolean interrupted = false; + while (true) { + try { + mLatch.await(); + break; + } catch (InterruptedException ignored) { + interrupted = true; + } + } + + if (interrupted) { + // Try to preserve the interrupt bit on this thread. + Thread.currentThread().interrupt(); + } } } @@ -487,7 +499,7 @@ public final class Completable { /** * Await the result by the {@link Completable.Values}. * - * @return the result once {@link ValueBase#onComplete()} + * @return the result once {@link ValueBase#onComplete()}. */ @AnyThread @Nullable @@ -499,7 +511,7 @@ public final class Completable { /** * Await the int result by the {@link Completable.Int}. * - * @return the result once {@link ValueBase#onComplete()} + * @return the result once {@link ValueBase#onComplete()}. */ @AnyThread public static int getIntResult(@NonNull Completable.Int value) { diff --git a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java index 9c3bb761b9f7..6609ebe49bf6 100644 --- a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java +++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java @@ -16,7 +16,11 @@ package com.android.internal.os; +import android.os.BatteryConsumer; import android.os.BatteryStats; +import android.os.BatteryUsageStats; +import android.os.BatteryUsageStatsQuery; +import android.os.SystemBatteryConsumer; import android.os.UserHandle; import android.util.SparseArray; @@ -26,11 +30,30 @@ import java.util.List; * Estimates power consumed by the ambient display */ public class AmbientDisplayPowerCalculator extends PowerCalculator { - - private final PowerProfile mPowerProfile; + private final UsageBasedPowerEstimator mPowerEstimator; public AmbientDisplayPowerCalculator(PowerProfile powerProfile) { - mPowerProfile = powerProfile; + mPowerEstimator = new UsageBasedPowerEstimator( + powerProfile.getAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY)); + } + + /** + * Ambient display power is the additional power the screen takes while in ambient display/ + * screen doze/always-on display (interchangeable terms) mode. + */ + @Override + public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query, + SparseArray<UserHandle> asUsers) { + final long durationMs = calculateDuration(batteryStats, rawRealtimeUs, + BatteryStats.STATS_SINCE_CHARGED); + final double powerMah = mPowerEstimator.calculatePower(durationMs); + if (powerMah > 0) { + builder.getOrCreateSystemBatteryConsumerBuilder( + SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah) + .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs); + } } /** @@ -42,16 +65,18 @@ public class AmbientDisplayPowerCalculator extends PowerCalculator { @Override public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { - - long ambientDisplayMs = batteryStats.getScreenDozeTime(rawRealtimeUs, statsType) / 1000; - double power = mPowerProfile.getAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY) - * ambientDisplayMs / (60 * 60 * 1000); - if (power > 0) { + final long durationMs = calculateDuration(batteryStats, rawRealtimeUs, statsType); + final double powerMah = mPowerEstimator.calculatePower(durationMs); + if (powerMah > 0) { BatterySipper bs = new BatterySipper(BatterySipper.DrainType.AMBIENT_DISPLAY, null, 0); - bs.usagePowerMah = power; - bs.usageTimeMs = ambientDisplayMs; + bs.usagePowerMah = powerMah; + bs.usageTimeMs = durationMs; bs.sumPower(); sippers.add(bs); } } + + private long calculateDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType) { + return batteryStats.getScreenDozeTime(rawRealtimeUs, statsType) / 1000; + } } diff --git a/core/java/com/android/internal/os/AudioPowerCalculator.java b/core/java/com/android/internal/os/AudioPowerCalculator.java new file mode 100644 index 000000000000..79b331da9c8a --- /dev/null +++ b/core/java/com/android/internal/os/AudioPowerCalculator.java @@ -0,0 +1,48 @@ +/* + * 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.internal.os; + +import android.os.BatteryConsumer; +import android.os.BatteryStats; +import android.os.BatteryUsageStatsQuery; +import android.os.UidBatteryConsumer; + +/** + * A {@link PowerCalculator} to calculate power consumed by audio hardware. + * + * Also see {@link PowerProfile#POWER_AUDIO}. + */ +public class AudioPowerCalculator extends PowerCalculator { + // Calculate audio power usage, an estimate based on the average power routed to different + // components like speaker, bluetooth, usb-c, earphone, etc. + // TODO(b/175344313): improve the model by taking into account different audio routes + private final UsageBasedPowerEstimator mPowerEstimator; + + public AudioPowerCalculator(PowerProfile powerProfile) { + mPowerEstimator = new UsageBasedPowerEstimator( + powerProfile.getAveragePower(PowerProfile.POWER_AUDIO)); + } + + @Override + protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { + final long durationMs = mPowerEstimator.calculateDuration(u.getAudioTurnedOnTimer(), + rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); + final double powerMah = mPowerEstimator.calculatePower(durationMs); + app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO, durationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_AUDIO, powerMah); + } +} diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 93dff9f76df1..33aa19078c9f 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -873,7 +873,9 @@ public class BatteryStatsImpl extends BatteryStats { protected StopwatchTimer mScreenDozeTimer; int mScreenBrightnessBin = -1; - final StopwatchTimer[] mScreenBrightnessTimer = new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS]; + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + protected final StopwatchTimer[] mScreenBrightnessTimer = + new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS]; boolean mPretendScreenOff; diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java index 9904d301ef72..e5d64a0e3c84 100644 --- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java +++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java @@ -66,7 +66,8 @@ public class BatteryUsageStatsProvider { mContext.getSystemService(SensorManager.class))); mPowerCalculators.add(new CameraPowerCalculator(mPowerProfile)); mPowerCalculators.add(new FlashlightPowerCalculator(mPowerProfile)); - mPowerCalculators.add(new MediaPowerCalculator(mPowerProfile)); + mPowerCalculators.add(new AudioPowerCalculator(mPowerProfile)); + mPowerCalculators.add(new VideoPowerCalculator(mPowerProfile)); mPowerCalculators.add(new PhonePowerCalculator(mPowerProfile)); mPowerCalculators.add(new ScreenPowerCalculator(mPowerProfile)); mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile)); diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java index f5690e0de38f..4c3b950ff715 100644 --- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java +++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java @@ -29,8 +29,8 @@ import android.util.SparseArray; import java.util.List; public class BluetoothPowerCalculator extends PowerCalculator { - private static final boolean DEBUG = BatteryStatsHelper.DEBUG; private static final String TAG = "BluetoothPowerCalc"; + private static final boolean DEBUG = BatteryStatsHelper.DEBUG; private final double mIdleMa; private final double mRxMa; private final double mTxMa; @@ -41,11 +41,6 @@ public class BluetoothPowerCalculator extends PowerCalculator { public double powerMah; } - // Objects used for passing calculation results. Fields are used to avoid allocations. - private final PowerAndDuration mUidPowerAndDuration = new PowerAndDuration(); - private final PowerAndDuration mTotalPowerAndDuration = new PowerAndDuration(); - private final PowerAndDuration mSystemPowerAndDuration = new PowerAndDuration(); - public BluetoothPowerCalculator(PowerProfile profile) { mIdleMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE); mRxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX); @@ -61,8 +56,7 @@ public class BluetoothPowerCalculator extends PowerCalculator { return; } - mTotalPowerAndDuration.durationMs = 0; - mTotalPowerAndDuration.powerMah = 0; + final PowerAndDuration total = new PowerAndDuration(); SystemBatteryConsumer.Builder systemBatteryConsumerBuilder = builder.getOrCreateSystemBatteryConsumerBuilder( @@ -72,24 +66,25 @@ public class BluetoothPowerCalculator extends PowerCalculator { builder.getUidBatteryConsumerBuilders(); for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); - calculateApp(app); + calculateApp(app, total); if (app.getUid() == Process.BLUETOOTH_UID) { app.setSystemComponent(true); systemBatteryConsumerBuilder.addUidBatteryConsumer(app); } } - final BatteryStats.ControllerActivityCounter counter = + final BatteryStats.ControllerActivityCounter activityCounter = batteryStats.getBluetoothControllerActivity(); - - calculatePowerAndDuration(counter, mSystemPowerAndDuration); + final long systemDurationMs = calculateDuration(activityCounter); + final double systemPowerMah = calculatePower(activityCounter); // Subtract what the apps used, but clamp to 0. - final long systemComponentDurationMs = Math.max(0, - mSystemPowerAndDuration.durationMs - mTotalPowerAndDuration.durationMs); - final double systemComponentPowerMah = Math.max(0, - mSystemPowerAndDuration.powerMah - mTotalPowerAndDuration.powerMah); - + final long systemComponentDurationMs = Math.max(0, systemDurationMs - total.durationMs); + final double systemComponentPowerMah = Math.max(0, systemPowerMah - total.powerMah); + if (DEBUG && systemComponentPowerMah != 0) { + Log.d(TAG, "Bluetooth active: time=" + (systemComponentDurationMs) + + " power=" + formatCharge(systemComponentPowerMah)); + } systemBatteryConsumerBuilder .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_BLUETOOTH, systemComponentDurationMs) @@ -97,17 +92,17 @@ public class BluetoothPowerCalculator extends PowerCalculator { systemComponentPowerMah); } - private void calculateApp(UidBatteryConsumer.Builder app) { - calculatePowerAndDuration(app.getBatteryStatsUid().getBluetoothControllerActivity(), - mUidPowerAndDuration); + private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total) { + final BatteryStats.ControllerActivityCounter activityCounter = + app.getBatteryStatsUid().getBluetoothControllerActivity(); + final long durationMs = calculateDuration(activityCounter); + final double powerMah = calculatePower(activityCounter); - app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_BLUETOOTH, - mUidPowerAndDuration.durationMs) - .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, - mUidPowerAndDuration.powerMah); + app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_BLUETOOTH, durationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerMah); - mTotalPowerAndDuration.powerMah += mUidPowerAndDuration.powerMah; - mTotalPowerAndDuration.durationMs += mUidPowerAndDuration.durationMs; + total.durationMs += durationMs; + total.powerMah += powerMah; } @Override @@ -117,20 +112,24 @@ public class BluetoothPowerCalculator extends PowerCalculator { return; } - mTotalPowerAndDuration.durationMs = 0; - mTotalPowerAndDuration.powerMah = 0; + PowerAndDuration total = new PowerAndDuration(); - super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers); + for (int i = sippers.size() - 1; i >= 0; i--) { + final BatterySipper app = sippers.get(i); + if (app.drainType == BatterySipper.DrainType.APP) { + calculateApp(app, app.uidObj, statsType, total); + } + } BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0); - calculatePowerAndDuration(batteryStats.getBluetoothControllerActivity(), - mSystemPowerAndDuration); + final BatteryStats.ControllerActivityCounter activityCounter = + batteryStats.getBluetoothControllerActivity(); + final double systemPowerMah = calculatePower(activityCounter); + final long systemDurationMs = calculateDuration(activityCounter); // Subtract what the apps used, but clamp to 0. - double powerMah = - Math.max(0, mSystemPowerAndDuration.powerMah - mTotalPowerAndDuration.powerMah); - final long durationMs = - Math.max(0, mSystemPowerAndDuration.durationMs - mTotalPowerAndDuration.durationMs); + final double powerMah = Math.max(0, systemPowerMah - total.powerMah); + final long durationMs = Math.max(0, systemDurationMs - total.durationMs); if (DEBUG && powerMah != 0) { Log.d(TAG, "Bluetooth active: time=" + (durationMs) + " power=" + formatCharge(powerMah)); @@ -152,27 +151,43 @@ public class BluetoothPowerCalculator extends PowerCalculator { } } - @Override - protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, - long rawUptimeUs, int statsType) { + private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType, + PowerAndDuration total) { + final BatteryStats.ControllerActivityCounter activityCounter = + u.getBluetoothControllerActivity(); + final long durationMs = calculateDuration(activityCounter); + final double powerMah = calculatePower(activityCounter); - calculatePowerAndDuration(u.getBluetoothControllerActivity(), mUidPowerAndDuration); - - app.bluetoothPowerMah = mUidPowerAndDuration.powerMah; - app.bluetoothRunningTimeMs = mUidPowerAndDuration.durationMs; + app.bluetoothRunningTimeMs = durationMs; + app.bluetoothPowerMah = powerMah; app.btRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_BT_RX_DATA, statsType); app.btTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_BT_TX_DATA, statsType); - mTotalPowerAndDuration.powerMah += mUidPowerAndDuration.powerMah; - mTotalPowerAndDuration.durationMs += mUidPowerAndDuration.durationMs; + total.durationMs += durationMs; + total.powerMah += powerMah; } - private void calculatePowerAndDuration(BatteryStats.ControllerActivityCounter counter, - PowerAndDuration powerAndDuration) { + private long calculateDuration(BatteryStats.ControllerActivityCounter counter) { if (counter == null) { - powerAndDuration.durationMs = 0; - powerAndDuration.powerMah = 0; - return; + return 0; + } + + return counter.getIdleTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED) + + counter.getRxTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED) + + counter.getTxTimeCounters()[0].getCountLocked(BatteryStats.STATS_SINCE_CHARGED); + } + + private double calculatePower(BatteryStats.ControllerActivityCounter counter) { + if (counter == null) { + return 0; + } + + final double powerMah = + counter.getPowerCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED) + / (double) (1000 * 60 * 60); + + if (powerMah != 0) { + return powerMah; } final long idleTimeMs = @@ -181,17 +196,7 @@ public class BluetoothPowerCalculator extends PowerCalculator { counter.getRxTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED); final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(BatteryStats.STATS_SINCE_CHARGED); - final long totalTimeMs = idleTimeMs + txTimeMs + rxTimeMs; - double powerMah = - counter.getPowerCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED) - / (double) (1000 * 60 * 60); - - if (powerMah == 0) { - powerMah = ((idleTimeMs * mIdleMa) + (rxTimeMs * mRxMa) + (txTimeMs * mTxMa)) - / (1000 * 60 * 60); - } - - powerAndDuration.durationMs = totalTimeMs; - powerAndDuration.powerMah = powerMah; + return ((idleTimeMs * mIdleMa) + (rxTimeMs * mRxMa) + (txTimeMs * mTxMa)) + / (1000 * 60 * 60); } } diff --git a/core/java/com/android/internal/os/CameraPowerCalculator.java b/core/java/com/android/internal/os/CameraPowerCalculator.java index 0365d9e9d600..6f8e9271d198 100644 --- a/core/java/com/android/internal/os/CameraPowerCalculator.java +++ b/core/java/com/android/internal/os/CameraPowerCalculator.java @@ -15,7 +15,10 @@ */ package com.android.internal.os; +import android.os.BatteryConsumer; import android.os.BatteryStats; +import android.os.BatteryUsageStatsQuery; +import android.os.UidBatteryConsumer; /** * Power calculator for the camera subsystem, excluding the flashlight. @@ -23,26 +26,33 @@ import android.os.BatteryStats; * Note: Power draw for the flash unit should be included in the FlashlightPowerCalculator. */ public class CameraPowerCalculator extends PowerCalculator { - private final double mCameraPowerOnAvg; + // Calculate camera power usage. Right now, this is a (very) rough estimate based on the + // average power usage for a typical camera application. + private final UsageBasedPowerEstimator mPowerEstimator; public CameraPowerCalculator(PowerProfile profile) { - mCameraPowerOnAvg = profile.getAveragePower(PowerProfile.POWER_CAMERA); + mPowerEstimator = new UsageBasedPowerEstimator( + profile.getAveragePower(PowerProfile.POWER_CAMERA)); } @Override - protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, - long rawUptimeUs, int statsType) { + protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { + final long durationMs = + mPowerEstimator.calculateDuration(u.getCameraTurnedOnTimer(), rawRealtimeUs, + BatteryStats.STATS_SINCE_CHARGED); + final double powerMah = mPowerEstimator.calculatePower(durationMs); + app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CAMERA, durationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA, powerMah); + } - // Calculate camera power usage. Right now, this is a (very) rough estimate based on the - // average power usage for a typical camera application. - final BatteryStats.Timer timer = u.getCameraTurnedOnTimer(); - if (timer != null) { - final long totalTime = timer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000; - app.cameraTimeMs = totalTime; - app.cameraPowerMah = (totalTime * mCameraPowerOnAvg) / (1000*60*60); - } else { - app.cameraTimeMs = 0; - app.cameraPowerMah = 0; - } + @Override + protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, + long rawUptimeUs, int statsType) { + final long durationMs = mPowerEstimator.calculateDuration(u.getCameraTurnedOnTimer(), + rawRealtimeUs, statsType); + final double powerMah = mPowerEstimator.calculatePower(durationMs); + app.cameraTimeMs = durationMs; + app.cameraPowerMah = powerMah; } } diff --git a/core/java/com/android/internal/os/FlashlightPowerCalculator.java b/core/java/com/android/internal/os/FlashlightPowerCalculator.java index 330feef8f117..6c29a91f081f 100644 --- a/core/java/com/android/internal/os/FlashlightPowerCalculator.java +++ b/core/java/com/android/internal/os/FlashlightPowerCalculator.java @@ -15,32 +15,41 @@ */ package com.android.internal.os; +import android.os.BatteryConsumer; import android.os.BatteryStats; +import android.os.BatteryUsageStatsQuery; +import android.os.UidBatteryConsumer; /** * Power calculator for the flashlight. */ public class FlashlightPowerCalculator extends PowerCalculator { - private final double mFlashlightPowerOnAvg; + // Calculate flashlight power usage. Right now, this is based on the average power draw + // of the flash unit when kept on over a short period of time. + private final UsageBasedPowerEstimator mPowerEstimator; public FlashlightPowerCalculator(PowerProfile profile) { - mFlashlightPowerOnAvg = profile.getAveragePower(PowerProfile.POWER_FLASHLIGHT); + mPowerEstimator = new UsageBasedPowerEstimator( + profile.getAveragePower(PowerProfile.POWER_FLASHLIGHT)); } @Override - protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, - long rawUptimeUs, int statsType) { + protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { + final long durationMs = mPowerEstimator.calculateDuration(u.getFlashlightTurnedOnTimer(), + rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); + final double powerMah = mPowerEstimator.calculatePower(durationMs); + app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_FLASHLIGHT, durationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, powerMah); + } - // Calculate flashlight power usage. Right now, this is based on the average power draw - // of the flash unit when kept on over a short period of time. - final BatteryStats.Timer timer = u.getFlashlightTurnedOnTimer(); - if (timer != null) { - final long totalTime = timer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000; - app.flashlightTimeMs = totalTime; - app.flashlightPowerMah = (totalTime * mFlashlightPowerOnAvg) / (1000*60*60); - } else { - app.flashlightTimeMs = 0; - app.flashlightPowerMah = 0; - } + @Override + protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, + long rawUptimeUs, int statsType) { + final long durationMs = mPowerEstimator.calculateDuration(u.getFlashlightTurnedOnTimer(), + rawRealtimeUs, statsType); + final double powerMah = mPowerEstimator.calculatePower(durationMs); + app.flashlightTimeMs = durationMs; + app.flashlightPowerMah = powerMah; } } diff --git a/core/java/com/android/internal/os/IdlePowerCalculator.java b/core/java/com/android/internal/os/IdlePowerCalculator.java index 44ad34417a4c..dcc8a15b2f50 100644 --- a/core/java/com/android/internal/os/IdlePowerCalculator.java +++ b/core/java/com/android/internal/os/IdlePowerCalculator.java @@ -16,7 +16,11 @@ package com.android.internal.os; +import android.os.BatteryConsumer; import android.os.BatteryStats; +import android.os.BatteryUsageStats; +import android.os.BatteryUsageStatsQuery; +import android.os.SystemBatteryConsumer; import android.os.UserHandle; import android.util.Log; import android.util.SparseArray; @@ -29,46 +33,70 @@ import java.util.List; public class IdlePowerCalculator extends PowerCalculator { private static final String TAG = "IdlePowerCalculator"; private static final boolean DEBUG = BatteryStatsHelper.DEBUG; - private final PowerProfile mPowerProfile; + private final double mAveragePowerCpuSuspendMahPerUs; + private final double mAveragePowerCpuIdleMahPerUs; + public long mDurationMs; + public double mPowerMah; public IdlePowerCalculator(PowerProfile powerProfile) { - mPowerProfile = powerProfile; + mAveragePowerCpuSuspendMahPerUs = + powerProfile.getAveragePower(PowerProfile.POWER_CPU_SUSPEND) + / (60 * 60 * 1_000_000.0); + mAveragePowerCpuIdleMahPerUs = + powerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE) + / (60 * 60 * 1_000_000.0); + } + + @Override + public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query, + SparseArray<UserHandle> asUsers) { + calculatePowerAndDuration(batteryStats, rawRealtimeUs, rawUptimeUs, + BatteryStats.STATS_SINCE_CHARGED); + if (mPowerMah != 0) { + builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_IDLE) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, mPowerMah) + .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, mDurationMs); + } + } + + @Override + public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { + calculatePowerAndDuration(batteryStats, rawRealtimeUs, rawUptimeUs, statsType); + + if (mPowerMah != 0) { + BatterySipper bs = new BatterySipper(BatterySipper.DrainType.IDLE, null, 0); + bs.usagePowerMah = mPowerMah; + bs.usageTimeMs = mDurationMs; + bs.sumPower(); + sippers.add(bs); + } } /** - * Calculate the baseline power usage for the device when it is in suspend and idle. + * Calculates the baseline power usage for the device when it is in suspend and idle. * The device is drawing POWER_CPU_SUSPEND power at its lowest power state. * The device is drawing POWER_CPU_SUSPEND + POWER_CPU_IDLE power when a wakelock is held. */ - @Override - public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, - long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { - long batteryUptimeUs = batteryStats.computeBatteryUptime(rawUptimeUs, statsType); + private void calculatePowerAndDuration(BatteryStats batteryStats, long rawRealtimeUs, + long rawUptimeUs, int statsType) { long batteryRealtimeUs = batteryStats.computeBatteryRealtime(rawRealtimeUs, statsType); - + long batteryUptimeUs = batteryStats.computeBatteryUptime(rawUptimeUs, statsType); if (DEBUG) { Log.d(TAG, "Battery type time: realtime=" + (batteryRealtimeUs / 1000) + " uptime=" + (batteryUptimeUs / 1000)); } - final double suspendPowerMaMs = (batteryRealtimeUs / 1000) - * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_SUSPEND); - final double idlePowerMaMs = (batteryUptimeUs / 1000) - * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE); - final double totalPowerMah = (suspendPowerMaMs + idlePowerMaMs) / (60 * 60 * 1000); - if (DEBUG && totalPowerMah != 0) { + final double suspendPowerMah = batteryRealtimeUs * mAveragePowerCpuSuspendMahPerUs; + final double idlePowerMah = batteryUptimeUs * mAveragePowerCpuIdleMahPerUs; + mPowerMah = suspendPowerMah + idlePowerMah; + if (DEBUG && mPowerMah != 0) { Log.d(TAG, "Suspend: time=" + (batteryRealtimeUs / 1000) - + " power=" + formatCharge(suspendPowerMaMs / (60 * 60 * 1000))); + + " power=" + formatCharge(suspendPowerMah)); Log.d(TAG, "Idle: time=" + (batteryUptimeUs / 1000) - + " power=" + formatCharge(idlePowerMaMs / (60 * 60 * 1000))); - } - - if (totalPowerMah != 0) { - BatterySipper bs = new BatterySipper(BatterySipper.DrainType.IDLE, null, 0); - bs.usagePowerMah = totalPowerMah; - bs.usageTimeMs = batteryRealtimeUs / 1000; - bs.sumPower(); - sippers.add(bs); + + " power=" + formatCharge(idlePowerMah)); } + mDurationMs = batteryRealtimeUs / 1000; } } diff --git a/core/java/com/android/internal/os/MemoryPowerCalculator.java b/core/java/com/android/internal/os/MemoryPowerCalculator.java index 10d9b6519992..df4605838b28 100644 --- a/core/java/com/android/internal/os/MemoryPowerCalculator.java +++ b/core/java/com/android/internal/os/MemoryPowerCalculator.java @@ -1,64 +1,75 @@ package com.android.internal.os; +import android.os.BatteryConsumer; import android.os.BatteryStats; +import android.os.BatteryUsageStats; +import android.os.BatteryUsageStatsQuery; +import android.os.SystemBatteryConsumer; import android.os.UserHandle; -import android.util.Log; import android.util.LongSparseArray; import android.util.SparseArray; import java.util.List; public class MemoryPowerCalculator extends PowerCalculator { - public static final String TAG = "MemoryPowerCalculator"; - private static final boolean DEBUG = BatteryStatsHelper.DEBUG; - private final double[] powerAverages; + private final UsageBasedPowerEstimator[] mPowerEstimators; public MemoryPowerCalculator(PowerProfile profile) { int numBuckets = profile.getNumElements(PowerProfile.POWER_MEMORY); - powerAverages = new double[numBuckets]; + mPowerEstimators = new UsageBasedPowerEstimator[numBuckets]; for (int i = 0; i < numBuckets; i++) { - powerAverages[i] = profile.getAveragePower(PowerProfile.POWER_MEMORY, i); - if (powerAverages[i] == 0 && DEBUG) { - Log.d(TAG, "Problem with PowerProfile. Received 0 value in MemoryPowerCalculator"); - } + mPowerEstimators[i] = new UsageBasedPowerEstimator( + profile.getAveragePower(PowerProfile.POWER_MEMORY, i)); } } @Override + public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query, + SparseArray<UserHandle> asUsers) { + final long durationMs = calculateDuration(batteryStats, rawRealtimeUs, + BatteryStats.STATS_SINCE_CHARGED); + final double powerMah = calculatePower(batteryStats, rawRealtimeUs, + BatteryStats.STATS_SINCE_CHARGED); + builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_MEMORY) + .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah); + } + + @Override public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { + final long durationMs = calculateDuration(batteryStats, rawRealtimeUs, statsType); + final double powerMah = calculatePower(batteryStats, rawRealtimeUs, statsType); BatterySipper memory = new BatterySipper(BatterySipper.DrainType.MEMORY, null, 0); - calculateRemaining(memory, batteryStats, rawRealtimeUs, rawUptimeUs, statsType); + memory.usageTimeMs = durationMs; + memory.usagePowerMah = powerMah; memory.sumPower(); if (memory.totalPowerMah > 0) { sippers.add(memory); } } - private void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, - long rawUptimeUs, int statsType) { - double totalMah = 0; - long totalTimeMs = 0; - LongSparseArray<? extends BatteryStats.Timer> timers = stats.getKernelMemoryStats(); - for (int i = 0; i < timers.size() && i < powerAverages.length; i++) { - double mAatRail = powerAverages[(int) timers.keyAt(i)]; - long timeMs = timers.valueAt(i).getTotalTimeLocked(rawRealtimeUs, statsType); - double mAm = (mAatRail * timeMs) / (1000*60); - if(DEBUG) { - Log.d(TAG, "Calculating mAh for bucket " + timers.keyAt(i) + " while unplugged"); - Log.d(TAG, "Converted power profile number from " - + powerAverages[(int) timers.keyAt(i)] + " into " + mAatRail); - Log.d(TAG, "Calculated mAm " + mAm); - } - totalMah += mAm/60; - totalTimeMs += timeMs; + private long calculateDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType) { + long usageDurationMs = 0; + LongSparseArray<? extends BatteryStats.Timer> timers = batteryStats.getKernelMemoryStats(); + for (int i = 0; i < timers.size() && i < mPowerEstimators.length; i++) { + usageDurationMs += mPowerEstimators[i].calculateDuration(timers.valueAt(i), + rawRealtimeUs, statsType); } - app.usagePowerMah = totalMah; - app.usageTimeMs = totalTimeMs; - if (DEBUG) { - Log.d(TAG, String.format("Calculated total mAh for memory %f while unplugged %d ", - totalMah, totalTimeMs)); + return usageDurationMs; + } + + private double calculatePower(BatteryStats batteryStats, long rawRealtimeUs, int statsType) { + double powerMah = 0; + LongSparseArray<? extends BatteryStats.Timer> timers = batteryStats.getKernelMemoryStats(); + for (int i = 0; i < timers.size() && i < mPowerEstimators.length; i++) { + UsageBasedPowerEstimator estimator = mPowerEstimators[(int) timers.keyAt(i)]; + final long usageDurationMs = + estimator.calculateDuration(timers.valueAt(i), rawRealtimeUs, statsType); + powerMah += estimator.calculatePower(usageDurationMs); } + return powerMah; } } diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS index 8f78b2a3a5ea..1b07aa0cf0b7 100644 --- a/core/java/com/android/internal/os/OWNERS +++ b/core/java/com/android/internal/os/OWNERS @@ -6,3 +6,5 @@ per-file BatterySipper.java = file:/BATTERY_STATS_OWNERS per-file BatteryStats* = file:/BATTERY_STATS_OWNERS per-file BatteryUsageStats* = file:/BATTERY_STATS_OWNERS per-file *PowerCalculator* = file:/BATTERY_STATS_OWNERS +per-file *PowerEstimator* = file:/BATTERY_STATS_OWNERS + diff --git a/core/java/com/android/internal/os/PhonePowerCalculator.java b/core/java/com/android/internal/os/PhonePowerCalculator.java index 992c487a9a66..6ab8c90d21d9 100644 --- a/core/java/com/android/internal/os/PhonePowerCalculator.java +++ b/core/java/com/android/internal/os/PhonePowerCalculator.java @@ -30,20 +30,20 @@ import java.util.List; * Estimates power consumed by telephony. */ public class PhonePowerCalculator extends PowerCalculator { - private final PowerProfile mPowerProfile; + private final UsageBasedPowerEstimator mPowerEstimator; public PhonePowerCalculator(PowerProfile powerProfile) { - mPowerProfile = powerProfile; + mPowerEstimator = new UsageBasedPowerEstimator( + powerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE)); } @Override public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query, SparseArray<UserHandle> asUsers) { - long phoneOnTimeMs = batteryStats.getPhoneOnTime(rawRealtimeUs, + final long phoneOnTimeMs = batteryStats.getPhoneOnTime(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED) / 1000; - double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) - * phoneOnTimeMs / (60 * 60 * 1000); + final double phoneOnPower = mPowerEstimator.calculatePower(phoneOnTimeMs); if (phoneOnPower != 0) { builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_PHONE) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, phoneOnPower) @@ -54,9 +54,8 @@ public class PhonePowerCalculator extends PowerCalculator { @Override public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { - long phoneOnTimeMs = batteryStats.getPhoneOnTime(rawRealtimeUs, statsType) / 1000; - double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) - * phoneOnTimeMs / (60 * 60 * 1000); + final long phoneOnTimeMs = batteryStats.getPhoneOnTime(rawRealtimeUs, statsType) / 1000; + final double phoneOnPower = mPowerEstimator.calculatePower(phoneOnTimeMs); if (phoneOnPower != 0) { BatterySipper bs = new BatterySipper(BatterySipper.DrainType.PHONE, null, 0); bs.usagePowerMah = phoneOnPower; diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java index 09fb75bc2d2d..25f6b4d16971 100644 --- a/core/java/com/android/internal/os/ScreenPowerCalculator.java +++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java @@ -16,7 +16,11 @@ package com.android.internal.os; +import android.os.BatteryConsumer; import android.os.BatteryStats; +import android.os.BatteryUsageStats; +import android.os.BatteryUsageStatsQuery; +import android.os.SystemBatteryConsumer; import android.os.UserHandle; import android.util.Log; import android.util.SparseArray; @@ -30,10 +34,29 @@ public class ScreenPowerCalculator extends PowerCalculator { private static final String TAG = "ScreenPowerCalculator"; private static final boolean DEBUG = BatteryStatsHelper.DEBUG; - private final PowerProfile mPowerProfile; + private final UsageBasedPowerEstimator mScreenOnPowerEstimator; + private final UsageBasedPowerEstimator mScreenFullPowerEstimator; public ScreenPowerCalculator(PowerProfile powerProfile) { - mPowerProfile = powerProfile; + mScreenOnPowerEstimator = new UsageBasedPowerEstimator( + powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON)); + mScreenFullPowerEstimator = new UsageBasedPowerEstimator( + powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL)); + } + + @Override + public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query, + SparseArray<UserHandle> asUsers) { + final long durationMs = computeDuration(batteryStats, rawRealtimeUs, + BatteryStats.STATS_SINCE_CHARGED); + final double powerMah = computePower(batteryStats, rawRealtimeUs, + BatteryStats.STATS_SINCE_CHARGED, durationMs); + if (powerMah != 0) { + builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_SCREEN) + .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah); + } } /** @@ -42,30 +65,35 @@ public class ScreenPowerCalculator extends PowerCalculator { @Override public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { - double power = 0; - final long screenOnTimeMs = batteryStats.getScreenOnTime(rawRealtimeUs, statsType) / 1000; - power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON); - final double screenFullPower = - mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL); + final long durationMs = computeDuration(batteryStats, rawRealtimeUs, statsType); + final double powerMah = computePower(batteryStats, rawRealtimeUs, statsType, durationMs); + if (powerMah != 0) { + final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.SCREEN, null, 0); + bs.usagePowerMah = powerMah; + bs.usageTimeMs = durationMs; + bs.sumPower(); + sippers.add(bs); + } + } + + private long computeDuration(BatteryStats batteryStats, long rawRealtimeUs, int statsType) { + return batteryStats.getScreenOnTime(rawRealtimeUs, statsType) / 1000; + } + + private double computePower(BatteryStats batteryStats, long rawRealtimeUs, int statsType, + long durationMs) { + double power = mScreenOnPowerEstimator.calculatePower(durationMs); for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) { - final double screenBinPower = screenFullPower * (i + 0.5f) - / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; final long brightnessTime = batteryStats.getScreenBrightnessTime(i, rawRealtimeUs, statsType) / 1000; - final double p = screenBinPower * brightnessTime; - if (DEBUG && p != 0) { + final double binPowerMah = mScreenFullPowerEstimator.calculatePower(brightnessTime) + * (i + 0.5f) / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; + if (DEBUG && binPowerMah != 0) { Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime - + " power=" + formatCharge(p / (60 * 60 * 1000))); + + " power=" + formatCharge(binPowerMah)); } - power += p; - } - power /= (60 * 60 * 1000); // To hours - if (power != 0) { - final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.SCREEN, null, 0); - bs.usagePowerMah = power; - bs.usageTimeMs = screenOnTimeMs; - bs.sumPower(); - sippers.add(bs); + power += binPowerMah; } + return power; } } diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java index b15dff67b619..55fc1bb607a9 100644 --- a/core/java/com/android/internal/os/SystemServicePowerCalculator.java +++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java @@ -16,7 +16,11 @@ package com.android.internal.os; +import android.os.BatteryConsumer; import android.os.BatteryStats; +import android.os.BatteryUsageStats; +import android.os.BatteryUsageStatsQuery; +import android.os.UidBatteryConsumer; import android.os.UserHandle; import android.util.Log; import android.util.SparseArray; @@ -46,28 +50,35 @@ public class SystemServicePowerCalculator extends PowerCalculator { } @Override + public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query, + SparseArray<UserHandle> asUsers) { + calculateSystemServicePower(batteryStats); + super.calculate(builder, batteryStats, rawRealtimeUs, rawUptimeUs, query, asUsers); + } + + @Override + protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { + app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES, + calculateSystemServerCpuPowerMah(u)); + } + + @Override public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { - updateSystemServicePower(batteryStats); + calculateSystemServicePower(batteryStats); super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers); } @Override protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) { - final double proportionalUsage = u.getProportionalSystemServiceUsage(); - if (proportionalUsage > 0 && mSystemServicePowerMaUs != null) { - double cpuPowerMaUs = 0; - for (int i = 0; i < mSystemServicePowerMaUs.length; i++) { - cpuPowerMaUs += mSystemServicePowerMaUs[i] * proportionalUsage; - } - - app.systemServiceCpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR; - } + app.systemServiceCpuPowerMah = calculateSystemServerCpuPowerMah(u); } - private void updateSystemServicePower(BatteryStats batteryStats) { + private void calculateSystemServicePower(BatteryStats batteryStats) { final long[] systemServiceTimeAtCpuSpeeds = batteryStats.getSystemServiceTimeAtCpuSpeeds(); if (systemServiceTimeAtCpuSpeeds == null) { return; @@ -94,6 +105,17 @@ public class SystemServicePowerCalculator extends PowerCalculator { } } + private double calculateSystemServerCpuPowerMah(BatteryStats.Uid u) { + double cpuPowerMaUs = 0; + final double proportionalUsage = u.getProportionalSystemServiceUsage(); + if (proportionalUsage > 0 && mSystemServicePowerMaUs != null) { + for (int i = 0; i < mSystemServicePowerMaUs.length; i++) { + cpuPowerMaUs += mSystemServicePowerMaUs[i] * proportionalUsage; + } + } + return cpuPowerMaUs / MICROSEC_IN_HR; + } + @Override public void reset() { mSystemServicePowerMaUs = null; diff --git a/core/java/com/android/internal/os/UsageBasedPowerEstimator.java b/core/java/com/android/internal/os/UsageBasedPowerEstimator.java new file mode 100644 index 000000000000..5910b61f0f6f --- /dev/null +++ b/core/java/com/android/internal/os/UsageBasedPowerEstimator.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 android.os.BatteryStats; + +/** + * Implements a simple linear power model based on the assumption that the power consumer + * consumes a fixed current when it is used and no current when it is unused. + * + * <code>power = usageDuration * averagePower</code> + */ +public class UsageBasedPowerEstimator { + private static final double MILLIS_IN_HOUR = 1000.0 * 60 * 60; + private final double mAveragePowerMahPerMs; + + public UsageBasedPowerEstimator(double averagePowerMilliAmp) { + mAveragePowerMahPerMs = averagePowerMilliAmp / MILLIS_IN_HOUR; + } + + /** + * Given a {@link BatteryStats.Timer}, returns the accumulated duration. + */ + public long calculateDuration(BatteryStats.Timer timer, long rawRealtimeUs, int statsType) { + return timer == null ? 0 : timer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000; + } + + /** + * Given a duration in milliseconds, return the estimated power consumption. + */ + public double calculatePower(long durationMs) { + return mAveragePowerMahPerMs * durationMs; + } +} diff --git a/core/java/com/android/internal/os/VideoPowerCalculator.java b/core/java/com/android/internal/os/VideoPowerCalculator.java new file mode 100644 index 000000000000..5d6caf578475 --- /dev/null +++ b/core/java/com/android/internal/os/VideoPowerCalculator.java @@ -0,0 +1,45 @@ +/* + * 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.internal.os; + +import android.os.BatteryConsumer; +import android.os.BatteryStats; +import android.os.BatteryUsageStatsQuery; +import android.os.UidBatteryConsumer; + +/** + * A {@link PowerCalculator} to calculate power consumed by video hardware. + * + * Also see {@link PowerProfile#POWER_VIDEO}. + */ +public class VideoPowerCalculator extends PowerCalculator { + private final UsageBasedPowerEstimator mPowerEstimator; + + public VideoPowerCalculator(PowerProfile powerProfile) { + mPowerEstimator = new UsageBasedPowerEstimator( + powerProfile.getAveragePower(PowerProfile.POWER_VIDEO)); + } + + @Override + protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { + final long durationMs = mPowerEstimator.calculateDuration(u.getVideoTurnedOnTimer(), + rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); + final double powerMah = mPowerEstimator.calculatePower(durationMs); + app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO, durationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_VIDEO, powerMah); + } +} diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 8c9da66ffb8a..94ef64f43ce2 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -347,12 +347,6 @@ cc_library_shared { }, }, - product_variables: { - experimental_mte: { - cflags: ["-DANDROID_EXPERIMENTAL_MTE"], - }, - }, - // Workaround Clang LTO crash. lto: { never: true, diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index efede2181628..3156f71fa113 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -843,7 +843,7 @@ static void MountEmulatedStorage(uid_t uid, jint mount_mode, PrepareDir(user_source, 0710, user_id ? AID_ROOT : AID_SHELL, multiuser_get_uid(user_id, AID_EVERYBODY), fail_fn); - bool isAppDataIsolationEnabled = GetBoolProperty(kVoldAppDataIsolation, true); + bool isAppDataIsolationEnabled = GetBoolProperty(kVoldAppDataIsolation, false); if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH) { const std::string pass_through_source = StringPrintf("/mnt/pass_through/%d", user_id); diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index 8de30f8547a7..944edb0ff750 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -521,6 +521,11 @@ message IncidentProto { (section).args = "power_stats --proto model" ]; + optional com.android.server.powerstats.PowerStatsServiceResidencyProto powerstats_residency = 3056 [ + (section).type = SECTION_DUMPSYS, + (section).args = "power_stats --proto residency" + ]; + // Dumps in text format (on userdebug and eng builds only): 4000 ~ 4999 optional android.util.TextDumpProto textdump_wifi = 4000 [ (section).type = SECTION_TEXT_DUMPSYS, diff --git a/core/proto/android/server/powerstatsservice.proto b/core/proto/android/server/powerstatsservice.proto index 9a7ed7cbe98f..30c427433639 100644 --- a/core/proto/android/server/powerstatsservice.proto +++ b/core/proto/android/server/powerstatsservice.proto @@ -41,6 +41,16 @@ message IncidentReportModelProto { } /** + * IncidentReportResidencyProto is used only in the parsing tool located + * in frameworks/base/tools which is used to parse this data out of + * incident reports. + */ +message IncidentReportResidencyProto { + /** Section number matches that in incident.proto */ + optional PowerStatsServiceResidencyProto incident_report = 3056; +} + +/** * EnergyConsumer (model) data is exposed by the PowerStats HAL. This data * represents modeled energy consumption estimates and is provided per * subsystem. The default subsystems are defined in EnergyConsumerId.aidl. @@ -63,6 +73,99 @@ message PowerStatsServiceMeterProto { } /** + * A PowerEntity is defined as a platform subsystem, peripheral, or power domain + * that impacts the total device power consumption. PowerEntityInfo is + * information related to each power entity. 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. + */ +message PowerStatsServiceResidencyProto { + repeated PowerEntityInfoProto power_entity_info = 1; + repeated StateResidencyResultProto state_residency_result = 2; +} + +/** + * Information about the possible states for a particular PowerEntity. + */ +message StateInfoProto { + /** + * Unique (for a given PowerEntityInfo) ID of this StateInfo + */ + optional int32 state_id = 1; + /** + * Unique (for a given PowerEntityInfo) name of the state. Vendor/device specific. + * Opaque to framework + */ + optional string state_name = 2; +} + +/** + * A PowerEntity is defined as a platform subsystem, peripheral, or power domain + * that impacts the total device power consumption. PowerEntityInfo is + * information about a PowerEntity. It includes an array of information about + * each possible state of the PowerEntity. + */ +message PowerEntityInfoProto { + /** + * Unique ID of this PowerEntityInfo + */ + optional int32 power_entity_id = 1; + /** + * Unique name of the PowerEntity. Vendor/device specific. Opaque to framework + */ + optional string power_entity_name = 2; + /** + * List of states that the PowerEntity may reside in + */ + repeated StateInfoProto states = 3; +} + +/** + * 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. + */ +message StateResidencyProto { + /** + * ID of the state associated with this residency + */ + optional int32 state_id = 1; + /** + * Total time in milliseconds that the corresponding PowerEntity resided + * in this state since boot + */ + optional int64 total_time_in_state_ms = 2; + /** + * Total number of times that the state was entered since boot + */ + optional int64 total_state_entry_count = 3; + /** + * Last time this state was entered. Time in milliseconds since boot + */ + optional int64 last_entry_timestamp_ms = 4; +} + +/** + * A StateResidencyResult is an array of StateResidencies for a particular + * PowerEntity. The StateResidencyResult can be matched to its corresponding + * PowerEntityInfo through the power_entity_id field. + */ +message StateResidencyResultProto { + /** + * ID of the PowerEntity associated with this result + */ + optional int32 power_entity_id = 1; + /** + * Residency for each state in the PowerEntity's state space + */ + repeated StateResidencyProto state_residency_data = 2; +} + +/** * Energy consumer ID: * A list of default subsystems for which energy consumption estimates * may be provided (hardware dependent). diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 8682fea1f8dc..ce3ed9dc8ba6 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2676,11 +2676,11 @@ The app can check whether it has this authorization by calling {@link android.provider.Settings#canDrawOverlays Settings.canDrawOverlays()}. - <p>Protection level: signature|preinstalled|appop|pre23|development --> + <p>Protection level: signature|appop|installer|pre23|development --> <permission android:name="android.permission.SYSTEM_ALERT_WINDOW" android:label="@string/permlab_systemAlertWindow" android:description="@string/permdesc_systemAlertWindow" - android:protectionLevel="signature|preinstalled|appop|pre23|development" /> + android:protectionLevel="signature|appop|installer|pre23|development" /> <!-- @deprecated Use {@link android.Manifest.permission#REQUEST_COMPANION_RUN_IN_BACKGROUND} @hide @@ -3591,6 +3591,14 @@ <permission android:name="android.permission.BIND_CONTENT_CAPTURE_SERVICE" android:protectionLevel="signature" /> + <!-- Must be required by a android.service.translation.TranslationService, + to ensure that only the system can bind to it. + @SystemApi @hide This is not a third-party API (intended for OEMs and system apps). + <p>Protection level: signature + --> + <permission android:name="android.permission.BIND_TRANSLATION_SERVICE" + android:protectionLevel="signature" /> + <!-- Must be required by a android.service.contentsuggestions.ContentSuggestionsService, to ensure that only the system can bind to it. @SystemApi @hide This is not a third-party API (intended for OEMs and system apps). @@ -5317,6 +5325,12 @@ <permission android:name="android.permission.BIND_IMPRESSION_ATTESTATION_SERVICE" android:protectionLevel="signature" /> + <!-- @hide @TestApi Allows an application to enable/disable toast rate limiting. + <p>Not for use by third-party applications. + --> + <permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING" + android:protectionLevel="signature" /> + <!-- Attribution for Geofencing service. --> <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/> <!-- Attribution for Country Detector. --> diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml index 000638475a10..41be36bc2b04 100644 --- a/core/res/res/layout/notification_template_material_base.xml +++ b/core/res/res/layout/notification_template_material_base.xml @@ -45,6 +45,45 @@ android:padding="@dimen/notification_icon_circle_padding" /> + <ImageView + android:id="@+id/right_icon" + android:layout_width="@dimen/notification_right_icon_size" + android:layout_height="@dimen/notification_right_icon_size" + android:layout_gravity="center_vertical|end" + android:layout_marginTop="@dimen/notification_right_icon_headerless_margin" + android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin" + android:layout_marginEnd="@dimen/notification_header_expand_icon_size" + android:background="@drawable/notification_large_icon_outline" + android:importantForAccessibility="no" + android:scaleType="centerCrop" + /> + + <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" + android:layout_gravity="end"> + + <com.android.internal.widget.NotificationExpandButton + android:id="@+id/expand_button" + android:layout_width="@dimen/notification_header_expand_icon_size" + android:layout_height="@dimen/notification_header_expand_icon_size" + android:layout_gravity="center_vertical|end" + android:contentDescription="@string/expand_button_content_description_collapsed" + android:paddingTop="@dimen/notification_expand_button_padding_top" + android:scaleType="center" + /> + + </FrameLayout> + <LinearLayout android:id="@+id/notification_headerless_view_column" android:layout_width="match_parent" @@ -64,6 +103,7 @@ variant is 56dp and the 2- and 3-line variants are both 76dp. --> <FrameLayout + android:id="@+id/notification_headerless_margin_extra_top" android:layout_width="match_parent" android:layout_height="@dimen/notification_headerless_margin_extra" android:layout_weight="1" @@ -135,6 +175,7 @@ variant is 56dp and the 2- and 3-line variants are both 76dp. --> <FrameLayout + android:id="@+id/notification_headerless_margin_extra_bottom" android:layout_width="match_parent" android:layout_height="@dimen/notification_headerless_margin_extra" android:layout_weight="1" @@ -142,43 +183,4 @@ </LinearLayout> - <ImageView - android:id="@+id/right_icon" - android:layout_width="@dimen/notification_right_icon_size" - android:layout_height="@dimen/notification_right_icon_size" - android:layout_gravity="center_vertical|end" - android:layout_marginTop="@dimen/notification_right_icon_headerless_margin" - android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin" - android:layout_marginEnd="@dimen/notification_header_expand_icon_size" - android:background="@drawable/notification_large_icon_outline" - android:importantForAccessibility="no" - android:scaleType="centerCrop" - /> - - <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" - android:layout_gravity="end"> - - <com.android.internal.widget.NotificationExpandButton - android:id="@+id/expand_button" - android:layout_width="@dimen/notification_header_expand_icon_size" - android:layout_height="@dimen/notification_header_expand_icon_size" - android:layout_gravity="center_vertical|end" - android:contentDescription="@string/expand_button_content_description_collapsed" - android:paddingTop="@dimen/notification_expand_button_padding_top" - android:scaleType="center" - /> - - </FrameLayout> - </com.android.internal.widget.NotificationMaxHeightFrameLayout> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index 0721f8eeea35..3adb318dd8b0 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -100,7 +100,7 @@ <string name="peerTtyModeHco" msgid="5626377160840915617">"নেটৱৰ্ক পীয়েৰে TTY ম\'ড HCOলৈ সলনি কৰিবলৈ অনুৰোধ কৰিছে"</string> <string name="peerTtyModeVco" msgid="572208600818270944">"নেটৱৰ্ক পীয়েৰে TTY ম\'ড VCO লৈ সলনি কৰিবলৈ অনুৰোধ কৰিছে"</string> <string name="peerTtyModeOff" msgid="2420380956369226583">"নেটৱৰ্ক পীয়েৰে TTY ম\'ড OFFলৈ সলনি কৰিবলৈ অনুৰোধ কৰিছে"</string> - <string name="serviceClassVoice" msgid="2065556932043454987">"কণ্ঠস্বৰ"</string> + <string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string> <string name="serviceClassData" msgid="4148080018967300248">"ডেটা"</string> <string name="serviceClassFAX" msgid="2561653371698904118">"ফেক্স"</string> <string name="serviceClassSMS" msgid="1547664561704509004">"এছএমএছ"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 967bb9278f10..016e77b5970f 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -1319,7 +1319,7 @@ <string name="usb_power_notification_message" msgid="7284765627437897702">"કનેક્ટ કરેલ ઉપકરણ ચાર્જ થઈ રહ્યું છે. વધુ વિકલ્પો માટે ટૅપ કરો."</string> <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_title" msgid="408390247354560331">"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> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 0df69d9e5738..a8afbbecf0c2 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -100,7 +100,7 @@ <string name="peerTtyModeHco" msgid="5626377160840915617">"पीयर ने टेलीटाइपराइटर (TTY) मोड एचसीओ (HCO) का अनुरोध किया"</string> <string name="peerTtyModeVco" msgid="572208600818270944">"पीयर ने टेलीटाइपराइटर (TTY) मोड वीसीओ (VCO) का अनुरोध किया"</string> <string name="peerTtyModeOff" msgid="2420380956369226583">"पीयर ने टेलीटाइपराइटर (TTY) मोड बंद का अनुरोध किया"</string> - <string name="serviceClassVoice" msgid="2065556932043454987">"आवाज़"</string> + <string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string> <string name="serviceClassData" msgid="4148080018967300248">"डेटा"</string> <string name="serviceClassFAX" msgid="2561653371698904118">"फ़ैक्स"</string> <string name="serviceClassSMS" msgid="1547664561704509004">"मैसेज (एसएमएस)"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index 937645ea0f05..b7356e175c6b 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -100,7 +100,7 @@ <string name="peerTtyModeHco" msgid="5626377160840915617">"ಪೀರ್ ವಿನಂತಿಸಿಕೊಂಡ TTY ಮೋಡ್ HCO"</string> <string name="peerTtyModeVco" msgid="572208600818270944">"ಪೀರ್ ವಿನಂತಿಸಿಕೊಂಡ TTY ಮೋಡ್ VCO"</string> <string name="peerTtyModeOff" msgid="2420380956369226583">"ಪೀರ್ ವಿನಂತಿಸಿಕೊಂಡ TTY ಮೋಡ್ ಆಫ್ ಆಗಿದೆ"</string> - <string name="serviceClassVoice" msgid="2065556932043454987">"ಧ್ವನಿ"</string> + <string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string> <string name="serviceClassData" msgid="4148080018967300248">"ಡೇಟಾ"</string> <string name="serviceClassFAX" msgid="2561653371698904118">"ಫ್ಯಾಕ್ಸ್"</string> <string name="serviceClassSMS" msgid="1547664561704509004">"SMS"</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index e7a16282bf46..30c0d03350fd 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -100,7 +100,7 @@ <string name="peerTtyModeHco" msgid="5626377160840915617">"പിയർ അഭ്യർത്ഥിച്ച TTY മോഡ് HCO"</string> <string name="peerTtyModeVco" msgid="572208600818270944">"പിയർ അഭ്യർത്ഥിച്ച TTY മോഡ് VCO"</string> <string name="peerTtyModeOff" msgid="2420380956369226583">"പിയർ അഭ്യർത്ഥിച്ച TTY മോഡ് \'ഓഫ്\'"</string> - <string name="serviceClassVoice" msgid="2065556932043454987">"ശബ്ദം"</string> + <string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string> <string name="serviceClassData" msgid="4148080018967300248">"ഡാറ്റ"</string> <string name="serviceClassFAX" msgid="2561653371698904118">"ഫാക്സ്"</string> <string name="serviceClassSMS" msgid="1547664561704509004">"SMS"</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index b5935e97b238..2124d49474b4 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -100,7 +100,7 @@ <string name="peerTtyModeHco" msgid="5626377160840915617">"ପୀଅର୍ ଅନୁରୋଧ କରିଥିବା TTY ମୋଡ୍ HCO ଅଟେ"</string> <string name="peerTtyModeVco" msgid="572208600818270944">"ପୀଅର୍ ଅନୁରୋଧ କରିଥିବା TTY ମୋଡ୍ VCO ଅଟେ"</string> <string name="peerTtyModeOff" msgid="2420380956369226583">"ପୀଅର୍ ଅନୁରୋଧ କରିଥିବା TTY ମୋଡ୍ OFF ଅଛି"</string> - <string name="serviceClassVoice" msgid="2065556932043454987">"ଭଏସ୍"</string> + <string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string> <string name="serviceClassData" msgid="4148080018967300248">"ଡାଟା"</string> <string name="serviceClassFAX" msgid="2561653371698904118">"ଫାକ୍ସ"</string> <string name="serviceClassSMS" msgid="1547664561704509004">"SMS"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 8d5e974a23b2..4518a3bfe535 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -1319,7 +1319,7 @@ <string name="usb_power_notification_message" msgid="7284765627437897702">"ਕਨੈਕਟ ਕੀਤਾ ਡੀਵਾਈਸ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ। ਹੋਰ ਵਿਕਲਪਾਂ ਲਈ ਟੈਪ ਕਰੋ।"</string> <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_title" msgid="408390247354560331">"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> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 0f5d000bc635..6483db1ea8cb 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -100,7 +100,7 @@ <string name="peerTtyModeHco" msgid="5626377160840915617">"అవతలి వారు HCO TTY మోడ్ని అభ్యర్థించారు"</string> <string name="peerTtyModeVco" msgid="572208600818270944">"అవతలి వారు VCO TTY మోడ్ని అభ్యర్థించారు"</string> <string name="peerTtyModeOff" msgid="2420380956369226583">"అవతలి వారు OFF TTY మోడ్ని అభ్యర్థించారు"</string> - <string name="serviceClassVoice" msgid="2065556932043454987">"వాయిస్"</string> + <string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string> <string name="serviceClassData" msgid="4148080018967300248">"డేటా"</string> <string name="serviceClassFAX" msgid="2561653371698904118">"ఫ్యాక్స్"</string> <string name="serviceClassSMS" msgid="1547664561704509004">"SMS"</string> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 7ca3fafdca47..ef54db1a422c 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -8292,6 +8292,23 @@ </declare-styleable> <!-- =============================== --> + <!-- Translation attributes --> + <!-- =============================== --> + <eat-comment /> + + <!-- Use <code>translation-service</code> as the root tag of the XML resource that describes + a {@link android.service.translation.TranslationService}, which is referenced from + its {@link android.service.translation.TranslationService#SERVICE_META_DATA} meta-data + entry. + @hide @SystemApi + --> + <declare-styleable name="TranslationService"> + <!-- Fully qualified class name of an activity that allows the user to modify + the settings for this service. --> + <attr name="settingsActivity" /> + </declare-styleable> + + <!-- =============================== --> <!-- Contacts meta-data attributes --> <!-- =============================== --> <eat-comment /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index da658cc3d525..cff1bdaa4477 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3724,6 +3724,14 @@ --> <string name="config_defaultAugmentedAutofillService" translatable="false"></string> + <!-- The package name for the system's translation service. + This service must be trusted, as it can be activated without explicit consent of the user. + If no service with the specified name exists on the device, translation wil be + disabled. + Example: "com.android.translation/.TranslationService" +--> + <string name="config_defaultTranslationService" translatable="false"></string> + <!-- The package name for the system's app prediction service. This service must be trusted, as it can be activated without explicit consent of the user. Example: "com.android.intelligence/.AppPredictionService" diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index c326a40f7324..3ae21311ea49 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -305,12 +305,18 @@ <!-- The top padding for the notification expand button. --> <dimen name="notification_expand_button_padding_top">1dp</dimen> - <!-- minimum vertical margin for the headerless notification content --> + <!-- minimum vertical margin for the headerless notification content, when cap = 60dp --> <dimen name="notification_headerless_margin_minimum">8dp</dimen> - <!-- extra vertical margin for the headerless notification content --> + <!-- extra vertical margin for the headerless notification content, when cap = 60dp --> <dimen name="notification_headerless_margin_extra">10dp</dimen> + <!-- minimum vertical margin for the headerless notification content, when cap = 48dp --> + <dimen name="notification_headerless_margin_constrained_minimum">14dp</dimen> + + <!-- extra vertical margin for the headerless notification content, when cap = 48dp --> + <dimen name="notification_headerless_margin_constrained_extra">4dp</dimen> + <!-- The height of each of the 1 or 2 lines in the headerless notification template --> <dimen name="notification_headerless_line_height">20sp</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 2df9684e0e6f..5b4294717e27 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2877,6 +2877,8 @@ <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="notification_headerless_margin_extra_top" /> + <java-symbol type="id" name="notification_headerless_margin_extra_bottom" /> <java-symbol type="id" name="time_divider" /> <java-symbol type="id" name="header_text_divider" /> <java-symbol type="id" name="header_text_secondary_divider" /> @@ -2898,6 +2900,8 @@ <java-symbol type="dimen" name="notification_header_icon_size" /> <java-symbol type="dimen" name="notification_header_app_name_margin_start" /> <java-symbol type="dimen" name="notification_header_separating_margin" /> + <java-symbol type="dimen" name="notification_headerless_margin_constrained_minimum" /> + <java-symbol type="dimen" name="notification_headerless_margin_constrained_extra" /> <java-symbol type="string" name="default_notification_channel_label" /> <java-symbol type="string" name="importance_from_user" /> <java-symbol type="string" name="importance_from_person" /> @@ -3480,6 +3484,7 @@ <java-symbol type="string" name="config_defaultWellbeingPackage" /> <java-symbol type="string" name="config_defaultContentCaptureService" /> <java-symbol type="string" name="config_defaultAugmentedAutofillService" /> + <java-symbol type="string" name="config_defaultTranslationService" /> <java-symbol type="string" name="config_defaultAppPredictionService" /> <java-symbol type="string" name="config_defaultContentSuggestionsService" /> <java-symbol type="string" name="config_defaultSearchUiService" /> diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java index 133efce58b70..aab922972c54 100644 --- a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java @@ -16,6 +16,7 @@ package android.app.appsearch; + import static com.google.common.truth.Truth.assertThat; import static org.testng.Assert.expectThrows; diff --git a/core/tests/coretests/src/android/graphics/OWNERS b/core/tests/coretests/src/android/graphics/OWNERS new file mode 100644 index 000000000000..1e8478eeb141 --- /dev/null +++ b/core/tests/coretests/src/android/graphics/OWNERS @@ -0,0 +1,6 @@ +# Bug component: 24939 + +include /graphics/java/android/graphics/OWNERS + +per-file Font* = file:/graphics/java/android/graphics/fonts/OWNERS +per-file Typeface* = file:/graphics/java/android/graphics/fonts/OWNERS diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java index 82d066f6ab16..05ff21853131 100644 --- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java +++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java @@ -67,6 +67,7 @@ public class TypefaceSystemFallbackTest { private static final String TEST_FONT_DIR; private static final String TEST_OEM_XML; private static final String TEST_OEM_DIR; + private static final String TEST_UPDATABLE_FONT_DIR; private static final float GLYPH_1EM_WIDTH; private static final float GLYPH_2EM_WIDTH; @@ -82,9 +83,11 @@ public class TypefaceSystemFallbackTest { TEST_FONTS_XML = new File(cacheDir, "fonts.xml").getAbsolutePath(); TEST_OEM_DIR = cacheDir.getAbsolutePath() + "/oem_fonts/"; TEST_OEM_XML = new File(cacheDir, "fonts_customization.xml").getAbsolutePath(); + TEST_UPDATABLE_FONT_DIR = cacheDir.getAbsolutePath() + "/updatable_fonts/"; new File(TEST_FONT_DIR).mkdirs(); new File(TEST_OEM_DIR).mkdirs(); + new File(TEST_UPDATABLE_FONT_DIR).mkdirs(); final AssetManager am = InstrumentationRegistry.getInstrumentation().getContext().getAssets(); @@ -103,18 +106,11 @@ public class TypefaceSystemFallbackTest { InstrumentationRegistry.getInstrumentation().getContext().getAssets(); for (final String fontFile : TEST_FONT_FILES) { final String sourceInAsset = "fonts/" + fontFile; - final File outInCache = new File(TEST_FONT_DIR, fontFile); - try (InputStream is = am.open(sourceInAsset)) { - Files.copy(is, outInCache.toPath(), StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e) { - throw new RuntimeException(e); - } - final File outOemInCache = new File(TEST_OEM_DIR, fontFile); - try (InputStream is = am.open(sourceInAsset)) { - Files.copy(is, outOemInCache.toPath(), StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e) { - throw new RuntimeException(e); - } + copyAssetToFile(sourceInAsset, new File(TEST_FONT_DIR, fontFile)); + copyAssetToFile(sourceInAsset, new File(TEST_OEM_DIR, fontFile)); + } + for (final File fontFile : new File(TEST_UPDATABLE_FONT_DIR).listFiles()) { + fontFile.delete(); } } @@ -124,7 +120,20 @@ public class TypefaceSystemFallbackTest { final File outInCache = new File(TEST_FONT_DIR, fontFile); outInCache.delete(); final File outOemInCache = new File(TEST_OEM_DIR, fontFile); - outInCache.delete(); + outOemInCache.delete(); + } + for (final File fontFile : new File(TEST_UPDATABLE_FONT_DIR).listFiles()) { + fontFile.delete(); + } + } + + private static void copyAssetToFile(String sourceInAsset, File out) { + final AssetManager am = + InstrumentationRegistry.getInstrumentation().getContext().getAssets(); + try (InputStream is = am.open(sourceInAsset)) { + Files.copy(is, out.toPath(), StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + throw new RuntimeException(e); } } @@ -138,7 +147,7 @@ public class TypefaceSystemFallbackTest { } final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(TEST_FONTS_XML, - TEST_FONT_DIR, oemCustomization, fallbackMap); + TEST_FONT_DIR, TEST_UPDATABLE_FONT_DIR, oemCustomization, fallbackMap); Typeface.initSystemDefaultTypefaces(fontMap, fallbackMap, aliases); } @@ -835,4 +844,32 @@ public class TypefaceSystemFallbackTest { + "</fonts-modification>"; readFontCustomization(oemXml); } + + + @Test + public void testBuildSystemFallback_UpdatableFont() { + final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<familyset>" + + " <family name='test'>" + + " <font weight='400' style='normal'>a3em.ttf</font>" + + " </family>" + + "</familyset>"; + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); + + // Install all2em.ttf as a3em.ttf + copyAssetToFile("fonts/all2em.ttf", new File(TEST_UPDATABLE_FONT_DIR, "a3em.ttf")); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + + final Paint paint = new Paint(); + + final Typeface sansSerifTypeface = fontMap.get("test"); + assertNotNull(sansSerifTypeface); + paint.setTypeface(sansSerifTypeface); + assertEquals(GLYPH_2EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_2EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_2EM_WIDTH, paint.measureText("c"), 0.0f); + } } diff --git a/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java index 9978648ee32e..9968f7975156 100644 --- a/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java +++ b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java @@ -87,7 +87,7 @@ public class TextViewOnReceiveContentTest { } @Test - public void testGetEditorInfoMimeTypes_fallbackToCommitContent() throws Throwable { + public void testGetFallbackMimeTypesForAutofill() throws Throwable { // Configure the EditText with an EditorInfo/InputConnection that supports some image MIME // types. String[] mimeTypes = {"image/gif", "image/png"}; @@ -99,11 +99,12 @@ public class TextViewOnReceiveContentTest { onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0)); // Assert that the default listener returns the MIME types declared in the EditorInfo. - assertThat(mDefaultReceiver.getEditorInfoMimeTypes(mEditText)).isEqualTo(mimeTypes); + assertThat(mDefaultReceiver.getFallbackMimeTypesForAutofill(mEditText)).isEqualTo( + mimeTypes); } @Test - public void testGetEditorInfoMimeTypes_fallbackToCommitContent_noMimeTypesInEditorInfo() + public void testGetFallbackMimeTypesForAutofill_noMimeTypesInEditorInfo() throws Throwable { // Configure the EditText with an EditorInfo/InputConnection that doesn't declare any MIME // types. @@ -115,7 +116,7 @@ public class TextViewOnReceiveContentTest { onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0)); // Assert that the default listener returns null as the MIME types. - assertThat(mDefaultReceiver.getEditorInfoMimeTypes(mEditText)).isNull(); + assertThat(mDefaultReceiver.getFallbackMimeTypesForAutofill(mEditText)).isNull(); } @Test diff --git a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java new file mode 100644 index 000000000000..e2a106484848 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java @@ -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. + */ + +package com.android.internal.os; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.BatteryConsumer; +import android.os.SystemBatteryConsumer; +import android.view.Display; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class AmbientDisplayPowerCalculatorTest { + private static final double PRECISION = 0.00001; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY, 360.0); + + @Test + public void testTimerBasedModel() { + BatteryStatsImpl stats = mStatsRule.getBatteryStats(); + + stats.noteScreenStateLocked(Display.STATE_ON, 1000, 1000, 1000); + stats.noteScreenStateLocked(Display.STATE_DOZE, 2000, 2000, 2000); + stats.noteScreenStateLocked(Display.STATE_OFF, 3000, 3000, 3000); + + AmbientDisplayPowerCalculator calculator = + new AmbientDisplayPowerCalculator(mStatsRule.getPowerProfile()); + + mStatsRule.apply(calculator); + + SystemBatteryConsumer consumer = + mStatsRule.getSystemBatteryConsumer( + SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY); + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE)) + .isEqualTo(1000); + assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE)) + .isWithin(PRECISION).of(0.1); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/AudioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/AudioPowerCalculatorTest.java new file mode 100644 index 000000000000..ed4638c1e7e0 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/AudioPowerCalculatorTest.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.internal.os; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.BatteryConsumer; +import android.os.Process; +import android.os.UidBatteryConsumer; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class AudioPowerCalculatorTest { + private static final double PRECISION = 0.00001; + + private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePower(PowerProfile.POWER_AUDIO, 360.0); + + @Test + public void testTimerBasedModel() { + BatteryStatsImpl.Uid uidStats = mStatsRule.getUidStats(APP_UID); + uidStats.noteAudioTurnedOnLocked(1000); + uidStats.noteAudioTurnedOffLocked(2000); + + AudioPowerCalculator calculator = + new AudioPowerCalculator(mStatsRule.getPowerProfile()); + + mStatsRule.apply(calculator); + + UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID); + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_AUDIO)) + .isEqualTo(1000); + assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AUDIO)) + .isWithin(PRECISION).of(0.1); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java index bd4154210cbf..8ff318e8d555 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java @@ -21,6 +21,8 @@ import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({ + AmbientDisplayPowerCalculatorTest.class, + AudioPowerCalculatorTest.class, BatteryStatsCpuTimesTest.class, BatteryStatsBackgroundStatsTest.class, BatteryStatsBinderCallStatsTest.class, @@ -42,6 +44,9 @@ import org.junit.runners.Suite; BatteryStatsUserLifecycleTests.class, BluetoothPowerCalculatorTest.class, BstatsCpuTimesValidationTest.class, + CameraPowerCalculatorTest.class, + FlashlightPowerCalculatorTest.class, + IdlePowerCalculatorTest.class, KernelCpuProcStringReaderTest.class, KernelCpuUidActiveTimeReaderTest.class, KernelCpuUidBpfMapReaderTest.class, @@ -55,6 +60,9 @@ import org.junit.runners.Suite; LongSamplingCounterArrayTest.class, PowerCalculatorTest.class, PowerProfileTest.class, + ScreenPowerCalculatorTest.class, + SystemServicePowerCalculatorTest.class, + VideoPowerCalculatorTest.class, com.android.internal.power.MeasuredEnergyStatsTest.class }) diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java new file mode 100644 index 000000000000..55f64f977933 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java @@ -0,0 +1,130 @@ +/* + * 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 org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.os.BatteryStats; +import android.os.BatteryUsageStats; +import android.os.BatteryUsageStatsQuery; +import android.os.SystemBatteryConsumer; +import android.os.UidBatteryConsumer; +import android.util.SparseArray; + +import androidx.test.InstrumentationRegistry; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +public class BatteryUsageStatsRule implements TestRule { + private final PowerProfile mPowerProfile; + private final MockClocks mMockClocks = new MockClocks(); + private final MockBatteryStatsImpl mBatteryStats = new MockBatteryStatsImpl(mMockClocks) { + @Override + public boolean hasBluetoothActivityReporting() { + return true; + } + }; + + private BatteryUsageStats mBatteryUsageStats; + + public BatteryUsageStatsRule() { + Context context = InstrumentationRegistry.getContext(); + mPowerProfile = spy(new PowerProfile(context, true /* forTest */)); + mBatteryStats.setPowerProfile(mPowerProfile); + } + + public BatteryUsageStatsRule setAveragePower(String key, double value) { + when(mPowerProfile.getAveragePower(key)).thenReturn(value); + return this; + } + + public BatteryUsageStatsRule setAveragePower(String key, double[] values) { + when(mPowerProfile.getNumElements(key)).thenReturn(values.length); + for (int i = 0; i < values.length; i++) { + when(mPowerProfile.getAveragePower(key, i)).thenReturn(values[i]); + } + return this; + } + + @Override + public Statement apply(Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + noteOnBattery(); + base.evaluate(); + } + }; + } + + private void noteOnBattery() { + mBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0); + } + + public PowerProfile getPowerProfile() { + return mPowerProfile; + } + + public MockBatteryStatsImpl getBatteryStats() { + return mBatteryStats; + } + + public BatteryStatsImpl.Uid getUidStats(int uid) { + return mBatteryStats.getUidStatsLocked(uid); + } + + public void setTime(long realtimeUs, long uptimeUs) { + mMockClocks.realtime = realtimeUs; + mMockClocks.uptime = uptimeUs; + } + + void apply(PowerCalculator calculator) { + BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(0, 0, false); + SparseArray<? extends BatteryStats.Uid> uidStats = mBatteryStats.getUidStats(); + for (int i = 0; i < uidStats.size(); i++) { + builder.getOrCreateUidBatteryConsumerBuilder(uidStats.valueAt(i)); + } + + calculator.calculate(builder, mBatteryStats, mMockClocks.realtime, mMockClocks.uptime, + BatteryUsageStatsQuery.DEFAULT, null); + + mBatteryUsageStats = builder.build(); + } + + public UidBatteryConsumer getUidBatteryConsumer(int uid) { + for (UidBatteryConsumer ubc : mBatteryUsageStats.getUidBatteryConsumers()) { + if (ubc.getUid() == uid) { + return ubc; + } + } + return null; + } + + public SystemBatteryConsumer getSystemBatteryConsumer( + @SystemBatteryConsumer.DrainType int drainType) { + for (SystemBatteryConsumer sbc : mBatteryUsageStats.getSystemBatteryConsumers()) { + if (sbc.getDrainType() == drainType) { + return sbc; + } + } + return null; + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java index 96eb8da71ddb..e5594712db10 100644 --- a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java @@ -18,24 +18,17 @@ package com.android.internal.os; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.when; - import android.annotation.Nullable; import android.os.BatteryConsumer; -import android.os.BatteryUsageStats; -import android.os.BatteryUsageStatsQuery; import android.os.Process; import android.os.SystemBatteryConsumer; -import android.os.UidBatteryConsumer; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) @SmallTest @@ -43,83 +36,69 @@ public class BluetoothPowerCalculatorTest { private static final double PRECISION = 0.00001; private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; - @Mock - private PowerProfile mMockPowerProfile; - private MockBatteryStatsImpl mMockBatteryStats; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mMockBatteryStats = new MockBatteryStatsImpl(new MockClocks()) { - @Override - public boolean hasBluetoothActivityReporting() { - return true; - } - }; - mMockBatteryStats.getOnBatteryTimeBase().setRunning(true, 100_000, 100_000); - when(mMockPowerProfile.getAveragePower( - PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE)).thenReturn(10.0); - when(mMockPowerProfile.getAveragePower( - PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX)).thenReturn(50.0); - when(mMockPowerProfile.getAveragePower( - PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX)).thenReturn(100.0); - } + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE, 10.0) + .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX, 50.0) + .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX, 100.0); @Test public void testTimerBasedModel() { - setDurationsAndPower( - mMockBatteryStats.getUidStatsLocked(Process.BLUETOOTH_UID) + setDurationsAndPower(mStatsRule.getUidStats(Process.BLUETOOTH_UID) .getOrCreateBluetoothControllerActivityLocked(), 1000, 2000, 3000, 0); - setDurationsAndPower(mMockBatteryStats.getUidStatsLocked(APP_UID) + setDurationsAndPower(mStatsRule.getUidStats(APP_UID) .getOrCreateBluetoothControllerActivityLocked(), 4000, 5000, 6000, 0); setDurationsAndPower((BatteryStatsImpl.ControllerActivityCounterImpl) - mMockBatteryStats.getBluetoothControllerActivity(), + mStatsRule.getBatteryStats().getBluetoothControllerActivity(), 6000, 8000, 10000, 0); - BatteryUsageStats batteryUsageStats = buildBatteryUsageStats(); + BluetoothPowerCalculator calculator = + new BluetoothPowerCalculator(mStatsRule.getPowerProfile()); + + mStatsRule.apply(calculator); assertBluetoothPowerAndDuration( - getUidBatteryConsumer(batteryUsageStats, Process.BLUETOOTH_UID), + mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID), 0.11388, 6000); assertBluetoothPowerAndDuration( - getUidBatteryConsumer(batteryUsageStats, APP_UID), + mStatsRule.getUidBatteryConsumer(APP_UID), 0.24722, 15000); assertBluetoothPowerAndDuration( - getBluetoothSystemBatteryConsumer(batteryUsageStats, - SystemBatteryConsumer.DRAIN_TYPE_BLUETOOTH), + mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_BLUETOOTH), 0.15833, 9000); } @Test public void testReportedPowerBasedModel() { - setDurationsAndPower( - mMockBatteryStats.getUidStatsLocked(Process.BLUETOOTH_UID) + setDurationsAndPower(mStatsRule.getUidStats(Process.BLUETOOTH_UID) .getOrCreateBluetoothControllerActivityLocked(), 1000, 2000, 3000, 360000); - setDurationsAndPower(mMockBatteryStats.getUidStatsLocked(APP_UID) + setDurationsAndPower(mStatsRule.getUidStats(APP_UID) .getOrCreateBluetoothControllerActivityLocked(), 4000, 5000, 6000, 720000); setDurationsAndPower((BatteryStatsImpl.ControllerActivityCounterImpl) - mMockBatteryStats.getBluetoothControllerActivity(), + mStatsRule.getBatteryStats().getBluetoothControllerActivity(), 6000, 8000, 10000, 1260000); - BatteryUsageStats batteryUsageStats = buildBatteryUsageStats(); + BluetoothPowerCalculator calculator = + new BluetoothPowerCalculator(mStatsRule.getPowerProfile()); + + mStatsRule.apply(calculator); assertBluetoothPowerAndDuration( - getUidBatteryConsumer(batteryUsageStats, Process.BLUETOOTH_UID), + mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID), 0.1, 6000); assertBluetoothPowerAndDuration( - getUidBatteryConsumer(batteryUsageStats, APP_UID), + mStatsRule.getUidBatteryConsumer(APP_UID), 0.2, 15000); assertBluetoothPowerAndDuration( - getBluetoothSystemBatteryConsumer(batteryUsageStats, - SystemBatteryConsumer.DRAIN_TYPE_BLUETOOTH), + mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_BLUETOOTH), 0.15, 9000); } @@ -132,38 +111,6 @@ public class BluetoothPowerCalculatorTest { controllerActivity.getPowerCounter().addCountLocked(powerMaMs); } - private BatteryUsageStats buildBatteryUsageStats() { - BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(0, 0, false); - builder.getOrCreateUidBatteryConsumerBuilder( - mMockBatteryStats.getUidStatsLocked(Process.BLUETOOTH_UID)); - builder.getOrCreateUidBatteryConsumerBuilder( - mMockBatteryStats.getUidStatsLocked(APP_UID)); - - BluetoothPowerCalculator bpc = new BluetoothPowerCalculator(mMockPowerProfile); - bpc.calculate(builder, mMockBatteryStats, 200_000, 200_000, BatteryUsageStatsQuery.DEFAULT, - null); - return builder.build(); - } - - private UidBatteryConsumer getUidBatteryConsumer(BatteryUsageStats batteryUsageStats, int uid) { - for (UidBatteryConsumer ubc : batteryUsageStats.getUidBatteryConsumers()) { - if (ubc.getUid() == uid) { - return ubc; - } - } - return null; - } - - private SystemBatteryConsumer getBluetoothSystemBatteryConsumer( - BatteryUsageStats batteryUsageStats, int drainType) { - for (SystemBatteryConsumer sbc : batteryUsageStats.getSystemBatteryConsumers()) { - if (sbc.getDrainType() == drainType) { - return sbc; - } - } - return null; - } - private void assertBluetoothPowerAndDuration(@Nullable BatteryConsumer batteryConsumer, double powerMah, int durationMs) { assertThat(batteryConsumer).isNotNull(); diff --git a/core/tests/coretests/src/com/android/internal/os/CameraPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CameraPowerCalculatorTest.java new file mode 100644 index 000000000000..a21dd58b0757 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/CameraPowerCalculatorTest.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.internal.os; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.BatteryConsumer; +import android.os.Process; +import android.os.UidBatteryConsumer; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class CameraPowerCalculatorTest { + private static final double PRECISION = 0.00001; + + private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePower(PowerProfile.POWER_CAMERA, 360.0); + + @Test + public void testTimerBasedModel() { + BatteryStatsImpl.Uid uidStats = mStatsRule.getUidStats(APP_UID); + uidStats.noteCameraTurnedOnLocked(1000); + uidStats.noteCameraTurnedOffLocked(2000); + + CameraPowerCalculator calculator = + new CameraPowerCalculator(mStatsRule.getPowerProfile()); + + mStatsRule.apply(calculator); + + UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID); + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CAMERA)) + .isEqualTo(1000); + assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) + .isWithin(PRECISION).of(0.1); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/FlashlightPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/FlashlightPowerCalculatorTest.java new file mode 100644 index 000000000000..b7bbedd9cdb7 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/FlashlightPowerCalculatorTest.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.internal.os; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.BatteryConsumer; +import android.os.Process; +import android.os.UidBatteryConsumer; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class FlashlightPowerCalculatorTest { + private static final double PRECISION = 0.00001; + + private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePower(PowerProfile.POWER_FLASHLIGHT, 360.0); + + @Test + public void testTimerBasedModel() { + BatteryStatsImpl.Uid uidStats = mStatsRule.getUidStats(APP_UID); + uidStats.noteFlashlightTurnedOnLocked(1000); + uidStats.noteFlashlightTurnedOffLocked(2000); + + FlashlightPowerCalculator calculator = + new FlashlightPowerCalculator(mStatsRule.getPowerProfile()); + + mStatsRule.apply(calculator); + + UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID); + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_FLASHLIGHT)) + .isEqualTo(1000); + assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) + .isWithin(PRECISION).of(0.1); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/IdlePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/IdlePowerCalculatorTest.java new file mode 100644 index 000000000000..781e72560279 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/IdlePowerCalculatorTest.java @@ -0,0 +1,56 @@ +/* + * 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.os.BatteryConsumer; +import android.os.SystemBatteryConsumer; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class IdlePowerCalculatorTest { + private static final double PRECISION = 0.00001; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePower(PowerProfile.POWER_CPU_IDLE, 720.0) + .setAveragePower(PowerProfile.POWER_CPU_SUSPEND, 360.0); + + @Test + public void testTimerBasedModel() { + mStatsRule.setTime(3_000_000, 2_000_000); + + IdlePowerCalculator calculator = new IdlePowerCalculator(mStatsRule.getPowerProfile()); + + mStatsRule.apply(calculator); + + SystemBatteryConsumer consumer = + mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_IDLE); + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE)) + .isEqualTo(3000); + assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE)) + .isWithin(PRECISION).of(0.7); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/MemoryPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MemoryPowerCalculatorTest.java new file mode 100644 index 000000000000..8f21503a6d77 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/MemoryPowerCalculatorTest.java @@ -0,0 +1,63 @@ +/* + * 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.os.BatteryConsumer; +import android.os.SystemBatteryConsumer; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class MemoryPowerCalculatorTest { + private static final double PRECISION = 0.00001; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePower(PowerProfile.POWER_MEMORY, new double[] {360.0, 720.0, 1080.0}); + + @Test + public void testTimerBasedModel() { + BatteryStatsImpl stats = mStatsRule.getBatteryStats(); + + // First update establishes a baseline + stats.getKernelMemoryTimerLocked(0).update(0, 1, 0); + stats.getKernelMemoryTimerLocked(2).update(0, 1, 0); + + stats.getKernelMemoryTimerLocked(0).update(1000000, 1, 4000000); + stats.getKernelMemoryTimerLocked(2).update(2000000, 1, 8000000); + + MemoryPowerCalculator calculator = + new MemoryPowerCalculator(mStatsRule.getPowerProfile()); + + mStatsRule.apply(calculator); + + SystemBatteryConsumer consumer = + mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_MEMORY); + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE)) + .isEqualTo(3000); + assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE)) + .isWithin(PRECISION).of(0.7); + } +} 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 c7751371d557..fc237219be51 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -46,6 +46,10 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { mOnBatteryTimeBase); mScreenDozeTimer = new BatteryStatsImpl.StopwatchTimer(clocks, null, -1, null, mOnBatteryTimeBase); + for (int i = 0; i < mScreenBrightnessTimer.length; i++) { + mScreenBrightnessTimer[i] = new BatteryStatsImpl.StopwatchTimer(clocks, null, -1, null, + mOnBatteryTimeBase); + } mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase); mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, 1); setExternalStatsSyncLocked(new DummyExternalStatsSync()); diff --git a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java new file mode 100644 index 000000000000..e43caa37f711 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java @@ -0,0 +1,63 @@ +/* + * 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.os.BatteryConsumer; +import android.os.SystemBatteryConsumer; +import android.view.Display; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class ScreenPowerCalculatorTest { + private static final double PRECISION = 0.00001; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePower(PowerProfile.POWER_SCREEN_ON, 360.0) + .setAveragePower(PowerProfile.POWER_SCREEN_FULL, 3600.0); + + @Test + public void testTimerBasedModel() { + BatteryStatsImpl stats = mStatsRule.getBatteryStats(); + + stats.noteScreenStateLocked(Display.STATE_ON, 1000, 1000, 1000); + stats.noteScreenBrightnessLocked(100, 1000, 1000); + stats.noteScreenBrightnessLocked(200, 2000, 2000); + stats.noteScreenStateLocked(Display.STATE_OFF, 3000, 3000, 3000); + + ScreenPowerCalculator calculator = + new ScreenPowerCalculator(mStatsRule.getPowerProfile()); + + mStatsRule.apply(calculator); + + SystemBatteryConsumer consumer = + mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_SCREEN); + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE)) + .isEqualTo(2000); + assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE)) + .isWithin(PRECISION).of(1.2); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java index dbb36fbd1650..a5cafb974b0e 100644 --- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java @@ -16,50 +16,46 @@ package com.android.internal.os; -import static org.junit.Assert.assertEquals; +import static com.google.common.truth.Truth.assertThat; -import android.content.Context; -import android.os.BatteryStats; +import android.os.BatteryConsumer; import android.os.Binder; import android.os.Process; import androidx.annotation.Nullable; -import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; -import java.util.List; @SmallTest @RunWith(AndroidJUnit4.class) public class SystemServicePowerCalculatorTest { - private PowerProfile mProfile; + private static final double PRECISION = 0.0000001; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(); + private MockBatteryStatsImpl mMockBatteryStats; private MockKernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader; private MockSystemServerCpuThreadReader mMockSystemServerCpuThreadReader; - private SystemServicePowerCalculator mSystemServicePowerCalculator; @Before public void setUp() throws IOException { - Context context = InstrumentationRegistry.getContext(); - mProfile = new PowerProfile(context, true /* forTest */); mMockSystemServerCpuThreadReader = new MockSystemServerCpuThreadReader(); mMockCpuUidFreqTimeReader = new MockKernelCpuUidFreqTimeReader(); - mMockBatteryStats = new MockBatteryStatsImpl(new MockClocks()) - .setPowerProfile(mProfile) + mMockBatteryStats = mStatsRule.getBatteryStats() .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader) .setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader) .setUserInfoProvider(new MockUserInfoProvider()); - mMockBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0); - mSystemServicePowerCalculator = new SystemServicePowerCalculator(mProfile); } @Test @@ -103,15 +99,17 @@ public class SystemServicePowerCalculatorTest { mMockBatteryStats.updateSystemServiceCallStats(); mMockBatteryStats.updateSystemServerThreadStats(); - BatterySipper app1 = new BatterySipper(BatterySipper.DrainType.APP, - mMockBatteryStats.getUidStatsLocked(workSourceUid1), 0); - BatterySipper app2 = new BatterySipper(BatterySipper.DrainType.APP, - mMockBatteryStats.getUidStatsLocked(workSourceUid2), 0); - mSystemServicePowerCalculator.calculate(List.of(app1, app2), mMockBatteryStats, 0, 0, - BatteryStats.STATS_SINCE_CHARGED, null); + SystemServicePowerCalculator calculator = new SystemServicePowerCalculator( + mStatsRule.getPowerProfile()); + + mStatsRule.apply(calculator); - assertEquals(0.00016269, app1.systemServiceCpuPowerMah, 0.0000001); - assertEquals(0.00146426, app2.systemServiceCpuPowerMah, 0.0000001); + assertThat(mStatsRule.getUidBatteryConsumer(workSourceUid1) + .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES)) + .isWithin(PRECISION).of(0.00016269); + assertThat(mStatsRule.getUidBatteryConsumer(workSourceUid2) + .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES)) + .isWithin(PRECISION).of(0.00146426); } private static class MockKernelCpuUidFreqTimeReader extends diff --git a/core/tests/coretests/src/com/android/internal/os/VideoPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/VideoPowerCalculatorTest.java new file mode 100644 index 000000000000..39eac49400ec --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/VideoPowerCalculatorTest.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.internal.os; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.BatteryConsumer; +import android.os.Process; +import android.os.UidBatteryConsumer; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class VideoPowerCalculatorTest { + private static final double PRECISION = 0.00001; + + private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePower(PowerProfile.POWER_VIDEO, 360.0); + + @Test + public void testTimerBasedModel() { + BatteryStatsImpl.Uid uidStats = mStatsRule.getUidStats(APP_UID); + uidStats.noteVideoTurnedOnLocked(1000); + uidStats.noteVideoTurnedOffLocked(2000); + + VideoPowerCalculator calculator = + new VideoPowerCalculator(mStatsRule.getPowerProfile()); + + mStatsRule.apply(calculator); + + UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID); + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_VIDEO)) + .isEqualTo(1000); + assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_VIDEO)) + .isWithin(PRECISION).of(0.1); + } +} diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java index 0782f8dfd9d3..73fff7207c45 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -16,6 +16,7 @@ package android.graphics; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.fonts.FontVariationAxis; import android.os.Build; @@ -25,6 +26,7 @@ import android.util.Xml; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -41,26 +43,26 @@ public class FontListParser { /* Parse fallback list (no names) */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static FontConfig parse(InputStream in) throws XmlPullParserException, IOException { - return parse(in, "/system/fonts"); + return parse(in, "/system/fonts", null); } /** * Parse the fonts.xml */ - public static FontConfig parse(InputStream in, String fontDir) - throws XmlPullParserException, IOException { + public static FontConfig parse(InputStream in, String fontDir, + @Nullable String updatableFontDir) throws XmlPullParserException, IOException { try { XmlPullParser parser = Xml.newPullParser(); parser.setInput(in, null); parser.nextTag(); - return readFamilies(parser, fontDir); + return readFamilies(parser, fontDir, updatableFontDir); } finally { in.close(); } } - private static FontConfig readFamilies(XmlPullParser parser, String fontDir) - throws XmlPullParserException, IOException { + private static FontConfig readFamilies(XmlPullParser parser, String fontDir, + @Nullable String updatableFontDir) throws XmlPullParserException, IOException { List<FontConfig.Family> families = new ArrayList<>(); List<FontConfig.Alias> aliases = new ArrayList<>(); @@ -69,7 +71,7 @@ public class FontListParser { if (parser.getEventType() != XmlPullParser.START_TAG) continue; String tag = parser.getName(); if (tag.equals("family")) { - families.add(readFamily(parser, fontDir)); + families.add(readFamily(parser, fontDir, updatableFontDir)); } else if (tag.equals("alias")) { aliases.add(readAlias(parser)); } else { @@ -83,8 +85,8 @@ public class FontListParser { /** * Reads a family element */ - public static FontConfig.Family readFamily(XmlPullParser parser, String fontDir) - throws XmlPullParserException, IOException { + public static FontConfig.Family readFamily(XmlPullParser parser, String fontDir, + @Nullable String updatableFontDir) throws XmlPullParserException, IOException { final String name = parser.getAttributeValue(null, "name"); final String lang = parser.getAttributeValue("", "lang"); final String variant = parser.getAttributeValue(null, "variant"); @@ -93,7 +95,7 @@ public class FontListParser { if (parser.getEventType() != XmlPullParser.START_TAG) continue; final String tag = parser.getName(); if (tag.equals("font")) { - fonts.add(readFont(parser, fontDir)); + fonts.add(readFont(parser, fontDir, updatableFontDir)); } else { skip(parser); } @@ -114,8 +116,8 @@ public class FontListParser { private static final Pattern FILENAME_WHITESPACE_PATTERN = Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$"); - private static FontConfig.Font readFont(XmlPullParser parser, String fontDir) - throws XmlPullParserException, IOException { + private static FontConfig.Font readFont(XmlPullParser parser, String fontDir, + @Nullable String updatableFontDir) throws XmlPullParserException, IOException { String indexStr = parser.getAttributeValue(null, "index"); int index = indexStr == null ? 0 : Integer.parseInt(indexStr); List<FontVariationAxis> axes = new ArrayList<FontVariationAxis>(); @@ -137,10 +139,22 @@ public class FontListParser { } } String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll(""); - return new FontConfig.Font(fontDir + sanitizedName, index, axes.toArray( + String fontName = findFontFile(sanitizedName, fontDir, updatableFontDir); + return new FontConfig.Font(fontName, index, axes.toArray( new FontVariationAxis[axes.size()]), weight, isItalic, fallbackFor); } + private static String findFontFile(String fileName, String fontDir, + @Nullable String updatableFontDir) { + if (updatableFontDir != null) { + String updatableFontName = updatableFontDir + fileName; + if (new File(updatableFontName).exists()) { + return updatableFontName; + } + } + return fontDir + fileName; + } + private static FontVariationAxis readAxis(XmlPullParser parser) throws XmlPullParserException, IOException { String tagStr = parser.getAttributeValue(null, "tag"); diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java index 0291d7484dc5..f95da82ee07c 100644 --- a/graphics/java/android/graphics/fonts/FontCustomizationParser.java +++ b/graphics/java/android/graphics/fonts/FontCustomizationParser.java @@ -97,7 +97,7 @@ public class FontCustomizationParser { throw new IllegalArgumentException("customizationType must be specified"); } if (customizationType.equals("new-named-family")) { - out.mAdditionalNamedFamilies.add(FontListParser.readFamily(parser, fontDir)); + out.mAdditionalNamedFamilies.add(FontListParser.readFamily(parser, fontDir, null)); } else { throw new IllegalArgumentException("Unknown customizationType=" + customizationType); } diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index fb6ea99be7ab..16a53c25db08 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -91,7 +91,9 @@ public final class SystemFonts { final FontCustomizationParser.Result oemCustomization = readFontCustomization("/product/etc/fonts_customization.xml", "/product/fonts/"); Map<String, FontFamily[]> map = new ArrayMap<>(); - buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", oemCustomization, map); + // TODO: use updated fonts + buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", null /* updatableFontDir */, + oemCustomization, map); Set<Font> res = new HashSet<>(); for (FontFamily[] families : map.values()) { for (FontFamily family : families) { @@ -226,11 +228,26 @@ public final class SystemFonts { } /** + * @see #buildSystemFallback(String, String, String, FontCustomizationParser.Result, Map) + * @hide + */ + @VisibleForTesting + public static FontConfig.Alias[] buildSystemFallback(@NonNull String xmlPath, + @NonNull String fontDir, + @NonNull FontCustomizationParser.Result oemCustomization, + @NonNull Map<String, FontFamily[]> fallbackMap) { + return buildSystemFallback(xmlPath, fontDir, null /* updatableFontDir */, + oemCustomization, fallbackMap); + } + + /** * Build the system fallback from xml file. * * @param xmlPath A full path string to the fonts.xml file. * @param fontDir A full path string to the system font directory. This must end with * slash('/'). + * @param updatableFontDir A full path string to the updatable system font directory. This + * must end with slash('/'). * @param fallbackMap An output system fallback map. Caller must pass empty map. * @return a list of aliases * @hide @@ -238,11 +255,12 @@ public final class SystemFonts { @VisibleForTesting public static FontConfig.Alias[] buildSystemFallback(@NonNull String xmlPath, @NonNull String fontDir, + @Nullable String updatableFontDir, @NonNull FontCustomizationParser.Result oemCustomization, @NonNull Map<String, FontFamily[]> fallbackMap) { try { final FileInputStream fontsIn = new FileInputStream(xmlPath); - final FontConfig fontConfig = FontListParser.parse(fontsIn, fontDir); + final FontConfig fontConfig = FontListParser.parse(fontsIn, fontDir, updatableFontDir); final HashMap<String, ByteBuffer> bufferCache = new HashMap<String, ByteBuffer>(); final FontConfig.Family[] xmlFamilies = fontConfig.getFamilies(); @@ -306,11 +324,17 @@ public final class SystemFonts { /** @hide */ public static @NonNull Pair<FontConfig.Alias[], Map<String, FontFamily[]>> initializePreinstalledFonts() { + return initializeSystemFonts(null); + } + + /** @hide */ + public static Pair<FontConfig.Alias[], Map<String, FontFamily[]>> + initializeSystemFonts(@Nullable String updatableFontDir) { final FontCustomizationParser.Result oemCustomization = readFontCustomization("/product/etc/fonts_customization.xml", "/product/fonts/"); Map<String, FontFamily[]> map = new ArrayMap<>(); FontConfig.Alias[] aliases = buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", - oemCustomization, map); + updatableFontDir, oemCustomization, map); synchronized (LOCK) { sFamilyMap = map; } diff --git a/identity/java/android/security/identity/Util.java b/identity/java/android/security/identity/Util.java index 6eefeb8f3f2a..e56bd5167906 100644 --- a/identity/java/android/security/identity/Util.java +++ b/identity/java/android/security/identity/Util.java @@ -16,6 +16,8 @@ package android.security.identity; +import android.annotation.NonNull; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.InvalidKeyException; @@ -28,7 +30,10 @@ import java.util.Collection; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; -class Util { +/** + * @hide + */ +public class Util { private static final String TAG = "Util"; static int[] integerCollectionToArray(Collection<Integer> collection) { @@ -91,8 +96,9 @@ class Util { * 255.DigestSize, where DigestSize is the size of the underlying HMAC. * @return size pseudorandom bytes. */ - static byte[] computeHkdf( - String macAlgorithm, final byte[] ikm, final byte[] salt, final byte[] info, int size) { + @NonNull public static byte[] computeHkdf( + @NonNull String macAlgorithm, @NonNull final byte[] ikm, @NonNull final byte[] salt, + @NonNull final byte[] info, int size) { Mac mac = null; try { mac = Mac.getInstance(macAlgorithm); @@ -137,4 +143,5 @@ class Util { } } + private Util() {} } diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json index 4070829fcfbe..0290d9f4b316 100644 --- a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json +++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json @@ -7,24 +7,12 @@ "group": "WM_SHELL_TASK_ORG", "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java" }, - "-1534364071": { - "message": "onTransitionReady %s: %s", - "level": "VERBOSE", - "group": "WM_SHELL_TRANSITIONS", - "at": "com\/android\/wm\/shell\/Transitions.java" - }, "-1501874464": { "message": "Fullscreen Task Appeared: #%d", "level": "VERBOSE", "group": "WM_SHELL_TASK_ORG", "at": "com\/android\/wm\/shell\/FullscreenTaskListener.java" }, - "-1480787369": { - "message": "Transition requested: type=%d %s", - "level": "VERBOSE", - "group": "WM_SHELL_TRANSITIONS", - "at": "com\/android\/wm\/shell\/Transitions.java" - }, "-1382704050": { "message": "Display removed: %d", "level": "VERBOSE", @@ -97,12 +85,6 @@ "group": "WM_SHELL_TASK_ORG", "at": "com\/android\/wm\/shell\/apppairs\/AppPairsController.java" }, - "-191422040": { - "message": "Transition animations finished, notifying core %s", - "level": "VERBOSE", - "group": "WM_SHELL_TRANSITIONS", - "at": "com\/android\/wm\/shell\/Transitions.java" - }, "157713005": { "message": "Task info changed taskId=%d", "level": "VERBOSE", @@ -115,6 +97,12 @@ "group": "WM_SHELL_DRAG_AND_DROP", "at": "com\/android\/wm\/shell\/draganddrop\/DropOutlineDrawable.java" }, + "325110414": { + "message": "Transition animations finished, notifying core %s", + "level": "VERBOSE", + "group": "WM_SHELL_TRANSITIONS", + "at": "com\/android\/wm\/shell\/transition\/Transitions.java" + }, "375908576": { "message": "Clip description: handlingDrag=%b itemCount=%d mimeTypes=%s", "level": "VERBOSE", @@ -145,6 +133,12 @@ "group": "WM_SHELL_TASK_ORG", "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java" }, + "846958769": { + "message": "Transition requested: type=%d %s", + "level": "VERBOSE", + "group": "WM_SHELL_TRANSITIONS", + "at": "com\/android\/wm\/shell\/transition\/Transitions.java" + }, "900599280": { "message": "Can't pair unresizeable tasks task1.isResizeable=%b task1.isResizeable=%b", "level": "ERROR", @@ -169,6 +163,12 @@ "group": "WM_SHELL_TASK_ORG", "at": "com\/android\/wm\/shell\/legacysplitscreen\/LegacySplitScreenTaskListener.java" }, + "1070270131": { + "message": "onTransitionReady %s: %s", + "level": "VERBOSE", + "group": "WM_SHELL_TRANSITIONS", + "at": "com\/android\/wm\/shell\/transition\/Transitions.java" + }, "1079041527": { "message": "incrementPool size=%d", "level": "VERBOSE", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java index 8817f8aaec7c..0146b728bcad 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java @@ -30,6 +30,7 @@ import androidx.annotation.NonNull; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.protolog.ShellProtoLogGroup; +import com.android.wm.shell.transition.Transitions; import java.io.PrintWriter; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java index 00567618ef06..0cee0a21bde3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java @@ -24,9 +24,9 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; +import com.android.wm.shell.transition.Transitions; import java.util.Optional; -import java.util.concurrent.TimeUnit; /** * The entry point implementation into the shell for initializing shell internal state. @@ -41,6 +41,7 @@ public class ShellInitImpl { private final Optional<AppPairs> mAppPairsOptional; private final FullscreenTaskListener mFullscreenTaskListener; private final ShellExecutor mMainExecutor; + private final Transitions mTransitions; private final InitImpl mImpl = new InitImpl(); @@ -50,6 +51,7 @@ public class ShellInitImpl { Optional<LegacySplitScreen> legacySplitScreenOptional, Optional<AppPairs> appPairsOptional, FullscreenTaskListener fullscreenTaskListener, + Transitions transitions, ShellExecutor mainExecutor) { return new ShellInitImpl(displayImeController, dragAndDropController, @@ -57,6 +59,7 @@ public class ShellInitImpl { legacySplitScreenOptional, appPairsOptional, fullscreenTaskListener, + transitions, mainExecutor).mImpl; } @@ -66,6 +69,7 @@ public class ShellInitImpl { Optional<LegacySplitScreen> legacySplitScreenOptional, Optional<AppPairs> appPairsOptional, FullscreenTaskListener fullscreenTaskListener, + Transitions transitions, ShellExecutor mainExecutor) { mDisplayImeController = displayImeController; mDragAndDropController = dragAndDropController; @@ -73,6 +77,7 @@ public class ShellInitImpl { mLegacySplitScreenOptional = legacySplitScreenOptional; mAppPairsOptional = appPairsOptional; mFullscreenTaskListener = fullscreenTaskListener; + mTransitions = transitions; mMainExecutor = mainExecutor; } @@ -89,6 +94,10 @@ public class ShellInitImpl { // Bind the splitscreen impl to the drag drop controller mDragAndDropController.initialize(mLegacySplitScreenOptional); + + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + mTransitions.register(mShellTaskOrganizer); + } } @ExternalThread diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java index 9e1f0a2ed493..a785cffb3df0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java @@ -43,7 +43,6 @@ import android.window.WindowContainerTransaction; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.Transitions; import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; @@ -54,6 +53,7 @@ import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.transition.Transitions; import java.io.PrintWriter; import java.lang.ref.WeakReference; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java index 3ed070b40816..5a493c234ce3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java @@ -38,8 +38,8 @@ import androidx.annotation.NonNull; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.Transitions; import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.transition.Transitions; import java.io.PrintWriter; import java.util.ArrayList; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java index 93520c0b59de..94b2cc0455bd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/SplitScreenTransitions.java @@ -38,9 +38,9 @@ import android.view.WindowManager; import android.window.TransitionInfo; import android.window.WindowContainerTransaction; -import com.android.wm.shell.Transitions; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ExternalThread; +import com.android.wm.shell.transition.Transitions; import java.util.ArrayList; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java index 90a8de02deb8..ad05e6d3e6c4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java @@ -39,8 +39,8 @@ import android.window.WindowContainerTransaction; import android.window.WindowOrganizer; import com.android.internal.annotations.GuardedBy; -import com.android.wm.shell.Transitions; import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.transition.Transitions; import java.util.ArrayList; import java.util.List; 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 e32d3b955081..df7c75332675 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 @@ -83,8 +83,18 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal = ThreadLocal.withInitial(() -> { - FrameCallbackScheduler scheduler = runnable -> + final Looper initialLooper = Looper.myLooper(); + final FrameCallbackScheduler scheduler = new FrameCallbackScheduler() { + @Override + public void postFrameCallback(@androidx.annotation.NonNull Runnable runnable) { Choreographer.getSfInstance().postFrameCallback(t -> runnable.run()); + } + + @Override + public boolean isCurrentThread() { + return Looper.myLooper() == initialLooper; + } + }; AnimationHandler handler = new AnimationHandler(scheduler); return handler; }); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java deleted file mode 100644 index ffa6c9921d97..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java +++ /dev/null @@ -1,550 +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.wm.shell.pip.tv; - -import static android.app.ActivityTaskManager.INVALID_STACK_ID; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static android.content.Intent.ACTION_MEDIA_RESOURCE_GRANTED; - -import static com.android.wm.shell.pip.tv.PipNotification.ACTION_CLOSE; -import static com.android.wm.shell.pip.tv.PipNotification.ACTION_MENU; - -import android.app.ActivityManager; -import android.app.ActivityTaskManager; -import android.app.ActivityTaskManager.RootTaskInfo; -import android.app.IActivityTaskManager; -import android.app.RemoteAction; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.ParceledListSlice; -import android.content.res.Configuration; -import android.graphics.Rect; -import android.os.Handler; -import android.os.RemoteException; -import android.os.UserHandle; -import android.text.TextUtils; -import android.util.Log; -import android.view.DisplayInfo; - -import com.android.wm.shell.R; -import com.android.wm.shell.WindowManagerShellWrapper; -import com.android.wm.shell.common.TaskStackListenerCallback; -import com.android.wm.shell.common.TaskStackListenerImpl; -import com.android.wm.shell.pip.PinnedStackListenerForwarder; -import com.android.wm.shell.pip.Pip; -import com.android.wm.shell.pip.PipBoundsAlgorithm; -import com.android.wm.shell.pip.PipBoundsState; -import com.android.wm.shell.pip.PipMediaController; -import com.android.wm.shell.pip.PipTaskOrganizer; - -import java.util.Objects; - -/** - * Manages the picture-in-picture (PIP) UI and states. - */ -public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallback, - TvPipMenuController.Delegate { - private static final String TAG = "TvPipController"; - static final boolean DEBUG = false; - - /** - * Unknown or invalid state - */ - public static final int STATE_UNKNOWN = -1; - /** - * State when there's no PIP. - */ - public static final int STATE_NO_PIP = 0; - /** - * State when PIP is shown. This is used as default PIP state. - */ - public static final int STATE_PIP = 1; - /** - * State when PIP menu dialog is shown. - */ - public static final int STATE_PIP_MENU = 2; - - private static final int TASK_ID_NO_PIP = -1; - private static final int INVALID_RESOURCE_TYPE = -1; - - private final Context mContext; - private final PipBoundsState mPipBoundsState; - private final PipBoundsAlgorithm mPipBoundsAlgorithm; - private final PipTaskOrganizer mPipTaskOrganizer; - private final PipMediaController mPipMediaController; - private final TvPipMenuController mTvPipMenuController; - private final PipNotification mPipNotification; - - private IActivityTaskManager mActivityTaskManager; - private int mState = STATE_NO_PIP; - private final Handler mHandler = new Handler(); - private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED; - private int mPipTaskId = TASK_ID_NO_PIP; - private int mPinnedStackId = INVALID_STACK_ID; - private String[] mLastPackagesResourceGranted; - private ParceledListSlice<RemoteAction> mCustomActions; - private WindowManagerShellWrapper mWindowManagerShellWrapper; - private int mResizeAnimationDuration; - - // Used to calculate the movement bounds - private final DisplayInfo mTmpDisplayInfo = new DisplayInfo(); - private final Rect mTmpInsetBounds = new Rect(); - - // Keeps track of the IME visibility to adjust the PiP when the IME is visible - private boolean mImeVisible; - private int mImeHeightAdjustment; - - private final Runnable mClosePipRunnable = this::closePip; - private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (DEBUG) { - Log.d(TAG, "mBroadcastReceiver, action: " + intent.getAction()); - } - switch (intent.getAction()) { - case ACTION_MENU: - showPictureInPictureMenu(); - break; - case ACTION_CLOSE: - closePip(); - break; - case ACTION_MEDIA_RESOURCE_GRANTED: - String[] packageNames = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); - int resourceType = intent.getIntExtra(Intent.EXTRA_MEDIA_RESOURCE_TYPE, - INVALID_RESOURCE_TYPE); - if (packageNames != null && packageNames.length > 0 - && resourceType == Intent.EXTRA_MEDIA_RESOURCE_TYPE_VIDEO_CODEC) { - handleMediaResourceGranted(packageNames); - } - break; - } - } - }; - - private final PinnedStackListenerForwarder.PinnedStackListener mPinnedStackListener = - new PipControllerPinnedStackListener(); - - @Override - public void registerSessionListenerForCurrentUser() { - mPipMediaController.registerSessionListenerForCurrentUser(); - } - - /** - * Handler for messages from the PIP controller. - */ - private class PipControllerPinnedStackListener extends - PinnedStackListenerForwarder.PinnedStackListener { - @Override - public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { - mPipBoundsState.setImeVisibility(imeVisible, imeHeight); - if (mState == STATE_PIP) { - if (mImeVisible != imeVisible) { - if (imeVisible) { - // Save the IME height adjustment, and offset to not occlude the IME - mPipBoundsState.getNormalBounds().offset(0, -imeHeight); - mImeHeightAdjustment = imeHeight; - } else { - // Apply the inverse adjustment when the IME is hidden - mPipBoundsState.getNormalBounds().offset(0, mImeHeightAdjustment); - } - mImeVisible = imeVisible; - resizePinnedStack(STATE_PIP); - } - } - } - - @Override - public void onMovementBoundsChanged(boolean fromImeAdjustment) { - mTmpDisplayInfo.copyFrom(mPipBoundsState.getDisplayInfo()); - mPipBoundsAlgorithm.getInsetBounds(mTmpInsetBounds); - } - - @Override - public void onActionsChanged(ParceledListSlice<RemoteAction> actions) { - mCustomActions = actions; - mTvPipMenuController.setAppActions(mCustomActions); - } - } - - public PipController(Context context, - PipBoundsState pipBoundsState, - PipBoundsAlgorithm pipBoundsAlgorithm, - PipTaskOrganizer pipTaskOrganizer, - TvPipMenuController tvPipMenuController, - PipMediaController pipMediaController, - PipNotification pipNotification, - TaskStackListenerImpl taskStackListener, - WindowManagerShellWrapper windowManagerShellWrapper) { - mContext = context; - mPipBoundsState = pipBoundsState; - mPipNotification = pipNotification; - mPipBoundsAlgorithm = pipBoundsAlgorithm; - mPipMediaController = pipMediaController; - mTvPipMenuController = tvPipMenuController; - mTvPipMenuController.setDelegate(this); - // Ensure that we have the display info in case we get calls to update the bounds - // before the listener calls back - final DisplayInfo displayInfo = new DisplayInfo(); - context.getDisplay().getDisplayInfo(displayInfo); - mPipBoundsState.setDisplayInfo(displayInfo); - - mResizeAnimationDuration = context.getResources() - .getInteger(R.integer.config_pipResizeAnimationDuration); - mPipTaskOrganizer = pipTaskOrganizer; - mPipTaskOrganizer.registerPipTransitionCallback(this); - mActivityTaskManager = ActivityTaskManager.getService(); - - final IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(ACTION_CLOSE); - intentFilter.addAction(ACTION_MENU); - intentFilter.addAction(ACTION_MEDIA_RESOURCE_GRANTED); - mContext.registerReceiver(mBroadcastReceiver, intentFilter, UserHandle.USER_ALL); - - // Initialize the last orientation and apply the current configuration - Configuration initialConfig = mContext.getResources().getConfiguration(); - mLastOrientation = initialConfig.orientation; - loadConfigurationsAndApply(initialConfig); - - mWindowManagerShellWrapper = windowManagerShellWrapper; - try { - mWindowManagerShellWrapper.addPinnedStackListener(mPinnedStackListener); - } catch (RemoteException e) { - Log.e(TAG, "Failed to register pinned stack listener", e); - } - - // Handle for system task stack changes. - taskStackListener.addListener( - new TaskStackListenerCallback() { - @Override - public void onTaskStackChanged() { - PipController.this.onTaskStackChanged(); - } - - @Override - public void onActivityPinned(String packageName, int userId, int taskId, - int stackId) { - PipController.this.onActivityPinned(packageName); - } - - @Override - public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, - boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { - PipController.this.onActivityRestartAttempt(task, clearedTask); - } - }); - } - - private void loadConfigurationsAndApply(Configuration newConfig) { - if (mLastOrientation != newConfig.orientation) { - // Don't resize the pinned stack on orientation change. TV does not care about this case - // and this could clobber the existing animation to the new bounds calculated by WM. - mLastOrientation = newConfig.orientation; - return; - } - - final Rect menuBounds = Rect.unflattenFromString( - mContext.getResources().getString(R.string.pip_menu_bounds)); - mPipBoundsState.setExpandedBounds(menuBounds); - - resizePinnedStack(getPinnedTaskInfo() == null ? STATE_NO_PIP : STATE_PIP); - } - - /** - * Updates the PIP per configuration changed. - */ - @Override - public void onConfigurationChanged(Configuration newConfig) { - loadConfigurationsAndApply(newConfig); - mPipNotification.onConfigurationChanged(mContext); - } - - /** - * Shows the picture-in-picture menu if an activity is in picture-in-picture mode. - */ - public void showPictureInPictureMenu() { - if (DEBUG) Log.d(TAG, "showPictureInPictureMenu(), current state=" + getStateDescription()); - - if (getState() == STATE_PIP) { - resizePinnedStack(STATE_PIP_MENU); - } - } - - /** - * Closes PIP (PIPed activity and PIP system UI). - */ - @Override - public void closePip() { - if (DEBUG) Log.d(TAG, "closePip(), current state=" + getStateDescription()); - - closePipInternal(true); - } - - private void closePipInternal(boolean removePipStack) { - if (DEBUG) { - Log.d(TAG, - "closePipInternal() removePipStack=" + removePipStack + ", current state=" - + getStateDescription()); - } - - mState = STATE_NO_PIP; - mPipTaskId = TASK_ID_NO_PIP; - if (removePipStack) { - try { - mActivityTaskManager.removeTask(mPinnedStackId); - } catch (RemoteException e) { - Log.e(TAG, "removeTask failed", e); - } finally { - mPinnedStackId = INVALID_STACK_ID; - } - } - mPipNotification.dismiss(); - mTvPipMenuController.hideMenu(); - mHandler.removeCallbacks(mClosePipRunnable); - } - - @Override - public void movePipToNormalPosition() { - resizePinnedStack(PipController.STATE_PIP); - } - - /** - * Moves the PIPed activity to the fullscreen and closes PIP system UI. - */ - @Override - public void movePipToFullscreen() { - if (DEBUG) Log.d(TAG, "movePipToFullscreen(), current state=" + getStateDescription()); - - mPipTaskId = TASK_ID_NO_PIP; - mTvPipMenuController.hideMenu(); - mPipNotification.dismiss(); - - resizePinnedStack(STATE_NO_PIP); - } - - private void onActivityPinned(String packageName) { - final RootTaskInfo taskInfo = getPinnedTaskInfo(); - if (DEBUG) Log.d(TAG, "onActivityPinned, task=" + taskInfo); - if (taskInfo == null) { - Log.w(TAG, "Cannot find pinned stack"); - return; - } - - // At this point PipBoundsState knows the correct aspect ratio for this pinned task, so we - // use PipBoundsAlgorithm to calculate the normal bounds for the task (PipBoundsAlgorithm - // will query PipBoundsState for the aspect ratio) and pass the bounds over to the - // PipBoundsState. - mPipBoundsState.setNormalBounds(mPipBoundsAlgorithm.getNormalBounds()); - - mPinnedStackId = taskInfo.taskId; - mPipTaskId = taskInfo.childTaskIds[taskInfo.childTaskIds.length - 1]; - - // Set state to STATE_PIP so we show it when the pinned stack animation ends. - mState = STATE_PIP; - mPipMediaController.onActivityPinned(); - mPipNotification.show(packageName); - } - - private void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, - boolean clearedTask) { - if (task.getWindowingMode() != WINDOWING_MODE_PINNED) { - return; - } - if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()"); - - // If PIPed activity is launched again by Launcher or intent, make it fullscreen. - movePipToFullscreen(); - } - - private void onTaskStackChanged() { - if (DEBUG) Log.d(TAG, "onTaskStackChanged()"); - - if (getState() != STATE_NO_PIP) { - boolean hasPip = false; - - RootTaskInfo taskInfo = getPinnedTaskInfo(); - if (taskInfo == null || taskInfo.childTaskIds == null) { - Log.w(TAG, "There is nothing in pinned stack"); - closePipInternal(false); - return; - } - for (int i = taskInfo.childTaskIds.length - 1; i >= 0; --i) { - if (taskInfo.childTaskIds[i] == mPipTaskId) { - // PIP task is still alive. - hasPip = true; - break; - } - } - if (!hasPip) { - // PIP task doesn't exist anymore in PINNED_STACK. - closePipInternal(true); - return; - } - } - if (getState() == STATE_PIP) { - if (!Objects.equals(mPipBoundsState.getBounds(), mPipBoundsState.getNormalBounds())) { - resizePinnedStack(STATE_PIP); - } - } - } - - /** - * Resize the Pip to the appropriate size for the input state. - * - * @param state In Pip state also used to determine the new size for the Pip. - */ - public void resizePinnedStack(int state) { - if (DEBUG) { - Log.d(TAG, "resizePinnedStack() state=" + stateToName(state) + ", current state=" - + getStateDescription(), new Exception()); - } - final boolean wasStateNoPip = (mState == STATE_NO_PIP); - mTvPipMenuController.hideMenu(); - mState = state; - final Rect newBounds; - switch (mState) { - case STATE_NO_PIP: - newBounds = null; - // If the state was already STATE_NO_PIP, then do not resize the stack below as it - // will not exist - if (wasStateNoPip) { - return; - } - break; - case STATE_PIP_MENU: - newBounds = mPipBoundsState.getExpandedBounds(); - break; - case STATE_PIP: // fallthrough - default: - newBounds = mPipBoundsState.getNormalBounds(); - break; - } - if (newBounds != null) { - mPipTaskOrganizer.scheduleAnimateResizePip(newBounds, mResizeAnimationDuration, null); - } else { - mPipTaskOrganizer.exitPip(mResizeAnimationDuration); - } - } - - /** - * @return the current state. - */ - private int getState() { - return mState; - } - - private void showPipMenu() { - if (DEBUG) Log.d(TAG, "showPipMenu(), current state=" + getStateDescription()); - - mState = STATE_PIP_MENU; - mTvPipMenuController.showMenu(); - } - - /** - * Returns {@code true} if PIP is shown. - */ - public boolean isPipShown() { - return mState != STATE_NO_PIP; - } - - private RootTaskInfo getPinnedTaskInfo() { - RootTaskInfo taskInfo = null; - try { - taskInfo = ActivityTaskManager.getService().getRootTaskInfo( - WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); - } catch (RemoteException e) { - Log.e(TAG, "getRootTaskInfo failed", e); - } - if (DEBUG) Log.d(TAG, "getPinnedTaskInfo(), taskInfo=" + taskInfo); - return taskInfo; - } - - private void handleMediaResourceGranted(String[] packageNames) { - if (getState() == STATE_NO_PIP) { - mLastPackagesResourceGranted = packageNames; - } else { - boolean requestedFromLastPackages = false; - if (mLastPackagesResourceGranted != null) { - for (String packageName : mLastPackagesResourceGranted) { - for (String newPackageName : packageNames) { - if (TextUtils.equals(newPackageName, packageName)) { - requestedFromLastPackages = true; - break; - } - } - } - } - mLastPackagesResourceGranted = packageNames; - if (!requestedFromLastPackages) { - closePip(); - } - } - } - - @Override - public void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) { - } - - PipMediaController getPipMediaController() { - return mPipMediaController; - } - - @Override - public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) { - } - - @Override - public void onPipTransitionFinished(ComponentName activity, int direction) { - onPipTransitionFinishedOrCanceled(); - } - - @Override - public void onPipTransitionCanceled(ComponentName activity, int direction) { - onPipTransitionFinishedOrCanceled(); - } - - private void onPipTransitionFinishedOrCanceled() { - if (DEBUG) Log.d(TAG, "onPipTransitionFinishedOrCanceled()"); - - if (getState() == STATE_PIP_MENU) { - showPipMenu(); - } - } - - private String getStateDescription() { - return stateToName(mState); - } - - private static String stateToName(int state) { - switch (state) { - case STATE_NO_PIP: - return "NO_PIP"; - - case STATE_PIP: - return "PIP"; - - case STATE_PIP_MENU: - return "PIP_MENU"; - - default: - return "UNKNOWN(" + state + ")"; - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java new file mode 100644 index 000000000000..8bc60f9dbcd9 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java @@ -0,0 +1,421 @@ +/* + * 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.tv; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; + +import android.annotation.IntDef; +import android.app.ActivityManager; +import android.app.ActivityTaskManager; +import android.app.RemoteAction; +import android.app.TaskInfo; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.ParceledListSlice; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Rect; +import android.os.RemoteException; +import android.util.Log; +import android.view.DisplayInfo; + +import com.android.wm.shell.R; +import com.android.wm.shell.WindowManagerShellWrapper; +import com.android.wm.shell.common.TaskStackListenerCallback; +import com.android.wm.shell.common.TaskStackListenerImpl; +import com.android.wm.shell.pip.PinnedStackListenerForwarder; +import com.android.wm.shell.pip.Pip; +import com.android.wm.shell.pip.PipBoundsAlgorithm; +import com.android.wm.shell.pip.PipBoundsState; +import com.android.wm.shell.pip.PipMediaController; +import com.android.wm.shell.pip.PipTaskOrganizer; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Manages the picture-in-picture (PIP) UI and states. + */ +public class TvPipController implements Pip, PipTaskOrganizer.PipTransitionCallback, + TvPipMenuController.Delegate, TvPipNotificationController.Delegate { + private static final String TAG = "TvPipController"; + static final boolean DEBUG = true; + + private static final int NONEXISTENT_TASK_ID = -1; + + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "STATE_" }, value = { + STATE_NO_PIP, + STATE_PIP, + STATE_PIP_MENU + }) + public @interface State {} + + /** + * State when there is no applications in Pip. + */ + private static final int STATE_NO_PIP = 0; + /** + * State when there is an applications in Pip and the Pip window located at its "normal" place + * (usually the bottom right corner). + */ + private static final int STATE_PIP = 1; + /** + * State when there is an applications in Pip and the Pip menu is open. In this state Pip window + * is usually moved from its "normal" position on the screen to the "menu" position - which is + * often at the middle of the screen, and gets slightly scaled up. + */ + private static final int STATE_PIP_MENU = 2; + + private final Context mContext; + + private final PipBoundsState mPipBoundsState; + private final PipBoundsAlgorithm mPipBoundsAlgorithm; + private final PipTaskOrganizer mPipTaskOrganizer; + private final PipMediaController mPipMediaController; + private final TvPipNotificationController mPipNotificationController; + private final TvPipMenuController mTvPipMenuController; + + private @State int mState = STATE_NO_PIP; + private int mPinnedTaskId = NONEXISTENT_TASK_ID; + + private int mResizeAnimationDuration; + + public TvPipController( + Context context, + PipBoundsState pipBoundsState, + PipBoundsAlgorithm pipBoundsAlgorithm, + PipTaskOrganizer pipTaskOrganizer, + TvPipMenuController tvPipMenuController, + PipMediaController pipMediaController, + TvPipNotificationController pipNotificationController, + TaskStackListenerImpl taskStackListener, + WindowManagerShellWrapper wmShell) { + mContext = context; + + mPipBoundsState = pipBoundsState; + mPipBoundsState.setDisplayInfo(getDisplayInfo()); + mPipBoundsAlgorithm = pipBoundsAlgorithm; + + mPipMediaController = pipMediaController; + + mPipNotificationController = pipNotificationController; + mPipNotificationController.setDelegate(this); + + mTvPipMenuController = tvPipMenuController; + mTvPipMenuController.setDelegate(this); + + mPipTaskOrganizer = pipTaskOrganizer; + mPipTaskOrganizer.registerPipTransitionCallback(this); + + loadConfigurations(); + + registerTaskStackListenerCallback(taskStackListener); + registerWmShellPinnedStackListener(wmShell); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + if (DEBUG) Log.d(TAG, "onConfigurationChanged(), state=" + stateToName(mState)); + + if (isPipShown()) { + if (DEBUG) Log.d(TAG, " > closing Pip."); + closePip(); + } + + loadConfigurations(); + mPipNotificationController.onConfigurationChanged(mContext); + } + + /** + * Returns {@code true} if Pip is shown. + */ + @Override + public boolean isPipShown() { + return mState != STATE_NO_PIP; + } + + /** + * Starts the process if bringing up the Pip menu if by issuing a command to move Pip + * task/window to the "Menu" position. We'll show the actual Menu UI (eg. actions) once the Pip + * task/window is properly positioned in {@link #onPipTransitionFinished(ComponentName, int)}. + */ + @Override + public void showPictureInPictureMenu() { + if (DEBUG) Log.d(TAG, "showPictureInPictureMenu(), state=" + stateToName(mState)); + + if (mState != STATE_PIP) { + if (DEBUG) Log.d(TAG, " > cannot open Menu from the current state."); + return; + } + + setState(STATE_PIP_MENU); + resizePinnedStack(STATE_PIP_MENU); + } + + /** + * Moves Pip window to its "normal" position. + */ + @Override + public void movePipToNormalPosition() { + if (DEBUG) Log.d(TAG, "movePipToNormalPosition(), state=" + stateToName(mState)); + + setState(STATE_PIP); + resizePinnedStack(STATE_PIP); + } + + /** + * Opens the "Pip-ed" Activity fullscreen. + */ + @Override + public void movePipToFullscreen() { + if (DEBUG) Log.d(TAG, "movePipToFullscreen(), state=" + stateToName(mState)); + + mPipTaskOrganizer.exitPip(mResizeAnimationDuration); + onPipDisappeared(); + } + + /** + * Closes Pip window. + */ + @Override + public void closePip() { + if (DEBUG) Log.d(TAG, "closePip(), state=" + stateToName(mState)); + + removeTask(mPinnedTaskId); + onPipDisappeared(); + } + + /** + * Resizes the Pip task/window to the appropriate size for the given state. + * This is a legacy API. Now we expect that the state argument passed to it should always match + * the current state of the Controller. If it does not match an {@link IllegalArgumentException} + * will be thrown. However, if the passed state does match - we'll determine the right bounds + * to the state and will move Pip task/window there. + * + * @param state the to determine the Pip bounds. IMPORTANT: should always match the current + * state of the Controller. + */ + @Override + public void resizePinnedStack(@State int state) { + if (state != mState) { + throw new IllegalArgumentException("The passed state should match the current state!"); + } + if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + stateToName(mState)); + + final Rect newBounds; + switch (mState) { + case STATE_PIP_MENU: + newBounds = mPipBoundsState.getExpandedBounds(); + break; + + case STATE_PIP: + // Let PipBoundsAlgorithm figure out what the correct bounds are at the moment. + // Internally, it will get the "default" bounds from PipBoundsState and adjust them + // as needed to account for things like IME state (will query PipBoundsState for + // this information as well, so it's important to keep PipBoundsState up to date). + newBounds = mPipBoundsAlgorithm.getNormalBounds(); + break; + + case STATE_NO_PIP: + default: + return; + } + + mPipTaskOrganizer.scheduleAnimateResizePip(newBounds, mResizeAnimationDuration, null); + } + + @Override + public void registerSessionListenerForCurrentUser() { + mPipMediaController.registerSessionListenerForCurrentUser(); + } + + private void checkIfPinnedTaskAppeared() { + final TaskInfo pinnedTask = getPinnedTaskInfo(); + if (DEBUG) Log.d(TAG, "checkIfPinnedTaskAppeared(), task=" + pinnedTask); + if (pinnedTask == null) return; + mPinnedTaskId = pinnedTask.taskId; + setState(STATE_PIP); + + mPipMediaController.onActivityPinned(); + mPipNotificationController.show(pinnedTask.topActivity.getPackageName()); + } + + private void checkIfPinnedTaskIsGone() { + if (DEBUG) Log.d(TAG, "onTaskStackChanged()"); + + if (isPipShown() && getPinnedTaskInfo() == null) { + Log.w(TAG, "Pinned task is gone."); + onPipDisappeared(); + } + } + + private void onPipDisappeared() { + if (DEBUG) Log.d(TAG, "onPipDisappeared() state=" + stateToName(mState)); + + mPipNotificationController.dismiss(); + mTvPipMenuController.hideMenu(); + setState(STATE_NO_PIP); + mPinnedTaskId = NONEXISTENT_TASK_ID; + } + + @Override + public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) { + if (DEBUG) Log.d(TAG, "onPipTransition_Started(), state=" + stateToName(mState)); + } + + @Override + public void onPipTransitionCanceled(ComponentName activity, int direction) { + if (DEBUG) Log.d(TAG, "onPipTransition_Canceled(), state=" + stateToName(mState)); + } + + @Override + public void onPipTransitionFinished(ComponentName activity, int direction) { + if (DEBUG) Log.d(TAG, "onPipTransition_Finished(), state=" + stateToName(mState)); + + if (mState == STATE_PIP_MENU) { + if (DEBUG) Log.d(TAG, " > show menu"); + mTvPipMenuController.showMenu(); + } + } + + private void setState(@State int state) { + if (DEBUG) { + Log.d(TAG, "setState(), state=" + stateToName(state) + ", prev=" + + stateToName(mState)); + } + mState = state; + } + + private void loadConfigurations() { + final Resources res = mContext.getResources(); + mResizeAnimationDuration = res.getInteger(R.integer.config_pipResizeAnimationDuration); + // "Cache" bounds for the Pip menu as "expanded" bounds in PipBoundsState. We'll refer back + // to this value in resizePinnedStack(), when we are adjusting Pip task/window position for + // the menu. + mPipBoundsState.setExpandedBounds( + Rect.unflattenFromString(res.getString(R.string.pip_menu_bounds))); + } + + private DisplayInfo getDisplayInfo() { + final DisplayInfo displayInfo = new DisplayInfo(); + mContext.getDisplay().getDisplayInfo(displayInfo); + return displayInfo; + } + + private void registerTaskStackListenerCallback(TaskStackListenerImpl taskStackListener) { + taskStackListener.addListener(new TaskStackListenerCallback() { + @Override + public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { + checkIfPinnedTaskAppeared(); + } + + @Override + public void onTaskStackChanged() { + checkIfPinnedTaskIsGone(); + } + + @Override + public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, + boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { + if (task.getWindowingMode() == WINDOWING_MODE_PINNED) { + if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()"); + + // If the "Pip-ed" Activity is launched again by Launcher or intent, make it + // fullscreen. + movePipToFullscreen(); + } + } + }); + } + + private void registerWmShellPinnedStackListener(WindowManagerShellWrapper wmShell) { + try { + wmShell.addPinnedStackListener(new PinnedStackListenerForwarder.PinnedStackListener() { + @Override + public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { + if (DEBUG) { + Log.d(TAG, "onImeVisibilityChanged(), visible=" + imeVisible + + ", height=" + imeHeight); + } + + if (imeVisible == mPipBoundsState.isImeShowing() + && (!imeVisible || imeHeight == mPipBoundsState.getImeHeight())) { + // Nothing changed: either IME has been and remains invisible, or remains + // visible with the same height. + return; + } + mPipBoundsState.setImeVisibility(imeVisible, imeHeight); + // "Normal" Pip bounds may have changed, so if we are in the "normal" state, + // let's update the bounds. + if (mState == STATE_PIP) { + resizePinnedStack(STATE_PIP); + } + } + + @Override + public void onMovementBoundsChanged(boolean fromImeAdjustment) {} + + @Override + public void onActionsChanged(ParceledListSlice<RemoteAction> actions) { + if (DEBUG) Log.d(TAG, "onActionsChanged()"); + + mTvPipMenuController.setAppActions(actions); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Failed to register pinned stack listener", e); + } + } + + private static TaskInfo getPinnedTaskInfo() { + if (DEBUG) Log.d(TAG, "getPinnedTaskInfo()"); + try { + final TaskInfo taskInfo = ActivityTaskManager.getService().getRootTaskInfo( + WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); + if (DEBUG) Log.d(TAG, " > taskInfo=" + taskInfo); + return taskInfo; + } catch (RemoteException e) { + Log.e(TAG, "getRootTaskInfo() failed", e); + return null; + } + } + + private static void removeTask(int taskId) { + if (DEBUG) Log.d(TAG, "removeTask(), taskId=" + taskId); + try { + ActivityTaskManager.getService().removeTask(taskId); + } catch (Exception e) { + Log.e(TAG, "Atm.removeTask() failed", e); + } + } + + private static String stateToName(@State int state) { + switch (state) { + case STATE_NO_PIP: + return "NO_PIP"; + case STATE_PIP: + return "PIP"; + case STATE_PIP_MENU: + return "PIP_MENU"; + default: + // This can't happen. + throw new IllegalArgumentException("Unknown state " + state); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java index 9192cf14cd9b..470ab0c9c0e3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java @@ -42,7 +42,7 @@ import java.util.List; */ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Listener { private static final String TAG = "TvPipMenuController"; - private static final boolean DEBUG = PipController.DEBUG; + private static final boolean DEBUG = TvPipController.DEBUG; private final Context mContext; private final SystemWindows mSystemWindows; @@ -134,10 +134,18 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis } void hideMenu() { - if (DEBUG) Log.d(TAG, "hideMenu()"); + hideMenu(true); + } + + void hideMenu(boolean movePipWindow) { + if (DEBUG) Log.d(TAG, "hideMenu(), movePipWindow=" + movePipWindow); + + if (!isMenuVisible()) { + return; + } - if (isMenuVisible()) { - mMenuView.hide(); + mMenuView.hide(); + if (movePipWindow) { mDelegate.movePipToNormalPosition(); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java index f7b76c1ec745..e08ca52fd2ca 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java @@ -53,7 +53,7 @@ import java.util.List; */ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { private static final String TAG = "TvPipMenuView"; - private static final boolean DEBUG = PipController.DEBUG; + private static final boolean DEBUG = TvPipController.DEBUG; private static final float DISABLED_ACTION_ALPHA = 0.54f; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java index 5716c7fc7162..ce4b60893367 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java @@ -19,14 +19,17 @@ package com.android.wm.shell.pip.tv; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.content.res.Resources; import android.graphics.Bitmap; import android.media.MediaMetadata; +import android.os.UserHandle; import android.text.TextUtils; +import android.util.Log; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.wm.shell.R; @@ -39,22 +42,27 @@ import java.util.Objects; * <p>Once it's created, it will manage the PIP notification UI by itself except for handling * configuration changes. */ -public class PipNotification { - private static final boolean DEBUG = PipController.DEBUG; - private static final String TAG = "PipNotification"; +public class TvPipNotificationController { + private static final String TAG = "TvPipNotification"; + private static final boolean DEBUG = TvPipController.DEBUG; - private static final String NOTIFICATION_TAG = PipNotification.class.getSimpleName(); - public static final String NOTIFICATION_CHANNEL_TVPIP = "TPP"; + // Referenced in com.android.systemui.util.NotificationChannels. + public static final String NOTIFICATION_CHANNEL = "TVPIP"; + private static final String NOTIFICATION_TAG = "TvPip"; - static final String ACTION_MENU = "PipNotification.menu"; - static final String ACTION_CLOSE = "PipNotification.close"; + private static final String ACTION_SHOW_PIP_MENU = + "com.android.wm.shell.pip.tv.notification.action.SHOW_PIP_MENU"; + private static final String ACTION_CLOSE_PIP = + "com.android.wm.shell.pip.tv.notification.action.CLOSE_PIP"; + private final Context mContext; private final PackageManager mPackageManager; private final NotificationManager mNotificationManager; private final Notification.Builder mNotificationBuilder; + private final ActionBroadcastReceiver mActionBroadcastReceiver; + private Delegate mDelegate; private String mDefaultTitle; - private int mDefaultIconResId; /** Package name for the application that owns PiP window. */ private String mPackageName; @@ -62,32 +70,56 @@ public class PipNotification { private String mMediaTitle; private Bitmap mArt; - public PipNotification(Context context, PipMediaController pipMediaController) { + public TvPipNotificationController(Context context, PipMediaController pipMediaController) { + mContext = context; mPackageManager = context.getPackageManager(); mNotificationManager = context.getSystemService(NotificationManager.class); - mNotificationBuilder = new Notification.Builder(context, NOTIFICATION_CHANNEL_TVPIP) + mNotificationBuilder = new Notification.Builder(context, NOTIFICATION_CHANNEL) .setLocalOnly(true) .setOngoing(false) .setCategory(Notification.CATEGORY_SYSTEM) + .setShowWhen(true) + .setSmallIcon(R.drawable.pip_icon) .extend(new Notification.TvExtender() - .setContentIntent(createPendingIntent(context, ACTION_MENU)) - .setDeleteIntent(createPendingIntent(context, ACTION_CLOSE))); + .setContentIntent(createPendingIntent(context, ACTION_SHOW_PIP_MENU)) + .setDeleteIntent(createPendingIntent(context, ACTION_CLOSE_PIP))); + + mActionBroadcastReceiver = new ActionBroadcastReceiver(); pipMediaController.addMetadataListener(this::onMediaMetadataChanged); onConfigurationChanged(context); } + void setDelegate(Delegate delegate) { + if (DEBUG) Log.d(TAG, "setDelegate(), delegate=" + delegate); + if (mDelegate != null) { + throw new IllegalStateException( + "The delegate has already been set and should not change."); + } + if (delegate == null) { + throw new IllegalArgumentException("The delegate must not be null."); + } + + mDelegate = delegate; + } + void show(String packageName) { + if (mDelegate == null) { + throw new IllegalStateException("Delegate is not set."); + } + mPackageName = packageName; update(); + mActionBroadcastReceiver.register(); } void dismiss() { mNotificationManager.cancel(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP); mNotified = false; mPackageName = null; + mActionBroadcastReceiver.unregister(); } private void onMediaMetadataChanged(MediaMetadata metadata) { @@ -101,11 +133,9 @@ public class PipNotification { * Called by {@link PipController} when the configuration is changed. */ void onConfigurationChanged(Context context) { - Resources res = context.getResources(); - mDefaultTitle = res.getString(R.string.pip_notification_unknown_title); - mDefaultIconResId = R.drawable.pip_icon; + mDefaultTitle = context.getResources().getString(R.string.pip_notification_unknown_title); if (mNotified) { - // update notification + // Update the notification. update(); } } @@ -113,9 +143,7 @@ public class PipNotification { private void update() { mNotified = true; mNotificationBuilder - .setShowWhen(true) .setWhen(System.currentTimeMillis()) - .setSmallIcon(mDefaultIconResId) .setContentTitle(getNotificationTitle()); if (mArt != null) { mNotificationBuilder.setStyle(new Notification.BigPictureStyle() @@ -178,4 +206,45 @@ public class PipNotification { return PendingIntent.getBroadcast(context, 0, new Intent(action), PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); } + + private class ActionBroadcastReceiver extends BroadcastReceiver { + final IntentFilter mIntentFilter; + { + mIntentFilter = new IntentFilter(); + mIntentFilter.addAction(ACTION_CLOSE_PIP); + mIntentFilter.addAction(ACTION_SHOW_PIP_MENU); + } + boolean mRegistered = false; + + void register() { + if (mRegistered) return; + + mContext.registerReceiver(this, mIntentFilter, UserHandle.USER_ALL); + mRegistered = true; + } + + void unregister() { + if (!mRegistered) return; + + mContext.unregisterReceiver(this); + mRegistered = false; + } + + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (DEBUG) Log.d(TAG, "on(Broadcast)Receive(), action=" + action); + + if (ACTION_SHOW_PIP_MENU.equals(action)) { + mDelegate.showPictureInPictureMenu(); + } else if (ACTION_CLOSE_PIP.equals(action)) { + mDelegate.closePip(); + } + } + } + + interface Delegate { + void showPictureInPictureMenu(); + void closePip(); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 5213f6c80989..7ce71b0a1158 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2021 The Android Open 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.wm.shell; +package com.android.wm.shell.transition; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; @@ -42,6 +42,7 @@ import android.window.WindowOrganizer; import androidx.annotation.BinderThread; import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.protolog.ShellProtoLogGroup; @@ -82,6 +83,7 @@ public class Transitions { mPlayerImpl = new TransitionPlayerImpl(); } + /** Register this transition handler with Core */ public void register(ShellTaskOrganizer taskOrganizer) { taskOrganizer.registerTransitionPlayer(mPlayerImpl); } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt index 75388bf2a189..5258e9030075 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt @@ -171,4 +171,4 @@ private val StatusBarNotification.deleteIntent: PendingIntent? get() = tvExtensions?.getParcelable("delete_intent") private fun StatusBarNotification.isPipNotificationWithTitle(expectedTitle: String): Boolean = - tag == "PipNotification" && title == expectedTitle
\ No newline at end of file + tag == "TvPip" && title == expectedTitle
\ No newline at end of file diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 602364e9e01a..a4abf3693096 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -196,8 +196,9 @@ static fields_t gFields; static int IP_V4_LENGTH = 4; static int IP_V6_LENGTH = 16; -void DestroyCallback(const C2Buffer * /* buf */, void *arg) { +void DestroyCallback(const C2Buffer * buf, void *arg) { android::sp<android::MediaEvent> event = (android::MediaEvent *)arg; + android::Mutex::Autolock autoLock(event->mLock); if (event->mLinearBlockObj != NULL) { JNIEnv *env = android::AndroidRuntime::getJNIEnv(); env->DeleteWeakGlobalRef(event->mLinearBlockObj); @@ -206,6 +207,7 @@ void DestroyCallback(const C2Buffer * /* buf */, void *arg) { event->mAvHandleRefCnt--; event->finalize(); + event->decStrong(buf); } namespace android { @@ -408,6 +410,7 @@ jobject MediaEvent::getLinearBlock() { pC2Buffer->setInfo(info); } pC2Buffer->registerOnDestroyNotify(&DestroyCallback, this); + incStrong(pC2Buffer.get()); jobject linearBlock = env->NewObject( env->FindClass("android/media/MediaCodec$LinearBlock"), @@ -4411,6 +4414,7 @@ static jobject android_media_tv_Tuner_media_event_get_linear_block( ALOGD("Failed get MediaEvent"); return NULL; } + android::Mutex::Autolock autoLock(mediaEventSp->mLock); return mediaEventSp->getLinearBlock(); } diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp index a26f715280a1..c8f3bd3666e4 100644 --- a/packages/Connectivity/service/Android.bp +++ b/packages/Connectivity/service/Android.bp @@ -14,8 +14,8 @@ // limitations under the License. // -cc_defaults { - name: "libservice-connectivity-defaults", +cc_library_shared { + name: "libservice-connectivity", // TODO: build against the NDK (sdk_version: "30" for example) cflags: [ "-Wall", @@ -26,6 +26,7 @@ cc_defaults { srcs: [ "jni/com_android_server_TestNetworkService.cpp", "jni/com_android_server_connectivity_Vpn.cpp", + "jni/onload.cpp", ], shared_libs: [ "libbase", @@ -35,27 +36,11 @@ cc_defaults { // addresses, and remove dependency on libnetutils. "libnetutils", ], -} - -cc_library_shared { - name: "libservice-connectivity", - defaults: ["libservice-connectivity-defaults"], - srcs: [ - "jni/onload.cpp", - ], apex_available: [ - // TODO: move this library to the tethering APEX and remove libservice-connectivity-static - // "com.android.tethering", + "com.android.tethering", ], } -// Static library linked into libservices.core until libservice-connectivity can be loaded from -// the tethering APEX instead. -cc_library_static { - name: "libservice-connectivity-static", - defaults: ["libservice-connectivity-defaults"], -} - java_library { name: "service-connectivity", srcs: [ @@ -75,5 +60,6 @@ java_library { ], apex_available: [ "//apex_available:platform", + "com.android.tethering", ], } diff --git a/packages/PrintSpooler/res/values-or/strings.xml b/packages/PrintSpooler/res/values-or/strings.xml index 7000b95b35d6..15cecd6f46cc 100644 --- a/packages/PrintSpooler/res/values-or/strings.xml +++ b/packages/PrintSpooler/res/values-or/strings.xml @@ -80,10 +80,10 @@ <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g>ଟି ପ୍ରିଣ୍ଟର୍ ଖୋଜିବା ପାଇଁ ଇନଷ୍ଟଲ୍ କରନ୍ତୁ</item> </plurals> <string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ପ୍ରିଣ୍ଟ କରାଯାଉଛି"</string> - <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> କ୍ୟାନ୍ସଲ୍ କରାଯାଉଛି"</string> + <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ବାତିଲ୍ କରାଯାଉଛି"</string> <string name="failed_notification_title_template" msgid="2256217208186530973">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ପ୍ରିଣ୍ଟର୍ ତ୍ରୁଟି"</string> <string name="blocked_notification_title_template" msgid="1175435827331588646">"ପ୍ରିଣ୍ଟର୍ ଦ୍ୱାରା ରୋକାଯାଇଥିବା <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string> - <string name="cancel" msgid="4373674107267141885">"କ୍ୟାନ୍ସଲ୍"</string> + <string name="cancel" msgid="4373674107267141885">"ବାତିଲ୍"</string> <string name="restart" msgid="2472034227037808749">"ରିଷ୍ଟାର୍ଟ କରନ୍ତୁ"</string> <string name="no_connection_to_printer" msgid="2159246915977282728">"ପ୍ରିଣ୍ଟର୍କୁ କୌଣସି ସଂଯୋଗ ନାହିଁ"</string> <string name="reason_unknown" msgid="5507940196503246139">"ଅଜଣା"</string> diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index c1c1d65fb9d7..e22f2649d540 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -217,6 +217,7 @@ public class SettingsBackupTest { Settings.Global.DEBUG_APP, Settings.Global.DEBUG_VIEW_ATTRIBUTES, Settings.Global.DEBUG_VIEW_ATTRIBUTES_APPLICATION_PACKAGE, + Settings.Global.DECORATED_CUSTOM_VIEW_NOTIF_DECORATION, Settings.Global.DEFAULT_DNS_SERVER, Settings.Global.DEFAULT_INSTALL_LOCATION, Settings.Global.DEFAULT_RESTRICT_BACKGROUND_DATA, @@ -285,6 +286,7 @@ public class SettingsBackupTest { Settings.Global.WIFI_ON_WHEN_PROXY_DISCONNECTED, Settings.Global.FSTRIM_MANDATORY_INTERVAL, Settings.Global.FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED, + Settings.Global.FULLY_CUSTOM_VIEW_NOTIF_DECORATION, Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST, Settings.Global.GLOBAL_HTTP_PROXY_HOST, Settings.Global.GLOBAL_HTTP_PROXY_PAC, diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 0a43014dee68..1415119117f8 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -379,6 +379,9 @@ <uses-permission android:name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS" /> <uses-permission android:name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" /> + <!-- Permission required for CTS tests to enable/disable rate limiting toasts. --> + <uses-permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index 19f82480284e..55bdebd2f582 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -89,7 +89,6 @@ 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" @@ -97,7 +96,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:gravity="center_horizontal" - android:textSize="200dp" + android:textSize="@dimen/large_clock_text_size" android:letterSpacing="0.02" android:lineSpacingMultiplier=".8" android:includeFontPadding="false" diff --git a/packages/SystemUI/res-keyguard/values-ml/strings.xml b/packages/SystemUI/res-keyguard/values-ml/strings.xml index 66d0ac756bf5..80761591e0bf 100644 --- a/packages/SystemUI/res-keyguard/values-ml/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ml/strings.xml @@ -38,7 +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="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ബാറ്ററി നില ഒപ്റ്റിമൈസ് ചെയ്യുന്നു"</string> + <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<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/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index fe4ba63fe6c3..0beb286b6f63 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Gebruiker"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nuwe gebruiker"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nie gekoppel nie"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Geen netwerk nie"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi af"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Wys profiel"</string> <string name="user_add_user" msgid="4336657383006913022">"Voeg gebruiker by"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nuwe gebruiker"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Beëindig gastesessie?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle programme en data in hierdie sessie sal uitgevee word."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Beëindig sessie"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welkom terug, gas!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Wiil jy jou sessie voortsit?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Begin van voor af"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 9149aa636180..f56e84ae1606 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"ተጠቃሚ"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"አዲስ ተጠቃሚ"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"በይነመረብ"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"አልተገናኘም"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ምንም አውታረ መረብ የለም"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ጠፍቷል"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"መገለጫ አሳይ"</string> <string name="user_add_user" msgid="4336657383006913022">"ተጠቃሚ አክል"</string> <string name="user_new_user_name" msgid="2019166282704195789">"አዲስ ተጠቃሚ"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"የእንግዳ ክፍለ-ጊዜ ይብቃ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"በዚህ ክፍለ-ጊዜ ውስጥ ያሉ ሁሉም መተግበሪያዎች እና ውሂብ ይሰረዛሉ።"</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"ክፍለ-ጊዜን አብቃ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"እንኳን በደህና ተመለሱ እንግዳ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ክፍለ-ጊዜዎን መቀጠል ይፈልጋሉ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"እንደገና ጀምር"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 0146fcddf4fd..aaaf7788c5c3 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -357,8 +357,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"المستخدم"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"مستخدم جديد"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"الإنترنت"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ليست متصلة"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"لا تتوفر شبكة"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"إيقاف Wi-Fi"</string> @@ -464,11 +463,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"عرض الملف الشخصي"</string> <string name="user_add_user" msgid="4336657383006913022">"إضافة مستخدم"</string> <string name="user_new_user_name" msgid="2019166282704195789">"مستخدم جديد"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"هل تريد إنهاء جلسة الضيف؟"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"سيتم حذف كل التطبيقات والبيانات في هذه الجلسة."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"إنهاء الجلسة"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"مرحبًا بك مجددًا في جلسة الضيف"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"هل تريد متابعة جلستك؟"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"البدء من جديد"</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 3c206f2949a3..7e3e3cb04280 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -456,11 +456,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"প্ৰ\'ফাইল দেখুৱাওক"</string> <string name="user_add_user" msgid="4336657383006913022">"ব্যৱহাৰকাৰী যোগ কৰক"</string> <string name="user_new_user_name" msgid="2019166282704195789">"নতুন ব্যৱহাৰকাৰী"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"অতিথিৰ ছেশ্বন সমাপ্ত কৰিবনে?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই ছেশ্বনৰ সকলো এপ্ আৰু ডেটা মচা হ\'ব।"</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"ছেশ্বন সমাপ্ত কৰক"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"আপোনাক পুনৰাই স্বাগতম জনাইছোঁ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"আপুনি আপোনাৰ ছেশ্বন অব্যাহত ৰাখিব বিচাৰেনে?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"আকৌ আৰম্ভ কৰক"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 67e1718a03fb..6c22cc34b83e 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"İstifadəçi"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Yeni istifadəçi"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"İnternet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Bağlantı yoxdur"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Şəbəkə yoxdur"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi sönülüdür"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string> <string name="user_add_user" msgid="4336657383006913022">"İstifadəçi əlavə edin"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Yeni istifadəçi"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Qonaq sessiyası bitirilsin?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu sessiyada bütün tətbiqlər və data silinəcək."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Sessiyanı bitirin"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Xoş gəlmisiniz!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Sessiya davam etsin?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Yenidən başlayın"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index ae8ffbfbe304..2f3d6d28526e 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -354,8 +354,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Korisnik"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novi korisnik"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Veza nije uspostavljena"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nema mreže"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi je isključen"</string> @@ -458,11 +457,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Prikaži profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Dodaj korisnika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Želite da završite sesiju gosta?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji će biti izbrisani."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Završi sesiju"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Dobro došli nazad, goste!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li da nastavite sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni iz početka"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index b90d57e7ff5d..925e86a8916b 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -355,8 +355,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Карыстальнік"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Новы карыстальнік"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Інтэрнэт"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Няма падключэння"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Няма сеткi"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi адключаны"</string> @@ -460,11 +459,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Паказаць профіль"</string> <string name="user_add_user" msgid="4336657383006913022">"Дадаць карыстальніка"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Новы карыстальнік"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Завяршыць гасцявы сеанс?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усе праграмы і даныя гэтага сеанса будуць выдалены."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Завяршыць сеанс"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"З вяртаннем, госць!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Хочаце працягнуць сеанс?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Пачаць зноў"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index bfc8ef1d1151..6a683c742931 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Потребител"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Нов потребител"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Няма връзка"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Няма мрежа"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi е изключен"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Показване на потребителския профил"</string> <string name="user_add_user" msgid="4336657383006913022">"Добавяне на потребител"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Нов потребител"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Да се прекрати ли сесията като гост?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Всички приложения и данни в тази сесия ще бъдат изтрити."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Прекратяване на сесията"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Добре дошли отново в сесията като гост!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Искате ли да продължите сесията си?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Започване отначало"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index c41a43404628..953ec006016b 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"ব্যবহারকারী"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"নতুন ব্যবহারকারী"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ওয়াই-ফাই"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"ইন্টারনেট"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"সংযুক্ত নয়"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"কোনো নেটওয়ার্ক নেই"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ওয়াই-ফাই বন্ধ"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"প্রোফাইল দেখান"</string> <string name="user_add_user" msgid="4336657383006913022">"ব্যবহারকারী জুড়ুন"</string> <string name="user_new_user_name" msgid="2019166282704195789">"নতুন ব্যবহারকারী"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"গেস্ট সেশন শেষ করতে চান?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই সেশনের সব অ্যাপ্লিকেশান ও ডেটা মুছে ফেলা হবে।"</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"সেশন শেষ করুন"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"অতিথি, আপনি ফিরে আসায় আপনাকে স্বাগত!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"আপনি কি আপনার সেশনটি অবিরত রাখতে চান?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"আবার শুরু করুন"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 9a2877351c51..6bf185230602 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -354,8 +354,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Korisnik"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novi korisnik"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nije povezano"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nema mreže"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi je isključen"</string> @@ -458,11 +457,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Pokaži profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Dodaj korisnika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Završiti sesiju gosta?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i svi podaci iz ove sesije bit će izbrisani."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Završi sesiju"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Zdravo! Lijepo je opet vidjeti goste."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 426005af098a..71105cc55b50 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Usuari"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Usuari nou"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Desconnectat"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No hi ha cap xarxa"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desconnectada"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostra el perfil"</string> <string name="user_add_user" msgid="4336657383006913022">"Afegeix un usuari"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Usuari nou"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Vols finalitzar la sessió de convidat?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Totes les aplicacions i les dades d\'aquesta sessió se suprimiran."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Finalitza la sessió"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Benvingut de nou, convidat."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vols continuar amb la sessió?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Torna a començar"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 3e015ec7c720..65e676737ca2 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -355,8 +355,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Uživatel"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nový uživatel"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nepřipojeno"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Žádná síť"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi vypnuta"</string> @@ -460,11 +459,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Zobrazit profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Přidat uživatele"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nový uživatel"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Ukončit relaci hosta?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Veškeré aplikace a data v této relaci budou vymazána."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Ukončit relaci"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Vítejte zpět v relaci hosta!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcete v relaci pokračovat?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začít znovu"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 8fd48b1bdd02..e50aa57e70cb 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Bruger"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Ny bruger"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ikke forbundet"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Intet netværk"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi slået fra"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Vis profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Tilføj bruger"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Ny bruger"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Vil du afslutte gæstesessionen?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps og data i denne session slettes."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Afslut sessionen"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Velkommen tilbage, gæst!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vil du fortsætte din session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start forfra"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 84b63ba2db37..3cdebf9c80d9 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Nutzer"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Neuer Nutzer"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WLAN"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nicht verbunden"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Kein Netz"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WLAN aus"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profil öffnen"</string> <string name="user_add_user" msgid="4336657383006913022">"Nutzer hinzufügen"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Neuer Nutzer"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Gastsitzung beenden?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle Apps und Daten in dieser Sitzung werden gelöscht."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Sitzung beenden"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Willkommen zurück im Gastmodus"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Möchtest du deine Sitzung fortsetzen?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Neu starten"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index bec78c0e57d6..c6ae7f4b0c51 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Χρήστης"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Νέος χρήστης"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Διαδίκτυο"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Μη συνδεδεμένο"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Κανένα δίκτυο"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ανενεργό"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Εμφάνιση προφίλ"</string> <string name="user_add_user" msgid="4336657383006913022">"Προσθήκη χρήστη"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Νέος χρήστης"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Λήξη περιόδου σύνδεσης επισκέπτη;"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Όλες οι εφαρμογές και τα δεδομένα αυτής της περιόδου σύνδεσης θα διαγραφούν."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Λήξη περιόδου σύνδεσης"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Επισκέπτη , καλώς όρισες ξανά!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Θέλετε να συνεχίσετε την περίοδο σύνδεσής σας;"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Έναρξη από την αρχή"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 21cac18abeb5..ea25ec6e771c 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"User"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 7ac4c0df0ca1..a373a5c0061d 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"User"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 21cac18abeb5..ea25ec6e771c 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"User"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 21cac18abeb5..ea25ec6e771c 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"User"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 2f30c412a159..101d11245ffc 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"User"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 7ef9cdb96df7..1fc1609c7dcc 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Usuario"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Usuario nuevo"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Sin conexión"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sin red"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desactivada"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 6c6b511e2fe4..dd36a64812ab 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Usuario"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nuevo usuario"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"No conectado"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No hay red."</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desactivado"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index c64a07e3b61b..fb27be994265 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Kasutaja"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Uus kasutaja"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ühendus puudub"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Võrku pole"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi-ühendus on väljas"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Kuva profiil"</string> <string name="user_add_user" msgid="4336657383006913022">"Lisa kasutaja"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Uus kasutaja"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Kas lõpetada külastajaseanss?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Seansi kõik rakendused ja andmed kustutatakse."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Lõpeta seanss"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Tere tulemast tagasi, külaline!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Kas soovite seansiga jätkata?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Alusta uuesti"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 5047cf17fefd..d8b21c633dfa 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Erabiltzailea"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Erabiltzaile berria"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wifia"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Konektatu gabe"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ez dago sarerik"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi konexioa desaktibatuta"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 34c6bf66f64c..df306f83e16e 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"کاربر"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"کاربر جدید"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"اینترنت"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"متصل نیست"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"شبکهای موجود نیست"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi خاموش است"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"نمایش نمایه"</string> <string name="user_add_user" msgid="4336657383006913022">"افزودن کاربر"</string> <string name="user_new_user_name" msgid="2019166282704195789">"کاربر جدید"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"جلسه مهمان تمام شود؟"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"همه برنامهها و دادههای این جلسه حذف خواهد شد."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"پایان جلسه"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"مهمان گرامی، بازگشتتان را خوش آمد میگوییم!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"آیا میخواهید جلسهتان را ادامه دهید؟"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"شروع مجدد"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 5486e39133d8..4e0af373f572 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Käyttäjä"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Uusi käyttäjä"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ei yhteyttä"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ei verkkoa"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi-yhteys pois käytöstä"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Näytä profiili"</string> <string name="user_add_user" msgid="4336657383006913022">"Lisää käyttäjä"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Uusi käyttäjä"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Lopetetaanko Vierailija-käyttökerta?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Kaikki sovellukset ja tämän istunnon tiedot poistetaan."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Lopeta käyttökerta"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Tervetuloa takaisin!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Haluatko jatkaa istuntoa?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Aloita alusta"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 63f65e08e189..8c944a4a0e0f 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Utilisateur"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nouvel utilisateur"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Non connecté"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Aucun réseau"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi désactivé"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Afficher le profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Ajouter un utilisateur"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nouvel utilisateur"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Mettre fin à la session d\'invité?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Fermer la session"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bienvenue à nouveau dans la session Invité"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Voulez-vous poursuivre la session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recommencer"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index ebf199bfae90..7ad239dc4ee2 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Utilisateur"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nouvel utilisateur"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Non connecté"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Aucun réseau"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi désactivé"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Afficher le profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Ajouter un utilisateur"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nouvel utilisateur"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Fermer la session Invité ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Fermer la session"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bienvenue à nouveau dans la session Invité"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Voulez-vous poursuivre la dernière session ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Non, nouvelle session"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 36f7f4cd9fb7..145b3c06b387 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Usuario"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo usuario"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wifi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Non conectada"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Non hai rede"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wifi desactivada"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string> <string name="user_add_user" msgid="4336657383006913022">"Engadir usuario"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novo usuario"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Queres finalizar a sesión de invitado?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Eliminaranse todas as aplicacións e datos desta sesión."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Finalizar sesión"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Benvido de novo, convidado."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Queres continuar coa túa sesión?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Comezar de novo"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 66423e46c4e5..ea7af17dcb6a 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"વપરાશકર્તા"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"નવો વપરાશકર્તા"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"વાઇ-ફાઇ"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"ઇન્ટરનેટ"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"કનેક્ટ થયેલ નથી"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"કોઈ નેટવર્ક નથી"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"વાઇ-ફાઇ બંધ"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"પ્રોફાઇલ બતાવો"</string> <string name="user_add_user" msgid="4336657383006913022">"વપરાશકર્તા ઉમેરો"</string> <string name="user_new_user_name" msgid="2019166282704195789">"નવો વપરાશકર્તા"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"શું અતિથિ સત્ર સમાપ્ત કરીએ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"આ સત્રમાંની તમામ ઍપ્લિકેશનો અને ડેટા કાઢી નાખવામાં આવશે."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"સત્ર સમાપ્ત કરો"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ફરી સ્વાગત છે, અતિથિ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"શું તમે તમારું સત્ર ચાલુ કરવા માંગો છો?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"શરૂ કરો"</string> diff --git a/packages/SystemUI/res/values-h700dp/dimens.xml b/packages/SystemUI/res/values-h700dp/dimens.xml new file mode 100644 index 000000000000..fbd985eaa751 --- /dev/null +++ b/packages/SystemUI/res/values-h700dp/dimens.xml @@ -0,0 +1,20 @@ +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT 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> + <!-- Large clock maximum font size (dp is intentional, to prevent any further scaling) --> + <dimen name="large_clock_text_size">170dp</dimen> +</resources> diff --git a/packages/SystemUI/res/values-h800dp/dimens.xml b/packages/SystemUI/res/values-h800dp/dimens.xml index 6a0e880675df..cfacbec0dff2 100644 --- a/packages/SystemUI/res/values-h800dp/dimens.xml +++ b/packages/SystemUI/res/values-h800dp/dimens.xml @@ -17,4 +17,7 @@ <resources> <!-- Minimum margin between clock and top of screen or ambient indication --> <dimen name="keyguard_clock_top_margin">76dp</dimen> -</resources>
\ No newline at end of file + + <!-- Large clock maximum font size (dp is intentional, to prevent any further scaling) --> + <dimen name="large_clock_text_size">200dp</dimen> +</resources> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 65cba2f9dc09..a8bed5b26401 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -355,8 +355,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"उपयोगकर्ता"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"नया उपयोगकर्ता"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"वाई-फ़ाई"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"इंटरनेट"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"कनेक्ट नहीं है"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"कोई नेटवर्क नहीं"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"वाई-फ़ाई बंद"</string> @@ -458,11 +457,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"प्रोफ़ाइल दिखाएं"</string> <string name="user_add_user" msgid="4336657383006913022">"उपयोगकर्ता जोड़ें"</string> <string name="user_new_user_name" msgid="2019166282704195789">"नया उपयोगकर्ता"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"मेहमान के तौर पर ब्राउज़ करने का सेशन खत्म करना चाहते हैं?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"इस सत्र के सभी ऐप्स और डेटा को हटा दिया जाएगा."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"सेशन खत्म करें"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"अतिथि, आपका फिर से स्वागत है!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"क्या आप अपना सत्र जारी रखना चाहते हैं?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"फिर से शुरू करें"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 7d60619e2f02..4ffa5b2e04ff 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -354,8 +354,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Korisnik"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novi korisnik"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nije povezano"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nema mreže"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi isključen"</string> @@ -458,11 +457,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Prikaz profila"</string> <string name="user_add_user" msgid="4336657383006913022">"Dodavanje korisnika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Završiti gostujuću sesiju?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji bit će izbrisani."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Završi sesiju"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Dobro došli natrag, gostu!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 2ee8913f00d1..cff5b0dc65b3 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Felhasználó"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Új felhasználó"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nincs kapcsolat"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nincs hálózat"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi kikapcsolva"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profil megjelenítése"</string> <string name="user_add_user" msgid="4336657383006913022">"Felhasználó hozzáadása"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Új felhasználó"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Befejezi a vendég munkamenetet?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"A munkamenetben található összes alkalmazás és adat törlődni fog."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Munkamenet befejezése"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Örülünk, hogy visszatért, vendég!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Folytatja a munkamenetet?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Újrakezdés"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 18af1eaf17e3..91ffe7308a91 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Օգտատեր"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Նոր օգտատեր"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Ինտերնետ"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Միացված չէ"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ցանց չկա"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi-ը անջատված է"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 4a048bb638ef..f9e7397993dc 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Pengguna"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Pengguna baru"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Tidak Terhubung"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tidak Ada Jaringan"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Mati"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Tampilkan profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Tambahkan pengguna"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Pengguna baru"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Akhiri sesi tamu?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua aplikasi dan data di sesi ini akan dihapus."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Akhiri sesi"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Selamat datang kembali, tamu!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Lanjutkan sesi Anda?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Mulai ulang"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 10e11fc64d7b..db5dac43a42a 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Notandi"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nýr notandi"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Engin tenging"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ekkert net"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Slökkt á Wi-Fi"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Sýna snið"</string> <string name="user_add_user" msgid="4336657383006913022">"Bæta notanda við"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nýr notandi"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Ljúka gestalotu?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Öllum forritum og gögnum í þessari lotu verður eytt."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Ljúka lotu"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Velkominn aftur, gestur!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Viltu halda áfram með lotuna?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Byrja upp á nýtt"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 24e3f474f1fe..86e65bbc3629 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -63,7 +63,7 @@ <string name="usb_debugging_allow" msgid="1722643858015321328">"Consenti"</string> <string name="usb_debugging_secondary_user_title" msgid="7843050591380107998">"Debug USB non consentito"</string> <string name="usb_debugging_secondary_user_message" msgid="3740347841470403244">"L\'utente che ha eseguito l\'accesso a questo dispositivo non può attivare il debug USB. Per utilizzare questa funzione, passa all\'utente principale."</string> - <string name="wifi_debugging_title" msgid="7300007687492186076">"Consentire debug wireless su questa rete?"</string> + <string name="wifi_debugging_title" msgid="7300007687492186076">"Consentire il debug wireless su questa rete?"</string> <string name="wifi_debugging_message" msgid="5461204211731802995">"Nome della rete (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nIndirizzo Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string> <string name="wifi_debugging_always" msgid="2968383799517975155">"Consenti sempre su questa rete"</string> <string name="wifi_debugging_allow" msgid="4573224609684957886">"Consenti"</string> @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Utente"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nuovo utente"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Non connessa"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nessuna rete"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi disattivato"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostra profilo"</string> <string name="user_add_user" msgid="4336657383006913022">"Aggiungi utente"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nuovo utente"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Vuoi terminare la sessione Ospite?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tutte le app e i dati di questa sessione verranno eliminati."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Termina sessione"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bentornato, ospite."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vuoi continuare la sessione?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Ricomincia"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 76dbdf99ca50..8548dae09ee2 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -355,8 +355,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"משתמש"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"משתמש חדש"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"אינטרנט"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"אין חיבור"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"אין רשת"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi כבוי"</string> @@ -460,11 +459,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"הצג פרופיל"</string> <string name="user_add_user" msgid="4336657383006913022">"הוספת משתמש"</string> <string name="user_new_user_name" msgid="2019166282704195789">"משתמש חדש"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"להפסיק את הגלישה כאורח?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"כל האפליקציות והנתונים בפעילות זו באתר יימחקו."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"הפסקת הגלישה"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"שמחים לראותך שוב!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"האם ברצונך להמשיך בפעילות באתר?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ברצוני להתחיל מחדש"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index b4edfe5c38dc..6cd560854960 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"ユーザー"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"新しいユーザー"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"インターネット"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"接続されていません"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ネットワークなし"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi OFF"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"プロファイルを表示"</string> <string name="user_add_user" msgid="4336657383006913022">"ユーザーを追加"</string> <string name="user_new_user_name" msgid="2019166282704195789">"新しいユーザー"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ゲスト セッションを終了しますか?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"このセッションでのアプリとデータはすべて削除されます。"</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"セッションを終了"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"おかえりなさい、ゲストさん"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"セッションを続行しますか?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"最初から開始"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 26bde65e0ba3..52d9f0e77451 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"მომხმარებელი"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ახალი მომხმარებელი"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"ინტერნეტი"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"არ არის დაკავშირებული."</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ქსელი არ არის"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi გამორთულია"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"პროფილის ჩვენება"</string> <string name="user_add_user" msgid="4336657383006913022">"მომხმარებლის დამატება"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ახალი მომხმარებელი"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"დასრულდეს სტუმრის სესია?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ამ სესიის ყველა აპი და მონაცემი წაიშლება."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"სესიის დასრულება"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"სტუმარო, გვიხარია, რომ დაბრუნდით!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"გსურთ, თქვენი სესიის გაგრძელება?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ხელახლა დაწყება"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 9ce0a29649dc..fcf87434ad3b 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Пайдаланушы"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Жаңа пайдаланушы"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Жалғанбаған"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Желі жоқ"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi өшірулі"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Профильді көрсету"</string> <string name="user_add_user" msgid="4336657383006913022">"Пайдаланушы қосу"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Жаңа пайдаланушы"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Қонақ сеансы аяқталсын ба?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Осы сеанстағы барлық қолданбалар мен деректер жойылады."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Сеансты аяқтау"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Қош келдіңіз, қонақ"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансты жалғастыру керек пе?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Қайта бастау"</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 1e6c7fa6ebea..e92bf9bda905 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"អ្នកប្រើ"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"អ្នកប្រើថ្មី"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"អ៊ីនធឺណិត"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"មិនបានតភ្ជាប់"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"គ្មានបណ្ដាញ"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"វ៉ាយហ្វាយបានបិទ"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"បង្ហាញប្រវត្តិរូប"</string> <string name="user_add_user" msgid="4336657383006913022">"បន្ថែមអ្នកប្រើ"</string> <string name="user_new_user_name" msgid="2019166282704195789">"អ្នកប្រើថ្មី"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"បញ្ចប់វគ្គភ្ញៀវឬ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ទិន្នន័យ និងកម្មវិធីទាំងអស់ក្នុងសម័យនេះនឹងត្រូវបានលុប។"</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"បញ្ចប់វគ្គ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"សូមស្វាគមន៍ការត្រឡប់មកវិញ, ភ្ញៀវ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"តើអ្នកចង់បន្តសម័យរបស់អ្នក?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ចាប់ផ្ដើម"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 2958b2947de0..d7a0adc85935 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"ಬಳಕೆದಾರ"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ಹೊಸ ಬಳಕೆದಾರರು"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ವೈ-ಫೈ"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"ಇಂಟರ್ನೆಟ್"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ಸಂಪರ್ಕಗೊಂಡಿಲ್ಲ"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ನೆಟ್ವರ್ಕ್ ಇಲ್ಲ"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ವೈ-ಫೈ ಆಫ್"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ಪ್ರೊಫೈಲ್ ತೋರಿಸು"</string> <string name="user_add_user" msgid="4336657383006913022">"ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸಿ"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ಹೊಸ ಬಳಕೆದಾರರು"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ಅತಿಥಿ ಸೆಷನ್ ಅಂತ್ಯಗೊಳಿಸುವುದೇ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ಈ ಸೆಷನ್ನಲ್ಲಿನ ಎಲ್ಲ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"ಸೆಷನ್ ಅಂತ್ಯಗೊಳಿಸಿ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ಮತ್ತೆ ಸುಸ್ವಾಗತ, ಅತಿಥಿ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ನಿಮ್ಮ ಸೆಷನ್ ಮುಂದುವರಿಸಲು ಇಚ್ಚಿಸುವಿರಾ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ಪ್ರಾರಂಭಿಸಿ"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index cd613b647e38..54bcfb978ac9 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"사용자"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"신규 사용자"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"인터넷"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"연결되어 있지 않음"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"네트워크가 연결되지 않음"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi 꺼짐"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"프로필 표시"</string> <string name="user_add_user" msgid="4336657383006913022">"사용자 추가"</string> <string name="user_new_user_name" msgid="2019166282704195789">"신규 사용자"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"게스트 세션을 종료하시겠습니까?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"세션 종료"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"게스트 세션 다시 시작"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"세션을 계속 진행하시겠습니까?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"다시 시작"</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index e6053411b74a..d513380f9355 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -355,8 +355,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Колдонуучу"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Жаңы колдонуучу"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Байланышкан жок"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Желе жок"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi өчүк"</string> @@ -458,11 +457,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Профилди көрсөтүү"</string> <string name="user_add_user" msgid="4336657383006913022">"Колдонуучу кошуу"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Жаңы колдонуучу"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Конок сеансы бүтүрүлсүнбү?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Бул сеанстагы бардык колдонмолор жана дайындар өчүрүлөт."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Сеансты бүтүрүү"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Кайтып келишиңиз менен, конок!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансыңызды улантасызбы?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Кайра баштоо"</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index b8cc3ded31e8..7e595cd83176 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"ຜູ້ໃຊ້"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ຜູ່ໃຊ້ໃໝ່"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"ອິນເຕີເນັດ"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ບໍ່ໄດ້ເຊື່ອມຕໍ່"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ບໍ່ມີເຄືອຂ່າຍ"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ປິດ"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ສະແດງໂປຣໄຟລ໌"</string> <string name="user_add_user" msgid="4336657383006913022">"ເພີ່ມຜູ້ໃຊ້"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ຜູ່ໃຊ້ໃໝ່"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ສິ້ນສຸດເຊດຊັນແຂກບໍ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ແອັບຯແລະຂໍ້ມູນທັງໝົດໃນເຊດຊັນນີ້ຈະຖືກລຶບອອກ."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"ສິ້ນສຸດເຊດຊັນ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ຍິນດີຕ້ອນຮັບກັບມາ, ຜູ່ຢ້ຽມຢາມ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ທ່ານຕ້ອງການສືບຕໍ່ເຊດຊັນຂອງທ່ານບໍ່?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ເລີ່ມຕົ້ນໃຫມ່"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 8a32436f2c0c..e0c27a96de44 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -355,8 +355,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Naudotojas"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Naujas naudotojas"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internetas"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Neprisijungta"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tinklo nėra"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"„Wi-Fi“ išjungta"</string> @@ -460,11 +459,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Rodyti profilį"</string> <string name="user_add_user" msgid="4336657383006913022">"Pridėti naudotoją"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Naujas naudotojas"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Baigti svečio sesiją?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bus ištrintos visos šios sesijos programos ir duomenys."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Baigti sesiją"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Sveiki sugrįžę, svety!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Ar norite tęsti sesiją?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Pradėti iš naujo"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 56ce376dd147..ff36f51b5603 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -354,8 +354,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Lietotājs"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Jauns lietotājs"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internets"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nav izveidots savienojums"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nav tīkla"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ir izslēgts"</string> @@ -458,11 +457,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Parādīt profilu"</string> <string name="user_add_user" msgid="4336657383006913022">"Lietotāja pievienošana"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Jauns lietotājs"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Vai beigt viesa sesiju?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tiks dzēstas visas šīs sesijas lietotnes un dati."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Beigt sesiju"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Laipni lūdzam atpakaļ, viesi!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vai vēlaties turpināt savu sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Sākt no sākuma"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index fc83bb7ee46d..203d8b90ae52 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Корисник"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Нов корисник"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Не е поврзано"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Нема мрежа"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi е исклучено"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Прикажи го профилот"</string> <string name="user_add_user" msgid="4336657383006913022">"Додај корисник"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Нов корисник"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Да се заврши гостинската сесија?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Сите апликации и податоци во сесијата ќе се избришат."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Заврши ја сесијата"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Добре дојде пак, гостине!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Дали сакате да продолжите со сесијата?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни одново"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 9cd041236c36..77925ae0c268 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"ഉപയോക്താവ്"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"പുതിയ ഉപയോക്താവ്"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"വൈഫൈ"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"ഇന്റർനെറ്റ്"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"കണക്റ്റ് ചെയ്തിട്ടില്ല"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"നെറ്റ്വർക്ക് ഒന്നുമില്ല"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"വൈഫൈ ഓഫുചെയ്യുക"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"പ്രൊഫൈൽ കാണിക്കുക"</string> <string name="user_add_user" msgid="4336657383006913022">"ഉപയോക്താവിനെ ചേര്ക്കുക"</string> <string name="user_new_user_name" msgid="2019166282704195789">"പുതിയ ഉപയോക്താവ്"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"അതിഥി സെഷൻ അവസാനിപ്പിക്കണോ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ഈ സെഷനിലെ എല്ലാ അപ്ലിക്കേഷനുകളും ഡാറ്റയും ഇല്ലാതാക്കും."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"സെഷൻ അവസാനിപ്പിക്കുക"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"അതിഥിയ്ക്ക് വീണ്ടും സ്വാഗതം!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"നിങ്ങളുടെ സെഷൻ തുടരണോ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"പുനരാംരംഭിക്കുക"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index b0159b027611..c772294a6bdf 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Хэрэглэгч"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Шинэ хэрэглэгч"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернэт"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Холбогдоогүй"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Сүлжээгүй"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi унтарсан"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Профайлыг харуулах"</string> <string name="user_add_user" msgid="4336657383006913022">"Хэрэглэгч нэмэх"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Шинэ хэрэглэгч"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Зочны сургалтыг дуусгах уу?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Энэ сешний бүх апп болон дата устах болно."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Сургалтыг дуусгах"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Тавтай морилно уу!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Та үргэлжлүүлэхийг хүсэж байна уу?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Дахин эхлүүлэх"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index b9b525ead63c..d3e360147d0f 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Pengguna"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Pengguna baharu"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Tidak Disambungkan"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tiada Rangkaian"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Dimatikan"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Tunjuk profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Tambah pengguna"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Pengguna baharu"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Tamatkan sesi tetamu?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua apl dan data dalam sesi ini akan dipadam."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Tamatkan sesi"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Selamat kembali, tetamu!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Adakah anda ingin meneruskan sesi anda?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Mulakan semula"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 9801ec60c89e..0c1f9cbbc060 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"အသုံးပြုသူ"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"အသုံးပြုသူ အသစ်"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"အင်တာနက်"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ချိတ်ဆက်မထားပါ"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ကွန်ရက်မရှိပါ"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ဝိုင်ဖိုင်ပိတ်ရန်"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ပရိုဖိုင်ကို ပြရန်"</string> <string name="user_add_user" msgid="4336657383006913022">"အသုံးပြုသူ ထည့်ရန်"</string> <string name="user_new_user_name" msgid="2019166282704195789">"အသုံးပြုသူ အသစ်"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ဧည့်သည်စက်ရှင်ကို အဆုံးသတ်မလား။"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ဒီချိတ်ဆက်မှု ထဲက အက်ပ်များ အားလုံး နှင့် ဒေတာကို ဖျက်ပစ်မည်။"</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"သတ်မှတ်ပေးထားသည့်အချိန် ပြီးဆုံးပြီ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ပြန်လာတာ ကြိုဆိုပါသည်၊ ဧည့်သည်!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"သင်သည် သင်၏ ချိတ်ဆက်မှုကို ဆက်ပြုလုပ် လိုပါသလား?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"အစမှ ပြန်စပါ"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 67242865b649..565e7fea215d 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Bruker"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Ny bruker"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internett"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ikke tilkoblet"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ingen nettverk"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi er av"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Vis profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Legg til brukere"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Ny bruker"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Vil du avslutte gjesteøkten?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle appene og all informasjon i denne økten slettes."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Avslutt økten"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Velkommen tilbake, gjest!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vil du fortsette økten?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start på nytt"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index c104ffdf0dcd..9d95cf042dad 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"प्रयोगकर्ता"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"नयाँ प्रयोगकर्ता"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"इन्टरनेट"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"जोडिएको छैन"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"नेटवर्क छैन"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi बन्द"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"प्रोफाइल देखाउनुहोस्"</string> <string name="user_add_user" msgid="4336657383006913022">"प्रयोगकर्ता थप्नुहोस्"</string> <string name="user_new_user_name" msgid="2019166282704195789">"नयाँ प्रयोगकर्ता"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"अतिथिको सत्र अन्त्य गर्ने हो?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"यस सत्रमा सबै एपहरू र डेटा मेटाइनेछ।"</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"सत्र अन्त्य गर्नुहोस्"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"पुनः स्वागत, अतिथि!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"तपाईं आफ्नो सत्र जारी गर्न चाहनुहुन्छ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"सुरु गर्नुहोस्"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 7d878170283f..f598fa3fae09 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Gebruiker"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nieuwe gebruiker"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wifi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Niet verbonden"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Geen netwerk"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wifi uit"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profiel weergeven"</string> <string name="user_add_user" msgid="4336657383006913022">"Gebruiker toevoegen"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nieuwe gebruiker"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Gastsessie beëindigen?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps en gegevens in deze sessie worden verwijderd."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Sessie beëindigen"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welkom terug, gast!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Wil je doorgaan met je sessie?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Opnieuw starten"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 7d7b84f785f1..1e07dd4b785e 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -456,11 +456,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ପ୍ରୋଫାଇଲ୍ ଦେଖାନ୍ତୁ"</string> <string name="user_add_user" msgid="4336657383006913022">"ଉପଯୋଗକର୍ତ୍ତାଙ୍କୁ ଯୋଗ କରନ୍ତୁ"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ଅତିଥି ସେସନ୍ ଶେଷ କରିବେ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ଏହି ଅବଧିର ସମସ୍ତ ଆପ୍ ଓ ଡାଟା ଡିଲିଟ୍ ହୋଇଯିବ।"</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"ସେସନ୍ ଶେଷ କରନ୍ତୁ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ପୁଣି ସ୍ୱାଗତ, ଅତିଥି!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ଆପଣ ନିଜର ଅବଧି ଜାରି ରଖିବାକୁ ଚାହାନ୍ତି କି?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ଆରମ୍ଭ କରନ୍ତୁ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 8a19e285463e..561e70dd14cb 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -355,8 +355,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Użytkownik"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nowy użytkownik"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Brak połączenia"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Brak sieci"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi wyłączone"</string> @@ -460,11 +459,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Pokaż profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Dodaj użytkownika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nowy użytkownik"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Zakończyć sesję gościa?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wszystkie aplikacje i dane w tej sesji zostaną usunięte."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Zakończ sesję"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Witaj ponownie, gościu!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcesz kontynuować sesję?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Rozpocznij nową"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 57b924d4e6f8..7fe53d348e15 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Usuário"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo usuário"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Não conectado"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sem rede"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desligado"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string> <string name="user_add_user" msgid="4336657383006913022">"Adicionar usuário"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novo usuário"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Encerrar sessão de visitante?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Encerrar sessão"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bem-vindo, convidado."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index e39f757ce416..dd154e8ad684 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Utilizador"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo utilizador"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Não Ligado"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sem Rede"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Desligado"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string> <string name="user_add_user" msgid="4336657383006913022">"Adicionar utilizador"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novo utilizador"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Pretende terminar a sessão de convidado?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todas as aplicações e dados desta sessão serão eliminados."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Terminar sessão"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bem-vindo de volta, caro(a) convidado(a)!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Pretende continuar a sessão?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 57b924d4e6f8..7fe53d348e15 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Usuário"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo usuário"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Não conectado"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sem rede"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desligado"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string> <string name="user_add_user" msgid="4336657383006913022">"Adicionar usuário"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novo usuário"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Encerrar sessão de visitante?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Encerrar sessão"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bem-vindo, convidado."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index acbe80140c2f..317fc098d522 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -354,8 +354,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Utilizator"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Utilizator nou"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Neconectată"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nicio rețea"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi deconectat"</string> @@ -458,11 +457,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Afișați profilul"</string> <string name="user_add_user" msgid="4336657383006913022">"Adăugați un utilizator"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Utilizator nou"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Încheiați sesiunea pentru invitați?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toate aplicațiile și datele din această sesiune vor fi șterse."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Încheiați sesiunea"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bine ați revenit în sesiunea pentru invitați!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vreți să continuați sesiunea?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Începeți din nou"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index c59bd2ae727d..bbb014e82b07 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -355,8 +355,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Пользователь"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Новый пользователь"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Нет соединения"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Нет сети"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi выкл."</string> @@ -460,11 +459,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Показать профиль."</string> <string name="user_add_user" msgid="4336657383006913022">"Добавить пользователя"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Новый пользователь"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Завершить гостевой сеанс?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Все приложения и данные этого профиля будут удалены."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Завершить сеанс"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Рады видеть вас снова!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Продолжить сеанс?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Начать заново"</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index a49ca863b3fa..a7a2bb7dbbff 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"පරිශීලක"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"නව පරිශීලකයා"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"අන්තර්ජාලය"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"සම්බන්ධ වී නොමැත"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ජාලයක් නැත"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi අක්රියයි"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"පැතිකඩ පෙන්වන්න"</string> <string name="user_add_user" msgid="4336657383006913022">"පරිශීලකයෙක් එක් කරන්න"</string> <string name="user_new_user_name" msgid="2019166282704195789">"නව පරිශීලකයා"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ආරාධිත සැසිය අවසන් කරන්නද?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"මෙම සැසියේ සියළුම යෙදුම් සහ දත්ත මකාවී."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"සැසිය අවසන් කරන්න"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"නැවත සාදරයෙන් පිළිගනිමු, අමුත්තා!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ඔබගේ සැසිය දිගටම කරගෙන යෑමට ඔබට අවශ්යද?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"යළි මුල සිට අරඹන්න"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index fe63b70f1c46..7eb5297a43f1 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -355,8 +355,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Používateľ"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nový používateľ"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi‑Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nepripojené"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Žiadna sieť"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Sieť Wi‑Fi je vypnutá"</string> @@ -460,11 +459,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Zobraziť profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Pridať používateľa"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nový používateľ"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Chcete ukončiť reláciu hosťa?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Všetky aplikácie a údaje v tejto relácii budú odstránené."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Ukončiť reláciu"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Hosť, vitajte späť!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcete v relácii pokračovať?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začať odznova"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 85956d6d0c72..6d23f26a6c8d 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -355,8 +355,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Uporabnik"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nov uporabnik"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Povezava ni vzpostavljena"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ni omrežja"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi izklopljen"</string> @@ -460,11 +459,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Prikaz profila"</string> <string name="user_add_user" msgid="4336657383006913022">"Dodajanje uporabnika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nov uporabnik"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Želite končati sejo gosta?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Vse aplikacije in podatki v tej seji bodo izbrisani."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Končaj sejo"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Znova pozdravljeni, gost!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite nadaljevati sejo?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začni znova"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 733db0e018a8..7e89b93cec82 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Përdoruesi"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Përdorues i ri"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nuk është i lidhur"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nuk ka rrjet"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi është i çaktivizuar"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index c5414a4e3f80..af5175c2bbf6 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -354,8 +354,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Корисник"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Нови корисник"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Веза није успостављена"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Нема мреже"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi је искључен"</string> @@ -458,11 +457,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Прикажи профил"</string> <string name="user_add_user" msgid="4336657383006913022">"Додај корисника"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Нови корисник"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Желите да завршите сесију госта?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Све апликације и подаци у овој сесији ће бити избрисани."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Заврши сесију"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Добро дошли назад, госте!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Желите ли да наставите сесију?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни из почетка"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index d2c993a0985a..41bca7c90b32 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Användare"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Ny användare"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ej ansluten"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Inget nätverk"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi av"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Visa profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Lägg till användare"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Ny användare"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Vill du avsluta gästsessionen?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alla appar och data i denna session kommer att raderas."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Avsluta session"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Välkommen tillbaka gäst!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vill du fortsätta sessionen?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Börja om"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 29045f3d69ae..f122013cc150 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Mtumiaji"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Mtumiaji mpya"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Intaneti"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Haijaunganishwa"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Hakuna Mtandao"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Imezimwa"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Onyesha wasifu"</string> <string name="user_add_user" msgid="4336657383006913022">"Ongeza mtumiaji"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Mtumiaji mpya"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Ungependa kumaliza kipindi cha mgeni?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Data na programu zote katika kipindi hiki zitafutwa."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Maliza kipindi"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Karibu tena, mwalikwa!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Je, unataka kuendelea na kipindi chako?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Anza tena"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 2ca67201d387..c076a031b93f 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"பயனர்"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"புதியவர்"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"வைஃபை"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"இணையம்"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"இணைக்கப்படவில்லை"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"நெட்வொர்க் இல்லை"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"வைஃபையை முடக்கு"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"சுயவிவரத்தைக் காட்டு"</string> <string name="user_add_user" msgid="4336657383006913022">"பயனரைச் சேர்"</string> <string name="user_new_user_name" msgid="2019166282704195789">"புதியவர்"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"விருந்தினர் அமர்வை நிறைவுசெய்யவா?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"இந்த அமர்வின் எல்லா பயன்பாடுகளும், தரவும் நீக்கப்படும்."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"அமர்வை நிறைவுசெய்"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"நல்வரவு!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"உங்கள் அமர்வைத் தொடர விருப்பமா?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"மீண்டும் தொடங்கு"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 9ce5fc745580..fca51077d06f 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"వినియోగదారు"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"కొత్త వినియోగదారు"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"ఇంటర్నెట్"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"కనెక్ట్ చేయబడలేదు"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"నెట్వర్క్ లేదు"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ఆఫ్లో ఉంది"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ప్రొఫైల్ని చూపు"</string> <string name="user_add_user" msgid="4336657383006913022">"వినియోగదారుని జోడించండి"</string> <string name="user_new_user_name" msgid="2019166282704195789">"కొత్త వినియోగదారు"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"గెస్ట్ సెషన్ను ముగించాలా?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ఈ సెషన్లోని అన్ని అనువర్తనాలు మరియు డేటా తొలగించబడతాయి."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"సెషన్ను ముగించు"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"పునఃస్వాగతం, అతిథి!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"మీరు మీ సెషన్ని కొనసాగించాలనుకుంటున్నారా?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"మొదటి నుండి ప్రారంభించు"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 900012eb267d..c909d379dedc 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"ผู้ใช้"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ผู้ใช้ใหม่"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"อินเทอร์เน็ต"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ไม่ได้เชื่อมต่อ"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ไม่มีเครือข่าย"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ปิด WiFi"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"แสดงโปรไฟล์"</string> <string name="user_add_user" msgid="4336657383006913022">"เพิ่มผู้ใช้"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ผู้ใช้ใหม่"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"จบเซสชันผู้เยี่ยมชมใช่ไหม"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ระบบจะลบแอปและข้อมูลทั้งหมดในเซสชันนี้"</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"จบเซสชัน"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ยินดีต้อนรับท่านผู้เยี่ยมชมกลับมาอีกครั้ง!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"คุณต้องการอยู่ในเซสชันต่อไปไหม"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"เริ่มต้นใหม่"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index f531de5a89de..2e75fa3de905 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"User"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Bagong user"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Hindi Nakakonekta"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Walang Network"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Naka-off ang Wi-Fi"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Ipakita ang profile"</string> <string name="user_add_user" msgid="4336657383006913022">"Magdagdag ng user"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Bagong user"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Tapusin ang session ng bisita?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ide-delete ang lahat ng app at data sa session na ito."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Tapusin ang session"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Maligayang pagbabalik, bisita!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Gusto mo bang ipagpatuloy ang iyong session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Magsimulang muli"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index ba70531a89f3..98552b1b135d 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Kullanıcı"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Yeni kullanıcı"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Kablosuz"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"İnternet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Bağlı Değil"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ağ yok"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Kablosuz Kapalı"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profili göster"</string> <string name="user_add_user" msgid="4336657383006913022">"Kullanıcı ekle"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Yeni kullanıcı"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Misafir oturumu sonlandırılsın mı?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu oturumdaki tüm uygulamalar ve veriler silinecek."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Oturumu sonlandır"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Tekrar hoş geldiniz sayın misafir!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Oturumunuza devam etmek istiyor musunuz?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Baştan başla"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index b2ae569cf26c..b70d36af64c2 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -355,8 +355,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Користувач"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Новий користувач"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Інтернет"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Не під’єднано."</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Немає мережі"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi вимкнено"</string> @@ -460,11 +459,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Показати профіль"</string> <string name="user_add_user" msgid="4336657383006913022">"Додати користувача"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Новий користувач"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Завершити сеанс у режимі \"Гість\"?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усі додатки й дані з цього сеансу буде видалено."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Завершити сеанс"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"З поверненням!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Продовжити сеанс?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почати знову"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 06a448b25579..bbadf6a7d5f1 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"صارف"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"نیا صارف"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"انٹرنیٹ"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"مربوط نہیں ہے"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"کوئی نیٹ ورک نہیں ہے"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi آف ہے"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"پروفائل دکھائیں"</string> <string name="user_add_user" msgid="4336657383006913022">"صارف کو شامل کریں"</string> <string name="user_new_user_name" msgid="2019166282704195789">"نیا صارف"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"مہمان سیشن ختم کریں؟"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"اس سیشن میں موجود سبھی ایپس اور ڈیٹا کو حذف کر دیا جائے گا۔"</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"سیشن ختم کریں"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"مہمان، پھر سے خوش آمدید!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"کیا آپ اپنا سیشن جاری رکھنا چاہتے ہیں؟"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"دوبارہ شروع کریں"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index e1add6503412..a20676dca577 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Foydalanuvchi"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Yangi foydalanuvchi"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ulanmagan"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tarmoq mavjud emas"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi o‘chiq"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 021044d29b34..5de7df6a5d69 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Người dùng"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Người dùng mới"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"Internet"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Chưa được kết nối"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Không có mạng nào"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Tắt Wi-Fi"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Hiển thị hồ sơ"</string> <string name="user_add_user" msgid="4336657383006913022">"Thêm người dùng"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Người dùng mới"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Kết thúc phiên khách?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tất cả ứng dụng và dữ liệu trong phiên này sẽ bị xóa."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Kết thúc phiên"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Chào mừng bạn trở lại!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Bạn có muốn tiếp tục phiên của mình không?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Bắt đầu lại"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 60acb839b33a..7f3f3943e3c5 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"用户"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"新用户"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WLAN"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"互联网"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"未连接"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"无网络"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WLAN:关闭"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"显示个人资料"</string> <string name="user_add_user" msgid="4336657383006913022">"添加用户"</string> <string name="user_new_user_name" msgid="2019166282704195789">"新用户"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"要结束访客会话吗?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"此会话中的所有应用和数据都将被删除。"</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"结束会话"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"访客,欢迎回来!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"要继续您的会话吗?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新开始"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index db55580872f4..485f1cf786be 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"使用者"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"新使用者"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"互聯網"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"未連線"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"沒有網絡"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi 關閉"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"顯示個人檔案"</string> <string name="user_add_user" msgid="4336657383006913022">"加入使用者"</string> <string name="user_new_user_name" msgid="2019166282704195789">"新使用者"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"要結束訪客工作階段嗎?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會被刪除。"</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"結束工作階段"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"訪客您好,歡迎回來!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"您要繼續您的工作階段嗎?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新開始"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 1487ad33d12f..1edeacd1a843 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"使用者"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"新使用者"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"網際網路"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"未連線"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"沒有網路"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi 已關閉"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"顯示設定檔"</string> <string name="user_add_user" msgid="4336657383006913022">"新增使用者"</string> <string name="user_new_user_name" msgid="2019166282704195789">"新使用者"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"要結束訪客工作階段嗎?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會遭到刪除。"</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"結束工作階段"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"訪客你好,歡迎回來!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"你要繼續這個工作階段嗎?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新開始"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index b0f084b7f83d..58baebc27cc6 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -353,8 +353,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Umsebenzisi"</string> <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Umsebenzisi omusha"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"I-Wi-Fi"</string> - <!-- no translation found for quick_settings_internet_label (6603068555872455463) --> - <skip /> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"I-inthanethi"</string> <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Akuxhunyiwe"</string> <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ayikho inethiwekhi"</string> <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"I-Wi-Fi icimile"</string> @@ -456,11 +455,9 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Bonisa iphrofayela"</string> <string name="user_add_user" msgid="4336657383006913022">"Engeza umsebenzisi"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Umsebenzisi omusha"</string> - <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) --> - <skip /> + <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Misa isikhathi sesihambeli?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Zonke izinhlelo zokusebenza nedatha kulesi sikhathi zizosuswa."</string> - <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) --> - <skip /> + <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Phothula iseshini"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Siyakwamukela futhi, sivakashi!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Ingabe ufuna ukuqhubeka ngesikhathi sakho?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Qala phansi"</string> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 6f69483106e5..8888ad6b0411 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1041,6 +1041,13 @@ burn-in on AOD. --> <dimen name="burn_in_prevention_offset_y">50dp</dimen> + <!-- The maximum offset in either direction that elements are moved vertically to prevent + burn-in on AOD. --> + <dimen name="burn_in_prevention_offset_y_large_clock">42dp</dimen> + + <!-- Large clock maximum font size (dp is intentional, to prevent any further scaling) --> + <dimen name="large_clock_text_size">150dp</dimen> + <!-- The maximum offset in either direction that icons move to prevent burn-in on AOD. --> <dimen name="default_burn_in_prevention_offset">15dp</dimen> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 6cc863a448c7..2d972e04bd1d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -24,6 +24,7 @@ import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.RelativeLayout; import android.widget.TextClock; +import android.widget.TextView; import com.android.internal.colorextraction.ColorExtractor; import com.android.keyguard.dagger.KeyguardStatusViewScope; @@ -147,6 +148,10 @@ public class KeyguardClockSwitch extends RelativeLayout { setTextSize(TypedValue.COMPLEX_UNIT_PX, mContext.getResources() .getDimensionPixelSize(R.dimen.widget_big_font_size)); + ((TextView) mNewLockscreenLargeClockFrame.getChildAt(0)) + .setTextSize(TypedValue.COMPLEX_UNIT_PX, mContext.getResources() + .getDimensionPixelSize(R.dimen.large_clock_text_size)); + mClockSwitchYAmount = mContext.getResources().getDimensionPixelSize( R.dimen.keyguard_clock_switch_y_shift); } @@ -391,7 +396,6 @@ public class KeyguardClockSwitch extends RelativeLayout { if (hasVisibleNotifications == mHasVisibleNotifications) { return; } - animateClockChange(!hasVisibleNotifications); mHasVisibleNotifications = hasVisibleNotifications; diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 08262defbdee..eefae5b586ee 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -111,7 +111,8 @@ public class SystemUIFactory { .setBubbles(mWMComponent.getBubbles()) .setHideDisplayCutout(mWMComponent.getHideDisplayCutout()) .setShellCommandHandler(mWMComponent.getShellCommandHandler()) - .setAppPairs(mWMComponent.getAppPairs()); + .setAppPairs(mWMComponent.getAppPairs()) + .setTaskViewFactory(mWMComponent.getTaskViewFactory()); } else { // TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option // is separating this logic into newly creating SystemUITestsFactory. @@ -122,7 +123,8 @@ public class SystemUIFactory { .setBubbles(Optional.ofNullable(null)) .setHideDisplayCutout(Optional.ofNullable(null)) .setShellCommandHandler(Optional.ofNullable(null)) - .setAppPairs(Optional.ofNullable(null)); + .setAppPairs(Optional.ofNullable(null)) + .setTaskViewFactory(Optional.ofNullable(null)); } mSysUIComponent = builder.build(); if (initializeComponents) { diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index c289ca2173be..1036c9916c8b 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -18,6 +18,7 @@ package com.android.systemui.appops; import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED; +import android.Manifest; import android.app.AppOpsManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -30,6 +31,7 @@ import android.media.AudioRecordingConfiguration; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; +import android.provider.DeviceConfig; import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; @@ -75,6 +77,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon private final AppOpsManager mAppOps; private final AudioManager mAudioManager; private final LocationManager mLocationManager; + // TODO ntmyren: remove t + private final PackageManager mPackageManager; // mLocationProviderPackages are cached and updated only occasionally private static final long LOCATION_PROVIDER_UPDATE_FREQUENCY_MS = 30000; @@ -127,6 +131,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon mAudioManager = audioManager; mMicMuted = audioManager.isMicrophoneMute(); mLocationManager = context.getSystemService(LocationManager.class); + mPackageManager = context.getPackageManager(); dumpManager.registerDumpable(TAG, this); } @@ -334,6 +339,16 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon return mLocationProviderPackages.contains(packageName); } + // TODO ntmyren: remove after teamfood is finished + private boolean shouldShowAppPredictor(String pkgName) { + if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, "permissions_hub_2_enabled", + false)) { + return false; + } + return mPackageManager.checkPermission(Manifest.permission.MANAGE_APP_PREDICTIONS, pkgName) + == PackageManager.PERMISSION_GRANTED; + } + /** * Does the app-op, uid and package name, refer to an operation that should be shown to the * user. Only specficic ops (like {@link AppOpsManager.OP_SYSTEM_ALERT_WINDOW}) or @@ -353,8 +368,9 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon || appOpCode == AppOpsManager.OP_PHONE_CALL_MICROPHONE) { return true; } - - if (appOpCode == AppOpsManager.OP_CAMERA && isLocationProvider(packageName)) { + // TODO ntmyren: Replace this with more robust check if this moves beyond teamfood + if ((appOpCode == AppOpsManager.OP_CAMERA && isLocationProvider(packageName)) + || shouldShowAppPredictor(packageName)) { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt index 6c28d11df655..ff55b76d4db7 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt @@ -26,7 +26,9 @@ import android.service.controls.actions.FloatAction import android.service.controls.actions.ModeAction import android.text.InputType import android.util.Log +import android.view.LayoutInflater import android.view.WindowManager +import android.view.inputmethod.InputMethodManager import android.widget.CheckBox import android.widget.EditText @@ -71,11 +73,21 @@ object ChallengeDialogs { R.string.controls_pin_instructions ) } - val builder = AlertDialog.Builder(cvh.context, STYLE).apply { + return object : AlertDialog(cvh.context, STYLE) { + override fun dismiss() { + window?.decorView?.let { + // workaround for b/159309083 + it.context.getSystemService(InputMethodManager::class.java) + ?.hideSoftInputFromWindow(it.windowToken, 0) + } + super.dismiss() + } + }.apply { setTitle(title) - setView(R.layout.controls_dialog_pin) - setPositiveButton( - android.R.string.ok, + setView(LayoutInflater.from(context).inflate(R.layout.controls_dialog_pin, null)) + setButton( + DialogInterface.BUTTON_POSITIVE, + context.getText(android.R.string.ok), DialogInterface.OnClickListener { dialog, _ -> if (dialog is Dialog) { dialog.requireViewById<EditText>(R.id.controls_pin_input) @@ -85,15 +97,15 @@ object ChallengeDialogs { dialog.dismiss() } }) - setNegativeButton( - android.R.string.cancel, + setButton( + DialogInterface.BUTTON_NEGATIVE, + context.getText(android.R.string.cancel), DialogInterface.OnClickListener { dialog, _ -> onCancel.invoke() dialog.cancel() } ) - } - return builder.create().apply { + getWindow().apply { setType(WINDOW_TYPE) setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE) diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt index ab8222547597..7cd7e18965c2 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt @@ -36,6 +36,8 @@ import com.android.systemui.globalactions.GlobalActionsComponent import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.concurrency.DelayableExecutor +import com.android.wm.shell.TaskViewFactory +import java.util.Optional import javax.inject.Inject @SysUISingleton @@ -45,7 +47,8 @@ class ControlActionCoordinatorImpl @Inject constructor( @Main private val uiExecutor: DelayableExecutor, private val activityStarter: ActivityStarter, private val keyguardStateController: KeyguardStateController, - private val globalActionsComponent: GlobalActionsComponent + private val globalActionsComponent: GlobalActionsComponent, + private val taskViewFactory: Optional<TaskViewFactory> ) : ControlActionCoordinator { private var dialog: Dialog? = null private val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator @@ -163,11 +166,13 @@ class ControlActionCoordinatorImpl @Inject constructor( uiExecutor.execute { // make sure the intent is valid before attempting to open the dialog - if (activities.isNotEmpty()) { - dialog = DetailDialog(cvh, intent).also { - it.setOnDismissListener { _ -> dialog = null } - it.show() - } + if (activities.isNotEmpty() && taskViewFactory.isPresent) { + taskViewFactory.get().create(cvh.context, uiExecutor, { + dialog = DetailDialog(cvh, it, intent).also { + it.setOnDismissListener { _ -> dialog = null } + it.show() + } + }) } else { cvh.setErrorStatus() } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt index 5b11627bbc3b..e4f906486f8c 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt @@ -16,8 +16,12 @@ package com.android.systemui.controls.ui -import android.app.ActivityView +import android.app.ActivityOptions +import android.app.ActivityTaskManager +import android.app.ActivityTaskManager.INVALID_TASK_ID import android.app.Dialog +import android.app.PendingIntent +import android.content.ComponentName import android.content.Intent import android.provider.Settings import android.view.View @@ -26,9 +30,9 @@ import android.view.WindowInsets import android.view.WindowInsets.Type import android.view.WindowManager import android.widget.ImageView - import com.android.internal.policy.ScreenDecorationsUtils import com.android.systemui.R +import com.android.wm.shell.TaskView /** * A dialog that provides an {@link ActivityView}, allowing the application to provide @@ -37,6 +41,7 @@ import com.android.systemui.R */ class DetailDialog( val cvh: ControlViewHolder, + val activityView: TaskView, val intent: Intent ) : Dialog(cvh.context, R.style.Theme_SystemUI_Dialog_Control_DetailPanel) { @@ -49,10 +54,16 @@ class DetailDialog( private const val EXTRA_USE_PANEL = "controls.DISPLAY_IN_PANEL" } - var activityView = ActivityView(context) + var detailTaskId = INVALID_TASK_ID + + fun removeDetailTask() { + if (detailTaskId == INVALID_TASK_ID) return + ActivityTaskManager.getInstance().removeTask(detailTaskId) + detailTaskId = INVALID_TASK_ID + } - val stateCallback: ActivityView.StateCallback = object : ActivityView.StateCallback() { - override fun onActivityViewReady(view: ActivityView) { + val stateCallback = object : TaskView.Listener { + override fun onInitialized() { val launchIntent = Intent(intent) launchIntent.putExtra(EXTRA_USE_PANEL, true) @@ -60,18 +71,31 @@ class DetailDialog( launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT) launchIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) - view.startActivity(launchIntent) + activityView.startActivity( + PendingIntent.getActivity(context, 0, launchIntent, + PendingIntent.FLAG_UPDATE_CURRENT), null, ActivityOptions.makeBasic()) } - override fun onActivityViewDestroyed(view: ActivityView) {} - override fun onTaskRemovalStarted(taskId: Int) { + detailTaskId = INVALID_TASK_ID dismiss() } + + override fun onTaskCreated(taskId: Int, name: ComponentName?) { + detailTaskId = taskId + } + + override fun onReleased() { + removeDetailTask() + } } init { window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY) + // To pass touches to the task inside TaskView. + window.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL) + window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY) + setContentView(R.layout.controls_detail_dialog) requireViewById<ViewGroup>(R.id.controls_activity_view).apply { @@ -84,6 +108,9 @@ class DetailDialog( requireViewById<ImageView>(R.id.control_detail_open_in_app).apply { setOnClickListener { v: View -> + // Remove the task explicitly, since onRelease() callback will be executed after + // startActivity() below is called. + removeDetailTask() dismiss() context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) v.context.startActivity(intent) @@ -126,7 +153,7 @@ class DetailDialog( } override fun show() { - activityView.setCallback(stateCallback) + activityView.setListener(cvh.uiExecutor, stateCallback) super.show() } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index f9505dec78fd..612a55970f02 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -25,6 +25,7 @@ import com.android.systemui.keyguard.KeyguardSliceProvider; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.InjectionInflationController; import com.android.wm.shell.ShellCommandHandler; +import com.android.wm.shell.TaskViewFactory; import com.android.wm.shell.apppairs.AppPairs; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; @@ -71,6 +72,9 @@ public interface SysUIComponent { Builder setBubbles(Optional<Bubbles> b); @BindsInstance + Builder setTaskViewFactory(Optional<TaskViewFactory> t); + + @BindsInstance Builder setHideDisplayCutout(Optional<HideDisplayCutout> h); @BindsInstance diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index 943a54e61c03..c75dc84f8570 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -19,6 +19,7 @@ package com.android.systemui.dagger; import com.android.systemui.wmshell.WMShellModule; import com.android.wm.shell.ShellCommandHandler; import com.android.wm.shell.ShellInit; +import com.android.wm.shell.TaskViewFactory; import com.android.wm.shell.apppairs.AppPairs; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; @@ -79,4 +80,7 @@ public interface WMComponent { @WMSingleton Optional<HideDisplayCutout> getHideDisplayCutout(); + + @WMSingleton + Optional<TaskViewFactory> getTaskViewFactory(); } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java index 4efe4d85156b..f0e4cce299ee 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java @@ -461,14 +461,8 @@ public class KeyButtonView extends ImageView implements ButtonInterface { @Override public void draw(Canvas canvas) { if (mHasOvalBg) { - canvas.save(); - int cx = (getLeft() + getRight()) / 2; - int cy = (getTop() + getBottom()) / 2; - canvas.translate(cx, cy); int d = Math.min(getWidth(), getHeight()); - int r = d / 2; - canvas.drawOval(-r, -r, r, r, mOvalBgPaint); - canvas.restore(); + canvas.drawOval(0, 0, d, d, mOvalBgPaint); } super.draw(canvas); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java index c2ba3440f19f..aa8d710e7570 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java +++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java @@ -130,7 +130,7 @@ public class ScreenPinningRequest implements View.OnClickListener, } } - private WindowManager.LayoutParams getWindowLayoutParams() { + protected WindowManager.LayoutParams getWindowLayoutParams() { final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java index 3811ca929f6e..bbc4b780bc52 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java @@ -50,18 +50,26 @@ public class FeatureFlags { @Inject public FeatureFlags(@Background Executor executor) { DeviceConfig.addOnPropertiesChangedListener( - "systemui", + /* namespace= */ "systemui", executor, this::onPropertiesChanged); } public boolean isNewNotifPipelineEnabled() { - return getDeviceConfigFlag("notification.newpipeline.enabled", true); + return getDeviceConfigFlag("notification.newpipeline.enabled", /* defaultValue= */ true); } public boolean isNewNotifPipelineRenderingEnabled() { return isNewNotifPipelineEnabled() - && getDeviceConfigFlag("notification.newpipeline.rendering", false); + && getDeviceConfigFlag("notification.newpipeline.rendering", /* defaultValue= */ + false); + } + + /** + * Flag used for guarding development of b/171917882. + */ + public boolean isTwoColumnNotificationShadeEnabled() { + return getDeviceConfigFlag("notification.twocolumn", /* defaultValue= */ false); } private void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { @@ -76,7 +84,7 @@ public class FeatureFlags { synchronized (mCachedDeviceConfigFlags) { Boolean flag = mCachedDeviceConfigFlags.get(key); if (flag == null) { - flag = DeviceConfig.getBoolean("systemui", key, defaultValue); + flag = DeviceConfig.getBoolean(/* namespace= */ "systemui", key, defaultValue); mCachedDeviceConfigFlags.put(key, flag); } return flag; 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 28ee9358737e..97201f5c9a34 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 @@ -134,12 +134,14 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { /** Shows or hides feedback indicator */ @Override public void showFeedbackIcon(boolean show, Pair<Integer, Integer> resIds) { - mFeedbackIcon.setVisibility(show ? View.VISIBLE : View.GONE); - if (show) { - if (mFeedbackIcon instanceof ImageButton) { - ((ImageButton) mFeedbackIcon).setImageResource(resIds.first); + if (mFeedbackIcon != null) { + mFeedbackIcon.setVisibility(show ? View.VISIBLE : View.GONE); + if (show) { + if (mFeedbackIcon instanceof ImageButton) { + ((ImageButton) mFeedbackIcon).setImageResource(resIds.first); + } + mFeedbackIcon.setContentDescription(mView.getContext().getString(resIds.second)); } - mFeedbackIcon.setContentDescription(mView.getContext().getString(resIds.second)); } } @@ -263,7 +265,9 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_ICON, mIcon); mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_EXPANDER, mExpandButton); - mTransformationHelper.addViewTransformingToSimilar(mWorkProfileImage); + if (mWorkProfileImage != null) { + mTransformationHelper.addViewTransformingToSimilar(mWorkProfileImage); + } if (mIsLowPriority && mHeaderText != null) { mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE, mHeaderText); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index 6da5d1b90cd9..6cd7a74cdf02 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -112,6 +112,11 @@ public class KeyguardClockPositionAlgorithm { private int mBurnInPreventionOffsetY; /** + * Burn-in prevention y translation for large clock layouts. + */ + private int mBurnInPreventionOffsetYLargeClock; + + /** * Doze/AOD transition amount. */ private float mDarkAmount; @@ -156,6 +161,8 @@ public class KeyguardClockPositionAlgorithm { R.dimen.burn_in_prevention_offset_x); mBurnInPreventionOffsetY = res.getDimensionPixelSize( R.dimen.burn_in_prevention_offset_y); + mBurnInPreventionOffsetYLargeClock = res.getDimensionPixelSize( + R.dimen.burn_in_prevention_offset_y_large_clock); } /** @@ -287,8 +294,12 @@ public class KeyguardClockPositionAlgorithm { } private float burnInPreventionOffsetY() { - return getBurnInOffset(mBurnInPreventionOffsetY * 2, false /* xAxis */) - - mBurnInPreventionOffsetY; + int offset = mBurnInPreventionOffsetY; + if (mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) { + offset = mBurnInPreventionOffsetYLargeClock; + } + + return getBurnInOffset(offset * 2, false /* xAxis */) - offset; } private float burnInPreventionOffsetX() { diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java index 0d63324966fd..0ba072e9e72f 100644 --- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java +++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java @@ -25,7 +25,7 @@ import android.provider.Settings; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; import com.android.systemui.SystemUI; -import com.android.wm.shell.pip.tv.PipNotification; +import com.android.wm.shell.pip.tv.TvPipNotificationController; import java.util.Arrays; @@ -36,7 +36,7 @@ public class NotificationChannels extends SystemUI { public static String GENERAL = "GEN"; public static String STORAGE = "DSK"; public static String BATTERY = "BAT"; - public static String TVPIP = PipNotification.NOTIFICATION_CHANNEL_TVPIP; + public static String TVPIP = TvPipNotificationController.NOTIFICATION_CHANNEL; // "TVPIP" public static String HINTS = "HNT"; public NotificationChannels(Context context) { diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java index 4d3af9c01153..8a79acef756c 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java @@ -32,9 +32,9 @@ import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipTaskOrganizer; import com.android.wm.shell.pip.PipUiEventLogger; -import com.android.wm.shell.pip.tv.PipController; -import com.android.wm.shell.pip.tv.PipNotification; +import com.android.wm.shell.pip.tv.TvPipController; import com.android.wm.shell.pip.tv.TvPipMenuController; +import com.android.wm.shell.pip.tv.TvPipNotificationController; import java.util.Optional; @@ -55,31 +55,24 @@ public abstract class TvPipModule { PipTaskOrganizer pipTaskOrganizer, TvPipMenuController tvPipMenuController, PipMediaController pipMediaController, - PipNotification pipNotification, + TvPipNotificationController tvPipNotificationController, TaskStackListenerImpl taskStackListener, WindowManagerShellWrapper windowManagerShellWrapper) { return Optional.of( - new PipController( + new TvPipController( context, pipBoundsState, pipBoundsAlgorithm, pipTaskOrganizer, tvPipMenuController, pipMediaController, - pipNotification, + tvPipNotificationController, taskStackListener, windowManagerShellWrapper)); } @WMSingleton @Provides - static PipNotification providePipNotification(Context context, - PipMediaController pipMediaController) { - return new PipNotification(context, pipMediaController); - } - - @WMSingleton - @Provides static PipBoundsAlgorithm providePipBoundsHandler(Context context, PipBoundsState pipBoundsState) { return new PipBoundsAlgorithm(context, pipBoundsState); @@ -93,7 +86,7 @@ public abstract class TvPipModule { @WMSingleton @Provides - static TvPipMenuController providesPipTvMenuController( + static TvPipMenuController providesTvPipMenuController( Context context, PipBoundsState pipBoundsState, SystemWindows systemWindows, @@ -103,15 +96,22 @@ public abstract class TvPipModule { @WMSingleton @Provides + static TvPipNotificationController provideTvPipNotificationController(Context context, + PipMediaController pipMediaController) { + return new TvPipNotificationController(context, pipMediaController); + } + + @WMSingleton + @Provides static PipTaskOrganizer providePipTaskOrganizer(Context context, - TvPipMenuController tvMenuController, + TvPipMenuController tvPipMenuController, PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm, PipSurfaceTransactionHelper pipSurfaceTransactionHelper, Optional<LegacySplitScreen> splitScreenOptional, DisplayController displayController, PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) { return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm, - tvMenuController, pipSurfaceTransactionHelper, splitScreenOptional, + tvPipMenuController, pipSurfaceTransactionHelper, splitScreenOptional, displayController, pipUiEventLogger, shellTaskOrganizer); } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java index 1d3f26e73080..e20cd44974bb 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java @@ -21,7 +21,6 @@ import android.view.IWindowManager; import com.android.systemui.dagger.WMSingleton; import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.Transitions; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.ShellExecutor; @@ -32,6 +31,7 @@ import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; +import com.android.wm.shell.transition.Transitions; import dagger.Module; import dagger.Provides; diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index b46751dd0056..572b15d5215a 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -40,7 +40,8 @@ import com.android.wm.shell.ShellCommandHandlerImpl; import com.android.wm.shell.ShellInit; import com.android.wm.shell.ShellInitImpl; import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.Transitions; +import com.android.wm.shell.TaskViewFactory; +import com.android.wm.shell.TaskViewFactoryController; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.apppairs.AppPairs; import com.android.wm.shell.bubbles.BubbleController; @@ -60,6 +61,7 @@ import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController; +import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; @@ -68,10 +70,9 @@ import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.pip.phone.PipAppOpsListener; import com.android.wm.shell.pip.phone.PipTouchHandler; -import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; +import com.android.wm.shell.transition.Transitions; import java.util.Optional; -import java.util.concurrent.TimeUnit; import dagger.BindsOptionalOf; import dagger.Module; @@ -175,6 +176,7 @@ public abstract class WMShellBaseModule { Optional<LegacySplitScreen> legacySplitScreenOptional, Optional<AppPairs> appPairsOptional, FullscreenTaskListener fullscreenTaskListener, + Transitions transitions, @ShellMainThread ShellExecutor shellMainExecutor) { return ShellInitImpl.create(displayImeController, dragAndDropController, @@ -182,6 +184,7 @@ public abstract class WMShellBaseModule { legacySplitScreenOptional, appPairsOptional, fullscreenTaskListener, + transitions, shellMainExecutor); } @@ -342,6 +345,14 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides + static Optional<TaskViewFactory> provideTaskViewFactory(ShellTaskOrganizer shellTaskOrganizer, + @ShellMainThread ShellExecutor mainExecutor) { + return Optional.of(new TaskViewFactoryController(shellTaskOrganizer, mainExecutor) + .getTaskViewFactory()); + } + + @WMSingleton + @Provides static FullscreenTaskListener provideFullscreenTaskListener( SyncTransactionQueue syncQueue) { return new FullscreenTaskListener(syncQueue); diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index 509419e5d11c..e635e1708841 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -17,13 +17,10 @@ package com.android.systemui.wmshell; import android.content.Context; -import android.os.Handler; import android.view.IWindowManager; import com.android.systemui.dagger.WMSingleton; -import com.android.systemui.dagger.qualifiers.Main; import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.Transitions; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.apppairs.AppPairs; import com.android.wm.shell.apppairs.AppPairsController; @@ -49,9 +46,9 @@ import com.android.wm.shell.pip.phone.PhonePipMenuController; import com.android.wm.shell.pip.phone.PipAppOpsListener; import com.android.wm.shell.pip.phone.PipController; import com.android.wm.shell.pip.phone.PipTouchHandler; +import com.android.wm.shell.transition.Transitions; import java.util.Optional; -import java.util.concurrent.Executor; import dagger.Module; import dagger.Provides; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java index 6978ef4fb9ac..4e4c33a27da8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java @@ -37,13 +37,14 @@ import android.view.accessibility.IRemoteMagnificationAnimationCallback; import android.view.animation.AccelerateInterpolator; import androidx.test.InstrumentationRegistry; -import androidx.test.filters.MediumTest; +import androidx.test.filters.LargeTest; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.SysuiTestCase; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -54,7 +55,8 @@ import org.mockito.MockitoAnnotations; import java.util.concurrent.atomic.AtomicReference; -@MediumTest +@Ignore +@LargeTest @RunWith(AndroidTestingRunner.class) public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { diff --git a/services/Android.bp b/services/Android.bp index 3447b5cbea52..da24719c0f68 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -30,6 +30,7 @@ filegroup { ":services.searchui-sources", ":services.startop.iorap-sources", ":services.systemcaptions-sources", + ":services.translation-sources", ":services.usage-sources", ":services.usb-sources", ":services.voiceinteraction-sources", @@ -76,12 +77,12 @@ java_library { "services.searchui", "services.startop", "services.systemcaptions", + "services.translation", "services.usage", "services.usb", "services.voiceinteraction", "services.wifi", "service-blobstore", - "service-connectivity", "service-jobscheduler", "android.hidl.base-V1.0-java", ], diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 8a27acce03c3..9d028358561f 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -919,9 +919,10 @@ public class CompanionDeviceManagerService extends SystemService implements Bind break; case "associate": { + int userId = getNextArgInt(); String pkg = getNextArgRequired(); String address = getNextArgRequired(); - addAssociation(new Association(getNextArgInt(), address, pkg, null, false)); + addAssociation(new Association(userId, address, pkg, null, false)); } break; diff --git a/services/core/java/android/power/OWNERS b/services/core/java/android/power/OWNERS new file mode 100644 index 000000000000..4068e2bc03b7 --- /dev/null +++ b/services/core/java/android/power/OWNERS @@ -0,0 +1 @@ +include /BATTERY_STATS_OWNERS diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 397eeb23396c..020c17a77084 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -620,7 +620,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private LingerMonitor mLingerMonitor; // sequence number of NetworkRequests - private int mNextNetworkRequestId = 1; + private int mNextNetworkRequestId = NetworkRequest.FIRST_REQUEST_ID; // Sequence number for NetworkProvider IDs. private final AtomicInteger mNextNetworkProviderId = new AtomicInteger( @@ -1238,6 +1238,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } private synchronized int nextNetworkRequestId() { + // TODO: Consider handle wrapping and exclude {@link NetworkRequest#REQUEST_ID_NONE} if + // doing that. return mNextNetworkRequestId++; } @@ -1329,15 +1331,20 @@ public class ConnectivityService extends IConnectivityManager.Stub /** * Check if UID should be blocked from using the specified network. */ - private boolean isNetworkWithLinkPropertiesBlocked(LinkProperties lp, int uid, - boolean ignoreBlocked) { + private boolean isNetworkWithCapabilitiesBlocked(@Nullable final NetworkCapabilities nc, + final int uid, final boolean ignoreBlocked) { // Networks aren't blocked when ignoring blocked status if (ignoreBlocked) { return false; } if (isUidBlockedByVpn(uid, mVpnBlockedUidRanges)) return true; - final String iface = (lp == null ? "" : lp.getInterfaceName()); - return mPolicyManagerInternal.isUidNetworkingBlocked(uid, iface); + final long ident = Binder.clearCallingIdentity(); + try { + final boolean metered = nc == null ? true : nc.isMetered(); + return mPolicyManager.isUidNetworkingBlocked(uid, metered); + } finally { + Binder.restoreCallingIdentity(ident); + } } private void maybeLogBlockedNetworkInfo(NetworkInfo ni, int uid) { @@ -1375,12 +1382,13 @@ public class ConnectivityService extends IConnectivityManager.Stub /** * Apply any relevant filters to {@link NetworkState} for the given UID. For * example, this may mark the network as {@link DetailedState#BLOCKED} based - * on {@link #isNetworkWithLinkPropertiesBlocked}. + * on {@link #isNetworkWithCapabilitiesBlocked}. */ private void filterNetworkStateForUid(NetworkState state, int uid, boolean ignoreBlocked) { if (state == null || state.networkInfo == null || state.linkProperties == null) return; - if (isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid, ignoreBlocked)) { + if (isNetworkWithCapabilitiesBlocked(state.networkCapabilities, uid, + ignoreBlocked)) { state.networkInfo.setDetailedState(DetailedState.BLOCKED, null, null); } synchronized (mVpns) { @@ -1440,8 +1448,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } } nai = getDefaultNetwork(); - if (nai != null - && isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid, ignoreBlocked)) { + if (nai != null && isNetworkWithCapabilitiesBlocked( + nai.networkCapabilities, uid, ignoreBlocked)) { nai = null; } return nai != null ? nai.network : null; @@ -1513,7 +1521,7 @@ public class ConnectivityService extends IConnectivityManager.Stub enforceAccessPermission(); final int uid = mDeps.getCallingUid(); NetworkState state = getFilteredNetworkState(networkType, uid); - if (!isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid, false)) { + if (!isNetworkWithCapabilitiesBlocked(state.networkCapabilities, uid, false)) { return state.network; } return null; @@ -4471,7 +4479,8 @@ public class ConnectivityService extends IConnectivityManager.Stub if (!nai.everConnected) { return; } - if (isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid, false)) { + final NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai); + if (isNetworkWithCapabilitiesBlocked(nc, uid, false)) { return; } nai.networkMonitor().forceReevaluation(uid); @@ -7065,11 +7074,11 @@ public class ConnectivityService extends IConnectivityManager.Stub log(" accepting network in place of " + previousSatisfier.toShortString()); } previousSatisfier.removeRequest(nri.request.requestId); - previousSatisfier.lingerRequest(nri.request, now, mLingerDelayMs); + previousSatisfier.lingerRequest(nri.request.requestId, now, mLingerDelayMs); } else { if (VDBG || DDBG) log(" accepting network in place of null"); } - newSatisfier.unlingerRequest(nri.request); + newSatisfier.unlingerRequest(nri.request.requestId); if (!newSatisfier.addRequest(nri.request)) { Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has " + nri.request); diff --git a/services/core/java/com/android/server/ConnectivityServiceInitializer.java b/services/core/java/com/android/server/ConnectivityServiceInitializer.java index f701688b2b7e..0779f7117d82 100644 --- a/services/core/java/com/android/server/ConnectivityServiceInitializer.java +++ b/services/core/java/com/android/server/ConnectivityServiceInitializer.java @@ -35,6 +35,8 @@ public final class ConnectivityServiceInitializer extends SystemService { public ConnectivityServiceInitializer(Context context) { super(context); + // Load JNI libraries used by ConnectivityService and its dependencies + System.loadLibrary("service-connectivity"); // TODO: Define formal APIs to get the needed services. mConnectivity = new ConnectivityService(context, getNetworkManagementService(), getNetworkStatsService()); diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index 99a1d86d244e..8b506bac4a85 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -54,9 +54,13 @@ import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParserException; +import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Retention; @@ -149,6 +153,11 @@ public class PackageWatchdog { private static final String ATTR_PASSED_HEALTH_CHECK = "passed-health-check"; private static final String ATTR_MITIGATION_CALLS = "mitigation-calls"; + // A file containing information about the current mitigation count in the case of a boot loop. + // This allows boot loop information to persist in the case of an fs-checkpoint being + // aborted. + private static final String METADATA_FILE = "/metadata/watchdog/mitigation_count.txt"; + @GuardedBy("PackageWatchdog.class") private static PackageWatchdog sPackageWatchdog; @@ -492,6 +501,7 @@ public class PackageWatchdog { } if (currentObserverToNotify != null) { mBootThreshold.setMitigationCount(mitigationCount); + mBootThreshold.saveMitigationCountToMetadata(); currentObserverToNotify.executeBootLoopMitigation(mitigationCount); } } @@ -1700,9 +1710,31 @@ public class PackageWatchdog { SystemProperties.set(property, Long.toString(newStart)); } + public void saveMitigationCountToMetadata() { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(METADATA_FILE))) { + writer.write(String.valueOf(getMitigationCount())); + } catch (Exception e) { + Slog.e(TAG, "Could not save metadata to file: " + e); + } + } + + public void readMitigationCountFromMetadataIfNecessary() { + File bootPropsFile = new File(METADATA_FILE); + if (bootPropsFile.exists()) { + try (BufferedReader reader = new BufferedReader(new FileReader(METADATA_FILE))) { + String mitigationCount = reader.readLine(); + setMitigationCount(Integer.parseInt(mitigationCount)); + bootPropsFile.delete(); + } catch (Exception e) { + Slog.i(TAG, "Could not read metadata file: " + e); + } + } + } + /** Increments the boot counter, and returns whether the device is bootlooping. */ public boolean incrementAndTest() { + readMitigationCountFromMetadataIfNecessary(); final long now = mSystemClock.uptimeMillis(); if (now - getStart() < 0) { Slog.e(TAG, "Window was less than zero. Resetting start to current time."); diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java index a1cf8162f0e9..db36e62e44a3 100644 --- a/services/core/java/com/android/server/RescueParty.java +++ b/services/core/java/com/android/server/RescueParty.java @@ -31,6 +31,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.FileUtils; +import android.os.PowerManager; import android.os.RecoverySystem; import android.os.RemoteCallback; import android.os.SystemClock; @@ -77,6 +78,7 @@ public class RescueParty { @VisibleForTesting static final String PROP_ENABLE_RESCUE = "persist.sys.enable_rescue"; static final String PROP_ATTEMPTING_FACTORY_RESET = "sys.attempting_factory_reset"; + static final String PROP_ATTEMPTING_REBOOT = "sys.attempting_reboot"; static final String PROP_MAX_RESCUE_LEVEL_ATTEMPTED = "sys.max_rescue_level_attempted"; @VisibleForTesting static final int LEVEL_NONE = 0; @@ -87,7 +89,9 @@ public class RescueParty { @VisibleForTesting static final int LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS = 3; @VisibleForTesting - static final int LEVEL_FACTORY_RESET = 4; + static final int LEVEL_WARM_REBOOT = 4; + @VisibleForTesting + static final int LEVEL_FACTORY_RESET = 5; @VisibleForTesting static final String PROP_RESCUE_BOOT_COUNT = "sys.rescue_boot_count"; @VisibleForTesting @@ -159,12 +163,24 @@ public class RescueParty { } /** - * Check if we're currently attempting to reboot for a factory reset. + * Check if we're currently attempting to reboot for a factory reset. This method must + * return true if RescueParty tries to reboot early during a boot loop, since the device + * will not be fully booted at this time. + * + * TODO(gavincorkery): Rename method since its scope has expanded. */ public static boolean isAttemptingFactoryReset() { + return isFactoryResetPropertySet() || isRebootPropertySet(); + } + + static boolean isFactoryResetPropertySet() { return SystemProperties.getBoolean(PROP_ATTEMPTING_FACTORY_RESET, false); } + static boolean isRebootPropertySet() { + return SystemProperties.getBoolean(PROP_ATTEMPTING_REBOOT, false); + } + /** * Called when {@code SettingsProvider} has been published, which is a good * opportunity to reset any settings depending on our rescue level. @@ -329,8 +345,10 @@ public class RescueParty { return LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES; } else if (mitigationCount == 3) { return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS; - } else if (mitigationCount >= 4) { - return getMaxRescueLevel(); + } else if (mitigationCount == 4) { + return Math.min(getMaxRescueLevel(), LEVEL_WARM_REBOOT); + } else if (mitigationCount >= 5) { + return Math.min(getMaxRescueLevel(), LEVEL_FACTORY_RESET); } else { Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount); return LEVEL_NONE; @@ -356,6 +374,8 @@ public class RescueParty { // Try our best to reset all settings possible, and once finished // rethrow any exception that we encountered Exception res = null; + Runnable runnable; + Thread thread; switch (level) { case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: try { @@ -396,11 +416,26 @@ public class RescueParty { res = e; } break; - case LEVEL_FACTORY_RESET: + case LEVEL_WARM_REBOOT: // Request the reboot from a separate thread to avoid deadlock on PackageWatchdog // when device shutting down. + SystemProperties.set(PROP_ATTEMPTING_REBOOT, "true"); + runnable = () -> { + try { + PowerManager pm = context.getSystemService(PowerManager.class); + if (pm != null) { + pm.reboot(TAG); + } + } catch (Throwable t) { + logRescueException(level, t); + } + }; + thread = new Thread(runnable); + thread.start(); + break; + case LEVEL_FACTORY_RESET: SystemProperties.set(PROP_ATTEMPTING_FACTORY_RESET, "true"); - Runnable runnable = new Runnable() { + runnable = new Runnable() { @Override public void run() { try { @@ -410,7 +445,7 @@ public class RescueParty { } } }; - Thread thread = new Thread(runnable); + thread = new Thread(runnable); thread.start(); break; } @@ -433,6 +468,7 @@ public class RescueParty { case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: return PackageHealthObserverImpact.USER_IMPACT_LOW; case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: + case LEVEL_WARM_REBOOT: case LEVEL_FACTORY_RESET: return PackageHealthObserverImpact.USER_IMPACT_HIGH; default: @@ -714,6 +750,7 @@ public class RescueParty { case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: return "RESET_SETTINGS_UNTRUSTED_DEFAULTS"; case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: return "RESET_SETTINGS_UNTRUSTED_CHANGES"; case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: return "RESET_SETTINGS_TRUSTED_DEFAULTS"; + case LEVEL_WARM_REBOOT: return "WARM_REBOOT"; case LEVEL_FACTORY_RESET: return "FACTORY_RESET"; default: return Integer.toString(level); } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index b0f2e24e3be2..c951fd438b78 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -1716,7 +1716,7 @@ class StorageManagerService extends IStorageManager.Stub public StorageManagerService(Context context) { sSelf = this; mVoldAppDataIsolationEnabled = SystemProperties.getBoolean( - ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true); + ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false); mContext = context; mResolver = mContext.getContentResolver(); mCallbacks = new Callbacks(FgThread.get().getLooper()); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 0cbf4ba722e6..c1ab5b6a315e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -174,6 +174,7 @@ import android.app.ProfilerInfo; import android.app.WaitResult; import android.app.backup.BackupManager.OperationType; import android.app.backup.IBackupManager; +import android.app.compat.CompatChanges; import android.app.usage.UsageEvents; import android.app.usage.UsageEvents.Event; import android.app.usage.UsageStatsManager; @@ -13706,10 +13707,34 @@ public class ActivityManagerService extends IActivityManager.Stub false, false, userId, "package unstartable"); break; case Intent.ACTION_CLOSE_SYSTEM_DIALOGS: - if (!mAtmInternal.checkCanCloseSystemDialogs(callingPid, callingUid, - callerPackage)) { - // Returning success seems to be the pattern here - return ActivityManager.BROADCAST_SUCCESS; + if (!canCloseSystemDialogs(callingPid, callingUid, callerApp)) { + // The app can't close system dialogs, throw only if it targets S+ + if (CompatChanges.isChangeEnabled( + ActivityManager.LOCK_DOWN_CLOSE_SYSTEM_DIALOGS, callingUid)) { + throw new SecurityException( + "Permission Denial: " + Intent.ACTION_CLOSE_SYSTEM_DIALOGS + + " broadcast from " + callerPackage + " (pid=" + + callingPid + ", uid=" + callingUid + ")" + + " requires " + + permission.BROADCAST_CLOSE_SYSTEM_DIALOGS + "."); + } else if (CompatChanges.isChangeEnabled( + ActivityManager.DROP_CLOSE_SYSTEM_DIALOGS, callingUid)) { + Slog.w(TAG, "Permission Denial: " + intent.getAction() + + " broadcast from " + callerPackage + " (pid=" + callingPid + + ", uid=" + callingUid + ")" + + " requires " + + permission.BROADCAST_CLOSE_SYSTEM_DIALOGS + + ", dropping broadcast."); + // Returning success seems to be the pattern here + return ActivityManager.BROADCAST_SUCCESS; + } else { + Slog.w(TAG, intent.getAction() + + " broadcast from " + callerPackage + " (pid=" + callingPid + + ", uid=" + callingUid + ")" + + " will require " + + permission.BROADCAST_CLOSE_SYSTEM_DIALOGS + + " in future builds."); + } } break; } @@ -14004,6 +14029,39 @@ public class ActivityManagerService extends IActivityManager.Stub return ActivityManager.BROADCAST_SUCCESS; } + private boolean canCloseSystemDialogs(int pid, int uid, @Nullable ProcessRecord callerApp) { + if (checkPermission(permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, pid, uid) + == PERMISSION_GRANTED) { + return true; + } + if (callerApp == null) { + synchronized (mPidsSelfLocked) { + callerApp = mPidsSelfLocked.get(pid); + } + } + + if (callerApp != null) { + // Check if the instrumentation of the process has the permission. This covers the usual + // test started from the shell (which has the permission) case. This is needed for apps + // targeting SDK level < S but we are also allowing for targetSdk S+ as a convenience to + // avoid breaking a bunch of existing tests and asking them to adopt shell permissions + // to do this. + ActiveInstrumentation instrumentation = callerApp.getActiveInstrumentation(); + if (instrumentation != null && checkPermission( + permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, -1, instrumentation.mSourceUid) + == PERMISSION_GRANTED) { + return true; + } + // This is the notification trampoline use-case for example, where apps use Intent.ACSD + // to close the shade prior to starting an activity. + WindowProcessController wmApp = callerApp.getWindowProcessController(); + if (wmApp.canCloseSystemDialogsByToken()) { + return true; + } + } + return false; + } + /** * @return uid from the extra field {@link Intent#EXTRA_UID} if present, Otherwise -1 */ diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 53d75d1ddd06..6f6cad043a42 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -711,7 +711,7 @@ public final class ProcessList { mAppDataIsolationEnabled = SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true); mVoldAppDataIsolationEnabled = SystemProperties.getBoolean( - ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, true); + ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false); mAppDataIsolationWhitelistedApps = new ArrayList<>( SystemConfig.getInstance().getAppDataIsolationWhitelistedApps()); diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 669d04a6e751..e90423c2566a 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -1431,9 +1431,7 @@ class ProcessRecord implements WindowProcessListener { void setActiveInstrumentation(ActiveInstrumentation instr) { mInstr = instr; boolean isInstrumenting = instr != null; - mWindowProcessController.setInstrumenting( - isInstrumenting, - isInstrumenting ? instr.mSourceUid : -1, + mWindowProcessController.setInstrumenting(isInstrumenting, isInstrumenting && instr.mHasBackgroundActivityStartsPermission); } diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationConstants.java b/services/core/java/com/android/server/apphibernation/AppHibernationConstants.java new file mode 100644 index 000000000000..9d43a39072bf --- /dev/null +++ b/services/core/java/com/android/server/apphibernation/AppHibernationConstants.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.apphibernation; + +/** + * Flags and constants that modify app hibernation behavior. + */ +final class AppHibernationConstants { + + private AppHibernationConstants() {} + + // Device config feature flag for app hibernation + static final String KEY_APP_HIBERNATION_ENABLED = "app_hibernation_enabled"; +} diff --git a/services/core/java/com/android/server/apphibernation/OWNERS b/services/core/java/com/android/server/apphibernation/OWNERS index 4804fa3eb915..c2e27e084c8c 100644 --- a/services/core/java/com/android/server/apphibernation/OWNERS +++ b/services/core/java/com/android/server/apphibernation/OWNERS @@ -1,3 +1 @@ -# TODO: Include /core/java/android/apphibernation/OWNERS. See b/177005153 -kevhan@google.com -rajekumar@google.com +include /core/java/android/apphibernation/OWNERS diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java index b8084d5135bf..fe946cb0189d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java @@ -21,6 +21,8 @@ import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricsProtoEnums; +import java.util.Map; + /** * ClientMonitor subclass for requesting authenticatorId invalidation. See * {@link InvalidationRequesterClient} for more info. @@ -29,18 +31,21 @@ public abstract class InvalidationClient<S extends BiometricAuthenticator.Identi extends ClientMonitor<T> { private final BiometricUtils<S> mUtils; + private final Map<Integer, Long> mAuthenticatorIds; public InvalidationClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, - int userId, int sensorId, @NonNull BiometricUtils<S> utils) { + int userId, int sensorId, @NonNull BiometricUtils<S> utils, + @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, context.getOpPackageName(), 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); mUtils = utils; + mAuthenticatorIds = authenticatorIds; } public void onAuthenticatorIdInvalidated(long newAuthenticatorId) { - // TODO: Update framework w/ newAuthenticatorId + mAuthenticatorIds.put(getTargetUserId(), newAuthenticatorId); mCallback.onClientFinished(this, true /* success */); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java index f512cef80d95..9c6438ecd014 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java @@ -16,10 +16,9 @@ package com.android.server.biometrics.sensors.face.aidl; +import android.annotation.NonNull; import android.content.Context; import android.hardware.biometrics.face.ISession; - -import android.annotation.NonNull; import android.hardware.face.Face; import android.os.RemoteException; import android.util.Slog; @@ -27,13 +26,15 @@ import android.util.Slog; import com.android.server.biometrics.sensors.InvalidationClient; import com.android.server.biometrics.sensors.face.FaceUtils; +import java.util.Map; + public class FaceInvalidationClient extends InvalidationClient<Face, ISession> { private static final String TAG = "FaceInvalidationClient"; public FaceInvalidationClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon, int userId, int sensorId, - @NonNull FaceUtils utils) { - super(context, lazyDaemon, userId, sensorId, utils); + @NonNull FaceUtils utils, @NonNull Map<Integer, Long> authenticatorIds) { + super(context, lazyDaemon, userId, sensorId, utils, authenticatorIds); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java index b6d8892c4874..3d07334f04a1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java @@ -26,13 +26,15 @@ import android.util.Slog; import com.android.server.biometrics.sensors.InvalidationClient; import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; +import java.util.Map; + public class FingerprintInvalidationClient extends InvalidationClient<Fingerprint, ISession> { private static final String TAG = "FingerprintInvalidationClient"; public FingerprintInvalidationClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon, int userId, int sensorId, - @NonNull FingerprintUtils utils) { - super(context, lazyDaemon, userId, sensorId, utils); + @NonNull FingerprintUtils utils, @NonNull Map<Integer, Long> authenticatorIds) { + super(context, lazyDaemon, userId, sensorId, utils, authenticatorIds); } @Override diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java index 2c06d8230f13..b5d875d5c162 100644 --- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java +++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java @@ -122,6 +122,8 @@ final public class IpConnectivityMetrics extends SystemService { public IpConnectivityMetrics(Context ctx, ToIntFunction<Context> capacityGetter) { super(ctx); + // Load JNI libraries used by the IpConnectivityMetrics service and its dependencies + System.loadLibrary("service-connectivity"); mCapacityGetter = capacityGetter; initBuffer(); } diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 7bde4d5a2770..55d8279a92d0 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -202,28 +202,28 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // either the linger timeout expiring and the network being taken down, or the network // satisfying a request again. public static class LingerTimer implements Comparable<LingerTimer> { - public final NetworkRequest request; + public final int requestId; public final long expiryMs; - public LingerTimer(NetworkRequest request, long expiryMs) { - this.request = request; + public LingerTimer(int requestId, long expiryMs) { + this.requestId = requestId; this.expiryMs = expiryMs; } public boolean equals(Object o) { if (!(o instanceof LingerTimer)) return false; LingerTimer other = (LingerTimer) o; - return (request.requestId == other.request.requestId) && (expiryMs == other.expiryMs); + return (requestId == other.requestId) && (expiryMs == other.expiryMs); } public int hashCode() { - return Objects.hash(request.requestId, expiryMs); + return Objects.hash(requestId, expiryMs); } public int compareTo(LingerTimer other) { return (expiryMs != other.expiryMs) ? Long.compare(expiryMs, other.expiryMs) : - Integer.compare(request.requestId, other.request.requestId); + Integer.compare(requestId, other.requestId); } public String toString() { - return String.format("%s, expires %dms", request.toString(), + return String.format("%s, expires %dms", requestId, expiryMs - SystemClock.elapsedRealtime()); } } @@ -693,7 +693,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { updateRequestCounts(REMOVE, existing); mNetworkRequests.remove(requestId); if (existing.isRequest()) { - unlingerRequest(existing); + unlingerRequest(existing.requestId); } } @@ -839,33 +839,33 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { } /** - * Sets the specified request to linger on this network for the specified time. Called by + * Sets the specified requestId to linger on this network for the specified time. Called by * ConnectivityService when the request is moved to another network with a higher score. */ - public void lingerRequest(NetworkRequest request, long now, long duration) { - if (mLingerTimerForRequest.get(request.requestId) != null) { + public void lingerRequest(int requestId, long now, long duration) { + if (mLingerTimerForRequest.get(requestId) != null) { // Cannot happen. Once a request is lingering on a particular network, we cannot // re-linger it unless that network becomes the best for that request again, in which // case we should have unlingered it. - Log.wtf(TAG, toShortString() + ": request " + request.requestId + " already lingered"); + Log.wtf(TAG, toShortString() + ": request " + requestId + " already lingered"); } final long expiryMs = now + duration; - LingerTimer timer = new LingerTimer(request, expiryMs); + LingerTimer timer = new LingerTimer(requestId, expiryMs); if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + toShortString()); mLingerTimers.add(timer); - mLingerTimerForRequest.put(request.requestId, timer); + mLingerTimerForRequest.put(requestId, timer); } /** * Cancel lingering. Called by ConnectivityService when a request is added to this network. - * Returns true if the given request was lingering on this network, false otherwise. + * Returns true if the given requestId was lingering on this network, false otherwise. */ - public boolean unlingerRequest(NetworkRequest request) { - LingerTimer timer = mLingerTimerForRequest.get(request.requestId); + public boolean unlingerRequest(int requestId) { + LingerTimer timer = mLingerTimerForRequest.get(requestId); if (timer != null) { if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + toShortString()); mLingerTimers.remove(timer); - mLingerTimerForRequest.remove(request.requestId); + mLingerTimerForRequest.remove(requestId); return true; } return false; diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java index 06721aea5540..93930ae16de8 100644 --- a/services/core/java/com/android/server/connectivity/PacManager.java +++ b/services/core/java/com/android/server/connectivity/PacManager.java @@ -177,7 +177,7 @@ public class PacManager { * @param proxy Proxy information that is about to be broadcast. * @return Returns whether the broadcast should be sent : either DO_ or DONT_SEND_BROADCAST */ - synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) { + public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) { if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) { if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) { // Allow to send broadcast, nothing to do. diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 07a4b89be4e9..b250f164a264 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -1850,34 +1850,6 @@ public class Vpn { } } - /** - * @param uid The target uid. - * - * @return {@code true} if {@code uid} is included in one of the mBlockedUidsAsToldToNetd - * ranges and the VPN is not connected, or if the VPN is connected but does not apply to - * the {@code uid}. - * - * @apiNote This method don't check VPN lockdown status. - * @see #mBlockedUidsAsToldToConnectivity - */ - public synchronized boolean isBlockingUid(int uid) { - if (mNetworkInfo.isConnected()) { - return !appliesToUid(uid); - } else { - return containsUid(mBlockedUidsAsToldToConnectivity, uid); - } - } - - private boolean containsUid(Collection<UidRangeParcel> ranges, int uid) { - if (ranges == null) return false; - for (UidRangeParcel range : ranges) { - if (range.start <= uid && uid <= range.stop) { - return true; - } - } - return false; - } - private void updateAlwaysOnNotification(DetailedState networkState) { final boolean visible = (mAlwaysOn && networkState != DetailedState.CONNECTED); diff --git a/services/core/java/com/android/server/graphics/fonts/OWNERS b/services/core/java/com/android/server/graphics/fonts/OWNERS new file mode 100644 index 000000000000..34ac813f02e0 --- /dev/null +++ b/services/core/java/com/android/server/graphics/fonts/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 24939 + +include /graphics/java/android/graphics/fonts/OWNERS 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 2b3c0bff8548..0d284fc2de4f 100644 --- a/services/core/java/com/android/server/location/timezone/ControllerImpl.java +++ b/services/core/java/com/android/server/location/timezone/ControllerImpl.java @@ -73,6 +73,10 @@ class ControllerImpl extends LocationTimeZoneProviderController { // Non-null after initialize() private Callback mCallback; + /** Indicates both providers have completed initialization. */ + @GuardedBy("mSharedLock") + private boolean mProvidersInitialized; + /** * Used for scheduling uncertainty timeouts, i.e after a provider has reported uncertainty. * This timeout is not provider-specific: it is started when the controller becomes uncertain @@ -108,6 +112,7 @@ class ControllerImpl extends LocationTimeZoneProviderController { ControllerImpl.this::onProviderStateChange; mPrimaryProvider.initialize(providerListener); mSecondaryProvider.initialize(providerListener); + mProvidersInitialized = true; alterProvidersStartedStateIfRequired( null /* oldConfiguration */, mCurrentUserConfiguration); @@ -322,6 +327,16 @@ class ControllerImpl extends LocationTimeZoneProviderController { assertProviderKnown(provider); synchronized (mSharedLock) { + // Ignore provider state changes during initialization. e.g. if the primary provider + // moves to PROVIDER_STATE_PERM_FAILED during initialization, the secondary will not + // be ready to take over yet. + if (!mProvidersInitialized) { + warnLog("onProviderStateChange: Ignoring provider state change because both" + + " providers have not yet completed initialization." + + " providerState=" + providerState); + return; + } + switch (providerState.stateEnum) { case PROVIDER_STATE_STARTED_INITIALIZING: case PROVIDER_STATE_STOPPED: diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java index 407cedf38917..141fa6a17873 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java @@ -44,12 +44,6 @@ public abstract class NetworkPolicyManagerInternal { public abstract boolean isUidRestrictedOnMeteredNetworks(int uid); /** - * @return true if networking is blocked on the given interface for the given uid according - * to current networking policies. - */ - public abstract boolean isUidNetworkingBlocked(int uid, String ifname); - - /** * Figure out if networking is blocked for a given set of conditions. * * This is used by ConnectivityService via passing stale copies of conditions, so it must not diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 0e7b4b8c9c5e..7c17356fa3cc 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -5352,7 +5352,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { public boolean isUidNetworkingBlocked(int uid, boolean isNetworkMetered) { final long startTime = mStatLogger.getTime(); - mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG); + enforceAnyPermissionOf(OBSERVE_NETWORK_POLICY, PERMISSION_MAINLINE_NETWORK_STACK); final int uidRules; final boolean isBackgroundRestricted; synchronized (mUidRulesFirstLock) { @@ -5451,32 +5451,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { && !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED); } - /** - * @return true if networking is blocked on the given interface for the given uid according - * to current networking policies. - */ - @Override - public boolean isUidNetworkingBlocked(int uid, String ifname) { - final long startTime = mStatLogger.getTime(); - - final int uidRules; - final boolean isBackgroundRestricted; - synchronized (mUidRulesFirstLock) { - uidRules = mUidRules.get(uid, RULE_NONE); - isBackgroundRestricted = mRestrictBackground; - } - final boolean isNetworkMetered; - synchronized (mMeteredIfacesLock) { - isNetworkMetered = mMeteredIfaces.contains(ifname); - } - final boolean ret = isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered, - isBackgroundRestricted, mLogger); - - mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime); - - return ret; - } - @Override public void onTempPowerSaveWhitelistChange(int appId, boolean added) { synchronized (mUidRulesFirstLock) { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 5ac581a77caf..4ab827908e34 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -446,16 +446,6 @@ public class NotificationManagerService extends SystemService { @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R) private static final long NOTIFICATION_TRAMPOLINE_BLOCK = 167676448L; - /** - * Rate limit showing toasts, on a per package basis. - * - * It limits the effects of {@link android.widget.Toast#show()} calls to prevent overburdening - * the user with too many toasts in a limited time. Any attempt to show more toasts than allowed - * in a certain time frame will result in the toast being discarded. - */ - @ChangeId - private static final long RATE_LIMIT_TOASTS = 154198299L; - private IActivityManager mAm; private ActivityTaskManagerInternal mAtm; private ActivityManager mActivityManager; @@ -527,6 +517,9 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>(); final ArrayList<ToastRecord> mToastQueue = new ArrayList<>(); + // set of uids for which toast rate limiting is disabled + @GuardedBy("mToastQueue") + private final Set<Integer> mToastRateLimitingDisabledUids = new ArraySet<>(); final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>(); // True if the toast that's on top of the queue is being shown at the moment. @@ -3068,6 +3061,22 @@ public class NotificationManagerService extends SystemService { } @Override + public void setToastRateLimitingEnabled(boolean enable) { + getContext().enforceCallingPermission( + android.Manifest.permission.MANAGE_TOAST_RATE_LIMITING, + "App doesn't have the permission to enable/disable toast rate limiting"); + + synchronized (mToastQueue) { + int uid = Binder.getCallingUid(); + if (enable) { + mToastRateLimitingDisabledUids.remove(uid); + } else { + mToastRateLimitingDisabledUids.add(uid); + } + } + } + + @Override public void finishToken(String pkg, IBinder token) { synchronized (mToastQueue) { final long callingId = Binder.clearCallingIdentity(); @@ -7378,7 +7387,7 @@ public class NotificationManagerService extends SystemService { while (record != null) { int userId = UserHandle.getUserId(record.uid); boolean rateLimitingEnabled = - CompatChanges.isChangeEnabled(RATE_LIMIT_TOASTS, record.uid); + !mToastRateLimitingDisabledUids.contains(record.uid); boolean isWithinQuota = mToastRateLimiter.isWithinQuota(userId, record.pkg, TOAST_QUOTA_TAG); diff --git a/services/core/java/com/android/server/pm/MULTIUSER_AND_ENTERPRISE_OWNERS b/services/core/java/com/android/server/pm/MULTIUSER_AND_ENTERPRISE_OWNERS new file mode 100644 index 000000000000..48f6bf84c28b --- /dev/null +++ b/services/core/java/com/android/server/pm/MULTIUSER_AND_ENTERPRISE_OWNERS @@ -0,0 +1,7 @@ +# OWNERS of Multiuser related files related to Enterprise + +# TODO: include /MULTIUSER_OWNERS + +# Enterprise owners +rubinxu@google.com +sandness@google.com 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 004c0154963b..8f422890c973 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -4463,12 +4463,10 @@ public class PermissionManagerService extends IPermissionManager.Stub { final PermissionPolicyInternal permissionPolicyInternal = LocalServices.getService( PermissionPolicyInternal.class); - permissionPolicyInternal.setOnInitializedCallback(userId -> { - // The SDK updated case is already handled when we run during the ctor. - synchronized (mLock) { - updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, false); - } - }); + permissionPolicyInternal.setOnInitializedCallback(userId -> + // The SDK updated case is already handled when we run during the ctor. + updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, false) + ); mSystemReady = true; diff --git a/services/core/java/com/android/server/powerstats/BatteryTrigger.java b/services/core/java/com/android/server/powerstats/BatteryTrigger.java index 6500523198ba..b35cb52d5025 100644 --- a/services/core/java/com/android/server/powerstats/BatteryTrigger.java +++ b/services/core/java/com/android/server/powerstats/BatteryTrigger.java @@ -43,7 +43,7 @@ public final class BatteryTrigger extends PowerStatsLogTrigger { if (newBatteryLevel < mBatteryLevel) { if (DEBUG) Slog.d(TAG, "Battery level dropped. Log rail data"); - logPowerStatsData(); + logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_BATTERY_DROP); } mBatteryLevel = newBatteryLevel; diff --git a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java index c9595c2eec2b..6d9cb7522bef 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java @@ -218,7 +218,7 @@ public class PowerStatsDataStorage { * array and written to on-device storage. */ public void write(byte[] data) { - if (data.length > 0) { + if (data != null && data.length > 0) { mLock.lock(); long currentTimeMillis = System.currentTimeMillis(); diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogTrigger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogTrigger.java index 1754185ea71b..e3672a8d09ca 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsLogTrigger.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsLogTrigger.java @@ -31,10 +31,8 @@ public abstract class PowerStatsLogTrigger { protected Context mContext; private PowerStatsLogger mPowerStatsLogger; - protected void logPowerStatsData() { - Message.obtain( - mPowerStatsLogger, - PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE).sendToTarget(); + protected void logPowerStatsData(int msgType) { + Message.obtain(mPowerStatsLogger, msgType).sendToTarget(); } public PowerStatsLogTrigger(Context context, PowerStatsLogger powerStatsLogger) { diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java index 409cd826b6bc..9ee34298a145 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java @@ -20,6 +20,8 @@ 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.StateResidencyResult; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -32,6 +34,8 @@ import com.android.server.powerstats.ProtoStreamUtils.ChannelInfoUtils; import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerIdUtils; import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerResultUtils; import com.android.server.powerstats.ProtoStreamUtils.EnergyMeasurementUtils; +import com.android.server.powerstats.ProtoStreamUtils.PowerEntityInfoUtils; +import com.android.server.powerstats.ProtoStreamUtils.StateResidencyResultUtils; import java.io.ByteArrayInputStream; import java.io.File; @@ -42,23 +46,25 @@ import java.io.IOException; * PowerStatsLogger is responsible for logging model and meter energy data to on-device storage. * Messages are sent to its message handler to request that energy data be logged, at which time it * queries the PowerStats HAL and logs the data to on-device storage. The on-device storage is - * dumped to file by calling writeModelDataToFile or writeMeterDataToFile with a file descriptor - * that points to the output file. + * dumped to file by calling writeModelDataToFile, writeMeterDataToFile, or writeResidencyDataToFile + * with a file descriptor that points to the output file. */ public final class PowerStatsLogger extends Handler { private static final String TAG = PowerStatsLogger.class.getSimpleName(); private static final boolean DEBUG = false; - protected static final int MSG_LOG_TO_DATA_STORAGE = 0; + protected static final int MSG_LOG_TO_DATA_STORAGE_TIMER = 0; + protected static final int MSG_LOG_TO_DATA_STORAGE_BATTERY_DROP = 1; private final PowerStatsDataStorage mPowerStatsMeterStorage; private final PowerStatsDataStorage mPowerStatsModelStorage; + private final PowerStatsDataStorage mPowerStatsResidencyStorage; private final IPowerStatsHALWrapper mPowerStatsHALWrapper; @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_LOG_TO_DATA_STORAGE: - if (DEBUG) Slog.d(TAG, "Logging to data storage"); + case MSG_LOG_TO_DATA_STORAGE_TIMER: + if (DEBUG) Slog.d(TAG, "Logging to data storage on timer"); // Log power meter data. EnergyMeasurement[] energyMeasurements = @@ -74,6 +80,17 @@ public final class PowerStatsLogger extends Handler { EnergyConsumerResultUtils.getProtoBytes(energyConsumerResults)); if (DEBUG) EnergyConsumerResultUtils.print(energyConsumerResults); break; + + case MSG_LOG_TO_DATA_STORAGE_BATTERY_DROP: + if (DEBUG) Slog.d(TAG, "Logging to data storage on battery drop"); + + // Log state residency data. + StateResidencyResult[] stateResidencyResults = + mPowerStatsHALWrapper.getStateResidency(new int[0]); + mPowerStatsResidencyStorage.write( + StateResidencyResultUtils.getProtoBytes(stateResidencyResults)); + if (DEBUG) StateResidencyResultUtils.print(stateResidencyResults); + break; } } @@ -159,13 +176,57 @@ public final class PowerStatsLogger extends Handler { pos.flush(); } + /** + * Writes residency data stored in PowerStatsDataStorage to a file descriptor. + * + * @param fd FileDescriptor where residency data stored in PowerStatsDataStorage is written. + * Data is written in protobuf format as defined by powerstatsservice.proto. + */ + public void writeResidencyDataToFile(FileDescriptor fd) { + if (DEBUG) Slog.d(TAG, "Writing residency data to file"); + + final ProtoOutputStream pos = new ProtoOutputStream(fd); + + try { + PowerEntityInfo[] powerEntityInfo = mPowerStatsHALWrapper.getPowerEntityInfo(); + PowerEntityInfoUtils.packProtoMessage(powerEntityInfo, pos); + if (DEBUG) PowerEntityInfoUtils.print(powerEntityInfo); + + mPowerStatsResidencyStorage.read(new PowerStatsDataStorage.DataElementReadCallback() { + @Override + public void onReadDataElement(byte[] data) { + try { + final ProtoInputStream pis = + new ProtoInputStream(new ByteArrayInputStream(data)); + // TODO(b/166535853): ProtoOutputStream doesn't provide a method to write + // a byte array that already contains a serialized proto, so I have to + // deserialize, then re-serialize. This is computationally inefficient. + StateResidencyResult[] stateResidencyResult = + StateResidencyResultUtils.unpackProtoMessage(data); + StateResidencyResultUtils.packProtoMessage(stateResidencyResult, pos); + if (DEBUG) StateResidencyResultUtils.print(stateResidencyResult); + } catch (IOException e) { + Slog.e(TAG, "Failed to write residency data to incident report."); + } + } + }); + } catch (IOException e) { + Slog.e(TAG, "Failed to write residency data to incident report."); + } + + pos.flush(); + } + public PowerStatsLogger(Context context, File dataStoragePath, String meterFilename, - String modelFilename, IPowerStatsHALWrapper powerStatsHALWrapper) { + String modelFilename, String residencyFilename, + IPowerStatsHALWrapper powerStatsHALWrapper) { super(Looper.getMainLooper()); mPowerStatsHALWrapper = powerStatsHALWrapper; mPowerStatsMeterStorage = new PowerStatsDataStorage(context, dataStoragePath, meterFilename); mPowerStatsModelStorage = new PowerStatsDataStorage(context, dataStoragePath, modelFilename); + mPowerStatsResidencyStorage = new PowerStatsDataStorage(context, dataStoragePath, + residencyFilename); } } diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java index ce50e5833c45..777857209de0 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsService.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java @@ -49,6 +49,8 @@ public class PowerStatsService extends SystemService { private static final int DATA_STORAGE_VERSION = 0; private static final String METER_FILENAME = "log.powerstats.meter." + DATA_STORAGE_VERSION; private static final String MODEL_FILENAME = "log.powerstats.model." + DATA_STORAGE_VERSION; + private static final String RESIDENCY_FILENAME = + "log.powerstats.residency." + DATA_STORAGE_VERSION; private final Injector mInjector; @@ -76,15 +78,19 @@ public class PowerStatsService extends SystemService { return MODEL_FILENAME; } + String createResidencyFilename() { + return RESIDENCY_FILENAME; + } + IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() { return PowerStatsHALWrapper.getPowerStatsHalImpl(); } PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath, - String meterFilename, String modelFilename, + String meterFilename, String modelFilename, String residencyFilename, IPowerStatsHALWrapper powerStatsHALWrapper) { return new PowerStatsLogger(context, dataStoragePath, meterFilename, - modelFilename, powerStatsHALWrapper); + modelFilename, residencyFilename, powerStatsHALWrapper); } BatteryTrigger createBatteryTrigger(Context context, PowerStatsLogger powerStatsLogger) { @@ -109,6 +115,8 @@ public class PowerStatsService extends SystemService { mPowerStatsLogger.writeModelDataToFile(fd); } else if ("meter".equals(args[1])) { mPowerStatsLogger.writeMeterDataToFile(fd); + } else if ("residency".equals(args[1])) { + mPowerStatsLogger.writeResidencyDataToFile(fd); } } else if (args.length == 0) { pw.println("PowerStatsService dumpsys: available PowerEntityInfos"); @@ -148,7 +156,8 @@ public class PowerStatsService extends SystemService { // Only start logger and triggers if initialization is successful. mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext, mInjector.createDataStoragePath(), mInjector.createMeterFilename(), - mInjector.createModelFilename(), mPowerStatsHALWrapper); + mInjector.createModelFilename(), mInjector.createResidencyFilename(), + mPowerStatsHALWrapper); mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger); mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger); } else { diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java index 5e23b86e0adb..ab9b3e0c41cd 100644 --- a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java +++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java @@ -20,6 +20,8 @@ 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 android.util.Slog; import android.util.proto.ProtoInputStream; @@ -45,6 +47,29 @@ public class ProtoStreamUtils { private static final String TAG = ProtoStreamUtils.class.getSimpleName(); static class PowerEntityInfoUtils { + public static void packProtoMessage(PowerEntityInfo[] powerEntityInfo, + ProtoOutputStream pos) { + if (powerEntityInfo == null) return; + + for (int i = 0; i < powerEntityInfo.length; i++) { + long peiToken = pos.start(PowerStatsServiceResidencyProto.POWER_ENTITY_INFO); + pos.write(PowerEntityInfoProto.POWER_ENTITY_ID, powerEntityInfo[i].powerEntityId); + pos.write(PowerEntityInfoProto.POWER_ENTITY_NAME, + powerEntityInfo[i].powerEntityName); + if (powerEntityInfo[i].states != null) { + final int statesLength = powerEntityInfo[i].states.length; + for (int j = 0; j < statesLength; j++) { + final StateInfo state = powerEntityInfo[i].states[j]; + long siToken = pos.start(PowerEntityInfoProto.STATES); + pos.write(StateInfoProto.STATE_ID, state.stateId); + pos.write(StateInfoProto.STATE_NAME, state.stateName); + pos.end(siToken); + } + } + pos.end(peiToken); + } + } + public static void print(PowerEntityInfo[] powerEntityInfo) { if (powerEntityInfo == null) return; @@ -77,6 +102,144 @@ public class ProtoStreamUtils { } static class StateResidencyResultUtils { + public static byte[] getProtoBytes(StateResidencyResult[] stateResidencyResult) { + ProtoOutputStream pos = new ProtoOutputStream(); + packProtoMessage(stateResidencyResult, pos); + return pos.getBytes(); + } + + public static void packProtoMessage(StateResidencyResult[] stateResidencyResult, + ProtoOutputStream pos) { + if (stateResidencyResult == null) return; + + for (int i = 0; i < stateResidencyResult.length; i++) { + final int stateLength = stateResidencyResult[i].stateResidencyData.length; + long srrToken = pos.start(PowerStatsServiceResidencyProto.STATE_RESIDENCY_RESULT); + pos.write(StateResidencyResultProto.POWER_ENTITY_ID, + stateResidencyResult[i].powerEntityId); + for (int j = 0; j < stateLength; j++) { + final StateResidency stateResidencyData = + stateResidencyResult[i].stateResidencyData[j]; + long srdToken = pos.start(StateResidencyResultProto.STATE_RESIDENCY_DATA); + pos.write(StateResidencyProto.STATE_ID, stateResidencyData.stateId); + pos.write(StateResidencyProto.TOTAL_TIME_IN_STATE_MS, + stateResidencyData.totalTimeInStateMs); + pos.write(StateResidencyProto.TOTAL_STATE_ENTRY_COUNT, + stateResidencyData.totalStateEntryCount); + pos.write(StateResidencyProto.LAST_ENTRY_TIMESTAMP_MS, + stateResidencyData.lastEntryTimestampMs); + pos.end(srdToken); + } + pos.end(srrToken); + } + } + + public static StateResidencyResult[] unpackProtoMessage(byte[] data) throws IOException { + final ProtoInputStream pis = new ProtoInputStream(new ByteArrayInputStream(data)); + List<StateResidencyResult> stateResidencyResultList = + new ArrayList<StateResidencyResult>(); + while (true) { + try { + int nextField = pis.nextField(); + StateResidencyResult stateResidencyResult = new StateResidencyResult(); + + if (nextField == (int) PowerStatsServiceResidencyProto.STATE_RESIDENCY_RESULT) { + long token = + pis.start(PowerStatsServiceResidencyProto.STATE_RESIDENCY_RESULT); + stateResidencyResultList.add(unpackStateResidencyResultProto(pis)); + pis.end(token); + } else if (nextField == ProtoInputStream.NO_MORE_FIELDS) { + return stateResidencyResultList.toArray( + new StateResidencyResult[stateResidencyResultList.size()]); + } else { + Slog.e(TAG, "Unhandled field in PowerStatsServiceResidencyProto: " + + ProtoUtils.currentFieldToString(pis)); + } + } catch (WireTypeMismatchException wtme) { + Slog.e(TAG, "Wire Type mismatch in PowerStatsServiceResidencyProto: " + + ProtoUtils.currentFieldToString(pis)); + } + } + } + + private static StateResidencyResult unpackStateResidencyResultProto(ProtoInputStream pis) + throws IOException { + StateResidencyResult stateResidencyResult = new StateResidencyResult(); + List<StateResidency> stateResidencyList = new ArrayList<StateResidency>(); + + while (true) { + try { + switch (pis.nextField()) { + case (int) StateResidencyResultProto.POWER_ENTITY_ID: + stateResidencyResult.powerEntityId = + pis.readInt(StateResidencyResultProto.POWER_ENTITY_ID); + break; + + case (int) StateResidencyResultProto.STATE_RESIDENCY_DATA: + long token = pis.start(StateResidencyResultProto.STATE_RESIDENCY_DATA); + stateResidencyList.add(unpackStateResidencyProto(pis)); + pis.end(token); + break; + + case ProtoInputStream.NO_MORE_FIELDS: + stateResidencyResult.stateResidencyData = stateResidencyList.toArray( + new StateResidency[stateResidencyList.size()]); + return stateResidencyResult; + + default: + Slog.e(TAG, "Unhandled field in StateResidencyResultProto: " + + ProtoUtils.currentFieldToString(pis)); + break; + } + } catch (WireTypeMismatchException wtme) { + Slog.e(TAG, "Wire Type mismatch in StateResidencyResultProto: " + + ProtoUtils.currentFieldToString(pis)); + } + } + } + + private static StateResidency unpackStateResidencyProto(ProtoInputStream pis) + throws IOException { + StateResidency stateResidency = new StateResidency(); + + while (true) { + try { + switch (pis.nextField()) { + case (int) StateResidencyProto.STATE_ID: + stateResidency.stateId = pis.readInt(StateResidencyProto.STATE_ID); + break; + + case (int) StateResidencyProto.TOTAL_TIME_IN_STATE_MS: + stateResidency.totalTimeInStateMs = + pis.readLong(StateResidencyProto.TOTAL_TIME_IN_STATE_MS); + break; + + case (int) StateResidencyProto.TOTAL_STATE_ENTRY_COUNT: + stateResidency.totalStateEntryCount = + pis.readLong(StateResidencyProto.TOTAL_STATE_ENTRY_COUNT); + break; + + case (int) StateResidencyProto.LAST_ENTRY_TIMESTAMP_MS: + stateResidency.lastEntryTimestampMs = + pis.readLong(StateResidencyProto.LAST_ENTRY_TIMESTAMP_MS); + break; + + case ProtoInputStream.NO_MORE_FIELDS: + return stateResidency; + + default: + Slog.e(TAG, "Unhandled field in StateResidencyProto: " + + ProtoUtils.currentFieldToString(pis)); + break; + + } + } catch (WireTypeMismatchException wtme) { + Slog.e(TAG, "Wire Type mismatch in StateResidencyProto: " + + ProtoUtils.currentFieldToString(pis)); + } + } + } + public static void print(StateResidencyResult[] stateResidencyResult) { if (stateResidencyResult == null) return; @@ -98,17 +261,14 @@ public class ProtoStreamUtils { static class ChannelInfoUtils { public static void packProtoMessage(ChannelInfo[] channelInfo, ProtoOutputStream pos) { - long token; - if (channelInfo == null) return; for (int i = 0; i < channelInfo.length; i++) { - token = pos.start(PowerStatsServiceMeterProto.CHANNEL_INFO); + long token = pos.start(PowerStatsServiceMeterProto.CHANNEL_INFO); pos.write(ChannelInfoProto.CHANNEL_ID, channelInfo[i].channelId); pos.write(ChannelInfoProto.CHANNEL_NAME, channelInfo[i].channelName); pos.end(token); } - } public static void print(ChannelInfo[] channelInfo) { @@ -139,12 +299,10 @@ public class ProtoStreamUtils { public static void packProtoMessage(EnergyMeasurement[] energyMeasurement, ProtoOutputStream pos) { - long token; - if (energyMeasurement == null) return; for (int i = 0; i < energyMeasurement.length; i++) { - token = pos.start(PowerStatsServiceMeterProto.ENERGY_MEASUREMENT); + long token = pos.start(PowerStatsServiceMeterProto.ENERGY_MEASUREMENT); pos.write(EnergyMeasurementProto.CHANNEL_ID, energyMeasurement[i].channelId); pos.write(EnergyMeasurementProto.TIMESTAMP_MS, energyMeasurement[i].timestampMs); pos.write(EnergyMeasurementProto.ENERGY_UWS, energyMeasurement[i].energyUWs); @@ -155,7 +313,6 @@ public class ProtoStreamUtils { public static EnergyMeasurement[] unpackProtoMessage(byte[] data) throws IOException { final ProtoInputStream pis = new ProtoInputStream(new ByteArrayInputStream(data)); List<EnergyMeasurement> energyMeasurementList = new ArrayList<EnergyMeasurement>(); - long token; while (true) { try { @@ -163,8 +320,8 @@ public class ProtoStreamUtils { EnergyMeasurement energyMeasurement = new EnergyMeasurement(); if (nextField == (int) PowerStatsServiceMeterProto.ENERGY_MEASUREMENT) { - token = pis.start(PowerStatsServiceMeterProto.ENERGY_MEASUREMENT); - energyMeasurementList.add(unpackProtoMessage(pis)); + long token = pis.start(PowerStatsServiceMeterProto.ENERGY_MEASUREMENT); + energyMeasurementList.add(unpackEnergyMeasurementProto(pis)); pis.end(token); } else if (nextField == ProtoInputStream.NO_MORE_FIELDS) { return energyMeasurementList.toArray( @@ -180,7 +337,7 @@ public class ProtoStreamUtils { } } - private static EnergyMeasurement unpackProtoMessage(ProtoInputStream pis) + private static EnergyMeasurement unpackEnergyMeasurementProto(ProtoInputStream pis) throws IOException { EnergyMeasurement energyMeasurement = new EnergyMeasurement(); @@ -230,12 +387,10 @@ public class ProtoStreamUtils { static class EnergyConsumerIdUtils { public static void packProtoMessage(int[] energyConsumerId, ProtoOutputStream pos) { - long token; - if (energyConsumerId == null) return; for (int i = 0; i < energyConsumerId.length; i++) { - token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_ID); + long token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_ID); pos.write(EnergyConsumerIdProto.ENERGY_CONSUMER_ID, energyConsumerId[i]); pos.end(token); } @@ -267,12 +422,10 @@ public class ProtoStreamUtils { public static void packProtoMessage(EnergyConsumerResult[] energyConsumerResult, ProtoOutputStream pos) { - long token; - if (energyConsumerResult == null) return; for (int i = 0; i < energyConsumerResult.length; i++) { - token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT); + long token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT); pos.write(EnergyConsumerResultProto.ENERGY_CONSUMER_ID, energyConsumerResult[i].energyConsumerId); pos.write(EnergyConsumerResultProto.TIMESTAMP_MS, @@ -286,16 +439,14 @@ public class ProtoStreamUtils { final ProtoInputStream pis = new ProtoInputStream(new ByteArrayInputStream(data)); List<EnergyConsumerResult> energyConsumerResultList = new ArrayList<EnergyConsumerResult>(); - long token; - while (true) { try { int nextField = pis.nextField(); EnergyConsumerResult energyConsumerResult = new EnergyConsumerResult(); if (nextField == (int) PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT) { - token = pis.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT); - energyConsumerResultList.add(unpackProtoMessage(pis)); + long token = pis.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT); + energyConsumerResultList.add(unpackEnergyConsumerResultProto(pis)); pis.end(token); } else if (nextField == ProtoInputStream.NO_MORE_FIELDS) { return energyConsumerResultList.toArray( @@ -311,7 +462,7 @@ public class ProtoStreamUtils { } } - private static EnergyConsumerResult unpackProtoMessage(ProtoInputStream pis) + private static EnergyConsumerResult unpackEnergyConsumerResultProto(ProtoInputStream pis) throws IOException { EnergyConsumerResult energyConsumerResult = new EnergyConsumerResult(); diff --git a/services/core/java/com/android/server/powerstats/TimerTrigger.java b/services/core/java/com/android/server/powerstats/TimerTrigger.java index 4b5929552856..7cba00f9a669 100644 --- a/services/core/java/com/android/server/powerstats/TimerTrigger.java +++ b/services/core/java/com/android/server/powerstats/TimerTrigger.java @@ -29,7 +29,7 @@ public final class TimerTrigger extends PowerStatsLogTrigger { private static final String TAG = TimerTrigger.class.getSimpleName(); private static final boolean DEBUG = false; // TODO(b/166689029): Make configurable through global settings. - private static final long LOG_PERIOD_MS = 60 * 1000; + private static final long LOG_PERIOD_MS = 120 * 1000; private final Handler mHandler; @@ -40,7 +40,7 @@ public final class TimerTrigger extends PowerStatsLogTrigger { // LOG_PERIOD_MS. mHandler.postDelayed(mLogData, LOG_PERIOD_MS); if (DEBUG) Slog.d(TAG, "Received delayed message. Log rail data"); - logPowerStatsData(); + logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_TIMER); } }; diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java index 35c9f9ae6683..3a08ddc6c405 100644 --- a/services/core/java/com/android/server/rollback/RollbackStore.java +++ b/services/core/java/com/android/server/rollback/RollbackStore.java @@ -25,6 +25,8 @@ import android.content.rollback.PackageRollbackInfo; import android.content.rollback.PackageRollbackInfo.RestoreInfo; import android.content.rollback.RollbackInfo; import android.os.UserHandle; +import android.system.ErrnoException; +import android.system.Os; import android.util.AtomicFile; import android.util.Slog; import android.util.SparseIntArray; @@ -237,8 +239,19 @@ class RollbackStore { targetDir.mkdirs(); File targetFile = new File(targetDir, sourceFile.getName()); - // TODO: Copy by hard link instead to save on cpu and storage space? - Files.copy(sourceFile.toPath(), targetFile.toPath()); + try { + // Create a hard link to avoid copy + // TODO(b/168562373) + // Linking between non-encrypted and encrypted is not supported and we have + // encrypted /data/rollback and non-encrypted /data/apex/active. For now this works + // because we happen to store encrypted files under /data/apex/active which is no + // longer the case when compressed apex rolls out. We have to handle this case in + // order not to fall back to copy. + Os.link(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath()); + } catch (ErrnoException ignore) { + // Fall back to copy if hardlink can't be created + Files.copy(sourceFile.toPath(), targetFile.toPath()); + } } /** diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 638232d23201..d65661771a84 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -138,7 +138,6 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.os.BatterySipper; import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BinderCallsStats.ExportedCallStat; -import com.android.internal.os.KernelCpuSpeedReader; import com.android.internal.os.KernelCpuThreadReader; import com.android.internal.os.KernelCpuThreadReaderDiff; import com.android.internal.os.KernelCpuThreadReaderSettingsObserver; @@ -301,8 +300,6 @@ public class StatsPullAtomService extends SystemService { @GuardedBy("mDiskIoLock") private StoragedUidIoStatsReader mStoragedUidIoStatsReader; - @GuardedBy("mCpuTimePerFreqLock") - private KernelCpuSpeedReader[] mKernelCpuSpeedReaders; // Disables throttler on CPU time readers. @GuardedBy("mCpuTimePerUidLock") private KernelCpuUidUserSysTimeReader mCpuUidUserSysTimeReader; @@ -353,7 +350,6 @@ public class StatsPullAtomService extends SystemService { private final Object mDataBytesTransferLock = new Object(); private final Object mBluetoothBytesTransferLock = new Object(); private final Object mKernelWakelockLock = new Object(); - private final Object mCpuTimePerFreqLock = new Object(); private final Object mCpuTimePerUidLock = new Object(); private final Object mCpuTimePerUidFreqLock = new Object(); private final Object mCpuActiveTimeLock = new Object(); @@ -442,10 +438,6 @@ public class StatsPullAtomService extends SystemService { synchronized (mKernelWakelockLock) { return pullKernelWakelockLocked(atomTag, data); } - case FrameworkStatsLog.CPU_TIME_PER_FREQ: - synchronized (mCpuTimePerFreqLock) { - return pullCpuTimePerFreqLocked(atomTag, data); - } case FrameworkStatsLog.CPU_TIME_PER_UID: synchronized (mCpuTimePerUidLock) { return pullCpuTimePerUidLocked(atomTag, data); @@ -722,18 +714,6 @@ public class StatsPullAtomService extends SystemService { mKernelWakelockReader = new KernelWakelockReader(); mTmpWakelockStats = new KernelWakelockStats(); - // Initialize state for CPU_TIME_PER_FREQ atom - PowerProfile powerProfile = new PowerProfile(mContext); - final int numClusters = powerProfile.getNumCpuClusters(); - mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters]; - int firstCpuOfCluster = 0; - for (int i = 0; i < numClusters; i++) { - final int numSpeedSteps = powerProfile.getNumSpeedStepsInCpuCluster(i); - mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster, - numSpeedSteps); - firstCpuOfCluster += powerProfile.getNumCoresInCpuCluster(i); - } - // Used for CPU_TIME_PER_THREAD_FREQ mKernelCpuThreadReader = KernelCpuThreadReaderSettingsObserver.getSettingsModifiedReader(mContext); @@ -793,7 +773,6 @@ public class StatsPullAtomService extends SystemService { mStatsCallbackImpl = new StatsPullAtomCallbackImpl(); registerBluetoothBytesTransfer(); registerKernelWakelock(); - registerCpuTimePerFreq(); registerCpuTimePerUid(); registerCpuCyclesPerUidCluster(); registerCpuTimePerUidFreq(); @@ -1465,32 +1444,6 @@ public class StatsPullAtomService extends SystemService { return StatsManager.PULL_SUCCESS; } - private void registerCpuTimePerFreq() { - int tagId = FrameworkStatsLog.CPU_TIME_PER_FREQ; - PullAtomMetadata metadata = new PullAtomMetadata.Builder() - .setAdditiveFields(new int[] {3}) - .build(); - mStatsManager.setPullAtomCallback( - tagId, - metadata, - DIRECT_EXECUTOR, - mStatsCallbackImpl - ); - } - - int pullCpuTimePerFreqLocked(int atomTag, List<StatsEvent> pulledData) { - for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) { - long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute(); - if (clusterTimeMs != null) { - for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) { - pulledData.add(FrameworkStatsLog.buildStatsEvent( - atomTag, cluster, speed, clusterTimeMs[speed])); - } - } - } - return StatsManager.PULL_SUCCESS; - } - private void registerCpuTimePerUid() { int tagId = FrameworkStatsLog.CPU_TIME_PER_UID; PullAtomMetadata metadata = new PullAtomMetadata.Builder() @@ -2078,7 +2031,8 @@ public class StatsPullAtomService extends SystemService { metrics.pageTablesKb, metrics.kernelStackKb, metrics.totalIonKb, - metrics.unaccountedKb)); + metrics.unaccountedKb, + metrics.gpuTotalUsageKb)); return StatsManager.PULL_SUCCESS; } diff --git a/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java b/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java index 99fc7c11c5b3..1e80c4fe89fb 100644 --- a/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java +++ b/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java @@ -27,6 +27,7 @@ final class SystemMemoryUtil { static Metrics getMetrics() { int totalIonKb = (int) Debug.getIonHeapsSizeKb(); + int gpuTotalUsageKb = (int) Debug.getGpuTotalUsageKb(); long[] mInfos = new long[Debug.MEMINFO_COUNT]; Debug.getMemInfo(mInfos); @@ -62,6 +63,7 @@ final class SystemMemoryUtil { result.pageTablesKb = (int) mInfos[Debug.MEMINFO_PAGE_TABLES]; result.kernelStackKb = (int) mInfos[Debug.MEMINFO_KERNEL_STACK]; result.totalIonKb = totalIonKb; + result.gpuTotalUsageKb = gpuTotalUsageKb; result.unaccountedKb = (int) (mInfos[Debug.MEMINFO_TOTAL] - accountedKb); return result; } @@ -72,6 +74,7 @@ final class SystemMemoryUtil { public int pageTablesKb; public int kernelStackKb; public int totalIonKb; + public int gpuTotalUsageKb; public int unaccountedKb; } } diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java index 865571e90338..d0c632350270 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java @@ -18,6 +18,8 @@ package com.android.server.timezonedetector; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.ActivityManager; import android.app.time.ITimeZoneDetectorListener; import android.app.time.TimeZoneCapabilitiesAndConfig; import android.app.time.TimeZoneConfiguration; @@ -26,12 +28,14 @@ import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion; import android.content.Context; import android.location.LocationManager; +import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.SystemProperties; +import android.os.UserHandle; import android.util.ArrayMap; import android.util.IndentingPrintWriter; import android.util.Slog; @@ -163,9 +167,13 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub @Override @NonNull public TimeZoneCapabilitiesAndConfig getCapabilitiesAndConfig() { + int userId = mCallerIdentityInjector.getCallingUserId(); + return getCapabilitiesAndConfig(userId); + } + + TimeZoneCapabilitiesAndConfig getCapabilitiesAndConfig(@UserIdInt int userId) { enforceManageTimeZoneDetectorPermission(); - int userId = mCallerIdentityInjector.getCallingUserId(); final long token = mCallerIdentityInjector.clearCallingIdentity(); try { ConfigurationInternal configurationInternal = @@ -178,13 +186,22 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub @Override public boolean updateConfiguration(@NonNull TimeZoneConfiguration configuration) { + int callingUserId = mCallerIdentityInjector.getCallingUserId(); + return updateConfiguration(callingUserId, configuration); + } + + boolean updateConfiguration( + @UserIdInt int userId, @NonNull TimeZoneConfiguration configuration) { + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + userId, false, false, "updateConfiguration", null); + enforceManageTimeZoneDetectorPermission(); + Objects.requireNonNull(configuration); - int callingUserId = mCallerIdentityInjector.getCallingUserId(); final long token = mCallerIdentityInjector.clearCallingIdentity(); try { - return mTimeZoneDetectorStrategy.updateConfiguration(callingUserId, configuration); + return mTimeZoneDetectorStrategy.updateConfiguration(userId, configuration); } finally { mCallerIdentityInjector.restoreCallingIdentity(token); } @@ -318,11 +335,17 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub return isGeoLocationTimeZoneDetectionEnabled(mContext); } - boolean isLocationEnabled() { + boolean isLocationEnabled(@UserIdInt int userId) { enforceManageTimeZoneDetectorPermission(); - return mContext.getSystemService(LocationManager.class) - .isLocationEnabledForUser(mContext.getUser()); + final long token = mCallerIdentityInjector.clearCallingIdentity(); + try { + UserHandle user = UserHandle.of(userId); + LocationManager locationManager = mContext.getSystemService(LocationManager.class); + return locationManager.isLocationEnabledForUser(user); + } finally { + mCallerIdentityInjector.restoreCallingIdentity(token); + } } @Override diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java index b2630300a6aa..e965f55e49d7 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java @@ -29,6 +29,7 @@ import android.app.time.TimeZoneConfiguration; import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion; import android.os.ShellCommand; +import android.os.UserHandle; import java.io.PrintWriter; import java.util.function.Consumer; @@ -76,7 +77,8 @@ class TimeZoneDetectorShellCommand extends ShellCommand { private int runIsAutoDetectionEnabled() { final PrintWriter pw = getOutPrintWriter(); - boolean enabled = mInterface.getCapabilitiesAndConfig() + int userId = UserHandle.USER_CURRENT; + boolean enabled = mInterface.getCapabilitiesAndConfig(userId) .getConfiguration() .isAutoDetectionEnabled(); pw.println(enabled); @@ -92,14 +94,16 @@ class TimeZoneDetectorShellCommand extends ShellCommand { private int runIsLocationEnabled() { final PrintWriter pw = getOutPrintWriter(); - boolean enabled = mInterface.isLocationEnabled(); + int userId = UserHandle.USER_CURRENT; + boolean enabled = mInterface.isLocationEnabled(userId); pw.println(enabled); return 0; } private int runIsGeoDetectionEnabled() { final PrintWriter pw = getOutPrintWriter(); - boolean enabled = mInterface.getCapabilitiesAndConfig() + int userId = UserHandle.USER_CURRENT; + boolean enabled = mInterface.getCapabilitiesAndConfig(userId) .getConfiguration() .isGeoDetectionEnabled(); pw.println(enabled); @@ -108,18 +112,20 @@ class TimeZoneDetectorShellCommand extends ShellCommand { private int runSetAutoDetectionEnabled() { boolean enabled = Boolean.parseBoolean(getNextArgRequired()); + int userId = UserHandle.USER_CURRENT; TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder() .setAutoDetectionEnabled(enabled) .build(); - return mInterface.updateConfiguration(configuration) ? 0 : 1; + return mInterface.updateConfiguration(userId, configuration) ? 0 : 1; } private int runSetGeoDetectionEnabled() { boolean enabled = Boolean.parseBoolean(getNextArgRequired()); + int userId = UserHandle.USER_CURRENT; TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder() .setGeoDetectionEnabled(enabled) .build(); - return mInterface.updateConfiguration(configuration) ? 0 : 1; + return mInterface.updateConfiguration(userId, configuration) ? 0 : 1; } private int runSuggestGeolocationTimeZone() { diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index b0847879f456..03cf021d0e9b 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -174,6 +174,10 @@ class ActivityMetricsLogger { boolean allDrawn() { return mAssociatedTransitionInfo != null && mAssociatedTransitionInfo.allDrawn(); } + + boolean contains(ActivityRecord r) { + return mAssociatedTransitionInfo != null && mAssociatedTransitionInfo.contains(r); + } } /** The information created when an activity is confirmed to be launched. */ @@ -793,6 +797,7 @@ class ActivityMetricsLogger { stopLaunchTrace(info); if (abort) { + mSupervisor.stopWaitingForActivityVisible(info.mLastLaunchedActivity); launchObserverNotifyActivityLaunchCancelled(info); } else { if (info.isInterestingToLoggerAndObserver()) { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 324e3acf8e73..a9c54741fcf6 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -5435,7 +5435,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final TransitionInfoSnapshot info = mTaskSupervisor .getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle); if (info != null) { - mTaskSupervisor.reportActivityLaunchedLocked(false /* timeout */, this, + mTaskSupervisor.reportActivityLaunched(false /* timeout */, this, info.windowsFullyDrawnDelayMs, info.getLaunchState()); } } @@ -5476,9 +5476,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // 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, + mTaskSupervisor.reportActivityLaunched(false /* timeout */, this, windowsDrawnDelayMs, launchState); - mTaskSupervisor.stopWaitingForActivityVisible(this, windowsDrawnDelayMs, launchState); } finishLaunchTickingLocked(); if (task != null) { diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 375c3e138a39..c6cc83b4c3bb 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -710,8 +710,12 @@ class ActivityStarter { // WaitResult. mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, res, mLastStartActivityRecord, originalOptions); - return getExternalResult(mRequest.waitResult == null ? res - : waitForResult(res, mLastStartActivityRecord)); + if (mRequest.waitResult != null) { + mRequest.waitResult.result = res; + res = waitResultIfNeeded(mRequest.waitResult, mLastStartActivityRecord, + launchingState); + } + return getExternalResult(res); } } finally { onExecutionComplete(); @@ -796,48 +800,21 @@ class ActivityStarter { /** * Wait for activity launch completes. */ - private int waitForResult(int res, ActivityRecord r) { - mRequest.waitResult.result = res; - switch(res) { - case START_SUCCESS: { - mSupervisor.mWaitingActivityLaunched.add(mRequest.waitResult); - do { - try { - mService.mGlobalLock.wait(); - } catch (InterruptedException e) { - } - } while (mRequest.waitResult.result != START_TASK_TO_FRONT - && !mRequest.waitResult.timeout && mRequest.waitResult.who == null); - if (mRequest.waitResult.result == START_TASK_TO_FRONT) { - res = START_TASK_TO_FRONT; - } - break; - } - case START_DELIVERED_TO_TOP: { - mRequest.waitResult.timeout = false; - mRequest.waitResult.who = r.mActivityComponent; - mRequest.waitResult.totalTime = 0; - break; - } - case START_TASK_TO_FRONT: { - // ActivityRecord may represent a different activity, but it should not be - // in the resumed state. - if (r.nowVisible && r.isState(RESUMED)) { - mRequest.waitResult.timeout = false; - mRequest.waitResult.who = r.mActivityComponent; - mRequest.waitResult.totalTime = 0; - } else { - mSupervisor.waitActivityVisible(r.mActivityComponent, mRequest.waitResult); - // Note: the timeout variable is not currently not ever set. - do { - try { - mService.mGlobalLock.wait(); - } catch (InterruptedException e) { - } - } while (!mRequest.waitResult.timeout && mRequest.waitResult.who == null); - } - break; - } + private int waitResultIfNeeded(WaitResult waitResult, ActivityRecord r, + LaunchingState launchingState) { + final int res = waitResult.result; + if (res == START_DELIVERED_TO_TOP + || (res == START_TASK_TO_FRONT && r.nowVisible && r.isState(RESUMED))) { + // The activity should already be visible, so nothing to wait. + waitResult.timeout = false; + waitResult.who = r.mActivityComponent; + waitResult.totalTime = 0; + return res; + } + mSupervisor.waitActivityVisibleOrLaunched(waitResult, r, launchingState); + if (res == START_SUCCESS && waitResult.result == START_TASK_TO_FRONT) { + // A trampoline activity is launched and it brings another existing activity to front. + return START_TASK_TO_FRONT; } return res; } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index 08e16c4719a3..9ffedde8e616 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -285,14 +285,6 @@ public abstract class ActivityTaskManagerInternal { public abstract void enforceCallerIsRecentsOrHasPermission(String permission, String func); /** - * Returns true if the app can close system dialogs. Otherwise it either throws a {@link - * SecurityException} or returns false with a logcat message depending on whether the app - * targets SDK level {@link android.os.Build.VERSION_CODES#S} or not. - */ - public abstract boolean checkCanCloseSystemDialogs(int pid, int uid, - @Nullable String packageName); - - /** * Called after the voice interaction service has changed. */ public abstract void notifyActiveVoiceInteractionServiceChanged(ComponentName component); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 9b9af0661501..461bbfb978e4 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -147,7 +147,6 @@ import android.app.WindowConfiguration; import android.app.admin.DevicePolicyCache; import android.app.assist.AssistContent; import android.app.assist.AssistStructure; -import android.app.compat.CompatChanges; import android.app.usage.UsageStatsManagerInternal; import android.content.ActivityNotFoundException; import android.content.ComponentName; @@ -2901,86 +2900,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - /** - * Returns true if the app can close system dialogs. Otherwise it either throws a {@link - * SecurityException} or returns false with a logcat message depending on whether the app - * targets SDK level {@link android.os.Build.VERSION_CODES#S} or not. - */ - private boolean checkCanCloseSystemDialogs(int pid, int uid, @Nullable String packageName) { - final WindowProcessController process; - synchronized (mGlobalLock) { - process = mProcessMap.getProcess(pid); - } - if (packageName == null && process != null) { - // WindowProcessController.mInfo is final, so after the synchronized memory barrier - // above, process.mInfo can't change. As for reading mInfo.packageName, - // WindowProcessController doesn't own the ApplicationInfo object referenced by mInfo. - // ProcessRecord for example also holds a reference to that object, so protecting access - // to packageName with the WM lock would not be enough as we'd also need to synchronize - // on the AM lock if we are worried about races, but we can't synchronize on AM lock - // here. Hence, since this is only used for logging, we don't synchronize here. - packageName = process.mInfo.packageName; - } - String caller = "(pid=" + pid + ", uid=" + uid + ")"; - if (packageName != null) { - caller = packageName + " " + caller; - } - if (!canCloseSystemDialogs(pid, uid, process)) { - // The app can't close system dialogs, throw only if it targets S+ - if (CompatChanges.isChangeEnabled( - ActivityManager.LOCK_DOWN_CLOSE_SYSTEM_DIALOGS, uid)) { - throw new SecurityException( - "Permission Denial: " + Intent.ACTION_CLOSE_SYSTEM_DIALOGS - + " broadcast from " + caller + " requires " - + Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS + "."); - } else if (CompatChanges.isChangeEnabled( - ActivityManager.DROP_CLOSE_SYSTEM_DIALOGS, uid)) { - Slog.e(TAG, - "Permission Denial: " + Intent.ACTION_CLOSE_SYSTEM_DIALOGS - + " broadcast from " + caller + " requires " - + Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS - + ", dropping broadcast."); - return false; - } else { - Slog.w(TAG, Intent.ACTION_CLOSE_SYSTEM_DIALOGS - + " broadcast from " + caller + " will require " - + Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS - + " in future builds."); - return true; - } - } - return true; - } - - private boolean canCloseSystemDialogs(int pid, int uid, - @Nullable WindowProcessController process) { - if (checkPermission(Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, pid, uid) - == PERMISSION_GRANTED) { - return true; - } - if (process != null) { - // Check if the instrumentation of the process has the permission. This covers the - // usual test started from the shell (which has the permission) case. This is needed - // for apps targeting SDK level < S but we are also allowing for targetSdk S+ as a - // convenience to avoid breaking a bunch of existing tests and asking them to adopt - // shell permissions to do this. - // Note that these getters all read from volatile fields in WindowProcessController, so - // no need to lock. - int sourceUid = process.getInstrumentationSourceUid(); - if (process.isInstrumenting() && sourceUid != -1 && checkPermission( - Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, -1, sourceUid) - == PERMISSION_GRANTED) { - return true; - } - // This is the notification trampoline use-case for example, where apps use Intent.ACSD - // to close the shade prior to starting an activity. - if (process.canCloseSystemDialogsByToken()) { - return true; - } - } - return false; - } - static void enforceTaskPermission(String func) { if (checkCallingPermission(MANAGE_ACTIVITY_TASKS) == PackageManager.PERMISSION_GRANTED) { return; @@ -5231,12 +5150,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public boolean checkCanCloseSystemDialogs(int pid, int uid, @Nullable String packageName) { - return ActivityTaskManagerService.this.checkCanCloseSystemDialogs(pid, uid, - packageName); - } - - @Override public void notifyActiveVoiceInteractionServiceChanged(ComponentName component) { synchronized (mGlobalLock) { mActiveVoiceInteractionServiceComponent = component; @@ -5736,12 +5649,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void closeSystemDialogs(String reason) { enforceNotIsolatedCaller("closeSystemDialogs"); + final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); - if (!checkCanCloseSystemDialogs(pid, uid, null)) { - return; - } - final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 73a6efdb6799..599bf3748dd9 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -266,11 +266,8 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { */ private final SparseIntArray mCurTaskIdForUser = new SparseIntArray(20); - /** List of processes waiting to find out when a specific activity becomes visible. */ - private final ArrayList<WaitInfo> mWaitingForActivityVisible = new ArrayList<>(); - - /** List of processes waiting to find out about the next launched activity. */ - final ArrayList<WaitResult> mWaitingActivityLaunched = new ArrayList<>(); + /** List of requests waiting for the target activity to be launched or visible. */ + private final ArrayList<WaitInfo> mWaitingActivityLaunched = new ArrayList<>(); /** List of activities that are ready to be stopped, but waiting for the next activity to * settle down before doing so. */ @@ -552,9 +549,21 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { return candidateTaskId; } - void waitActivityVisible(ComponentName name, WaitResult result) { - final WaitInfo waitInfo = new WaitInfo(name, result); - mWaitingForActivityVisible.add(waitInfo); + void waitActivityVisibleOrLaunched(WaitResult w, ActivityRecord r, + LaunchingState launchingState) { + if (w.result != ActivityManager.START_TASK_TO_FRONT + && w.result != ActivityManager.START_SUCCESS) { + // Not a result code that can make activity visible or launched. + return; + } + final WaitInfo waitInfo = new WaitInfo(w, r.mActivityComponent, launchingState); + mWaitingActivityLaunched.add(waitInfo); + do { + try { + mService.mGlobalLock.wait(); + } catch (InterruptedException ignored) { + } + } while (mWaitingActivityLaunched.contains(waitInfo)); } void cleanupActivity(ActivityRecord r) { @@ -568,23 +577,25 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { /** There is no valid launch time, just stop waiting. */ void stopWaitingForActivityVisible(ActivityRecord r) { - stopWaitingForActivityVisible(r, WaitResult.INVALID_DELAY, WaitResult.LAUNCH_STATE_UNKNOWN); + reportActivityLaunched(false /* timeout */, r, WaitResult.INVALID_DELAY, + WaitResult.LAUNCH_STATE_UNKNOWN); } - void stopWaitingForActivityVisible(ActivityRecord r, long totalTime, + void reportActivityLaunched(boolean timeout, 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); - if (w.matches(r.mActivityComponent)) { - final WaitResult result = w.getResult(); - changed = true; - result.timeout = false; - result.who = w.getComponent(); - result.totalTime = totalTime; - result.launchState = launchState; - mWaitingForActivityVisible.remove(w); + for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) { + final WaitInfo info = mWaitingActivityLaunched.get(i); + if (!info.matches(r)) { + continue; } + final WaitResult w = info.mResult; + w.timeout = timeout; + w.who = r.mActivityComponent; + w.totalTime = totalTime; + w.launchState = launchState; + mWaitingActivityLaunched.remove(i); + changed = true; } if (changed) { mService.mGlobalLock.notifyAll(); @@ -603,38 +614,18 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { boolean changed = false; for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) { - WaitResult w = mWaitingActivityLaunched.remove(i); - if (w.who == null) { - changed = true; - w.result = result; - + final WaitInfo info = mWaitingActivityLaunched.get(i); + if (!info.matches(r)) { + continue; + } + final WaitResult w = info.mResult; + w.result = result; + if (result == START_DELIVERED_TO_TOP) { // Unlike START_TASK_TO_FRONT, When an intent is delivered to top, there // will be no followup launch signals. Assign the result and launched component. - if (result == START_DELIVERED_TO_TOP) { - w.who = r.mActivityComponent; - } - } - } - - if (changed) { - mService.mGlobalLock.notifyAll(); - } - } - - void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r, long totalTime, - @WaitResult.LaunchState int launchState) { - boolean changed = false; - for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) { - WaitResult w = mWaitingActivityLaunched.remove(i); - if (w.who == null) { + w.who = r.mActivityComponent; + mWaitingActivityLaunched.remove(i); changed = true; - w.timeout = timeout; - if (r != null) { - w.who = new ComponentName(r.info.packageName, r.info.name); - } - w.totalTime = totalTime; - w.launchState = launchState; - // Do not modify w.result. } } if (changed) { @@ -1295,8 +1286,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { mHandler.removeMessages(IDLE_TIMEOUT_MSG, r); r.finishLaunchTickingLocked(); if (fromTimeout) { - reportActivityLaunchedLocked(fromTimeout, r, INVALID_DELAY, - -1 /* launchState */); + reportActivityLaunched(fromTimeout, r, INVALID_DELAY, -1 /* launchState */); } // This is a hack to semi-deal with a race condition @@ -1940,14 +1930,14 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser); pw.println(prefix + "mUserRootTaskInFront=" + mRootWindowContainer.mUserRootTaskInFront); pw.println(prefix + "mVisibilityTransactionDepth=" + mVisibilityTransactionDepth); - if (!mWaitingForActivityVisible.isEmpty()) { - pw.println(prefix + "mWaitingForActivityVisible="); - for (int i = 0; i < mWaitingForActivityVisible.size(); ++i) { - pw.print(prefix + prefix); mWaitingForActivityVisible.get(i).dump(pw, prefix); - } - } pw.print(prefix); pw.print("isHomeRecentsComponent="); pw.println(mRecentTasks.isRecentsComponentHomeActivity(mRootWindowContainer.mCurrentUser)); + if (!mWaitingActivityLaunched.isEmpty()) { + pw.println(prefix + "mWaitingActivityLaunched="); + for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) { + mWaitingActivityLaunched.get(i).dump(pw, prefix + " "); + } + } pw.println(); } @@ -2604,32 +2594,30 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { /** * Internal container to store a match qualifier alongside a WaitResult. */ - static class WaitInfo { - private final ComponentName mTargetComponent; - private final WaitResult mResult; - - WaitInfo(ComponentName targetComponent, WaitResult result) { - this.mTargetComponent = targetComponent; - this.mResult = result; - } - - public boolean matches(ComponentName targetComponent) { - return mTargetComponent == null || mTargetComponent.equals(targetComponent); - } + private static class WaitInfo { + final WaitResult mResult; + final ComponentName mTargetComponent; + /** + * The target component may not be the final drawn activity. The launching state is managed + * by {@link ActivityMetricsLogger} that can track consecutive launching sequence. + */ + final LaunchingState mLaunchingState; - public WaitResult getResult() { - return mResult; + WaitInfo(WaitResult result, ComponentName component, LaunchingState launchingState) { + mResult = result; + mTargetComponent = component; + mLaunchingState = launchingState; } - public ComponentName getComponent() { - return mTargetComponent; + boolean matches(ActivityRecord r) { + return mTargetComponent.equals(r.mActivityComponent) || mLaunchingState.contains(r); } - public void dump(PrintWriter pw, String prefix) { + void dump(PrintWriter pw, String prefix) { pw.println(prefix + "WaitInfo:"); pw.println(prefix + " mTargetComponent=" + mTargetComponent); pw.println(prefix + " mResult="); - mResult.dump(pw, prefix); + mResult.dump(pw, prefix + " "); } } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 1c41978e2117..a88e4537d1a2 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -5104,7 +5104,9 @@ class Task extends WindowContainer<WindowContainer> { } void onPictureInPictureParamsChanged() { - dispatchTaskInfoChangedIfNeeded(true /* force */); + if (inPinnedWindowingMode()) { + dispatchTaskInfoChangedIfNeeded(true /* force */); + } } /** @@ -5436,14 +5438,6 @@ class Task extends WindowContainer<WindowContainer> { r.completeResumeLocked(); } - private void clearLaunchTime(ActivityRecord r) { - // Make sure that there is no activity waiting for this to launch. - if (!mTaskSupervisor.mWaitingActivityLaunched.isEmpty()) { - mTaskSupervisor.removeIdleTimeoutForActivity(r); - mTaskSupervisor.scheduleIdleTimeout(r); - } - } - void awakeFromSleepingLocked() { if (!isLeafTask()) { forAllLeafTasks((task) -> task.awakeFromSleepingLocked(), @@ -5586,7 +5580,6 @@ class Task extends WindowContainer<WindowContainer> { mLastNoHistoryActivity = prev.isNoHistory() ? prev : null; prev.setState(PAUSING, "startPausingLocked"); prev.getTask().touchActiveTime(); - clearLaunchTime(prev); mAtmService.updateCpuStats(); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 0b60debecee5..4eeae6c0710c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2332,9 +2332,15 @@ public class WindowManagerService extends IWindowManager.Stub } } - // Create surfaceControl before surface placement otherwise layout will be skipped - // (because WS.isGoneForLayout() is true when there is no surface. + // We may be deferring layout passes at the moment, but since the client is interested + // in the new out values right now we need to force a layout. + mWindowPlacerLocked.performSurfacePlacement(true /* force */); + if (shouldRelayout) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1"); + + result = win.relayoutVisibleWindow(result, attrChanges); + try { result = createSurfaceControl(outSurfaceControl, result, win, winAnimator); } catch (Exception e) { @@ -2346,17 +2352,6 @@ public class WindowManagerService extends IWindowManager.Stub Binder.restoreCallingIdentity(origId); return 0; } - } - - // We may be deferring layout passes at the moment, but since the client is interested - // in the new out values right now we need to force a layout. - mWindowPlacerLocked.performSurfacePlacement(true /* force */); - - if (shouldRelayout) { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1"); - - result = win.relayoutVisibleWindow(result, attrChanges); - if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { focusMayChange = true; } @@ -3243,11 +3238,6 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void closeSystemDialogs(String reason) { - int callingPid = Binder.getCallingPid(); - int callingUid = Binder.getCallingUid(); - if (!mAtmInternal.checkCanCloseSystemDialogs(callingPid, callingUid, null)) { - return; - } synchronized (mGlobalLock) { mRoot.closeSystemDialogs(reason); } diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 663d91ecc82a..8aa154bb9dd1 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -23,7 +23,6 @@ import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static android.view.Display.INVALID_DISPLAY; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; -import static com.android.internal.util.Preconditions.checkArgument; import static com.android.server.am.ActivityManagerService.MY_PID; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE; @@ -162,8 +161,6 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio private volatile boolean mDebugging; // Active instrumentation running in process? private volatile boolean mInstrumenting; - // If there is active instrumentation, this is the source - private volatile int mInstrumentationSourceUid = -1; // Active instrumentation with background activity starts privilege running in process? private volatile boolean mInstrumentingWithBackgroundActivityStartPrivileges; // This process it perceptible by the user. @@ -626,16 +623,9 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio mBoundClientUids = boundClientUids; } - /** - * Set instrumentation-related info. - * - * If {@code instrumenting} is {@code false}, {@code sourceUid} has to be -1. - */ - public void setInstrumenting(boolean instrumenting, int sourceUid, + public void setInstrumenting(boolean instrumenting, boolean hasBackgroundActivityStartPrivileges) { - checkArgument(instrumenting || sourceUid == -1); mInstrumenting = instrumenting; - mInstrumentationSourceUid = sourceUid; mInstrumentingWithBackgroundActivityStartPrivileges = hasBackgroundActivityStartPrivileges; } @@ -643,11 +633,6 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return mInstrumenting; } - /** Returns the uid of the active instrumentation source if there is one, otherwise -1. */ - int getInstrumentationSourceUid() { - return mInstrumentationSourceUid; - } - public void setPerceptible(boolean perceptible) { mPerceptible = perceptible; } diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 996462f22e8f..13078b68c92b 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -171,7 +171,6 @@ cc_defaults { static_libs: [ "android.hardware.broadcastradio@common-utils-1x-lib", - "libservice-connectivity-static", ], product_variables: { diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index 85ef39470cb3..189332107a5d 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -41,8 +41,6 @@ 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_location_GnssLocationProvider(JNIEnv* env); -int register_android_server_connectivity_Vpn(JNIEnv* env); -int register_android_server_TestNetworkService(JNIEnv* env); int register_android_server_devicepolicy_CryptoTestHelper(JNIEnv*); int register_android_server_tv_TvUinputBridge(JNIEnv* env); int register_android_server_tv_TvInputHal(JNIEnv* env); @@ -96,8 +94,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_VibratorManagerService(env); register_android_server_SystemServer(env); register_android_server_location_GnssLocationProvider(env); - register_android_server_connectivity_Vpn(env); - register_android_server_TestNetworkService(env); register_android_server_devicepolicy_CryptoTestHelper(env); register_android_server_ConsumerIrService(env); register_android_server_BatteryStatsService(env); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java index a281180d77d1..48f8b1505d3a 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java @@ -134,6 +134,8 @@ class ActiveAdmin { private static final String TAG_ALWAYS_ON_VPN_LOCKDOWN = "vpn-lockdown"; private static final String TAG_COMMON_CRITERIA_MODE = "common-criteria-mode"; private static final String TAG_PASSWORD_COMPLEXITY = "password-complexity"; + private static final String TAG_ORGANIZATION_ID = "organization-id"; + private static final String TAG_ENROLLMENT_SPECIFIC_ID = "enrollment-specific-id"; private static final String ATTR_VALUE = "value"; private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification"; private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications"; @@ -273,6 +275,8 @@ class ActiveAdmin { public String mAlwaysOnVpnPackage; public boolean mAlwaysOnVpnLockdown; boolean mCommonCriteriaMode; + public String mOrganizationId; + public String mEnrollmentSpecificId; ActiveAdmin(DeviceAdminInfo info, boolean isParent) { this.info = info; @@ -533,6 +537,12 @@ class ActiveAdmin { if (mPasswordComplexity != PASSWORD_COMPLEXITY_NONE) { writeAttributeValueToXml(out, TAG_PASSWORD_COMPLEXITY, mPasswordComplexity); } + if (!TextUtils.isEmpty(mOrganizationId)) { + writeTextToXml(out, TAG_ORGANIZATION_ID, mOrganizationId); + } + if (!TextUtils.isEmpty(mEnrollmentSpecificId)) { + writeTextToXml(out, TAG_ENROLLMENT_SPECIFIC_ID, mEnrollmentSpecificId); + } } void writeTextToXml(TypedXmlSerializer out, String tag, String text) throws IOException { @@ -766,6 +776,22 @@ class ActiveAdmin { mCommonCriteriaMode = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else if (TAG_PASSWORD_COMPLEXITY.equals(tag)) { mPasswordComplexity = parser.getAttributeInt(null, ATTR_VALUE); + } else if (TAG_ORGANIZATION_ID.equals(tag)) { + type = parser.next(); + if (type == TypedXmlPullParser.TEXT) { + mOrganizationId = parser.getText(); + } else { + Log.w(DevicePolicyManagerService.LOG_TAG, + "Missing Organization ID."); + } + } else if (TAG_ENROLLMENT_SPECIFIC_ID.equals(tag)) { + type = parser.next(); + if (type == TypedXmlPullParser.TEXT) { + mEnrollmentSpecificId = parser.getText(); + } else { + Log.w(DevicePolicyManagerService.LOG_TAG, + "Missing Enrollment-specific ID."); + } } else { Slog.w(DevicePolicyManagerService.LOG_TAG, "Unknown admin tag: " + tag); XmlUtils.skipCurrentTag(parser); @@ -1107,5 +1133,15 @@ class ActiveAdmin { pw.print("mPasswordComplexity="); pw.println(mPasswordComplexity); + + if (!TextUtils.isEmpty(mOrganizationId)) { + pw.print("mOrganizationId="); + pw.println(mOrganizationId); + } + + if (!TextUtils.isEmpty(mEnrollmentSpecificId)) { + pw.print("mEnrollmentSpecificId="); + pw.println(mEnrollmentSpecificId); + } } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 6f1d451e7224..22e9725f49ab 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -15,6 +15,7 @@ */ package com.android.server.devicepolicy; +import android.annotation.NonNull; import android.app.admin.DevicePolicySafetyChecker; import android.app.admin.IDevicePolicyManager; import android.content.ComponentName; @@ -101,4 +102,11 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { public boolean canProfileOwnerResetPasswordWhenLocked(int userId) { return false; } + + public String getEnrollmentSpecificId() { + return ""; + } + + public void setOrganizationIdForUser( + @NonNull String callerPackage, @NonNull String enterpriseId, int userId) {} } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index fdbd85a77a5b..4654fca250a3 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -819,6 +819,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle); + removeCredentialManagementApp(intent.getData().getSchemeSpecificPart()); } else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)) { clearWipeProfileNotification(); } else if (Intent.ACTION_DATE_CHANGED.equals(action) @@ -949,6 +950,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + private void removeCredentialManagementApp(String packageName) { + mBackgroundHandler.post(() -> { + try (KeyChainConnection connection = mInjector.keyChainBind()) { + IKeyChainService service = connection.getService(); + if (service.hasCredentialManagementApp() + && packageName.equals(service.getCredentialManagementAppPackageName())) { + service.removeCredentialManagementApp(); + } + } catch (RemoteException | InterruptedException | IllegalStateException e) { + Log.e(LOG_TAG, "Unable to remove the credential management app"); + } + }); + } + private boolean isRemovedPackage(String changedPackage, String targetPackage, int userHandle) { try { return targetPackage != null @@ -1419,6 +1434,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return SecurityLog.isLoggingEnabled(); } + KeyChainConnection keyChainBind() throws InterruptedException { + return KeyChain.bind(mContext); + } + KeyChainConnection keyChainBindAsUser(UserHandle user) throws InterruptedException { return KeyChain.bindAsUser(mContext, user); } @@ -8421,7 +8440,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity())); + return getProfileOwnerNameUnchecked(userHandle); + } + private String getProfileOwnerNameUnchecked(int userHandle) { ComponentName profileOwner = getProfileOwnerAsUser(userHandle); if (profileOwner == null) { return null; @@ -9731,7 +9753,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Set admin. setActiveAdmin(profileOwner, /* refreshing= */ true, userId); - final String ownerName = getProfileOwnerName(Process.myUserHandle().getIdentifier()); + final String ownerName = getProfileOwnerNameUnchecked( + Process.myUserHandle().getIdentifier()); setProfileOwner(profileOwner, ownerName, userId); synchronized (getLockObject()) { @@ -15577,4 +15600,69 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return true; } } + + @Override + public String getEnrollmentSpecificId() { + if (!mHasFeature) { + return ""; + } + + final CallerIdentity caller = getCallerIdentity(); + Preconditions.checkCallAuthorization( + isDeviceOwner(caller) || isProfileOwner(caller)); + + synchronized (getLockObject()) { + final ActiveAdmin requiredAdmin = getDeviceOrProfileOwnerAdminLocked( + caller.getUserId()); + final String esid = requiredAdmin.mEnrollmentSpecificId; + return esid != null ? esid : ""; + } + } + + @Override + public void setOrganizationIdForUser( + @NonNull String callerPackage, @NonNull String organizationId, int userId) { + if (!mHasFeature) { + return; + } + Objects.requireNonNull(callerPackage); + + final CallerIdentity caller = getCallerIdentity(callerPackage); + // Only the DPC can set this ID. + Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller), + "Only a Device Owner or Profile Owner may set the Enterprise ID."); + // Empty enterprise ID must not be provided in calls to this method. + Preconditions.checkArgument(!TextUtils.isEmpty(organizationId), + "Enterprise ID may not be empty."); + + Log.i(LOG_TAG, + String.format("Setting Enterprise ID to %s for user %d", organizationId, userId)); + + synchronized (getLockObject()) { + ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId); + // As the caller is the system, it must specify the component name of the profile owner + // as a safety check. + Preconditions.checkCallAuthorization( + owner != null && owner.getUserHandle().getIdentifier() == userId, + String.format("The Profile Owner or Device Owner may only set the Enterprise ID" + + " on its own user, called on user %d but owner user is %d", userId, + owner.getUserHandle().getIdentifier())); + Preconditions.checkState( + TextUtils.isEmpty(owner.mOrganizationId) || owner.mOrganizationId.equals( + organizationId), + "The organization ID has been previously set to a different value and cannot " + + "be changed"); + final String dpcPackage = owner.info.getPackageName(); + mInjector.binderWithCleanCallingIdentity(() -> { + EnterpriseSpecificIdCalculator esidCalculator = + new EnterpriseSpecificIdCalculator(mContext); + + final String esid = esidCalculator.calculateEnterpriseId(dpcPackage, + organizationId); + owner.mOrganizationId = organizationId; + owner.mEnrollmentSpecificId = esid; + saveSettingsLocked(userId); + }); + } + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java b/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java new file mode 100644 index 000000000000..df7f3084aeb3 --- /dev/null +++ b/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java @@ -0,0 +1,145 @@ +/* + * 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.devicepolicy; + +import android.content.Context; +import android.content.pm.VerifierDeviceIdentity; +import android.net.wifi.WifiManager; +import android.os.Build; +import android.security.identity.Util; +import android.telephony.TelephonyManager; +import android.text.TextUtils; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; + +import java.nio.ByteBuffer; + +class EnterpriseSpecificIdCalculator { + private static final int PADDED_HW_ID_LENGTH = 16; + private static final int PADDED_PROFILE_OWNER_LENGTH = 64; + private static final int PADDED_ENTERPRISE_ID_LENGTH = 64; + private static final int ESID_LENGTH = 16; + + private final String mImei; + private final String mMeid; + private final String mSerialNumber; + private final String mMacAddress; + + @VisibleForTesting + EnterpriseSpecificIdCalculator(String imei, String meid, String serialNumber, + String macAddress) { + mImei = imei; + mMeid = meid; + mSerialNumber = serialNumber; + mMacAddress = macAddress; + } + + EnterpriseSpecificIdCalculator(Context context) { + TelephonyManager telephonyService = context.getSystemService(TelephonyManager.class); + Preconditions.checkState(telephonyService != null, "Unable to access telephony service"); + mImei = telephonyService.getImei(0); + mMeid = telephonyService.getMeid(0); + mSerialNumber = Build.getSerial(); + WifiManager wifiManager = context.getSystemService(WifiManager.class); + Preconditions.checkState(wifiManager != null, "Unable to access WiFi service"); + final String[] macAddresses = wifiManager.getFactoryMacAddresses(); + if (macAddresses == null || macAddresses.length == 0) { + mMacAddress = ""; + } else { + mMacAddress = macAddresses[0]; + } + } + + private static String getPaddedTruncatedString(String input, int maxLength) { + final String paddedValue = String.format("%" + maxLength + "s", input); + return paddedValue.substring(0, maxLength); + } + + private static String getPaddedHardwareIdentifier(String hardwareIdentifier) { + if (hardwareIdentifier == null) { + hardwareIdentifier = ""; + } + return getPaddedTruncatedString(hardwareIdentifier, PADDED_HW_ID_LENGTH); + } + + String getPaddedImei() { + return getPaddedHardwareIdentifier(mImei); + } + + String getPaddedMeid() { + return getPaddedHardwareIdentifier(mMeid); + } + + String getPaddedSerialNumber() { + return getPaddedHardwareIdentifier(mSerialNumber); + } + + String getPaddedProfileOwnerName(String profileOwnerPackage) { + return getPaddedTruncatedString(profileOwnerPackage, PADDED_PROFILE_OWNER_LENGTH); + } + + String getPaddedEnterpriseId(String enterpriseId) { + return getPaddedTruncatedString(enterpriseId, PADDED_ENTERPRISE_ID_LENGTH); + } + + /** + * Calculates the ESID. + * @param profileOwnerPackage Package of the Device Policy Client that manages the device/ + * profile. May not be null. + * @param enterpriseIdString The identifier for the enterprise in which the device/profile is + * being enrolled. This parameter may not be empty, but may be null. + * If called with {@code null}, will calculate an ESID with empty + * Enterprise ID. + */ + public String calculateEnterpriseId(String profileOwnerPackage, String enterpriseIdString) { + Preconditions.checkArgument(!TextUtils.isEmpty(profileOwnerPackage), + "owner package must be specified."); + + Preconditions.checkArgument(enterpriseIdString == null || !enterpriseIdString.isEmpty(), + "enterprise ID must either be null or non-empty."); + + if (enterpriseIdString == null) { + enterpriseIdString = ""; + } + + final byte[] serialNumber = getPaddedSerialNumber().getBytes(); + final byte[] imei = getPaddedImei().getBytes(); + final byte[] meid = getPaddedMeid().getBytes(); + final byte[] macAddress = mMacAddress.getBytes(); + final int totalIdentifiersLength = serialNumber.length + imei.length + meid.length + + macAddress.length; + final ByteBuffer fixedIdentifiers = ByteBuffer.allocate(totalIdentifiersLength); + fixedIdentifiers.put(serialNumber); + fixedIdentifiers.put(imei); + fixedIdentifiers.put(meid); + fixedIdentifiers.put(macAddress); + + final byte[] dpcPackage = getPaddedProfileOwnerName(profileOwnerPackage).getBytes(); + final byte[] enterpriseId = getPaddedEnterpriseId(enterpriseIdString).getBytes(); + final ByteBuffer info = ByteBuffer.allocate(dpcPackage.length + enterpriseId.length); + info.put(dpcPackage); + info.put(enterpriseId); + final byte[] esidBytes = Util.computeHkdf("HMACSHA256", fixedIdentifiers.array(), null, + info.array(), ESID_LENGTH); + ByteBuffer esidByteBuffer = ByteBuffer.wrap(esidBytes); + + VerifierDeviceIdentity firstId = new VerifierDeviceIdentity(esidByteBuffer.getLong()); + VerifierDeviceIdentity secondId = new VerifierDeviceIdentity(esidByteBuffer.getLong()); + return firstId.toString() + secondId.toString(); + } +} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 4d15ced48c83..d9350f39ee58 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -237,6 +237,8 @@ public final class SystemServer implements Dumpable { "com.android.server.companion.CompanionDeviceManagerService"; private static final String STATS_COMPANION_APEX_PATH = "/apex/com.android.os.statsd/javalib/service-statsd.jar"; + private static final String CONNECTIVITY_SERVICE_APEX_PATH = + "/apex/com.android.tethering/javalib/service-connectivity.jar"; private static final String STATS_COMPANION_LIFECYCLE_CLASS = "com.android.server.stats.StatsCompanion$Lifecycle"; private static final String STATS_PULL_ATOM_SERVICE_CLASS = @@ -1356,7 +1358,8 @@ public final class SystemServer implements Dumpable { } t.traceBegin("IpConnectivityMetrics"); - mSystemServiceManager.startService(IP_CONNECTIVITY_METRICS_CLASS); + mSystemServiceManager.startServiceFromJar(IP_CONNECTIVITY_METRICS_CLASS, + CONNECTIVITY_SERVICE_APEX_PATH); t.traceEnd(); t.traceBegin("NetworkWatchlistService"); @@ -1721,8 +1724,8 @@ public final class SystemServer implements Dumpable { // This has to be called after NetworkManagementService, NetworkStatsService // and NetworkPolicyManager because ConnectivityService needs to take these // services to initialize. - // TODO: Dynamically load service-connectivity.jar by using startServiceFromJar. - mSystemServiceManager.startService(CONNECTIVITY_SERVICE_INITIALIZER_CLASS); + mSystemServiceManager.startServiceFromJar(CONNECTIVITY_SERVICE_INITIALIZER_CLASS, + CONNECTIVITY_SERVICE_APEX_PATH); connectivity = IConnectivityManager.Stub.asInterface( ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); // TODO: Use ConnectivityManager instead of ConnectivityService. diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java index 49a41f02c484..16b9165915e2 100644 --- a/services/people/java/com/android/server/people/PeopleService.java +++ b/services/people/java/com/android/server/people/PeopleService.java @@ -19,7 +19,9 @@ package com.android.server.people; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.ActivityManager; import android.app.people.ConversationChannel; +import android.app.people.ConversationStatus; import android.app.people.IPeopleManager; import android.app.prediction.AppPredictionContext; import android.app.prediction.AppPredictionSessionId; @@ -27,6 +29,7 @@ import android.app.prediction.AppTarget; import android.app.prediction.AppTargetEvent; import android.app.prediction.IPredictionCallback; import android.content.Context; +import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.os.Binder; import android.os.CancellationSignal; @@ -38,9 +41,11 @@ import android.util.ArrayMap; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.people.data.DataManager; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Consumer; @@ -54,6 +59,8 @@ public class PeopleService extends SystemService { private final DataManager mDataManager; + private PackageManagerInternal mPackageManagerInternal; + /** * Initializes the system service. * @@ -83,6 +90,7 @@ public class PeopleService extends SystemService { publishBinderService(Context.PEOPLE_SERVICE, mService); } publishLocalService(PeopleServiceInternal.class, new LocalService()); + mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); } @Override @@ -112,6 +120,26 @@ public class PeopleService extends SystemService { return UserHandle.isSameApp(uid, Process.SYSTEM_UID) || uid == Process.ROOT_UID; } + private int handleIncomingUser(int userId) { + try { + return ActivityManager.getService().handleIncomingUser( + Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null); + } catch (RemoteException re) { + // Shouldn't happen, local. + } + return userId; + } + + private void checkCallerIsSameApp(String pkg) { + final int callingUid = Binder.getCallingUid(); + final int callingUserId = UserHandle.getUserId(callingUid); + + if (mPackageManagerInternal.getPackageUid(pkg, /*flags=*/ 0, + callingUserId) != callingUid) { + throw new SecurityException("Calling uid " + callingUid + " cannot query events" + + "for package " + pkg); + } + } /** * Enforces that only the system, root UID or SystemUI can make certain calls. @@ -154,6 +182,40 @@ public class PeopleService extends SystemService { enforceSystemRootOrSystemUI(getContext(), "get last interaction"); return mDataManager.getLastInteraction(packageName, userId, shortcutId); } + + @Override + public void addOrUpdateStatus(String packageName, int userId, String conversationId, + ConversationStatus status) { + handleIncomingUser(userId); + checkCallerIsSameApp(packageName); + mDataManager.addOrUpdateStatus(packageName, userId, conversationId, status); + } + + @Override + public void clearStatus(String packageName, int userId, String conversationId, + String statusId) { + handleIncomingUser(userId); + checkCallerIsSameApp(packageName); + mDataManager.clearStatus(packageName, userId, conversationId, statusId); + } + + @Override + public void clearStatuses(String packageName, int userId, String conversationId) { + handleIncomingUser(userId); + checkCallerIsSameApp(packageName); + mDataManager.clearStatuses(packageName, userId, conversationId); + } + + @Override + public ParceledListSlice<ConversationStatus> getStatuses(String packageName, int userId, + String conversationId) { + handleIncomingUser(userId); + if (!isSystemOrRoot()) { + checkCallerIsSameApp(packageName); + } + return new ParceledListSlice<>( + mDataManager.getStatuses(packageName, userId, conversationId)); + } }; @VisibleForTesting diff --git a/services/people/java/com/android/server/people/data/ConversationInfo.java b/services/people/java/com/android/server/people/data/ConversationInfo.java index 45f389cbd3ff..16c4c29a798e 100644 --- a/services/people/java/com/android/server/people/data/ConversationInfo.java +++ b/services/people/java/com/android/server/people/data/ConversationInfo.java @@ -19,6 +19,7 @@ package com.android.server.people.data; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.people.ConversationStatus; import android.content.LocusId; import android.content.LocusIdProto; import android.content.pm.ShortcutInfo; @@ -39,6 +40,10 @@ import java.io.DataOutputStream; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Objects; /** @@ -101,6 +106,8 @@ public class ConversationInfo { @ConversationFlags private int mConversationFlags; + private Map<String, ConversationStatus> mCurrStatuses; + private ConversationInfo(Builder builder) { mShortcutId = builder.mShortcutId; mLocusId = builder.mLocusId; @@ -111,6 +118,7 @@ public class ConversationInfo { mLastEventTimestamp = builder.mLastEventTimestamp; mShortcutFlags = builder.mShortcutFlags; mConversationFlags = builder.mConversationFlags; + mCurrStatuses = builder.mCurrStatuses; } @NonNull @@ -213,6 +221,10 @@ public class ConversationInfo { return hasConversationFlags(FLAG_CONTACT_STARRED); } + public Collection<ConversationStatus> getStatuses() { + return mCurrStatuses.values(); + } + @Override public boolean equals(Object obj) { if (this == obj) { @@ -230,14 +242,15 @@ public class ConversationInfo { && Objects.equals(mParentNotificationChannelId, other.mParentNotificationChannelId) && Objects.equals(mLastEventTimestamp, other.mLastEventTimestamp) && mShortcutFlags == other.mShortcutFlags - && mConversationFlags == other.mConversationFlags; + && mConversationFlags == other.mConversationFlags + && Objects.equals(mCurrStatuses, other.mCurrStatuses); } @Override public int hashCode() { return Objects.hash(mShortcutId, mLocusId, mContactUri, mContactPhoneNumber, mNotificationChannelId, mParentNotificationChannelId, mLastEventTimestamp, - mShortcutFlags, mConversationFlags); + mShortcutFlags, mConversationFlags, mCurrStatuses); } @Override @@ -251,6 +264,7 @@ public class ConversationInfo { sb.append(", notificationChannelId=").append(mNotificationChannelId); sb.append(", parentNotificationChannelId=").append(mParentNotificationChannelId); sb.append(", lastEventTimestamp=").append(mLastEventTimestamp); + sb.append(", statuses=").append(mCurrStatuses); sb.append(", shortcutFlags=0x").append(Integer.toHexString(mShortcutFlags)); sb.append(" ["); if (isShortcutLongLived()) { @@ -321,6 +335,7 @@ public class ConversationInfo { protoOutputStream.write(ConversationInfoProto.CONTACT_PHONE_NUMBER, mContactPhoneNumber); } + // ConversationStatus is a transient object and not persisted } @Nullable @@ -337,6 +352,7 @@ public class ConversationInfo { out.writeUTF(mContactPhoneNumber != null ? mContactPhoneNumber : ""); out.writeUTF(mParentNotificationChannelId != null ? mParentNotificationChannelId : ""); out.writeLong(mLastEventTimestamp); + // ConversationStatus is a transient object and not persisted } catch (IOException e) { Slog.e(TAG, "Failed to write fields to backup payload.", e); return null; @@ -469,6 +485,8 @@ public class ConversationInfo { @ConversationFlags private int mConversationFlags; + private Map<String, ConversationStatus> mCurrStatuses = new HashMap<>(); + Builder() { } @@ -486,6 +504,7 @@ public class ConversationInfo { mLastEventTimestamp = conversationInfo.mLastEventTimestamp; mShortcutFlags = conversationInfo.mShortcutFlags; mConversationFlags = conversationInfo.mConversationFlags; + mCurrStatuses = conversationInfo.mCurrStatuses; } Builder setShortcutId(@NonNull String shortcutId) { @@ -579,6 +598,26 @@ public class ConversationInfo { return this; } + Builder setStatuses(List<ConversationStatus> statuses) { + mCurrStatuses.clear(); + if (statuses != null) { + for (ConversationStatus status : statuses) { + mCurrStatuses.put(status.getId(), status); + } + } + return this; + } + + Builder addOrUpdateStatus(ConversationStatus status) { + mCurrStatuses.put(status.getId(), status); + return this; + } + + Builder clearStatus(String statusId) { + mCurrStatuses.remove(statusId); + return this; + } + ConversationInfo build() { Objects.requireNonNull(mShortcutId); return new ConversationInfo(this); diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java index b5e595a42ca9..e04e287d80f6 100644 --- a/services/people/java/com/android/server/people/data/DataManager.java +++ b/services/people/java/com/android/server/people/data/DataManager.java @@ -26,6 +26,7 @@ import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.app.Person; import android.app.people.ConversationChannel; +import android.app.people.ConversationStatus; import android.app.prediction.AppTarget; import android.app.prediction.AppTargetEvent; import android.app.usage.UsageEvents; @@ -75,6 +76,7 @@ import com.android.server.notification.NotificationManagerInternal; import com.android.server.notification.ShortcutHelper; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; @@ -308,6 +310,73 @@ public class DataManager { return 0L; } + public void addOrUpdateStatus(String packageName, int userId, String conversationId, + ConversationStatus status) { + ConversationStore cs = getConversationStoreOrThrow(packageName, userId); + ConversationInfo convToModify = getConversationInfoOrThrow(cs, conversationId); + ConversationInfo.Builder builder = new ConversationInfo.Builder(convToModify); + builder.addOrUpdateStatus(status); + cs.addOrUpdate(builder.build()); + } + + public void clearStatus(String packageName, int userId, String conversationId, + String statusId) { + ConversationStore cs = getConversationStoreOrThrow(packageName, userId); + ConversationInfo convToModify = getConversationInfoOrThrow(cs, conversationId); + ConversationInfo.Builder builder = new ConversationInfo.Builder(convToModify); + builder.clearStatus(statusId); + cs.addOrUpdate(builder.build()); + } + + public void clearStatuses(String packageName, int userId, String conversationId) { + ConversationStore cs = getConversationStoreOrThrow(packageName, userId); + ConversationInfo convToModify = getConversationInfoOrThrow(cs, conversationId); + ConversationInfo.Builder builder = new ConversationInfo.Builder(convToModify); + builder.setStatuses(null); + cs.addOrUpdate(builder.build()); + } + + public @NonNull List<ConversationStatus> getStatuses(String packageName, int userId, + String conversationId) { + ConversationStore cs = getConversationStoreOrThrow(packageName, userId); + ConversationInfo conversationInfo = getConversationInfoOrThrow(cs, conversationId); + Collection<ConversationStatus> statuses = conversationInfo.getStatuses(); + if (statuses != null) { + final ArrayList<ConversationStatus> list = new ArrayList<>(statuses.size()); + list.addAll(statuses); + return list; + } + return new ArrayList<>(); + } + + /** + * Returns a conversation store for a package, if it exists. + */ + private @NonNull ConversationStore getConversationStoreOrThrow(String packageName, int userId) { + final PackageData packageData = getPackage(packageName, userId); + if (packageData == null) { + throw new IllegalArgumentException("No settings exist for package " + packageName); + } + ConversationStore cs = packageData.getConversationStore(); + if (cs == null) { + throw new IllegalArgumentException("No conversations exist for package " + packageName); + } + return cs; + } + + /** + * Returns a conversation store for a package, if it exists. + */ + private @NonNull ConversationInfo getConversationInfoOrThrow(ConversationStore cs, + String conversationId) { + ConversationInfo ci = cs.getConversation(conversationId); + + if (ci == null) { + throw new IllegalArgumentException("Conversation does not exist"); + } + return ci; + } + /** Reports the sharing related {@link AppTargetEvent} from App Prediction Manager. */ public void reportShareTargetEvent(@NonNull AppTargetEvent event, @NonNull IntentFilter intentFilter) { diff --git a/services/tests/mockingservicestests/src/com/android/server/OWNERS b/services/tests/mockingservicestests/src/com/android/server/OWNERS index e779e21bb987..c0f0ce047da6 100644 --- a/services/tests/mockingservicestests/src/com/android/server/OWNERS +++ b/services/tests/mockingservicestests/src/com/android/server/OWNERS @@ -1 +1,3 @@ per-file *Alarm* = file:/apex/jobscheduler/OWNERS +per-file *AppStateTracker* = file:/apex/jobscheduler/OWNERS +per-file *DeviceIdleController* = file:/apex/jobscheduler/OWNERS diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java index f375421043fd..fd364ae77240 100644 --- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java @@ -233,8 +233,10 @@ public class RescuePartyTest { verifiedTimesMap); noteBoot(4); + assertTrue(RescueParty.isRebootPropertySet()); - assertTrue(RescueParty.isAttemptingFactoryReset()); + noteBoot(5); + assertTrue(RescueParty.isFactoryResetPropertySet()); } @Test @@ -255,7 +257,10 @@ public class RescuePartyTest { /*configResetVerifiedTimesMap=*/ null); notePersistentAppCrash(4); - assertTrue(RescueParty.isAttemptingFactoryReset()); + assertTrue(RescueParty.isRebootPropertySet()); + + notePersistentAppCrash(5); + assertTrue(RescueParty.isFactoryResetPropertySet()); } @Test @@ -306,7 +311,11 @@ public class RescuePartyTest { observer.execute(new VersionedPackage( CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4); - assertTrue(RescueParty.isAttemptingFactoryReset()); + assertTrue(RescueParty.isRebootPropertySet()); + + observer.execute(new VersionedPackage( + CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5); + assertTrue(RescueParty.isFactoryResetPropertySet()); } @Test @@ -367,7 +376,11 @@ public class RescuePartyTest { observer.execute(new VersionedPackage( CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4); - assertTrue(RescueParty.isAttemptingFactoryReset()); + assertTrue(RescueParty.isRebootPropertySet()); + + observer.execute(new VersionedPackage( + CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5); + assertTrue(RescueParty.isFactoryResetPropertySet()); } @Test @@ -376,6 +389,7 @@ public class RescuePartyTest { noteBoot(i + 1); } assertTrue(RescueParty.isAttemptingFactoryReset()); + assertTrue(RescueParty.isFactoryResetPropertySet()); } @Test @@ -424,7 +438,7 @@ public class RescuePartyTest { for (int i = 0; i < LEVEL_FACTORY_RESET; i++) { noteBoot(i + 1); } - assertFalse(RescueParty.isAttemptingFactoryReset()); + assertFalse(RescueParty.isFactoryResetPropertySet()); // Restore the property value initialized in SetUp() SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, ""); diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/OWNERS b/services/tests/servicestests/src/com/android/server/apphibernation/OWNERS new file mode 100644 index 000000000000..c2e27e084c8c --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/apphibernation/OWNERS @@ -0,0 +1 @@ +include /core/java/android/apphibernation/OWNERS 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 f29b0594db8f..c6fde87f5953 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 @@ -22,10 +22,12 @@ import static org.testng.Assert.expectThrows; import android.app.appsearch.AppSearchSchema; import android.app.appsearch.GenericDocument; +import android.app.appsearch.SearchResult; import android.app.appsearch.SearchResultPage; import android.app.appsearch.SearchSpec; import android.app.appsearch.exceptions.AppSearchException; +import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter; import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter; import com.android.server.appsearch.proto.DocumentProto; import com.android.server.appsearch.proto.GetOptimizeInfoResultProto; @@ -33,6 +35,7 @@ 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.SearchResultProto; import com.android.server.appsearch.proto.SearchSpecProto; import com.android.server.appsearch.proto.StringIndexingConfig; import com.android.server.appsearch.proto.TermMatchType; @@ -316,14 +319,14 @@ public class AppSearchImplTest { DocumentProto insideDocument = DocumentProto.newBuilder() .setUri("inside-uri") - .setSchema("package$databaseName1/type") - .setNamespace("package$databaseName2/namespace") + .setSchema("package$databaseName/type") + .setNamespace("package$databaseName/namespace") .build(); DocumentProto documentProto = DocumentProto.newBuilder() .setUri("uri") - .setSchema("package$databaseName2/type") - .setNamespace("package$databaseName3/namespace") + .setSchema("package$databaseName/type") + .setNamespace("package$databaseName/namespace") .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument)) .build(); @@ -345,11 +348,56 @@ public class AppSearchImplTest { .build(); DocumentProto.Builder actualDocument = documentProto.toBuilder(); - mAppSearchImpl.removePrefixesFromDocument(actualDocument); + assertThat(mAppSearchImpl.removePrefixesFromDocument(actualDocument)) + .isEqualTo("package$databaseName/"); assertThat(actualDocument.build()).isEqualTo(expectedDocumentProto); } @Test + public void testRemoveDatabasesFromDocumentThrowsException() throws Exception { + // Set two different database names in the document, which should never happen + DocumentProto documentProto = + DocumentProto.newBuilder() + .setUri("uri") + .setSchema("prefix1/type") + .setNamespace("prefix2/namespace") + .build(); + + DocumentProto.Builder actualDocument = documentProto.toBuilder(); + AppSearchException e = + expectThrows( + AppSearchException.class, + () -> mAppSearchImpl.removePrefixesFromDocument(actualDocument)); + assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names"); + } + + @Test + public void testNestedRemoveDatabasesFromDocumentThrowsException() throws Exception { + // Set two different database names in the outer and inner document, which should never + // happen. + DocumentProto insideDocument = + DocumentProto.newBuilder() + .setUri("inside-uri") + .setSchema("prefix1/type") + .setNamespace("prefix1/namespace") + .build(); + DocumentProto documentProto = + DocumentProto.newBuilder() + .setUri("uri") + .setSchema("prefix2/type") + .setNamespace("prefix2/namespace") + .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument)) + .build(); + + DocumentProto.Builder actualDocument = documentProto.toBuilder(); + AppSearchException e = + expectThrows( + AppSearchException.class, + () -> mAppSearchImpl.removePrefixesFromDocument(actualDocument)); + assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names"); + } + + @Test public void testOptimize() throws Exception { // Insert schema List<AppSearchSchema> schemas = @@ -865,4 +913,40 @@ public class AppSearchImplTest { AppSearchImpl.createPrefix("package", "database1"), AppSearchImpl.createPrefix("package", "database2")); } + + @Test + public void testRewriteSearchResultProto() throws Exception { + final String database = + "com.package.foo" + + AppSearchImpl.PACKAGE_DELIMITER + + "databaseName" + + AppSearchImpl.DATABASE_DELIMITER; + final String uri = "uri"; + final String namespace = database + "namespace"; + final String schemaType = database + "schema"; + + // Building the SearchResult received from query. + DocumentProto documentProto = + DocumentProto.newBuilder() + .setUri(uri) + .setNamespace(namespace) + .setSchema(schemaType) + .build(); + SearchResultProto.ResultProto resultProto = + SearchResultProto.ResultProto.newBuilder().setDocument(documentProto).build(); + SearchResultProto searchResultProto = + SearchResultProto.newBuilder().addResults(resultProto).build(); + + DocumentProto.Builder strippedDocumentProto = documentProto.toBuilder(); + AppSearchImpl.removePrefixesFromDocument(strippedDocumentProto); + SearchResultPage searchResultPage = + AppSearchImpl.rewriteSearchResultProto(searchResultProto); + for (SearchResult result : searchResultPage.getResults()) { + assertThat(result.getPackageName()).isEqualTo("com.package.foo"); + assertThat(result.getDocument()) + .isEqualTo( + GenericDocumentToProtoConverter.toGenericDocument( + strippedDocumentProto.build())); + } + } } 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 7c68c6be4883..a3f0f6bf280e 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 @@ -29,6 +29,8 @@ import com.android.server.appsearch.proto.SnippetProto; import org.junit.Test; +import java.util.Collections; + public class SnippetTest { // TODO(tytytyww): Add tests for Double and Long Snippets. @@ -83,7 +85,8 @@ public class SnippetTest { // Making ResultReader and getting Snippet values. SearchResultPage searchResultPage = - SearchResultToProtoConverter.toSearchResultPage(searchResultProto); + SearchResultToProtoConverter.toSearchResultPage( + searchResultProto, Collections.singletonList("packageName")); for (SearchResult result : searchResultPage.getResults()) { SearchResult.MatchInfo match = result.getMatches().get(0); assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString); @@ -131,7 +134,8 @@ public class SnippetTest { SearchResultProto.newBuilder().addResults(resultProto).build(); SearchResultPage searchResultPage = - SearchResultToProtoConverter.toSearchResultPage(searchResultProto); + SearchResultToProtoConverter.toSearchResultPage( + searchResultProto, Collections.singletonList("packageName")); for (SearchResult result : searchResultPage.getResults()) { assertThat(result.getMatches()).isEmpty(); } @@ -196,7 +200,8 @@ public class SnippetTest { // Making ResultReader and getting Snippet values. SearchResultPage searchResultPage = - SearchResultToProtoConverter.toSearchResultPage(searchResultProto); + SearchResultToProtoConverter.toSearchResultPage( + searchResultProto, Collections.singletonList("packageName")); for (SearchResult result : searchResultPage.getResults()) { SearchResult.MatchInfo match1 = result.getMatches().get(0); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java index 17324bab70d2..9c28c99fcc07 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java @@ -460,6 +460,11 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi } @Override + KeyChain.KeyChainConnection keyChainBind() { + return services.keyChainConnection; + } + + @Override KeyChain.KeyChainConnection keyChainBindAsUser(UserHandle user) { return services.keyChainConnection; } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index c1b1133dbb22..39fa20e4153f 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -1628,6 +1628,33 @@ public class DevicePolicyManagerTest extends DpmTestBase { )), eq(user)); } + @Test + public void testRemoveCredentialManagementApp() throws Exception { + final String packageName = "com.test.cred.mng"; + Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED); + intent.setData(Uri.parse("package:" + packageName)); + dpms.mReceiver.setPendingResult( + new BroadcastReceiver.PendingResult(Activity.RESULT_OK, + "resultData", + /* resultExtras= */ null, + BroadcastReceiver.PendingResult.TYPE_UNREGISTERED, + /* ordered= */ true, + /* sticky= */ false, + /* token= */ null, + CALLER_USER_HANDLE, + /* flags= */ 0)); + when(getServices().keyChainConnection.getService().hasCredentialManagementApp()) + .thenReturn(true); + when(getServices().keyChainConnection.getService().getCredentialManagementAppPackageName()) + .thenReturn(packageName); + + dpms.mReceiver.onReceive(mContext, intent); + + flushTasks(dpms); + verify(getServices().keyChainConnection.getService()).hasCredentialManagementApp(); + verify(getServices().keyChainConnection.getService()).removeCredentialManagementApp(); + } + /** * Simple test for delegate set/get and general delegation. Tests verifying that delegated * privileges can acually be exercised by a delegate are not covered here. diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/EnterpriseSpecificIdCalculatorTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/EnterpriseSpecificIdCalculatorTest.java new file mode 100644 index 000000000000..c2c1d5b4f3be --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/EnterpriseSpecificIdCalculatorTest.java @@ -0,0 +1,107 @@ +/* + * 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.devicepolicy; + +import static com.google.common.truth.Truth.assertThat; + +import static org.testng.Assert.assertThrows; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class EnterpriseSpecificIdCalculatorTest { + private static final String SOME_IMEI = "56134231542345"; + private static final String SOME_SERIAL_NUMBER = "XZ663CCAJA7"; + private static final String SOME_MAC_ADDRESS = "65:ca:f3:fe:9d:b1"; + private static final String NO_MEID = null; + private static final String SOME_PACKAGE = "com.example.test.dpc"; + private static final String ANOTHER_PACKAGE = "org.example.test.another.dpc"; + private static final String SOME_ENTERPRISE_ID = "73456234"; + private static final String ANOTHER_ENTERPRISE_ID = "243441"; + + private EnterpriseSpecificIdCalculator mEsidCalculator; + + @Before + public void createDefaultEsidCalculator() { + mEsidCalculator = new EnterpriseSpecificIdCalculator(SOME_IMEI, NO_MEID, SOME_SERIAL_NUMBER, + SOME_MAC_ADDRESS); + } + + @Test + public void paddingOfIdentifiers() { + assertThat(mEsidCalculator.getPaddedImei()).isEqualTo(" 56134231542345"); + assertThat(mEsidCalculator.getPaddedMeid()).isEqualTo(" "); + assertThat(mEsidCalculator.getPaddedSerialNumber()).isEqualTo(" XZ663CCAJA7"); + } + + @Test + public void truncationOfLongIdentifier() { + EnterpriseSpecificIdCalculator esidCalculator = new EnterpriseSpecificIdCalculator( + SOME_IMEI, NO_MEID, "XZ663CCAJA7XZ663CCAJA7XZ663CCAJA7", + SOME_MAC_ADDRESS); + assertThat(esidCalculator.getPaddedSerialNumber()).isEqualTo("XZ663CCAJA7XZ663"); + } + + @Test + public void paddingOfPackageName() { + assertThat(mEsidCalculator.getPaddedProfileOwnerName(SOME_PACKAGE)).isEqualTo( + " " + SOME_PACKAGE); + } + + @Test + public void paddingOfEnterpriseId() { + assertThat(mEsidCalculator.getPaddedEnterpriseId(SOME_ENTERPRISE_ID)).isEqualTo( + " " + SOME_ENTERPRISE_ID); + } + + @Test + public void emptyEnterpriseIdYieldsEmptyEsid() { + assertThrows(IllegalArgumentException.class, () -> + mEsidCalculator.calculateEnterpriseId(SOME_PACKAGE, "")); + } + + @Test + public void emptyDpcPackageYieldsEmptyEsid() { + assertThrows(IllegalArgumentException.class, () -> + mEsidCalculator.calculateEnterpriseId("", SOME_ENTERPRISE_ID)); + } + + // On upgrade, an ESID will be calculated with an empty Enterprise ID. This is signalled + // to the EnterpriseSpecificIdCalculator by passing in null. + @Test + public void nullEnterpriseIdYieldsValidEsid() { + assertThat(mEsidCalculator.calculateEnterpriseId(SOME_PACKAGE, null)).isEqualTo( + "C4W7-VUJT-PHSA-HMY53-CLHX-L4HW-L"); + } + + @Test + public void knownValues() { + assertThat( + mEsidCalculator.calculateEnterpriseId(SOME_PACKAGE, SOME_ENTERPRISE_ID)).isEqualTo( + "FP7B-RXQW-Q77F-7J6FC-5RXZ-UJI6-6"); + assertThat(mEsidCalculator.calculateEnterpriseId(SOME_PACKAGE, + ANOTHER_ENTERPRISE_ID)).isEqualTo("ATAL-VPIX-GBNZ-NE3TF-TDEV-3OVO-C"); + assertThat(mEsidCalculator.calculateEnterpriseId(ANOTHER_PACKAGE, + SOME_ENTERPRISE_ID)).isEqualTo("JHU3-6SHH-YLHC-ZGETD-PWNI-7NPQ-S"); + assertThat(mEsidCalculator.calculateEnterpriseId(ANOTHER_PACKAGE, + ANOTHER_ENTERPRISE_ID)).isEqualTo("LEF3-QBEC-UQ6O-RIOCX-TQF6-GRLV-F"); + } +} diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/OWNERS b/services/tests/servicestests/src/com/android/server/graphics/fonts/OWNERS new file mode 100644 index 000000000000..34ac813f02e0 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 24939 + +include /graphics/java/android/graphics/fonts/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java index be8e569c7a45..99ecb868db6e 100644 --- a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java +++ b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java @@ -41,6 +41,7 @@ import android.provider.Settings; import android.util.Log; import androidx.test.InstrumentationRegistry; +import androidx.test.filters.FlakyTest; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; @@ -131,6 +132,7 @@ public class BackgroundRestrictionsTest { } @Test + @FlakyTest public void testPowerWhiteList() throws Exception { scheduleAndAssertJobStarted(); setAppOpsModeAllowed(false); 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 5cff2081e59e..972b3bb8b459 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 @@ -89,6 +89,81 @@ public class ControllerImplTest { } @Test + public void initializationFailure_primary() { + ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + TestEnvironment testEnvironment = new TestEnvironment( + mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + Duration expectedInitTimeout = testEnvironment.getProviderInitializationTimeout() + .plus(testEnvironment.getProviderInitializationTimeoutFuzz()); + + mTestPrimaryLocationTimeZoneProvider.setFailDuringInitialization(true); + + // Initialize. After initialization the providers must be initialized and one should be + // started. + controllerImpl.initialize(testEnvironment, mTestCallback); + + mTestPrimaryLocationTimeZoneProvider.assertInitialized(); + mTestSecondaryLocationTimeZoneProvider.assertInitialized(); + + mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertInitializationTimeoutSet(expectedInitTimeout); + mTestCallback.assertNoSuggestionMade(); + assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + } + + @Test + public void initializationFailure_secondary() { + ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + TestEnvironment testEnvironment = new TestEnvironment( + mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + Duration expectedInitTimeout = testEnvironment.getProviderInitializationTimeout() + .plus(testEnvironment.getProviderInitializationTimeoutFuzz()); + + mTestSecondaryLocationTimeZoneProvider.setFailDuringInitialization(true); + + // Initialize. After initialization the providers must be initialized and one should be + // started. + controllerImpl.initialize(testEnvironment, mTestCallback); + + mTestPrimaryLocationTimeZoneProvider.assertInitialized(); + mTestSecondaryLocationTimeZoneProvider.assertInitialized(); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestPrimaryLocationTimeZoneProvider.assertInitializationTimeoutSet(expectedInitTimeout); + mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); + mTestCallback.assertNoSuggestionMade(); + assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + } + + @Test + public void initializationFailure_both() { + ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + TestEnvironment testEnvironment = new TestEnvironment( + mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + + mTestPrimaryLocationTimeZoneProvider.setFailDuringInitialization(true); + mTestSecondaryLocationTimeZoneProvider.setFailDuringInitialization(true); + + // Initialize. After initialization the providers must be initialized and one should be + // started. + controllerImpl.initialize(testEnvironment, mTestCallback); + + mTestPrimaryLocationTimeZoneProvider.assertInitialized(); + mTestSecondaryLocationTimeZoneProvider.assertInitialized(); + + mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); + mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); + mTestCallback.assertUncertainSuggestionMadeAndCommit(); + assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + } + + @Test public void initialState_started() { ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); @@ -1097,6 +1172,7 @@ public class ControllerImplTest { /** Used to track historic provider states for tests. */ private final TestState<ProviderState> mTestProviderState = new TestState<>(); + private boolean mFailDuringInitialization; private boolean mInitialized; private boolean mDestroyed; @@ -1107,9 +1183,16 @@ public class ControllerImplTest { super(threadingDomain, providerName); } + public void setFailDuringInitialization(boolean failInitialization) { + mFailDuringInitialization = failInitialization; + } + @Override void onInitialize() { mInitialized = true; + if (mFailDuringInitialization) { + throw new RuntimeException("Simulated initialization failure"); + } } @Override diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java index 9c8a38219a9c..ac9316e7d908 100644 --- a/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java @@ -24,6 +24,9 @@ import static org.junit.Assert.fail; import android.net.ConnectivityMetricsEvent; import android.net.IIpConnectivityMetrics; import android.net.INetdEventCallback; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -106,6 +109,16 @@ public class NetworkWatchlistServiceTests { counter--; return true; } + + // TODO: mark @Override when aosp/1541935 automerges to master. + public void logDefaultNetworkValidity(boolean valid) { + } + + // TODO: mark @Override when aosp/1541935 automerges to master. + public void logDefaultNetworkEvent(Network defaultNetwork, int score, boolean validated, + LinkProperties lp, NetworkCapabilities nc, Network previousDefaultNetwork, + int previousScore, LinkProperties previousLp, NetworkCapabilities previousNc) { + } }; ServiceThread mHandlerThread; diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java index c6823ebfd655..8139310a4c3d 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java @@ -16,11 +16,17 @@ package com.android.server.people.data; +import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY; +import static android.app.people.ConversationStatus.ACTIVITY_GAME; + +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.assertNull; import static org.junit.Assert.assertTrue; +import android.app.people.ConversationStatus; import android.content.LocusId; import android.content.pm.ShortcutInfo; import android.net.Uri; @@ -41,6 +47,9 @@ public final class ConversationInfoTest { @Test public void testBuild() { + ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build(); + ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build(); + ConversationInfo conversationInfo = new ConversationInfo.Builder() .setShortcutId(SHORTCUT_ID) .setLocusId(LOCUS_ID) @@ -58,6 +67,8 @@ public final class ConversationInfoTest { .setPersonImportant(true) .setPersonBot(true) .setContactStarred(true) + .addOrUpdateStatus(cs) + .addOrUpdateStatus(cs2) .build(); assertEquals(SHORTCUT_ID, conversationInfo.getShortcutId()); @@ -77,6 +88,8 @@ public final class ConversationInfoTest { assertTrue(conversationInfo.isPersonImportant()); assertTrue(conversationInfo.isPersonBot()); assertTrue(conversationInfo.isContactStarred()); + assertThat(conversationInfo.getStatuses()).contains(cs); + assertThat(conversationInfo.getStatuses()).contains(cs2); } @Test @@ -101,10 +114,15 @@ public final class ConversationInfoTest { assertFalse(conversationInfo.isPersonImportant()); assertFalse(conversationInfo.isPersonBot()); assertFalse(conversationInfo.isContactStarred()); + assertThat(conversationInfo.getStatuses()).isNotNull(); + assertThat(conversationInfo.getStatuses()).isEmpty(); } @Test public void testBuildFromAnotherConversationInfo() { + ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build(); + ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build(); + ConversationInfo source = new ConversationInfo.Builder() .setShortcutId(SHORTCUT_ID) .setLocusId(LOCUS_ID) @@ -120,6 +138,8 @@ public final class ConversationInfoTest { .setPersonImportant(true) .setPersonBot(true) .setContactStarred(true) + .addOrUpdateStatus(cs) + .addOrUpdateStatus(cs2) .build(); ConversationInfo destination = new ConversationInfo.Builder(source) @@ -141,5 +161,7 @@ public final class ConversationInfoTest { assertTrue(destination.isPersonImportant()); assertTrue(destination.isPersonBot()); assertFalse(destination.isContactStarred()); + assertThat(destination.getStatuses()).contains(cs); + assertThat(destination.getStatuses()).contains(cs2); } } diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java index 2471210f6325..be8a99c5ce36 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java @@ -16,6 +16,8 @@ package com.android.server.people.data; +import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY; +import static android.app.people.ConversationStatus.ACTIVITY_GAME; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED; @@ -24,6 +26,8 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat import static com.google.common.truth.Truth.assertThat; +import static junit.framework.Assert.fail; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -50,6 +54,7 @@ import android.app.NotificationManager; import android.app.Person; import android.app.job.JobScheduler; import android.app.people.ConversationChannel; +import android.app.people.ConversationStatus; import android.app.prediction.AppTarget; import android.app.prediction.AppTargetEvent; import android.app.prediction.AppTargetId; @@ -937,6 +942,83 @@ public final class DataManagerTest { } @Test + public void testAddOrUpdateStatus_noCachedShortcut() { + mDataManager.onUserUnlocked(USER_ID_PRIMARY); + + ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build(); + + try { + mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs); + fail("Updated a conversation info that didn't previously exist"); + } catch (IllegalArgumentException e) { + // good + } + } + + @Test + public void testAddOrUpdateStatus() { + mDataManager.onUserUnlocked(USER_ID_PRIMARY); + + ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, + buildPerson()); + mDataManager.addOrUpdateConversationInfo(shortcut); + + ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build(); + mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs); + + assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID)) + .contains(cs); + + ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build(); + mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2); + + assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID)) + .contains(cs); + assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID)) + .contains(cs2); + } + + @Test + public void testClearStatus() { + mDataManager.onUserUnlocked(USER_ID_PRIMARY); + + ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, + buildPerson()); + mDataManager.addOrUpdateConversationInfo(shortcut); + + ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build(); + ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build(); + mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs); + mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2); + + mDataManager.clearStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2.getId()); + + assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID)) + .contains(cs); + assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID)) + .doesNotContain(cs2); + } + + @Test + public void testClearStatuses() { + mDataManager.onUserUnlocked(USER_ID_PRIMARY); + + ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, + buildPerson()); + mDataManager.addOrUpdateConversationInfo(shortcut); + + ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build(); + ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build(); + mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs); + mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2); + + mDataManager.clearStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID); + + assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID)) + .isEmpty(); + } + + @Test public void testNonCachedShortcutNotInRecentList() { mDataManager.onUserUnlocked(USER_ID_PRIMARY); 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 08d4caacd777..5f654406812f 100644 --- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java @@ -32,8 +32,13 @@ import androidx.test.InstrumentationRegistry; import com.android.server.SystemService; import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper; +import com.android.server.powerstats.nano.PowerEntityInfoProto; import com.android.server.powerstats.nano.PowerStatsServiceMeterProto; import com.android.server.powerstats.nano.PowerStatsServiceModelProto; +import com.android.server.powerstats.nano.PowerStatsServiceResidencyProto; +import com.android.server.powerstats.nano.StateInfoProto; +import com.android.server.powerstats.nano.StateResidencyProto; +import com.android.server.powerstats.nano.StateResidencyResultProto; import org.junit.Before; import org.junit.Test; @@ -58,6 +63,7 @@ public class PowerStatsServiceTest { private static final String DATA_STORAGE_SUBDIR = "powerstatstest"; private static final String METER_FILENAME = "metertest"; private static final String MODEL_FILENAME = "modeltest"; + private static final String RESIDENCY_FILENAME = "residencytest"; private static final String PROTO_OUTPUT_FILENAME = "powerstats.proto"; private static final String CHANNEL_NAME = "channelname"; private static final String POWER_ENTITY_NAME = "powerentityinfo"; @@ -72,6 +78,7 @@ public class PowerStatsServiceTest { private PowerStatsService mService; private File mDataStorageDir; private TimerTrigger mTimerTrigger; + private BatteryTrigger mBatteryTrigger; private PowerStatsLogger mPowerStatsLogger; private final PowerStatsService.Injector mInjector = new PowerStatsService.Injector() { @@ -99,22 +106,29 @@ public class PowerStatsServiceTest { } @Override + String createResidencyFilename() { + return RESIDENCY_FILENAME; + } + + @Override IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() { return new TestPowerStatsHALWrapper(); } @Override PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath, - String meterFilename, String modelFilename, + String meterFilename, String modelFilename, String residencyFilename, IPowerStatsHALWrapper powerStatsHALWrapper) { mPowerStatsLogger = new PowerStatsLogger(context, dataStoragePath, meterFilename, - modelFilename, powerStatsHALWrapper); + modelFilename, residencyFilename, powerStatsHALWrapper); return mPowerStatsLogger; } @Override BatteryTrigger createBatteryTrigger(Context context, PowerStatsLogger powerStatsLogger) { - return new BatteryTrigger(context, powerStatsLogger, false /* trigger enabled */); + mBatteryTrigger = new BatteryTrigger(context, powerStatsLogger, + false /* trigger enabled */); + return mBatteryTrigger; } @Override @@ -137,7 +151,7 @@ public class PowerStatsServiceTest { 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); + powerEntityInfoList[i].states[j].stateName = new String(STATE_NAME + j); } } return powerEntityInfoList; @@ -154,6 +168,7 @@ public class PowerStatsServiceTest { 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].stateId = j; stateResidencyResultList[i].stateResidencyData[j].totalTimeInStateMs = j; stateResidencyResultList[i].stateResidencyData[j].totalStateEntryCount = j; stateResidencyResultList[i].stateResidencyData[j].lastEntryTimestampMs = j; @@ -225,7 +240,7 @@ public class PowerStatsServiceTest { mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); // Write data to on-device storage. - mTimerTrigger.logPowerStatsData(); + mTimerTrigger.logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_TIMER); // The above call puts a message on a handler. Wait for // it to be processed. @@ -266,7 +281,7 @@ public class PowerStatsServiceTest { mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); // Write data to on-device storage. - mTimerTrigger.logPowerStatsData(); + mTimerTrigger.logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_TIMER); // The above call puts a message on a handler. Wait for // it to be processed. @@ -301,6 +316,61 @@ public class PowerStatsServiceTest { } @Test + public void testWrittenResidencyDataMatchesReadIncidentReportData() + throws InterruptedException, IOException { + mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + + // Write data to on-device storage. + mBatteryTrigger.logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_BATTERY_DROP); + + // The above call puts a message on a handler. Wait for + // it to be processed. + Thread.sleep(100); + + // Write on-device storage to an incident report. + File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME); + FileOutputStream fos = new FileOutputStream(incidentReport); + mPowerStatsLogger.writeResidencyDataToFile(fos.getFD()); + + // Read the incident report in to a byte array. + FileInputStream fis = new FileInputStream(incidentReport); + byte[] fileContent = new byte[(int) incidentReport.length()]; + fis.read(fileContent); + + // Parse the incident data into a PowerStatsServiceResidencyProto object. + PowerStatsServiceResidencyProto pssProto = + PowerStatsServiceResidencyProto.parseFrom(fileContent); + + // Validate the powerEntityInfo array matches what was written to on-device storage. + assertTrue(pssProto.powerEntityInfo.length == POWER_ENTITY_COUNT); + for (int i = 0; i < pssProto.powerEntityInfo.length; i++) { + PowerEntityInfoProto powerEntityInfo = pssProto.powerEntityInfo[i]; + assertTrue(powerEntityInfo.powerEntityId == i); + assertTrue(powerEntityInfo.powerEntityName.equals(POWER_ENTITY_NAME + i)); + for (int j = 0; j < powerEntityInfo.states.length; j++) { + StateInfoProto stateInfo = powerEntityInfo.states[j]; + assertTrue(stateInfo.stateId == j); + assertTrue(stateInfo.stateName.equals(STATE_NAME + j)); + } + } + + // Validate the stateResidencyResult array matches what was written to on-device storage. + assertTrue(pssProto.stateResidencyResult.length == POWER_ENTITY_COUNT); + for (int i = 0; i < pssProto.stateResidencyResult.length; i++) { + StateResidencyResultProto stateResidencyResult = pssProto.stateResidencyResult[i]; + assertTrue(stateResidencyResult.powerEntityId == i); + assertTrue(stateResidencyResult.stateResidencyData.length == STATE_RESIDENCY_COUNT); + for (int j = 0; j < stateResidencyResult.stateResidencyData.length; j++) { + StateResidencyProto stateResidency = stateResidencyResult.stateResidencyData[j]; + assertTrue(stateResidency.stateId == j); + assertTrue(stateResidency.totalTimeInStateMs == j); + assertTrue(stateResidency.totalStateEntryCount == j); + assertTrue(stateResidency.lastEntryTimestampMs == j); + } + } + } + + @Test public void testCorruptOnDeviceMeterStorage() throws IOException { mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); @@ -384,6 +454,55 @@ public class PowerStatsServiceTest { } @Test + public void testCorruptOnDeviceResidencyStorage() throws IOException { + mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + + // Generate random array of bytes to emulate corrupt data. + Random rd = new Random(); + byte[] bytes = new byte[100]; + rd.nextBytes(bytes); + + // Store corrupt data in on-device storage. Add fake timestamp to filename + // to match format expected by FileRotator. + File onDeviceStorageFile = new File(mDataStorageDir, RESIDENCY_FILENAME + ".1234-2234"); + FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile); + onDeviceStorageFos.write(bytes); + onDeviceStorageFos.close(); + + // Write on-device storage to an incident report. + File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME); + FileOutputStream incidentReportFos = new FileOutputStream(incidentReport); + mPowerStatsLogger.writeResidencyDataToFile(incidentReportFos.getFD()); + + // Read the incident report in to a byte array. + FileInputStream fis = new FileInputStream(incidentReport); + byte[] fileContent = new byte[(int) incidentReport.length()]; + fis.read(fileContent); + + // Parse the incident data into a PowerStatsServiceResidencyProto object. + PowerStatsServiceResidencyProto pssProto = + PowerStatsServiceResidencyProto.parseFrom(fileContent); + + // Valid powerEntityInfo data is written to the incident report in the call to + // mPowerStatsLogger.writeResidencyDataToFile(). + assertTrue(pssProto.powerEntityInfo.length == POWER_ENTITY_COUNT); + for (int i = 0; i < pssProto.powerEntityInfo.length; i++) { + PowerEntityInfoProto powerEntityInfo = pssProto.powerEntityInfo[i]; + assertTrue(powerEntityInfo.powerEntityId == i); + assertTrue(powerEntityInfo.powerEntityName.equals(POWER_ENTITY_NAME + i)); + for (int j = 0; j < powerEntityInfo.states.length; j++) { + StateInfoProto stateInfo = powerEntityInfo.states[j]; + assertTrue(stateInfo.stateId == j); + assertTrue(stateInfo.stateName.equals(STATE_NAME + j)); + } + } + + // No stateResidencyResults should be written to the incident report since it + // is all corrupt (random bytes generated above). + assertTrue(pssProto.stateResidencyResult.length == 0); + } + + @Test public void testNotEnoughBytesAfterMeterLengthField() throws IOException { mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); @@ -467,4 +586,54 @@ public class PowerStatsServiceTest { // input buffer had only length and no data. assertTrue(pssProto.energyConsumerResult.length == 0); } + + @Test + public void testNotEnoughBytesAfterResidencyLengthField() throws IOException { + mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + + // Create corrupt data. + // Length field is correct, but there is no data following the length. + ByteArrayOutputStream data = new ByteArrayOutputStream(); + data.write(ByteBuffer.allocate(4).putInt(50).array()); + byte[] test = data.toByteArray(); + + // Store corrupt data in on-device storage. Add fake timestamp to filename + // to match format expected by FileRotator. + File onDeviceStorageFile = new File(mDataStorageDir, RESIDENCY_FILENAME + ".1234-2234"); + FileOutputStream onDeviceStorageFos = new FileOutputStream(onDeviceStorageFile); + onDeviceStorageFos.write(data.toByteArray()); + onDeviceStorageFos.close(); + + // Write on-device storage to an incident report. + File incidentReport = new File(mDataStorageDir, PROTO_OUTPUT_FILENAME); + FileOutputStream incidentReportFos = new FileOutputStream(incidentReport); + mPowerStatsLogger.writeResidencyDataToFile(incidentReportFos.getFD()); + + // Read the incident report in to a byte array. + FileInputStream fis = new FileInputStream(incidentReport); + byte[] fileContent = new byte[(int) incidentReport.length()]; + fis.read(fileContent); + + // Parse the incident data into a PowerStatsServiceResidencyProto object. + PowerStatsServiceResidencyProto pssProto = + PowerStatsServiceResidencyProto.parseFrom(fileContent); + + // Valid powerEntityInfo data is written to the incident report in the call to + // mPowerStatsLogger.writeResidencyDataToFile(). + assertTrue(pssProto.powerEntityInfo.length == POWER_ENTITY_COUNT); + for (int i = 0; i < pssProto.powerEntityInfo.length; i++) { + PowerEntityInfoProto powerEntityInfo = pssProto.powerEntityInfo[i]; + assertTrue(powerEntityInfo.powerEntityId == i); + assertTrue(powerEntityInfo.powerEntityName.equals(POWER_ENTITY_NAME + i)); + for (int j = 0; j < powerEntityInfo.states.length; j++) { + StateInfoProto stateInfo = powerEntityInfo.states[j]; + assertTrue(stateInfo.stateId == j); + assertTrue(stateInfo.stateName.equals(STATE_NAME + j)); + } + } + + // No stateResidencyResults should be written to the incident report since the + // input buffer had only length and no data. + assertTrue(pssProto.stateResidencyResult.length == 0); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index fee848b1a10d..e8045e06d9ff 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -720,7 +720,6 @@ public class ActivityStarterTests extends WindowTestsBase { } // caller is instrumenting with background activity starts privileges callerApp.setInstrumenting(callerIsInstrumentingWithBackgroundActivityStartPrivileges, - callerIsInstrumentingWithBackgroundActivityStartPrivileges ? Process.SHELL_UID : -1, callerIsInstrumentingWithBackgroundActivityStartPrivileges); // callingUid is the device owner doReturn(isCallingUidDeviceOwner).when(mAtm).isDeviceOwner(callingUid); 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 475e462bdd3d..009c011105de 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java @@ -31,13 +31,17 @@ 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.assertNull; +import static org.junit.Assert.assertTrue; 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 static org.mockito.Mockito.timeout; import android.app.WaitResult; +import android.content.ComponentName; import android.content.pm.ActivityInfo; +import android.os.ConditionVariable; import android.platform.test.annotations.Presubmit; import android.view.Display; @@ -58,6 +62,7 @@ import java.util.concurrent.TimeUnit; @Presubmit @RunWith(WindowTestRunner.class) public class ActivityTaskSupervisorTests extends WindowTestsBase { + private static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10); /** * Ensures that an activity is removed from the stopping activities list once it is resumed. @@ -74,30 +79,72 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase { } /** - * Ensures that waiting results are notified of launches. + * Assume an activity has been started with result code START_SUCCESS. And before it is drawn, + * it launches another existing activity. This test ensures that waiting results are notified + * or updated while the result code of next launch is TASK_TO_FRONT or DELIVERED_TO_TOP. */ @Test - public void testReportWaitingActivityLaunchedIfNeeded() { + public void testReportWaitingActivityLaunched() { final ActivityRecord firstActivity = new ActivityBuilder(mAtm) .setCreateTask(true).build(); - + final ActivityRecord secondActivity = new ActivityBuilder(mAtm) + .setCreateTask(true).build(); + final ConditionVariable condition = new ConditionVariable(); final WaitResult taskToFrontWait = new WaitResult(); - mSupervisor.mWaitingActivityLaunched.add(taskToFrontWait); - // #notifyAll will be called on the ActivityTaskManagerService#mGlobalLock. The lock is hold - // implicitly by WindowManagerGlobalLockRule. - mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity, START_TASK_TO_FRONT); - - assertThat(mSupervisor.mWaitingActivityLaunched).isEmpty(); + final ComponentName[] launchedComponent = { null }; + // Create a new thread so the waiting method in test can be notified. + new Thread(() -> { + synchronized (mAtm.mGlobalLock) { + // Note that TASK_TO_FRONT doesn't unblock the waiting thread. + mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity, + START_TASK_TO_FRONT); + launchedComponent[0] = taskToFrontWait.who; + // Assume that another task is brought to front because first activity launches it. + mSupervisor.reportActivityLaunched(false /* timeout */, secondActivity, + 100 /* totalTime */, WaitResult.LAUNCH_STATE_HOT); + } + condition.open(); + }).start(); + final ActivityMetricsLogger.LaunchingState launchingState = + new ActivityMetricsLogger.LaunchingState(); + spyOn(launchingState); + doReturn(true).when(launchingState).contains(eq(secondActivity)); + // The test case already runs inside global lock, so above thread can only execute after + // this waiting method that releases the lock. + mSupervisor.waitActivityVisibleOrLaunched(taskToFrontWait, firstActivity, launchingState); + + // Assert that the thread is finished. + assertTrue(condition.block(TIMEOUT_MS)); assertEquals(taskToFrontWait.result, START_TASK_TO_FRONT); - assertNull(taskToFrontWait.who); + assertEquals(taskToFrontWait.who, secondActivity.mActivityComponent); + assertEquals(taskToFrontWait.launchState, WaitResult.LAUNCH_STATE_HOT); + // START_TASK_TO_FRONT means that another component will be visible, so the component + // should not be assigned as the first activity. + assertNull(launchedComponent[0]); + condition.close(); final WaitResult deliverToTopWait = new WaitResult(); - mSupervisor.mWaitingActivityLaunched.add(deliverToTopWait); - mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity, START_DELIVERED_TO_TOP); - - assertThat(mSupervisor.mWaitingActivityLaunched).isEmpty(); + new Thread(() -> { + synchronized (mAtm.mGlobalLock) { + // Put a noise which isn't tracked by the current wait result. The waiting procedure + // should ignore it and keep waiting for the target activity. + mSupervisor.reportActivityLaunched(false /* timeout */, mock(ActivityRecord.class), + 1000 /* totalTime */, WaitResult.LAUNCH_STATE_COLD); + // Assume that the first activity launches an existing top activity, so the waiting + // thread should be unblocked. + mSupervisor.reportWaitingActivityLaunchedIfNeeded(secondActivity, + START_DELIVERED_TO_TOP); + } + condition.open(); + }).start(); + mSupervisor.waitActivityVisibleOrLaunched(deliverToTopWait, firstActivity, launchingState); + + assertTrue(condition.block(TIMEOUT_MS)); assertEquals(deliverToTopWait.result, START_DELIVERED_TO_TOP); - assertEquals(deliverToTopWait.who, firstActivity.mActivityComponent); + assertEquals(deliverToTopWait.who, secondActivity.mActivityComponent); + // The result state must be unknown because DELIVERED_TO_TOP means that the target activity + // is already visible so there is no valid launch time. + assertEquals(deliverToTopWait.launchState, WaitResult.LAUNCH_STATE_UNKNOWN); } /** @@ -202,7 +249,6 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase { public void testStartHomeAfterUserUnlocked() { mSupervisor.onUserUnlocked(0); waitHandlerIdle(mAtm.mH); - verify(mRootWindowContainer, timeout(TimeUnit.SECONDS.toMillis(10))) - .startHomeOnEmptyDisplays("userUnlocked"); + verify(mRootWindowContainer, timeout(TIMEOUT_MS)).startHomeOnEmptyDisplays("userUnlocked"); } } diff --git a/services/translation/Android.bp b/services/translation/Android.bp new file mode 100644 index 000000000000..804a6177e94e --- /dev/null +++ b/services/translation/Android.bp @@ -0,0 +1,13 @@ +filegroup { + name: "services.translation-sources", + srcs: ["java/**/*.java"], + path: "java", + visibility: ["//frameworks/base/services"], +} + +java_library_static { + name: "services.translation", + defaults: ["platform_service_defaults"], + srcs: [":services.translation-sources"], + libs: ["services.core"], +}
\ No newline at end of file diff --git a/services/translation/OWNERS b/services/translation/OWNERS new file mode 100644 index 000000000000..a1e663aa8ff7 --- /dev/null +++ b/services/translation/OWNERS @@ -0,0 +1,8 @@ +# Bug component: 994311 + +adamhe@google.com +augale@google.com +joannechung@google.com +lpeter@google.com +svetoslavganov@google.com +tymtsai@google.com diff --git a/services/translation/java/com/android/server/translation/RemoteTranslationService.java b/services/translation/java/com/android/server/translation/RemoteTranslationService.java new file mode 100644 index 000000000000..0c7e61765501 --- /dev/null +++ b/services/translation/java/com/android/server/translation/RemoteTranslationService.java @@ -0,0 +1,87 @@ +/* + * 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.translation; + +import android.annotation.NonNull; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.service.translation.ITranslationService; +import android.service.translation.TranslationService; +import android.util.Slog; +import android.view.translation.TranslationSpec; + +import com.android.internal.infra.AbstractRemoteService; +import com.android.internal.infra.ServiceConnector; +import com.android.internal.os.IResultReceiver; + +final class RemoteTranslationService extends ServiceConnector.Impl<ITranslationService> { + + private static final String TAG = RemoteTranslationService.class.getSimpleName(); + + // TODO(b/176590870): Make PERMANENT now. + private static final long TIMEOUT_IDLE_UNBIND_MS = + AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS; + private static final int TIMEOUT_REQUEST_MS = 5_000; + + private final long mIdleUnbindTimeoutMs; + private final int mRequestTimeoutMs; + private final ComponentName mComponentName; + + RemoteTranslationService(Context context, ComponentName serviceName, + int userId, boolean bindInstantServiceAllowed) { + super(context, + new Intent(TranslationService.SERVICE_INTERFACE).setComponent(serviceName), + bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0, + userId, ITranslationService.Stub::asInterface); + mIdleUnbindTimeoutMs = TIMEOUT_IDLE_UNBIND_MS; + mRequestTimeoutMs = TIMEOUT_REQUEST_MS; + mComponentName = serviceName; + + // Bind right away. + connect(); + } + + public ComponentName getComponentName() { + return mComponentName; + } + + @Override // from ServiceConnector.Impl + protected void onServiceConnectionStatusChanged(ITranslationService service, + boolean connected) { + try { + if (connected) { + service.onConnected(); + } else { + service.onDisconnected(); + } + } catch (Exception e) { + Slog.w(TAG, + "Exception calling onServiceConnectionStatusChanged(" + connected + "): ", e); + } + } + + @Override // from AbstractRemoteService + protected long getAutoDisconnectTimeoutMs() { + return mIdleUnbindTimeoutMs; + } + + public void onSessionCreated(@NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec destSpec, int sessionId, IResultReceiver resultReceiver) { + run((s) -> s.onCreateTranslationSession(sourceSpec, destSpec, sessionId, resultReceiver)); + } +} diff --git a/services/translation/java/com/android/server/translation/TranslationManagerService.java b/services/translation/java/com/android/server/translation/TranslationManagerService.java new file mode 100644 index 000000000000..e2aabe6a89ea --- /dev/null +++ b/services/translation/java/com/android/server/translation/TranslationManagerService.java @@ -0,0 +1,92 @@ +/* + * 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.translation; + +import static android.content.Context.TRANSLATION_MANAGER_SERVICE; +import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_FAIL; + +import android.content.Context; +import android.os.RemoteException; +import android.util.Slog; +import android.view.translation.ITranslationManager; +import android.view.translation.TranslationSpec; + +import com.android.internal.os.IResultReceiver; +import com.android.server.infra.AbstractMasterSystemService; +import com.android.server.infra.FrameworkResourcesServiceNameResolver; + +/** + * Entry point service for translation management. + * + * <p>This service provides the {@link ITranslationManager} implementation and keeps a list of + * {@link TranslationManagerServiceImpl} per user; the real work is done by + * {@link TranslationManagerServiceImpl} itself. + */ +public final class TranslationManagerService + extends AbstractMasterSystemService<TranslationManagerService, + TranslationManagerServiceImpl> { + + private static final String TAG = "TranslationManagerService"; + + public TranslationManagerService(Context context) { + // TODO: Discuss the disallow policy + super(context, new FrameworkResourcesServiceNameResolver(context, + com.android.internal.R.string.config_defaultTranslationService), + /* disallowProperty */ null, PACKAGE_UPDATE_POLICY_REFRESH_EAGER); + } + + @Override + protected TranslationManagerServiceImpl newServiceLocked(int resolvedUserId, boolean disabled) { + return new TranslationManagerServiceImpl(this, mLock, resolvedUserId, disabled); + } + + final class TranslationManagerServiceStub extends ITranslationManager.Stub { + @Override + public void getSupportedLocales(IResultReceiver receiver, int userId) + throws RemoteException { + synchronized (mLock) { + final TranslationManagerServiceImpl service = getServiceForUserLocked(userId); + if (service != null) { + service.getSupportedLocalesLocked(receiver); + } else { + Slog.v(TAG, "getSupportedLocales(): no service for " + userId); + receiver.send(STATUS_SYNC_CALL_FAIL, null); + } + } + } + + @Override + public void onSessionCreated(TranslationSpec sourceSpec, TranslationSpec destSpec, + int sessionId, IResultReceiver receiver, int userId) throws RemoteException { + synchronized (mLock) { + final TranslationManagerServiceImpl service = getServiceForUserLocked(userId); + if (service != null) { + service.onSessionCreatedLocked(sourceSpec, destSpec, sessionId, receiver); + } else { + Slog.v(TAG, "onSessionCreated(): no service for " + userId); + receiver.send(STATUS_SYNC_CALL_FAIL, null); + } + } + } + } + + @Override // from SystemService + public void onStart() { + publishBinderService(TRANSLATION_MANAGER_SERVICE, + new TranslationManagerService.TranslationManagerServiceStub()); + } +} diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java new file mode 100644 index 000000000000..b1f6f80d4158 --- /dev/null +++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java @@ -0,0 +1,125 @@ +/* + * 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.translation; + +import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_SUCCESS; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.os.RemoteException; +import android.service.translation.TranslationServiceInfo; +import android.util.Slog; +import android.view.translation.TranslationSpec; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.IResultReceiver; +import com.android.internal.util.SyncResultReceiver; +import com.android.server.infra.AbstractPerUserSystemService; + +import java.util.ArrayList; + +final class TranslationManagerServiceImpl extends + AbstractPerUserSystemService<TranslationManagerServiceImpl, TranslationManagerService> { + + private static final String TAG = "TranslationManagerServiceImpl"; + + @GuardedBy("mLock") + @Nullable + private RemoteTranslationService mRemoteTranslationService; + + @GuardedBy("mLock") + @Nullable + private ServiceInfo mRemoteTranslationServiceInfo; + + protected TranslationManagerServiceImpl( + @NonNull TranslationManagerService master, + @NonNull Object lock, int userId, boolean disabled) { + super(master, lock, userId); + updateRemoteServiceLocked(); + } + + @GuardedBy("mLock") + @Override // from PerUserSystemService + protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent) + throws PackageManager.NameNotFoundException { + final TranslationServiceInfo info = new TranslationServiceInfo(getContext(), + serviceComponent, isTemporaryServiceSetLocked(), mUserId); + mRemoteTranslationServiceInfo = info.getServiceInfo(); + return info.getServiceInfo(); + } + + @GuardedBy("mLock") + @Override // from PerUserSystemService + protected boolean updateLocked(boolean disabled) { + final boolean enabledChanged = super.updateLocked(disabled); + updateRemoteServiceLocked(); + return enabledChanged; + } + + /** + * Updates the reference to the remote service. + */ + @GuardedBy("mLock") + private void updateRemoteServiceLocked() { + if (mRemoteTranslationService != null) { + if (mMaster.debug) Slog.d(TAG, "updateRemoteService(): destroying old remote service"); + mRemoteTranslationService.unbind(); + mRemoteTranslationService = null; + } + } + + @GuardedBy("mLock") + @Nullable + private RemoteTranslationService ensureRemoteServiceLocked() { + if (mRemoteTranslationService == null) { + final String serviceName = getComponentNameLocked(); + if (serviceName == null) { + if (mMaster.verbose) { + Slog.v(TAG, "ensureRemoteServiceLocked(): no service component name."); + } + return null; + } + final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName); + mRemoteTranslationService = new RemoteTranslationService(getContext(), + serviceComponent, mUserId, /* isInstantAllowed= */ false); + } + return mRemoteTranslationService; + } + + @GuardedBy("mLock") + void getSupportedLocalesLocked(@NonNull IResultReceiver resultReceiver) { + // TODO: implement this + try { + resultReceiver.send(STATUS_SYNC_CALL_SUCCESS, + SyncResultReceiver.bundleFor(new ArrayList<>())); + } catch (RemoteException e) { + Slog.w(TAG, "RemoteException returning supported locales: " + e); + } + } + + @GuardedBy("mLock") + void onSessionCreatedLocked(@NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec destSpec, int sessionId, IResultReceiver resultReceiver) { + final RemoteTranslationService remoteService = ensureRemoteServiceLocked(); + if (remoteService != null) { + remoteService.onSessionCreated(sourceSpec, destSpec, sessionId, resultReceiver); + } + } +} diff --git a/tests/net/Android.bp b/tests/net/Android.bp index a7622198cec7..f6a2846c9b3c 100644 --- a/tests/net/Android.bp +++ b/tests/net/Android.bp @@ -70,4 +70,7 @@ android_test { "android.test.base", "android.test.mock", ], + jni_libs: [ + "libservice-connectivity", + ], } diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 3433880f3c0e..e318207cdc75 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -1199,6 +1199,8 @@ public class ConnectivityServiceTest { updateState(NetworkInfo.DetailedState.DISCONNECTED, "disconnect"); } mAgentRegistered = false; + setUids(null); + mInterface = null; } @Override @@ -3624,51 +3626,55 @@ public class ConnectivityServiceTest { // Register the factory and expect it to start looking for a network. testFactory.expectAddRequestsWithScores(0); // Score 0 as the request is not served yet. testFactory.register(); - testFactory.waitForNetworkRequests(1); - assertTrue(testFactory.getMyStartRequested()); - // Bring up wifi. The factory stops looking for a network. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - // Score 60 - 40 penalty for not validated yet, then 60 when it validates - testFactory.expectAddRequestsWithScores(20, 60); - mWiFiNetworkAgent.connect(true); - testFactory.waitForRequests(); - assertFalse(testFactory.getMyStartRequested()); - - ContentResolver cr = mServiceContext.getContentResolver(); - - // Turn on mobile data always on. The factory starts looking again. - testFactory.expectAddRequestsWithScores(0); // Always on requests comes up with score 0 - setAlwaysOnNetworks(true); - testFactory.waitForNetworkRequests(2); - assertTrue(testFactory.getMyStartRequested()); - - // Bring up cell data and check that the factory stops looking. - assertLength(1, mCm.getAllNetworks()); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - testFactory.expectAddRequestsWithScores(10, 50); // Unvalidated, then validated - mCellNetworkAgent.connect(true); - cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - testFactory.waitForNetworkRequests(2); - assertFalse(testFactory.getMyStartRequested()); // Because the cell network outscores us. + try { + testFactory.waitForNetworkRequests(1); + assertTrue(testFactory.getMyStartRequested()); - // Check that cell data stays up. - waitForIdle(); - verifyActiveNetwork(TRANSPORT_WIFI); - assertLength(2, mCm.getAllNetworks()); + // Bring up wifi. The factory stops looking for a network. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + // Score 60 - 40 penalty for not validated yet, then 60 when it validates + testFactory.expectAddRequestsWithScores(20, 60); + mWiFiNetworkAgent.connect(true); + testFactory.waitForRequests(); + assertFalse(testFactory.getMyStartRequested()); - // Turn off mobile data always on and expect the request to disappear... - testFactory.expectRemoveRequests(1); - setAlwaysOnNetworks(false); - testFactory.waitForNetworkRequests(1); + ContentResolver cr = mServiceContext.getContentResolver(); + + // Turn on mobile data always on. The factory starts looking again. + testFactory.expectAddRequestsWithScores(0); // Always on requests comes up with score 0 + setAlwaysOnNetworks(true); + testFactory.waitForNetworkRequests(2); + assertTrue(testFactory.getMyStartRequested()); + + // Bring up cell data and check that the factory stops looking. + assertLength(1, mCm.getAllNetworks()); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + testFactory.expectAddRequestsWithScores(10, 50); // Unvalidated, then validated + mCellNetworkAgent.connect(true); + cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + testFactory.waitForNetworkRequests(2); + assertFalse( + testFactory.getMyStartRequested()); // Because the cell network outscores us. + + // Check that cell data stays up. + waitForIdle(); + verifyActiveNetwork(TRANSPORT_WIFI); + assertLength(2, mCm.getAllNetworks()); - // ... and cell data to be torn down. - cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - assertLength(1, mCm.getAllNetworks()); + // Turn off mobile data always on and expect the request to disappear... + testFactory.expectRemoveRequests(1); + setAlwaysOnNetworks(false); + testFactory.waitForNetworkRequests(1); - testFactory.terminate(); - mCm.unregisterNetworkCallback(cellNetworkCallback); - handlerThread.quit(); + // ... and cell data to be torn down. + cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + assertLength(1, mCm.getAllNetworks()); + } finally { + testFactory.terminate(); + mCm.unregisterNetworkCallback(cellNetworkCallback); + handlerThread.quit(); + } } @Test @@ -6176,11 +6182,15 @@ public class ConnectivityServiceTest { // Create a fake restricted profile whose parent is our user ID. final int userId = UserHandle.getUserId(uid); + when(mUserManager.canHaveRestrictedProfile(userId)).thenReturn(true); 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); + when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, restrictedUserId)) + .thenReturn(UserHandle.getUid(restrictedUserId, VPN_UID)); + final Intent addedIntent = new Intent(ACTION_USER_ADDED); addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, restrictedUserId); @@ -6220,6 +6230,54 @@ public class ConnectivityServiceTest { && caps.getUids().contains(new UidRange(uid, uid)) && caps.hasTransport(TRANSPORT_VPN) && !caps.hasTransport(TRANSPORT_WIFI)); + + // Test lockdown with restricted profiles. + mServiceContext.setPermission( + Manifest.permission.CONTROL_ALWAYS_ON_VPN, PERMISSION_GRANTED); + mServiceContext.setPermission( + Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED); + mServiceContext.setPermission( + Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED); + + // Connect wifi and check that UIDs in the main and restricted profiles have network access. + mMockVpn.disconnect(); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true /* validated */); + final int restrictedUid = UserHandle.getUid(restrictedUserId, 42 /* appId */); + assertNotNull(mCm.getActiveNetworkForUid(uid)); + assertNotNull(mCm.getActiveNetworkForUid(restrictedUid)); + + // Enable always-on VPN lockdown. The main user loses network access because no VPN is up. + final ArrayList<String> allowList = new ArrayList<>(); + mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList); + waitForIdle(); + assertNull(mCm.getActiveNetworkForUid(uid)); + assertNotNull(mCm.getActiveNetworkForUid(restrictedUid)); + + // Start the restricted profile, and check that the UID within it loses network access. + when(mUserManager.getAliveUsers()).thenReturn( + Arrays.asList(new UserInfo[] { + new UserInfo(userId, "", 0), + info + })); + // TODO: check that VPN app within restricted profile still has access, etc. + handler.post(() -> mServiceContext.sendBroadcast(addedIntent)); + waitForIdle(); + assertNull(mCm.getActiveNetworkForUid(uid)); + assertNull(mCm.getActiveNetworkForUid(restrictedUid)); + + // Stop the restricted profile, and check that the UID within it has network access again. + when(mUserManager.getAliveUsers()).thenReturn( + Arrays.asList(new UserInfo[] { + new UserInfo(userId, "", 0), + })); + handler.post(() -> mServiceContext.sendBroadcast(removedIntent)); + waitForIdle(); + assertNull(mCm.getActiveNetworkForUid(uid)); + assertNotNull(mCm.getActiveNetworkForUid(restrictedUid)); + + mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList); + waitForIdle(); } @Test diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index 3648c4db1e16..02a2aadc4c79 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -339,14 +339,8 @@ public class VpnTest { final Vpn vpn = createVpn(primaryUser.id); final UidRange user = PRI_USER_RANGE; - // Default state. - assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], - user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]); - // Set always-on without lockdown. assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null, mKeyStore)); - assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], - user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]); // Set always-on with lockdown. assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null, mKeyStore)); @@ -355,10 +349,6 @@ public class VpnTest { new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop) })); - assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[2], - user.start + PKG_UIDS[3]); - assertUnblocked(vpn, user.start + PKG_UIDS[1]); - // Switch to another app. assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore)); verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] { @@ -369,9 +359,6 @@ public class VpnTest { new UidRangeParcel(user.start, user.start + PKG_UIDS[3] - 1), new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop) })); - assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], - user.start + PKG_UIDS[2]); - assertUnblocked(vpn, user.start + PKG_UIDS[3]); } @Test @@ -386,8 +373,6 @@ public class VpnTest { new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1), new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop) })); - assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]); - assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]); // Change allowed app list to PKGS[3]. assertTrue(vpn.setAlwaysOnPackage( PKGS[1], true, Collections.singletonList(PKGS[3]), mKeyStore)); @@ -398,8 +383,6 @@ public class VpnTest { new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1), new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop) })); - assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[2]); - assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[3]); // Change the VPN app. assertTrue(vpn.setAlwaysOnPackage( @@ -412,8 +395,6 @@ public class VpnTest { new UidRangeParcel(user.start, user.start + PKG_UIDS[0] - 1), new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1) })); - assertBlocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]); - assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]); // Remove the list of allowed packages. assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null, mKeyStore)); @@ -424,9 +405,6 @@ public class VpnTest { verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.stop), })); - assertBlocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], - user.start + PKG_UIDS[3]); - assertUnblocked(vpn, user.start + PKG_UIDS[0]); // Add the list of allowed packages. assertTrue(vpn.setAlwaysOnPackage( @@ -438,8 +416,6 @@ public class VpnTest { new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1), new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop) })); - assertBlocked(vpn, user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]); - assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1]); // Try allowing a package with a comma, should be rejected. assertFalse(vpn.setAlwaysOnPackage( @@ -460,45 +436,6 @@ public class VpnTest { } @Test - public void testLockdownAddingAProfile() throws Exception { - final Vpn vpn = createVpn(primaryUser.id); - setMockedUsers(primaryUser); - - // Make a copy of the restricted profile, as we're going to mark it deleted halfway through. - final UserInfo tempProfile = new UserInfo(restrictedProfileA.id, restrictedProfileA.name, - restrictedProfileA.flags); - tempProfile.restrictedProfileParentId = primaryUser.id; - - final UidRange user = PRI_USER_RANGE; - final UidRange profile = UidRange.createForUser(tempProfile.id); - - // Set lockdown. - assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore)); - verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { - new UidRangeParcel(user.start, user.start + PKG_UIDS[3] - 1), - new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop) - })); - // Verify restricted user isn't affected at first. - assertUnblocked(vpn, profile.start + PKG_UIDS[0]); - - // Add the restricted user. - setMockedUsers(primaryUser, tempProfile); - vpn.onUserAdded(tempProfile.id); - verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { - new UidRangeParcel(profile.start, profile.start + PKG_UIDS[3] - 1), - new UidRangeParcel(profile.start + PKG_UIDS[3] + 1, profile.stop) - })); - - // Remove the restricted user. - tempProfile.partial = true; - vpn.onUserRemoved(tempProfile.id); - verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] { - new UidRangeParcel(profile.start, profile.start + PKG_UIDS[3] - 1), - new UidRangeParcel(profile.start + PKG_UIDS[3] + 1, profile.stop) - })); - } - - @Test public void testLockdownRuleRepeatability() throws Exception { final Vpn vpn = createVpn(primaryUser.id); final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] { @@ -1207,20 +1144,6 @@ public class VpnTest { return vpn; } - private static void assertBlocked(Vpn vpn, int... uids) { - for (int uid : uids) { - final boolean blocked = vpn.getLockdown() && vpn.isBlockingUid(uid); - assertTrue("Uid " + uid + " should be blocked", blocked); - } - } - - private static void assertUnblocked(Vpn vpn, int... uids) { - for (int uid : uids) { - final boolean blocked = vpn.getLockdown() && vpn.isBlockingUid(uid); - assertFalse("Uid " + uid + " should not be blocked", blocked); - } - } - /** * Populate {@link #mUserManager} with a list of fake users. */ diff --git a/tools/powerstats/PowerStatsServiceProtoParser.java b/tools/powerstats/PowerStatsServiceProtoParser.java index 76edd6341b55..97a2a402b537 100644 --- a/tools/powerstats/PowerStatsServiceProtoParser.java +++ b/tools/powerstats/PowerStatsServiceProtoParser.java @@ -90,6 +90,38 @@ public class PowerStatsServiceProtoParser { } } + private static void printPowerEntityInfo(PowerStatsServiceResidencyProto proto) { + String csvHeader = new String(); + for (int i = 0; i < proto.getPowerEntityInfoCount(); i++) { + PowerEntityInfoProto powerEntityInfo = proto.getPowerEntityInfo(i); + csvHeader += powerEntityInfo.getPowerEntityId() + "," + + powerEntityInfo.getPowerEntityName() + ","; + for (int j = 0; j < powerEntityInfo.getStatesCount(); j++) { + StateInfoProto stateInfo = powerEntityInfo.getStates(j); + csvHeader += stateInfo.getStateId() + "," + stateInfo.getStateName() + ","; + } + } + System.out.println(csvHeader); + } + + private static void printStateResidencyResult(PowerStatsServiceResidencyProto proto) { + for (int i = 0; i < proto.getStateResidencyResultCount(); i++) { + String csvRow = new String(); + + StateResidencyResultProto stateResidencyResult = proto.getStateResidencyResult(i); + csvRow += stateResidencyResult.getPowerEntityId() + ","; + + for (int j = 0; j < stateResidencyResult.getStateResidencyDataCount(); j++) { + StateResidencyProto stateResidency = stateResidencyResult.getStateResidencyData(j); + csvRow += stateResidency.getStateId() + "," + + stateResidency.getTotalTimeInStateMs() + "," + + stateResidency.getTotalStateEntryCount() + "," + + stateResidency.getLastEntryTimestampMs() + ","; + } + System.out.println(csvRow); + } + } + private static void generateCsvFile(String pathToIncidentReport) { try { // Print power meter data. @@ -115,6 +147,21 @@ public class PowerStatsServiceProtoParser { } else { System.out.println("Model incident report not found. Exiting."); } + + // Print state residency data. + IncidentReportResidencyProto irResidencyProto = + IncidentReportResidencyProto.parseFrom( + new FileInputStream(pathToIncidentReport)); + + if (irResidencyProto.hasIncidentReport()) { + PowerStatsServiceResidencyProto pssResidencyProto = + irResidencyProto.getIncidentReport(); + printPowerEntityInfo(pssResidencyProto); + printStateResidencyResult(pssResidencyProto); + } else { + System.out.println("Residency incident report not found. Exiting."); + } + } catch (IOException e) { System.out.println("Unable to open incident report file: " + pathToIncidentReport); System.out.println(e); |