Merge "Check updatable font dir in FontListParser."
diff --git a/MULTIUSER_OWNERS b/MULTIUSER_OWNERS
new file mode 100644
index 0000000..fbc611a
--- /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 ae9e7ff..ae32fba 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -100,6 +100,7 @@
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 @@
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 @@
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 @@
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 85207f7..11e7fab 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.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 @@
/** 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 @@
}
/**
+ * 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 8b20c09..43be442 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 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 1c360a6..b9503ee 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 @@
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 5ffa7c9..eb0b7324 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 @@
/** @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 @@
@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 @@
@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 @@
}
/**
+ * 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 400b630..c3f0d8a 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 @@
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 ad3ee05..e9c4cb4 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 @@
/**
* 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 @@
* certificate.
*
* <p>This method is inefficient to call repeatedly.
- * @hide
*/
@NonNull
public Map<String, Set<PackageIdentifier>> getSchemasVisibleToPackages() {
@@ -141,7 +139,6 @@
*
* @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 @@
* @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 704f180..b1a33a4 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 @@
*
* <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 47a81eb..a2126b1 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.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 @@
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 @@
* 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 @@
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 @@
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 @@
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 @@
}
/** 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 5474cd0..a2386ec 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 @@
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 4d107a9..ccd567d 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.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 @@
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 2b1ec08..73f64dc 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 9e22bf6..6859747 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 @@
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 8383df4..e439c5a 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 @@
* 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 @@
* 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 @@
* <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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
/**
* 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 33dc379..2d09247 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 @@
* 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 f387a17..328c65c 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.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 @@
/**
* 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 2ce0b35..32134fc 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7135,7 +7135,7 @@
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 +7186,7 @@
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 +7492,7 @@
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 +7827,52 @@
}
+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 +10362,7 @@
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
@@ -48717,6 +48765,7 @@
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);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 9a15e77..473a280 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -906,6 +906,7 @@
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 +1371,6 @@
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);
@@ -8618,6 +8617,7 @@
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";
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index f0a0297..c03461a 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -18,6 +18,7 @@
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 @@
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.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 55df824..55b858e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5151,12 +5151,6 @@
* #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 697a377..bda2fa9 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -228,4 +228,6 @@
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 e56274a..5fbc948 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -102,6 +102,7 @@
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 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 @@
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 @@
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 @@
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 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 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 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 @@
.viewType(StandardTemplateParams.VIEW_TYPE_HEADS_UP)
.fillTextsFrom(this)
.setMaxRemoteInputHistory(1);
- return applyStandardTemplateWithActions(getHeadsUpBaseLayoutResource(),
- p,
+ return applyStandardTemplateWithActions(getHeadsUpBaseLayoutResource(), p,
null /* result */);
}
@@ -6596,11 +6683,7 @@
*/
@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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
* @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 @@
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 58f382d..6cce270 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1730,6 +1730,23 @@
}
/**
+ * 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 06ad9c9..c804e642 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 2ce0e87..050d194 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -29,7 +29,9 @@
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 @@
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 @@
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 806cb49..7bdcbc1 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -306,7 +306,13 @@
* 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 +604,12 @@
* 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 +667,9 @@
* 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 +683,10 @@
*
* <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 +713,9 @@
* 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 +1192,15 @@
"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 +1257,8 @@
@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 +1385,15 @@
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 +2471,16 @@
* <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 +2529,7 @@
* @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 +2571,11 @@
* 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";
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 0f7fac4..65a2164 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -672,6 +672,7 @@
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 @@
String mWebDomain;
Bundle mExtras;
LocaleList mLocaleList;
+ String[] mOnReceiveContentMimeTypes;
ViewNode[] mChildren;
@@ -880,6 +882,9 @@
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 @@
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 @@
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 @@
}
/**
+ * 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 @@
}
@Override
+ public void setOnReceiveContentMimeTypes(@Nullable String[] mimeTypes) {
+ mNode.mOnReceiveContentMimeTypes = mimeTypes;
+ }
+
+ @Override
public void setInputType(int inputType) {
mNode.mInputType = inputType;
}
@@ -2422,6 +2447,10 @@
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 0000000..acfe135
--- /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 0000000..d2a0255
--- /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 c547ef1..0d12ed0 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 @@
* 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 0000000..de7ba62
--- /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 ba1f612..8dde2c5 100644
--- a/core/java/android/app/role/RoleControllerManager.java
+++ b/core/java/android/app/role/RoleControllerManager.java
@@ -20,6 +20,8 @@
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 @@
*
* @hide
*/
+@SystemService(Context.ROLE_CONTROLLER_SERVICE)
+@TestApi
public class RoleControllerManager {
private static final String LOG_TAG = RoleControllerManager.class.getSimpleName();
@@ -195,11 +199,32 @@
}
/**
+ * @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 @@
* @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 9ddd5be..8b2e07b 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -174,9 +174,6 @@
@NonNull
private final Object mListenersLock = new Object();
- @NonNull
- private final RoleControllerManager mRoleControllerManager;
-
/**
* @hide
*/
@@ -184,7 +181,6 @@
mContext = context;
mService = IRoleManager.Stub.asInterface(ServiceManager.getServiceOrThrow(
Context.ROLE_SERVICE));
- mRoleControllerManager = new RoleControllerManager(context);
}
/**
@@ -680,44 +676,6 @@
}
}
- /**
- * 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 7a6ff79..381318b 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -824,6 +824,25 @@
* 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 @@
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 13b1b4f..088b016 100644
--- a/core/java/android/bluetooth/BluetoothGattServer.java
+++ b/core/java/android/bluetooth/BluetoothGattServer.java
@@ -443,6 +443,25 @@
* 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 @@
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 3b4fe0a..d5c1c3e 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -225,6 +225,24 @@
*
* @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 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 @@
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 33f9607..43011fc 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -40,6 +40,7 @@
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;
@@ -4821,6 +4822,16 @@
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 +5312,10 @@
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/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index b3b2dd8..8497525 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -20,7 +20,6 @@
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 @@
* 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 @@
}
try {
- mService.addIndividualSensorPrivacyListener(userId, sensor, iListener);
+ mService.addIndividualSensorPrivacyListener(mContext.getUserId(), sensor,
+ iListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -230,10 +229,9 @@
*
* @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 @@
/**
* 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 @@
/**
* 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 33527f8..be10df1 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/permission/PermGroupUsage.java b/core/java/android/permission/PermGroupUsage.java
index 3bee401..7335e00 100644
--- a/core/java/android/permission/PermGroupUsage.java
+++ b/core/java/android/permission/PermGroupUsage.java
@@ -30,17 +30,19 @@
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 @@
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 4868827..00ba867 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -187,7 +187,7 @@
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 ec4d81c..7a856e8 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -101,6 +101,13 @@
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 69ca28a..9db7ca0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14630,6 +14630,34 @@
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/view/View.java b/core/java/android/view/View.java
index 0f53215..e8584da 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8821,6 +8821,7 @@
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 d863ea5..0097b2e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3150,6 +3150,8 @@
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 @@
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();
}
- mPendingTransitions.clear();
+
+ // We may never draw since it's not visible. Report back that we're finished
+ // drawing.
+ if (!wasReportNextDraw && mReportNextDraw) {
+ mReportNextDraw = false;
+ pendingDrawFinished();
+ }
}
+
+ // 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 29ce231..f5aa97a 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -372,6 +372,14 @@
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 e731d4b..4e9c229 100644
--- a/core/java/android/view/contentcapture/ViewNode.java
+++ b/core/java/android/view/contentcapture/ViewNode.java
@@ -81,6 +81,7 @@
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 @@
private String[] mAutofillHints;
private AutofillValue mAutofillValue;
private CharSequence[] mAutofillOptions;
+ private String[] mOnReceiveContentMimeTypes;
/** @hide */
public ViewNode() {
@@ -169,6 +171,9 @@
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 @@
return mAutofillOptions;
}
+ @Override
+ @Nullable
+ public String[] getOnReceiveContentMimeTypes() {
+ return mOnReceiveContentMimeTypes;
+ }
+
@Nullable
@Override
public LocaleList getLocaleList() {
@@ -508,6 +519,9 @@
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 @@
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 @@
}
@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/widget/Editor.java b/core/java/android/widget/Editor.java
index 0025d1e..26dd5e3 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -99,6 +99,7 @@
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 @@
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 @@
}
updateSelection(event);
+ if (mTextView.hasSelection() && mEndHandle != null) {
+ mEndHandle.updateMagnifier(event);
+ }
break;
case MotionEvent.ACTION_UP:
@@ -6623,6 +6637,9 @@
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 220a31c..8dafc5d 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -75,6 +75,8 @@
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 @@
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 @@
}
/**
+ * 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 @@
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 @@
}
/**
+ * 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 @@
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 1599f2b..b866025 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -11717,6 +11717,18 @@
}
}
}
+ 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 8cef106..91fac55 100644
--- a/core/java/android/widget/TextViewOnReceiveContentListener.java
+++ b/core/java/android/widget/TextViewOnReceiveContentListener.java
@@ -60,7 +60,7 @@
*
* @hide
*/
-@VisibleForTesting
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public final class TextViewOnReceiveContentListener implements OnReceiveContentListener {
private static final String LOG_TAG = "ReceiveContent";
@@ -261,10 +261,17 @@
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 b82ba81..d6a4663 100644
--- a/core/java/com/android/internal/inputmethod/Completable.java
+++ b/core/java/com/android/internal/inputmethod/Completable.java
@@ -117,8 +117,8 @@
}
/**
- * @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 @@
}
/**
- * 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 @@
/**
* 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 @@
/**
* 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/OWNERS b/core/java/com/android/internal/os/OWNERS
index 8f78b2a..1b07aa0 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -6,3 +6,5 @@
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/jni/Android.bp b/core/jni/Android.bp
index 8c9da66..94ef64f 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -347,12 +347,6 @@
},
},
- product_variables: {
- experimental_mte: {
- cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
- },
- },
-
// Workaround Clang LTO crash.
lto: {
never: true,
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 8de30f8..944edb0 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -521,6 +521,11 @@
(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 9a7ed7c..30c4274 100644
--- a/core/proto/android/server/powerstatsservice.proto
+++ b/core/proto/android/server/powerstatsservice.proto
@@ -41,6 +41,16 @@
}
/**
+ * 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 @@
}
/**
+ * 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 8682fea..3053518 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5317,6 +5317,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 0006384..41be36b 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 0721f8e..3adb318 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 967bb92..016e77b 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 0df69d9..a8afbbe 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 937645e..b7356e1 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 e7a1628..30c0d03 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 b5935e9..2124d49 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 8d5e974..4518a3b 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 0f5d000..6483db1 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/dimens.xml b/core/res/res/values/dimens.xml
index c326a40..3ae2131 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 2df9684..31b4edd 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" />
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 133efce..aab9229 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 0000000..1e8478e
--- /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/widget/TextViewOnReceiveContentTest.java b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java
index 9978648..9968f79 100644
--- a/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java
@@ -87,7 +87,7 @@
}
@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 @@
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 @@
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/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 e32d3b9..df7c753 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 @@
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/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 602364e..a4abf36 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -196,8 +196,9 @@
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 @@
event->mAvHandleRefCnt--;
event->finalize();
+ event->decStrong(buf);
}
namespace android {
@@ -408,6 +410,7 @@
pC2Buffer->setInfo(info);
}
pC2Buffer->registerOnDestroyNotify(&DestroyCallback, this);
+ incStrong(pC2Buffer.get());
jobject linearBlock =
env->NewObject(
env->FindClass("android/media/MediaCodec$LinearBlock"),
@@ -4411,6 +4414,7 @@
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 a26f715..c8f3bd3 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 @@
srcs: [
"jni/com_android_server_TestNetworkService.cpp",
"jni/com_android_server_connectivity_Vpn.cpp",
+ "jni/onload.cpp",
],
shared_libs: [
"libbase",
@@ -35,27 +36,11 @@
// 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 @@
],
apex_available: [
"//apex_available:platform",
+ "com.android.tethering",
],
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index c1c1d65..e22f264 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -217,6 +217,7 @@
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 @@
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 0a43014..14151191 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 19f8248..55bdebd 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/values-h700dp/dimens.xml b/packages/SystemUI/res/values-h700dp/dimens.xml
new file mode 100644
index 0000000..fbd985e
--- /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 6a0e880..cfacbec 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/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6f69483..8888ad6 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 6cc863a4..2d972e0 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.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 @@
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 @@
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 08262de..eefae5b 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -111,7 +111,8 @@
.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 @@
.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 c289ca2..1036c99 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 @@
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.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 @@
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 @@
mAudioManager = audioManager;
mMicMuted = audioManager.isMicrophoneMute();
mLocationManager = context.getSystemService(LocationManager.class);
+ mPackageManager = context.getPackageManager();
dumpManager.registerDumpable(TAG, this);
}
@@ -334,6 +339,16 @@
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 @@
|| 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 6c28d11..ff55b76d 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.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 @@
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 @@
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 ab82225..7cd7e18 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.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 @@
@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 @@
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 5b11627..e4f9064 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.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 @@
*/
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 @@
private const val EXTRA_USE_PANEL = "controls.DISPLAY_IN_PANEL"
}
- var activityView = ActivityView(context)
+ var detailTaskId = INVALID_TASK_ID
- val stateCallback: ActivityView.StateCallback = object : ActivityView.StateCallback() {
- override fun onActivityViewReady(view: ActivityView) {
+ fun removeDetailTask() {
+ if (detailTaskId == INVALID_TASK_ID) return
+ ActivityTaskManager.getInstance().removeTask(detailTaskId)
+ detailTaskId = INVALID_TASK_ID
+ }
+
+ val stateCallback = object : TaskView.Listener {
+ override fun onInitialized() {
val launchIntent = Intent(intent)
launchIntent.putExtra(EXTRA_USE_PANEL, true)
@@ -60,18 +71,31 @@
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 @@
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 @@
}
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 f9505de..612a559 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.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 @@
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 943a54e..c75dc84 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 @@
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 @@
@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 4efe4d8..f0e4cce 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 @@
@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/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 28ee935..97201f5 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 @@
/** 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 @@
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 6da5d1b9..6cd7a74 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 @@
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 @@
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 @@
}
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/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index b46751d..b0bb3fd 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -40,6 +40,8 @@
import com.android.wm.shell.ShellInit;
import com.android.wm.shell.ShellInitImpl;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.TaskViewFactory;
+import com.android.wm.shell.TaskViewFactoryController;
import com.android.wm.shell.Transitions;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.apppairs.AppPairs;
@@ -60,6 +62,7 @@
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 +71,8 @@
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 java.util.Optional;
-import java.util.concurrent.TimeUnit;
import dagger.BindsOptionalOf;
import dagger.Module;
@@ -342,6 +343,14 @@
@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/services/Android.bp b/services/Android.bp
index 3447b5c..b51e4b0 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -81,7 +81,6 @@
"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 8a27acc..9d02835 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 @@
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 0000000..4068e2b
--- /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/ConnectivityServiceInitializer.java b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
index f701688..0779f71 100644
--- a/services/core/java/com/android/server/ConnectivityServiceInitializer.java
+++ b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
@@ -35,6 +35,8 @@
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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0cbf4ba..c1ab5b6 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.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 @@
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 @@
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/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 669d04a..e90423c 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 @@
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 0000000..9d43a39
--- /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 4804fa3..c2e27e0 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 b8084d5..fe946cb 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.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 @@
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 f512cef..9c6438e 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 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 b6d8892..3d07334 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 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 2c06d82..b5d875d 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 @@
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/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index 06721ae..93930ae 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 @@
* @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/graphics/fonts/OWNERS b/services/core/java/com/android/server/graphics/fonts/OWNERS
new file mode 100644
index 0000000..34ac813
--- /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 2b3c0bf..0d284fc 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 @@
// 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 @@
ControllerImpl.this::onProviderStateChange;
mPrimaryProvider.initialize(providerListener);
mSecondaryProvider.initialize(providerListener);
+ mProvidersInitialized = true;
alterProvidersStartedStateIfRequired(
null /* oldConfiguration */, mCurrentUserConfiguration);
@@ -322,6 +327,16 @@
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/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5ac581a..4ab8279 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 @@
@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 @@
@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 @@
}
@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 @@
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 0000000..48f6bf8
--- /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 004c015..8f42289 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 @@
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 6500523..b35cb52 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 @@
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 c9595c2..6d9cb75 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 @@
* 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 1754185..e3672a8 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 @@
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 409cd82..9ee3429 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.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.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 @@
* 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 @@
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 @@
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 ce50e583..7778572 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 @@
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 @@
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 @@
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 @@
// 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 5e23b86..ab9b3e0 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.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 @@
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 @@
}
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 @@
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 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 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 @@
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 @@
}
}
- private static EnergyMeasurement unpackProtoMessage(ProtoInputStream pis)
+ private static EnergyMeasurement unpackEnergyMeasurementProto(ProtoInputStream pis)
throws IOException {
EnergyMeasurement energyMeasurement = new EnergyMeasurement();
@@ -230,12 +387,10 @@
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 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 @@
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 @@
}
}
- 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 4b59295..7cba00f 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 @@
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 @@
// 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 35c9f9a..3a08ddc 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.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 @@
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 638232d..9088d7b 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -2078,7 +2078,8 @@
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 99fc7c1..1e80c4f 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 @@
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 @@
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 @@
public int pageTablesKb;
public int kernelStackKb;
public int totalIonKb;
+ public int gpuTotalUsageKb;
public int unaccountedKb;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 08e16c4..9ffedde 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 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 9b9af06..461bbfb 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.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 @@
}
}
- /**
- * 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 @@
}
@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 @@
@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/Task.java b/services/core/java/com/android/server/wm/Task.java
index 1c41978..61410f8 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 @@
}
void onPictureInPictureParamsChanged() {
- dispatchTaskInfoChangedIfNeeded(true /* force */);
+ if (inPinnedWindowingMode()) {
+ dispatchTaskInfoChangedIfNeeded(true /* force */);
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0b60deb..4eeae6c 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 @@
}
}
- // 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 @@
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 @@
@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 663d91e..8aa154b 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.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 @@
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 @@
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 @@
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 996462f..13078b6 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -171,7 +171,6 @@
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 85ef394..1893321 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -41,8 +41,6 @@
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 @@
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/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index fdbd85a..3e51b75 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8421,7 +8421,10 @@
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 +9734,8 @@
// 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()) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4d15ced..d9350f3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -237,6 +237,8 @@
"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 @@
}
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 @@
// 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 49a41f0..16b9165 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 @@
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.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.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 @@
private final DataManager mDataManager;
+ private PackageManagerInternal mPackageManagerInternal;
+
/**
* Initializes the system service.
*
@@ -83,6 +90,7 @@
publishBinderService(Context.PEOPLE_SERVICE, mService);
}
publishLocalService(PeopleServiceInternal.class, new LocalService());
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
}
@Override
@@ -112,6 +120,26 @@
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 @@
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 45f389c..16c4c29 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 @@
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.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 @@
@ConversationFlags
private int mConversationFlags;
+ private Map<String, ConversationStatus> mCurrStatuses;
+
private ConversationInfo(Builder builder) {
mShortcutId = builder.mShortcutId;
mLocusId = builder.mLocusId;
@@ -111,6 +118,7 @@
mLastEventTimestamp = builder.mLastEventTimestamp;
mShortcutFlags = builder.mShortcutFlags;
mConversationFlags = builder.mConversationFlags;
+ mCurrStatuses = builder.mCurrStatuses;
}
@NonNull
@@ -213,6 +221,10 @@
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 @@
&& 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 @@
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 @@
protoOutputStream.write(ConversationInfoProto.CONTACT_PHONE_NUMBER,
mContactPhoneNumber);
}
+ // ConversationStatus is a transient object and not persisted
}
@Nullable
@@ -337,6 +352,7 @@
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 @@
@ConversationFlags
private int mConversationFlags;
+ private Map<String, ConversationStatus> mCurrStatuses = new HashMap<>();
+
Builder() {
}
@@ -486,6 +504,7 @@
mLastEventTimestamp = conversationInfo.mLastEventTimestamp;
mShortcutFlags = conversationInfo.mShortcutFlags;
mConversationFlags = conversationInfo.mConversationFlags;
+ mCurrStatuses = conversationInfo.mCurrStatuses;
}
Builder setShortcutId(@NonNull String shortcutId) {
@@ -579,6 +598,26 @@
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 b5e595a..e04e287 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.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.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 @@
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 e779e21..c0f0ce0 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/servicestests/src/com/android/server/apphibernation/OWNERS b/services/tests/servicestests/src/com/android/server/apphibernation/OWNERS
new file mode 100644
index 0000000..c2e27e0
--- /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 f29b059..c6fde87 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 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.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 @@
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 @@
.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 @@
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 7c68c6b..a3f0f6b 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 org.junit.Test;
+import java.util.Collections;
+
public class SnippetTest {
// TODO(tytytyww): Add tests for Double and Long Snippets.
@@ -83,7 +85,8 @@
// 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 @@
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 @@
// 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/graphics/fonts/OWNERS b/services/tests/servicestests/src/com/android/server/graphics/fonts/OWNERS
new file mode 100644
index 0000000..34ac813
--- /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 be8e569..99ecb86 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.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 @@
}
@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 5cff208..972b3bb 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 @@
}
@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 @@
/** 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 @@
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/people/data/ConversationInfoTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
index c6823eb..8139310 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 @@
@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 @@
.setPersonImportant(true)
.setPersonBot(true)
.setContactStarred(true)
+ .addOrUpdateStatus(cs)
+ .addOrUpdateStatus(cs2)
.build();
assertEquals(SHORTCUT_ID, conversationInfo.getShortcutId());
@@ -77,6 +88,8 @@
assertTrue(conversationInfo.isPersonImportant());
assertTrue(conversationInfo.isPersonBot());
assertTrue(conversationInfo.isContactStarred());
+ assertThat(conversationInfo.getStatuses()).contains(cs);
+ assertThat(conversationInfo.getStatuses()).contains(cs2);
}
@Test
@@ -101,10 +114,15 @@
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 @@
.setPersonImportant(true)
.setPersonBot(true)
.setContactStarred(true)
+ .addOrUpdateStatus(cs)
+ .addOrUpdateStatus(cs2)
.build();
ConversationInfo destination = new ConversationInfo.Builder(source)
@@ -141,5 +161,7 @@
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 2471210..be8a99c 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 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.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 @@
}
@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 08d4caa..5f65440 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 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 @@
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 @@
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 @@
}
@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 @@
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 @@
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 @@
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 @@
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 @@
}
@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 @@
}
@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 @@
// 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 fee848b..e8045e0 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 @@
}
// 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/tests/net/Android.bp b/tests/net/Android.bp
index a762219..f6a2846 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -70,4 +70,7 @@
"android.test.base",
"android.test.mock",
],
+ jni_libs: [
+ "libservice-connectivity",
+ ],
}
diff --git a/tools/powerstats/PowerStatsServiceProtoParser.java b/tools/powerstats/PowerStatsServiceProtoParser.java
index 76edd63..97a2a40 100644
--- a/tools/powerstats/PowerStatsServiceProtoParser.java
+++ b/tools/powerstats/PowerStatsServiceProtoParser.java
@@ -90,6 +90,38 @@
}
}
+ 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 @@
} 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);