Merge changes I9ea3677d,I0a59dce8
* changes:
Migrate away from using ServiceManager
Migrate StatsManager to apex
diff --git a/Android.bp b/Android.bp
index 58f6119..9411eec 100644
--- a/Android.bp
+++ b/Android.bp
@@ -297,6 +297,8 @@
],
}
+// AIDL files under these paths are mixture of public and private ones.
+// They shouldn't be exported across module boundaries.
java_defaults {
name: "framework-aidl-export-defaults",
aidl: {
@@ -321,12 +323,6 @@
"wifi/aidl-export",
],
},
-
- required: [
- // TODO: remove gps_debug and protolog.conf.json when the build system propagates "required" properly.
- "gps_debug.conf",
- "protolog.conf.json.gz",
- ],
}
// Collection of classes that are generated from non-Java files that are not listed in
@@ -344,6 +340,7 @@
"android.hardware.cas-V1.2-java",
"android.hardware.contexthub-V1.0-java",
"android.hardware.gnss-V1.0-java",
+ "android.hardware.gnss-V2.1-java",
"android.hardware.health-V1.0-java-constants",
"android.hardware.radio-V1.0-java",
"android.hardware.radio-V1.1-java",
@@ -416,6 +413,12 @@
"view-inspector-annotation-processor",
"staledataclass-annotation-processor",
],
+
+ required: [
+ // TODO: remove gps_debug and protolog.conf.json when the build system propagates "required" properly.
+ "gps_debug.conf",
+ "protolog.conf.json.gz",
+ ],
}
filegroup {
@@ -539,7 +542,6 @@
java_library {
name: "framework-annotation-proc",
- defaults: ["framework-aidl-export-defaults"],
srcs: [":framework-all-sources"],
libs: [
"app-compat-annotations",
@@ -713,7 +715,10 @@
name: "framework-tethering-annotations",
srcs: [
"core/java/android/annotation/NonNull.java",
+ "core/java/android/annotation/Nullable.java",
+ "core/java/android/annotation/RequiresPermission.java",
"core/java/android/annotation/SystemApi.java",
+ "core/java/android/annotation/TestApi.java",
],
}
// Build ext.jar
diff --git a/apex/appsearch/apex_manifest.json b/apex/appsearch/apex_manifest.json
index 273b867..39a2d38 100644
--- a/apex/appsearch/apex_manifest.json
+++ b/apex/appsearch/apex_manifest.json
@@ -1,4 +1,4 @@
{
"name": "com.android.appsearch",
- "version": 1
+ "version": 300000000
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearch.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearch.java
index fd20186..8bf13ee 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearch.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearch.java
@@ -101,6 +101,54 @@
this(document.mProto, document.mPropertyBundle);
}
+ /** @hide */
+ Document(@NonNull DocumentProto documentProto) {
+ this(documentProto, new Bundle());
+ for (int i = 0; i < documentProto.getPropertiesCount(); i++) {
+ PropertyProto property = documentProto.getProperties(i);
+ String name = property.getName();
+ if (property.getStringValuesCount() > 0) {
+ String[] values = new String[property.getStringValuesCount()];
+ for (int j = 0; j < values.length; j++) {
+ values[j] = property.getStringValues(j);
+ }
+ mPropertyBundle.putStringArray(name, values);
+ } else if (property.getInt64ValuesCount() > 0) {
+ long[] values = new long[property.getInt64ValuesCount()];
+ for (int j = 0; j < values.length; j++) {
+ values[j] = property.getInt64Values(j);
+ }
+ mPropertyBundle.putLongArray(property.getName(), values);
+ } else if (property.getDoubleValuesCount() > 0) {
+ double[] values = new double[property.getDoubleValuesCount()];
+ for (int j = 0; j < values.length; j++) {
+ values[j] = property.getDoubleValues(j);
+ }
+ mPropertyBundle.putDoubleArray(property.getName(), values);
+ } else if (property.getBooleanValuesCount() > 0) {
+ boolean[] values = new boolean[property.getBooleanValuesCount()];
+ for (int j = 0; j < values.length; j++) {
+ values[j] = property.getBooleanValues(j);
+ }
+ mPropertyBundle.putBooleanArray(property.getName(), values);
+ } else if (property.getBytesValuesCount() > 0) {
+ byte[][] values = new byte[property.getBytesValuesCount()][];
+ for (int j = 0; j < values.length; j++) {
+ values[j] = property.getBytesValues(j).toByteArray();
+ }
+ mPropertyBundle.putObject(name, values);
+ } else if (property.getDocumentValuesCount() > 0) {
+ Document[] values = new Document[property.getDocumentValuesCount()];
+ for (int j = 0; j < values.length; j++) {
+ values[j] = new Document(property.getDocumentValues(j));
+ }
+ mPropertyBundle.putObject(name, values);
+ } else {
+ throw new IllegalStateException("Unknown type of value: " + name);
+ }
+ }
+ }
+
/**
* Creates a new {@link Document.Builder}.
*
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index e3f6b3d..15c3368 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -26,6 +26,7 @@
import com.google.android.icing.proto.SchemaProto;
import com.google.android.icing.proto.SearchResultProto;
+import com.google.android.icing.proto.SearchSpecProto;
import com.google.android.icing.proto.StatusProto;
import com.google.android.icing.protobuf.InvalidProtocolBufferException;
@@ -186,28 +187,28 @@
*<p>Currently we support following features in the raw query format:
* <ul>
* <li>AND
- * AND joins (e.g. “match documents that have both the terms ‘dog’ 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
- * OR joins (e.g. “match documents that have either the term ‘dog’ or
+ * <p>OR joins (e.g. “match documents that have either the term ‘dog’ or
* ‘cat’”).
* Example: dog OR puppy
* <li>Exclusion
- * Exclude a term (e.g. “match documents that do
+ * <p>Exclude a term (e.g. “match documents that do
* not have the term ‘dog’”).
* Example: -dog excludes the term ‘dog’
* <li>Grouping terms
- * Allow for conceptual grouping of subqueries to enable hierarchical structures (e.g.
+ * <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
- * which properties of a document to specifically match terms in (e.g.
+ * <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
- * This is similar to property restricts, but allows for restricts on top-level document
+ * <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
@@ -263,7 +264,11 @@
}, executor);
try {
- mService.query(queryExpression, searchSpec.getProto().toByteArray(), future);
+ SearchSpecProto searchSpecProto = searchSpec.getSearchSpecProto();
+ searchSpecProto = searchSpecProto.toBuilder().setQuery(queryExpression).build();
+ mService.query(searchSpecProto.toByteArray(),
+ searchSpec.getResultSpecProto().toByteArray(),
+ searchSpec.getScoringSpecProto().toByteArray(), future);
} catch (RemoteException e) {
future.completeExceptionally(e);
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
index 20c8af98..eef41ed 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -47,12 +47,14 @@
void putDocuments(in List documentsBytes, in AndroidFuture<AppSearchBatchResult> callback);
/**
- * Searches a document based on a given query string.
+ * Searches a document based on a given specifications.
*
- * @param queryExpression Query String to search.
- * @param searchSpec Serialized SearchSpecProto.
+ * @param searchSpecBytes Serialized SearchSpecProto.
+ * @param resultSpecBytes Serialized SearchResultsProto.
+ * @param scoringSpecBytes Serialized ScoringSpecProto.
* @param callback {@link AndroidFuture}. Will be completed with a serialized
* {@link SearchResultsProto}, or completed exceptionally if query fails.
*/
- void query(in String queryExpression, in byte[] searchSpecBytes, in AndroidFuture callback);
+ void query(in byte[] searchSpecBytes, in byte[] resultSpecBytes,
+ in byte[] scoringSpecBytes, in AndroidFuture callback);
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/MatchInfo.java b/apex/appsearch/framework/java/android/app/appsearch/MatchInfo.java
new file mode 100644
index 0000000..6aa91a3f
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/MatchInfo.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch;
+
+import android.annotation.NonNull;
+import android.util.Range;
+
+import com.google.android.icing.proto.SnippetMatchProto;
+
+/**
+ * Snippet: It refers to a substring of text from the content of document that is returned as a
+ * part of search result.
+ * This class represents a match objects for any Snippets that might be present in
+ * {@link SearchResults} from query. Using this class user can get the full text, exact matches and
+ * Snippets of document content for a given match.
+ *
+ * <p>Class Example 1:
+ * A document contains following text in property subject:
+ * <p>A commonly used fake word is foo. Another nonsense word that’s used a lot is bar.
+ *
+ * <p>If the queryExpression is "foo".
+ *
+ * <p>{@link MatchInfo#getPropertyPath()} returns "subject"
+ * <p>{@link MatchInfo#getFullText()} returns "A commonly used fake word is foo. Another nonsense
+ * word that’s used a lot is bar."
+ * <p>{@link MatchInfo#getExactMatchPosition()} returns [29, 32]
+ * <p>{@link MatchInfo#getExactMatch()} returns "foo"
+ * <p>{@link MatchInfo#getSnippetPosition()} returns [29, 41]
+ * <p>{@link MatchInfo#getSnippet()} returns "is foo. Another"
+ * <p>
+ * <p>Class Example 2:
+ * A document contains a property name sender which contains 2 property names name and email, so
+ * we will have 2 property paths: {@code sender.name} and {@code sender.email}.
+ * <p> Let {@code sender.name = "Test Name Jr."} and {@code sender.email = "TestNameJr@gmail.com"}
+ *
+ * <p>If the queryExpression is "Test". We will have 2 matches.
+ *
+ * <p> Match-1
+ * <p>{@link MatchInfo#getPropertyPath()} returns "sender.name"
+ * <p>{@link MatchInfo#getFullText()} returns "Test Name Jr."
+ * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 4]
+ * <p>{@link MatchInfo#getExactMatch()} returns "Test"
+ * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 9]
+ * <p>{@link MatchInfo#getSnippet()} returns "Test Name Jr."
+ * <p> Match-2
+ * <p>{@link MatchInfo#getPropertyPath()} returns "sender.email"
+ * <p>{@link MatchInfo#getFullText()} returns "TestNameJr@gmail.com"
+ * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 20]
+ * <p>{@link MatchInfo#getExactMatch()} returns "TestNameJr@gmail.com"
+ * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 20]
+ * <p>{@link MatchInfo#getSnippet()} returns "TestNameJr@gmail.com"
+ * @hide
+ */
+// TODO(sidchhabra): Capture real snippet after integration with icingLib.
+public final class MatchInfo {
+
+ private final String mPropertyPath;
+ private final SnippetMatchProto mSnippetMatch;
+ private final AppSearch.Document mDocument;
+ /**
+ * List of content with same property path in a document when there are multiple matches in
+ * repeated sections.
+ */
+ private final String[] mValues;
+
+ /** @hide */
+ public MatchInfo(@NonNull String propertyPath, @NonNull SnippetMatchProto snippetMatch,
+ @NonNull AppSearch.Document document) {
+ mPropertyPath = propertyPath;
+ mSnippetMatch = snippetMatch;
+ mDocument = document;
+ // In IcingLib snippeting is available for only 3 data types i.e String, double and long,
+ // so we need to check which of these three are requested.
+ // TODO (sidchhabra): getPropertyStringArray takes property name, handle for property path.
+ String[] values = mDocument.getPropertyStringArray(propertyPath);
+ if (values == null) {
+ values = doubleToString(mDocument.getPropertyDoubleArray(propertyPath));
+ }
+ if (values == null) {
+ values = longToString(mDocument.getPropertyLongArray(propertyPath));
+ }
+ if (values == null) {
+ throw new IllegalStateException("No content found for requested property path!");
+ }
+ mValues = values;
+ }
+
+ /**
+ * Gets the property path corresponding to the given entry.
+ * <p>Property Path: '.' - delimited sequence of property names indicating which property in
+ * the Document these snippets correspond to.
+ * <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc.
+ * For class example 1 this returns "subject"
+ */
+ @NonNull
+ public String getPropertyPath() {
+ return mPropertyPath;
+ }
+
+ /**
+ * Gets the full text corresponding to the given entry.
+ * <p>For class example this returns "A commonly used fake word is foo. Another nonsense word
+ * that’s used a lot is bar."
+ */
+ @NonNull
+ public String getFullText() {
+ return mValues[mSnippetMatch.getValuesIndex()];
+ }
+
+ /**
+ * Gets the exact match range corresponding to the given entry.
+ * <p>For class example 1 this returns [29, 32]
+ */
+ @NonNull
+ public Range getExactMatchPosition() {
+ return new Range(mSnippetMatch.getExactMatchPosition(),
+ mSnippetMatch.getExactMatchPosition() + mSnippetMatch.getExactMatchBytes());
+ }
+
+ /**
+ * Gets the exact match corresponding to the given entry.
+ * <p>For class example 1 this returns "foo"
+ */
+ @NonNull
+ public CharSequence getExactMatch() {
+ return getSubstring(getExactMatchPosition());
+ }
+
+ /**
+ * Gets the snippet range corresponding to the given entry.
+ * <p>For class example 1 this returns [29, 41]
+ */
+ @NonNull
+ public Range getSnippetPosition() {
+ return new Range(mSnippetMatch.getWindowPosition(),
+ mSnippetMatch.getWindowPosition() + mSnippetMatch.getWindowBytes());
+ }
+
+ /**
+ * Gets the snippet corresponding to the given entry.
+ * <p>Snippet - Provides a subset of the content to display. The
+ * length of this content can be changed {@link SearchSpec.Builder#setMaxSnippetSize(int)}.
+ * Windowing is centered around the middle of the matched token with content on either side
+ * clipped to token boundaries.
+ * <p>For class example 1 this returns "foo. Another"
+ */
+ @NonNull
+ public CharSequence getSnippet() {
+ return getSubstring(getSnippetPosition());
+ }
+
+ private CharSequence getSubstring(Range range) {
+ return getFullText()
+ .substring((int) range.getLower(), (int) range.getUpper());
+ }
+
+ /** Utility method to convert double[] to String[] */
+ private String[] doubleToString(double[] values) {
+ //TODO(sidchhabra): Implement the method.
+ return null;
+ }
+
+ /** Utility method to convert long[] to String[] */
+ private String[] longToString(long[] values) {
+ //TODO(sidchhabra): Implement the method.
+ return null;
+ }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
index d763103..f48ebde 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
@@ -17,27 +17,51 @@
package android.app.appsearch;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import com.google.android.icing.proto.SearchResultProto;
+import com.google.android.icing.proto.SnippetMatchProto;
+import com.google.android.icing.proto.SnippetProto;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.NoSuchElementException;
/**
* SearchResults are a list of results that are returned from a query. Each result from this
* list contains a document and may contain other fields like snippets based on request.
+ * This iterator class is not thread safe.
* @hide
*/
-public final class SearchResults {
+public final class SearchResults implements Iterator<SearchResults.Result> {
private final SearchResultProto mSearchResultProto;
+ private int mNextIdx;
/** @hide */
public SearchResults(SearchResultProto searchResultProto) {
mSearchResultProto = searchResultProto;
}
+ @Override
+ public boolean hasNext() {
+ return mNextIdx < mSearchResultProto.getResultsCount();
+ }
+
+ @NonNull
+ @Override
+ public Result next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ Result result = new Result(mSearchResultProto.getResults(mNextIdx));
+ mNextIdx++;
+ return result;
+ }
+
+
+
/**
* This class represents the result obtained from the query. It will contain the document which
* which matched the specified query string and specifications.
@@ -46,6 +70,9 @@
public static final class Result {
private final SearchResultProto.ResultProto mResultProto;
+ @Nullable
+ private AppSearch.Document mDocument;
+
private Result(SearchResultProto.ResultProto resultProto) {
mResultProto = resultProto;
}
@@ -55,35 +82,47 @@
* @return Document object which matched the query.
* @hide
*/
- // TODO(sidchhabra): Switch to Document constructor that takes proto.
@NonNull
public AppSearch.Document getDocument() {
- return AppSearch.Document.newBuilder(mResultProto.getDocument().getUri(),
- mResultProto.getDocument().getSchema())
- .setCreationTimestampMillis(mResultProto.getDocument().getCreationTimestampMs())
- .setScore(mResultProto.getDocument().getScore())
- .build();
+ if (mDocument == null) {
+ mDocument = new AppSearch.Document(mResultProto.getDocument());
+ }
+ return mDocument;
}
- // TODO(sidchhabra): Add Getter for ResultReader for Snippet.
+ /**
+ * Contains a list of Snippets that matched the request. Only populated when requested in
+ * {@link SearchSpec.Builder#setMaxSnippetSize(int)}.
+ * @return List of matches based on {@link SearchSpec}, if snippeting is disabled and this
+ * method is called it will return {@code null}. Users can also restrict snippet population
+ * using {@link SearchSpec.Builder#setNumToSnippet} and
+ * {@link SearchSpec.Builder#setNumMatchesPerProperty}, for all results after that value
+ * this method will return {@code null}.
+ * @hide
+ */
+ // TODO(sidchhabra): Replace Document with proper constructor.
+ @Nullable
+ public List<MatchInfo> getMatchInfo() {
+ if (!mResultProto.hasSnippet()) {
+ return null;
+ }
+ AppSearch.Document document = getDocument();
+ List<MatchInfo> matchList = new ArrayList<>();
+ for (Iterator entryProtoIterator = mResultProto.getSnippet()
+ .getEntriesList().iterator(); entryProtoIterator.hasNext(); ) {
+ SnippetProto.EntryProto entry = (SnippetProto.EntryProto) entryProtoIterator.next();
+ for (Iterator snippetMatchProtoIterator = entry.getSnippetMatchesList().iterator();
+ snippetMatchProtoIterator.hasNext(); ) {
+ matchList.add(new MatchInfo(entry.getPropertyName(),
+ (SnippetMatchProto) snippetMatchProtoIterator.next(), document));
+ }
+ }
+ return matchList;
+ }
}
@Override
public String toString() {
return mSearchResultProto.toString();
}
-
- /**
- * Returns a {@link Result} iterator. Returns Empty Iterator if there are no matching results.
- * @hide
- */
- @NonNull
- public Iterator<Result> getResults() {
- List<Result> results = new ArrayList<>();
- // TODO(sidchhabra): Pass results using a RemoteStream.
- for (SearchResultProto.ResultProto resultProto : mSearchResultProto.getResultsList()) {
- results.add(new Result(resultProto));
- }
- return results.iterator();
- }
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java
index 5df7108..c276ae1 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java
@@ -19,25 +19,32 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import com.google.android.icing.proto.ResultSpecProto;
+import com.google.android.icing.proto.ScoringSpecProto;
import com.google.android.icing.proto.SearchSpecProto;
import com.google.android.icing.proto.TermMatchType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+
/**
* This class represents the specification logic for AppSearch. It can be used to set the type of
* search, like prefix or exact only or apply filters to search for a specific schema type only etc.
* @hide
- *
*/
// TODO(sidchhabra) : AddResultSpec fields for Snippets etc.
public final class SearchSpec {
private final SearchSpecProto mSearchSpecProto;
+ private final ResultSpecProto mResultSpecProto;
+ private final ScoringSpecProto mScoringSpecProto;
- private SearchSpec(SearchSpecProto searchSpecProto) {
+ private SearchSpec(@NonNull SearchSpecProto searchSpecProto,
+ @NonNull ResultSpecProto resultSpecProto, @NonNull ScoringSpecProto scoringSpecProto) {
mSearchSpecProto = searchSpecProto;
+ mResultSpecProto = resultSpecProto;
+ mScoringSpecProto = scoringSpecProto;
}
/** Creates a new {@link SearchSpec.Builder}. */
@@ -48,10 +55,22 @@
/** @hide */
@NonNull
- SearchSpecProto getProto() {
+ SearchSpecProto getSearchSpecProto() {
return mSearchSpecProto;
}
+ /** @hide */
+ @NonNull
+ ResultSpecProto getResultSpecProto() {
+ return mResultSpecProto;
+ }
+
+ /** @hide */
+ @NonNull
+ ScoringSpecProto getScoringSpecProto() {
+ return mScoringSpecProto;
+ }
+
/** Term Match Type for the query. */
// NOTE: The integer values of these constants must match the proto enum constants in
// {@link com.google.android.icing.proto.SearchSpecProto.termMatchType}
@@ -62,54 +81,167 @@
@Retention(RetentionPolicy.SOURCE)
public @interface TermMatchTypeCode {}
+ /**
+ * Query terms will only match exact tokens in the index.
+ * <p>Ex. A query term "foo" will only match indexed token "foo", and not "foot" or "football".
+ */
public static final int TERM_MATCH_TYPE_EXACT_ONLY = 1;
+ /**
+ * Query terms will match indexed tokens when the query term is a prefix of the token.
+ * <p>Ex. A query term "foo" will match indexed tokens like "foo", "foot", and "football".
+ */
public static final int TERM_MATCH_TYPE_PREFIX = 2;
+ /** Ranking Strategy for query result.*/
+ // NOTE: The integer values of these constants must match the proto enum constants in
+ // {@link ScoringSpecProto.RankingStrategy.Code }
+ @IntDef(prefix = {"RANKING_STRATEGY_"}, value = {
+ RANKING_STRATEGY_NONE,
+ RANKING_STRATEGY_DOCUMENT_SCORE,
+ RANKING_STRATEGY_CREATION_TIMESTAMP
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RankingStrategyCode {}
+
+ /** No Ranking, results are returned in arbitrary order.*/
+ public static final int RANKING_STRATEGY_NONE = 0;
+ /** Ranked by app-provided document scores. */
+ public static final int RANKING_STRATEGY_DOCUMENT_SCORE = 1;
+ /** Ranked by document creation timestamps. */
+ public static final int RANKING_STRATEGY_CREATION_TIMESTAMP = 2;
+
+ /** Order for query result.*/
+ // NOTE: The integer values of these constants must match the proto enum constants in
+ // {@link ScoringSpecProto.Order.Code }
+ @IntDef(prefix = {"ORDER_"}, value = {
+ ORDER_DESCENDING,
+ ORDER_ASCENDING
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface OrderCode {}
+
+ /** Search results will be returned in a descending order. */
+ public static final int ORDER_DESCENDING = 0;
+ /** Search results will be returned in an ascending order. */
+ public static final int ORDER_ASCENDING = 1;
+
/** Builder for {@link SearchSpec objects}. */
public static final class Builder {
- private final SearchSpecProto.Builder mBuilder = SearchSpecProto.newBuilder();
+ private final SearchSpecProto.Builder mSearchSpecBuilder = SearchSpecProto.newBuilder();
+ private final ResultSpecProto.Builder mResultSpecBuilder = ResultSpecProto.newBuilder();
+ private final ScoringSpecProto.Builder mScoringSpecBuilder = ScoringSpecProto.newBuilder();
+ private final ResultSpecProto.SnippetSpecProto.Builder mSnippetSpecBuilder =
+ ResultSpecProto.SnippetSpecProto.newBuilder();
- private Builder(){}
+ private Builder() {
+ }
/**
* Indicates how the query terms should match {@link TermMatchTypeCode} in the index.
- *
- * TermMatchType.Code=EXACT_ONLY
- * Query terms will only match exact tokens in the index.
- * Ex. A query term "foo" will only match indexed token "foo", and not "foot"
- * or "football"
- *
- * TermMatchType.Code=PREFIX
- * Query terms will match indexed tokens when the query term is a prefix of
- * the token.
- * Ex. A query term "foo" will match indexed tokens like "foo", "foot", and
- * "football".
*/
@NonNull
public Builder setTermMatchType(@TermMatchTypeCode int termMatchTypeCode) {
TermMatchType.Code termMatchTypeCodeProto =
TermMatchType.Code.forNumber(termMatchTypeCode);
if (termMatchTypeCodeProto == null) {
- throw new IllegalArgumentException("Invalid term match type: " + termMatchTypeCode);
+ throw new IllegalArgumentException("Invalid term match type: "
+ + termMatchTypeCode);
}
- mBuilder.setTermMatchType(termMatchTypeCodeProto);
+ mSearchSpecBuilder.setTermMatchType(termMatchTypeCodeProto);
return this;
}
/**
- * Adds a Schema type filter to {@link SearchSpec} Entry.
- * Only search for documents that have the specified schema types.
- * If unset, the query will search over all schema types.
+ * Adds a Schema type filter to {@link SearchSpec} Entry. Only search for documents that
+ * have the specified schema types.
+ * <p>If unset, the query will search over all schema types.
*/
@NonNull
public Builder setSchemaTypes(@NonNull String... schemaTypes) {
for (String schemaType : schemaTypes) {
- mBuilder.addSchemaTypeFilters(schemaType);
+ mSearchSpecBuilder.addSchemaTypeFilters(schemaType);
}
return this;
}
+ /** Sets the maximum number of results to retrieve from the query */
+ @NonNull
+ public SearchSpec.Builder setNumToRetrieve(int numToRetrieve) {
+ mResultSpecBuilder.setNumToRetrieve(numToRetrieve);
+ return this;
+ }
+
+ /** Sets ranking strategy for AppSearch results.*/
+ @NonNull
+ public Builder setRankingStrategy(@RankingStrategyCode int rankingStrategy) {
+ ScoringSpecProto.RankingStrategy.Code rankingStrategyCodeProto =
+ ScoringSpecProto.RankingStrategy.Code.forNumber(rankingStrategy);
+ if (rankingStrategyCodeProto == null) {
+ throw new IllegalArgumentException("Invalid result ranking strategy: "
+ + rankingStrategyCodeProto);
+ }
+ mScoringSpecBuilder.setRankBy(rankingStrategyCodeProto);
+ return this;
+ }
+
+ /**
+ * Indicates the order of returned search results, the default is DESC, meaning that results
+ * with higher scores come first.
+ * <p>This order field will be ignored if RankingStrategy = {@code RANKING_STRATEGY_NONE}.
+ */
+ @NonNull
+ public Builder setOrder(@OrderCode int order) {
+ ScoringSpecProto.Order.Code orderCodeProto =
+ ScoringSpecProto.Order.Code.forNumber(order);
+ if (orderCodeProto == null) {
+ throw new IllegalArgumentException("Invalid result ranking order: "
+ + orderCodeProto);
+ }
+ mScoringSpecBuilder.setOrderBy(orderCodeProto);
+ return this;
+ }
+
+ /**
+ * Only the first {@code numToSnippet} documents based on the ranking strategy
+ * will have snippet information provided.
+ * <p>If set to 0 (default), snippeting is disabled and
+ * {@link SearchResults.Result#getMatchInfo} will return {@code null} for that result.
+ */
+ @NonNull
+ public SearchSpec.Builder setNumToSnippet(int numToSnippet) {
+ mSnippetSpecBuilder.setNumToSnippet(numToSnippet);
+ return this;
+ }
+
+ /**
+ * Only the first {@code numMatchesPerProperty} matches for a every property of
+ * {@link AppSearchDocument} will contain snippet information.
+ * <p>If set to 0, snippeting is disabled and {@link SearchResults.Result#getMatchInfo}
+ * will return {@code null} for that result.
+ */
+ @NonNull
+ public SearchSpec.Builder setNumMatchesPerProperty(int numMatchesPerProperty) {
+ mSnippetSpecBuilder.setNumMatchesPerProperty(numMatchesPerProperty);
+ return this;
+ }
+
+ /**
+ * Sets {@code maxSnippetSize}, the maximum snippet size. Snippet windows start at
+ * {@code maxSnippetSize/2} bytes before the middle of the matching token and end at
+ * {@code maxSnippetSize/2} bytes after the middle of the matching token. It respects
+ * token boundaries, therefore the returned window may be smaller than requested.
+ * <p> Setting {@code maxSnippetSize} to 0 will disable windowing and an empty string will
+ * be returned. If matches enabled is also set to false, then snippeting is disabled.
+ * <p>Ex. {@code maxSnippetSize} = 16. "foo bar baz bat rat" with a query of "baz" will
+ * return a window of "bar baz bat" which is only 11 bytes long.
+ */
+ @NonNull
+ public SearchSpec.Builder setMaxSnippetSize(int maxSnippetSize) {
+ mSnippetSpecBuilder.setMaxWindowBytes(maxSnippetSize);
+ return this;
+ }
+
/**
* Constructs a new {@link SearchSpec} from the contents of this builder.
*
@@ -117,11 +249,12 @@
*/
@NonNull
public SearchSpec build() {
- if (mBuilder.getTermMatchType() == TermMatchType.Code.UNKNOWN) {
+ if (mSearchSpecBuilder.getTermMatchType() == TermMatchType.Code.UNKNOWN) {
throw new IllegalSearchSpecException("Missing termMatchType field.");
}
- return new SearchSpec(mBuilder.build());
+ mResultSpecBuilder.setSnippetSpec(mSnippetSpecBuilder);
+ return new SearchSpec(mSearchSpecBuilder.build(), mResultSpecBuilder.build(),
+ mScoringSpecBuilder.build());
}
}
-
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index d2d9cf9..6293ee7 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -106,10 +106,11 @@
// TODO(sidchhabra):Init FakeIcing properly.
// TODO(sidchhabra): Do this in a threadpool.
@Override
- public void query(@NonNull String queryExpression, @NonNull byte[] searchSpec,
- AndroidFuture callback) {
- Preconditions.checkNotNull(queryExpression);
+ public void query(@NonNull byte[] searchSpec, @NonNull byte[] resultSpec,
+ @NonNull byte[] scoringSpec, AndroidFuture callback) {
Preconditions.checkNotNull(searchSpec);
+ Preconditions.checkNotNull(resultSpec);
+ Preconditions.checkNotNull(scoringSpec);
SearchSpecProto searchSpecProto = null;
try {
searchSpecProto = SearchSpecProto.parseFrom(searchSpec);
@@ -117,7 +118,7 @@
throw new RuntimeException(e);
}
SearchResultProto searchResults =
- mFakeIcing.query(queryExpression);
+ mFakeIcing.query(searchSpecProto.getQuery());
callback.complete(searchResults.toByteArray());
}
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java b/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
index 02a79a1..d07ef4b 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
@@ -25,6 +25,7 @@
import com.google.android.icing.proto.DocumentProto;
import com.google.android.icing.proto.PropertyProto;
import com.google.android.icing.proto.SearchResultProto;
+import com.google.android.icing.proto.StatusProto;
import java.util.Locale;
import java.util.Map;
@@ -97,10 +98,12 @@
public SearchResultProto query(@NonNull String term) {
String normTerm = normalizeString(term);
Set<Integer> docIds = mIndex.get(normTerm);
+ SearchResultProto.Builder results = SearchResultProto.newBuilder()
+ .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.OK));
if (docIds == null || docIds.isEmpty()) {
- return SearchResultProto.getDefaultInstance();
+ return results.build();
}
- SearchResultProto.Builder results = SearchResultProto.newBuilder();
+
for (int docId : docIds) {
DocumentProto document = mDocStore.get(docId);
if (document != null) {
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index 47af7c0..00d9efb 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -323,6 +323,28 @@
}
/**
+ * Opens a file descriptor to read the blob content already written into this session.
+ *
+ * @return a {@link ParcelFileDescriptor} for reading from the blob file.
+ *
+ * @throws IOException when there is an I/O error while opening the file to read.
+ * @throws SecurityException when the caller is not the owner of the session.
+ * @throws IllegalStateException when the caller tries to read the file after it is
+ * abandoned (using {@link #abandon()})
+ * or closed (using {@link #close()}).
+ */
+ public @NonNull ParcelFileDescriptor openRead() throws IOException {
+ try {
+ return mSession.openRead();
+ } catch (ParcelableException e) {
+ e.maybeRethrow(IOException.class);
+ throw new RuntimeException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Gets the size of the blob file that was written to the session so far.
*
* @return the size of the blob file so far.
diff --git a/apex/blobstore/framework/java/android/app/blob/IBlobStoreSession.aidl b/apex/blobstore/framework/java/android/app/blob/IBlobStoreSession.aidl
index 4ae919b..4035b96 100644
--- a/apex/blobstore/framework/java/android/app/blob/IBlobStoreSession.aidl
+++ b/apex/blobstore/framework/java/android/app/blob/IBlobStoreSession.aidl
@@ -21,6 +21,7 @@
/** {@hide} */
interface IBlobStoreSession {
ParcelFileDescriptor openWrite(long offsetBytes, long lengthBytes);
+ ParcelFileDescriptor openRead();
void allowPackageAccess(in String packageName, in byte[] certificate);
void allowSameSignatureAccess();
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index 29092b3..612fd89 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -17,6 +17,7 @@
import static android.app.blob.BlobStoreManager.COMMIT_RESULT_ERROR;
import static android.system.OsConstants.O_CREAT;
+import static android.system.OsConstants.O_RDONLY;
import static android.system.OsConstants.O_RDWR;
import static android.system.OsConstants.SEEK_SET;
@@ -187,6 +188,40 @@
}
@Override
+ @NonNull
+ public ParcelFileDescriptor openRead() {
+ assertCallerIsOwner();
+ synchronized (mSessionLock) {
+ if (mState != STATE_OPENED) {
+ throw new IllegalStateException("Not allowed to read in state: "
+ + stateToString(mState));
+ }
+
+ try {
+ return openReadLocked();
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
+ }
+ }
+ }
+
+ @GuardedBy("mSessionLock")
+ @NonNull
+ private ParcelFileDescriptor openReadLocked() throws IOException {
+ FileDescriptor fd = null;
+ try {
+ final File sessionFile = getSessionFile();
+ if (sessionFile == null) {
+ throw new IllegalStateException("Couldn't get the file for this session");
+ }
+ fd = Os.open(sessionFile.getPath(), O_RDONLY, 0);
+ } catch (ErrnoException e) {
+ e.rethrowAsIOException();
+ }
+ return createRevocableFdLocked(fd);
+ }
+
+ @Override
@BytesLong
public long getSize() {
return 0;
diff --git a/apex/extservices/apex_manifest.json b/apex/extservices/apex_manifest.json
index 7ba2157..b4acf128 100644
--- a/apex/extservices/apex_manifest.json
+++ b/apex/extservices/apex_manifest.json
@@ -1,4 +1,4 @@
{
"name": "com.android.extservices",
- "version": 1
+ "version": 300000000
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index ef1351e..b96161a 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -48,6 +48,13 @@
public static final int REASON_DEVICE_IDLE = JobProtoEnums.STOP_REASON_DEVICE_IDLE; // 4.
/** @hide */
public static final int REASON_DEVICE_THERMAL = JobProtoEnums.STOP_REASON_DEVICE_THERMAL; // 5.
+ /**
+ * The job is in the {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED}
+ * bucket.
+ *
+ * @hide
+ */
+ public static final int REASON_RESTRAINED = JobProtoEnums.STOP_REASON_RESTRAINED; // 6.
/**
* All the stop reason codes. This should be regarded as an immutable array at runtime.
@@ -65,6 +72,7 @@
REASON_TIMEOUT,
REASON_DEVICE_IDLE,
REASON_DEVICE_THERMAL,
+ REASON_RESTRAINED,
};
/**
@@ -80,6 +88,7 @@
case REASON_TIMEOUT: return "timeout";
case REASON_DEVICE_IDLE: return "device_idle";
case REASON_DEVICE_THERMAL: return "thermal";
+ case REASON_RESTRAINED: return "restrained";
default: return "unknown:" + reason;
}
}
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index 6109b71..d2d942a 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -103,6 +103,8 @@
/**
* Changes an app's standby bucket to the provided value. The caller can only set the standby
* bucket for a different app than itself.
+ * If attempting to automatically place an app in the RESTRICTED bucket, use
+ * {@link #restrictApp(String, int, int)} instead.
*/
void setAppStandbyBucket(@NonNull String packageName, int bucket, int userId, int callingUid,
int callingPid);
@@ -113,6 +115,17 @@
void setAppStandbyBuckets(@NonNull List<AppStandbyInfo> appBuckets, int userId, int callingUid,
int callingPid);
+ /**
+ * Put the specified app in the
+ * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED}
+ * bucket. If it has been used by the user recently, the restriction will delayed until an
+ * appropriate time.
+ *
+ * @param restrictReason The restrictReason for restricting the app. Should be one of the
+ * UsageStatsManager.REASON_SUB_RESTRICT_* reasons.
+ */
+ void restrictApp(@NonNull String packageName, int userId, int restrictReason);
+
void addActiveDeviceAdmin(String adminPkg, int userId);
void setActiveAdminApps(Set<String> adminPkgs, int userId);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 102e848..ed5626a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -101,6 +101,7 @@
import com.android.server.job.controllers.IdleController;
import com.android.server.job.controllers.JobStatus;
import com.android.server.job.controllers.QuotaController;
+import com.android.server.job.controllers.RestrictingController;
import com.android.server.job.controllers.StateController;
import com.android.server.job.controllers.StorageController;
import com.android.server.job.controllers.TimeController;
@@ -241,6 +242,11 @@
/** List of controllers that will notify this service of updates to jobs. */
final List<StateController> mControllers;
+ /**
+ * List of controllers that will apply to all jobs in the RESTRICTED bucket. This is a subset of
+ * {@link #mControllers}.
+ */
+ private final List<RestrictingController> mRestrictiveControllers;
/** Need direct access to this for testing. */
private final BatteryController mBatteryController;
/** Need direct access to this for testing. */
@@ -277,6 +283,7 @@
DeviceIdleInternal mLocalDeviceIdleController;
AppStateTracker mAppStateTracker;
final UsageStatsManagerInternal mUsageStats;
+ private final AppStandbyInternal mAppStandbyInternal;
/**
* Set to true once we are allowed to run third party apps.
@@ -312,6 +319,9 @@
public static final int FREQUENT_INDEX = 2;
public static final int RARE_INDEX = 3;
public static final int NEVER_INDEX = 4;
+ // Putting RESTRICTED_INDEX after NEVER_INDEX to make it easier for proto dumping
+ // (ScheduledJobStateChanged and JobStatusDumpProto).
+ public static final int RESTRICTED_INDEX = 5;
// -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
@@ -1062,7 +1072,8 @@
packageName == null ? job.getService().getPackageName() : packageName;
if (!mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG)) {
Slog.e(TAG, userId + "-" + pkg + " has called schedule() too many times");
- // TODO(b/145551233): attempt to restrict app
+ mAppStandbyInternal.restrictApp(
+ pkg, userId, UsageStatsManager.REASON_SUB_RESTRICT_BUGGY);
if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION
&& mPlatformCompat.isChangeEnabledByPackageName(
CRASH_ON_EXCEEDED_LIMIT, pkg, userId)) {
@@ -1365,6 +1376,40 @@
}
}
+ @Override
+ public void onRestrictedBucketChanged(List<JobStatus> jobs) {
+ final int len = jobs.size();
+ if (len == 0) {
+ Slog.wtf(TAG, "onRestrictedBucketChanged called with no jobs");
+ return;
+ }
+ synchronized (mLock) {
+ for (int i = 0; i < len; ++i) {
+ JobStatus js = jobs.get(i);
+ for (int j = mRestrictiveControllers.size() - 1; j >= 0; --j) {
+ // Effective standby bucket can change after this in some situations so use
+ // the real bucket so that the job is tracked by the controllers.
+ if (js.getStandbyBucket() == RESTRICTED_INDEX) {
+ js.addDynamicConstraint(JobStatus.CONSTRAINT_BATTERY_NOT_LOW);
+ js.addDynamicConstraint(JobStatus.CONSTRAINT_CHARGING);
+ js.addDynamicConstraint(JobStatus.CONSTRAINT_CONNECTIVITY);
+ js.addDynamicConstraint(JobStatus.CONSTRAINT_IDLE);
+
+ mRestrictiveControllers.get(j).startTrackingRestrictedJobLocked(js);
+ } else {
+ js.removeDynamicConstraint(JobStatus.CONSTRAINT_BATTERY_NOT_LOW);
+ js.removeDynamicConstraint(JobStatus.CONSTRAINT_CHARGING);
+ js.removeDynamicConstraint(JobStatus.CONSTRAINT_CONNECTIVITY);
+ js.removeDynamicConstraint(JobStatus.CONSTRAINT_IDLE);
+
+ mRestrictiveControllers.get(j).stopTrackingRestrictedJobLocked(js);
+ }
+ }
+ }
+ }
+ mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+ }
+
void reportActiveLocked() {
// active is true if pending queue contains jobs OR some job is running.
boolean active = mPendingJobs.size() > 0;
@@ -1430,8 +1475,8 @@
mConstants.API_QUOTA_SCHEDULE_COUNT,
mConstants.API_QUOTA_SCHEDULE_WINDOW_MS);
- AppStandbyInternal appStandby = LocalServices.getService(AppStandbyInternal.class);
- appStandby.addListener(mStandbyTracker);
+ mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class);
+ mAppStandbyInternal.addListener(mStandbyTracker);
// The job store needs to call back
publishLocalService(JobSchedulerInternal.class, new LocalService());
@@ -1441,9 +1486,11 @@
// Create the controllers.
mControllers = new ArrayList<StateController>();
- mControllers.add(new ConnectivityController(this));
+ final ConnectivityController connectivityController = new ConnectivityController(this);
+ mControllers.add(connectivityController);
mControllers.add(new TimeController(this));
- mControllers.add(new IdleController(this));
+ final IdleController idleController = new IdleController(this);
+ mControllers.add(idleController);
mBatteryController = new BatteryController(this);
mControllers.add(mBatteryController);
mStorageController = new StorageController(this);
@@ -1455,6 +1502,11 @@
mQuotaController = new QuotaController(this);
mControllers.add(mQuotaController);
+ mRestrictiveControllers = new ArrayList<>();
+ mRestrictiveControllers.add(mBatteryController);
+ mRestrictiveControllers.add(connectivityController);
+ mRestrictiveControllers.add(idleController);
+
// Create restrictions
mJobRestrictions = new ArrayList<>();
mJobRestrictions.add(new ThermalStatusRestriction(this));
@@ -2125,11 +2177,13 @@
}
} catch (RemoteException e) {
}
- if (mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT > 1
+ // Restricted jobs must always be batched
+ if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX
+ || (mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT > 1
&& job.getEffectiveStandbyBucket() != ACTIVE_INDEX
&& (job.getFirstForceBatchedTimeElapsed() == 0
|| sElapsedRealtimeClock.millis() - job.getFirstForceBatchedTimeElapsed()
- < mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS)) {
+ < mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS))) {
// Force batching non-ACTIVE jobs. Don't include them in the other counts.
forceBatchedCount++;
if (job.getFirstForceBatchedTimeElapsed() == 0) {
@@ -2536,11 +2590,19 @@
public static int standbyBucketToBucketIndex(int bucket) {
// Normalize AppStandby constants to indices into our bookkeeping
- if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) return NEVER_INDEX;
- else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) return RARE_INDEX;
- else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) return FREQUENT_INDEX;
- else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) return WORKING_INDEX;
- else return ACTIVE_INDEX;
+ if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) {
+ return NEVER_INDEX;
+ } else if (bucket > UsageStatsManager.STANDBY_BUCKET_RARE) {
+ return RESTRICTED_INDEX;
+ } else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) {
+ return RARE_INDEX;
+ } else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) {
+ return FREQUENT_INDEX;
+ } else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) {
+ return WORKING_INDEX;
+ } else {
+ return ACTIVE_INDEX;
+ }
}
// Static to support external callers
diff --git a/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java b/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java
index 87bfc27..cb3c437 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java
@@ -16,8 +16,12 @@
package com.android.server.job;
+import android.annotation.NonNull;
+
import com.android.server.job.controllers.JobStatus;
+import java.util.List;
+
/**
* Interface through which a {@link com.android.server.job.controllers.StateController} informs
* the {@link com.android.server.job.JobSchedulerService} that there are some tasks potentially
@@ -39,4 +43,10 @@
public void onRunJobNow(JobStatus jobStatus);
public void onDeviceIdleStateChanged(boolean deviceIdle);
+
+ /**
+ * Called when these jobs are added or removed from the
+ * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket.
+ */
+ void onRestrictedBucketChanged(@NonNull List<JobStatus> jobs);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
index 46658ad..461ef21 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
@@ -43,7 +43,7 @@
* be charging when it's been plugged in for more than two minutes, and the system has broadcast
* ACTION_BATTERY_OK.
*/
-public final class BatteryController extends StateController {
+public final class BatteryController extends RestrictingController {
private static final String TAG = "JobScheduler.Battery";
private static final boolean DEBUG = JobSchedulerService.DEBUG
|| Log.isLoggable(TAG, Log.DEBUG);
@@ -73,12 +73,24 @@
}
@Override
+ public void startTrackingRestrictedJobLocked(JobStatus jobStatus) {
+ maybeStartTrackingJobLocked(jobStatus, null);
+ }
+
+ @Override
public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) {
if (taskStatus.clearTrackingController(JobStatus.TRACKING_BATTERY)) {
mTrackedTasks.remove(taskStatus);
}
}
+ @Override
+ public void stopTrackingRestrictedJobLocked(JobStatus jobStatus) {
+ if (!jobStatus.hasPowerConstraint()) {
+ maybeStopTrackingJobLocked(jobStatus, null, false);
+ }
+ }
+
private void maybeReportNewChargingStateLocked() {
final boolean stablePower = mChargeTracker.isOnStablePower();
final boolean batteryNotLow = mChargeTracker.isBatteryNotLow();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 8eeea1b..a0e83da 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -20,6 +20,8 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
+
import android.app.job.JobInfo;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
@@ -63,7 +65,7 @@
*
* Test: atest com.android.server.job.controllers.ConnectivityControllerTest
*/
-public final class ConnectivityController extends StateController implements
+public final class ConnectivityController extends RestrictingController implements
ConnectivityManager.OnNetworkActiveListener {
private static final String TAG = "JobScheduler.Connectivity";
private static final boolean DEBUG = JobSchedulerService.DEBUG
@@ -138,8 +140,22 @@
}
}
+ @Override
+ public void startTrackingRestrictedJobLocked(JobStatus jobStatus) {
+ // Don't need to start tracking the job. If the job needed network, it would already be
+ // tracked.
+ updateConstraintsSatisfied(jobStatus);
+ }
+
+ @Override
+ public void stopTrackingRestrictedJobLocked(JobStatus jobStatus) {
+ // Shouldn't stop tracking the job here. If the job was tracked, it still needs network,
+ // even after being unrestricted.
+ updateConstraintsSatisfied(jobStatus);
+ }
+
/**
- * Returns true if the job's requested network is available. This DOES NOT necesarilly mean
+ * Returns true if the job's requested network is available. This DOES NOT necessarily mean
* that the UID has been granted access to the network.
*/
public boolean isNetworkAvailable(JobStatus job) {
@@ -353,14 +369,24 @@
private static boolean isStrictSatisfied(JobStatus jobStatus, Network network,
NetworkCapabilities capabilities, Constants constants) {
- return jobStatus.getJob().getRequiredNetwork().networkCapabilities
- .satisfiedByNetworkCapabilities(capabilities);
+ final NetworkCapabilities required;
+ // A restricted job that's out of quota MUST use an unmetered network.
+ if (jobStatus.getEffectiveStandbyBucket() == RESTRICTED_INDEX
+ && !jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) {
+ required = new NetworkCapabilities(
+ jobStatus.getJob().getRequiredNetwork().networkCapabilities)
+ .addCapability(NET_CAPABILITY_NOT_METERED);
+ } else {
+ required = jobStatus.getJob().getRequiredNetwork().networkCapabilities;
+ }
+
+ return required.satisfiedByNetworkCapabilities(capabilities);
}
private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
NetworkCapabilities capabilities, Constants constants) {
- // Only consider doing this for prefetching jobs
- if (!jobStatus.getJob().isPrefetch()) {
+ // Only consider doing this for unrestricted prefetching jobs
+ if (!jobStatus.getJob().isPrefetch() || jobStatus.getStandbyBucket() == RESTRICTED_INDEX) {
return false;
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
index d355715..c0b3204 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
@@ -32,7 +32,15 @@
import java.util.function.Predicate;
-public final class IdleController extends StateController implements IdlenessListener {
+/**
+ * Simple controller that tracks whether the device is idle or not. Idleness depends on the device
+ * type and is not related to device-idle (Doze mode) despite the similar naming.
+ *
+ * @see CarIdlenessTracker
+ * @see DeviceIdlenessTracker
+ * @see IdlenessTracker
+ */
+public final class IdleController extends RestrictingController implements IdlenessListener {
private static final String TAG = "JobScheduler.IdleController";
// Policy: we decide that we're "idle" if the device has been unused /
// screen off or dreaming or wireless charging dock idle for at least this long
@@ -57,6 +65,11 @@
}
@Override
+ public void startTrackingRestrictedJobLocked(JobStatus jobStatus) {
+ maybeStartTrackingJobLocked(jobStatus, null);
+ }
+
+ @Override
public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob,
boolean forUpdate) {
if (taskStatus.clearTrackingController(JobStatus.TRACKING_IDLE)) {
@@ -64,6 +77,13 @@
}
}
+ @Override
+ public void stopTrackingRestrictedJobLocked(JobStatus jobStatus) {
+ if (!jobStatus.hasIdleConstraint()) {
+ maybeStopTrackingJobLocked(jobStatus, null, false);
+ }
+ }
+
/**
* State-change notifications from the idleness tracker
*/
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index a8d8bd9..dbdce70 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -69,13 +69,14 @@
public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE;
public static final long NO_EARLIEST_RUNTIME = 0L;
- static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0
- static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE; // 1 << 2
- static final int CONSTRAINT_BATTERY_NOT_LOW = JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1
+ public static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0
+ public static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE; // 1 << 2
+ public static final int CONSTRAINT_BATTERY_NOT_LOW =
+ JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1
static final int CONSTRAINT_STORAGE_NOT_LOW = JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW; // 1 << 3
static final int CONSTRAINT_TIMING_DELAY = 1<<31;
static final int CONSTRAINT_DEADLINE = 1<<30;
- static final int CONSTRAINT_CONNECTIVITY = 1<<28;
+ public static final int CONSTRAINT_CONNECTIVITY = 1 << 28;
static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26;
static final int CONSTRAINT_DEVICE_NOT_DOZING = 1 << 25; // Implicit constraint
static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24; // Implicit constraint
@@ -117,7 +118,7 @@
/** The minimum possible update delay is 1/2 second. */
public static final long MIN_TRIGGER_UPDATE_DELAY = 500;
- /** If not specified, trigger maxumum delay is 2 minutes. */
+ /** If not specified, trigger maximum delay is 2 minutes. */
public static final long DEFAULT_TRIGGER_MAX_DELAY = 2*60*1000;
/** The minimum possible update delay is 1 second. */
@@ -188,6 +189,11 @@
private final int mRequiredConstraintsOfInterest;
int satisfiedConstraints = 0;
private int mSatisfiedConstraintsOfInterest = 0;
+ /**
+ * Set of constraints that must be satisfied for the job if/because it's in the RESTRICTED
+ * bucket.
+ */
+ private int mDynamicConstraints = 0;
// Set to true if doze constraint was satisfied due to app being whitelisted.
public boolean dozeWhitelisted;
@@ -328,6 +334,9 @@
/** The job is within its quota based on its standby bucket. */
private boolean mReadyWithinQuota;
+ /** The job's dynamic requirements have been satisfied. */
+ private boolean mReadyDynamicSatisfied;
+
/** Provide a handle to the service that this job will be run on. */
public int getServiceToken() {
return callingUid;
@@ -410,6 +419,7 @@
this.requiredConstraints = requiredConstraints;
mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST;
mReadyNotDozing = (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
+ mReadyDynamicSatisfied = true;
mLastSuccessfulRunTime = lastSuccessfulRunTime;
mLastFailedRunTime = lastFailedRunTime;
@@ -830,41 +840,54 @@
/** Does this job have any sort of networking constraint? */
public boolean hasConnectivityConstraint() {
+ // No need to check mDynamicConstraints since connectivity will only be in that list if
+ // it's already in the requiredConstraints list.
return (requiredConstraints&CONSTRAINT_CONNECTIVITY) != 0;
}
public boolean hasChargingConstraint() {
- return (requiredConstraints&CONSTRAINT_CHARGING) != 0;
+ return hasConstraint(CONSTRAINT_CHARGING);
}
public boolean hasBatteryNotLowConstraint() {
- return (requiredConstraints&CONSTRAINT_BATTERY_NOT_LOW) != 0;
+ return hasConstraint(CONSTRAINT_BATTERY_NOT_LOW);
}
- public boolean hasPowerConstraint() {
- return (requiredConstraints&(CONSTRAINT_CHARGING|CONSTRAINT_BATTERY_NOT_LOW)) != 0;
+ /** Returns true if the job requires charging OR battery not low. */
+ boolean hasPowerConstraint() {
+ return hasConstraint(CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW);
}
public boolean hasStorageNotLowConstraint() {
- return (requiredConstraints&CONSTRAINT_STORAGE_NOT_LOW) != 0;
+ return hasConstraint(CONSTRAINT_STORAGE_NOT_LOW);
}
public boolean hasTimingDelayConstraint() {
- return (requiredConstraints&CONSTRAINT_TIMING_DELAY) != 0;
+ return hasConstraint(CONSTRAINT_TIMING_DELAY);
}
public boolean hasDeadlineConstraint() {
- return (requiredConstraints&CONSTRAINT_DEADLINE) != 0;
+ return hasConstraint(CONSTRAINT_DEADLINE);
}
public boolean hasIdleConstraint() {
- return (requiredConstraints&CONSTRAINT_IDLE) != 0;
+ return hasConstraint(CONSTRAINT_IDLE);
}
public boolean hasContentTriggerConstraint() {
+ // No need to check mDynamicConstraints since content trigger will only be in that list if
+ // it's already in the requiredConstraints list.
return (requiredConstraints&CONSTRAINT_CONTENT_TRIGGER) != 0;
}
+ /**
+ * Checks both {@link #requiredConstraints} and {@link #mDynamicConstraints} to see if this job
+ * requires the specified constraint.
+ */
+ private boolean hasConstraint(int constraint) {
+ return (requiredConstraints & constraint) != 0 || (mDynamicConstraints & constraint) != 0;
+ }
+
public long getTriggerContentUpdateDelay() {
long time = job.getTriggerContentUpdateDelay();
if (time < 0) {
@@ -1033,6 +1056,8 @@
}
satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0);
mSatisfiedConstraintsOfInterest = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
+ mReadyDynamicSatisfied =
+ mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
if (STATS_LOG_ENABLED && (STATSD_CONSTRAINTS_TO_LOG & constraint) != 0) {
StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED,
sourceUid, null, getBatteryName(), getProtoConstraint(constraint),
@@ -1058,6 +1083,43 @@
trackingControllers |= which;
}
+ /**
+ * Indicates that this job cannot run without the specified constraint. This is evaluated
+ * separately from the job's explicitly requested constraints and MUST be satisfied before
+ * the job can run if the app doesn't have quota.
+ *
+ */
+ public void addDynamicConstraint(int constraint) {
+ if (constraint == CONSTRAINT_WITHIN_QUOTA) {
+ Slog.wtf(TAG, "Tried to set quota as a dynamic constraint");
+ return;
+ }
+
+ // Connectivity and content trigger are special since they're only valid to add if the
+ // job has requested network or specific content URIs. Adding these constraints to jobs
+ // that don't need them doesn't make sense.
+ if ((constraint == CONSTRAINT_CONNECTIVITY && !hasConnectivityConstraint())
+ || (constraint == CONSTRAINT_CONTENT_TRIGGER && !hasContentTriggerConstraint())) {
+ return;
+ }
+
+ mDynamicConstraints |= constraint;
+ mReadyDynamicSatisfied =
+ mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
+ }
+
+ /**
+ * Removes a dynamic constraint from a job, meaning that the requirement is not required for
+ * the job to run (if the job itself hasn't requested the constraint. This is separate from
+ * the job's explicitly requested constraints and does not remove those requested constraints.
+ *
+ */
+ public void removeDynamicConstraint(int constraint) {
+ mDynamicConstraints &= ~constraint;
+ mReadyDynamicSatisfied =
+ mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
+ }
+
public long getLastSuccessfulRunTime() {
return mLastSuccessfulRunTime;
}
@@ -1099,6 +1161,8 @@
break;
default:
satisfied |= constraint;
+ mReadyDynamicSatisfied =
+ mDynamicConstraints == (satisfied & mDynamicConstraints);
break;
}
@@ -1117,24 +1181,29 @@
case CONSTRAINT_WITHIN_QUOTA:
mReadyWithinQuota = oldValue;
break;
+ default:
+ mReadyDynamicSatisfied =
+ mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
+ break;
}
return toReturn;
}
private boolean isReady(int satisfiedConstraints) {
- // Quota constraints trumps all other constraints.
- if (!mReadyWithinQuota) {
+ // Quota and dynamic constraints trump all other constraints.
+ if (!mReadyWithinQuota && !mReadyDynamicSatisfied) {
return false;
}
- // Deadline constraint trumps other constraints besides quota (except for periodic jobs
- // where deadline is an implementation detail. A periodic job should only run if its
- // constraints are satisfied).
+ // Deadline constraint trumps other constraints besides quota and dynamic (except for
+ // periodic jobs where deadline is an implementation detail. A periodic job should only
+ // run if its constraints are satisfied).
// DeviceNotDozing implicit constraint must be satisfied
// NotRestrictedInBackground implicit constraint must be satisfied
return mReadyNotDozing && mReadyNotRestrictedInBg && (mReadyDeadlineSatisfied
|| isConstraintsSatisfied(satisfiedConstraints));
}
+ /** All constraints besides implicit and deadline. */
static final int CONSTRAINTS_OF_INTEREST = CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW
| CONSTRAINT_STORAGE_NOT_LOW | CONSTRAINT_TIMING_DELAY | CONSTRAINT_CONNECTIVITY
| CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER;
@@ -1441,6 +1510,8 @@
case 2: return "FREQUENT";
case 3: return "RARE";
case 4: return "NEVER";
+ case 5:
+ return "RESTRICTED";
default:
return "Unknown: " + standbyBucket;
}
@@ -1560,6 +1631,10 @@
pw.print(prefix); pw.print("Required constraints:");
dumpConstraints(pw, requiredConstraints);
pw.println();
+ pw.print(prefix);
+ pw.print("Dynamic constraints:");
+ dumpConstraints(pw, mDynamicConstraints);
+ pw.println();
if (full) {
pw.print(prefix); pw.print("Satisfied constraints:");
dumpConstraints(pw, satisfiedConstraints);
@@ -1599,6 +1674,9 @@
pw.print(prefix); pw.print(" readyDeadlineSatisfied: ");
pw.println(mReadyDeadlineSatisfied);
}
+ pw.print(prefix);
+ pw.print(" readyDynamicSatisfied: ");
+ pw.println(mReadyDynamicSatisfied);
if (changedAuthorities != null) {
pw.print(prefix); pw.println("Changed authorities:");
@@ -1760,6 +1838,7 @@
}
dumpConstraints(proto, JobStatusDumpProto.REQUIRED_CONSTRAINTS, requiredConstraints);
+ dumpConstraints(proto, JobStatusDumpProto.DYNAMIC_CONSTRAINTS, mDynamicConstraints);
if (full) {
dumpConstraints(proto, JobStatusDumpProto.SATISFIED_CONSTRAINTS, satisfiedConstraints);
dumpConstraints(proto, JobStatusDumpProto.UNSATISFIED_CONSTRAINTS,
@@ -1807,6 +1886,8 @@
mReadyNotRestrictedInBg);
// mReadyDeadlineSatisfied isn't an implicit constraint...and can be determined from other
// field values.
+ proto.write(JobStatusDumpProto.ImplicitConstraints.IS_DYNAMIC_SATISFIED,
+ mReadyDynamicSatisfied);
proto.end(icToken);
if (changedAuthorities != null) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 2e735a4..8eefac8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -24,6 +24,7 @@
import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
import static com.android.server.job.JobSchedulerService.RARE_INDEX;
+import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
@@ -424,7 +425,9 @@
QcConstants.DEFAULT_WINDOW_SIZE_ACTIVE_MS,
QcConstants.DEFAULT_WINDOW_SIZE_WORKING_MS,
QcConstants.DEFAULT_WINDOW_SIZE_FREQUENT_MS,
- QcConstants.DEFAULT_WINDOW_SIZE_RARE_MS
+ QcConstants.DEFAULT_WINDOW_SIZE_RARE_MS,
+ 0, // NEVER
+ QcConstants.DEFAULT_WINDOW_SIZE_RESTRICTED_MS
};
/** The maximum period any bucket can have. */
@@ -441,7 +444,9 @@
QcConstants.DEFAULT_MAX_JOB_COUNT_ACTIVE,
QcConstants.DEFAULT_MAX_JOB_COUNT_WORKING,
QcConstants.DEFAULT_MAX_JOB_COUNT_FREQUENT,
- QcConstants.DEFAULT_MAX_JOB_COUNT_RARE
+ QcConstants.DEFAULT_MAX_JOB_COUNT_RARE,
+ 0, // NEVER
+ QcConstants.DEFAULT_MAX_JOB_COUNT_RESTRICTED
};
/**
@@ -455,7 +460,9 @@
QcConstants.DEFAULT_MAX_SESSION_COUNT_ACTIVE,
QcConstants.DEFAULT_MAX_SESSION_COUNT_WORKING,
QcConstants.DEFAULT_MAX_SESSION_COUNT_FREQUENT,
- QcConstants.DEFAULT_MAX_SESSION_COUNT_RARE
+ QcConstants.DEFAULT_MAX_SESSION_COUNT_RARE,
+ 0, // NEVER
+ QcConstants.DEFAULT_MAX_SESSION_COUNT_RESTRICTED,
};
/**
@@ -648,7 +655,11 @@
// Quota constraint is not enforced while charging.
if (mChargeTracker.isCharging()) {
- return true;
+ // Restricted jobs require additional constraints when charging, so don't immediately
+ // mark quota as free when charging.
+ if (standbyBucket != RESTRICTED_INDEX) {
+ return true;
+ }
}
ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
@@ -1105,14 +1116,37 @@
}
}
+ private class TimerChargingUpdateFunctor implements Consumer<Timer> {
+ private long mNowElapsed;
+ private boolean mIsCharging;
+
+ private void setStatus(long nowElapsed, boolean isCharging) {
+ mNowElapsed = nowElapsed;
+ mIsCharging = isCharging;
+ }
+
+ @Override
+ public void accept(Timer timer) {
+ if (JobSchedulerService.standbyBucketForPackage(timer.mPkg.packageName,
+ timer.mPkg.userId, mNowElapsed) != RESTRICTED_INDEX) {
+ // Restricted jobs need additional constraints even when charging, so don't
+ // immediately say that quota is free.
+ timer.onStateChangedLocked(mNowElapsed, mIsCharging);
+ }
+ }
+ }
+
+ private final TimerChargingUpdateFunctor
+ mTimerChargingUpdateFunctor = new TimerChargingUpdateFunctor();
+
private void handleNewChargingStateLocked() {
- final long nowElapsed = sElapsedRealtimeClock.millis();
- final boolean isCharging = mChargeTracker.isCharging();
+ mTimerChargingUpdateFunctor.setStatus(sElapsedRealtimeClock.millis(),
+ mChargeTracker.isCharging());
if (DEBUG) {
- Slog.d(TAG, "handleNewChargingStateLocked: " + isCharging);
+ Slog.d(TAG, "handleNewChargingStateLocked: " + mChargeTracker.isCharging());
}
// Deal with Timers first.
- mPkgTimers.forEach((t) -> t.onStateChangedLocked(nowElapsed, isCharging));
+ mPkgTimers.forEach(mTimerChargingUpdateFunctor);
// Now update jobs.
maybeUpdateAllConstraintsLocked();
}
@@ -1555,7 +1589,10 @@
}
private boolean shouldTrackLocked() {
- return !mChargeTracker.isCharging() && !mForegroundUids.get(mUid);
+ final int standbyBucket = JobSchedulerService.standbyBucketForPackage(mPkg.packageName,
+ mPkg.userId, sElapsedRealtimeClock.millis());
+ return (standbyBucket == RESTRICTED_INDEX || !mChargeTracker.isCharging())
+ && !mForegroundUids.get(mUid);
}
void onStateChangedLocked(long nowElapsed, boolean isQuotaFree) {
@@ -1670,6 +1707,7 @@
Slog.i(TAG, "Moving pkg " + string(userId, packageName) + " to bucketIndex "
+ bucketIndex);
}
+ List<JobStatus> restrictedChanges = new ArrayList<>();
synchronized (mLock) {
ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, packageName);
if (jobs == null || jobs.size() == 0) {
@@ -1677,6 +1715,13 @@
}
for (int i = jobs.size() - 1; i >= 0; i--) {
JobStatus js = jobs.valueAt(i);
+ // Effective standby bucket can change after this in some situations so
+ // use the real bucket so that the job is tracked by the controllers.
+ if ((bucketIndex == RESTRICTED_INDEX
+ || js.getStandbyBucket() == RESTRICTED_INDEX)
+ && bucketIndex != js.getStandbyBucket()) {
+ restrictedChanges.add(js);
+ }
js.setStandbyBucket(bucketIndex);
}
Timer timer = mPkgTimers.get(userId, packageName);
@@ -1687,6 +1732,9 @@
mStateChangedListener.onControllerStateChanged();
}
}
+ if (restrictedChanges.size() > 0) {
+ mStateChangedListener.onRestrictedBucketChanged(restrictedChanges);
+ }
});
}
}
@@ -1863,11 +1911,13 @@
private static final String KEY_WINDOW_SIZE_WORKING_MS = "window_size_working_ms";
private static final String KEY_WINDOW_SIZE_FREQUENT_MS = "window_size_frequent_ms";
private static final String KEY_WINDOW_SIZE_RARE_MS = "window_size_rare_ms";
+ private static final String KEY_WINDOW_SIZE_RESTRICTED_MS = "window_size_restricted_ms";
private static final String KEY_MAX_EXECUTION_TIME_MS = "max_execution_time_ms";
private static final String KEY_MAX_JOB_COUNT_ACTIVE = "max_job_count_active";
private static final String KEY_MAX_JOB_COUNT_WORKING = "max_job_count_working";
private static final String KEY_MAX_JOB_COUNT_FREQUENT = "max_job_count_frequent";
private static final String KEY_MAX_JOB_COUNT_RARE = "max_job_count_rare";
+ private static final String KEY_MAX_JOB_COUNT_RESTRICTED = "max_job_count_restricted";
private static final String KEY_RATE_LIMITING_WINDOW_MS = "rate_limiting_window_ms";
private static final String KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW =
"max_job_count_per_rate_limiting_window";
@@ -1875,6 +1925,8 @@
private static final String KEY_MAX_SESSION_COUNT_WORKING = "max_session_count_working";
private static final String KEY_MAX_SESSION_COUNT_FREQUENT = "max_session_count_frequent";
private static final String KEY_MAX_SESSION_COUNT_RARE = "max_session_count_rare";
+ private static final String KEY_MAX_SESSION_COUNT_RESTRICTED =
+ "max_session_count_restricted";
private static final String KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW =
"max_session_count_per_rate_limiting_window";
private static final String KEY_TIMING_SESSION_COALESCING_DURATION_MS =
@@ -1892,6 +1944,8 @@
8 * 60 * 60 * 1000L; // 8 hours
private static final long DEFAULT_WINDOW_SIZE_RARE_MS =
24 * 60 * 60 * 1000L; // 24 hours
+ private static final long DEFAULT_WINDOW_SIZE_RESTRICTED_MS =
+ 24 * 60 * 60 * 1000L; // 24 hours
private static final long DEFAULT_MAX_EXECUTION_TIME_MS =
4 * HOUR_IN_MILLIS;
private static final long DEFAULT_RATE_LIMITING_WINDOW_MS =
@@ -1905,6 +1959,7 @@
(int) (25.0 * DEFAULT_WINDOW_SIZE_FREQUENT_MS / HOUR_IN_MILLIS);
private static final int DEFAULT_MAX_JOB_COUNT_RARE = // 48/window = 2/hr = 16/session
(int) (2.0 * DEFAULT_WINDOW_SIZE_RARE_MS / HOUR_IN_MILLIS);
+ private static final int DEFAULT_MAX_JOB_COUNT_RESTRICTED = 10;
private static final int DEFAULT_MAX_SESSION_COUNT_ACTIVE =
75; // 450/hr
private static final int DEFAULT_MAX_SESSION_COUNT_WORKING =
@@ -1913,6 +1968,7 @@
8; // 1/hr
private static final int DEFAULT_MAX_SESSION_COUNT_RARE =
3; // .125/hr
+ private static final int DEFAULT_MAX_SESSION_COUNT_RESTRICTED = 1; // 1/day
private static final int DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = 20;
private static final long DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS = 5000; // 5 seconds
@@ -1954,6 +2010,13 @@
public long WINDOW_SIZE_RARE_MS = DEFAULT_WINDOW_SIZE_RARE_MS;
/**
+ * The quota window size of the particular standby bucket. Apps in this standby bucket are
+ * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past
+ * WINDOW_SIZE_MS.
+ */
+ public long WINDOW_SIZE_RESTRICTED_MS = DEFAULT_WINDOW_SIZE_RESTRICTED_MS;
+
+ /**
* The maximum amount of time an app can have its jobs running within a 24 hour window.
*/
public long MAX_EXECUTION_TIME_MS = DEFAULT_MAX_EXECUTION_TIME_MS;
@@ -1982,6 +2045,12 @@
*/
public int MAX_JOB_COUNT_RARE = DEFAULT_MAX_JOB_COUNT_RARE;
+ /**
+ * The maximum number of jobs an app can run within this particular standby bucket's
+ * window size.
+ */
+ public int MAX_JOB_COUNT_RESTRICTED = DEFAULT_MAX_JOB_COUNT_RESTRICTED;
+
/** The period of time used to rate limit recently run jobs. */
public long RATE_LIMITING_WINDOW_MS = DEFAULT_RATE_LIMITING_WINDOW_MS;
@@ -2016,6 +2085,12 @@
public int MAX_SESSION_COUNT_RARE = DEFAULT_MAX_SESSION_COUNT_RARE;
/**
+ * The maximum number of {@link TimingSession}s an app can run within this particular
+ * standby bucket's window size.
+ */
+ public int MAX_SESSION_COUNT_RESTRICTED = DEFAULT_MAX_SESSION_COUNT_RESTRICTED;
+
+ /**
* The maximum number of {@link TimingSession}s that can run within the past
* {@link #ALLOWED_TIME_PER_PERIOD_MS}.
*/
@@ -2087,6 +2162,8 @@
KEY_WINDOW_SIZE_FREQUENT_MS, DEFAULT_WINDOW_SIZE_FREQUENT_MS);
WINDOW_SIZE_RARE_MS = mParser.getDurationMillis(
KEY_WINDOW_SIZE_RARE_MS, DEFAULT_WINDOW_SIZE_RARE_MS);
+ WINDOW_SIZE_RESTRICTED_MS = mParser.getDurationMillis(
+ KEY_WINDOW_SIZE_RESTRICTED_MS, DEFAULT_WINDOW_SIZE_RESTRICTED_MS);
MAX_EXECUTION_TIME_MS = mParser.getDurationMillis(
KEY_MAX_EXECUTION_TIME_MS, DEFAULT_MAX_EXECUTION_TIME_MS);
MAX_JOB_COUNT_ACTIVE = mParser.getInt(
@@ -2097,6 +2174,8 @@
KEY_MAX_JOB_COUNT_FREQUENT, DEFAULT_MAX_JOB_COUNT_FREQUENT);
MAX_JOB_COUNT_RARE = mParser.getInt(
KEY_MAX_JOB_COUNT_RARE, DEFAULT_MAX_JOB_COUNT_RARE);
+ MAX_JOB_COUNT_RESTRICTED = mParser.getInt(
+ KEY_MAX_JOB_COUNT_RESTRICTED, DEFAULT_MAX_JOB_COUNT_RESTRICTED);
RATE_LIMITING_WINDOW_MS = mParser.getLong(
KEY_RATE_LIMITING_WINDOW_MS, DEFAULT_RATE_LIMITING_WINDOW_MS);
MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = mParser.getInt(
@@ -2110,6 +2189,8 @@
KEY_MAX_SESSION_COUNT_FREQUENT, DEFAULT_MAX_SESSION_COUNT_FREQUENT);
MAX_SESSION_COUNT_RARE = mParser.getInt(
KEY_MAX_SESSION_COUNT_RARE, DEFAULT_MAX_SESSION_COUNT_RARE);
+ MAX_SESSION_COUNT_RESTRICTED = mParser.getInt(
+ KEY_MAX_SESSION_COUNT_RESTRICTED, DEFAULT_MAX_SESSION_COUNT_RESTRICTED);
MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = mParser.getInt(
KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW);
@@ -2173,6 +2254,13 @@
mBucketPeriodsMs[RARE_INDEX] = newRarePeriodMs;
changed = true;
}
+ // Fit in the range [allowed time (10 mins), 1 week].
+ long newRestrictedPeriodMs = Math.max(mAllowedTimePerPeriodMs,
+ Math.min(7 * 24 * 60 * MINUTE_IN_MILLIS, WINDOW_SIZE_RESTRICTED_MS));
+ if (mBucketPeriodsMs[RESTRICTED_INDEX] != newRestrictedPeriodMs) {
+ mBucketPeriodsMs[RESTRICTED_INDEX] = newRestrictedPeriodMs;
+ changed = true;
+ }
long newRateLimitingWindowMs = Math.min(MAX_PERIOD_MS,
Math.max(MIN_RATE_LIMITING_WINDOW_MS, RATE_LIMITING_WINDOW_MS));
if (mRateLimitingWindowMs != newRateLimitingWindowMs) {
@@ -2206,6 +2294,12 @@
mMaxBucketJobCounts[RARE_INDEX] = newRareMaxJobCount;
changed = true;
}
+ int newRestrictedMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT,
+ MAX_JOB_COUNT_RESTRICTED);
+ if (mMaxBucketJobCounts[RESTRICTED_INDEX] != newRestrictedMaxJobCount) {
+ mMaxBucketJobCounts[RESTRICTED_INDEX] = newRestrictedMaxJobCount;
+ changed = true;
+ }
int newMaxSessionCountPerRateLimitPeriod = Math.max(
MIN_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW);
@@ -2237,6 +2331,11 @@
mMaxBucketSessionCounts[RARE_INDEX] = newRareMaxSessionCount;
changed = true;
}
+ int newRestrictedMaxSessionCount = Math.max(0, MAX_SESSION_COUNT_RESTRICTED);
+ if (mMaxBucketSessionCounts[RESTRICTED_INDEX] != newRestrictedMaxSessionCount) {
+ mMaxBucketSessionCounts[RESTRICTED_INDEX] = newRestrictedMaxSessionCount;
+ changed = true;
+ }
long newSessionCoalescingDurationMs = Math.min(15 * MINUTE_IN_MILLIS,
Math.max(0, TIMING_SESSION_COALESCING_DURATION_MS));
if (mTimingSessionCoalescingDurationMs != newSessionCoalescingDurationMs) {
@@ -2266,11 +2365,13 @@
pw.printPair(KEY_WINDOW_SIZE_WORKING_MS, WINDOW_SIZE_WORKING_MS).println();
pw.printPair(KEY_WINDOW_SIZE_FREQUENT_MS, WINDOW_SIZE_FREQUENT_MS).println();
pw.printPair(KEY_WINDOW_SIZE_RARE_MS, WINDOW_SIZE_RARE_MS).println();
+ pw.printPair(KEY_WINDOW_SIZE_RESTRICTED_MS, WINDOW_SIZE_RESTRICTED_MS).println();
pw.printPair(KEY_MAX_EXECUTION_TIME_MS, MAX_EXECUTION_TIME_MS).println();
pw.printPair(KEY_MAX_JOB_COUNT_ACTIVE, MAX_JOB_COUNT_ACTIVE).println();
pw.printPair(KEY_MAX_JOB_COUNT_WORKING, MAX_JOB_COUNT_WORKING).println();
pw.printPair(KEY_MAX_JOB_COUNT_FREQUENT, MAX_JOB_COUNT_FREQUENT).println();
pw.printPair(KEY_MAX_JOB_COUNT_RARE, MAX_JOB_COUNT_RARE).println();
+ pw.printPair(KEY_MAX_JOB_COUNT_RESTRICTED, MAX_JOB_COUNT_RESTRICTED).println();
pw.printPair(KEY_RATE_LIMITING_WINDOW_MS, RATE_LIMITING_WINDOW_MS).println();
pw.printPair(KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW).println();
@@ -2278,6 +2379,7 @@
pw.printPair(KEY_MAX_SESSION_COUNT_WORKING, MAX_SESSION_COUNT_WORKING).println();
pw.printPair(KEY_MAX_SESSION_COUNT_FREQUENT, MAX_SESSION_COUNT_FREQUENT).println();
pw.printPair(KEY_MAX_SESSION_COUNT_RARE, MAX_SESSION_COUNT_RARE).println();
+ pw.printPair(KEY_MAX_SESSION_COUNT_RESTRICTED, MAX_SESSION_COUNT_RESTRICTED).println();
pw.printPair(KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW).println();
pw.printPair(KEY_TIMING_SESSION_COALESCING_DURATION_MS,
@@ -2297,6 +2399,8 @@
proto.write(ConstantsProto.QuotaController.FREQUENT_WINDOW_SIZE_MS,
WINDOW_SIZE_FREQUENT_MS);
proto.write(ConstantsProto.QuotaController.RARE_WINDOW_SIZE_MS, WINDOW_SIZE_RARE_MS);
+ proto.write(ConstantsProto.QuotaController.RESTRICTED_WINDOW_SIZE_MS,
+ WINDOW_SIZE_RESTRICTED_MS);
proto.write(ConstantsProto.QuotaController.MAX_EXECUTION_TIME_MS,
MAX_EXECUTION_TIME_MS);
proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_ACTIVE, MAX_JOB_COUNT_ACTIVE);
@@ -2305,6 +2409,8 @@
proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_FREQUENT,
MAX_JOB_COUNT_FREQUENT);
proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_RARE, MAX_JOB_COUNT_RARE);
+ proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_RESTRICTED,
+ MAX_JOB_COUNT_RESTRICTED);
proto.write(ConstantsProto.QuotaController.RATE_LIMITING_WINDOW_MS,
RATE_LIMITING_WINDOW_MS);
proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
@@ -2317,6 +2423,8 @@
MAX_SESSION_COUNT_FREQUENT);
proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_RARE,
MAX_SESSION_COUNT_RARE);
+ proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_RESTRICTED,
+ MAX_SESSION_COUNT_RESTRICTED);
proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW,
MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW);
proto.write(ConstantsProto.QuotaController.TIMING_SESSION_COALESCING_DURATION_MS,
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/RestrictingController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/RestrictingController.java
new file mode 100644
index 0000000..5c637bb
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/RestrictingController.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.job.controllers;
+
+import com.android.server.job.JobSchedulerService;
+
+/**
+ * Controller that can also handle jobs in the
+ * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket.
+ */
+public abstract class RestrictingController extends StateController {
+ RestrictingController(JobSchedulerService service) {
+ super(service);
+ }
+
+ /**
+ * Start tracking a job that has been added to the
+ * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket.
+ */
+ public abstract void startTrackingRestrictedJobLocked(JobStatus jobStatus);
+
+ /**
+ * Stop tracking a job that has been removed from the
+ * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket.
+ */
+ public abstract void stopTrackingRestrictedJobLocked(JobStatus jobStatus);
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index b9df30a..9d6e012 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -25,8 +25,11 @@
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+import static com.android.server.usage.AppStandbyController.isUserUsage;
+
import android.app.usage.AppStandbyInfo;
import android.app.usage.UsageStatsManager;
import android.os.SystemClock;
@@ -81,6 +84,8 @@
private static final String ATTR_SCREEN_IDLE = "screenIdleTime";
// Elapsed timebase time when app was last used
private static final String ATTR_ELAPSED_IDLE = "elapsedIdleTime";
+ // Elapsed timebase time when app was last used by the user
+ private static final String ATTR_LAST_USED_BY_USER_ELAPSED = "lastUsedByUserElapsedTime";
// Elapsed timebase time when the app bucket was last predicted externally
private static final String ATTR_LAST_PREDICTED_TIME = "lastPredictedTime";
// The standby bucket for the app
@@ -93,6 +98,12 @@
private static final String ATTR_BUCKET_ACTIVE_TIMEOUT_TIME = "activeTimeoutTime";
// The time when the forced working_set state can be overridden.
private static final String ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME = "workingSetTimeoutTime";
+ // Elapsed timebase time when the app was last marked for restriction.
+ private static final String ATTR_LAST_RESTRICTION_ATTEMPT_ELAPSED =
+ "lastRestrictionAttemptElapsedTime";
+ // Reason why the app was last marked for restriction.
+ private static final String ATTR_LAST_RESTRICTION_ATTEMPT_REASON =
+ "lastRestrictionAttemptReason";
// device on time = mElapsedDuration + (timeNow - mElapsedSnapshot)
private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration
@@ -107,8 +118,10 @@
private boolean mScreenOn;
static class AppUsageHistory {
- // Last used time using elapsed timebase
+ // Last used time (including system usage), using elapsed timebase
long lastUsedElapsedTime;
+ // Last time the user used the app, using elapsed timebase
+ long lastUsedByUserElapsedTime;
// Last used time using screen_on timebase
long lastUsedScreenTime;
// Last predicted time using elapsed timebase
@@ -136,6 +149,10 @@
// under any active state timeout, so that it becomes applicable after the active state
// timeout expires.
long bucketWorkingSetTimeoutTime;
+ // The last time an agent attempted to put the app into the RESTRICTED bucket.
+ long lastRestrictAttemptElapsedTime;
+ // The last reason the app was marked to be put into the RESTRICTED bucket.
+ int lastRestrictReason;
}
AppIdleHistory(File storageDir, long elapsedRealtime) {
@@ -229,25 +246,37 @@
*/
public AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName,
int newBucket, int usageReason, long elapsedRealtime, long timeout) {
- // Set the timeout if applicable
- if (timeout > elapsedRealtime) {
- // Convert to elapsed timebase
- final long timeoutTime = mElapsedDuration + (timeout - mElapsedSnapshot);
- if (newBucket == STANDBY_BUCKET_ACTIVE) {
- appUsageHistory.bucketActiveTimeoutTime = Math.max(timeoutTime,
- appUsageHistory.bucketActiveTimeoutTime);
- } else if (newBucket == STANDBY_BUCKET_WORKING_SET) {
- appUsageHistory.bucketWorkingSetTimeoutTime = Math.max(timeoutTime,
- appUsageHistory.bucketWorkingSetTimeoutTime);
- } else {
- throw new IllegalArgumentException("Cannot set a timeout on bucket=" +
- newBucket);
+ int bucketingReason = REASON_MAIN_USAGE | usageReason;
+ final boolean isUserUsage = isUserUsage(bucketingReason);
+
+ if (appUsageHistory.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage) {
+ // Only user usage should bring an app out of the RESTRICTED bucket.
+ newBucket = STANDBY_BUCKET_RESTRICTED;
+ bucketingReason = appUsageHistory.bucketingReason;
+ } else {
+ // Set the timeout if applicable
+ if (timeout > elapsedRealtime) {
+ // Convert to elapsed timebase
+ final long timeoutTime = mElapsedDuration + (timeout - mElapsedSnapshot);
+ if (newBucket == STANDBY_BUCKET_ACTIVE) {
+ appUsageHistory.bucketActiveTimeoutTime = Math.max(timeoutTime,
+ appUsageHistory.bucketActiveTimeoutTime);
+ } else if (newBucket == STANDBY_BUCKET_WORKING_SET) {
+ appUsageHistory.bucketWorkingSetTimeoutTime = Math.max(timeoutTime,
+ appUsageHistory.bucketWorkingSetTimeoutTime);
+ } else {
+ throw new IllegalArgumentException("Cannot set a timeout on bucket="
+ + newBucket);
+ }
}
}
if (elapsedRealtime != 0) {
appUsageHistory.lastUsedElapsedTime = mElapsedDuration
+ (elapsedRealtime - mElapsedSnapshot);
+ if (isUserUsage) {
+ appUsageHistory.lastUsedByUserElapsedTime = appUsageHistory.lastUsedElapsedTime;
+ }
appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
}
@@ -259,7 +288,7 @@
+ ", reason=0x0" + Integer.toHexString(appUsageHistory.bucketingReason));
}
}
- appUsageHistory.bucketingReason = REASON_MAIN_USAGE | usageReason;
+ appUsageHistory.bucketingReason = bucketingReason;
return appUsageHistory;
}
@@ -386,6 +415,24 @@
}
/**
+ * Notes an attempt to put the app in the {@link UsageStatsManager#STANDBY_BUCKET_RESTRICTED}
+ * bucket.
+ *
+ * @param packageName The package name of the app that is being restricted
+ * @param userId The ID of the user in which the app is being restricted
+ * @param elapsedRealtime The time the attempt was made, in the (unadjusted) elapsed realtime
+ * timebase
+ * @param reason The reason for the restriction attempt
+ */
+ void noteRestrictionAttempt(String packageName, int userId, long elapsedRealtime, int reason) {
+ ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+ AppUsageHistory appUsageHistory =
+ getPackageHistory(userHistory, packageName, elapsedRealtime, true);
+ appUsageHistory.lastRestrictAttemptElapsedTime = getElapsedTime(elapsedRealtime);
+ appUsageHistory.lastRestrictReason = reason;
+ }
+
+ /**
* Returns the time since the last job was run for this app. This can be larger than the
* current elapsedRealtime, in case it happened before boot or a really large value if no jobs
* were ever run.
@@ -547,6 +594,9 @@
AppUsageHistory appUsageHistory = new AppUsageHistory();
appUsageHistory.lastUsedElapsedTime =
Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE));
+ appUsageHistory.lastUsedByUserElapsedTime = getLongValue(parser,
+ ATTR_LAST_USED_BY_USER_ELAPSED,
+ appUsageHistory.lastUsedElapsedTime);
appUsageHistory.lastUsedScreenTime =
Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE));
appUsageHistory.lastPredictedTime = getLongValue(parser,
@@ -570,6 +620,19 @@
appUsageHistory.bucketingReason =
Integer.parseInt(bucketingReason, 16);
} catch (NumberFormatException nfe) {
+ Slog.wtf(TAG, "Unable to read bucketing reason", nfe);
+ }
+ }
+ appUsageHistory.lastRestrictAttemptElapsedTime =
+ getLongValue(parser, ATTR_LAST_RESTRICTION_ATTEMPT_ELAPSED, 0);
+ String lastRestrictReason = parser.getAttributeValue(
+ null, ATTR_LAST_RESTRICTION_ATTEMPT_REASON);
+ if (lastRestrictReason != null) {
+ try {
+ appUsageHistory.lastRestrictReason =
+ Integer.parseInt(lastRestrictReason, 16);
+ } catch (NumberFormatException nfe) {
+ Slog.wtf(TAG, "Unable to read last restrict reason", nfe);
}
}
appUsageHistory.lastInformedBucket = -1;
@@ -618,6 +681,8 @@
xml.attribute(null, ATTR_NAME, packageName);
xml.attribute(null, ATTR_ELAPSED_IDLE,
Long.toString(history.lastUsedElapsedTime));
+ xml.attribute(null, ATTR_LAST_USED_BY_USER_ELAPSED,
+ Long.toString(history.lastUsedByUserElapsedTime));
xml.attribute(null, ATTR_SCREEN_IDLE,
Long.toString(history.lastUsedScreenTime));
xml.attribute(null, ATTR_LAST_PREDICTED_TIME,
@@ -638,6 +703,12 @@
xml.attribute(null, ATTR_LAST_RUN_JOB_TIME, Long.toString(history
.lastJobRunTime));
}
+ if (history.lastRestrictAttemptElapsedTime > 0) {
+ xml.attribute(null, ATTR_LAST_RESTRICTION_ATTEMPT_ELAPSED,
+ Long.toString(history.lastRestrictAttemptElapsedTime));
+ }
+ xml.attribute(null, ATTR_LAST_RESTRICTION_ATTEMPT_REASON,
+ Integer.toHexString(history.lastRestrictReason));
xml.endTag(null, TAG_PACKAGE);
}
@@ -672,6 +743,9 @@
+ UsageStatsManager.reasonToString(appUsageHistory.bucketingReason));
idpw.print(" used=");
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw);
+ idpw.print(" usedByUser=");
+ TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedByUserElapsedTime,
+ idpw);
idpw.print(" usedScr=");
TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw);
idpw.print(" lastPred=");
@@ -684,6 +758,13 @@
idpw);
idpw.print(" lastJob=");
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastJobRunTime, idpw);
+ if (appUsageHistory.lastRestrictAttemptElapsedTime > 0) {
+ idpw.print(" lastRestrictAttempt=");
+ TimeUtils.formatDuration(
+ totalElapsedTime - appUsageHistory.lastRestrictAttemptElapsedTime, idpw);
+ idpw.print(" lastRestrictReason="
+ + UsageStatsManager.reasonToString(appUsageHistory.lastRestrictReason));
+ }
idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n"));
idpw.println();
}
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index eb0b54b..b1b8fba 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -23,6 +23,7 @@
import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_MASK;
import static android.app.usage.UsageStatsManager.REASON_SUB_PREDICTED_RESTORED;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE;
@@ -44,6 +45,7 @@
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
@@ -73,6 +75,7 @@
import android.net.NetworkRequest;
import android.net.NetworkScoreManager;
import android.os.BatteryStats;
+import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.IDeviceIdleController;
@@ -93,7 +96,9 @@
import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.view.Display;
+import android.widget.Toast;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
@@ -124,7 +129,7 @@
public class AppStandbyController implements AppStandbyInternal {
private static final String TAG = "AppStandbyController";
- static final boolean DEBUG = false;
+ static final boolean DEBUG = true;
static final boolean COMPRESS_TIME = false;
private static final long ONE_MINUTE = 60 * 1000;
@@ -615,6 +620,16 @@
Slog.d(TAG, " Keeping at WORKING_SET due to min timeout");
}
}
+
+ if (app.lastRestrictAttemptElapsedTime > app.lastUsedByUserElapsedTime
+ && elapsedTimeAdjusted - app.lastUsedByUserElapsedTime
+ >= mInjector.getRestrictedBucketDelayMs()) {
+ newBucket = STANDBY_BUCKET_RESTRICTED;
+ reason = app.lastRestrictReason;
+ if (DEBUG) {
+ Slog.d(TAG, "Bringing down to RESTRICTED due to timeout");
+ }
+ }
if (DEBUG) {
Slog.d(TAG, " Old bucket=" + oldBucket
+ ", newBucket=" + newBucket);
@@ -733,15 +748,16 @@
elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis);
nextCheckTime = mStrongUsageTimeoutMillis;
}
- mHandler.sendMessageDelayed(mHandler.obtainMessage
- (MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, pkg),
- nextCheckTime);
- final boolean userStartedInteracting =
- appHistory.currentBucket == STANDBY_BUCKET_ACTIVE &&
- prevBucket != appHistory.currentBucket &&
- (prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE;
- maybeInformListeners(pkg, userId, elapsedRealtime,
- appHistory.currentBucket, reason, userStartedInteracting);
+ if (appHistory.currentBucket != prevBucket) {
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, pkg),
+ nextCheckTime);
+ final boolean userStartedInteracting =
+ appHistory.currentBucket == STANDBY_BUCKET_ACTIVE
+ && (prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE;
+ maybeInformListeners(pkg, userId, elapsedRealtime,
+ appHistory.currentBucket, reason, userStartedInteracting);
+ }
if (previouslyIdle) {
notifyBatteryStats(pkg, userId, false);
@@ -923,6 +939,15 @@
}
}
+ static boolean isUserUsage(int reason) {
+ if ((reason & REASON_MAIN_MASK) == REASON_MAIN_USAGE) {
+ final int subReason = reason & REASON_SUB_MASK;
+ return subReason == REASON_SUB_USAGE_USER_INTERACTION
+ || subReason == REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
+ }
+ return false;
+ }
+
@Override
public int[] getIdleUidsForUser(int userId) {
if (!mAppIdleEnabled) {
@@ -1017,6 +1042,20 @@
}
@Override
+ public void restrictApp(@NonNull String packageName, int userId, int restrictReason) {
+ // If the package is not installed, don't allow the bucket to be set.
+ if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
+ Slog.e(TAG, "Tried to restrict uninstalled app: " + packageName);
+ return;
+ }
+
+ final int reason = REASON_MAIN_FORCED_BY_SYSTEM | (REASON_SUB_MASK & restrictReason);
+ final long nowElapsed = mInjector.elapsedRealtime();
+ setAppStandbyBucket(packageName, userId, STANDBY_BUCKET_RESTRICTED, reason,
+ nowElapsed, false);
+ }
+
+ @Override
public void setAppStandbyBucket(@NonNull String packageName, int bucket, int userId,
int callingUid, int callingPid) {
setAppStandbyBuckets(
@@ -1080,6 +1119,7 @@
synchronized (mAppIdleLock) {
// If the package is not installed, don't allow the bucket to be set.
if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
+ Slog.e(TAG, "Tried to set bucket of uninstalled app: " + packageName);
return;
}
AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName,
@@ -1089,8 +1129,9 @@
// Don't allow changing bucket if higher than ACTIVE
if (app.currentBucket < STANDBY_BUCKET_ACTIVE) return;
- // Don't allow prediction to change from/to NEVER
+ // Don't allow prediction to change from/to NEVER or from RESTRICTED.
if ((app.currentBucket == STANDBY_BUCKET_NEVER
+ || app.currentBucket == STANDBY_BUCKET_RESTRICTED
|| newBucket == STANDBY_BUCKET_NEVER)
&& predicted) {
return;
@@ -1103,6 +1144,50 @@
return;
}
+ final boolean isForcedByUser =
+ (reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER;
+
+ // If the current bucket is RESTRICTED, only user force or usage should bring it out.
+ if (app.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage(reason)
+ && !isForcedByUser) {
+ return;
+ }
+
+ if (newBucket == STANDBY_BUCKET_RESTRICTED) {
+ mAppIdleHistory
+ .noteRestrictionAttempt(packageName, userId, elapsedRealtime, reason);
+
+ if (isForcedByUser) {
+ // Only user force can bypass the delay restriction. If the user forced the
+ // app into the RESTRICTED bucket, then a toast confirming the action
+ // shouldn't be surprising.
+ if (Build.IS_DEBUGGABLE) {
+ Toast.makeText(mContext,
+ // Since AppStandbyController sits low in the lock hierarchy,
+ // make sure not to call out with the lock held.
+ mHandler.getLooper(),
+ mContext.getResources().getString(
+ R.string.as_app_forced_to_restricted_bucket, packageName),
+ Toast.LENGTH_SHORT)
+ .show();
+ } else {
+ Slog.i(TAG, packageName + " restricted by user");
+ }
+ } else {
+ final long timeUntilRestrictPossibleMs = app.lastUsedByUserElapsedTime
+ + mInjector.getRestrictedBucketDelayMs() - elapsedRealtime;
+ if (timeUntilRestrictPossibleMs > 0) {
+ Slog.w(TAG, "Tried to restrict recently used app: " + packageName
+ + " due to " + reason);
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(
+ MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, packageName),
+ timeUntilRestrictPossibleMs);
+ return;
+ }
+ }
+ }
+
// If the bucket is required to stay in a higher state for a specified duration, don't
// override unless the duration has passed
if (predicted) {
@@ -1435,6 +1520,12 @@
private DisplayManager mDisplayManager;
private PowerManager mPowerManager;
int mBootPhase;
+ /**
+ * The minimum amount of time required since the last user interaction before an app can be
+ * placed in the RESTRICTED bucket.
+ */
+ // TODO: make configurable via DeviceConfig
+ private long mRestrictedBucketDelayMs = ONE_DAY;
Injector(Context context, Looper looper) {
mContext = context;
@@ -1459,6 +1550,12 @@
mDisplayManager = (DisplayManager) mContext.getSystemService(
Context.DISPLAY_SERVICE);
mPowerManager = mContext.getSystemService(PowerManager.class);
+
+ final ActivityManager activityManager =
+ (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ if (activityManager.isLowRamDevice() || ActivityManager.isSmallBatteryDevice()) {
+ mRestrictedBucketDelayMs = 12 * ONE_HOUR;
+ }
}
mBootPhase = phase;
}
@@ -1498,6 +1595,10 @@
return Environment.getDataSystemDirectory();
}
+ long getRestrictedBucketDelayMs() {
+ return mRestrictedBucketDelayMs;
+ }
+
void noteEvent(int event, String packageName, int uid) throws RemoteException {
mBatteryStats.noteEvent(event, packageName, uid);
}
diff --git a/apex/permission/apex_manifest.json b/apex/permission/apex_manifest.json
index 2a8c4f7..7960598 100644
--- a/apex/permission/apex_manifest.json
+++ b/apex/permission/apex_manifest.json
@@ -1,4 +1,4 @@
{
"name": "com.android.permission",
- "version": 1
+ "version": 300000000
}
diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
index 51b911a..1dbad45 100644
--- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
+++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ApexContext;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.AtomicFile;
@@ -48,6 +49,8 @@
private static final String LOG_TAG = RuntimePermissionsPersistenceImpl.class.getSimpleName();
+ private static final String APEX_MODULE_NAME = "com.android.permission";
+
private static final String RUNTIME_PERMISSIONS_FILE_NAME = "runtime-permissions.xml";
private static final String TAG_PACKAGE = "package";
@@ -253,9 +256,8 @@
@NonNull
private static File getFile(@NonNull UserHandle user) {
- // TODO: Use an API for this.
- File dataDirectory = new File("/data/misc_de/" + user.getIdentifier()
- + "/apexdata/com.android.permission");
+ ApexContext apexContext = ApexContext.getApexContext(APEX_MODULE_NAME);
+ File dataDirectory = apexContext.getDeviceProtectedDataDirForUser(user);
return new File(dataDirectory, RUNTIME_PERMISSIONS_FILE_NAME);
}
}
diff --git a/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java b/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java
index 5061742..06fad77 100644
--- a/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java
+++ b/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ApexContext;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -50,6 +51,8 @@
private static final String LOG_TAG = RolesPersistenceImpl.class.getSimpleName();
+ private static final String APEX_MODULE_NAME = "com.android.permission";
+
private static final String ROLES_FILE_NAME = "roles.xml";
private static final String TAG_ROLES = "roles";
@@ -209,9 +212,8 @@
@NonNull
private static File getFile(@NonNull UserHandle user) {
- // TODO: Use an API for this.
- File dataDirectory = new File("/data/misc_de/" + user.getIdentifier()
- + "/apexdata/com.android.permission");
+ ApexContext apexContext = ApexContext.getApexContext(APEX_MODULE_NAME);
+ File dataDirectory = apexContext.getDeviceProtectedDataDirForUser(user);
return new File(dataDirectory, ROLES_FILE_NAME);
}
}
diff --git a/apex/sdkextensions/manifest.json b/apex/sdkextensions/manifest.json
index 048f5c4..deeb29e 100644
--- a/apex/sdkextensions/manifest.json
+++ b/apex/sdkextensions/manifest.json
@@ -1,4 +1,4 @@
{
"name": "com.android.sdkext",
- "version": 1
+ "version": 300000000
}
diff --git a/apex/statsd/apex_manifest.json b/apex/statsd/apex_manifest.json
index 0c0ad86..e2972e7 100644
--- a/apex/statsd/apex_manifest.json
+++ b/apex/statsd/apex_manifest.json
@@ -1,5 +1,5 @@
{
"name": "com.android.os.statsd",
- "version": 1
+ "version": 300000000
}
diff --git a/api/current.txt b/api/current.txt
index 5575675..c050347 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -333,6 +333,9 @@
field public static final int autoUrlDetect = 16843404; // 0x101028c
field public static final int autoVerify = 16844014; // 0x10104ee
field public static final int autofillHints = 16844118; // 0x1010556
+ field public static final int autofillInlineSuggestionChip = 16844307; // 0x1010613
+ field public static final int autofillInlineSuggestionSubtitle = 16844309; // 0x1010615
+ field public static final int autofillInlineSuggestionTitle = 16844308; // 0x1010614
field public static final int autofilledHighlight = 16844136; // 0x1010568
field public static final int background = 16842964; // 0x10100d4
field public static final int backgroundDimAmount = 16842802; // 0x1010032
@@ -2253,6 +2256,7 @@
field public static final int ThemeOverlay_Material_Dialog = 16974550; // 0x10302d6
field public static final int ThemeOverlay_Material_Dialog_Alert = 16974551; // 0x10302d7
field public static final int ThemeOverlay_Material_Light = 16974410; // 0x103024a
+ field public static final int Theme_AutofillInlineSuggestion = 16974565; // 0x10302e5
field public static final int Theme_Black = 16973832; // 0x1030008
field public static final int Theme_Black_NoTitleBar = 16973833; // 0x1030009
field public static final int Theme_Black_NoTitleBar_Fullscreen = 16973834; // 0x103000a
@@ -6853,6 +6857,7 @@
method @Nullable public java.util.List<java.lang.String> getPermittedAccessibilityServices(@NonNull android.content.ComponentName);
method @Nullable public java.util.List<java.lang.String> getPermittedCrossProfileNotificationListeners(@NonNull android.content.ComponentName);
method @Nullable public java.util.List<java.lang.String> getPermittedInputMethods(@NonNull android.content.ComponentName);
+ method public int getPersonalAppsSuspendedReasons(@NonNull android.content.ComponentName);
method @NonNull public java.util.List<java.lang.String> getProtectedPackages(@NonNull android.content.ComponentName);
method public long getRequiredStrongAuthTimeout(@Nullable android.content.ComponentName);
method public boolean getScreenCaptureDisabled(@Nullable android.content.ComponentName);
@@ -6979,6 +6984,7 @@
method public boolean setPermittedAccessibilityServices(@NonNull android.content.ComponentName, java.util.List<java.lang.String>);
method public boolean setPermittedCrossProfileNotificationListeners(@NonNull android.content.ComponentName, @Nullable java.util.List<java.lang.String>);
method public boolean setPermittedInputMethods(@NonNull android.content.ComponentName, java.util.List<java.lang.String>);
+ method public void setPersonalAppsSuspended(@NonNull android.content.ComponentName, boolean);
method public void setProfileEnabled(@NonNull android.content.ComponentName);
method public void setProfileName(@NonNull android.content.ComponentName, String);
method public void setProtectedPackages(@NonNull android.content.ComponentName, @NonNull java.util.List<java.lang.String>);
@@ -7014,6 +7020,7 @@
field public static final String ACTION_ADMIN_POLICY_COMPLIANCE = "android.app.action.ADMIN_POLICY_COMPLIANCE";
field public static final String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED";
field public static final String ACTION_BIND_SECONDARY_LOCKSCREEN_SERVICE = "android.app.action.BIND_SECONDARY_LOCKSCREEN_SERVICE";
+ field public static final String ACTION_CHECK_POLICY_COMPLIANCE = "android.app.action.CHECK_POLICY_COMPLIANCE";
field public static final String ACTION_DEVICE_ADMIN_SERVICE = "android.app.action.DEVICE_ADMIN_SERVICE";
field public static final String ACTION_DEVICE_OWNER_CHANGED = "android.app.action.DEVICE_OWNER_CHANGED";
field public static final String ACTION_GET_PROVISIONING_MODE = "android.app.action.GET_PROVISIONING_MODE";
@@ -7137,6 +7144,8 @@
field public static final int PERMISSION_POLICY_AUTO_DENY = 2; // 0x2
field public static final int PERMISSION_POLICY_AUTO_GRANT = 1; // 0x1
field public static final int PERMISSION_POLICY_PROMPT = 0; // 0x0
+ field public static final int PERSONAL_APPS_NOT_SUSPENDED = 0; // 0x0
+ field public static final int PERSONAL_APPS_SUSPENDED_EXPLICITLY = 1; // 0x1
field public static final String POLICY_DISABLE_CAMERA = "policy_disable_camera";
field public static final String POLICY_DISABLE_SCREEN_CAPTURE = "policy_disable_screen_capture";
field public static final int PRIVATE_DNS_MODE_OFF = 1; // 0x1
@@ -7538,6 +7547,7 @@
method public boolean isPackageAccessAllowed(@NonNull String, @NonNull byte[]) throws java.io.IOException;
method public boolean isPublicAccessAllowed() throws java.io.IOException;
method public boolean isSameSignatureAccessAllowed() throws java.io.IOException;
+ method @NonNull public android.os.ParcelFileDescriptor openRead() throws java.io.IOException;
method @NonNull public android.os.ParcelFileDescriptor openWrite(long, long) throws java.io.IOException;
}
@@ -8030,6 +8040,7 @@
field public static final int STANDBY_BUCKET_ACTIVE = 10; // 0xa
field public static final int STANDBY_BUCKET_FREQUENT = 30; // 0x1e
field public static final int STANDBY_BUCKET_RARE = 40; // 0x28
+ field public static final int STANDBY_BUCKET_RESTRICTED = 45; // 0x2d
field public static final int STANDBY_BUCKET_WORKING_SET = 20; // 0x14
}
@@ -9964,6 +9975,7 @@
method public abstract android.content.Context createDisplayContext(@NonNull android.view.Display);
method @NonNull public android.content.Context createFeatureContext(@Nullable String);
method public abstract android.content.Context createPackageContext(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public android.content.Context createWindowContext(int);
method public abstract String[] databaseList();
method public abstract boolean deleteDatabase(String);
method public abstract boolean deleteFile(String);
@@ -9988,6 +10000,7 @@
method public abstract java.io.File getDataDir();
method public abstract java.io.File getDatabasePath(String);
method public abstract java.io.File getDir(String, int);
+ method @Nullable public android.view.Display getDisplay();
method @Nullable public final android.graphics.drawable.Drawable getDrawable(@DrawableRes int);
method @Nullable public abstract java.io.File getExternalCacheDir();
method public abstract java.io.File[] getExternalCacheDirs();
@@ -10100,6 +10113,7 @@
field public static final String CARRIER_CONFIG_SERVICE = "carrier_config";
field public static final String CLIPBOARD_SERVICE = "clipboard";
field public static final String COMPANION_DEVICE_SERVICE = "companiondevice";
+ field public static final String CONNECTIVITY_DIAGNOSTICS_SERVICE = "connectivity_diagnostics";
field public static final String CONNECTIVITY_SERVICE = "connectivity";
field public static final String CONSUMER_IR_SERVICE = "consumer_ir";
field public static final int CONTEXT_IGNORE_SECURITY = 2; // 0x2
@@ -11769,6 +11783,7 @@
method @Nullable public CharSequence getAppLabel();
method @Nullable public String getAppPackageName();
method @NonNull public int[] getChildSessionIds();
+ method public long getCreatedMillis();
method public int getInstallLocation();
method public int getInstallReason();
method @Nullable public String getInstallerPackageName();
@@ -11885,7 +11900,7 @@
method @NonNull public abstract CharSequence getApplicationLabel(@NonNull android.content.pm.ApplicationInfo);
method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull android.content.pm.ApplicationInfo);
method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
- method @NonNull public CharSequence getBackgroundPermissionButtonLabel();
+ method @NonNull public CharSequence getBackgroundPermissionOptionLabel();
method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int);
method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName);
method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
@@ -12076,6 +12091,7 @@
field public static final String FEATURE_VR_HEADTRACKING = "android.hardware.vr.headtracking";
field @Deprecated public static final String FEATURE_VR_MODE = "android.software.vr.mode";
field public static final String FEATURE_VR_MODE_HIGH_PERFORMANCE = "android.hardware.vr.high_performance";
+ field public static final String FEATURE_VULKAN_DEQP_LEVEL = "android.software.vulkan.deqp.level";
field public static final String FEATURE_VULKAN_HARDWARE_COMPUTE = "android.hardware.vulkan.compute";
field public static final String FEATURE_VULKAN_HARDWARE_LEVEL = "android.hardware.vulkan.level";
field public static final String FEATURE_VULKAN_HARDWARE_VERSION = "android.hardware.vulkan.version";
@@ -13514,227 +13530,227 @@
package android.drm {
- public class DrmConvertedStatus {
- ctor public DrmConvertedStatus(int, byte[], int);
- field public static final int STATUS_ERROR = 3; // 0x3
- field public static final int STATUS_INPUTDATA_ERROR = 2; // 0x2
- field public static final int STATUS_OK = 1; // 0x1
- field public final byte[] convertedData;
- field public final int offset;
- field public final int statusCode;
+ @Deprecated public class DrmConvertedStatus {
+ ctor @Deprecated public DrmConvertedStatus(int, byte[], int);
+ field @Deprecated public static final int STATUS_ERROR = 3; // 0x3
+ field @Deprecated public static final int STATUS_INPUTDATA_ERROR = 2; // 0x2
+ field @Deprecated public static final int STATUS_OK = 1; // 0x1
+ field @Deprecated public final byte[] convertedData;
+ field @Deprecated public final int offset;
+ field @Deprecated public final int statusCode;
}
- public class DrmErrorEvent extends android.drm.DrmEvent {
- ctor public DrmErrorEvent(int, int, String);
- ctor public DrmErrorEvent(int, int, String, java.util.HashMap<java.lang.String,java.lang.Object>);
- field public static final int TYPE_ACQUIRE_DRM_INFO_FAILED = 2008; // 0x7d8
- field public static final int TYPE_NOT_SUPPORTED = 2003; // 0x7d3
- field public static final int TYPE_NO_INTERNET_CONNECTION = 2005; // 0x7d5
- field public static final int TYPE_OUT_OF_MEMORY = 2004; // 0x7d4
- field public static final int TYPE_PROCESS_DRM_INFO_FAILED = 2006; // 0x7d6
- field public static final int TYPE_REMOVE_ALL_RIGHTS_FAILED = 2007; // 0x7d7
- field public static final int TYPE_RIGHTS_NOT_INSTALLED = 2001; // 0x7d1
- field public static final int TYPE_RIGHTS_RENEWAL_NOT_ALLOWED = 2002; // 0x7d2
+ @Deprecated public class DrmErrorEvent extends android.drm.DrmEvent {
+ ctor @Deprecated public DrmErrorEvent(int, int, String);
+ ctor @Deprecated public DrmErrorEvent(int, int, String, java.util.HashMap<java.lang.String,java.lang.Object>);
+ field @Deprecated public static final int TYPE_ACQUIRE_DRM_INFO_FAILED = 2008; // 0x7d8
+ field @Deprecated public static final int TYPE_NOT_SUPPORTED = 2003; // 0x7d3
+ field @Deprecated public static final int TYPE_NO_INTERNET_CONNECTION = 2005; // 0x7d5
+ field @Deprecated public static final int TYPE_OUT_OF_MEMORY = 2004; // 0x7d4
+ field @Deprecated public static final int TYPE_PROCESS_DRM_INFO_FAILED = 2006; // 0x7d6
+ field @Deprecated public static final int TYPE_REMOVE_ALL_RIGHTS_FAILED = 2007; // 0x7d7
+ field @Deprecated public static final int TYPE_RIGHTS_NOT_INSTALLED = 2001; // 0x7d1
+ field @Deprecated public static final int TYPE_RIGHTS_RENEWAL_NOT_ALLOWED = 2002; // 0x7d2
}
- public class DrmEvent {
- ctor protected DrmEvent(int, int, String, java.util.HashMap<java.lang.String,java.lang.Object>);
- ctor protected DrmEvent(int, int, String);
- method public Object getAttribute(String);
- method public String getMessage();
- method public int getType();
- method public int getUniqueId();
- field public static final String DRM_INFO_OBJECT = "drm_info_object";
- field public static final String DRM_INFO_STATUS_OBJECT = "drm_info_status_object";
- field public static final int TYPE_ALL_RIGHTS_REMOVED = 1001; // 0x3e9
- field public static final int TYPE_DRM_INFO_PROCESSED = 1002; // 0x3ea
+ @Deprecated public class DrmEvent {
+ ctor @Deprecated protected DrmEvent(int, int, String, java.util.HashMap<java.lang.String,java.lang.Object>);
+ ctor @Deprecated protected DrmEvent(int, int, String);
+ method @Deprecated public Object getAttribute(String);
+ method @Deprecated public String getMessage();
+ method @Deprecated public int getType();
+ method @Deprecated public int getUniqueId();
+ field @Deprecated public static final String DRM_INFO_OBJECT = "drm_info_object";
+ field @Deprecated public static final String DRM_INFO_STATUS_OBJECT = "drm_info_status_object";
+ field @Deprecated public static final int TYPE_ALL_RIGHTS_REMOVED = 1001; // 0x3e9
+ field @Deprecated public static final int TYPE_DRM_INFO_PROCESSED = 1002; // 0x3ea
}
- public class DrmInfo {
- ctor public DrmInfo(int, byte[], String);
- ctor public DrmInfo(int, String, String);
- method public Object get(String);
- method public byte[] getData();
- method public int getInfoType();
- method public String getMimeType();
- method public java.util.Iterator<java.lang.Object> iterator();
- method public java.util.Iterator<java.lang.String> keyIterator();
- method public void put(String, Object);
+ @Deprecated public class DrmInfo {
+ ctor @Deprecated public DrmInfo(int, byte[], String);
+ ctor @Deprecated public DrmInfo(int, String, String);
+ method @Deprecated public Object get(String);
+ method @Deprecated public byte[] getData();
+ method @Deprecated public int getInfoType();
+ method @Deprecated public String getMimeType();
+ method @Deprecated public java.util.Iterator<java.lang.Object> iterator();
+ method @Deprecated public java.util.Iterator<java.lang.String> keyIterator();
+ method @Deprecated public void put(String, Object);
}
- public class DrmInfoEvent extends android.drm.DrmEvent {
- ctor public DrmInfoEvent(int, int, String);
- ctor public DrmInfoEvent(int, int, String, java.util.HashMap<java.lang.String,java.lang.Object>);
- field public static final int TYPE_ACCOUNT_ALREADY_REGISTERED = 5; // 0x5
- field public static final int TYPE_ALREADY_REGISTERED_BY_ANOTHER_ACCOUNT = 1; // 0x1
- field public static final int TYPE_REMOVE_RIGHTS = 2; // 0x2
- field public static final int TYPE_RIGHTS_INSTALLED = 3; // 0x3
- field public static final int TYPE_RIGHTS_REMOVED = 6; // 0x6
- field public static final int TYPE_WAIT_FOR_RIGHTS = 4; // 0x4
+ @Deprecated public class DrmInfoEvent extends android.drm.DrmEvent {
+ ctor @Deprecated public DrmInfoEvent(int, int, String);
+ ctor @Deprecated public DrmInfoEvent(int, int, String, java.util.HashMap<java.lang.String,java.lang.Object>);
+ field @Deprecated public static final int TYPE_ACCOUNT_ALREADY_REGISTERED = 5; // 0x5
+ field @Deprecated public static final int TYPE_ALREADY_REGISTERED_BY_ANOTHER_ACCOUNT = 1; // 0x1
+ field @Deprecated public static final int TYPE_REMOVE_RIGHTS = 2; // 0x2
+ field @Deprecated public static final int TYPE_RIGHTS_INSTALLED = 3; // 0x3
+ field @Deprecated public static final int TYPE_RIGHTS_REMOVED = 6; // 0x6
+ field @Deprecated public static final int TYPE_WAIT_FOR_RIGHTS = 4; // 0x4
}
- public class DrmInfoRequest {
- ctor public DrmInfoRequest(int, String);
- method public Object get(String);
- method public int getInfoType();
- method public String getMimeType();
- method public java.util.Iterator<java.lang.Object> iterator();
- method public java.util.Iterator<java.lang.String> keyIterator();
- method public void put(String, Object);
- field public static final String ACCOUNT_ID = "account_id";
- field public static final String SUBSCRIPTION_ID = "subscription_id";
- field public static final int TYPE_REGISTRATION_INFO = 1; // 0x1
- field public static final int TYPE_RIGHTS_ACQUISITION_INFO = 3; // 0x3
- field public static final int TYPE_RIGHTS_ACQUISITION_PROGRESS_INFO = 4; // 0x4
- field public static final int TYPE_UNREGISTRATION_INFO = 2; // 0x2
+ @Deprecated public class DrmInfoRequest {
+ ctor @Deprecated public DrmInfoRequest(int, String);
+ method @Deprecated public Object get(String);
+ method @Deprecated public int getInfoType();
+ method @Deprecated public String getMimeType();
+ method @Deprecated public java.util.Iterator<java.lang.Object> iterator();
+ method @Deprecated public java.util.Iterator<java.lang.String> keyIterator();
+ method @Deprecated public void put(String, Object);
+ field @Deprecated public static final String ACCOUNT_ID = "account_id";
+ field @Deprecated public static final String SUBSCRIPTION_ID = "subscription_id";
+ field @Deprecated public static final int TYPE_REGISTRATION_INFO = 1; // 0x1
+ field @Deprecated public static final int TYPE_RIGHTS_ACQUISITION_INFO = 3; // 0x3
+ field @Deprecated public static final int TYPE_RIGHTS_ACQUISITION_PROGRESS_INFO = 4; // 0x4
+ field @Deprecated public static final int TYPE_UNREGISTRATION_INFO = 2; // 0x2
}
- public class DrmInfoStatus {
- ctor public DrmInfoStatus(int, int, android.drm.ProcessedData, String);
- field public static final int STATUS_ERROR = 2; // 0x2
- field public static final int STATUS_OK = 1; // 0x1
- field public final android.drm.ProcessedData data;
- field public final int infoType;
- field public final String mimeType;
- field public final int statusCode;
+ @Deprecated public class DrmInfoStatus {
+ ctor @Deprecated public DrmInfoStatus(int, int, android.drm.ProcessedData, String);
+ field @Deprecated public static final int STATUS_ERROR = 2; // 0x2
+ field @Deprecated public static final int STATUS_OK = 1; // 0x1
+ field @Deprecated public final android.drm.ProcessedData data;
+ field @Deprecated public final int infoType;
+ field @Deprecated public final String mimeType;
+ field @Deprecated public final int statusCode;
}
- public class DrmManagerClient implements java.lang.AutoCloseable {
- ctor public DrmManagerClient(android.content.Context);
- method public android.drm.DrmInfo acquireDrmInfo(android.drm.DrmInfoRequest);
- method public int acquireRights(android.drm.DrmInfoRequest);
- method public boolean canHandle(String, String);
- method public boolean canHandle(android.net.Uri, String);
- method public int checkRightsStatus(String);
- method public int checkRightsStatus(android.net.Uri);
- method public int checkRightsStatus(String, int);
- method public int checkRightsStatus(android.net.Uri, int);
- method public void close();
- method public android.drm.DrmConvertedStatus closeConvertSession(int);
- method public android.drm.DrmConvertedStatus convertData(int, byte[]);
- method public String[] getAvailableDrmEngines();
- method @NonNull public java.util.Collection<android.drm.DrmSupportInfo> getAvailableDrmSupportInfo();
- method public android.content.ContentValues getConstraints(String, int);
- method public android.content.ContentValues getConstraints(android.net.Uri, int);
- method public int getDrmObjectType(String, String);
- method public int getDrmObjectType(android.net.Uri, String);
- method public android.content.ContentValues getMetadata(String);
- method public android.content.ContentValues getMetadata(android.net.Uri);
- method public String getOriginalMimeType(String);
- method public String getOriginalMimeType(android.net.Uri);
- method public int openConvertSession(String);
- method public int processDrmInfo(android.drm.DrmInfo);
+ @Deprecated public class DrmManagerClient implements java.lang.AutoCloseable {
+ ctor @Deprecated public DrmManagerClient(android.content.Context);
+ method @Deprecated public android.drm.DrmInfo acquireDrmInfo(android.drm.DrmInfoRequest);
+ method @Deprecated public int acquireRights(android.drm.DrmInfoRequest);
+ method @Deprecated public boolean canHandle(String, String);
+ method @Deprecated public boolean canHandle(android.net.Uri, String);
+ method @Deprecated public int checkRightsStatus(String);
+ method @Deprecated public int checkRightsStatus(android.net.Uri);
+ method @Deprecated public int checkRightsStatus(String, int);
+ method @Deprecated public int checkRightsStatus(android.net.Uri, int);
+ method @Deprecated public void close();
+ method @Deprecated public android.drm.DrmConvertedStatus closeConvertSession(int);
+ method @Deprecated public android.drm.DrmConvertedStatus convertData(int, byte[]);
+ method @Deprecated public String[] getAvailableDrmEngines();
+ method @Deprecated @NonNull public java.util.Collection<android.drm.DrmSupportInfo> getAvailableDrmSupportInfo();
+ method @Deprecated public android.content.ContentValues getConstraints(String, int);
+ method @Deprecated public android.content.ContentValues getConstraints(android.net.Uri, int);
+ method @Deprecated public int getDrmObjectType(String, String);
+ method @Deprecated public int getDrmObjectType(android.net.Uri, String);
+ method @Deprecated public android.content.ContentValues getMetadata(String);
+ method @Deprecated public android.content.ContentValues getMetadata(android.net.Uri);
+ method @Deprecated public String getOriginalMimeType(String);
+ method @Deprecated public String getOriginalMimeType(android.net.Uri);
+ method @Deprecated public int openConvertSession(String);
+ method @Deprecated public int processDrmInfo(android.drm.DrmInfo);
method @Deprecated public void release();
- method public int removeAllRights();
- method public int removeRights(String);
- method public int removeRights(android.net.Uri);
- method public int saveRights(android.drm.DrmRights, String, String) throws java.io.IOException;
- method public void setOnErrorListener(android.drm.DrmManagerClient.OnErrorListener);
- method public void setOnEventListener(android.drm.DrmManagerClient.OnEventListener);
- method public void setOnInfoListener(android.drm.DrmManagerClient.OnInfoListener);
- field public static final int ERROR_NONE = 0; // 0x0
- field public static final int ERROR_UNKNOWN = -2000; // 0xfffff830
+ method @Deprecated public int removeAllRights();
+ method @Deprecated public int removeRights(String);
+ method @Deprecated public int removeRights(android.net.Uri);
+ method @Deprecated public int saveRights(android.drm.DrmRights, String, String) throws java.io.IOException;
+ method @Deprecated public void setOnErrorListener(android.drm.DrmManagerClient.OnErrorListener);
+ method @Deprecated public void setOnEventListener(android.drm.DrmManagerClient.OnEventListener);
+ method @Deprecated public void setOnInfoListener(android.drm.DrmManagerClient.OnInfoListener);
+ field @Deprecated public static final int ERROR_NONE = 0; // 0x0
+ field @Deprecated public static final int ERROR_UNKNOWN = -2000; // 0xfffff830
}
- public static interface DrmManagerClient.OnErrorListener {
- method public void onError(android.drm.DrmManagerClient, android.drm.DrmErrorEvent);
+ @Deprecated public static interface DrmManagerClient.OnErrorListener {
+ method @Deprecated public void onError(android.drm.DrmManagerClient, android.drm.DrmErrorEvent);
}
- public static interface DrmManagerClient.OnEventListener {
- method public void onEvent(android.drm.DrmManagerClient, android.drm.DrmEvent);
+ @Deprecated public static interface DrmManagerClient.OnEventListener {
+ method @Deprecated public void onEvent(android.drm.DrmManagerClient, android.drm.DrmEvent);
}
- public static interface DrmManagerClient.OnInfoListener {
- method public void onInfo(android.drm.DrmManagerClient, android.drm.DrmInfoEvent);
+ @Deprecated public static interface DrmManagerClient.OnInfoListener {
+ method @Deprecated public void onInfo(android.drm.DrmManagerClient, android.drm.DrmInfoEvent);
}
- public class DrmRights {
- ctor public DrmRights(String, String);
- ctor public DrmRights(String, String, String);
- ctor public DrmRights(String, String, String, String);
- ctor public DrmRights(java.io.File, String);
- ctor public DrmRights(android.drm.ProcessedData, String);
- method public String getAccountId();
- method public byte[] getData();
- method public String getMimeType();
- method public String getSubscriptionId();
+ @Deprecated public class DrmRights {
+ ctor @Deprecated public DrmRights(String, String);
+ ctor @Deprecated public DrmRights(String, String, String);
+ ctor @Deprecated public DrmRights(String, String, String, String);
+ ctor @Deprecated public DrmRights(java.io.File, String);
+ ctor @Deprecated public DrmRights(android.drm.ProcessedData, String);
+ method @Deprecated public String getAccountId();
+ method @Deprecated public byte[] getData();
+ method @Deprecated public String getMimeType();
+ method @Deprecated public String getSubscriptionId();
}
- public class DrmStore {
+ @Deprecated public class DrmStore {
ctor @Deprecated public DrmStore();
}
- public static class DrmStore.Action {
+ @Deprecated public static class DrmStore.Action {
ctor @Deprecated public DrmStore.Action();
- field public static final int DEFAULT = 0; // 0x0
- field public static final int DISPLAY = 7; // 0x7
- field public static final int EXECUTE = 6; // 0x6
- field public static final int OUTPUT = 4; // 0x4
- field public static final int PLAY = 1; // 0x1
- field public static final int PREVIEW = 5; // 0x5
- field public static final int RINGTONE = 2; // 0x2
- field public static final int TRANSFER = 3; // 0x3
+ field @Deprecated public static final int DEFAULT = 0; // 0x0
+ field @Deprecated public static final int DISPLAY = 7; // 0x7
+ field @Deprecated public static final int EXECUTE = 6; // 0x6
+ field @Deprecated public static final int OUTPUT = 4; // 0x4
+ field @Deprecated public static final int PLAY = 1; // 0x1
+ field @Deprecated public static final int PREVIEW = 5; // 0x5
+ field @Deprecated public static final int RINGTONE = 2; // 0x2
+ field @Deprecated public static final int TRANSFER = 3; // 0x3
}
- public static interface DrmStore.ConstraintsColumns {
- field public static final String EXTENDED_METADATA = "extended_metadata";
- field public static final String LICENSE_AVAILABLE_TIME = "license_available_time";
- field public static final String LICENSE_EXPIRY_TIME = "license_expiry_time";
- field public static final String LICENSE_START_TIME = "license_start_time";
- field public static final String MAX_REPEAT_COUNT = "max_repeat_count";
- field public static final String REMAINING_REPEAT_COUNT = "remaining_repeat_count";
+ @Deprecated public static interface DrmStore.ConstraintsColumns {
+ field @Deprecated public static final String EXTENDED_METADATA = "extended_metadata";
+ field @Deprecated public static final String LICENSE_AVAILABLE_TIME = "license_available_time";
+ field @Deprecated public static final String LICENSE_EXPIRY_TIME = "license_expiry_time";
+ field @Deprecated public static final String LICENSE_START_TIME = "license_start_time";
+ field @Deprecated public static final String MAX_REPEAT_COUNT = "max_repeat_count";
+ field @Deprecated public static final String REMAINING_REPEAT_COUNT = "remaining_repeat_count";
}
- public static class DrmStore.DrmObjectType {
+ @Deprecated public static class DrmStore.DrmObjectType {
ctor @Deprecated public DrmStore.DrmObjectType();
- field public static final int CONTENT = 1; // 0x1
- field public static final int RIGHTS_OBJECT = 2; // 0x2
- field public static final int TRIGGER_OBJECT = 3; // 0x3
- field public static final int UNKNOWN = 0; // 0x0
+ field @Deprecated public static final int CONTENT = 1; // 0x1
+ field @Deprecated public static final int RIGHTS_OBJECT = 2; // 0x2
+ field @Deprecated public static final int TRIGGER_OBJECT = 3; // 0x3
+ field @Deprecated public static final int UNKNOWN = 0; // 0x0
}
- public static class DrmStore.Playback {
+ @Deprecated public static class DrmStore.Playback {
ctor @Deprecated public DrmStore.Playback();
- field public static final int PAUSE = 2; // 0x2
- field public static final int RESUME = 3; // 0x3
- field public static final int START = 0; // 0x0
- field public static final int STOP = 1; // 0x1
+ field @Deprecated public static final int PAUSE = 2; // 0x2
+ field @Deprecated public static final int RESUME = 3; // 0x3
+ field @Deprecated public static final int START = 0; // 0x0
+ field @Deprecated public static final int STOP = 1; // 0x1
}
- public static class DrmStore.RightsStatus {
+ @Deprecated public static class DrmStore.RightsStatus {
ctor @Deprecated public DrmStore.RightsStatus();
- field public static final int RIGHTS_EXPIRED = 2; // 0x2
- field public static final int RIGHTS_INVALID = 1; // 0x1
- field public static final int RIGHTS_NOT_ACQUIRED = 3; // 0x3
- field public static final int RIGHTS_VALID = 0; // 0x0
+ field @Deprecated public static final int RIGHTS_EXPIRED = 2; // 0x2
+ field @Deprecated public static final int RIGHTS_INVALID = 1; // 0x1
+ field @Deprecated public static final int RIGHTS_NOT_ACQUIRED = 3; // 0x3
+ field @Deprecated public static final int RIGHTS_VALID = 0; // 0x0
}
- public class DrmSupportInfo {
- ctor public DrmSupportInfo();
- method public void addFileSuffix(String);
- method public void addMimeType(String);
+ @Deprecated public class DrmSupportInfo {
+ ctor @Deprecated public DrmSupportInfo();
+ method @Deprecated public void addFileSuffix(String);
+ method @Deprecated public void addMimeType(String);
method @Deprecated public String getDescriprition();
- method public String getDescription();
- method public java.util.Iterator<java.lang.String> getFileSuffixIterator();
- method public java.util.Iterator<java.lang.String> getMimeTypeIterator();
- method public void setDescription(String);
+ method @Deprecated public String getDescription();
+ method @Deprecated public java.util.Iterator<java.lang.String> getFileSuffixIterator();
+ method @Deprecated public java.util.Iterator<java.lang.String> getMimeTypeIterator();
+ method @Deprecated public void setDescription(String);
}
- public class DrmUtils {
- ctor public DrmUtils();
- method public static android.drm.DrmUtils.ExtendedMetadataParser getExtendedMetadataParser(byte[]);
+ @Deprecated public class DrmUtils {
+ ctor @Deprecated public DrmUtils();
+ method @Deprecated public static android.drm.DrmUtils.ExtendedMetadataParser getExtendedMetadataParser(byte[]);
}
- public static class DrmUtils.ExtendedMetadataParser {
- method public String get(String);
- method public java.util.Iterator<java.lang.String> iterator();
- method public java.util.Iterator<java.lang.String> keyIterator();
+ @Deprecated public static class DrmUtils.ExtendedMetadataParser {
+ method @Deprecated public String get(String);
+ method @Deprecated public java.util.Iterator<java.lang.String> iterator();
+ method @Deprecated public java.util.Iterator<java.lang.String> keyIterator();
}
- public class ProcessedData {
- method public String getAccountId();
- method public byte[] getData();
- method public String getSubscriptionId();
+ @Deprecated public class ProcessedData {
+ method @Deprecated public String getAccountId();
+ method @Deprecated public byte[] getData();
+ method @Deprecated public String getSubscriptionId();
}
}
@@ -17248,6 +17264,8 @@
method public void onCameraAccessPrioritiesChanged();
method public void onCameraAvailable(@NonNull String);
method public void onCameraUnavailable(@NonNull String);
+ method public void onPhysicalCameraAvailable(@NonNull String, @NonNull String);
+ method public void onPhysicalCameraUnavailable(@NonNull String, @NonNull String);
}
public abstract static class CameraManager.TorchCallback {
@@ -23379,6 +23397,9 @@
method public long getFullBiasNanos();
method public int getHardwareClockDiscontinuityCount();
method public int getLeapSecond();
+ method @FloatRange(from=0.0) public double getReferenceCarrierFrequencyHzForIsb();
+ method @NonNull public String getReferenceCodeTypeForIsb();
+ method public int getReferenceConstellationTypeForIsb();
method public long getTimeNanos();
method @FloatRange(from=0.0f) public double getTimeUncertaintyNanos();
method public boolean hasBiasNanos();
@@ -23389,6 +23410,9 @@
method public boolean hasElapsedRealtimeUncertaintyNanos();
method public boolean hasFullBiasNanos();
method public boolean hasLeapSecond();
+ method public boolean hasReferenceCarrierFrequencyHzForIsb();
+ method public boolean hasReferenceCodeTypeForIsb();
+ method public boolean hasReferenceConstellationTypeForIsb();
method public boolean hasTimeUncertaintyNanos();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssClock> CREATOR;
@@ -23400,12 +23424,12 @@
method public int getAccumulatedDeltaRangeState();
method public double getAccumulatedDeltaRangeUncertaintyMeters();
method public double getAutomaticGainControlLevelDb();
- method @FloatRange(from=0, to=50) public double getBasebandCn0DbHz();
+ method @FloatRange(from=0, to=63) public double getBasebandCn0DbHz();
method @Deprecated public long getCarrierCycles();
method public float getCarrierFrequencyHz();
method @Deprecated public double getCarrierPhase();
method @Deprecated public double getCarrierPhaseUncertainty();
- method public double getCn0DbHz();
+ method @FloatRange(from=0, to=63) public double getCn0DbHz();
method @NonNull public String getCodeType();
method public int getConstellationType();
method public int getMultipathIndicator();
@@ -23413,6 +23437,10 @@
method public double getPseudorangeRateUncertaintyMetersPerSecond();
method public long getReceivedSvTimeNanos();
method public long getReceivedSvTimeUncertaintyNanos();
+ method public double getReceiverInterSignalBiasNanos();
+ method @FloatRange(from=0.0) public double getReceiverInterSignalBiasUncertaintyNanos();
+ method public double getSatelliteInterSignalBiasNanos();
+ method @FloatRange(from=0.0) public double getSatelliteInterSignalBiasUncertaintyNanos();
method public double getSnrInDb();
method public int getState();
method public int getSvid();
@@ -23424,6 +23452,10 @@
method @Deprecated public boolean hasCarrierPhase();
method @Deprecated public boolean hasCarrierPhaseUncertainty();
method public boolean hasCodeType();
+ method public boolean hasReceiverInterSignalBiasNanos();
+ method public boolean hasReceiverInterSignalBiasUncertaintyNanos();
+ method public boolean hasSatelliteInterSignalBiasNanos();
+ method public boolean hasSatelliteInterSignalBiasUncertaintyNanos();
method public boolean hasSnrInDb();
method public void writeToParcel(android.os.Parcel, int);
field public static final int ADR_STATE_CYCLE_SLIP = 4; // 0x4
@@ -23487,6 +23519,8 @@
field public static final int STATUS_PARITY_PASSED = 1; // 0x1
field public static final int STATUS_PARITY_REBUILT = 2; // 0x2
field public static final int STATUS_UNKNOWN = 0; // 0x0
+ field public static final int TYPE_BDS_CNAV1 = 1283; // 0x503
+ field public static final int TYPE_BDS_CNAV2 = 1284; // 0x504
field public static final int TYPE_BDS_D1 = 1281; // 0x501
field public static final int TYPE_BDS_D2 = 1282; // 0x502
field public static final int TYPE_GAL_F = 1538; // 0x602
@@ -23496,6 +23530,9 @@
field public static final int TYPE_GPS_L1CA = 257; // 0x101
field public static final int TYPE_GPS_L2CNAV = 258; // 0x102
field public static final int TYPE_GPS_L5CNAV = 259; // 0x103
+ field public static final int TYPE_IRN_L5CA = 1793; // 0x701
+ field public static final int TYPE_QZS_L1CA = 1025; // 0x401
+ field public static final int TYPE_SBS = 513; // 0x201
field public static final int TYPE_UNKNOWN = 0; // 0x0
}
@@ -23822,6 +23859,8 @@
method @NonNull public int[] getChannelCounts();
method @NonNull public int[] getChannelIndexMasks();
method @NonNull public int[] getChannelMasks();
+ method @NonNull public int[] getEncapsulationMetadataTypes();
+ method @NonNull public int[] getEncapsulationModes();
method @NonNull public int[] getEncodings();
method public int getId();
method public CharSequence getProductName();
@@ -24179,8 +24218,10 @@
public final class AudioPlaybackCaptureConfiguration {
method @NonNull public int[] getExcludeUids();
method @NonNull public int[] getExcludeUsages();
+ method @NonNull public int[] getExcludeUserIds();
method @NonNull public int[] getMatchingUids();
method @NonNull public int[] getMatchingUsages();
+ method @NonNull public int[] getMatchingUserIds();
method @NonNull public android.media.projection.MediaProjection getMediaProjection();
}
@@ -24188,9 +24229,11 @@
ctor public AudioPlaybackCaptureConfiguration.Builder(@NonNull android.media.projection.MediaProjection);
method @NonNull public android.media.AudioPlaybackCaptureConfiguration.Builder addMatchingUid(int);
method @NonNull public android.media.AudioPlaybackCaptureConfiguration.Builder addMatchingUsage(int);
+ method @NonNull public android.media.AudioPlaybackCaptureConfiguration.Builder addMatchingUserId(int);
method @NonNull public android.media.AudioPlaybackCaptureConfiguration build();
method @NonNull public android.media.AudioPlaybackCaptureConfiguration.Builder excludeUid(int);
method @NonNull public android.media.AudioPlaybackCaptureConfiguration.Builder excludeUsage(int);
+ method @NonNull public android.media.AudioPlaybackCaptureConfiguration.Builder excludeUserId(int);
}
public final class AudioPlaybackConfiguration implements android.os.Parcelable {
@@ -24369,12 +24412,14 @@
method protected void finalize();
method public void flush();
method @NonNull public android.media.AudioAttributes getAudioAttributes();
+ method public float getAudioDescriptionMixLeveldB();
method public int getAudioFormat();
method public int getAudioSessionId();
method @IntRange(from=0) public int getBufferCapacityInFrames();
method @IntRange(from=0) public int getBufferSizeInFrames();
method public int getChannelConfiguration();
method public int getChannelCount();
+ method public int getDualMonoMode();
method @NonNull public android.media.AudioFormat getFormat();
method public static float getMaxVolume();
method public android.os.PersistableBundle getMetrics();
@@ -24408,8 +24453,10 @@
method public void removeOnCodecFormatChangedListener(@NonNull android.media.AudioTrack.OnCodecFormatChangedListener);
method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
method @Deprecated public void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
+ method public boolean setAudioDescriptionMixLeveldB(@FloatRange(to=48.0f, toInclusive=true) float);
method public int setAuxEffectSendLevel(@FloatRange(from=0.0) float);
method public int setBufferSizeInFrames(@IntRange(from=0) int);
+ method public boolean setDualMonoMode(int);
method public int setLoopPoints(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0xffffffff) int);
method public int setNotificationMarkerPosition(int);
method public void setOffloadDelayPadding(@IntRange(from=0) int, @IntRange(from=0) int);
@@ -24434,6 +24481,12 @@
method public int write(@NonNull float[], int, int, int);
method public int write(@NonNull java.nio.ByteBuffer, int, int);
method public int write(@NonNull java.nio.ByteBuffer, int, int, long);
+ field public static final int DUAL_MONO_MODE_LL = 2; // 0x2
+ field public static final int DUAL_MONO_MODE_LR = 1; // 0x1
+ field public static final int DUAL_MONO_MODE_OFF = 0; // 0x0
+ field public static final int DUAL_MONO_MODE_RR = 3; // 0x3
+ field public static final int ENCAPSULATION_METADATA_TYPE_DVB_AD_DESCRIPTOR = 2; // 0x2
+ field public static final int ENCAPSULATION_METADATA_TYPE_FRAMEWORK_TUNER = 1; // 0x1
field public static final int ENCAPSULATION_MODE_ELEMENTARY_STREAM = 1; // 0x1
field public static final int ENCAPSULATION_MODE_HANDLE = 2; // 0x2
field public static final int ENCAPSULATION_MODE_NONE = 0; // 0x0
@@ -24983,7 +25036,9 @@
method @Deprecated @NonNull public java.nio.ByteBuffer[] getOutputBuffers();
method @NonNull public android.media.MediaFormat getOutputFormat();
method @NonNull public android.media.MediaFormat getOutputFormat(int);
+ method @NonNull public android.media.MediaCodec.OutputFrame getOutputFrame(int);
method @Nullable public android.media.Image getOutputImage(int);
+ method @NonNull public android.media.MediaCodec.QueueRequest getQueueRequest(int);
method public void queueInputBuffer(int, int, int, long, int) throws android.media.MediaCodec.CryptoException;
method public void queueSecureInputBuffer(int, int, @NonNull android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException;
method public void release();
@@ -25007,6 +25062,7 @@
field public static final int BUFFER_FLAG_PARTIAL_FRAME = 8; // 0x8
field @Deprecated public static final int BUFFER_FLAG_SYNC_FRAME = 1; // 0x1
field public static final int CONFIGURE_FLAG_ENCODE = 1; // 0x1
+ field public static final int CONFIGURE_FLAG_USE_BLOCK_MODEL = 2; // 0x2
field public static final int CRYPTO_MODE_AES_CBC = 2; // 0x2
field public static final int CRYPTO_MODE_AES_CTR = 1; // 0x1
field public static final int CRYPTO_MODE_UNENCRYPTED = 0; // 0x0
@@ -25083,6 +25139,24 @@
method public void set(int, int);
}
+ public static final class MediaCodec.GraphicBlock {
+ method protected void finalize();
+ method public static boolean isCodecCopyFreeCompatible(@NonNull String[]);
+ method public boolean isMappable();
+ method @NonNull public android.media.Image map();
+ method @NonNull public static android.media.MediaCodec.GraphicBlock obtain(int, int, int, long, @NonNull String[]);
+ method public void recycle();
+ }
+
+ public static final class MediaCodec.LinearBlock {
+ method protected void finalize();
+ method public static boolean isCodecCopyFreeCompatible(@NonNull String[]);
+ method public boolean isMappable();
+ method @NonNull public java.nio.ByteBuffer map();
+ method @Nullable public static android.media.MediaCodec.LinearBlock obtain(int, @NonNull String[]);
+ method public void recycle();
+ }
+
public static final class MediaCodec.MetricsConstants {
field public static final String CODEC = "android.media.mediacodec.codec";
field public static final String ENCODER = "android.media.mediacodec.encoder";
@@ -25100,6 +25174,27 @@
method public void onFrameRendered(@NonNull android.media.MediaCodec, long, long);
}
+ public static final class MediaCodec.OutputFrame {
+ method public void getChangedKeys(@NonNull java.util.Set<java.lang.String>);
+ method public int getFlags();
+ method @NonNull public android.media.MediaFormat getFormat();
+ method @Nullable public android.media.MediaCodec.GraphicBlock getGraphicBlock();
+ method @Nullable public android.media.MediaCodec.LinearBlock getLinearBlock();
+ method public long getPresentationTimeUs();
+ }
+
+ public final class MediaCodec.QueueRequest {
+ method public void queue();
+ method @NonNull public android.media.MediaCodec.QueueRequest setByteBufferParameter(@NonNull String, @NonNull java.nio.ByteBuffer);
+ method @NonNull public android.media.MediaCodec.QueueRequest setEncryptedLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, @NonNull android.media.MediaCodec.CryptoInfo, long, int);
+ method @NonNull public android.media.MediaCodec.QueueRequest setFloatParameter(@NonNull String, float);
+ method @NonNull public android.media.MediaCodec.QueueRequest setGraphicBlock(@NonNull android.media.MediaCodec.GraphicBlock, long, int);
+ method @NonNull public android.media.MediaCodec.QueueRequest setIntegerParameter(@NonNull String, int);
+ method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, long, int);
+ method @NonNull public android.media.MediaCodec.QueueRequest setLongParameter(@NonNull String, long);
+ method @NonNull public android.media.MediaCodec.QueueRequest setStringParameter(@NonNull String, @NonNull String);
+ }
+
public final class MediaCodecInfo {
method @NonNull public String getCanonicalName();
method public android.media.MediaCodecInfo.CodecCapabilities getCapabilitiesForType(String);
@@ -25893,6 +25988,7 @@
field public static final String KEY_GRID_COLUMNS = "grid-cols";
field public static final String KEY_GRID_ROWS = "grid-rows";
field public static final String KEY_HAPTIC_CHANNEL_COUNT = "haptic-channel-count";
+ field public static final String KEY_HARDWARE_AV_SYNC_ID = "hw-av-sync-id";
field public static final String KEY_HDR10_PLUS_INFO = "hdr10-plus-info";
field public static final String KEY_HDR_STATIC_INFO = "hdr-static-info";
field public static final String KEY_HEIGHT = "height";
@@ -27559,6 +27655,8 @@
field public static final int CONTENT_TYPE_VOICE = 3; // 0x3
field public static final String EFFECT_AUXILIARY = "Auxiliary";
field public static final String EFFECT_INSERT = "Insert";
+ field public static final String EFFECT_POST_PROCESSING = "Post Processing";
+ field public static final String EFFECT_PRE_PROCESSING = "Pre Processing";
field public static final java.util.UUID EFFECT_TYPE_AEC;
field public static final java.util.UUID EFFECT_TYPE_AGC;
field public static final java.util.UUID EFFECT_TYPE_BASS_BOOST;
@@ -29490,8 +29588,6 @@
public class ConnectivityDiagnosticsManager {
method public void registerConnectivityDiagnosticsCallback(@NonNull android.net.NetworkRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback);
method public void unregisterConnectivityDiagnosticsCallback(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback);
- field public static final int DETECTION_METHOD_DNS_EVENTS = 1; // 0x1
- field public static final int DETECTION_METHOD_TCP_METRICS = 2; // 0x2
}
public abstract static class ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback {
@@ -29501,21 +29597,29 @@
method public void onNetworkConnectivityReported(@NonNull android.net.Network, boolean);
}
- public static class ConnectivityDiagnosticsManager.ConnectivityReport {
+ public static final class ConnectivityDiagnosticsManager.ConnectivityReport implements android.os.Parcelable {
ctor public ConnectivityDiagnosticsManager.ConnectivityReport(@NonNull android.net.Network, long, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle);
- field @NonNull public final android.os.PersistableBundle additionalInfo;
- field @NonNull public final android.net.LinkProperties linkProperties;
- field @NonNull public final android.net.Network network;
- field @NonNull public final android.net.NetworkCapabilities networkCapabilities;
- field public final long reportTimestamp;
+ method public int describeContents();
+ method @NonNull public android.os.PersistableBundle getAdditionalInfo();
+ method @NonNull public android.net.LinkProperties getLinkProperties();
+ method @NonNull public android.net.Network getNetwork();
+ method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
+ method public long getReportTimestamp();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.ConnectivityReport> CREATOR;
}
- public static class ConnectivityDiagnosticsManager.DataStallReport {
+ public static final class ConnectivityDiagnosticsManager.DataStallReport implements android.os.Parcelable {
ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.os.PersistableBundle);
- field public final int detectionMethod;
- field @NonNull public final android.net.Network network;
- field public final long reportTimestamp;
- field @NonNull public final android.os.PersistableBundle stallDetails;
+ method public int describeContents();
+ method public int getDetectionMethod();
+ method @NonNull public android.net.Network getNetwork();
+ method public long getReportTimestamp();
+ method @NonNull public android.os.PersistableBundle getStallDetails();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.DataStallReport> CREATOR;
+ field public static final int DETECTION_METHOD_DNS_EVENTS = 1; // 0x1
+ field public static final int DETECTION_METHOD_TCP_METRICS = 2; // 0x2
}
public class ConnectivityManager {
@@ -31013,7 +31117,7 @@
method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration);
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE}) public void addSuggestionConnectionStatusListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SuggestionConnectionStatusListener);
method @Deprecated public static int calculateSignalLevel(int, int);
- method public int calculateSignalLevel(int);
+ method @IntRange(from=0) public int calculateSignalLevel(int);
method @Deprecated public void cancelWps(android.net.wifi.WifiManager.WpsCallback);
method public static int compareSignalLevel(int, int);
method public android.net.wifi.WifiManager.MulticastLock createMulticastLock(String);
@@ -31026,7 +31130,7 @@
method public android.net.wifi.WifiInfo getConnectionInfo();
method public android.net.DhcpInfo getDhcpInfo();
method public int getMaxNumberOfNetworkSuggestionsPerApp();
- method public int getMaxSignalLevel();
+ method @IntRange(from=0) public int getMaxSignalLevel();
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public java.util.List<android.net.wifi.WifiNetworkSuggestion> getNetworkSuggestions();
method @Deprecated @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", "android.permission.NETWORK_SETUP_WIZARD"}) public java.util.List<android.net.wifi.hotspot2.PasspointConfiguration> getPasspointConfigurations();
method public java.util.List<android.net.wifi.ScanResult> getScanResults();
@@ -43467,7 +43571,7 @@
field public static final int STATE_HARDWARE_UNAVAILABLE = -2; // 0xfffffffe
field public static final int STATE_KEYPHRASE_ENROLLED = 2; // 0x2
field public static final int STATE_KEYPHRASE_UNENROLLED = 1; // 0x1
- field public static final int STATE_KEYPHRASE_UNSUPPORTED = -1; // 0xffffffff
+ field @Deprecated public static final int STATE_KEYPHRASE_UNSUPPORTED = -1; // 0xffffffff
}
public abstract static class AlwaysOnHotwordDetector.Callback {
@@ -43686,6 +43790,7 @@
method public void onSurfaceRedrawNeeded(android.view.SurfaceHolder);
method public void onTouchEvent(android.view.MotionEvent);
method public void onVisibilityChanged(boolean);
+ method public void onZoomChanged(@FloatRange(from=0.0f, to=1.0f) float);
method public void setOffsetNotificationsEnabled(boolean);
method public void setTouchEventsEnabled(boolean);
}
@@ -44829,6 +44934,7 @@
field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
+ field public static final int PROPERTY_IS_ADHOC_CONFERENCE = 8192; // 0x2000
field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 2048; // 0x800
field public static final int PROPERTY_RTT = 1024; // 0x400
@@ -44906,6 +45012,7 @@
public abstract class Conference extends android.telecom.Conferenceable {
ctor public Conference(android.telecom.PhoneAccountHandle);
method public final boolean addConnection(android.telecom.Connection);
+ method @NonNull public static android.telecom.Conference createFailedConference(@NonNull android.telecom.DisconnectCause, @NonNull android.telecom.PhoneAccountHandle);
method public final void destroy();
method public final android.telecom.CallAudioState getCallAudioState();
method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
@@ -44920,6 +45027,8 @@
method public final android.telecom.StatusHints getStatusHints();
method public android.telecom.Connection.VideoProvider getVideoProvider();
method public int getVideoState();
+ method public final boolean isRingbackRequested();
+ method public void onAnswer(int);
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onConnectionAdded(android.telecom.Connection);
method public void onDisconnect();
@@ -44928,6 +45037,7 @@
method public void onMerge(android.telecom.Connection);
method public void onMerge();
method public void onPlayDtmfTone(char);
+ method public void onReject();
method public void onSeparate(android.telecom.Connection);
method public void onStopDtmfTone();
method public void onSwap();
@@ -44947,6 +45057,8 @@
method public final void setDisconnected(android.telecom.DisconnectCause);
method public final void setExtras(@Nullable android.os.Bundle);
method public final void setOnHold();
+ method public final void setRingbackRequested(boolean);
+ method public final void setRinging();
method public final void setStatusHints(android.telecom.StatusHints);
method public final void setVideoProvider(android.telecom.Connection, android.telecom.Connection.VideoProvider);
method public final void setVideoState(android.telecom.Connection, int);
@@ -45104,6 +45216,7 @@
field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
field public static final int PROPERTY_HIGH_DEF_AUDIO = 4; // 0x4
+ field public static final int PROPERTY_IS_ADHOC_CONFERENCE = 4096; // 0x1000
field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
field public static final int PROPERTY_IS_RTT = 256; // 0x100
field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 1024; // 0x400
@@ -45176,8 +45289,10 @@
method public android.telecom.PhoneAccountHandle getAccountHandle();
method public android.net.Uri getAddress();
method public android.os.Bundle getExtras();
+ method @Nullable public java.util.List<android.net.Uri> getParticipants();
method public android.telecom.Connection.RttTextStream getRttTextStream();
method public int getVideoState();
+ method public boolean isAdhocConferenceCall();
method public boolean isRequestingRtt();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telecom.ConnectionRequest> CREATOR;
@@ -45197,9 +45312,13 @@
method public void onConference(android.telecom.Connection, android.telecom.Connection);
method public void onConnectionServiceFocusGained();
method public void onConnectionServiceFocusLost();
+ method @Nullable public android.telecom.Conference onCreateIncomingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
+ method public void onCreateIncomingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateIncomingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method @Nullable public android.telecom.Conference onCreateOutgoingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
+ method public void onCreateOutgoingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public void onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
@@ -45314,6 +45433,7 @@
method public boolean supportsUriScheme(String);
method public android.telecom.PhoneAccount.Builder toBuilder();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CAPABILITY_ADHOC_CONFERENCE_CALLING = 16384; // 0x4000
field public static final int CAPABILITY_CALL_PROVIDER = 2; // 0x2
field public static final int CAPABILITY_CALL_SUBJECT = 64; // 0x40
field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
@@ -45509,6 +45629,7 @@
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall(int);
method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
+ method public void addNewIncomingConference(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void cancelMissedCallsNotification();
method public android.content.Intent createManageBlockedNumbersIntent();
method @Deprecated @RequiresPermission(android.Manifest.permission.ANSWER_PHONE_CALLS) public boolean endCall();
@@ -45536,6 +45657,7 @@
method public void registerPhoneAccount(android.telecom.PhoneAccount);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void showInCallScreen(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void silenceRinger();
+ method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void startConference(@NonNull java.util.List<android.net.Uri>, @NonNull android.os.Bundle);
method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
field public static final String ACTION_CHANGE_DEFAULT_DIALER = "android.telecom.action.CHANGE_DEFAULT_DIALER";
field public static final String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
@@ -45856,6 +45978,7 @@
field public static final String KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrp_thresholds_int_array";
field public static final String KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrq_thresholds_int_array";
field public static final String KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY = "5g_nr_sssinr_thresholds_int_array";
+ field public static final String KEY_5G_WATCHDOG_TIME_MS_LONG = "5g_watchdog_time_long";
field public static final String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
field public static final String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
field public static final String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
@@ -45939,6 +46062,7 @@
field public static final String KEY_DATA_LIMIT_NOTIFICATION_BOOL = "data_limit_notification_bool";
field public static final String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
field public static final String KEY_DATA_RAPID_NOTIFICATION_BOOL = "data_rapid_notification_bool";
+ field public static final String KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG = "data_switch_validation_timeout_long";
field public static final String KEY_DATA_WARNING_NOTIFICATION_BOOL = "data_warning_notification_bool";
field public static final String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long";
field public static final String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
@@ -46021,6 +46145,8 @@
field public static final String KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL = "only_auto_select_in_home_network";
field public static final String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
field public static final String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
+ field public static final String KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG = "opportunistic_network_backoff_time_long";
+ field public static final String KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG = "opportunistic_network_data_switch_exit_hysteresis_time_long";
field public static final String KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_HYSTERESIS_TIME_LONG = "opportunistic_network_data_switch_hysteresis_time_long";
field public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_OR_EXIT_HYSTERESIS_TIME_LONG = "opportunistic_network_entry_or_exit_hysteresis_time_long";
field public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_BANDWIDTH_INT = "opportunistic_network_entry_threshold_bandwidth_int";
@@ -46028,6 +46154,9 @@
field public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT = "opportunistic_network_entry_threshold_rssnr_int";
field public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT = "opportunistic_network_exit_threshold_rsrp_int";
field public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT = "opportunistic_network_exit_threshold_rssnr_int";
+ field public static final String KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG = "opportunistic_network_max_backoff_time_long";
+ field public static final String KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG = "opportunistic_network_ping_pong_time_long";
+ field public static final String KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL = "ping_test_before_data_switch_bool";
field public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
field public static final String KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL = "prevent_clir_activation_and_deactivation_code_bool";
field public static final String KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY = "radio_restart_failure_causes_int_array";
@@ -46044,6 +46173,7 @@
field public static final String KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL = "show_call_blocking_disabled_notification_always_bool";
field public static final String KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING = "show_carrier_data_icon_pattern_string";
field public static final String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
+ field public static final String KEY_SHOW_FORWARDED_NUMBER_BOOL = "show_forwarded_number_bool";
field public static final String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool";
field public static final String KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL = "show_ims_registration_status_bool";
field public static final String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
@@ -46054,6 +46184,7 @@
field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
field public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
+ field public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL = "support_adhoc_conference_calls_bool";
field public static final String KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL = "support_clir_network_default_bool";
field public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
field public static final String KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL = "support_emergency_sms_over_ims_bool";
@@ -46063,6 +46194,7 @@
field public static final String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
field public static final String KEY_SUPPORT_TDSCDMA_BOOL = "support_tdscdma_bool";
field public static final String KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY = "support_tdscdma_roaming_networks_string_array";
+ field public static final String KEY_SWITCH_DATA_TO_PRIMARY_IF_PRIMARY_IS_OOS_BOOL = "switch_data_to_primary_if_primary_is_oos_bool";
field public static final String KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL = "treat_downgraded_video_calls_as_video_calls_bool";
field public static final String KEY_TTY_SUPPORTED_BOOL = "tty_supported_bool";
field public static final String KEY_UNLOGGABLE_NUMBERS_STRING_ARRAY = "unloggable_numbers_string_array";
@@ -46088,6 +46220,15 @@
field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
}
+ public static final class CarrierConfigManager.Apn {
+ field public static final String KEY_PREFIX = "apn.";
+ field public static final String KEY_SETTINGS_DEFAULT_PROTOCOL_STRING = "apn.settings_default_protocol_string";
+ field public static final String KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING = "apn.settings_default_roaming_protocol_string";
+ field public static final String PROTOCOL_IPV4 = "IP";
+ field public static final String PROTOCOL_IPV4V6 = "IPV4V6";
+ field public static final String PROTOCOL_IPV6 = "IPV6";
+ }
+
public static final class CarrierConfigManager.Gps {
field public static final String KEY_PERSIST_LPP_MODE_BOOL = "gps.persist_lpp_mode_bool";
field public static final String KEY_PREFIX = "gps.";
@@ -46170,6 +46311,7 @@
}
public final class CellIdentityGsm extends android.telephony.CellIdentity {
+ method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns();
method public int getArfcn();
method public int getBsic();
method public int getCid();
@@ -46185,8 +46327,11 @@
}
public final class CellIdentityLte extends android.telephony.CellIdentity {
+ method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns();
+ method @NonNull public java.util.List<java.lang.Integer> getBands();
method public int getBandwidth();
method public int getCi();
+ method @Nullable public android.telephony.ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo();
method public int getEarfcn();
method @Deprecated public int getMcc();
method @Nullable public String getMccString();
@@ -46200,6 +46345,8 @@
}
public final class CellIdentityNr extends android.telephony.CellIdentity {
+ method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns();
+ method @NonNull public java.util.List<java.lang.Integer> getBands();
method @Nullable public String getMccString();
method @Nullable public String getMncString();
method public long getNci();
@@ -46211,7 +46358,9 @@
}
public final class CellIdentityTdscdma extends android.telephony.CellIdentity {
+ method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns();
method public int getCid();
+ method @Nullable public android.telephony.ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo();
method public int getCpid();
method public int getLac();
method @Nullable public String getMccString();
@@ -46223,7 +46372,9 @@
}
public final class CellIdentityWcdma extends android.telephony.CellIdentity {
+ method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns();
method public int getCid();
+ method @Nullable public android.telephony.ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo();
method public int getLac();
method @Deprecated public int getMcc();
method @Nullable public String getMccString();
@@ -46393,6 +46544,15 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthWcdma> CREATOR;
}
+ public final class ClosedSubscriberGroupInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0, to=134217727) public int getCsgIdentity();
+ method public boolean getCsgIndicator();
+ method @NonNull public String getHomeNodebName();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ClosedSubscriberGroupInfo> CREATOR;
+ }
+
public class IccOpenLogicalChannelResponse implements android.os.Parcelable {
method public int describeContents();
method public int getChannel();
@@ -47107,6 +47267,7 @@
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getImei();
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getImei(int);
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS}) public String getLine1Number();
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public String getManualNetworkSelectionPlmn();
method @Nullable public String getManufacturerCode();
method @Nullable public String getManufacturerCode(int);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getMeid();
@@ -47162,6 +47323,7 @@
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled();
method public boolean isEmergencyNumber(@NonNull String);
method public boolean isHearingAidCompatibilitySupported();
+ method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isManualNetworkSelectionAllowed();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int isMultiSimSupported();
method public boolean isNetworkRoaming();
method public boolean isRttSupported();
@@ -47570,10 +47732,42 @@
field public static final int EMBEDDED_SUBSCRIPTION_RESULT_ERROR = 2; // 0x2
field public static final int EMBEDDED_SUBSCRIPTION_RESULT_OK = 0; // 0x0
field public static final int EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR = 1; // 0x1
+ field public static final int ERROR_ADDRESS_MISSING = 10011; // 0x271b
+ field public static final int ERROR_CARRIER_LOCKED = 10000; // 0x2710
+ field public static final int ERROR_CERTIFICATE_ERROR = 10012; // 0x271c
+ field public static final int ERROR_CONNECTION_ERROR = 10014; // 0x271e
+ field public static final int ERROR_DISALLOWED_BY_PPR = 10010; // 0x271a
+ field public static final int ERROR_EUICC_INSUFFICIENT_MEMORY = 10004; // 0x2714
+ field public static final int ERROR_EUICC_MISSING = 10006; // 0x2716
+ field public static final int ERROR_INCOMPATIBLE_CARRIER = 10003; // 0x2713
+ field public static final int ERROR_INSTALL_PROFILE = 10009; // 0x2719
+ field public static final int ERROR_INVALID_ACTIVATION_CODE = 10001; // 0x2711
+ field public static final int ERROR_INVALID_CONFIRMATION_CODE = 10002; // 0x2712
+ field public static final int ERROR_INVALID_RESPONSE = 10015; // 0x271f
+ field public static final int ERROR_NO_PROFILES_AVAILABLE = 10013; // 0x271d
+ field public static final int ERROR_OPERATION_BUSY = 10016; // 0x2720
+ field public static final int ERROR_SIM_MISSING = 10008; // 0x2718
+ field public static final int ERROR_TIME_OUT = 10005; // 0x2715
+ field public static final int ERROR_UNSUPPORTED_VERSION = 10007; // 0x2717
field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DETAILED_CODE";
field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION";
+ field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_ERROR_CODE = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_ERROR_CODE";
+ field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_OPERATION_CODE = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_OPERATION_CODE";
+ field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE";
+ field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE";
field public static final String EXTRA_USE_QR_SCANNER = "android.telephony.euicc.extra.USE_QR_SCANNER";
field public static final String META_DATA_CARRIER_ICON = "android.telephony.euicc.carriericon";
+ field public static final int OPERATION_APDU = 8; // 0x8
+ field public static final int OPERATION_DOWNLOAD = 5; // 0x5
+ field public static final int OPERATION_EUICC_CARD = 3; // 0x3
+ field public static final int OPERATION_EUICC_GSMA = 7; // 0x7
+ field public static final int OPERATION_HTTP = 11; // 0xb
+ field public static final int OPERATION_METADATA = 6; // 0x6
+ field public static final int OPERATION_SIM_SLOT = 2; // 0x2
+ field public static final int OPERATION_SMDX = 9; // 0x9
+ field public static final int OPERATION_SMDX_SUBJECT_REASON_CODE = 10; // 0xa
+ field public static final int OPERATION_SWITCH = 4; // 0x4
+ field public static final int OPERATION_SYSTEM = 1; // 0x1
}
}
@@ -52446,6 +52640,7 @@
method public android.graphics.Canvas lockHardwareCanvas();
method public void readFromParcel(android.os.Parcel);
method public void release();
+ method public void setFrameRate(@FloatRange(from=0.0) float);
method @Deprecated public void unlockCanvas(android.graphics.Canvas);
method public void unlockCanvasAndPost(android.graphics.Canvas);
method public void writeToParcel(android.os.Parcel, int);
@@ -52489,6 +52684,7 @@
method @NonNull public android.view.SurfaceControl.Transaction reparent(@NonNull android.view.SurfaceControl, @Nullable android.view.SurfaceControl);
method @NonNull public android.view.SurfaceControl.Transaction setAlpha(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0, to=1.0) float);
method @NonNull public android.view.SurfaceControl.Transaction setBufferSize(@NonNull android.view.SurfaceControl, @IntRange(from=0) int, @IntRange(from=0) int);
+ method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float);
method @NonNull public android.view.SurfaceControl.Transaction setGeometry(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, int);
method @NonNull public android.view.SurfaceControl.Transaction setLayer(@NonNull android.view.SurfaceControl, @IntRange(from=java.lang.Integer.MIN_VALUE, to=java.lang.Integer.MAX_VALUE) int);
method @NonNull public android.view.SurfaceControl.Transaction setVisibility(@NonNull android.view.SurfaceControl, boolean);
@@ -52496,6 +52692,20 @@
field @NonNull public static final android.os.Parcelable.Creator<android.view.SurfaceControl.Transaction> CREATOR;
}
+ public class SurfaceControlViewHost {
+ ctor public SurfaceControlViewHost(@NonNull android.content.Context, @NonNull android.view.Display, @Nullable android.os.IBinder);
+ method public void addView(@NonNull android.view.View, int, int);
+ method @Nullable public android.view.SurfaceControlViewHost.SurfacePackage getSurfacePackage();
+ method public void relayout(int, int);
+ method public void release();
+ }
+
+ public static final class SurfaceControlViewHost.SurfacePackage implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.SurfaceControlViewHost.SurfacePackage> CREATOR;
+ }
+
public interface SurfaceHolder {
method public void addCallback(android.view.SurfaceHolder.Callback);
method public android.view.Surface getSurface();
@@ -52540,7 +52750,9 @@
ctor public SurfaceView(android.content.Context, android.util.AttributeSet, int, int);
method public boolean gatherTransparentRegion(android.graphics.Region);
method public android.view.SurfaceHolder getHolder();
+ method @Nullable public android.os.IBinder getHostToken();
method public android.view.SurfaceControl getSurfaceControl();
+ method public void setChildSurfacePackage(@NonNull android.view.SurfaceControlViewHost.SurfacePackage);
method public void setSecure(boolean);
method public void setZOrderMediaOverlay(boolean);
method public void setZOrderOnTop(boolean);
@@ -52680,7 +52892,7 @@
method protected void dispatchSetPressed(boolean);
method protected void dispatchSetSelected(boolean);
method @CallSuper public void dispatchStartTemporaryDetach();
- method public void dispatchSystemUiVisibilityChanged(int);
+ method @Deprecated public void dispatchSystemUiVisibilityChanged(int);
method public boolean dispatchTouchEvent(android.view.MotionEvent);
method public boolean dispatchTrackballEvent(android.view.MotionEvent);
method public boolean dispatchUnhandledMove(android.view.View, int);
@@ -52690,7 +52902,7 @@
method public void dispatchWindowInsetsAnimationPrepare(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation);
method @NonNull public android.view.WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull android.view.WindowInsets);
method @NonNull public android.view.WindowInsetsAnimationCallback.AnimationBounds dispatchWindowInsetsAnimationStart(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds);
- method public void dispatchWindowSystemUiVisiblityChanged(int);
+ method @Deprecated public void dispatchWindowSystemUiVisiblityChanged(int);
method public void dispatchWindowVisibilityChanged(int);
method @CallSuper public void draw(android.graphics.Canvas);
method @CallSuper public void drawableHotspotChanged(float, float);
@@ -52843,7 +53055,7 @@
method protected int getSuggestedMinimumHeight();
method protected int getSuggestedMinimumWidth();
method @NonNull public java.util.List<android.graphics.Rect> getSystemGestureExclusionRects();
- method public int getSystemUiVisibility();
+ method @Deprecated public int getSystemUiVisibility();
method @android.view.ViewDebug.ExportedProperty public Object getTag();
method public Object getTag(int);
method @android.view.ViewDebug.ExportedProperty(category="text", mapping={@android.view.ViewDebug.IntToString(from=android.view.View.TEXT_ALIGNMENT_INHERIT, to="INHERIT"), @android.view.ViewDebug.IntToString(from=android.view.View.TEXT_ALIGNMENT_GRAVITY, to="GRAVITY"), @android.view.ViewDebug.IntToString(from=android.view.View.TEXT_ALIGNMENT_TEXT_START, to="TEXT_START"), @android.view.ViewDebug.IntToString(from=android.view.View.TEXT_ALIGNMENT_TEXT_END, to="TEXT_END"), @android.view.ViewDebug.IntToString(from=android.view.View.TEXT_ALIGNMENT_CENTER, to="CENTER"), @android.view.ViewDebug.IntToString(from=android.view.View.TEXT_ALIGNMENT_VIEW_START, to="VIEW_START"), @android.view.ViewDebug.IntToString(from=android.view.View.TEXT_ALIGNMENT_VIEW_END, to="VIEW_END")}) public int getTextAlignment();
@@ -52871,7 +53083,7 @@
method protected int getWindowAttachCount();
method public android.view.WindowId getWindowId();
method @Nullable public android.view.WindowInsetsController getWindowInsetsController();
- method public int getWindowSystemUiVisibility();
+ method @Deprecated public int getWindowSystemUiVisibility();
method public android.os.IBinder getWindowToken();
method public int getWindowVisibility();
method public void getWindowVisibleDisplayFrame(android.graphics.Rect);
@@ -53009,7 +53221,7 @@
method @CallSuper public void onVisibilityAggregated(boolean);
method protected void onVisibilityChanged(@NonNull android.view.View, int);
method public void onWindowFocusChanged(boolean);
- method public void onWindowSystemUiVisibilityChanged(int);
+ method @Deprecated public void onWindowSystemUiVisibilityChanged(int);
method protected void onWindowVisibilityChanged(int);
method protected boolean overScrollBy(int, int, int, int, int, int, int, int, boolean);
method public boolean performAccessibilityAction(int, android.os.Bundle);
@@ -53151,7 +53363,7 @@
method public void setOnKeyListener(android.view.View.OnKeyListener);
method public void setOnLongClickListener(@Nullable android.view.View.OnLongClickListener);
method public void setOnScrollChangeListener(android.view.View.OnScrollChangeListener);
- method public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener);
+ method @Deprecated public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener);
method public void setOnTouchListener(android.view.View.OnTouchListener);
method public void setOutlineAmbientShadowColor(@ColorInt int);
method public void setOutlineProvider(android.view.ViewOutlineProvider);
@@ -53188,7 +53400,7 @@
method public void setStateDescription(@Nullable CharSequence);
method public void setStateListAnimator(android.animation.StateListAnimator);
method public void setSystemGestureExclusionRects(@NonNull java.util.List<android.graphics.Rect>);
- method public void setSystemUiVisibility(int);
+ method @Deprecated public void setSystemUiVisibility(int);
method public void setTag(Object);
method public void setTag(int, Object);
method public void setTextAlignment(int);
@@ -53366,18 +53578,18 @@
field public static final int SOUND_EFFECTS_ENABLED = 134217728; // 0x8000000
field @Deprecated public static final int STATUS_BAR_HIDDEN = 1; // 0x1
field @Deprecated public static final int STATUS_BAR_VISIBLE = 0; // 0x0
- field public static final int SYSTEM_UI_FLAG_FULLSCREEN = 4; // 0x4
- field public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2; // 0x2
- field public static final int SYSTEM_UI_FLAG_IMMERSIVE = 2048; // 0x800
- field public static final int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 4096; // 0x1000
- field public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024; // 0x400
- field public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512; // 0x200
- field public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 256; // 0x100
- field public static final int SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR = 16; // 0x10
- field public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 8192; // 0x2000
- field public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 1; // 0x1
- field public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0
- field public static final int SYSTEM_UI_LAYOUT_FLAGS = 1536; // 0x600
+ field @Deprecated public static final int SYSTEM_UI_FLAG_FULLSCREEN = 4; // 0x4
+ field @Deprecated public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2; // 0x2
+ field @Deprecated public static final int SYSTEM_UI_FLAG_IMMERSIVE = 2048; // 0x800
+ field @Deprecated public static final int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 4096; // 0x1000
+ field @Deprecated public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024; // 0x400
+ field @Deprecated public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512; // 0x200
+ field @Deprecated public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 256; // 0x100
+ field @Deprecated public static final int SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR = 16; // 0x10
+ field @Deprecated public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 8192; // 0x2000
+ field @Deprecated public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 1; // 0x1
+ field @Deprecated public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0
+ field @Deprecated public static final int SYSTEM_UI_LAYOUT_FLAGS = 1536; // 0x600
field public static final int TEXT_ALIGNMENT_CENTER = 4; // 0x4
field public static final int TEXT_ALIGNMENT_GRAVITY = 1; // 0x1
field public static final int TEXT_ALIGNMENT_INHERIT = 0; // 0x0
@@ -53501,8 +53713,8 @@
method public void onScrollChange(android.view.View, int, int, int, int);
}
- public static interface View.OnSystemUiVisibilityChangeListener {
- method public void onSystemUiVisibilityChange(int);
+ @Deprecated public static interface View.OnSystemUiVisibilityChangeListener {
+ method @Deprecated public void onSystemUiVisibilityChange(int);
}
public static interface View.OnTouchListener {
@@ -54139,6 +54351,7 @@
method public final void removeOnFrameMetricsAvailableListener(android.view.Window.OnFrameMetricsAvailableListener);
method public boolean requestFeature(int);
method @NonNull public final <T extends android.view.View> T requireViewById(@IdRes int);
+ method public void resetOnContentApplyWindowInsetsListener();
method public abstract void restoreHierarchyState(android.os.Bundle);
method public abstract android.os.Bundle saveHierarchyState();
method public void setAllowEnterTransitionOverlap(boolean);
@@ -54177,6 +54390,7 @@
method public abstract void setNavigationBarColor(@ColorInt int);
method public void setNavigationBarContrastEnforced(boolean);
method public void setNavigationBarDividerColor(@ColorInt int);
+ method public void setOnContentApplyWindowInsetsListener(@Nullable android.view.Window.OnContentApplyWindowInsetsListener);
method public void setPreferMinimalPostProcessing(boolean);
method public void setReenterTransition(android.transition.Transition);
method public abstract void setResizingCaptionDrawable(android.graphics.drawable.Drawable);
@@ -54271,6 +54485,10 @@
method @Nullable public android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
}
+ public static interface Window.OnContentApplyWindowInsetsListener {
+ method @NonNull public android.util.Pair<android.graphics.Insets,android.view.WindowInsets> onContentApplyWindowInsets(@NonNull android.view.WindowInsets);
+ }
+
public static interface Window.OnFrameMetricsAvailableListener {
method public void onFrameMetricsAvailable(android.view.Window, android.view.FrameMetrics, int);
}
@@ -54315,23 +54533,23 @@
method @Deprecated @NonNull public android.view.WindowInsets consumeSystemWindowInsets();
method @Nullable public android.view.DisplayCutout getDisplayCutout();
method @NonNull public android.graphics.Insets getInsets(int);
- method @NonNull public android.graphics.Insets getMandatorySystemGestureInsets();
- method @NonNull public android.graphics.Insets getMaxInsets(int) throws java.lang.IllegalArgumentException;
- method public int getStableInsetBottom();
- method public int getStableInsetLeft();
- method public int getStableInsetRight();
- method public int getStableInsetTop();
- method @NonNull public android.graphics.Insets getStableInsets();
- method @NonNull public android.graphics.Insets getSystemGestureInsets();
- method public int getSystemWindowInsetBottom();
- method public int getSystemWindowInsetLeft();
- method public int getSystemWindowInsetRight();
- method public int getSystemWindowInsetTop();
- method @NonNull public android.graphics.Insets getSystemWindowInsets();
- method @NonNull public android.graphics.Insets getTappableElementInsets();
+ method @NonNull public android.graphics.Insets getInsetsIgnoringVisibility(int);
+ method @Deprecated @NonNull public android.graphics.Insets getMandatorySystemGestureInsets();
+ method @Deprecated public int getStableInsetBottom();
+ method @Deprecated public int getStableInsetLeft();
+ method @Deprecated public int getStableInsetRight();
+ method @Deprecated public int getStableInsetTop();
+ method @Deprecated @NonNull public android.graphics.Insets getStableInsets();
+ method @Deprecated @NonNull public android.graphics.Insets getSystemGestureInsets();
+ method @Deprecated public int getSystemWindowInsetBottom();
+ method @Deprecated public int getSystemWindowInsetLeft();
+ method @Deprecated public int getSystemWindowInsetRight();
+ method @Deprecated public int getSystemWindowInsetTop();
+ method @Deprecated @NonNull public android.graphics.Insets getSystemWindowInsets();
+ method @Deprecated @NonNull public android.graphics.Insets getTappableElementInsets();
method public boolean hasInsets();
- method public boolean hasStableInsets();
- method public boolean hasSystemWindowInsets();
+ method @Deprecated public boolean hasStableInsets();
+ method @Deprecated public boolean hasSystemWindowInsets();
method @NonNull public android.view.WindowInsets inset(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
method public boolean isConsumed();
method public boolean isRound();
@@ -54347,17 +54565,24 @@
method @NonNull public android.view.WindowInsets build();
method @NonNull public android.view.WindowInsets.Builder setDisplayCutout(@Nullable android.view.DisplayCutout);
method @NonNull public android.view.WindowInsets.Builder setInsets(int, @NonNull android.graphics.Insets);
- method @NonNull public android.view.WindowInsets.Builder setMandatorySystemGestureInsets(@NonNull android.graphics.Insets);
- method @NonNull public android.view.WindowInsets.Builder setMaxInsets(int, @NonNull android.graphics.Insets) throws java.lang.IllegalArgumentException;
- method @NonNull public android.view.WindowInsets.Builder setStableInsets(@NonNull android.graphics.Insets);
- method @NonNull public android.view.WindowInsets.Builder setSystemGestureInsets(@NonNull android.graphics.Insets);
- method @NonNull public android.view.WindowInsets.Builder setSystemWindowInsets(@NonNull android.graphics.Insets);
- method @NonNull public android.view.WindowInsets.Builder setTappableElementInsets(@NonNull android.graphics.Insets);
+ method @NonNull public android.view.WindowInsets.Builder setInsetsIgnoringVisibility(int, @NonNull android.graphics.Insets) throws java.lang.IllegalArgumentException;
+ method @Deprecated @NonNull public android.view.WindowInsets.Builder setMandatorySystemGestureInsets(@NonNull android.graphics.Insets);
+ method @Deprecated @NonNull public android.view.WindowInsets.Builder setStableInsets(@NonNull android.graphics.Insets);
+ method @Deprecated @NonNull public android.view.WindowInsets.Builder setSystemGestureInsets(@NonNull android.graphics.Insets);
+ method @Deprecated @NonNull public android.view.WindowInsets.Builder setSystemWindowInsets(@NonNull android.graphics.Insets);
+ method @Deprecated @NonNull public android.view.WindowInsets.Builder setTappableElementInsets(@NonNull android.graphics.Insets);
method @NonNull public android.view.WindowInsets.Builder setVisible(int, boolean);
}
- public static final class WindowInsets.Type {
+ public static final class WindowInsets.Side {
method public static int all();
+ field public static final int BOTTOM = 8; // 0x8
+ field public static final int LEFT = 1; // 0x1
+ field public static final int RIGHT = 4; // 0x4
+ field public static final int TOP = 2; // 0x2
+ }
+
+ public static final class WindowInsets.Type {
method public static int captionBar();
method public static int ime();
method public static int mandatorySystemGestures();
@@ -54366,7 +54591,6 @@
method public static int systemBars();
method public static int systemGestures();
method public static int tappableElement();
- method public static int windowDecor();
}
public interface WindowInsetsAnimationCallback {
@@ -54394,7 +54618,7 @@
method public float getInterpolatedFraction();
method @Nullable public android.view.animation.Interpolator getInterpolator();
method public int getTypeMask();
- method public void setDuration(long);
+ method public void setAlpha(@FloatRange(from=0.0f, to=1.0f) float);
method public void setFraction(@FloatRange(from=0.0f, to=1.0f) float);
}
@@ -54415,19 +54639,27 @@
}
public interface WindowInsetsController {
- method public default void controlInputMethodAnimation(long, @NonNull android.view.WindowInsetsAnimationControlListener);
+ method public default void controlInputMethodAnimation(long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener);
+ method public void controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener);
method public int getSystemBarsAppearance();
method public int getSystemBarsBehavior();
+ method public void hide(int);
method public default void hideInputMethod();
method public void setSystemBarsAppearance(int, int);
method public void setSystemBarsBehavior(int);
+ method public void show(int);
method public default void showInputMethod();
field public static final int APPEARANCE_LIGHT_NAVIGATION_BARS = 16; // 0x10
field public static final int APPEARANCE_LIGHT_STATUS_BARS = 8; // 0x8
+ field public static final int BEHAVIOR_SHOW_BARS_BY_SWIPE = 1; // 0x1
+ field public static final int BEHAVIOR_SHOW_BARS_BY_TOUCH = 0; // 0x0
+ field public static final int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2; // 0x2
}
public interface WindowManager extends android.view.ViewManager {
- method public android.view.Display getDefaultDisplay();
+ method @NonNull public default android.view.WindowMetrics getCurrentWindowMetrics();
+ method @Deprecated public android.view.Display getDefaultDisplay();
+ method @NonNull public default android.view.WindowMetrics getMaximumWindowMetrics();
method public void removeViewImmediate(android.view.View);
}
@@ -54453,9 +54685,15 @@
method public String debug(String);
method public int describeContents();
method public int getColorMode();
+ method public int getFitInsetsSides();
+ method public int getFitInsetsTypes();
method public final CharSequence getTitle();
+ method public boolean isFitInsetsIgnoringVisibility();
method public static boolean mayUseInputMethod(int);
method public void setColorMode(int);
+ method public void setFitInsetsIgnoringVisibility(boolean);
+ method public void setFitInsetsSides(int);
+ method public void setFitInsetsTypes(int);
method public final void setTitle(CharSequence);
method public void writeToParcel(android.os.Parcel, int);
field public static final int ALPHA_CHANGED = 128; // 0x80
@@ -54476,13 +54714,13 @@
field @Deprecated public static final int FLAG_DISMISS_KEYGUARD = 4194304; // 0x400000
field @Deprecated public static final int FLAG_DITHER = 4096; // 0x1000
field public static final int FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = -2147483648; // 0x80000000
- field public static final int FLAG_FORCE_NOT_FULLSCREEN = 2048; // 0x800
- field public static final int FLAG_FULLSCREEN = 1024; // 0x400
+ field @Deprecated public static final int FLAG_FORCE_NOT_FULLSCREEN = 2048; // 0x800
+ field @Deprecated public static final int FLAG_FULLSCREEN = 1024; // 0x400
field public static final int FLAG_HARDWARE_ACCELERATED = 16777216; // 0x1000000
field public static final int FLAG_IGNORE_CHEEK_PRESSES = 32768; // 0x8000
field public static final int FLAG_KEEP_SCREEN_ON = 128; // 0x80
- field public static final int FLAG_LAYOUT_ATTACHED_IN_DECOR = 1073741824; // 0x40000000
- field public static final int FLAG_LAYOUT_INSET_DECOR = 65536; // 0x10000
+ field @Deprecated public static final int FLAG_LAYOUT_ATTACHED_IN_DECOR = 1073741824; // 0x40000000
+ field @Deprecated public static final int FLAG_LAYOUT_INSET_DECOR = 65536; // 0x10000
field @Deprecated public static final int FLAG_LAYOUT_IN_OVERSCAN = 33554432; // 0x2000000
field public static final int FLAG_LAYOUT_IN_SCREEN = 256; // 0x100
field public static final int FLAG_LAYOUT_NO_LIMITS = 512; // 0x200
@@ -54496,8 +54734,8 @@
field @Deprecated public static final int FLAG_SHOW_WHEN_LOCKED = 524288; // 0x80000
field public static final int FLAG_SPLIT_TOUCH = 8388608; // 0x800000
field @Deprecated public static final int FLAG_TOUCHABLE_WHEN_WAKING = 64; // 0x40
- field public static final int FLAG_TRANSLUCENT_NAVIGATION = 134217728; // 0x8000000
- field public static final int FLAG_TRANSLUCENT_STATUS = 67108864; // 0x4000000
+ field @Deprecated public static final int FLAG_TRANSLUCENT_NAVIGATION = 134217728; // 0x8000000
+ field @Deprecated public static final int FLAG_TRANSLUCENT_STATUS = 67108864; // 0x4000000
field @Deprecated public static final int FLAG_TURN_SCREEN_ON = 2097152; // 0x200000
field public static final int FLAG_WATCH_OUTSIDE_TOUCH = 262144; // 0x40000
field public static final int FORMAT_CHANGED = 8; // 0x8
@@ -54523,7 +54761,7 @@
field public static final int SCREEN_ORIENTATION_CHANGED = 1024; // 0x400
field public static final int SOFT_INPUT_ADJUST_NOTHING = 48; // 0x30
field public static final int SOFT_INPUT_ADJUST_PAN = 32; // 0x20
- field public static final int SOFT_INPUT_ADJUST_RESIZE = 16; // 0x10
+ field @Deprecated public static final int SOFT_INPUT_ADJUST_RESIZE = 16; // 0x10
field public static final int SOFT_INPUT_ADJUST_UNSPECIFIED = 0; // 0x0
field public static final int SOFT_INPUT_IS_FORWARD_NAVIGATION = 256; // 0x100
field public static final int SOFT_INPUT_MASK_ADJUST = 240; // 0xf0
@@ -54580,7 +54818,7 @@
field public float screenBrightness;
field public int screenOrientation;
field public int softInputMode;
- field public int systemUiVisibility;
+ field @Deprecated public int systemUiVisibility;
field public android.os.IBinder token;
field @android.view.ViewDebug.ExportedProperty(mapping={@android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION, to="BASE_APPLICATION"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION, to="APPLICATION"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING, to="APPLICATION_STARTING"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION, to="DRAWN_APPLICATION"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL, to="APPLICATION_PANEL"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA, to="APPLICATION_MEDIA"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL, to="APPLICATION_SUB_PANEL"), @android.view.ViewDebug.IntToString(from=0x3ed, to="APPLICATION_ABOVE_SUB_PANEL"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG, to="APPLICATION_ATTACHED_DIALOG"), @android.view.ViewDebug.IntToString(from=0x3ec, to="APPLICATION_MEDIA_OVERLAY"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR, to="STATUS_BAR"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR, to="SEARCH_BAR"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_PHONE, to="PHONE"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, to="SYSTEM_ALERT"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_TOAST, to="TOAST"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, to="SYSTEM_OVERLAY"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE, to="PRIORITY_PHONE"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG, to="SYSTEM_DIALOG"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG, to="KEYGUARD_DIALOG"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR, to="SYSTEM_ERROR"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD, to="INPUT_METHOD"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG, to="INPUT_METHOD_DIALOG"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_WALLPAPER, to="WALLPAPER"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, to="STATUS_BAR_PANEL"), @android.view.ViewDebug.IntToString(from=0x7df, to="SECURE_SYSTEM_OVERLAY"), @android.view.ViewDebug.IntToString(from=0x7e0, to="DRAG"), @android.view.ViewDebug.IntToString(from=0x7e1, to="STATUS_BAR_SUB_PANEL"), @android.view.ViewDebug.IntToString(from=0x7e2, to="POINTER"), @android.view.ViewDebug.IntToString(from=0x7e3, to="NAVIGATION_BAR"), @android.view.ViewDebug.IntToString(from=0x7e4, to="VOLUME_OVERLAY"), @android.view.ViewDebug.IntToString(from=0x7e5, to="BOOT_PROGRESS"), @android.view.ViewDebug.IntToString(from=0x7e6, to="INPUT_CONSUMER"), @android.view.ViewDebug.IntToString(from=0x7e7, to="DREAM"), @android.view.ViewDebug.IntToString(from=0x7e8, to="NAVIGATION_BAR_PANEL"), @android.view.ViewDebug.IntToString(from=0x7ea, to="DISPLAY_OVERLAY"), @android.view.ViewDebug.IntToString(from=0x7eb, to="MAGNIFICATION_OVERLAY"), @android.view.ViewDebug.IntToString(from=0x7f5, to="PRESENTATION"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION, to="PRIVATE_PRESENTATION"), @android.view.ViewDebug.IntToString(from=0x7ef, to="VOICE_INTERACTION"), @android.view.ViewDebug.IntToString(from=0x7f1, to="VOICE_INTERACTION_STARTING"), @android.view.ViewDebug.IntToString(from=0x7f2, to="DOCK_DIVIDER"), @android.view.ViewDebug.IntToString(from=0x7f3, to="QS_DIALOG"), @android.view.ViewDebug.IntToString(from=0x7f4, to="SCREENSHOT"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, to="APPLICATION_OVERLAY")}) public int type;
field public float verticalMargin;
@@ -54590,6 +54828,12 @@
field @android.view.ViewDebug.ExportedProperty public int y;
}
+ public final class WindowMetrics {
+ ctor public WindowMetrics(@NonNull android.util.Size, @NonNull android.view.WindowInsets);
+ method @NonNull public android.util.Size getSize();
+ method @NonNull public android.view.WindowInsets getWindowInsets();
+ }
+
}
package android.view.accessibility {
@@ -55687,7 +55931,12 @@
ctor public EditorInfo();
method public int describeContents();
method public void dump(android.util.Printer, String);
+ method @Nullable public CharSequence getInitialSelectedText(int);
+ method @Nullable public CharSequence getInitialTextAfterCursor(int, int);
+ method @Nullable public CharSequence getInitialTextBeforeCursor(int, int);
method public final void makeCompatible(int);
+ method public void setInitialSurroundingSubText(@NonNull CharSequence, int);
+ method public void setInitialSurroundingText(@NonNull CharSequence);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.EditorInfo> CREATOR;
field public static final int IME_ACTION_DONE = 6; // 0x6
diff --git a/api/system-current.txt b/api/system-current.txt
index b694402..a2bd999 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -55,13 +55,16 @@
field public static final String CAPTURE_AUDIO_HOTWORD = "android.permission.CAPTURE_AUDIO_HOTWORD";
field public static final String CAPTURE_MEDIA_OUTPUT = "android.permission.CAPTURE_MEDIA_OUTPUT";
field public static final String CAPTURE_TV_INPUT = "android.permission.CAPTURE_TV_INPUT";
+ field public static final String CAPTURE_VOICE_COMMUNICATION_OUTPUT = "android.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT";
field public static final String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE";
field public static final String CHANGE_DEVICE_IDLE_TEMP_WHITELIST = "android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST";
field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
+ field public static final String COMPANION_APPROVE_WIFI_CONNECTIONS = "android.permission.COMPANION_APPROVE_WIFI_CONNECTIONS";
field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
field public static final String CONFIGURE_WIFI_DISPLAY = "android.permission.CONFIGURE_WIFI_DISPLAY";
field @Deprecated public static final String CONNECTIVITY_INTERNAL = "android.permission.CONNECTIVITY_INTERNAL";
field public static final String CONNECTIVITY_USE_RESTRICTED_NETWORKS = "android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS";
+ field public static final String CONTROL_DEVICE_LIGHTS = "android.permission.CONTROL_DEVICE_LIGHTS";
field public static final String CONTROL_DISPLAY_COLOR_TRANSFORMS = "android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS";
field public static final String CONTROL_DISPLAY_SATURATION = "android.permission.CONTROL_DISPLAY_SATURATION";
field public static final String CONTROL_INCALL_EXPERIENCE = "android.permission.CONTROL_INCALL_EXPERIENCE";
@@ -129,6 +132,7 @@
field public static final String MODIFY_PARENTAL_CONTROLS = "android.permission.MODIFY_PARENTAL_CONTROLS";
field public static final String MODIFY_QUIET_MODE = "android.permission.MODIFY_QUIET_MODE";
field public static final String MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE = "android.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE";
+ field public static final String MONITOR_DEVICE_CONFIG_ACCESS = "android.permission.MONITOR_DEVICE_CONFIG_ACCESS";
field public static final String MOVE_PACKAGE = "android.permission.MOVE_PACKAGE";
field public static final String NETWORK_AIRPLANE_MODE = "android.permission.NETWORK_AIRPLANE_MODE";
field public static final String NETWORK_CARRIER_PROVISIONING = "android.permission.NETWORK_CARRIER_PROVISIONING";
@@ -186,6 +190,7 @@
field public static final String REMOTE_DISPLAY_PROVIDER = "android.permission.REMOTE_DISPLAY_PROVIDER";
field public static final String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
+ field public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
field public static final String RESTORE_RUNTIME_PERMISSIONS = "android.permission.RESTORE_RUNTIME_PERMISSIONS";
@@ -231,7 +236,7 @@
field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
field public static final String WRITE_DREAM_STATE = "android.permission.WRITE_DREAM_STATE";
field public static final String WRITE_EMBEDDED_SUBSCRIPTIONS = "android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS";
- field public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
+ field @Deprecated public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
field public static final String WRITE_OBB = "android.permission.WRITE_OBB";
}
@@ -245,6 +250,7 @@
public static final class R.attr {
field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
+ field public static final int isAutofillInlineSuggestionTheme = 16844310; // 0x1010616
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int minExtensionVersion = 16844306; // 0x1010612
field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
@@ -388,6 +394,7 @@
field public static final String OPSTR_POST_NOTIFICATION = "android:post_notification";
field public static final String OPSTR_PROJECT_MEDIA = "android:project_media";
field public static final String OPSTR_READ_CLIPBOARD = "android:read_clipboard";
+ field public static final String OPSTR_READ_DEVICE_IDENTIFIERS = "android:read_device_identifiers";
field public static final String OPSTR_READ_ICC_SMS = "android:read_icc_sms";
field public static final String OPSTR_READ_MEDIA_AUDIO = "android:read_media_audio";
field public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
@@ -1174,6 +1181,7 @@
method public void classifyContentSelections(@NonNull android.app.contentsuggestions.ClassificationsRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.contentsuggestions.ContentSuggestionsManager.ClassificationsCallback);
method public boolean isEnabled();
method public void notifyInteraction(@NonNull String, @NonNull android.os.Bundle);
+ method public void provideContextImage(@NonNull android.graphics.Bitmap, @NonNull android.os.Bundle);
method public void provideContextImage(int, @NonNull android.os.Bundle);
method public void suggestContentSelections(@NonNull android.app.contentsuggestions.SelectionsRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.contentsuggestions.ContentSuggestionsManager.SelectionsCallback);
}
@@ -1605,12 +1613,13 @@
field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
}
- public final class BluetoothPan implements android.bluetooth.BluetoothProfile {
- method protected void finalize();
+ public final class BluetoothPan implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public void close();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH) protected void finalize();
method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
- method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
- method public boolean isTetheringOn();
- method public void setBluetoothTethering(boolean);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isTetheringOn();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public void setBluetoothTethering(boolean);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
field public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE";
@@ -1711,7 +1720,7 @@
package android.companion {
public final class CompanionDeviceManager {
- method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean isDeviceAssociated(@NonNull String, @NonNull android.net.MacAddress, @NonNull android.os.UserHandle);
+ method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean isDeviceAssociatedForWifiConnection(@NonNull String, @NonNull android.net.MacAddress, @NonNull android.os.UserHandle);
}
}
@@ -2055,6 +2064,21 @@
method @NonNull public final int getType();
}
+ public final class InstallationFile implements android.os.Parcelable {
+ ctor public InstallationFile(@NonNull String, long, @Nullable byte[]);
+ method public int describeContents();
+ method public int getFileType();
+ method @Nullable public byte[] getMetadata();
+ method @NonNull public String getName();
+ method public long getSize();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.InstallationFile> CREATOR;
+ field public static final int FILE_TYPE_APK = 0; // 0x0
+ field public static final int FILE_TYPE_LIB = 1; // 0x1
+ field public static final int FILE_TYPE_OBB = 2; // 0x2
+ field public static final int FILE_TYPE_UNKNOWN = -1; // 0xffffffff
+ }
+
public final class InstantAppInfo implements android.os.Parcelable {
ctor public InstantAppInfo(android.content.pm.ApplicationInfo, String[], String[]);
ctor public InstantAppInfo(String, CharSequence, String[], String[]);
@@ -2145,11 +2169,16 @@
field public static final int DATA_LOADER_TYPE_NONE = 0; // 0x0
field public static final int DATA_LOADER_TYPE_STREAMING = 1; // 0x1
field public static final String EXTRA_DATA_LOADER_TYPE = "android.content.pm.extra.DATA_LOADER_TYPE";
+ field public static final int LOCATION_DATA_APP = 0; // 0x0
+ field public static final int LOCATION_MEDIA_DATA = 2; // 0x2
+ field public static final int LOCATION_MEDIA_OBB = 1; // 0x1
}
public static class PackageInstaller.Session implements java.io.Closeable {
- method public void addFile(@NonNull String, long, @NonNull byte[]);
+ method public void addFile(int, @NonNull String, long, @NonNull byte[], @Nullable byte[]);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void commitTransferred(@NonNull android.content.IntentSender);
+ method @Nullable public android.content.pm.DataLoaderParams getDataLoaderParams();
+ method public void removeFile(int, @NonNull String);
}
public static class PackageInstaller.SessionInfo implements android.os.Parcelable {
@@ -2234,6 +2263,7 @@
field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio";
field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
+ field public static final int FLAGS_PERMISSION_RESERVED_PERMISSIONCONTROLLER = -268435456; // 0xf0000000
field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000
field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20
field public static final int FLAG_PERMISSION_GRANTED_BY_ROLE = 32768; // 0x8000
@@ -2920,6 +2950,48 @@
}
+package android.hardware.lights {
+
+ public final class Light implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getId();
+ method public int getOrdinal();
+ method public int getType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.Light> CREATOR;
+ }
+
+ public final class LightState implements android.os.Parcelable {
+ ctor public LightState(@ColorInt int);
+ method public int describeContents();
+ method @ColorInt public int getColor();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.LightState> CREATOR;
+ }
+
+ public final class LightsManager {
+ method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public java.util.List<android.hardware.lights.Light> getLights();
+ method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public android.hardware.lights.LightsManager.LightsSession openSession();
+ field public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8
+ }
+
+ public final class LightsManager.LightsSession implements java.lang.AutoCloseable {
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void close();
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void setLights(@NonNull android.hardware.lights.LightsRequest);
+ }
+
+ public final class LightsRequest {
+ }
+
+ public static final class LightsRequest.Builder {
+ ctor public LightsRequest.Builder();
+ method @NonNull public android.hardware.lights.LightsRequest build();
+ method @NonNull public android.hardware.lights.LightsRequest.Builder clearLight(@NonNull android.hardware.lights.Light);
+ method @NonNull public android.hardware.lights.LightsRequest.Builder setLight(@NonNull android.hardware.lights.Light, @NonNull android.hardware.lights.LightState);
+ }
+
+}
+
package android.hardware.location {
public class ContextHubClient implements java.io.Closeable {
@@ -3632,9 +3704,34 @@
package android.hardware.soundtrigger {
public class SoundTrigger {
+ field public static final int RECOGNITION_MODE_GENERIC = 8; // 0x8
+ field public static final int RECOGNITION_MODE_USER_AUTHENTICATION = 4; // 0x4
+ field public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 2; // 0x2
+ field public static final int RECOGNITION_MODE_VOICE_TRIGGER = 1; // 0x1
field public static final int STATUS_OK = 0; // 0x0
}
+ public static final class SoundTrigger.Keyphrase implements android.os.Parcelable {
+ ctor public SoundTrigger.Keyphrase(int, int, @NonNull java.util.Locale, @NonNull String, @Nullable int[]);
+ method @NonNull public static android.hardware.soundtrigger.SoundTrigger.Keyphrase readFromParcel(@NonNull android.os.Parcel);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.Keyphrase> CREATOR;
+ field public final int id;
+ field @NonNull public final java.util.Locale locale;
+ field public final int recognitionModes;
+ field @NonNull public final String text;
+ field @NonNull public final int[] users;
+ }
+
+ public static final class SoundTrigger.KeyphraseSoundModel extends android.hardware.soundtrigger.SoundTrigger.SoundModel implements android.os.Parcelable {
+ ctor public SoundTrigger.KeyphraseSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], @Nullable android.hardware.soundtrigger.SoundTrigger.Keyphrase[], int);
+ ctor public SoundTrigger.KeyphraseSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], @Nullable android.hardware.soundtrigger.SoundTrigger.Keyphrase[]);
+ method @NonNull public static android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel readFromParcel(@NonNull android.os.Parcel);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel> CREATOR;
+ field @NonNull public final android.hardware.soundtrigger.SoundTrigger.Keyphrase[] keyphrases;
+ }
+
public static final class SoundTrigger.ModelParamRange implements android.os.Parcelable {
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.ModelParamRange> CREATOR;
@@ -3673,6 +3770,16 @@
method public boolean isCaptureAvailable();
}
+ public static class SoundTrigger.SoundModel {
+ field public static final int TYPE_GENERIC_SOUND = 1; // 0x1
+ field public static final int TYPE_KEYPHRASE = 0; // 0x0
+ field @NonNull public final byte[] data;
+ field public final int type;
+ field @NonNull public final java.util.UUID uuid;
+ field @NonNull public final java.util.UUID vendorUuid;
+ field public final int version;
+ }
+
}
package android.hardware.usb {
@@ -3685,6 +3792,7 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public long getCurrentFunctions();
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_USB) public java.util.List<android.hardware.usb.UsbPort> getPorts();
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void grantPermission(android.hardware.usb.UsbDevice, String);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void resetUsbGadget();
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long);
field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE";
@@ -4181,9 +4289,11 @@
method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
method public void clearAudioServerStateCallback();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
+ method @IntRange(from=0) public int getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies();
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioVolumeGroup> getAudioVolumeGroups();
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAddress> getDevicesForAttributes(@NonNull android.media.AudioAttributes);
+ method @IntRange(from=0) public int getMaxAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAddress getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
@@ -4197,6 +4307,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException;
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int requestAudioFocus(@NonNull android.media.AudioFocusRequest, @Nullable android.media.audiopolicy.AudioPolicy);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) int);
method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAddress);
@@ -4323,6 +4434,7 @@
field public static final int RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET = 2; // 0x2
field public static final int RULE_MATCH_ATTRIBUTE_USAGE = 1; // 0x1
field public static final int RULE_MATCH_UID = 4; // 0x4
+ field public static final int RULE_MATCH_USERID = 8; // 0x8
}
public static class AudioMixingRule.Builder {
@@ -4343,9 +4455,11 @@
method public int getFocusDuckingBehavior();
method public int getStatus();
method public boolean removeUidDeviceAffinity(int);
+ method public boolean removeUserIdDeviceAffinity(int);
method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void setRegistration(String);
method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
+ method public boolean setUserIdDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
method public String toLogFriendlyString();
field public static final int FOCUS_POLICY_DUCKING_DEFAULT = 0; // 0x0
field public static final int FOCUS_POLICY_DUCKING_IN_APP = 0; // 0x0
@@ -4717,6 +4831,7 @@
method public long getSectionFilterLength();
method public int getTsFilterCount();
method public int getVideoFilterCount();
+ method public boolean isTimeFilterSupported();
}
public class Descrambler implements java.lang.AutoCloseable {
@@ -4762,7 +4877,7 @@
public class Tuner implements java.lang.AutoCloseable {
ctor @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public Tuner(@NonNull android.content.Context, @NonNull String, int, @Nullable android.media.tv.tuner.Tuner.OnResourceLostListener);
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void clearOnTuneEventListener();
- method public void close();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void close();
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int connectCiCam(int);
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int disconnectCiCam();
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int getAvSyncHwId(@NonNull android.media.tv.tuner.filter.Filter);
@@ -4780,10 +4895,11 @@
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int scan(@NonNull android.media.tv.tuner.frontend.FrontendSettings, int, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.ScanCallback);
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setLna(boolean);
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void setOnTuneEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.OnTuneEventListener);
- method public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int stopScan();
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int stopTune();
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void updateResourcePriority(int, int);
}
public static interface Tuner.OnResourceLostListener {
@@ -5007,9 +5123,11 @@
}
public class MediaEvent extends android.media.tv.tuner.filter.FilterEvent {
+ method public long getAudioHandle();
method public long getAvDataId();
method public long getDataLength();
method @Nullable public android.media.tv.tuner.filter.AudioDescriptor getExtraMetaData();
+ method @Nullable public android.media.MediaCodec.LinearBlock getLinearBlock();
method public int getMpuSequenceNumber();
method public long getOffset();
method public long getPts();
@@ -5102,7 +5220,16 @@
method public int getVersion();
}
- public class SectionSettings extends android.media.tv.tuner.filter.Settings {
+ public abstract class SectionSettings extends android.media.tv.tuner.filter.Settings {
+ method public boolean isCrcEnabled();
+ method public boolean isRaw();
+ method public boolean isRepeat();
+ }
+
+ public abstract static class SectionSettings.Builder<T extends android.media.tv.tuner.filter.SectionSettings.Builder<T>> extends android.media.tv.tuner.filter.Settings.Builder<android.media.tv.tuner.filter.SectionSettings.Builder<T>> {
+ method @NonNull public T setCrcEnabled(boolean);
+ method @NonNull public T setRaw(boolean);
+ method @NonNull public T setRepeat(boolean);
}
public class SectionSettingsWithSectionBits extends android.media.tv.tuner.filter.SectionSettings {
@@ -5112,7 +5239,7 @@
method @NonNull public byte[] getMode();
}
- public static class SectionSettingsWithSectionBits.Builder extends android.media.tv.tuner.filter.Settings.Builder<android.media.tv.tuner.filter.SectionSettingsWithSectionBits.Builder> {
+ public static class SectionSettingsWithSectionBits.Builder extends android.media.tv.tuner.filter.SectionSettings.Builder<android.media.tv.tuner.filter.SectionSettingsWithSectionBits.Builder> {
method @NonNull public android.media.tv.tuner.filter.SectionSettingsWithSectionBits build();
method @NonNull public android.media.tv.tuner.filter.SectionSettingsWithSectionBits.Builder setFilter(@NonNull byte[]);
method @NonNull public android.media.tv.tuner.filter.SectionSettingsWithSectionBits.Builder setMask(@NonNull byte[]);
@@ -5125,7 +5252,7 @@
method public int getVersion();
}
- public static class SectionSettingsWithTableInfo.Builder extends android.media.tv.tuner.filter.Settings.Builder<android.media.tv.tuner.filter.SectionSettingsWithTableInfo.Builder> {
+ public static class SectionSettingsWithTableInfo.Builder extends android.media.tv.tuner.filter.SectionSettings.Builder<android.media.tv.tuner.filter.SectionSettingsWithTableInfo.Builder> {
method @NonNull public android.media.tv.tuner.filter.SectionSettingsWithTableInfo build();
method @NonNull public android.media.tv.tuner.filter.SectionSettingsWithTableInfo.Builder setTableId(int);
method @NonNull public android.media.tv.tuner.filter.SectionSettingsWithTableInfo.Builder setVersion(int);
@@ -5163,7 +5290,7 @@
public static class TlvFilterConfiguration.Builder extends android.media.tv.tuner.filter.FilterConfiguration.Builder<android.media.tv.tuner.filter.TlvFilterConfiguration.Builder> {
method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration build();
- method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration.Builder setIsCompressedIpPacket(boolean);
+ method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration.Builder setCompressedIpPacket(boolean);
method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration.Builder setPacketType(int);
method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration.Builder setPassthrough(boolean);
}
@@ -5864,6 +5991,16 @@
}
+package android.media.voice {
+
+ public final class KeyphraseModelManager {
+ method @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public void deleteKeyphraseSoundModel(int, @NonNull java.util.Locale);
+ method @Nullable @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel getKeyphraseSoundModel(int, @NonNull java.util.Locale);
+ method @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public void updateKeyphraseSoundModel(@NonNull android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel);
+ }
+
+}
+
package android.metrics {
public class LogMaker {
@@ -5950,27 +6087,27 @@
method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull android.os.ParcelFileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCaptivePortalServerUrl();
- method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
- method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
method @Deprecated public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int, int, @NonNull android.os.Handler);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi();
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
- method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider);
- method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
field public static final int TETHERING_BLUETOOTH = 2; // 0x2
field public static final int TETHERING_USB = 1; // 0x1
field public static final int TETHERING_WIFI = 0; // 0x0
- field public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; // 0xd
- field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
- field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
+ field @Deprecated public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; // 0xd
+ field @Deprecated public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
+ field @Deprecated public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
field public static final int TYPE_NONE = -1; // 0xffffffff
field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd
}
@@ -5981,13 +6118,13 @@
method public void onTetheringStarted();
}
- public static interface ConnectivityManager.OnTetheringEntitlementResultListener {
- method public void onTetheringEntitlementResult(int);
+ @Deprecated public static interface ConnectivityManager.OnTetheringEntitlementResultListener {
+ method @Deprecated public void onTetheringEntitlementResult(int);
}
- public abstract static class ConnectivityManager.OnTetheringEventCallback {
- ctor public ConnectivityManager.OnTetheringEventCallback();
- method public void onUpstreamChanged(@Nullable android.net.Network);
+ @Deprecated public abstract static class ConnectivityManager.OnTetheringEventCallback {
+ ctor @Deprecated public ConnectivityManager.OnTetheringEventCallback();
+ method @Deprecated public void onUpstreamChanged(@Nullable android.net.Network);
}
public class InvalidPacketException extends java.lang.Exception {
@@ -6059,13 +6196,18 @@
public class LinkAddress implements android.os.Parcelable {
ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int);
+ ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int, long, long);
ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
ctor public LinkAddress(@NonNull String);
ctor public LinkAddress(@NonNull String, int, int);
+ method public long getDeprecationTime();
+ method public long getExpirationTime();
method public boolean isGlobalPreferred();
method public boolean isIpv4();
method public boolean isIpv6();
method public boolean isSameAddressAs(@Nullable android.net.LinkAddress);
+ field public static final long LIFETIME_PERMANENT = 9223372036854775807L; // 0x7fffffffffffffffL
+ field public static final long LIFETIME_UNKNOWN = -1L; // 0xffffffffffffffffL
}
public final class LinkProperties implements android.os.Parcelable {
@@ -6193,7 +6335,7 @@
public class NetworkKey implements android.os.Parcelable {
ctor public NetworkKey(android.net.WifiKey);
- method @Nullable public static android.net.NetworkKey createFromScanResult(@Nullable android.net.wifi.ScanResult);
+ method @Nullable public static android.net.NetworkKey createFromScanResult(@NonNull android.net.wifi.ScanResult);
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkKey> CREATOR;
@@ -6202,6 +6344,22 @@
field public final android.net.WifiKey wifiKey;
}
+ public class NetworkPolicyManager {
+ method @NonNull public android.telephony.SubscriptionPlan[] getSubscriptionPlans(int, @NonNull String);
+ method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void registerSubscriptionCallback(@NonNull android.net.NetworkPolicyManager.SubscriptionCallback);
+ method public void setSubscriptionOverride(int, int, int, long, @NonNull String);
+ method public void setSubscriptionPlans(int, @NonNull android.telephony.SubscriptionPlan[], @NonNull String);
+ method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void unregisterSubscriptionCallback(@NonNull android.net.NetworkPolicyManager.SubscriptionCallback);
+ field public static final int SUBSCRIPTION_OVERRIDE_CONGESTED = 2; // 0x2
+ field public static final int SUBSCRIPTION_OVERRIDE_UNMETERED = 1; // 0x1
+ }
+
+ public static class NetworkPolicyManager.SubscriptionCallback {
+ ctor public NetworkPolicyManager.SubscriptionCallback();
+ method public void onSubscriptionOverride(int, int, int);
+ method public void onSubscriptionPlansChanged(int, @NonNull android.telephony.SubscriptionPlan[]);
+ }
+
public class NetworkProvider {
ctor public NetworkProvider(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String);
method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void declareNetworkRequestUnfulfillable(@NonNull android.net.NetworkRequest);
@@ -6228,12 +6386,12 @@
}
public class NetworkScoreManager {
- method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, "android.permission.REQUEST_NETWORK_SCORES"}) public boolean clearScores() throws java.lang.SecurityException;
- method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, "android.permission.REQUEST_NETWORK_SCORES"}) public void disableScoring() throws java.lang.SecurityException;
- method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, "android.permission.REQUEST_NETWORK_SCORES"}) public String getActiveScorerPackage();
- method @RequiresPermission("android.permission.REQUEST_NETWORK_SCORES") public void registerNetworkScoreCallback(int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.NetworkScoreManager.NetworkScoreCallback) throws java.lang.SecurityException;
- method @RequiresPermission("android.permission.REQUEST_NETWORK_SCORES") public boolean requestScores(@NonNull android.net.NetworkKey[]) throws java.lang.SecurityException;
- method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, "android.permission.REQUEST_NETWORK_SCORES"}) public boolean setActiveScorer(String) throws java.lang.SecurityException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public boolean clearScores() throws java.lang.SecurityException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public void disableScoring() throws java.lang.SecurityException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public String getActiveScorerPackage();
+ method @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES) public void registerNetworkScoreCallback(int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.NetworkScoreManager.NetworkScoreCallback) throws java.lang.SecurityException;
+ method @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES) public boolean requestScores(@NonNull java.util.Collection<android.net.NetworkKey>) throws java.lang.SecurityException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public boolean setActiveScorer(String) throws java.lang.SecurityException;
method @RequiresPermission(android.Manifest.permission.SCORE_NETWORKS) public boolean updateScores(@NonNull android.net.ScoredNetwork[]) throws java.lang.SecurityException;
field @Deprecated public static final String ACTION_CHANGE_ACTIVE = "android.net.scoring.CHANGE_ACTIVE";
field public static final String ACTION_CUSTOM_ENABLE = "android.net.scoring.CUSTOM_ENABLE";
@@ -6248,9 +6406,10 @@
field public static final int SCORE_FILTER_SCAN_RESULTS = 2; // 0x2
}
- public static interface NetworkScoreManager.NetworkScoreCallback {
- method public void clearScores();
- method public void updateScores(@NonNull java.util.List<android.net.ScoredNetwork>);
+ public abstract static class NetworkScoreManager.NetworkScoreCallback {
+ ctor public NetworkScoreManager.NetworkScoreCallback();
+ method public abstract void onScoresInvalidated();
+ method public abstract void onScoresUpdated(@NonNull java.util.Collection<android.net.ScoredNetwork>);
}
public abstract class NetworkSpecifier {
@@ -6359,19 +6518,65 @@
method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@Nullable android.net.LinkAddress);
}
- public final class StringNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
- ctor public StringNetworkSpecifier(@NonNull String);
- method public int describeContents();
- method public boolean satisfiedBy(android.net.NetworkSpecifier);
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.StringNetworkSpecifier> CREATOR;
- field @NonNull public final String specifier;
- }
-
public final class TelephonyNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
method public boolean satisfiedBy(android.net.NetworkSpecifier);
}
+ public class TetheringManager {
+ method public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
+ method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
+ method public void stopAllTethering();
+ method public void stopTethering(int);
+ method public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
+ field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
+ field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
+ field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
+ field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
+ field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
+ field public static final int TETHERING_BLUETOOTH = 2; // 0x2
+ field public static final int TETHERING_INVALID = -1; // 0xffffffff
+ field public static final int TETHERING_USB = 1; // 0x1
+ field public static final int TETHERING_WIFI = 0; // 0x0
+ field public static final int TETHERING_WIFI_P2P = 3; // 0x3
+ field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
+ field public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; // 0x9
+ field public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; // 0x8
+ field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
+ field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
+ field public static final int TETHER_ERROR_MASTER_ERROR = 5; // 0x5
+ field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
+ field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
+ field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
+ field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
+ field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
+ field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
+ field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
+ field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
+ field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
+ field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
+ }
+
+ public static interface TetheringManager.OnTetheringEntitlementResultListener {
+ method public void onTetheringEntitlementResult(int);
+ }
+
+ public abstract static class TetheringManager.TetheringEventCallback {
+ ctor public TetheringManager.TetheringEventCallback();
+ method public void onError(@NonNull String, int);
+ method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
+ method public void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+ method public void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+ method public void onTetheringSupported(boolean);
+ method public void onUpstreamChanged(@Nullable android.net.Network);
+ }
+
+ @Deprecated public static class TetheringManager.TetheringInterfaceRegexps {
+ ctor @Deprecated public TetheringManager.TetheringInterfaceRegexps(@NonNull String[], @NonNull String[], @NonNull String[]);
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
+ }
+
public class TrafficStats {
method public static void setThreadStatsTagApp();
method public static void setThreadStatsTagBackup();
@@ -7329,12 +7534,12 @@
method @NonNull public String getCaPath();
method @NonNull public String getClientCertificateAlias();
method public int getOcsp();
- method @Nullable public String getWapiCertSuite();
+ method @NonNull public String getWapiCertSuite();
method public void setCaCertificateAliases(@Nullable String[]);
- method public void setCaPath(@Nullable String);
- method public void setClientCertificateAlias(@Nullable String);
+ method public void setCaPath(@NonNull String);
+ method public void setClientCertificateAlias(@NonNull String);
method public void setOcsp(int);
- method public void setWapiCertSuite(@Nullable String);
+ method public void setWapiCertSuite(@NonNull String);
field public static final int OCSP_NONE = 0; // 0x0
field public static final int OCSP_REQUEST_CERT_STATUS = 1; // 0x1
field public static final int OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS = 3; // 0x3
@@ -7372,6 +7577,7 @@
public class WifiManager {
method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void addOnWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoin(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoinGlobal(boolean);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoinPasspoint(@NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void clearWifiConnectedNetworkScorer();
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
@@ -7387,6 +7593,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String[] getFactoryMacAddresses();
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,java.util.List<android.net.wifi.ScanResult>> getMatchingOsuProviders(@Nullable java.util.List<android.net.wifi.ScanResult>);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,android.net.wifi.hotspot2.PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(@NonNull java.util.Set<android.net.wifi.hotspot2.OsuProvider>);
+ method @NonNull @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE}) public java.util.Map<android.net.wifi.WifiNetworkSuggestion,java.util.List<android.net.wifi.ScanResult>> getMatchingScanResults(@NonNull java.util.List<android.net.wifi.WifiNetworkSuggestion>, @Nullable java.util.List<android.net.wifi.ScanResult>);
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.SoftApConfiguration getSoftApConfiguration();
method public int getVerboseLoggingLevel();
@@ -7401,11 +7608,8 @@
method public boolean isPortableHotspotSupported();
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled();
method public boolean isWifiScannerSupported();
- method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerNetworkRequestMatchCallback(@NonNull android.net.wifi.WifiManager.NetworkRequestMatchCallback);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerNetworkRequestMatchCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.NetworkRequestMatchCallback);
- method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerSoftApCallback(@NonNull android.net.wifi.WifiManager.SoftApCallback);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerSoftApCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback);
- method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerTrafficStateCallback(@NonNull android.net.wifi.WifiManager.TrafficStateCallback);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerTrafficStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.TrafficStateCallback);
method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void removeOnWifiUsabilityStatsListener(@NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void restoreBackupData(@NonNull byte[]);
@@ -8141,22 +8345,22 @@
method @NonNull public android.os.BatterySaverPolicyConfig.Builder setLocationMode(int);
}
- public class BatteryStatsManager {
+ public final class BatteryStatsManager {
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.os.connectivity.CellularBatteryStats getCellularBatteryStats();
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.os.connectivity.WifiBatteryStats getWifiBatteryStats();
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteFullWifiLockAcquiredFromSource(@NonNull android.os.WorkSource);
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteFullWifiLockReleasedFromSource(@NonNull android.os.WorkSource);
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiBatchedScanStartedFromSource(@NonNull android.os.WorkSource, @IntRange(from=0) int);
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiBatchedScanStoppedFromSource(@NonNull android.os.WorkSource);
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiMulticastDisabled(int);
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiMulticastEnabled(int);
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiOff();
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiOn();
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiRssiChanged(@IntRange(from=0xffffff81, to=0) int);
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiScanStartedFromSource(@NonNull android.os.WorkSource);
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiScanStoppedFromSource(@NonNull android.os.WorkSource);
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiState(int, @Nullable String);
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void noteWifiSupplicantStateChanged(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockAcquiredFromSource(@NonNull android.os.WorkSource);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockReleasedFromSource(@NonNull android.os.WorkSource);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiBatchedScanStartedFromSource(@NonNull android.os.WorkSource, @IntRange(from=0) int);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiBatchedScanStoppedFromSource(@NonNull android.os.WorkSource);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiMulticastDisabled(int);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiMulticastEnabled(int);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiOff();
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiOn();
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiRssiChanged(@IntRange(from=0xffffff81, to=0) int);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiScanStartedFromSource(@NonNull android.os.WorkSource);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiScanStoppedFromSource(@NonNull android.os.WorkSource);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiState(int, @Nullable String);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiSupplicantStateChanged(int, boolean);
field public static final int WIFI_STATE_OFF = 0; // 0x0
field public static final int WIFI_STATE_OFF_SCANNING = 1; // 0x1
field public static final int WIFI_STATE_ON_CONNECTED_P2P = 5; // 0x5
@@ -8620,18 +8824,14 @@
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getEuiccCardControllerServiceRegisterer();
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getEuiccControllerService();
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getIccPhoneBookServiceRegisterer();
- method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getNetworkPolicyServiceRegisterer();
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getOpportunisticNetworkServiceRegisterer();
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getPackageManagerServiceRegisterer();
- method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getPermissionManagerServiceRegisterer();
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getPhoneSubServiceRegisterer();
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getSmsServiceRegisterer();
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getSubscriptionServiceRegisterer();
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyImsServiceRegisterer();
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyRcsMessageServiceRegisterer();
- method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyRegistryServiceRegisterer();
method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyServiceRegisterer();
- method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getWindowServiceRegisterer();
}
public static class TelephonyServiceManager.ServiceNotFoundException extends java.lang.Exception {
@@ -8851,12 +9051,12 @@
public final class WifiBatteryStats implements android.os.Parcelable {
method public int describeContents();
+ method public long getAppScanRequestCount();
method public long getEnergyConsumedMaMillis();
method public long getIdleTimeMillis();
method public long getKernelActiveTimeMillis();
method public long getLoggingDurationMillis();
method public long getMonitoredRailChargeConsumedMaMillis();
- method public long getNumAppScanRequest();
method public long getNumBytesRx();
method public long getNumBytesTx();
method public long getNumPacketsRx();
@@ -9555,6 +9755,7 @@
field public static final String HPLMNS = "hplmns";
field public static final String ICC_ID = "icc_id";
field public static final String IMSI = "imsi";
+ field public static final String IMS_RCS_UCE_ENABLED = "ims_rcs_uce_enabled";
field public static final String ISO_COUNTRY_CODE = "iso_country_code";
field public static final String IS_EMBEDDED = "is_embedded";
field public static final String IS_OPPORTUNISTIC = "is_opportunistic";
@@ -9967,6 +10168,16 @@
public abstract class DataLoaderService extends android.app.Service {
ctor public DataLoaderService();
+ method @Nullable public android.service.dataloader.DataLoaderService.DataLoader onCreateDataLoader();
+ }
+
+ public static interface DataLoaderService.DataLoader {
+ method public boolean onCreate(@NonNull android.content.pm.DataLoaderParams, @NonNull android.service.dataloader.DataLoaderService.FileSystemConnector);
+ method public boolean onPrepareImage(@NonNull java.util.Collection<android.content.pm.InstallationFile>, @NonNull java.util.Collection<java.lang.String>);
+ }
+
+ public static final class DataLoaderService.FileSystemConnector {
+ method public void writeData(@NonNull String, long, long, @NonNull android.os.ParcelFileDescriptor) throws java.io.IOException;
}
}
@@ -10035,6 +10246,7 @@
public abstract class EuiccService extends android.app.Service {
ctor public EuiccService();
method public void dump(@NonNull java.io.PrintWriter);
+ method public int encodeSmdxSubjectAndReasonCode(@Nullable String, @Nullable String) throws java.lang.IllegalArgumentException, java.lang.NumberFormatException, java.lang.UnsupportedOperationException;
method @CallSuper public android.os.IBinder onBind(android.content.Intent);
method public abstract int onDeleteSubscription(int, String);
method public android.service.euicc.DownloadSubscriptionResult onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean, @Nullable android.os.Bundle);
@@ -10133,6 +10345,7 @@
field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
field public static final String KEY_CONTEXTUAL_ACTIONS = "key_contextual_actions";
field public static final String KEY_IMPORTANCE = "key_importance";
+ field public static final String KEY_NOT_CONVERSATION = "key_not_conversation";
field public static final String KEY_PEOPLE = "key_people";
field public static final String KEY_RANKING_SCORE = "key_ranking_score";
field public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
@@ -10394,6 +10607,14 @@
}
+package android.service.voice {
+
+ public class VoiceInteractionService extends android.app.Service {
+ method @NonNull @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public final android.media.voice.KeyphraseModelManager createKeyphraseModelManager();
+ }
+
+}
+
package android.service.wallpaper {
public class WallpaperService.Engine {
@@ -10739,6 +10960,13 @@
field public static final int TRANSPORT_TYPE_INVALID = -1; // 0xffffffff
}
+ public static final class AccessNetworkConstants.NgranBands {
+ method public static int getFrequencyRangeGroup(int);
+ field public static final int FREQUENCY_RANGE_GROUP_1 = 1; // 0x1
+ field public static final int FREQUENCY_RANGE_GROUP_2 = 2; // 0x2
+ field public static final int FREQUENCY_RANGE_GROUP_UNKNOWN = 0; // 0x0
+ }
+
public final class BarringInfo implements android.os.Parcelable {
ctor public BarringInfo();
method @NonNull public android.telephony.BarringInfo createLocationInfoSanitizedCopy();
@@ -11375,7 +11603,7 @@
}
public final class ModemActivityInfo implements android.os.Parcelable {
- ctor public ModemActivityInfo(long, int, int, @Nullable int[], int);
+ ctor public ModemActivityInfo(long, int, int, @NonNull int[], int);
method public int describeContents();
method public int getIdleTimeMillis();
method public int getReceiveTimeMillis();
@@ -11776,6 +12004,7 @@
}
public final class SmsManager {
+ method @RequiresPermission(android.Manifest.permission.ACCESS_MESSAGES_ON_ICC) public boolean copyMessageToIcc(@Nullable byte[], @NonNull byte[], int);
method @RequiresPermission(android.Manifest.permission.ACCESS_MESSAGES_ON_ICC) public boolean deleteMessageFromIcc(int);
method public boolean disableCellBroadcastRange(int, int, int);
method public boolean enableCellBroadcastRange(int, int, int);
@@ -11793,6 +12022,7 @@
public class SmsMessage {
method @Nullable public static android.telephony.SmsMessage createFromNativeSmsSubmitPdu(@NonNull byte[], boolean);
+ method @Nullable public static android.telephony.SmsMessage.SubmitPdu getSmsPdu(int, int, @Nullable String, @NonNull String, @NonNull String, long);
method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static byte[] getSubmitPduEncodedMessage(boolean, @NonNull String, @NonNull String, int, int, int, int, int, int);
}
@@ -11945,7 +12175,6 @@
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isManualNetworkSelectionAllowed();
method public boolean isModemEnabledForSlot(int);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
@@ -11958,6 +12187,7 @@
method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
method public boolean needsOtaServiceProvisioning();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyUserActivity();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean);
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
@@ -11981,6 +12211,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setIccLockEnabled(boolean, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setNetworkSelectionModeManual(@NonNull String, int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPolicyDataEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
@@ -12015,6 +12246,7 @@
field public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
field public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED = "android.intent.action.EMERGENCY_CALL_STATE_CHANGED";
field public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE = "com.android.omadm.service.CONFIGURATION_UPDATE";
+ field public static final String ACTION_SERVICE_PROVIDERS_UPDATED = "android.telephony.action.SERVICE_PROVIDERS_UPDATED";
field public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS = "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS";
field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
@@ -12037,6 +12269,7 @@
field public static final String EXTRA_APN_PROTOCOL_INT = "apnProtoInt";
field @Deprecated public static final String EXTRA_APN_TYPE = "apnType";
field public static final String EXTRA_APN_TYPE_INT = "apnTypeInt";
+ field public static final String EXTRA_DATA_SPN = "android.telephony.extra.DATA_SPN";
field public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = "defaultNetworkAvailable";
field public static final String EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE = "android.telephony.extra.DEFAULT_SUBSCRIPTION_SELECT_TYPE";
field public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL = 4; // 0x4
@@ -12049,17 +12282,22 @@
field public static final String EXTRA_PCO_VALUE = "pcoValue";
field public static final String EXTRA_PHONE_IN_ECM_STATE = "android.telephony.extra.PHONE_IN_ECM_STATE";
field public static final String EXTRA_PHONE_IN_EMERGENCY_CALL = "android.telephony.extra.PHONE_IN_EMERGENCY_CALL";
+ field public static final String EXTRA_PLMN = "android.telephony.extra.PLMN";
field public static final String EXTRA_REDIRECTION_URL = "redirectionUrl";
+ field public static final String EXTRA_SHOW_PLMN = "android.telephony.extra.SHOW_PLMN";
+ field public static final String EXTRA_SHOW_SPN = "android.telephony.extra.SHOW_SPN";
field public static final String EXTRA_SIM_COMBINATION_NAMES = "android.telephony.extra.SIM_COMBINATION_NAMES";
field public static final String EXTRA_SIM_COMBINATION_WARNING_TYPE = "android.telephony.extra.SIM_COMBINATION_WARNING_TYPE";
field public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA = 1; // 0x1
field public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE = 0; // 0x0
field public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
+ field public static final String EXTRA_SPN = "android.telephony.extra.SPN";
field public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
field public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff
field public static final int KEY_TYPE_EPDG = 1; // 0x1
field public static final int KEY_TYPE_WLAN = 2; // 0x2
+ field public static final String MODEM_ACTIVITY_RESULT_KEY = "controller_activity";
field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L
field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L
@@ -12104,10 +12342,37 @@
public class TelephonyRegistryManager {
method public void addOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
method public void addOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
+ method public void listenForSubscriber(int, @NonNull String, @NonNull String, @NonNull android.telephony.PhoneStateListener, int, boolean);
+ method public void notifyActiveDataSubIdChanged(int);
method public void notifyBarringInfoChanged(int, int, @NonNull android.telephony.BarringInfo);
+ method public void notifyCallForwardingChanged(int, boolean);
+ method public void notifyCallQualityChanged(int, int, @NonNull android.telephony.CallQuality, int);
+ method public void notifyCallStateChanged(int, int, int, @Nullable String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyCallStateChangedForAllSubscriptions(int, @Nullable String);
method public void notifyCarrierNetworkChange(boolean);
+ method public void notifyCellInfoChanged(int, @NonNull java.util.List<android.telephony.CellInfo>);
+ method public void notifyCellLocation(int, @NonNull android.telephony.CellIdentity);
+ method public void notifyDataActivationStateChanged(int, int, int);
+ method public void notifyDataActivityChanged(int, int);
+ method public void notifyDataConnectionForSubscriber(int, int, int, @Nullable android.telephony.PreciseDataConnectionState);
+ method public void notifyDisconnectCause(int, int, int, int);
+ method public void notifyEmergencyNumberList(int, int);
+ method public void notifyImsDisconnectCause(int, @NonNull android.telephony.ims.ImsReasonInfo);
+ method public void notifyMessageWaitingChanged(int, int, boolean);
+ method public void notifyOpportunisticSubscriptionInfoChanged();
+ method public void notifyOutgoingEmergencyCall(int, int, @NonNull android.telephony.emergency.EmergencyNumber);
+ method public void notifyOutgoingEmergencySms(int, int, @NonNull android.telephony.emergency.EmergencyNumber);
+ method public void notifyPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
+ method public void notifyPreciseCallState(int, int, int, int, int);
+ method public void notifyPreciseDataConnectionFailed(int, int, int, @Nullable String, int);
+ method public void notifyRadioPowerStateChanged(int, int, int);
method public void notifyRegistrationFailed(int, int, @NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
+ method public void notifyServiceStateChanged(int, int, @NonNull android.telephony.ServiceState);
+ method public void notifySignalStrengthChanged(int, int, @NonNull android.telephony.SignalStrength);
+ method public void notifySrvccStateChanged(int, int);
+ method public void notifySubscriptionInfoChanged();
+ method public void notifyUserMobileDataStateChanged(int, int, boolean);
+ method public void notifyVoiceActivationStateChanged(int, int, int);
method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
method public void removeOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
}
@@ -12411,6 +12676,11 @@
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDefaultDownloadableSubscriptionList(android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public int getOtaStatus();
+ method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public java.util.List<java.lang.String> getSupportedCountries();
+ method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public java.util.List<java.lang.String> getUnsupportedCountries();
+ method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public boolean isSupportedCountry(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void setSupportedCountries(@NonNull java.util.List<java.lang.String>);
+ method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void setUnsupportedCountries(@NonNull java.util.List<java.lang.String>);
field public static final String ACTION_DELETE_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.DELETE_SUBSCRIPTION_PRIVILEGED";
field @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public static final String ACTION_OTA_STATUS_CHANGED = "android.telephony.euicc.action.OTA_STATUS_CHANGED";
field public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
@@ -12576,6 +12846,7 @@
field public static final String EXTRA_DIALSTRING = "dialstring";
field public static final String EXTRA_DISPLAY_TEXT = "DisplayText";
field public static final String EXTRA_EMERGENCY_CALL = "e_call";
+ field public static final String EXTRA_FORWARDED_NUMBER = "android.telephony.ims.extra.FORWARDED_NUMBER";
field public static final String EXTRA_IS_CALL_PULL = "CallPull";
field public static final String EXTRA_OI = "oi";
field public static final String EXTRA_OIR = "oir";
@@ -12687,6 +12958,9 @@
method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int);
method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
+ field public static final String ACTION_WFC_IMS_REGISTRATION_ERROR = "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
+ field public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
+ field public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
}
public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
@@ -13101,6 +13375,29 @@
method @NonNull public android.telephony.ims.RcsContactUceCapability build();
}
+ public class RcsUceAdapter {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
+ field public static final int ERROR_ALREADY_IN_QUEUE = 13; // 0xd
+ field public static final int ERROR_FORBIDDEN = 6; // 0x6
+ field public static final int ERROR_GENERIC_FAILURE = 1; // 0x1
+ field public static final int ERROR_INSUFFICIENT_MEMORY = 11; // 0xb
+ field public static final int ERROR_LOST_NETWORK = 12; // 0xc
+ field public static final int ERROR_NOT_AUTHORIZED = 5; // 0x5
+ field public static final int ERROR_NOT_AVAILABLE = 3; // 0x3
+ field public static final int ERROR_NOT_ENABLED = 2; // 0x2
+ field public static final int ERROR_NOT_FOUND = 7; // 0x7
+ field public static final int ERROR_NOT_REGISTERED = 4; // 0x4
+ field public static final int ERROR_REQUEST_TIMEOUT = 10; // 0xa
+ field public static final int ERROR_REQUEST_TOO_LARGE = 8; // 0x8
+ field public static final int PUBLISH_STATE_200_OK = 1; // 0x1
+ field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2
+ field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6
+ field public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; // 0x4
+ field public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; // 0x5
+ field public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3; // 0x3
+ }
+
}
package android.telephony.ims.feature {
@@ -13362,6 +13659,7 @@
method public int transact(android.os.Bundle);
method public int updateCallBarring(int, int, String[]);
method public int updateCallBarringForServiceClass(int, int, String[], int);
+ method public int updateCallBarringWithPassword(int, int, @Nullable String[], int, @NonNull String);
method public int updateCallForward(int, int, String, int, int);
method public int updateCallWaiting(boolean, int);
method public int updateClip(boolean);
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index fde6bb3..23e1ed7 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -8,6 +8,15 @@
ActionValue: android.net.wifi.WifiManager#ACTION_LINK_CONFIGURATION_CHANGED:
+// Tethering broadcast action / extras cannot change name for backwards compatibility
+ActionValue: android.net.TetheringManager#ACTION_TETHER_STATE_CHANGED:
+ Inconsistent action value; expected `android.net.action.TETHER_STATE_CHANGED`, was `android.net.conn.TETHER_STATE_CHANGED`
+ActionValue: android.net.TetheringManager#EXTRA_ACTIVE_TETHER:
+ Inconsistent extra value; expected `android.net.extra.ACTIVE_TETHER`, was `tetherArray`
+ActionValue: android.net.TetheringManager#EXTRA_AVAILABLE_TETHER:
+ Inconsistent extra value; expected `android.net.extra.AVAILABLE_TETHER`, was `availableArray`
+ActionValue: android.net.TetheringManager#EXTRA_ERRORED_TETHER:
+ Inconsistent extra value; expected `android.net.extra.ERRORED_TETHER`, was `erroredArray`
ArrayReturn: android.bluetooth.BluetoothCodecStatus#BluetoothCodecStatus(android.bluetooth.BluetoothCodecConfig, android.bluetooth.BluetoothCodecConfig[], android.bluetooth.BluetoothCodecConfig[]) parameter #1:
Method parameter should be Collection<BluetoothCodecConfig> (or subclass) instead of raw array; was `android.bluetooth.BluetoothCodecConfig[]`
diff --git a/api/test-current.txt b/api/test-current.txt
index 75297e5..e6e8779 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -10,6 +10,7 @@
field public static final String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE";
field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
+ field public static final String CONTROL_DEVICE_LIGHTS = "android.permission.CONTROL_DEVICE_LIGHTS";
field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
field public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
field public static final String MANAGE_CRATES = "android.permission.MANAGE_CRATES";
@@ -19,7 +20,7 @@
field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
- field public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
+ field @Deprecated public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
field public static final String WRITE_OBB = "android.permission.WRITE_OBB";
}
@@ -704,7 +705,7 @@
package android.companion {
public final class CompanionDeviceManager {
- method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean isDeviceAssociated(@NonNull String, @NonNull android.net.MacAddress, @NonNull android.os.UserHandle);
+ method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean isDeviceAssociatedForWifiConnection(@NonNull String, @NonNull android.net.MacAddress, @NonNull android.os.UserHandle);
}
}
@@ -742,6 +743,10 @@
field @Nullable public final android.util.ArraySet<android.content.ComponentName> whitelistedComponents;
}
+ public abstract class ContentProvider implements android.content.ComponentCallbacks2 {
+ method @NonNull public static android.os.UserHandle getUserHandleFromUri(@NonNull android.net.Uri);
+ }
+
public class ContentProviderClient implements java.lang.AutoCloseable {
method @RequiresPermission(android.Manifest.permission.REMOVE_TASKS) public void setDetectNotResponding(long);
}
@@ -754,7 +759,6 @@
method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle, int);
method @NonNull public android.content.Context createPackageContextAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public java.io.File getCrateDir(@NonNull String);
- method public abstract android.view.Display getDisplay();
method public abstract int getDisplayId();
method public android.os.UserHandle getUser();
method public int getUserId();
@@ -775,7 +779,6 @@
}
public class ContextWrapper extends android.content.Context {
- method public android.view.Display getDisplay();
method public int getDisplayId();
}
@@ -1128,6 +1131,49 @@
}
+package android.hardware.lights {
+
+ public final class Light implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getId();
+ method public int getOrdinal();
+ method public int getType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.Light> CREATOR;
+ }
+
+ public final class LightState implements android.os.Parcelable {
+ ctor public LightState(@ColorInt int);
+ method public int describeContents();
+ method @ColorInt public int getColor();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.LightState> CREATOR;
+ }
+
+ public final class LightsManager {
+ method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public android.hardware.lights.LightState getLightState(@NonNull android.hardware.lights.Light);
+ method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public java.util.List<android.hardware.lights.Light> getLights();
+ method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public android.hardware.lights.LightsManager.LightsSession openSession();
+ field public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8
+ }
+
+ public final class LightsManager.LightsSession implements java.lang.AutoCloseable {
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void close();
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void setLights(@NonNull android.hardware.lights.LightsRequest);
+ }
+
+ public final class LightsRequest {
+ }
+
+ public static final class LightsRequest.Builder {
+ ctor public LightsRequest.Builder();
+ method @NonNull public android.hardware.lights.LightsRequest build();
+ method @NonNull public android.hardware.lights.LightsRequest.Builder clearLight(@NonNull android.hardware.lights.Light);
+ method @NonNull public android.hardware.lights.LightsRequest.Builder setLight(@NonNull android.hardware.lights.Light, @NonNull android.hardware.lights.LightState);
+ }
+
+}
+
package android.location {
public final class GnssClock implements android.os.Parcelable {
@@ -1141,6 +1187,9 @@
method public void resetElapsedRealtimeUncertaintyNanos();
method public void resetFullBiasNanos();
method public void resetLeapSecond();
+ method public void resetReferenceCarrierFrequencyHzForIsb();
+ method public void resetReferenceCodeTypeForIsb();
+ method public void resetReferenceConstellationTypeForIsb();
method public void resetTimeUncertaintyNanos();
method public void set(android.location.GnssClock);
method public void setBiasNanos(double);
@@ -1152,6 +1201,9 @@
method public void setFullBiasNanos(long);
method public void setHardwareClockDiscontinuityCount(int);
method public void setLeapSecond(int);
+ method public void setReferenceCarrierFrequencyHzForIsb(@FloatRange(from=0.0) double);
+ method public void setReferenceCodeTypeForIsb(@NonNull String);
+ method public void setReferenceConstellationTypeForIsb(int);
method public void setTimeNanos(long);
method public void setTimeUncertaintyNanos(@FloatRange(from=0.0f) double);
}
@@ -1166,6 +1218,10 @@
method @Deprecated public void resetCarrierPhase();
method @Deprecated public void resetCarrierPhaseUncertainty();
method public void resetCodeType();
+ method public void resetReceiverInterSignalBiasNanos();
+ method public void resetReceiverInterSignalBiasUncertaintyNanos();
+ method public void resetSatelliteInterSignalBiasNanos();
+ method public void resetSatelliteInterSignalBiasUncertaintyNanos();
method public void resetSnrInDb();
method public void set(android.location.GnssMeasurement);
method public void setAccumulatedDeltaRangeMeters(double);
@@ -1185,6 +1241,10 @@
method public void setPseudorangeRateUncertaintyMetersPerSecond(double);
method public void setReceivedSvTimeNanos(long);
method public void setReceivedSvTimeUncertaintyNanos(long);
+ method public void setReceiverInterSignalBiasNanos(double);
+ method public void setReceiverInterSignalBiasUncertaintyNanos(@FloatRange(from=0.0) double);
+ method public void setSatelliteInterSignalBiasNanos(double);
+ method public void setSatelliteInterSignalBiasUncertaintyNanos(@FloatRange(from=0.0) double);
method public void setSnrInDb(double);
method public void setState(int);
method public void setSvid(int);
@@ -1395,6 +1455,7 @@
field public static final int RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET = 2; // 0x2
field public static final int RULE_MATCH_ATTRIBUTE_USAGE = 1; // 0x1
field public static final int RULE_MATCH_UID = 4; // 0x4
+ field public static final int RULE_MATCH_USERID = 8; // 0x8
}
public static class AudioMixingRule.Builder {
@@ -1415,9 +1476,11 @@
method public int getFocusDuckingBehavior();
method public int getStatus();
method public boolean removeUidDeviceAffinity(int);
+ method public boolean removeUserIdDeviceAffinity(int);
method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void setRegistration(String);
method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
+ method public boolean setUserIdDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
method public String toLogFriendlyString();
field public static final int FOCUS_POLICY_DUCKING_DEFAULT = 0; // 0x0
field public static final int FOCUS_POLICY_DUCKING_IN_APP = 0; // 0x0
@@ -1558,9 +1621,12 @@
public class LinkAddress implements android.os.Parcelable {
ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int);
+ ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int, long, long);
ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
ctor public LinkAddress(@NonNull String);
ctor public LinkAddress(@NonNull String, int, int);
+ method public long getDeprecationTime();
+ method public long getExpirationTime();
method public boolean isGlobalPreferred();
method public boolean isIpv4();
method public boolean isIpv6();
@@ -1660,6 +1726,61 @@
method public void teardownTestNetwork(@NonNull android.net.Network);
}
+ public class TetheringManager {
+ method public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
+ method @RequiresPermission("android.permission.TETHER_PRIVILEGED") public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
+ method public void stopAllTethering();
+ method public void stopTethering(int);
+ method public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
+ field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
+ field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
+ field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
+ field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
+ field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
+ field public static final int TETHERING_BLUETOOTH = 2; // 0x2
+ field public static final int TETHERING_INVALID = -1; // 0xffffffff
+ field public static final int TETHERING_USB = 1; // 0x1
+ field public static final int TETHERING_WIFI = 0; // 0x0
+ field public static final int TETHERING_WIFI_P2P = 3; // 0x3
+ field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
+ field public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; // 0x9
+ field public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; // 0x8
+ field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
+ field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
+ field public static final int TETHER_ERROR_MASTER_ERROR = 5; // 0x5
+ field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
+ field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
+ field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
+ field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
+ field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
+ field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
+ field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
+ field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
+ field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
+ field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
+ }
+
+ public static interface TetheringManager.OnTetheringEntitlementResultListener {
+ method public void onTetheringEntitlementResult(int);
+ }
+
+ public abstract static class TetheringManager.TetheringEventCallback {
+ ctor public TetheringManager.TetheringEventCallback();
+ method public void onError(@NonNull String, int);
+ method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
+ method public void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+ method public void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+ method public void onTetheringSupported(boolean);
+ method public void onUpstreamChanged(@Nullable android.net.Network);
+ }
+
+ @Deprecated public static class TetheringManager.TetheringInterfaceRegexps {
+ ctor @Deprecated public TetheringManager.TetheringInterfaceRegexps(@NonNull String[], @NonNull String[], @NonNull String[]);
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
+ }
+
public class TrafficStats {
method public static long getLoopbackRxBytes();
method public static long getLoopbackRxPackets();
@@ -3152,6 +3273,8 @@
method @NonNull public android.telecom.ConnectionRequest.Builder setAccountHandle(@NonNull android.telecom.PhoneAccountHandle);
method @NonNull public android.telecom.ConnectionRequest.Builder setAddress(@NonNull android.net.Uri);
method @NonNull public android.telecom.ConnectionRequest.Builder setExtras(@NonNull android.os.Bundle);
+ method @NonNull public android.telecom.ConnectionRequest.Builder setIsAdhocConferenceCall(boolean);
+ method @NonNull public android.telecom.ConnectionRequest.Builder setParticipants(@Nullable java.util.List<android.net.Uri>);
method @NonNull public android.telecom.ConnectionRequest.Builder setRttPipeFromInCall(@NonNull android.os.ParcelFileDescriptor);
method @NonNull public android.telecom.ConnectionRequest.Builder setRttPipeToInCall(@NonNull android.os.ParcelFileDescriptor);
method @NonNull public android.telecom.ConnectionRequest.Builder setShouldShowIncomingCallUi(boolean);
@@ -3191,6 +3314,13 @@
field public static final int TRANSPORT_TYPE_INVALID = -1; // 0xffffffff
}
+ public static final class AccessNetworkConstants.NgranBands {
+ method public static int getFrequencyRangeGroup(int);
+ field public static final int FREQUENCY_RANGE_GROUP_1 = 1; // 0x1
+ field public static final int FREQUENCY_RANGE_GROUP_2 = 2; // 0x2
+ field public static final int FREQUENCY_RANGE_GROUP_UNKNOWN = 0; // 0x0
+ }
+
public final class BarringInfo implements android.os.Parcelable {
ctor public BarringInfo();
ctor public BarringInfo(@Nullable android.telephony.CellIdentity, @NonNull android.util.SparseArray<android.telephony.BarringInfo.BarringServiceInfo>);
@@ -3367,6 +3497,42 @@
field public static final int UNKNOWN_CARRIER_ID_LIST_VERSION = -1; // 0xffffffff
}
+ public class TelephonyRegistryManager {
+ method public void addOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
+ method public void addOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
+ method public void listenForSubscriber(int, @NonNull String, @NonNull String, @NonNull android.telephony.PhoneStateListener, int, boolean);
+ method public void notifyActiveDataSubIdChanged(int);
+ method public void notifyBarringInfoChanged(int, int, @NonNull android.telephony.BarringInfo);
+ method public void notifyCallForwardingChanged(int, boolean);
+ method public void notifyCallQualityChanged(int, int, @NonNull android.telephony.CallQuality, int);
+ method public void notifyCallStateChanged(int, int, int, @Nullable String);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyCallStateChangedForAllSubscriptions(int, @Nullable String);
+ method public void notifyCarrierNetworkChange(boolean);
+ method public void notifyCellInfoChanged(int, @NonNull java.util.List<android.telephony.CellInfo>);
+ method public void notifyCellLocation(int, @NonNull android.telephony.CellIdentity);
+ method public void notifyDataActivationStateChanged(int, int, int);
+ method public void notifyDataActivityChanged(int, int);
+ method public void notifyDataConnectionForSubscriber(int, int, int, @Nullable android.telephony.PreciseDataConnectionState);
+ method public void notifyDisconnectCause(int, int, int, int);
+ method public void notifyEmergencyNumberList(int, int);
+ method public void notifyImsDisconnectCause(int, @NonNull android.telephony.ims.ImsReasonInfo);
+ method public void notifyMessageWaitingChanged(int, int, boolean);
+ method public void notifyOutgoingEmergencyCall(int, int, @NonNull android.telephony.emergency.EmergencyNumber);
+ method public void notifyOutgoingEmergencySms(int, int, @NonNull android.telephony.emergency.EmergencyNumber);
+ method public void notifyPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
+ method public void notifyPreciseCallState(int, int, int, int, int);
+ method public void notifyPreciseDataConnectionFailed(int, int, int, @Nullable String, int);
+ method public void notifyRadioPowerStateChanged(int, int, int);
+ method public void notifyRegistrationFailed(int, int, @NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
+ method public void notifyServiceStateChanged(int, int, @NonNull android.telephony.ServiceState);
+ method public void notifySignalStrengthChanged(int, int, @NonNull android.telephony.SignalStrength);
+ method public void notifySrvccStateChanged(int, int);
+ method public void notifyUserMobileDataStateChanged(int, int, boolean);
+ method public void notifyVoiceActivationStateChanged(int, int, int);
+ method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
+ method public void removeOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
+ }
+
}
package android.telephony.emergency {
@@ -3476,6 +3642,7 @@
field public static final String EXTRA_DIALSTRING = "dialstring";
field public static final String EXTRA_DISPLAY_TEXT = "DisplayText";
field public static final String EXTRA_EMERGENCY_CALL = "e_call";
+ field public static final String EXTRA_FORWARDED_NUMBER = "android.telephony.ims.extra.FORWARDED_NUMBER";
field public static final String EXTRA_IS_CALL_PULL = "CallPull";
field public static final String EXTRA_OEM_EXTRAS = "android.telephony.ims.extra.OEM_EXTRAS";
field public static final String EXTRA_OI = "oi";
@@ -3588,6 +3755,9 @@
method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int);
method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
+ field public static final String ACTION_WFC_IMS_REGISTRATION_ERROR = "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
+ field public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
+ field public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
}
public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
@@ -3950,6 +4120,29 @@
method public void onProvisioningStringChanged(int, @NonNull String);
}
+ public class RcsUceAdapter {
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
+ field public static final int ERROR_ALREADY_IN_QUEUE = 13; // 0xd
+ field public static final int ERROR_FORBIDDEN = 6; // 0x6
+ field public static final int ERROR_GENERIC_FAILURE = 1; // 0x1
+ field public static final int ERROR_INSUFFICIENT_MEMORY = 11; // 0xb
+ field public static final int ERROR_LOST_NETWORK = 12; // 0xc
+ field public static final int ERROR_NOT_AUTHORIZED = 5; // 0x5
+ field public static final int ERROR_NOT_AVAILABLE = 3; // 0x3
+ field public static final int ERROR_NOT_ENABLED = 2; // 0x2
+ field public static final int ERROR_NOT_FOUND = 7; // 0x7
+ field public static final int ERROR_NOT_REGISTERED = 4; // 0x4
+ field public static final int ERROR_REQUEST_TIMEOUT = 10; // 0xa
+ field public static final int ERROR_REQUEST_TOO_LARGE = 8; // 0x8
+ field public static final int PUBLISH_STATE_200_OK = 1; // 0x1
+ field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2
+ field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6
+ field public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; // 0x4
+ field public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; // 0x5
+ field public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3; // 0x3
+ }
+
}
package android.telephony.ims.feature {
@@ -4211,6 +4404,7 @@
method public int transact(android.os.Bundle);
method public int updateCallBarring(int, int, String[]);
method public int updateCallBarringForServiceClass(int, int, String[], int);
+ method public int updateCallBarringWithPassword(int, int, @Nullable String[], int, @NonNull String);
method public int updateCallForward(int, int, String, int, int);
method public int updateCallWaiting(boolean, int);
method public int updateClip(boolean);
@@ -4498,22 +4692,6 @@
method public abstract String asyncImpl() default "";
}
- public class SurfaceControlViewHost {
- ctor public SurfaceControlViewHost(@NonNull android.content.Context, @NonNull android.view.Display, @Nullable android.os.IBinder);
- method public void addView(android.view.View, android.view.WindowManager.LayoutParams);
- method public void dispose();
- method @Nullable public android.view.SurfaceControlViewHost.SurfacePackage getSurfacePackage();
- method public void relayout(android.view.WindowManager.LayoutParams);
- }
-
- public class SurfaceControlViewHost.SurfacePackage {
- method @NonNull public android.view.SurfaceControl getSurfaceControl();
- }
-
- public class SurfaceView extends android.view.View {
- method @Nullable public android.os.IBinder getInputToken();
- }
-
@UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
method public android.view.View getTooltipView();
method public boolean isAutofilled();
@@ -4558,7 +4736,7 @@
field public static final int ACCESSIBILITY_TITLE_CHANGED = 33554432; // 0x2000000
field public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 64; // 0x40
field public CharSequence accessibilityTitle;
- field @android.view.ViewDebug.ExportedProperty(flagMapping={@android.view.ViewDebug.FlagToString(mask=0x1, equals=0x1, name="FAKE_HARDWARE_ACCELERATED"), @android.view.ViewDebug.FlagToString(mask=0x2, equals=0x2, name="FORCE_HARDWARE_ACCELERATED"), @android.view.ViewDebug.FlagToString(mask=0x4, equals=0x4, name="WANTS_OFFSET_NOTIFICATIONS"), @android.view.ViewDebug.FlagToString(mask=0x10, equals=0x10, name="SHOW_FOR_ALL_USERS"), @android.view.ViewDebug.FlagToString(mask=android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION, equals=android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION, name="NO_MOVE_ANIMATION"), @android.view.ViewDebug.FlagToString(mask=0x80, equals=0x80, name="COMPATIBLE_WINDOW"), @android.view.ViewDebug.FlagToString(mask=0x100, equals=0x100, name="SYSTEM_ERROR"), @android.view.ViewDebug.FlagToString(mask=0x800, equals=0x800, name="DISABLE_WALLPAPER_TOUCH_EVENTS"), @android.view.ViewDebug.FlagToString(mask=0x1000, equals=0x1000, name="FORCE_STATUS_BAR_VISIBLE"), @android.view.ViewDebug.FlagToString(mask=0x2000, equals=0x2000, name="PRESERVE_GEOMETRY"), @android.view.ViewDebug.FlagToString(mask=0x4000, equals=0x4000, name="FORCE_DECOR_VIEW_VISIBILITY"), @android.view.ViewDebug.FlagToString(mask=0x8000, equals=0x8000, name="WILL_NOT_REPLACE_ON_RELAUNCH"), @android.view.ViewDebug.FlagToString(mask=0x10000, equals=0x10000, name="LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME"), @android.view.ViewDebug.FlagToString(mask=0x20000, equals=0x20000, name="FORCE_DRAW_STATUS_BAR_BACKGROUND"), @android.view.ViewDebug.FlagToString(mask=0x40000, equals=0x40000, name="SUSTAINED_PERFORMANCE_MODE"), @android.view.ViewDebug.FlagToString(mask=0x80000, equals=0x80000, name="HIDE_NON_SYSTEM_OVERLAY_WINDOWS"), @android.view.ViewDebug.FlagToString(mask=0x100000, equals=0x100000, name="IS_ROUNDED_CORNERS_OVERLAY"), @android.view.ViewDebug.FlagToString(mask=0x400000, equals=0x400000, name="IS_SCREEN_DECOR"), @android.view.ViewDebug.FlagToString(mask=0x800000, equals=0x800000, name="STATUS_FORCE_SHOW_NAVIGATION"), @android.view.ViewDebug.FlagToString(mask=0x1000000, equals=0x1000000, name="COLOR_SPACE_AGNOSTIC"), @android.view.ViewDebug.FlagToString(mask=0x4000000, equals=0x4000000, name="APPEARANCE_CONTROLLED"), @android.view.ViewDebug.FlagToString(mask=0x8000000, equals=0x8000000, name="BEHAVIOR_CONTROLLED"), @android.view.ViewDebug.FlagToString(mask=0x10000000, equals=0x10000000, name="FIT_INSETS_CONTROLLED"), @android.view.ViewDebug.FlagToString(mask=0x20000000, equals=0x20000000, name="ONLY_DRAW_BOTTOM_BAR_BACKGROUND")}) public int privateFlags;
+ field @android.view.ViewDebug.ExportedProperty(flagMapping={@android.view.ViewDebug.FlagToString(mask=0x1, equals=0x1, name="FAKE_HARDWARE_ACCELERATED"), @android.view.ViewDebug.FlagToString(mask=0x2, equals=0x2, name="FORCE_HARDWARE_ACCELERATED"), @android.view.ViewDebug.FlagToString(mask=0x4, equals=0x4, name="WANTS_OFFSET_NOTIFICATIONS"), @android.view.ViewDebug.FlagToString(mask=0x10, equals=0x10, name="SHOW_FOR_ALL_USERS"), @android.view.ViewDebug.FlagToString(mask=android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION, equals=android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION, name="NO_MOVE_ANIMATION"), @android.view.ViewDebug.FlagToString(mask=0x80, equals=0x80, name="COMPATIBLE_WINDOW"), @android.view.ViewDebug.FlagToString(mask=0x100, equals=0x100, name="SYSTEM_ERROR"), @android.view.ViewDebug.FlagToString(mask=0x800, equals=0x800, name="DISABLE_WALLPAPER_TOUCH_EVENTS"), @android.view.ViewDebug.FlagToString(mask=0x1000, equals=0x1000, name="FORCE_STATUS_BAR_VISIBLE"), @android.view.ViewDebug.FlagToString(mask=0x2000, equals=0x2000, name="PRESERVE_GEOMETRY"), @android.view.ViewDebug.FlagToString(mask=0x4000, equals=0x4000, name="FORCE_DECOR_VIEW_VISIBILITY"), @android.view.ViewDebug.FlagToString(mask=0x8000, equals=0x8000, name="WILL_NOT_REPLACE_ON_RELAUNCH"), @android.view.ViewDebug.FlagToString(mask=0x10000, equals=0x10000, name="LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME"), @android.view.ViewDebug.FlagToString(mask=0x20000, equals=0x20000, name="FORCE_DRAW_STATUS_BAR_BACKGROUND"), @android.view.ViewDebug.FlagToString(mask=0x40000, equals=0x40000, name="SUSTAINED_PERFORMANCE_MODE"), @android.view.ViewDebug.FlagToString(mask=0x80000, equals=0x80000, name="HIDE_NON_SYSTEM_OVERLAY_WINDOWS"), @android.view.ViewDebug.FlagToString(mask=0x100000, equals=0x100000, name="IS_ROUNDED_CORNERS_OVERLAY"), @android.view.ViewDebug.FlagToString(mask=0x400000, equals=0x400000, name="IS_SCREEN_DECOR"), @android.view.ViewDebug.FlagToString(mask=0x800000, equals=0x800000, name="STATUS_FORCE_SHOW_NAVIGATION"), @android.view.ViewDebug.FlagToString(mask=0x1000000, equals=0x1000000, name="COLOR_SPACE_AGNOSTIC"), @android.view.ViewDebug.FlagToString(mask=0x4000000, equals=0x4000000, name="APPEARANCE_CONTROLLED"), @android.view.ViewDebug.FlagToString(mask=0x8000000, equals=0x8000000, name="BEHAVIOR_CONTROLLED"), @android.view.ViewDebug.FlagToString(mask=0x10000000, equals=0x10000000, name="FIT_INSETS_CONTROLLED")}) public int privateFlags;
}
}
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index 603f7a2..a9c1836 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -7,6 +7,16 @@
ActionValue: android.location.Location#EXTRA_NO_GPS_LOCATION:
+// Tethering broadcast action / extras cannot change name for backwards compatibility
+ActionValue: android.net.TetheringManager#ACTION_TETHER_STATE_CHANGED:
+ Inconsistent action value; expected `android.net.action.TETHER_STATE_CHANGED`, was `android.net.conn.TETHER_STATE_CHANGED`
+ActionValue: android.net.TetheringManager#EXTRA_ACTIVE_TETHER:
+ Inconsistent extra value; expected `android.net.extra.ACTIVE_TETHER`, was `tetherArray`
+ActionValue: android.net.TetheringManager#EXTRA_AVAILABLE_TETHER:
+ Inconsistent extra value; expected `android.net.extra.AVAILABLE_TETHER`, was `availableArray`
+ActionValue: android.net.TetheringManager#EXTRA_ERRORED_TETHER:
+ Inconsistent extra value; expected `android.net.extra.ERRORED_TETHER`, was `erroredArray`
+
ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_ADDITIONAL_CALL_INFO:
ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CALL_RAT_TYPE:
@@ -396,7 +406,13 @@
GetterSetterNames: android.location.GnssClock#setFullBiasNanos(long):
GetterSetterNames: android.location.GnssClock#setLeapSecond(int):
-
+
+GetterSetterNames: android.location.GnssClock#setReferenceConstellationTypeForIsb(int):
+
+GetterSetterNames: android.location.GnssClock#setReferenceCarrierFrequencyHzForIsb(double):
+
+GetterSetterNames: android.location.GnssClock#setReferenceCodeTypeForIsb(String):
+
GetterSetterNames: android.location.GnssClock#setTimeUncertaintyNanos(double):
GetterSetterNames: android.location.GnssMeasurement#setBasebandCn0DbHz(double):
@@ -404,7 +420,15 @@
GetterSetterNames: android.location.GnssMeasurement#setCarrierFrequencyHz(float):
GetterSetterNames: android.location.GnssMeasurement#setCodeType(String):
-
+
+GetterSetterNames: android.location.GnssMeasurement#setReceiverInterSignalBiasNanos(double):
+
+GetterSetterNames: android.location.GnssMeasurement#setReceiverInterSignalBiasUncertaintyNanos(double):
+
+GetterSetterNames: android.location.GnssMeasurement#setSatelliteInterSignalBiasNanos(double):
+
+GetterSetterNames: android.location.GnssMeasurement#setSatelliteInterSignalBiasUncertaintyNanos(double):
+
GetterSetterNames: android.location.GnssMeasurement#setSnrInDb(double):
GetterSetterNames: android.location.LocationRequest#isLocationSettingsIgnored():
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 192f3f0..d9b3a6c 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -810,6 +810,7 @@
FREQUENT = 2;
RARE = 3;
NEVER = 4;
+ RESTRICTED = 5;
}
optional Bucket standby_bucket = 5 [default = UNKNOWN];
@@ -1723,6 +1724,7 @@
REASON_EXPLICIT_HEALTH_CHECK = 2;
REASON_APP_CRASH = 3;
REASON_APP_NOT_RESPONDING = 4;
+ REASON_NATIVE_CRASH_DURING_BOOT = 5;
}
optional RollbackReasonType rollback_reason = 4;
diff --git a/cmds/svc/src/com/android/commands/svc/UsbCommand.java b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
index 3893be4..cd751f4 100644
--- a/cmds/svc/src/com/android/commands/svc/UsbCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
@@ -42,7 +42,9 @@
+ " Sets the functions which, if the device was charging, become current on"
+ "screen unlock. If function is blank, turn off this feature.\n"
+ " svc usb getFunctions\n"
- + " Gets the list of currently enabled functions\n\n"
+ + " Gets the list of currently enabled functions\n"
+ + " svc usb resetUsbGadget\n"
+ + " Reset usb gadget\n\n"
+ "possible values of [function] are any of 'mtp', 'ptp', 'rndis', 'midi'\n";
}
@@ -75,6 +77,13 @@
System.err.println("Error communicating with UsbManager: " + e);
}
return;
+ } else if ("resetUsbGadget".equals(args[1])) {
+ try {
+ usbMgr.resetUsbGadget();
+ } catch (RemoteException e) {
+ System.err.println("Error communicating with UsbManager: " + e);
+ }
+ return;
}
}
System.err.println(longHelp());
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 57ba181..48f0087 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -7328,6 +7328,15 @@
}
}
+ float getFloatCoreSetting(String key, float defaultValue) {
+ synchronized (mResourcesManager) {
+ if (mCoreSettings != null) {
+ return mCoreSettings.getFloat(key, defaultValue);
+ }
+ return defaultValue;
+ }
+ }
+
private static class AndroidOs extends ForwardingOs {
/**
* Install selective syscall interception. For example, this is used to
diff --git a/core/java/android/app/AppGlobals.java b/core/java/android/app/AppGlobals.java
index 81e1565..f66bf0d 100644
--- a/core/java/android/app/AppGlobals.java
+++ b/core/java/android/app/AppGlobals.java
@@ -75,4 +75,20 @@
return defaultValue;
}
}
+
+ /**
+ * Gets the value of a float core setting.
+ *
+ * @param key The setting key.
+ * @param defaultValue The setting default value.
+ * @return The core settings.
+ */
+ public static float getFloatCoreSetting(String key, float defaultValue) {
+ ActivityThread currentActivityThread = ActivityThread.currentActivityThread();
+ if (currentActivityThread != null) {
+ return currentActivityThread.getFloatCoreSetting(key, defaultValue);
+ } else {
+ return defaultValue;
+ }
+ }
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index bc7e1e5..46f8669 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1159,6 +1159,7 @@
@SystemApi
public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
/** @hide Read device identifiers */
+ @SystemApi
public static final String OPSTR_READ_DEVICE_IDENTIFIERS = "android:read_device_identifiers";
/** @hide Query all packages on device */
public static final String OPSTR_QUERY_ALL_PACKAGES = "android:query_all_packages";
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index fd56834..4e6319d 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -868,7 +868,7 @@
}
@Override
- public CharSequence getBackgroundPermissionButtonLabel() {
+ public CharSequence getBackgroundPermissionOptionLabel() {
try {
String permissionController = getPermissionControllerPackageName();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index cd84310..b7555ee 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -19,7 +19,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AutofillOptions;
import android.content.BroadcastReceiver;
@@ -201,7 +200,7 @@
@UnsupportedAppUsage
private @Nullable ClassLoader mClassLoader;
- private final @Nullable IBinder mActivityToken;
+ private final @Nullable IBinder mToken;
private final @NonNull UserHandle mUser;
@@ -219,7 +218,7 @@
private final @NonNull ResourcesManager mResourcesManager;
@UnsupportedAppUsage
private @NonNull Resources mResources;
- private @Nullable Display mDisplay; // may be null if default display
+ private @Nullable Display mDisplay; // may be null if invalid display or not initialized yet.
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final int mFlags;
@@ -244,6 +243,9 @@
private final Object mSync = new Object();
+ private boolean mIsSystemOrSystemUiContext;
+ private boolean mIsUiContext;
+
@GuardedBy("mSync")
private File mDatabasesDir;
@GuardedBy("mSync")
@@ -1883,6 +1885,9 @@
@Override
public Object getSystemService(String name) {
+ if (isUiComponent(name) && !isUiContext()) {
+ Log.w(TAG, name + " should be accessed from Activity or other visual Context");
+ }
return SystemServiceRegistry.getSystemService(this, name);
}
@@ -1891,6 +1896,15 @@
return SystemServiceRegistry.getSystemServiceName(serviceClass);
}
+ boolean isUiContext() {
+ return mIsSystemOrSystemUiContext || mIsUiContext;
+ }
+
+ private static boolean isUiComponent(String name) {
+ return WINDOW_SERVICE.equals(name) || LAYOUT_INFLATER_SERVICE.equals(name)
+ || WALLPAPER_SERVICE.equals(name);
+ }
+
@Override
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
@@ -2229,12 +2243,12 @@
LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
flags | CONTEXT_REGISTER_PACKAGE);
if (pi != null) {
- ContextImpl c = new ContextImpl(this, mMainThread, pi, null, null, mActivityToken,
+ ContextImpl c = new ContextImpl(this, mMainThread, pi, null, null, mToken,
new UserHandle(UserHandle.getUserId(application.uid)), flags, null, null);
final int displayId = getDisplayId();
- c.setResources(createResources(mActivityToken, pi, null, displayId, null,
+ c.setResources(createResources(mToken, pi, null, displayId, null,
getDisplayAdjustments(displayId).getCompatibilityInfo()));
if (c.mResources != null) {
return c;
@@ -2258,18 +2272,18 @@
// The system resources are loaded in every application, so we can safely copy
// the context without reloading Resources.
return new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, null,
- mActivityToken, user, flags, null, null);
+ mToken, user, flags, null, null);
}
LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
if (pi != null) {
ContextImpl c = new ContextImpl(this, mMainThread, pi, mFeatureId, null,
- mActivityToken, user, flags, null, null);
+ mToken, user, flags, null, null);
final int displayId = getDisplayId();
- c.setResources(createResources(mActivityToken, pi, null, displayId, null,
+ c.setResources(createResources(mToken, pi, null, displayId, null,
getDisplayAdjustments(displayId).getCompatibilityInfo()));
if (c.mResources != null) {
return c;
@@ -2301,12 +2315,12 @@
final String[] paths = mPackageInfo.getSplitPaths(splitName);
final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo,
- mFeatureId, splitName, mActivityToken, mUser, mFlags, classLoader, null);
+ mFeatureId, splitName, mToken, mUser, mFlags, classLoader, null);
final int displayId = getDisplayId();
context.setResources(ResourcesManager.getInstance().getResources(
- mActivityToken,
+ mToken,
mPackageInfo.getResDir(),
paths,
mPackageInfo.getOverlayDirs(),
@@ -2325,10 +2339,10 @@
}
ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId,
- mSplitName, mActivityToken, mUser, mFlags, mClassLoader, null);
+ mSplitName, mToken, mUser, mFlags, mClassLoader, null);
final int displayId = getDisplayId();
- context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
+ context.setResources(createResources(mToken, mPackageInfo, mSplitName, displayId,
overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo()));
return context;
}
@@ -2340,19 +2354,36 @@
}
ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId,
- mSplitName, mActivityToken, mUser, mFlags, mClassLoader, null);
+ mSplitName, mToken, mUser, mFlags, mClassLoader, null);
final int displayId = display.getDisplayId();
- context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
+ context.setResources(createResources(mToken, mPackageInfo, mSplitName, displayId,
null, getDisplayAdjustments(displayId).getCompatibilityInfo()));
context.mDisplay = display;
return context;
}
@Override
+ public @NonNull WindowContext createWindowContext(int type) {
+ if (getDisplay() == null) {
+ throw new UnsupportedOperationException("WindowContext can only be created from "
+ + "other visual contexts, such as Activity or one created with "
+ + "Context#createDisplayContext(Display)");
+ }
+ return new WindowContext(this, null /* token */, type);
+ }
+
+ ContextImpl createBaseWindowContext(IBinder token) {
+ ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId,
+ mSplitName, token, mUser, mFlags, mClassLoader, null);
+ context.mIsUiContext = true;
+ return context;
+ }
+
+ @Override
public @NonNull Context createFeatureContext(@Nullable String featureId) {
return new ContextImpl(this, mMainThread, mPackageInfo, featureId, mSplitName,
- mActivityToken, mUser, mFlags, mClassLoader, null);
+ mToken, mUser, mFlags, mClassLoader, null);
}
@Override
@@ -2360,7 +2391,7 @@
final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE)
| Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
return new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, mSplitName,
- mActivityToken, mUser, flags, mClassLoader, null);
+ mToken, mUser, flags, mClassLoader, null);
}
@Override
@@ -2368,7 +2399,7 @@
final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE)
| Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
return new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, mSplitName,
- mActivityToken, mUser, flags, mClassLoader, null);
+ mToken, mUser, flags, mClassLoader, null);
}
@Override
@@ -2394,8 +2425,6 @@
return (mFlags & Context.CONTEXT_IGNORE_SECURITY) != 0;
}
- @UnsupportedAppUsage
- @TestApi
@Override
public Display getDisplay() {
if (mDisplay == null) {
@@ -2408,7 +2437,8 @@
@Override
public int getDisplayId() {
- return mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
+ final Display display = getDisplay();
+ return display != null ? display.getDisplayId() : Display.DEFAULT_DISPLAY;
}
@Override
@@ -2518,6 +2548,7 @@
context.setResources(packageInfo.getResources());
context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
context.mResourcesManager.getDisplayMetrics());
+ context.mIsSystemOrSystemUiContext = true;
return context;
}
@@ -2535,6 +2566,7 @@
context.setResources(createResources(null, packageInfo, null, displayId, null,
packageInfo.getCompatibilityInfo()));
context.updateDisplay(displayId);
+ context.mIsSystemOrSystemUiContext = true;
return context;
}
@@ -2584,6 +2616,7 @@
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null,
activityInfo.splitName, activityToken, null, 0, classLoader, null);
+ context.mIsUiContext = true;
// Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
@@ -2629,7 +2662,7 @@
}
mMainThread = mainThread;
- mActivityToken = activityToken;
+ mToken = activityToken;
mFlags = flags;
if (user == null) {
@@ -2649,6 +2682,7 @@
opPackageName = container.mOpPackageName;
setResources(container.mResources);
mDisplay = container.mDisplay;
+ mIsSystemOrSystemUiContext = container.mIsSystemOrSystemUiContext;
} else {
mBasePackageName = packageInfo.mPackageName;
ApplicationInfo ainfo = packageInfo.getApplicationInfo();
@@ -2710,7 +2744,7 @@
@Override
@UnsupportedAppUsage
public IBinder getActivityToken() {
- return mActivityToken;
+ return mToken;
}
private void checkMode(int mode) {
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 4cb8d93..34684c4 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -175,4 +175,11 @@
* Called from SystemUI when it shows the AoD UI.
*/
oneway void setInAmbientMode(boolean inAmbientMode, long animationDuration);
+
+ /**
+ * Called when the wallpaper needs to zoom out.
+ * The zoom value goes from 0 to 1 (inclusive) where 1 means fully zoomed out,
+ * 0 means fully zoomed in
+ */
+ oneway void setWallpaperZoomOut(float zoom, String callingPackage, int displayId);
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 0c5e67c..f0d0e98 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1180,13 +1180,26 @@
}
try {
- java.lang.ClassLoader cl = getClassLoader();
+ final java.lang.ClassLoader cl = getClassLoader();
if (!mPackageName.equals("android")) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"initializeJavaContextClassLoader");
initializeJavaContextClassLoader();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
+
+ // Rewrite the R 'constants' for all library apks.
+ SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers(
+ false, false);
+ for (int i = 0, n = packageIdentifiers.size(); i < n; i++) {
+ final int id = packageIdentifiers.keyAt(i);
+ if (id == 0x01 || id == 0x7f) {
+ continue;
+ }
+
+ rewriteRValues(cl, packageIdentifiers.valueAt(i), id);
+ }
+
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
@@ -1215,19 +1228,6 @@
}
}
- // Rewrite the R 'constants' for all library apks.
- SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers(
- false, false);
- final int N = packageIdentifiers.size();
- for (int i = 0; i < N; i++) {
- final int id = packageIdentifiers.keyAt(i);
- if (id == 0x01 || id == 0x7f) {
- continue;
- }
-
- rewriteRValues(getClassLoader(), packageIdentifiers.valueAt(i), id);
- }
-
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return app;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 3c4e861..1af275f 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1004,6 +1004,31 @@
*/
public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
+
+ /**
+ * {@link #extras} key: this is a remote input history which can include media messages
+ * in addition to text, as supplied to
+ * {@link Builder#setRemoteInputHistory(RemoteInputHistoryItem[])} or
+ * {@link Builder#setRemoteInputHistory(CharSequence[])}.
+ *
+ * SystemUI can populate this through
+ * {@link Builder#setRemoteInputHistory(RemoteInputHistoryItem[])} with the most recent inputs
+ * that have been sent through a {@link RemoteInput} of this Notification. These items can
+ * represent either media content (specified by a URI and a MIME type) or a text message
+ * (described by a CharSequence).
+ *
+ * To maintain compatibility, this can also be set by apps with
+ * {@link Builder#setRemoteInputHistory(CharSequence[])}, which will create a
+ * {@link RemoteInputHistoryItem} for each of the provided text-only messages.
+ *
+ * The extra with this key is of type {@link RemoteInputHistoryItem[]} and contains the most
+ * recent entry at the 0 index, the second most recent at the 1 index, etc.
+ *
+ * @see Builder#setRemoteInputHistory(RemoteInputHistoryItem[])
+ * @hide
+ */
+ public static final String EXTRA_REMOTE_INPUT_HISTORY_ITEMS = "android.remoteInputHistoryItems";
+
/**
* {@link #extras} key: boolean as supplied to
* {@link Builder#setShowRemoteInputSpinner(boolean)}.
@@ -3833,12 +3858,37 @@
if (text == null) {
mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null);
} else {
- final int N = Math.min(MAX_REPLY_HISTORY, text.length);
- CharSequence[] safe = new CharSequence[N];
- for (int i = 0; i < N; i++) {
+ final int itemCount = Math.min(MAX_REPLY_HISTORY, text.length);
+ CharSequence[] safe = new CharSequence[itemCount];
+ RemoteInputHistoryItem[] items = new RemoteInputHistoryItem[itemCount];
+ for (int i = 0; i < itemCount; i++) {
safe[i] = safeCharSequence(text[i]);
+ items[i] = new RemoteInputHistoryItem(text[i]);
}
mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe);
+
+ // Also add these messages as structured history items.
+ mN.extras.putParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS, items);
+ }
+ return this;
+ }
+
+ /**
+ * Set the remote input history, with support for embedding URIs and mime types for
+ * images and other media.
+ * @hide
+ */
+ @NonNull
+ public Builder setRemoteInputHistory(RemoteInputHistoryItem[] items) {
+ if (items == null) {
+ mN.extras.putParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS, null);
+ } else {
+ final int itemCount = Math.min(MAX_REPLY_HISTORY, items.length);
+ RemoteInputHistoryItem[] history = new RemoteInputHistoryItem[itemCount];
+ for (int i = 0; i < itemCount; i++) {
+ history[i] = items[i];
+ }
+ mN.extras.putParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS, history);
}
return this;
}
@@ -5246,16 +5296,17 @@
big.setViewVisibility(R.id.actions_container, View.GONE);
}
- CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
- if (validRemoteInput && replyText != null
- && replyText.length > 0 && !TextUtils.isEmpty(replyText[0])
+ RemoteInputHistoryItem[] replyText = (RemoteInputHistoryItem[])
+ mN.extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+ if (validRemoteInput && replyText != null && replyText.length > 0
+ && !TextUtils.isEmpty(replyText[0].getText())
&& p.maxRemoteInputHistory > 0) {
boolean showSpinner = mN.extras.getBoolean(EXTRA_SHOW_REMOTE_INPUT_SPINNER);
big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE);
big.setViewVisibility(R.id.notification_material_reply_text_1_container,
View.VISIBLE);
big.setTextViewText(R.id.notification_material_reply_text_1,
- processTextSpans(replyText[0]));
+ processTextSpans(replyText[0].getText()));
setTextViewColorSecondary(big, R.id.notification_material_reply_text_1, p);
big.setViewVisibility(R.id.notification_material_reply_progress,
showSpinner ? View.VISIBLE : View.GONE);
@@ -5264,19 +5315,19 @@
ColorStateList.valueOf(
isColorized(p) ? getPrimaryTextColor(p) : resolveContrastColor(p)));
- if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])
+ if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1].getText())
&& p.maxRemoteInputHistory > 1) {
big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
big.setTextViewText(R.id.notification_material_reply_text_2,
- processTextSpans(replyText[1]));
+ processTextSpans(replyText[1].getText()));
setTextViewColorSecondary(big, R.id.notification_material_reply_text_2, p);
- if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])
+ if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2].getText())
&& p.maxRemoteInputHistory > 2) {
big.setViewVisibility(
R.id.notification_material_reply_text_3, View.VISIBLE);
big.setTextViewText(R.id.notification_material_reply_text_3,
- processTextSpans(replyText[2]));
+ processTextSpans(replyText[2].getText()));
setTextViewColorSecondary(big, R.id.notification_material_reply_text_3, p);
}
}
@@ -7517,7 +7568,7 @@
@Nullable
private final Person mSender;
/** True if this message was generated from the extra
- * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY}
+ * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY_ITEMS}
*/
private final boolean mRemoteInputHistory;
@@ -7569,7 +7620,7 @@
* Should be <code>null</code> for messages by the current user, in which case
* the platform will insert the user set in {@code MessagingStyle(Person)}.
* @param remoteInputHistory True if the messages was generated from the extra
- * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY}.
+ * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY_ITEMS}.
* <p>
* The person provided should contain an Icon, set with
* {@link Person.Builder#setIcon(Icon)} and also have a name provided
@@ -7676,7 +7727,7 @@
/**
* @return True if the message was generated from
- * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY}.
+ * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY_ITEMS}.
* @hide
*/
public boolean isRemoteInputHistory() {
@@ -7906,8 +7957,8 @@
if (mBuilder.mActions.size() > 0) {
maxRows--;
}
- CharSequence[] remoteInputHistory = mBuilder.mN.extras.getCharSequenceArray(
- EXTRA_REMOTE_INPUT_HISTORY);
+ RemoteInputHistoryItem[] remoteInputHistory = (RemoteInputHistoryItem[])
+ mBuilder.mN.extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
if (remoteInputHistory != null
&& remoteInputHistory.length > NUMBER_OF_HISTORY_ALLOWED_UNTIL_REDUCTION) {
// Let's remove some messages to make room for the remote input history.
diff --git a/core/java/android/app/RemoteInputHistoryItem.java b/core/java/android/app/RemoteInputHistoryItem.java
new file mode 100644
index 0000000..091db3f
--- /dev/null
+++ b/core/java/android/app/RemoteInputHistoryItem.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Stores historical input from a RemoteInput attached to a Notification.
+ *
+ * History items represent either a text message (specified by providing a CharSequence,
+ * or a media message (specified by providing a URI and a MIME type). Media messages must also
+ * include text to insert when the image cannot be loaded, ex. when URI read permission has not been
+ * granted correctly.
+ *
+ * @hide
+ */
+public class RemoteInputHistoryItem implements Parcelable {
+ private CharSequence mText;
+ private String mMimeType;
+ private Uri mUri;
+
+ public RemoteInputHistoryItem(String mimeType, Uri uri, CharSequence backupText) {
+ this.mMimeType = mimeType;
+ this.mUri = uri;
+ this.mText = Notification.safeCharSequence(backupText);
+ }
+
+ public RemoteInputHistoryItem(CharSequence text) {
+ this.mText = Notification.safeCharSequence(text);
+ }
+
+ protected RemoteInputHistoryItem(Parcel in) {
+ mText = in.readCharSequence();
+ mMimeType = in.readStringNoHelper();
+ mUri = in.readParcelable(Uri.class.getClassLoader());
+ }
+
+ public static final Creator<RemoteInputHistoryItem> CREATOR =
+ new Creator<RemoteInputHistoryItem>() {
+ @Override
+ public RemoteInputHistoryItem createFromParcel(Parcel in) {
+ return new RemoteInputHistoryItem(in);
+ }
+
+ @Override
+ public RemoteInputHistoryItem[] newArray(int size) {
+ return new RemoteInputHistoryItem[size];
+ }
+ };
+
+ public CharSequence getText() {
+ return mText;
+ }
+
+ public String getMimeType() {
+ return mMimeType;
+ }
+
+ public Uri getUri() {
+ return mUri;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeCharSequence(mText);
+ dest.writeStringNoHelper(mMimeType);
+ dest.writeParcelable(mUri, flags);
+ }
+}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index e0c39c2..dcd179f 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -89,6 +89,7 @@
import android.hardware.input.InputManager;
import android.hardware.iris.IIrisService;
import android.hardware.iris.IrisManager;
+import android.hardware.lights.LightsManager;
import android.hardware.location.ContextHubManager;
import android.hardware.radio.RadioManager;
import android.hardware.usb.IUsbManager;
@@ -106,6 +107,7 @@
import android.media.soundtrigger.SoundTriggerManager;
import android.media.tv.ITvInputManager;
import android.media.tv.TvInputManager;
+import android.net.ConnectivityDiagnosticsManager;
import android.net.ConnectivityManager;
import android.net.ConnectivityThread;
import android.net.EthernetManager;
@@ -377,6 +379,18 @@
return new IpSecManager(ctx, service);
}});
+ registerService(Context.CONNECTIVITY_DIAGNOSTICS_SERVICE,
+ ConnectivityDiagnosticsManager.class,
+ new CachedServiceFetcher<ConnectivityDiagnosticsManager>() {
+ @Override
+ public ConnectivityDiagnosticsManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ // ConnectivityDiagnosticsManager is backed by ConnectivityService
+ IBinder b = ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE);
+ IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
+ return new ConnectivityDiagnosticsManager(ctx, service);
+ }});
+
registerService(
Context.TEST_NETWORK_SERVICE,
TestNetworkManager.class,
@@ -1256,6 +1270,13 @@
Context.DATA_LOADER_MANAGER_SERVICE);
return new DataLoaderManager(IDataLoaderManager.Stub.asInterface(b));
}});
+ registerService(Context.LIGHTS_SERVICE, LightsManager.class,
+ new CachedServiceFetcher<LightsManager>() {
+ @Override
+ public LightsManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ return new LightsManager(ctx);
+ }});
//TODO(b/136132412): refactor this: 1) merge IIncrementalManager.aidl and
//IIncrementalManagerNative.aidl, 2) implement the binder interface in
//IncrementalManagerService.java, 3) use JNI to call native functions
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index a885009..6f1effd 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1848,6 +1848,24 @@
}
/**
+ * Set the current zoom out level of the wallpaper
+ * @param zoom from 0 to 1 (inclusive) where 1 means fully zoomed out, 0 means fully zoomed in
+ *
+ * @hide
+ */
+ public void setWallpaperZoomOut(float zoom) {
+ if (zoom < 0 || zoom > 1f) {
+ throw new IllegalArgumentException("zoom must be between 0 and one: " + zoom);
+ }
+ try {
+ sGlobals.mService.setWallpaperZoomOut(zoom, mContext.getOpPackageName(),
+ mContext.getDisplayId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns whether wallpapers are supported for the calling user. If this function returns
* {@code false}, any attempts to changing the wallpaper will have no effect,
* and any attempt to obtain of the wallpaper will return {@code null}.
diff --git a/core/java/android/app/WindowContext.java b/core/java/android/app/WindowContext.java
new file mode 100644
index 0000000..22cc14b
--- /dev/null
+++ b/core/java/android/app/WindowContext.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+import android.view.WindowManagerImpl;
+
+/**
+ * {@link WindowContext} is a context for non-activity windows such as
+ * {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY} windows or system
+ * windows. Its resources and configuration are adjusted to the area of the display that will be
+ * used when a new window is added via {@link android.view.WindowManager.addView}.
+ *
+ * @see Context#createWindowContext(int)
+ * @hide
+ */
+// TODO(b/128338354): Handle config/display changes from server side.
+public class WindowContext extends ContextWrapper {
+ private final WindowManagerImpl mWindowManager;
+ private final IWindowManager mWms;
+ private final IBinder mToken;
+ private final int mDisplayId;
+ private boolean mOwnsToken;
+
+ /**
+ * Default constructor. Can either accept an existing token or generate one and registers it
+ * with the server if necessary.
+ *
+ * @param base Base {@link Context} for this new instance.
+ * @param token A valid {@link com.android.server.wm.WindowToken}. Pass {@code null} to generate
+ * one.
+ * @param type Window type to be used with this context.
+ * @hide
+ */
+ public WindowContext(Context base, IBinder token, int type) {
+ super(null /* base */);
+
+ mWms = WindowManagerGlobal.getWindowManagerService();
+ if (token != null && !isWindowToken(token)) {
+ throw new IllegalArgumentException("Token must be registered to server.");
+ }
+
+ final ContextImpl contextImpl = createBaseWindowContext(base, token);
+ attachBaseContext(contextImpl);
+ contextImpl.setOuterContext(this);
+
+ mToken = token != null ? token : new Binder();
+ mDisplayId = getDisplayId();
+ mWindowManager = new WindowManagerImpl(this);
+ mWindowManager.setDefaultToken(mToken);
+
+ // TODO(b/128338354): Obtain the correct config from WM and adjust resources.
+ if (token != null) {
+ mOwnsToken = false;
+ return;
+ }
+ try {
+ mWms.addWindowContextToken(mToken, type, mDisplayId, getPackageName());
+ // TODO(window-context): remove token with a DeathObserver
+ } catch (RemoteException e) {
+ mOwnsToken = false;
+ throw e.rethrowFromSystemServer();
+ }
+ mOwnsToken = true;
+ }
+
+ /** Check if the passed window token is registered with the server. */
+ private boolean isWindowToken(@NonNull IBinder token) {
+ try {
+ return mWms.isWindowToken(token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return false;
+ }
+
+ private static ContextImpl createBaseWindowContext(Context outer, IBinder token) {
+ final ContextImpl contextImpl = ContextImpl.getImpl(outer);
+ return contextImpl.createBaseWindowContext(token);
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ if (WINDOW_SERVICE.equals(name)) {
+ return mWindowManager;
+ }
+ return super.getSystemService(name);
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ if (mOwnsToken) {
+ try {
+ mWms.removeWindowToken(mToken, mDisplayId);
+ mOwnsToken = false;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ super.finalize();
+ }
+}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 1bf6c99..fa9dd27 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2391,6 +2391,28 @@
"android.app.action.BIND_SECONDARY_LOCKSCREEN_SERVICE";
/**
+ * Return value for {@link #getPersonalAppsSuspendedReasons} when personal apps are not
+ * suspended.
+ */
+ public static final int PERSONAL_APPS_NOT_SUSPENDED = 0;
+
+ /**
+ * Flag for {@link #getPersonalAppsSuspendedReasons} return value. Set when personal
+ * apps are suspended by an admin explicitly via {@link #setPersonalAppsSuspended}.
+ */
+ public static final int PERSONAL_APPS_SUSPENDED_EXPLICITLY = 1 << 0;
+
+ /**
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "PERSONAL_APPS_" }, value = {
+ PERSONAL_APPS_NOT_SUSPENDED,
+ PERSONAL_APPS_SUSPENDED_EXPLICITLY
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PersonalAppSuspensionReason {}
+
+ /**
* Return true if the given administrator component is currently active (enabled) in the system.
*
* @param admin The administrator component to check for.
@@ -4577,6 +4599,18 @@
= "android.app.action.START_ENCRYPTION";
/**
+ * Activity action: launch the DPC to check policy compliance. This intent is launched when
+ * the user taps on the notification about personal apps suspension. When handling this intent
+ * the DPC must check if personal apps should still be suspended and either unsuspend them or
+ * instruct the user on how to resolve the noncompliance causing the suspension.
+ *
+ * @see #setPersonalAppsSuspended
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_CHECK_POLICY_COMPLIANCE =
+ "android.app.action.CHECK_POLICY_COMPLIANCE";
+
+ /**
* Broadcast action: notify managed provisioning that new managed user is created.
*
* @hide
@@ -8226,6 +8260,11 @@
* actual package file remain. This function can be called by a device owner, profile owner, or
* by a delegate given the {@link #DELEGATION_PACKAGE_ACCESS} scope via
* {@link #setDelegatedScopes}.
+ * <p>
+ * This method can be called on the {@link DevicePolicyManager} instance, returned by
+ * {@link #getParentProfileInstance(ComponentName)}, where the caller must be the profile owner
+ * of an organization-owned managed profile and the package must be a system package. If called
+ * on the parent instance, then the package is hidden or unhidden in the personal profile.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
* {@code null} if the caller is a package access delegate.
@@ -8233,17 +8272,20 @@
* @param hidden {@code true} if the package should be hidden, {@code false} if it should be
* unhidden.
* @return boolean Whether the hidden setting of the package was successfully updated.
- * @throws SecurityException if {@code admin} is not a device or profile owner.
+ * @throws SecurityException if {@code admin} is not a device or profile owner or if called on
+ * the parent profile and the {@code admin} is not a profile owner of an
+ * organization-owned managed profile.
+ * @throws IllegalArgumentException if called on the parent profile and the package provided
+ * is not a system package.
* @see #setDelegatedScopes
* @see #DELEGATION_PACKAGE_ACCESS
*/
public boolean setApplicationHidden(@NonNull ComponentName admin, String packageName,
boolean hidden) {
- throwIfParentInstance("setApplicationHidden");
if (mService != null) {
try {
return mService.setApplicationHidden(admin, mContext.getPackageName(), packageName,
- hidden);
+ hidden, mParentInstance);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -8255,20 +8297,30 @@
* Determine if a package is hidden. This function can be called by a device owner, profile
* owner, or by a delegate given the {@link #DELEGATION_PACKAGE_ACCESS} scope via
* {@link #setDelegatedScopes}.
+ * <p>
+ * This method can be called on the {@link DevicePolicyManager} instance, returned by
+ * {@link #getParentProfileInstance(ComponentName)}, where the caller must be the profile owner
+ * of an organization-owned managed profile and the package must be a system package. If called
+ * on the parent instance, this will determine whether the package is hidden or unhidden in the
+ * personal profile.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
* {@code null} if the caller is a package access delegate.
* @param packageName The name of the package to retrieve the hidden status of.
* @return boolean {@code true} if the package is hidden, {@code false} otherwise.
- * @throws SecurityException if {@code admin} is not a device or profile owner.
+ * @throws SecurityException if {@code admin} is not a device or profile owner or if called on
+ * the parent profile and the {@code admin} is not a profile owner of an
+ * organization-owned managed profile.
+ * @throws IllegalArgumentException if called on the parent profile and the package provided
+ * is not a system package.
* @see #setDelegatedScopes
* @see #DELEGATION_PACKAGE_ACCESS
*/
public boolean isApplicationHidden(@NonNull ComponentName admin, String packageName) {
- throwIfParentInstance("isApplicationHidden");
if (mService != null) {
try {
- return mService.isApplicationHidden(admin, mContext.getPackageName(), packageName);
+ return mService.isApplicationHidden(admin, mContext.getPackageName(), packageName,
+ mParentInstance);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -9057,7 +9109,8 @@
}
/**
- * Called by device owners to set a local system update policy. When a new policy is set,
+ * Called by device owners or profile owners of an organization-owned managed profile to to set
+ * a local system update policy. When a new policy is set,
* {@link #ACTION_SYSTEM_UPDATE_POLICY_CHANGED} is broadcasted.
* <p>
* If the supplied system update policy has freeze periods set but the freeze periods do not
@@ -9075,7 +9128,8 @@
* components in the device owner package can set system update policies and the most
* recent policy takes effect.
* @param policy the new policy, or {@code null} to clear the current policy.
- * @throws SecurityException if {@code admin} is not a device owner.
+ * @throws SecurityException if {@code admin} is not a device owner or a profile owner of an
+ * organization-owned managed profile.
* @throws IllegalArgumentException if the policy type or maintenance window is not valid.
* @throws SystemUpdatePolicy.ValidationFailedException if the policy's freeze period does not
* meet the requirement.
@@ -9334,6 +9388,16 @@
* {@link android.os.Build.VERSION_CODES#M} the app-op matching the permission is set to
* {@link android.app.AppOpsManager#MODE_IGNORED}, but the permission stays granted.
*
+ * NOTE: Starting from Android R, location-related permissions cannot be granted by the
+ * admin: Calling this method with {@link #PERMISSION_GRANT_STATE_GRANTED} for any of the
+ * following permissions will return false:
+ *
+ * <ul>
+ * <li>{@code ACCESS_FINE_LOCATION}</li>
+ * <li>{@code ACCESS_BACKGROUND_LOCATION}</li>
+ * <li>{@code ACCESS_COARSE_LOCATION}</li>
+ * </ul>
+ *
* @param admin Which profile or device owner this request is associated with.
* @param packageName The application to grant or revoke a permission to.
* @param permission The permission to grant or revoke.
@@ -11150,7 +11214,8 @@
}
/**
- * Called by device owner to install a system update from the given file. The device will be
+ * Called by device owner or profile owner of an organization-owned managed profile to install
+ * a system update from the given file. The device will be
* rebooted in order to finish installing the update. Note that if the device is rebooted, this
* doesn't necessarily mean that the update has been applied successfully. The caller should
* additionally check the system version with {@link android.os.Build#FINGERPRINT} or {@link
@@ -11665,4 +11730,48 @@
}
return false;
}
+
+ /**
+ * Called by profile owner of an organization-owned managed profile to check whether
+ * personal apps are suspended.
+ *
+ * @return a bitmask of reasons for personal apps suspension or
+ * {@link #PERSONAL_APPS_NOT_SUSPENDED} if apps are not suspended.
+ * @see #setPersonalAppsSuspended
+ */
+ public @PersonalAppSuspensionReason int getPersonalAppsSuspendedReasons(
+ @NonNull ComponentName admin) {
+ throwIfParentInstance("getPersonalAppsSuspendedReasons");
+ if (mService != null) {
+ try {
+ return mService.getPersonalAppsSuspendedReasons(admin);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Called by a profile owner of an organization-owned managed profile to suspend personal
+ * apps on the device. When personal apps are suspended the device can only be used for calls.
+ *
+ * <p>When personal apps are suspended, an ongoing notification about that is shown to the user.
+ * When the user taps the notification, system invokes {@link #ACTION_CHECK_POLICY_COMPLIANCE}
+ * in the profile owner package. Profile owner implementation that uses personal apps suspension
+ * must handle this intent.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with
+ * @param suspended Whether personal apps should be suspended.
+ */
+ public void setPersonalAppsSuspended(@NonNull ComponentName admin, boolean suspended) {
+ throwIfParentInstance("setPersonalAppsSuspended");
+ if (mService != null) {
+ try {
+ mService.setPersonalAppsSuspended(admin, suspended);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index e7667c0..3d6bf9d 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -233,8 +233,8 @@
boolean isNotificationListenerServicePermitted(in String packageName, int userId);
Intent createAdminSupportIntent(in String restriction);
- boolean setApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName, boolean hidden);
- boolean isApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName);
+ boolean setApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName, boolean hidden, boolean parent);
+ boolean isApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName, boolean parent);
UserHandle createAndManageUser(in ComponentName who, in String name, in ComponentName profileOwner, in PersistableBundle adminExtras, in int flags);
boolean removeUser(in ComponentName who, in UserHandle userHandle);
@@ -470,4 +470,7 @@
void setCommonCriteriaModeEnabled(in ComponentName admin, boolean enabled);
boolean isCommonCriteriaModeEnabled(in ComponentName admin);
+
+ int getPersonalAppsSuspendedReasons(in ComponentName admin);
+ void setPersonalAppsSuspended(in ComponentName admin, boolean suspended);
}
diff --git a/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java b/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java
index 1e6ab41..bea1bd6 100644
--- a/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java
+++ b/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java
@@ -21,6 +21,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.UserIdInt;
+import android.graphics.Bitmap;
import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
@@ -77,6 +78,28 @@
}
/**
+ * Hints to the system that a new context image using the provided bitmap should be sent to
+ * the system content suggestions service.
+ *
+ * @param bitmap the new context image
+ * @param imageContextRequestExtras sent with request to provide implementation specific
+ * extra information.
+ */
+ public void provideContextImage(
+ @NonNull Bitmap bitmap, @NonNull Bundle imageContextRequestExtras) {
+ if (mService == null) {
+ Log.e(TAG, "provideContextImage called, but no ContentSuggestionsManager configured");
+ return;
+ }
+
+ try {
+ mService.provideContextBitmap(mUser, bitmap, imageContextRequestExtras);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Hints to the system that a new context image for the provided task should be sent to the
* system content suggestions service.
*
diff --git a/core/java/android/app/contentsuggestions/IContentSuggestionsManager.aidl b/core/java/android/app/contentsuggestions/IContentSuggestionsManager.aidl
index b18a758..8e6338b 100644
--- a/core/java/android/app/contentsuggestions/IContentSuggestionsManager.aidl
+++ b/core/java/android/app/contentsuggestions/IContentSuggestionsManager.aidl
@@ -20,6 +20,7 @@
import android.app.contentsuggestions.ISelectionsCallback;
import android.app.contentsuggestions.ClassificationsRequest;
import android.app.contentsuggestions.SelectionsRequest;
+import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.UserHandle;
import com.android.internal.os.IResultReceiver;
@@ -30,6 +31,10 @@
int userId,
int taskId,
in Bundle imageContextRequestExtras);
+ void provideContextBitmap(
+ int userId,
+ in Bitmap bitmap,
+ in Bundle imageContextRequestExtras);
void suggestContentSelections(
int userId,
in SelectionsRequest request,
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index a60e591..5668944 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -142,7 +142,7 @@
/**
* The app has not be used for several days and/or is unlikely to be used for several days.
- * Apps in this bucket will have the most restrictions, including network restrictions, except
+ * Apps in this bucket will have more restrictions, including network restrictions, except
* during certain short periods (at a minimum, once a day) when they are allowed to execute
* jobs, access the network, etc.
* @see #getAppStandbyBucket()
@@ -150,6 +150,15 @@
public static final int STANDBY_BUCKET_RARE = 40;
/**
+ * The app has not be used for several days, is unlikely to be used for several days, and has
+ * been misbehaving in some manner.
+ * Apps in this bucket will have the most restrictions, including network restrictions and
+ * additional restrictions on jobs.
+ * @see #getAppStandbyBucket()
+ */
+ public static final int STANDBY_BUCKET_RESTRICTED = 45;
+
+ /**
* The app has never been used.
* {@hide}
*/
@@ -278,6 +287,26 @@
* @hide
*/
public static final int REASON_SUB_PREDICTED_RESTORED = 0x0001;
+ /**
+ * The reason for restricting the app is unknown or undefined.
+ * @hide
+ */
+ public static final int REASON_SUB_RESTRICT_UNDEFINED = 0x0000;
+ /**
+ * The app was unnecessarily using system resources (battery, memory, etc) in the background.
+ * @hide
+ */
+ public static final int REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE = 0x0001;
+ /**
+ * The app was deemed to be intentionally abusive.
+ * @hide
+ */
+ public static final int REASON_SUB_RESTRICT_ABUSE = 0x0002;
+ /**
+ * The app was displaying buggy behavior.
+ * @hide
+ */
+ public static final int REASON_SUB_RESTRICT_BUGGY = 0x0003;
/** @hide */
@@ -287,6 +316,7 @@
STANDBY_BUCKET_WORKING_SET,
STANDBY_BUCKET_FREQUENT,
STANDBY_BUCKET_RARE,
+ STANDBY_BUCKET_RESTRICTED,
STANDBY_BUCKET_NEVER,
})
@Retention(RetentionPolicy.SOURCE)
@@ -598,7 +628,7 @@
* state of the app based on app usage patterns. Standby buckets determine how much an app will
* be restricted from running background tasks such as jobs and alarms.
* <p>Restrictions increase progressively from {@link #STANDBY_BUCKET_ACTIVE} to
- * {@link #STANDBY_BUCKET_RARE}, with {@link #STANDBY_BUCKET_ACTIVE} being the least
+ * {@link #STANDBY_BUCKET_RESTRICTED}, with {@link #STANDBY_BUCKET_ACTIVE} being the least
* restrictive. The battery level of the device might also affect the restrictions.
* <p>Apps in buckets ≤ {@link #STANDBY_BUCKET_ACTIVE} have no standby restrictions imposed.
* Apps in buckets > {@link #STANDBY_BUCKET_FREQUENT} may have network access restricted when
@@ -642,7 +672,8 @@
/**
* {@hide}
* Changes an app's standby bucket to the provided value. The caller can only set the standby
- * bucket for a different app than itself.
+ * bucket for a different app than itself. The caller will not be able to change an app's
+ * standby bucket if that app is in the {@link #STANDBY_BUCKET_RESTRICTED} bucket.
* @param packageName the package name of the app to set the bucket for. A SecurityException
* will be thrown if the package name is that of the caller.
* @param bucket the standby bucket to set it to, which should be one of STANDBY_BUCKET_*.
@@ -688,7 +719,8 @@
/**
* {@hide}
* Changes the app standby bucket for multiple apps at once. The Map is keyed by the package
- * name and the value is one of STANDBY_BUCKET_*.
+ * name and the value is one of STANDBY_BUCKET_*. The caller will not be able to change an
+ * app's standby bucket if that app is in the {@link #STANDBY_BUCKET_RESTRICTED} bucket.
* @param appBuckets a map of package name to bucket value.
*/
@SystemApi
@@ -1027,6 +1059,20 @@
break;
case REASON_MAIN_FORCED_BY_SYSTEM:
sb.append("s");
+ switch (standbyReason & REASON_SUB_MASK) {
+ case REASON_SUB_RESTRICT_ABUSE:
+ sb.append("-ra");
+ break;
+ case REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE:
+ sb.append("-rbru");
+ break;
+ case REASON_SUB_RESTRICT_BUGGY:
+ sb.append("-rb");
+ break;
+ case REASON_SUB_RESTRICT_UNDEFINED:
+ sb.append("-r");
+ break;
+ }
break;
case REASON_MAIN_FORCED_BY_USER:
sb.append("f");
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index cb1f055..e751354 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1222,6 +1222,7 @@
if (mService != null) {
return mService.factoryReset();
}
+ Log.e(TAG, "factoryReset(): IBluetooth Service is null");
SystemProperties.set("persist.bluetooth.factoryreset", "true");
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1239,7 +1240,7 @@
*/
@UnsupportedAppUsage
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public @NonNull ParcelUuid[] getUuids() {
+ public @Nullable ParcelUuid[] getUuids() {
if (getState() != STATE_ON) {
return null;
}
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index 024bb06..ec63fd0 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -30,6 +30,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.CloseGuard;
import android.util.Log;
import java.lang.annotation.Retention;
@@ -50,10 +51,11 @@
* @hide
*/
@SystemApi
-public final class BluetoothPan implements BluetoothProfile {
+public final class BluetoothPan implements BluetoothProfile, AutoCloseable {
private static final String TAG = "BluetoothPan";
private static final boolean DBG = true;
private static final boolean VDBG = false;
+ private CloseGuard mCloseGuard;
/**
* Intent used to broadcast the change in connection state of the Pan
@@ -166,10 +168,15 @@
mAdapter = BluetoothAdapter.getDefaultAdapter();
mContext = context;
mProfileConnector.connect(context, listener);
+ mCloseGuard = new CloseGuard();
+ mCloseGuard.open("close");
}
- @UnsupportedAppUsage
- /*package*/ void close() {
+ /**
+ * Closes the connection to the service and unregisters callbacks
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public void close() {
if (VDBG) log("close()");
mProfileConnector.disconnect();
}
@@ -178,8 +185,11 @@
return mProfileConnector.getService();
}
-
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
protected void finalize() {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
close();
}
@@ -316,6 +326,7 @@
* @hide
*/
@Override
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothPan service = getService();
@@ -335,6 +346,7 @@
* {@inheritDoc}
*/
@Override
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
public int getConnectionState(@Nullable BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
final IBluetoothPan service = getService();
@@ -355,6 +367,7 @@
*
* @param value is whether to enable or disable bluetooth tethering
*/
+ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
public void setBluetoothTethering(boolean value) {
String pkgName = mContext.getOpPackageName();
if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName);
@@ -373,6 +386,7 @@
*
* @return true if tethering is on, false if not or some error occurred
*/
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
public boolean isTetheringOn() {
if (VDBG) log("isTetheringOn()");
final IBluetoothPan service = getService();
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 3107c63..d4b5b1a 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -269,7 +269,7 @@
@SystemApi
@TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
- public boolean isDeviceAssociated(
+ public boolean isDeviceAssociatedForWifiConnection(
@NonNull String packageName,
@NonNull MacAddress macAddress,
@NonNull UserHandle user) {
@@ -280,7 +280,7 @@
Objects.requireNonNull(macAddress, "mac address cannot be null");
Objects.requireNonNull(user, "user cannot be null");
try {
- return mService.isDeviceAssociated(
+ return mService.isDeviceAssociatedForWifiConnection(
packageName, macAddress.toString(), user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index 2e1ff0b..b323f58 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -40,5 +40,6 @@
boolean hasNotificationAccess(in ComponentName component);
PendingIntent requestNotificationAccess(in ComponentName component);
- boolean isDeviceAssociated(in String packageName, in String macAddress, int userId);
+ boolean isDeviceAssociatedForWifiConnection(in String packageName, in String macAddress,
+ int userId);
}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 85826fd..c271e3c 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -28,6 +28,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.AppOpsManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.PackageManager;
@@ -2526,6 +2527,16 @@
}
/**
+ * Returns the user associated with the given URI.
+ *
+ * @hide
+ */
+ @TestApi
+ public @NonNull static UserHandle getUserHandleFromUri(@NonNull Uri uri) {
+ return UserHandle.of(getUserIdFromUri(uri, Process.myUserHandle().getIdentifier()));
+ }
+
+ /**
* Removes userId part from authority string. Expects format:
* userId@some.authority
* If there is no userId in the authority, it symply returns the argument
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2943e39..ebc5e0e 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3480,6 +3480,7 @@
//@hide: TIME_DETECTOR_SERVICE,
//@hide: TIME_ZONE_DETECTOR_SERVICE,
PERMISSION_SERVICE,
+ LIGHTS_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -3984,6 +3985,16 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a {@link
+ * android.net.ConnectivityDiagnosticsManager} for performing network connectivity diagnostics
+ * as well as receiving network connectivity information from the system.
+ *
+ * @see #getSystemService(String)
+ * @see android.net.ConnectivityDiagnosticsManager
+ */
+ public static final String CONNECTIVITY_DIAGNOSTICS_SERVICE = "connectivity_diagnostics";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.TestNetworkManager} for building TUNs and limited-use Networks
*
* @see #getSystemService(String)
@@ -5111,6 +5122,15 @@
public static final String FILE_INTEGRITY_SERVICE = "file_integrity";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.hardware.lights.LightsManager} for controlling device lights.
+ *
+ * @see #getSystemService(String)
+ * @hide
+ */
+ public static final String LIGHTS_SERVICE = "lights";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
@@ -5720,6 +5740,63 @@
public abstract Context createDisplayContext(@NonNull Display display);
/**
+ * Creates a Context for a non-activity window.
+ *
+ * <p>
+ * A window context is a context that can be used to add non-activity windows, such as
+ * {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY}. A window context
+ * must be created from a context that has an associated {@link Display}, such as
+ * {@link android.app.Activity Activity} or a context created with
+ * {@link #createDisplayContext(Display)}.
+ *
+ * <p>
+ * The window context is created with the appropriate {@link Configuration} for the area of the
+ * display that the windows created with it can occupy; it must be used when
+ * {@link android.view.LayoutInflater inflating} views, such that they can be inflated with
+ * proper {@link Resources}.
+ *
+ * Below is a sample code to <b>add an application overlay window on the primary display:<b/>
+ * <pre class="prettyprint">
+ * ...
+ * final DisplayManager dm = anyContext.getSystemService(DisplayManager.class);
+ * final Display primaryDisplay = dm.getDisplay(DEFAULT_DISPLAY);
+ * final Context windowContext = anyContext.createDisplayContext(primaryDisplay)
+ * .createWindowContext(TYPE_APPLICATION_OVERLAY);
+ * final View overlayView = Inflater.from(windowContext).inflate(someLayoutXml, null);
+ *
+ * // WindowManager.LayoutParams initialization
+ * ...
+ * mParams.type = TYPE_APPLICATION_OVERLAY;
+ * ...
+ *
+ * mWindowContext.getSystemService(WindowManager.class).addView(overlayView, mParams);
+ * </pre>
+ *
+ * <p>
+ * This context's configuration and resources are adjusted to a display area where the windows
+ * with provided type will be added. <b>Note that all windows associated with the same context
+ * will have an affinity and can only be moved together between different displays or areas on a
+ * display.</b> If there is a need to add different window types, or non-associated windows,
+ * separate Contexts should be used.
+ * </p>
+ *
+ * @param type Window type in {@link WindowManager.LayoutParams}
+ * @return A {@link Context} that can be used to create windows.
+ * @throws UnsupportedOperationException if this is called on a non-UI context, such as
+ * {@link android.app.Application Application} or {@link android.app.Service Service}.
+ *
+ * @see #getSystemService(String)
+ * @see #getSystemService(Class)
+ * @see #WINDOW_SERVICE
+ * @see #LAYOUT_INFLATER_SERVICE
+ * @see #WALLPAPER_SERVICE
+ * @throws IllegalArgumentException if token is invalid
+ */
+ public @NonNull Context createWindowContext(int type) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+
+ /**
* Return a new Context object for the current Context but for a different feature in the app.
* Features can be used by complex apps to separate logical parts.
*
@@ -5803,17 +5880,22 @@
public abstract DisplayAdjustments getDisplayAdjustments(int displayId);
/**
+ * Get the display this context is associated with. Applications should use this method with
+ * {@link android.app.Activity} or a context associated with a {@link Display} via
+ * {@link #createDisplayContext(Display)} to get a display object associated with a Context, or
+ * {@link android.hardware.display.DisplayManager#getDisplay} to get a display object by id.
* @return Returns the {@link Display} object this context is associated with.
- * @hide
*/
- @UnsupportedAppUsage
- @TestApi
- public abstract Display getDisplay();
+ @Nullable
+ public Display getDisplay() {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
/**
- * Gets the display ID.
+ * Gets the ID of the display this context is associated with.
*
* @return display ID associated with this {@link Context}.
+ * @see #getDisplay()
* @hide
*/
@TestApi
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 6fe1187..b2b7988 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -977,6 +977,12 @@
}
@Override
+ @NonNull
+ public Context createWindowContext(int type) {
+ return mBase.createWindowContext(type);
+ }
+
+ @Override
public @NonNull Context createFeatureContext(@Nullable String featureId) {
return mBase.createFeatureContext(featureId);
}
@@ -992,11 +998,8 @@
return mBase.getDisplayAdjustments(displayId);
}
- /** @hide */
- @UnsupportedAppUsage
- @TestApi
@Override
- public Display getDisplay() {
+ public @Nullable Display getDisplay() {
return mBase.getDisplay();
}
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index e86bb25..fc20263 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.content.pm.DataLoaderParamsParcel;
import android.content.pm.IPackageInstallObserver2;
import android.content.IntentSender;
import android.os.ParcelFileDescriptor;
@@ -39,8 +40,9 @@
void transfer(in String packageName);
void abandon();
- void addFile(String name, long lengthBytes, in byte[] metadata);
- void removeFile(String name);
+ DataLoaderParamsParcel getDataLoaderParams();
+ void addFile(int location, String name, long lengthBytes, in byte[] metadata, in byte[] signature);
+ void removeFile(int location, String name);
boolean isMultiPackage();
int[] getChildSessionIds();
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index b3d8eb5..93126b8 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -230,7 +230,7 @@
* @param versionedPackage The package to delete.
* @param observer a callback to use to notify when the package deletion in finished.
* @param userId the id of the user for whom to delete the package
- * @param flags - possible values: {@link #DONT_DELETE_DATA}
+ * @param flags - possible values: {@link #DELETE_KEEP_DATA}
*/
void deletePackageVersioned(in VersionedPackage versionedPackage,
IPackageDeleteObserver2 observer, int userId, int flags);
diff --git a/core/java/android/content/pm/InstallationFile.java b/core/java/android/content/pm/InstallationFile.java
index ac5fd1e..111ad32 100644
--- a/core/java/android/content/pm/InstallationFile.java
+++ b/core/java/android/content/pm/InstallationFile.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -31,12 +32,14 @@
*
* @hide
*/
+@SystemApi
public final class InstallationFile implements Parcelable {
public static final int FILE_TYPE_UNKNOWN = -1;
public static final int FILE_TYPE_APK = 0;
public static final int FILE_TYPE_LIB = 1;
public static final int FILE_TYPE_OBB = 2;
+ /** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"FILE_TYPE_"}, value = {
FILE_TYPE_APK,
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index f264adb..b1b9454 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -365,6 +365,41 @@
@SystemApi
public static final int DATA_LOADER_TYPE_INCREMENTAL = DataLoaderType.INCREMENTAL;
+ /**
+ * Target location for the file in installation session is /data/app/<packageName>-<id>.
+ * This is the intended location for APKs.
+ * Requires permission to install packages.
+ * {@hide}
+ */
+ @SystemApi
+ public static final int LOCATION_DATA_APP = 0;
+
+ /**
+ * Target location for the file in installation session is
+ * /data/media/<userid>/Android/obb/<packageName>. This is the intended location for OBBs.
+ * {@hide}
+ */
+ @SystemApi
+ public static final int LOCATION_MEDIA_OBB = 1;
+
+ /**
+ * Target location for the file in installation session is
+ * /data/media/<userid>/Android/data/<packageName>.
+ * This is the intended location for application data.
+ * Can only be used by an app itself running under specific user.
+ * {@hide}
+ */
+ @SystemApi
+ public static final int LOCATION_MEDIA_DATA = 2;
+
+ /** @hide */
+ @IntDef(prefix = { "LOCATION_" }, value = {
+ LOCATION_DATA_APP,
+ LOCATION_MEDIA_OBB,
+ LOCATION_MEDIA_DATA})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FileLocation{}
+
private final IPackageInstaller mInstaller;
private final int mUserId;
private final String mInstallerPackageName;
@@ -1071,10 +1106,33 @@
}
}
+ /**
+ * @return data loader params or null if the session is not using one.
+ *
+ * WARNING: This is a system API to aid internal development.
+ * Use at your own risk. It will change or be removed without warning.
+ * {@hide}
+ */
+ @SystemApi
+ public @Nullable DataLoaderParams getDataLoaderParams() {
+ try {
+ DataLoaderParamsParcel data = mSession.getDataLoaderParams();
+ if (data == null) {
+ return null;
+ }
+ return new DataLoaderParams(data);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
/**
* Adds a file to session. On commit this file will be pulled from dataLoader.
*
+ * @param location target location for the file. Possible values:
+ * {@link #LOCATION_DATA_APP},
+ * {@link #LOCATION_MEDIA_OBB},
+ * {@link #LOCATION_MEDIA_DATA}.
* @param name arbitrary, unique name of your choosing to identify the
* APK being written. You can open a file again for
* additional writes (such as after a reboot) by using the
@@ -1084,6 +1142,8 @@
* The system may clear various caches as needed to allocate
* this space.
* @param metadata additional info use by dataLoader to pull data for the file.
+ * @param signature additional file signature, e.g.
+ * <a href="https://source.android.com/security/apksigning/v4.html">APK Signature Scheme v4</a>
* @throws SecurityException if called after the session has been
* sealed or abandoned
* @throws IllegalStateException if called for non-callback session
@@ -1093,9 +1153,10 @@
* {@hide}
*/
@SystemApi
- public void addFile(@NonNull String name, long lengthBytes, @NonNull byte[] metadata) {
+ public void addFile(@FileLocation int location, @NonNull String name, long lengthBytes,
+ @NonNull byte[] metadata, @Nullable byte[] signature) {
try {
- mSession.addFile(name, lengthBytes, metadata);
+ mSession.addFile(location, name, lengthBytes, metadata, signature);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1104,15 +1165,20 @@
/**
* Removes a file.
*
+ * @param location target location for the file. Possible values:
+ * {@link #LOCATION_DATA_APP},
+ * {@link #LOCATION_MEDIA_OBB},
+ * {@link #LOCATION_MEDIA_DATA}.
* @param name name of a file, e.g. split.
* @throws SecurityException if called after the session has been
* sealed or abandoned
* @throws IllegalStateException if called for non-callback session
* {@hide}
*/
- public void removeFile(@NonNull String name) {
+ @SystemApi
+ public void removeFile(@FileLocation int location, @NonNull String name) {
try {
- mSession.removeFile(name);
+ mSession.removeFile(location, name);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2029,6 +2095,9 @@
public boolean isCommitted;
/** {@hide} */
+ public long createdMillis;
+
+ /** {@hide} */
public long updatedMillis;
/** {@hide} */
@@ -2078,6 +2147,7 @@
mStagedSessionErrorMessage = source.readString();
isCommitted = source.readBoolean();
rollbackDataPolicy = source.readInt();
+ createdMillis = source.readLong();
}
/**
@@ -2520,6 +2590,13 @@
}
/**
+ * The timestamp of the initial creation of the session.
+ */
+ public long getCreatedMillis() {
+ return createdMillis;
+ }
+
+ /**
* The timestamp of the last update that occurred to the session, including changing of
* states in case of staged sessions.
*/
@@ -2568,6 +2645,7 @@
dest.writeString(mStagedSessionErrorMessage);
dest.writeBoolean(isCommitted);
dest.writeInt(rollbackDataPolicy);
+ dest.writeLong(createdMillis);
}
public static final Parcelable.Creator<SessionInfo>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 3c08302..9f8d32b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -369,7 +369,7 @@
* Flag parameter to retrieve some information about all applications (even
* uninstalled ones) which have data directories. This state could have
* resulted if applications have been deleted with flag
- * {@code DONT_DELETE_DATA} with a possibility of being replaced or
+ * {@code DELETE_KEEP_DATA} with a possibility of being replaced or
* reinstalled in future.
* <p>
* Note: this flag may cause less information about currently installed
@@ -2203,6 +2203,23 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature(String, int)}: If this feature is supported, the feature version
+ * specifies a date such that the device is known to pass the Vulkan dEQP test suite associated
+ * with that date. The date is encoded as follows:
+ * <ul>
+ * <li>Year in bits 31-16</li>
+ * <li>Month in bits 15-8</li>
+ * <li>Day in bits 7-0</li>
+ * </ul>
+ * <p>
+ * Example: 2019-03-01 is encoded as 0x07E30301, and would indicate that the device passes the
+ * Vulkan dEQP test suite version that was current on 2019-03-01.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_VULKAN_DEQP_LEVEL = "android.software.vulkan.deqp.level";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device includes broadcast radio tuner.
* @hide
*/
@@ -3311,6 +3328,15 @@
public static final int FLAG_PERMISSION_ONE_TIME = 1 << 16;
/**
+ * Permission flags: Reserved for use by the permission controller.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int FLAGS_PERMISSION_RESERVED_PERMISSIONCONTROLLER = 1 << 28 | 1 << 29
+ | 1 << 30 | 1 << 31;
+
+ /**
* Permission flags: Bitwise or of all permission flags allowing an
* exemption for a restricted permission.
* @hide
@@ -3517,7 +3543,7 @@
* information is retrieved from the list of uninstalled
* applications (which includes installed applications as well as
* applications with data directory i.e. applications which had been
- * deleted with {@code DONT_DELETE_DATA} flag set).
+ * deleted with {@code DELETE_KEEP_DATA} flag set).
* @throws NameNotFoundException if a package with the given name cannot be
* found on the system.
*/
@@ -3543,7 +3569,7 @@
* information is retrieved from the list of uninstalled
* applications (which includes installed applications as well as
* applications with data directory i.e. applications which had been
- * deleted with {@code DONT_DELETE_DATA} flag set).
+ * deleted with {@code DELETE_KEEP_DATA} flag set).
* @throws NameNotFoundException if a package with the given name cannot be
* found on the system.
*/
@@ -3564,7 +3590,7 @@
* information is retrieved from the list of uninstalled
* applications (which includes installed applications as well as
* applications with data directory i.e. applications which had been
- * deleted with {@code DONT_DELETE_DATA} flag set).
+ * deleted with {@code DELETE_KEEP_DATA} flag set).
* @throws NameNotFoundException if a package with the given name cannot be
* found on the system.
* @hide
@@ -3811,7 +3837,7 @@
* the application information is retrieved from the list of
* uninstalled applications (which includes installed applications
* as well as applications with data directory i.e. applications
- * which had been deleted with {@code DONT_DELETE_DATA} flag set).
+ * which had been deleted with {@code DELETE_KEEP_DATA} flag set).
* @throws NameNotFoundException if a package with the given name cannot be
* found on the system.
*/
@@ -3838,7 +3864,7 @@
* the application information is retrieved from the list of
* uninstalled applications (which includes installed applications
* as well as applications with data directory i.e. applications
- * which had been deleted with {@code DONT_DELETE_DATA} flag set).
+ * which had been deleted with {@code DELETE_KEEP_DATA} flag set).
* @throws NameNotFoundException if a package with the given name cannot be
* found on the system.
* @hide
@@ -3961,7 +3987,7 @@
* information is retrieved from the list of uninstalled
* applications (which includes installed applications as well as
* applications with data directory i.e. applications which had been
- * deleted with {@code DONT_DELETE_DATA} flag set).
+ * deleted with {@code DELETE_KEEP_DATA} flag set).
*/
@NonNull
public abstract List<PackageInfo> getInstalledPackages(@PackageInfoFlags int flags);
@@ -3979,7 +4005,7 @@
* information is retrieved from the list of uninstalled
* applications (which includes installed applications as well as
* applications with data directory i.e. applications which had been
- * deleted with {@code DONT_DELETE_DATA} flag set).
+ * deleted with {@code DELETE_KEEP_DATA} flag set).
*/
@NonNull
public abstract List<PackageInfo> getPackagesHoldingPermissions(
@@ -3998,7 +4024,7 @@
* information is retrieved from the list of uninstalled
* applications (which includes installed applications as well as
* applications with data directory i.e. applications which had been
- * deleted with {@code DONT_DELETE_DATA} flag set).
+ * deleted with {@code DELETE_KEEP_DATA} flag set).
* @hide
*/
@NonNull
@@ -4410,14 +4436,17 @@
public abstract boolean shouldShowRequestPermissionRationale(@NonNull String permName);
/**
- * Gets the string that is displayed on the button which corresponds to granting background
- * location in settings. The intended use for this is to help apps instruct users how to
- * grant a background permission by providing the string that users will see.
+ * Gets the localized label that corresponds to the option in settings for granting
+ * background access.
*
- * @return the string shown on the button for granting background location
+ * <p>The intended use is for apps to reference this label in its instruction for users to grant
+ * a background permission.
+ *
+ * @return the localized label that corresponds to the settings option for granting
+ * background access
*/
@NonNull
- public CharSequence getBackgroundPermissionButtonLabel() {
+ public CharSequence getBackgroundPermissionOptionLabel() {
return "";
}
@@ -4541,7 +4570,7 @@
/**
* Return a List of all application packages that are installed for the
* current user. If flag GET_UNINSTALLED_PACKAGES has been set, a list of all
- * applications including those deleted with {@code DONT_DELETE_DATA}
+ * applications including those deleted with {@code DELETE_KEEP_DATA}
* (partially installed apps with data directory) will be returned.
*
* @param flags Additional option flags to modify the data returned.
@@ -4552,7 +4581,7 @@
* information is retrieved from the list of uninstalled
* applications (which includes installed applications as well as
* applications with data directory i.e. applications which had been
- * deleted with {@code DONT_DELETE_DATA} flag set).
+ * deleted with {@code DELETE_KEEP_DATA} flag set).
*/
@NonNull
public abstract List<ApplicationInfo> getInstalledApplications(@ApplicationInfoFlags int flags);
@@ -4561,7 +4590,7 @@
* Return a List of all application packages that are installed on the
* device, for a specific user. If flag GET_UNINSTALLED_PACKAGES has been
* set, a list of all applications including those deleted with
- * {@code DONT_DELETE_DATA} (partially installed apps with data directory)
+ * {@code DELETE_KEEP_DATA} (partially installed apps with data directory)
* will be returned.
*
* @param flags Additional option flags to modify the data returned.
@@ -4574,7 +4603,7 @@
* information is retrieved from the list of uninstalled
* applications (which includes installed applications as well as
* applications with data directory i.e. applications which had been
- * deleted with {@code DONT_DELETE_DATA} flag set).
+ * deleted with {@code DELETE_KEEP_DATA} flag set).
* @hide
*/
@NonNull
diff --git a/core/java/android/content/pm/ProcessInfo.java b/core/java/android/content/pm/ProcessInfo.java
new file mode 100644
index 0000000..c77a267
--- /dev/null
+++ b/core/java/android/content/pm/ProcessInfo.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.ArraySet;
+
+/**
+ * Information about a process an app may run. This corresponds to information collected from the
+ * AndroidManifest.xml's <permission-group> tags.
+ * @hide
+ */
+public class ProcessInfo implements Parcelable {
+ /**
+ * The name of the process, fully-qualified based on the app's package name.
+ */
+ public String name;
+
+ /**
+ * If non-null, these are permissions that are not allowed in this process.
+ */
+ @Nullable
+ public ArraySet<String> deniedPermissions;
+
+ public ProcessInfo(String name, ArraySet<String> deniedPermissions) {
+ this.name = name;
+ this.deniedPermissions = deniedPermissions;
+ }
+
+ @Deprecated
+ public ProcessInfo(@NonNull ProcessInfo orig) {
+ this.name = orig.name;
+ this.deniedPermissions = orig.deniedPermissions;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int parcelableFlags) {
+ dest.writeString(this.name);
+ final int numDenied = this.deniedPermissions != null
+ ? this.deniedPermissions.size() : 0;
+ dest.writeInt(numDenied);
+ for (int i = 0; i < numDenied; i++) {
+ dest.writeString(this.deniedPermissions.valueAt(i));
+ }
+ }
+
+ public static final @NonNull Creator<ProcessInfo> CREATOR =
+ new Creator<ProcessInfo>() {
+ public ProcessInfo createFromParcel(Parcel source) {
+ return new ProcessInfo(source);
+ }
+ public ProcessInfo[] newArray(int size) {
+ return new ProcessInfo[size];
+ }
+ };
+
+ private ProcessInfo(Parcel source) {
+ this.name = source.readString();
+ final int numDenied = source.readInt();
+ if (numDenied > 0) {
+ this.deniedPermissions = new ArraySet<>(numDenied);
+ for (int i = numDenied - 1; i >= 0; i--) {
+ this.deniedPermissions.add(TextUtils.safeIntern(source.readString()));
+ }
+ }
+ }
+}
diff --git a/core/java/android/content/pm/parsing/AndroidPackage.java b/core/java/android/content/pm/parsing/AndroidPackage.java
index 990c835..fbe5a48 100644
--- a/core/java/android/content/pm/parsing/AndroidPackage.java
+++ b/core/java/android/content/pm/parsing/AndroidPackage.java
@@ -36,6 +36,7 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -379,6 +380,9 @@
@Nullable
long[] getUsesStaticLibrariesVersions();
+ @Nullable
+ ArrayMap<String, ComponentParseUtils.ParsedProcess> getProcesses();
+
int getVersionCode();
int getVersionCodeMajor();
diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java
index 9b069ac..3018230 100644
--- a/core/java/android/content/pm/parsing/ApkParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkParseUtils.java
@@ -2426,6 +2426,21 @@
XmlUtils.skipCurrentTag(parser);
break;
+ case "processes":
+ ArrayMap<String, ComponentParseUtils.ParsedProcess> processes =
+ ComponentParseUtils.parseProcesses(separateProcesses,
+ parsingPackage,
+ res, parser, flags,
+ outError);
+ if (processes == null) {
+ return parseInput.error(
+ PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ outError[0]
+ );
+ }
+
+ parsingPackage.setProcesses(processes);
+ break;
case "uses-package":
// Dependencies for app installers; we don't currently try to
// enforce this.
diff --git a/core/java/android/content/pm/parsing/ComponentParseUtils.java b/core/java/android/content/pm/parsing/ComponentParseUtils.java
index 3846202a..9a0a6d5 100644
--- a/core/java/android/content/pm/parsing/ComponentParseUtils.java
+++ b/core/java/android/content/pm/parsing/ComponentParseUtils.java
@@ -50,6 +50,7 @@
import android.os.Parcelable;
import android.os.PatternMatcher;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Log;
@@ -1366,6 +1367,72 @@
};
}
+ public static class ParsedProcess implements Parcelable {
+
+ public String name;
+ @Nullable
+ public ArraySet<String> deniedPermissions;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(this.name);
+ final int numDenied = this.deniedPermissions != null
+ ? this.deniedPermissions.size() : 0;
+ dest.writeInt(numDenied);
+ for (int i = 0; i < numDenied; i++) {
+ dest.writeString(this.deniedPermissions.valueAt(i));
+ }
+ }
+
+ public ParsedProcess() {
+ }
+
+ public ParsedProcess(@NonNull ParsedProcess other) {
+ name = other.name;
+ if (other.deniedPermissions != null) {
+ deniedPermissions = new ArraySet<>(other.deniedPermissions);
+ }
+ }
+
+ public void addStateFrom(@NonNull ParsedProcess other) {
+ if (other.deniedPermissions != null) {
+ for (int i = other.deniedPermissions.size() - 1; i >= 0; i--) {
+ if (deniedPermissions == null) {
+ deniedPermissions = new ArraySet<>(other.deniedPermissions.size());
+ }
+ deniedPermissions.add(other.deniedPermissions.valueAt(i));
+ }
+ }
+ }
+
+ protected ParsedProcess(Parcel in) {
+ this.name = TextUtils.safeIntern(in.readString());
+ final int numDenied = in.readInt();
+ if (numDenied > 0) {
+ this.deniedPermissions = new ArraySet<>(numDenied);
+ this.deniedPermissions.add(TextUtils.safeIntern(in.readString()));
+ }
+ }
+
+ public static final Creator<ParsedProcess> CREATOR =
+ new Creator<ParsedProcess>() {
+ @Override
+ public ParsedProcess createFromParcel(Parcel source) {
+ return new ParsedProcess(source);
+ }
+
+ @Override
+ public ParsedProcess[] newArray(int size) {
+ return new ParsedProcess[size];
+ }
+ };
+ }
+
public static ParsedActivity parseActivity(
String[] separateProcesses,
ParsingPackage parsingPackage,
@@ -3266,6 +3333,189 @@
return result;
}
+ private static @Nullable ArraySet<String> parseDenyPermission(
+ ArraySet<String> perms,
+ Resources res,
+ XmlResourceParser parser,
+ String[] outError
+ ) throws IOException, XmlPullParserException {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestDenyPermission);
+ if (sa == null) {
+ outError[0] = "<deny-permission> could not be parsed";
+ return null;
+ }
+
+ try {
+ String perm = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestDenyPermission_name,0);
+ if (perm != null && perm.equals(android.Manifest.permission.INTERNET)) {
+ if (perms == null) {
+ perms = new ArraySet<>();
+ }
+ perms.add(perm);
+ }
+ } finally {
+ sa.recycle();
+ }
+ XmlUtils.skipCurrentTag(parser);
+ return perms;
+ }
+
+ private static ArraySet<String> parseAllowPermission(
+ ArraySet<String> perms,
+ Resources res,
+ XmlResourceParser parser,
+ String[] outError
+ ) throws IOException, XmlPullParserException {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestAllowPermission);
+ if (sa == null) {
+ outError[0] = "<allow-permission> could not be parsed";
+ return null;
+ }
+
+ try {
+ String perm = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestAllowPermission_name,0);
+ if (perm != null && perm.equals(android.Manifest.permission.INTERNET)
+ && perms != null) {
+ perms.remove(perm);
+ if (perms.size() <= 0) {
+ perms = null;
+ }
+ }
+ } finally {
+ sa.recycle();
+ }
+ XmlUtils.skipCurrentTag(parser);
+ return perms;
+ }
+
+ public static ParsedProcess parseProcess(
+ ArraySet<String> perms,
+ String[] separateProcesses,
+ ParsingPackage parsingPackage,
+ Resources res,
+ XmlResourceParser parser,
+ int flags,
+ String[] outError
+ ) throws IOException, XmlPullParserException {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProcess);
+ if (sa == null) {
+ outError[0] = "<process> could not be parsed";
+ return null;
+ }
+
+ ParsedProcess proc = new ParsedProcess();
+ if (perms != null) {
+ proc.deniedPermissions = new ArraySet(perms);
+ }
+
+ try {
+ proc.name = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestProcess_process,0);
+ proc.name = PackageParser.buildProcessName(parsingPackage.getPackageName(),
+ null, proc.name, flags, separateProcesses, outError);
+
+ if (proc.name == null || proc.name.length() <= 0) {
+ outError[0] = "<process> does not specify android:process";
+ return null;
+ }
+ proc.name = PackageParser.buildProcessName(parsingPackage.getPackageName(),
+ parsingPackage.getPackageName(), proc.name,
+ flags, separateProcesses, outError);
+ if (outError[0] != null) {
+ return null;
+ }
+ } finally {
+ sa.recycle();
+ }
+
+ int type;
+ final int innerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals("deny-permission")) {
+ proc.deniedPermissions = parseDenyPermission(proc.deniedPermissions, res, parser,
+ outError);
+ if (outError[0] != null) {
+ return null;
+ }
+ } else if (tagName.equals("allow-permission")) {
+ proc.deniedPermissions = parseAllowPermission(proc.deniedPermissions, res, parser,
+ outError);
+ if (outError[0] != null) {
+ return null;
+ }
+ } else {
+ Slog.w(TAG, "Unknown element under <process>: " + tagName
+ + " at " + parsingPackage.getBaseCodePath() + " "
+ + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ }
+
+ return proc;
+ }
+
+ public static ArrayMap<String, ParsedProcess> parseProcesses(
+ String[] separateProcesses,
+ ParsingPackage parsingPackage,
+ Resources res,
+ XmlResourceParser parser,
+ int flags,
+ String[] outError
+ ) throws IOException, XmlPullParserException {
+ ArraySet<String> deniedPerms = null;
+ ArrayMap<String, ParsedProcess> processes = new ArrayMap<>();
+
+ int type;
+ final int innerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals("deny-permission")) {
+ deniedPerms = parseDenyPermission(deniedPerms, res, parser, outError);
+ if (outError[0] != null) {
+ return null;
+ }
+ } else if (tagName.equals("allow-permission")) {
+ deniedPerms = parseAllowPermission(deniedPerms, res, parser, outError);
+ if (outError[0] != null) {
+ return null;
+ }
+ } else if (tagName.equals("process")) {
+ ParsedProcess proc = parseProcess(deniedPerms, separateProcesses, parsingPackage,
+ res, parser, flags, outError);
+ if (outError[0] != null) {
+ return null;
+ }
+ if (processes.get(proc.name) != null) {
+ outError[0] = "<process> specified existing name '" + proc.name + "'";
+ return null;
+ }
+ processes.put(proc.name, proc);
+ } else {
+ Slog.w(TAG, "Unknown element under <processes>: " + tagName
+ + " at " + parsingPackage.getBaseCodePath() + " "
+ + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ }
+
+ return processes;
+ }
+
public static ActivityInfo.WindowLayout parseLayout(Resources res, AttributeSet attrs) {
TypedArray sw = res.obtainAttributes(attrs,
R.styleable.AndroidManifestLayout);
diff --git a/core/java/android/content/pm/parsing/PackageImpl.java b/core/java/android/content/pm/parsing/PackageImpl.java
index 8677fce..9baf325 100644
--- a/core/java/android/content/pm/parsing/PackageImpl.java
+++ b/core/java/android/content/pm/parsing/PackageImpl.java
@@ -215,6 +215,9 @@
@Nullable
private ArrayList<String> queriesPackages;
+ @Nullable
+ private ArrayMap<String, ComponentParseUtils.ParsedProcess> processes;
+
private String[] splitClassLoaderNames;
private String[] splitCodePaths;
private SparseArray<int[]> splitDependencies;
@@ -527,6 +530,12 @@
return usesStaticLibraries;
}
+ @Nullable
+ @Override
+ public ArrayMap<String, ComponentParseUtils.ParsedProcess> getProcesses() {
+ return processes;
+ }
+
@Override
public boolean isBaseHardwareAccelerated() {
return baseHardwareAccelerated;
@@ -948,6 +957,12 @@
}
@Override
+ public PackageImpl setProcesses(ArrayMap<String, ComponentParseUtils.ParsedProcess> processes) {
+ this.processes = processes;
+ return this;
+ }
+
+ @Override
public PackageImpl setSupportsSmallScreens(int supportsSmallScreens) {
if (supportsSmallScreens == 1) {
return this;
@@ -3010,6 +3025,11 @@
dest.writeStringList(this.usesOptionalLibraries);
dest.writeStringList(this.usesStaticLibraries);
dest.writeLongArray(this.usesStaticLibrariesVersions);
+ final int numProcesses = this.processes != null ? this.processes.size() : 0;
+ dest.writeInt(numProcesses);
+ for (int i = 0; i < numProcesses; i++) {
+ this.processes.valueAt(i).writeToParcel(dest, 0);
+ }
if (this.usesStaticLibrariesCertDigests == null) {
dest.writeInt(-1);
@@ -3161,6 +3181,16 @@
this.usesStaticLibraries = in.createStringArrayList();
internStringArrayList(usesStaticLibraries);
this.usesStaticLibrariesVersions = in.createLongArray();
+ final int numProcesses = in.readInt();
+ if (numProcesses > 0) {
+ this.processes = new ArrayMap<>(numProcesses);
+ for (int i = 0; i < numProcesses; i++) {
+ ComponentParseUtils.ParsedProcess proc = new ComponentParseUtils.ParsedProcess(in);
+ this.processes.put(proc.name, proc);
+ }
+ } else {
+ this.processes = null;
+ }
int digestsSize = in.readInt();
if (digestsSize >= 0) {
diff --git a/core/java/android/content/pm/parsing/PackageInfoUtils.java b/core/java/android/content/pm/parsing/PackageInfoUtils.java
index e0ba99b..72df189 100644
--- a/core/java/android/content/pm/parsing/PackageInfoUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoUtils.java
@@ -32,6 +32,7 @@
import android.content.pm.PackageUserState;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
+import android.content.pm.ProcessInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.SELinuxUtil;
import android.content.pm.ServiceInfo;
@@ -41,11 +42,11 @@
import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation;
import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup;
+import android.util.ArrayMap;
import android.util.ArraySet;
import com.android.internal.util.ArrayUtils;
-import java.util.LinkedHashSet;
import java.util.Set;
/** @hide */
@@ -459,6 +460,24 @@
return ii;
}
+ public static ArrayMap<String, ProcessInfo> generateProcessInfo(
+ ArrayMap<String, ComponentParseUtils.ParsedProcess> procs,
+ @PackageManager.ComponentInfoFlags int flags) {
+ if (procs == null) {
+ return null;
+ }
+
+ final int numProcs = procs.size();
+ ArrayMap<String, ProcessInfo> retProcs = new ArrayMap(numProcs);
+ for (int i = 0; i < numProcs; i++) {
+ ComponentParseUtils.ParsedProcess proc = procs.valueAt(i);
+ retProcs.put(proc.name, new ProcessInfo(proc.name,
+ proc.deniedPermissions != null
+ ? new ArraySet<>(proc.deniedPermissions) : null));
+ }
+ return retProcs;
+ }
+
public static PermissionInfo generatePermissionInfo(ParsedPermission p,
@PackageManager.ComponentInfoFlags int flags) {
if (p == null) return null;
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 411c749..9ddcc09 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -31,6 +31,7 @@
import android.content.pm.parsing.ComponentParseUtils.ParsedProvider;
import android.content.pm.parsing.ComponentParseUtils.ParsedService;
import android.os.Bundle;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -99,6 +100,8 @@
ParsingPackage addQueriesPackage(String packageName);
+ ParsingPackage setProcesses(ArrayMap<String, ComponentParseUtils.ParsedProcess> processes);
+
ParsingPackage asSplit(
String[] splitNames,
String[] splitCodePaths,
diff --git a/core/java/android/hardware/CameraStatus.java b/core/java/android/hardware/CameraStatus.java
index 08b5b77..29802cb 100644
--- a/core/java/android/hardware/CameraStatus.java
+++ b/core/java/android/hardware/CameraStatus.java
@@ -30,6 +30,7 @@
public class CameraStatus implements Parcelable {
public String cameraId;
public int status;
+ public String[] unavailablePhysicalCameras;
@Override
public int describeContents() {
@@ -40,11 +41,13 @@
public void writeToParcel(Parcel out, int flags) {
out.writeString(cameraId);
out.writeInt(status);
+ out.writeStringArray(unavailablePhysicalCameras);
}
public void readFromParcel(Parcel in) {
cameraId = in.readString();
status = in.readInt();
+ unavailablePhysicalCameras = in.readStringArray();
}
public static final @android.annotation.NonNull Parcelable.Creator<CameraStatus> CREATOR =
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 9b58578..55025f0 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -718,6 +718,52 @@
public void onCameraAccessPrioritiesChanged() {
// default empty implementation
}
+
+ /**
+ * A physical camera has become available for use again.
+ *
+ * <p>By default, all of the physical cameras of a logical multi-camera are
+ * available, so {@link #onPhysicalCameraAvailable} is not called for any of the physical
+ * cameras of a logical multi-camera, when {@link #onCameraAvailable} for the logical
+ * multi-camera is invoked. However, if some specific physical cameras are unavailable
+ * to begin with, {@link #onPhysicalCameraUnavailable} may be invoked after
+ * {@link #onCameraAvailable}.</p>
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * @param cameraId The unique identifier of the logical multi-camera.
+ * @param physicalCameraId The unique identifier of the physical camera.
+ *
+ * @see #onCameraAvailable
+ * @see #onPhysicalCameraUnavailable
+ */
+ public void onPhysicalCameraAvailable(@NonNull String cameraId,
+ @NonNull String physicalCameraId) {
+ // default empty implementation
+ }
+
+ /**
+ * A previously-available physical camera has become unavailable for use.
+ *
+ * <p>By default, all of the physical cameras of a logical multi-camera are
+ * available, so {@link #onPhysicalCameraAvailable} is not called for any of the physical
+ * cameras of a logical multi-camera, when {@link #onCameraAvailable} for the logical
+ * multi-camera is invoked. If some specific physical cameras are unavailable
+ * to begin with, {@link #onPhysicalCameraUnavailable} may be invoked after
+ * {@link #onCameraAvailable}.</p>
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * @param cameraId The unique identifier of the logical multi-camera.
+ * @param physicalCameraId The unique identifier of the physical camera.
+ *
+ * @see #onCameraAvailable
+ * @see #onPhysicalCameraAvailable
+ */
+ public void onPhysicalCameraUnavailable(@NonNull String cameraId,
+ @NonNull String physicalCameraId) {
+ // default empty implementation
+ }
}
/**
@@ -914,6 +960,9 @@
private final ScheduledExecutorService mScheduler = Executors.newScheduledThreadPool(1);
// Camera ID -> Status map
private final ArrayMap<String, Integer> mDeviceStatus = new ArrayMap<String, Integer>();
+ // Camera ID -> (physical camera ID -> Status map)
+ private final ArrayMap<String, ArrayList<String>> mUnavailablePhysicalDevices =
+ new ArrayMap<String, ArrayList<String>>();
// Registered availablility callbacks and their executors
private final ArrayMap<AvailabilityCallback, Executor> mCallbackMap =
@@ -1003,6 +1052,14 @@
CameraStatus[] cameraStatuses = cameraService.addListener(this);
for (CameraStatus c : cameraStatuses) {
onStatusChangedLocked(c.status, c.cameraId);
+
+ if (c.unavailablePhysicalCameras != null) {
+ for (String unavailPhysicalCamera : c.unavailablePhysicalCameras) {
+ onPhysicalCameraStatusChangedLocked(
+ ICameraServiceListener.STATUS_NOT_PRESENT,
+ c.cameraId, unavailPhysicalCamera);
+ }
+ }
}
mCameraService = cameraService;
} catch(ServiceSpecificException e) {
@@ -1086,6 +1143,10 @@
public void onStatusChanged(int status, String id) throws RemoteException {
}
@Override
+ public void onPhysicalCameraStatusChanged(int status,
+ String id, String physicalId) throws RemoteException {
+ }
+ @Override
public void onTorchStatusChanged(int status, String id) throws RemoteException {
}
@Override
@@ -1236,7 +1297,7 @@
}
private void postSingleUpdate(final AvailabilityCallback callback, final Executor executor,
- final String id, final int status) {
+ final String id, final String physicalId, final int status) {
if (isAvailable(status)) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -1244,7 +1305,11 @@
new Runnable() {
@Override
public void run() {
- callback.onCameraAvailable(id);
+ if (physicalId == null) {
+ callback.onCameraAvailable(id);
+ } else {
+ callback.onPhysicalCameraAvailable(id, physicalId);
+ }
}
});
} finally {
@@ -1257,7 +1322,11 @@
new Runnable() {
@Override
public void run() {
- callback.onCameraUnavailable(id);
+ if (physicalId == null) {
+ callback.onCameraUnavailable(id);
+ } else {
+ callback.onPhysicalCameraUnavailable(id, physicalId);
+ }
}
});
} finally {
@@ -1304,7 +1373,16 @@
for (int i = 0; i < mDeviceStatus.size(); i++) {
String id = mDeviceStatus.keyAt(i);
Integer status = mDeviceStatus.valueAt(i);
- postSingleUpdate(callback, executor, id, status);
+ postSingleUpdate(callback, executor, id, null /*physicalId*/, status);
+
+ // Send the NOT_PRESENT state for unavailable physical cameras
+ if (isAvailable(status) && mUnavailablePhysicalDevices.containsKey(id)) {
+ ArrayList<String> unavailableIds = mUnavailablePhysicalDevices.get(id);
+ for (String unavailableId : unavailableIds) {
+ postSingleUpdate(callback, executor, id, unavailableId,
+ ICameraServiceListener.STATUS_NOT_PRESENT);
+ }
+ }
}
}
@@ -1323,8 +1401,12 @@
Integer oldStatus;
if (status == ICameraServiceListener.STATUS_NOT_PRESENT) {
oldStatus = mDeviceStatus.remove(id);
+ mUnavailablePhysicalDevices.remove(id);
} else {
oldStatus = mDeviceStatus.put(id, status);
+ if (oldStatus == null) {
+ mUnavailablePhysicalDevices.put(id, new ArrayList<String>());
+ }
}
if (oldStatus != null && oldStatus == status) {
@@ -1366,10 +1448,62 @@
Executor executor = mCallbackMap.valueAt(i);
final AvailabilityCallback callback = mCallbackMap.keyAt(i);
- postSingleUpdate(callback, executor, id, status);
+ postSingleUpdate(callback, executor, id, null /*physicalId*/, status);
}
} // onStatusChangedLocked
+ private void onPhysicalCameraStatusChangedLocked(int status,
+ String id, String physicalId) {
+ if (DEBUG) {
+ Log.v(TAG,
+ String.format("Camera id %s physical camera id %s has status "
+ + "changed to 0x%x", id, physicalId, status));
+ }
+
+ if (!validStatus(status)) {
+ Log.e(TAG, String.format(
+ "Ignoring invalid device %s physical device %s status 0x%x", id,
+ physicalId, status));
+ return;
+ }
+
+ //TODO: Do we need to treat this as error?
+ if (!mDeviceStatus.containsKey(id) || !isAvailable(mDeviceStatus.get(id))
+ || !mUnavailablePhysicalDevices.containsKey(id)) {
+ Log.e(TAG, String.format("Camera %s is not available. Ignore physical camera "
+ + "status change", id));
+ return;
+ }
+
+ ArrayList<String> unavailablePhysicalDevices = mUnavailablePhysicalDevices.get(id);
+ if (!isAvailable(status)
+ && !unavailablePhysicalDevices.contains(physicalId)) {
+ unavailablePhysicalDevices.add(physicalId);
+ } else if (isAvailable(status)
+ && unavailablePhysicalDevices.contains(physicalId)) {
+ unavailablePhysicalDevices.remove(physicalId);
+ } else {
+ if (DEBUG) {
+ Log.v(TAG,
+ String.format(
+ "Physical camera device status was previously available (%b), "
+ + " and is now again available (%b)"
+ + "so no new client visible update will be sent",
+ !unavailablePhysicalDevices.contains(physicalId),
+ isAvailable(status)));
+ }
+ return;
+ }
+
+ final int callbackCount = mCallbackMap.size();
+ for (int i = 0; i < callbackCount; i++) {
+ Executor executor = mCallbackMap.valueAt(i);
+ final AvailabilityCallback callback = mCallbackMap.keyAt(i);
+
+ postSingleUpdate(callback, executor, id, physicalId, status);
+ }
+ } // onPhysicalCameraStatusChangedLocked
+
private void updateTorchCallbackLocked(TorchCallback callback, Executor executor) {
for (int i = 0; i < mTorchStatus.size(); i++) {
String id = mTorchStatus.keyAt(i);
@@ -1478,6 +1612,14 @@
}
@Override
+ public void onPhysicalCameraStatusChanged(int status, String cameraId,
+ String physicalCameraId) throws RemoteException {
+ synchronized (mLock) {
+ onPhysicalCameraStatusChangedLocked(status, cameraId, physicalCameraId);
+ }
+ }
+
+ @Override
public void onTorchStatusChanged(int status, String cameraId) throws RemoteException {
synchronized (mLock) {
onTorchStatusChangedLocked(status, cameraId);
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 89dac2a..4dd3a30 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -865,14 +865,20 @@
* <p>The camera device is a logical camera backed by two or more physical cameras.</p>
* <p>In API level 28, the physical cameras must also be exposed to the application via
* {@link android.hardware.camera2.CameraManager#getCameraIdList }.</p>
- * <p>Starting from API level 29, some or all physical cameras may not be independently
- * exposed to the application, in which case the physical camera IDs will not be
- * available in {@link android.hardware.camera2.CameraManager#getCameraIdList }. But the
+ * <p>Starting from API level 29:</p>
+ * <ul>
+ * <li>Some or all physical cameras may not be independently exposed to the application,
+ * in which case the physical camera IDs will not be available in
+ * {@link android.hardware.camera2.CameraManager#getCameraIdList }. But the
* application can still query the physical cameras' characteristics by calling
- * {@link android.hardware.camera2.CameraManager#getCameraCharacteristics }. Additionally,
- * if a physical camera is hidden from camera ID list, the mandatory stream combinations
- * for that physical camera must be supported through the logical camera using physical
- * streams.</p>
+ * {@link android.hardware.camera2.CameraManager#getCameraCharacteristics }.</li>
+ * <li>If a physical camera is hidden from camera ID list, the mandatory stream
+ * combinations for that physical camera must be supported through the logical camera
+ * using physical streams. One exception is that in API level 30, a physical camera
+ * may become unavailable via
+ * {@link CameraManager.AvailabilityCallback#onPhysicalCameraUnavailable }
+ * callback.</li>
+ * </ul>
* <p>Combinations of logical and physical streams, or physical streams from different
* physical cameras are not guaranteed. However, if the camera device supports
* {@link CameraDevice#isSessionConfigurationSupported },
diff --git a/core/java/android/hardware/lights/ILightsManager.aidl b/core/java/android/hardware/lights/ILightsManager.aidl
new file mode 100644
index 0000000..6ea24b7
--- /dev/null
+++ b/core/java/android/hardware/lights/ILightsManager.aidl
@@ -0,0 +1,33 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.lights;
+
+import android.hardware.lights.Light;
+import android.hardware.lights.LightState;
+
+/**
+ * API to lights manager service.
+ *
+ * {@hide}
+ */
+interface ILightsManager {
+ List<Light> getLights();
+ LightState getLightState(int lightId);
+ void openSession(in IBinder sessionToken);
+ void closeSession(in IBinder sessionToken);
+ void setLightStates(in IBinder sessionToken, in int[] lightIds, in LightState[] states);
+}
diff --git a/core/java/android/hardware/lights/Light.aidl b/core/java/android/hardware/lights/Light.aidl
new file mode 100644
index 0000000..946e06d4
--- /dev/null
+++ b/core/java/android/hardware/lights/Light.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.lights;
+
+/** @hide */
+parcelable Light;
diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java
new file mode 100644
index 0000000..c5cb803
--- /dev/null
+++ b/core/java/android/hardware/lights/Light.java
@@ -0,0 +1,105 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.lights;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents a logical light on the device.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class Light implements Parcelable {
+ private final int mId;
+ private final int mOrdinal;
+ private final int mType;
+
+ /**
+ * Creates a new light with the given data.
+ *
+ * @hide */
+ public Light(int id, int ordinal, int type) {
+ mId = id;
+ mOrdinal = ordinal;
+ mType = type;
+ }
+
+ private Light(@NonNull Parcel in) {
+ mId = in.readInt();
+ mOrdinal = in.readInt();
+ mType = in.readInt();
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mId);
+ dest.writeInt(mOrdinal);
+ dest.writeInt(mType);
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ public static final @android.annotation.NonNull Parcelable.Creator<Light> CREATOR =
+ new Parcelable.Creator<Light>() {
+ public Light createFromParcel(Parcel in) {
+ return new Light(in);
+ }
+
+ public Light[] newArray(int size) {
+ return new Light[size];
+ }
+ };
+
+ /**
+ * Returns the id of the light.
+ */
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * Returns the ordinal of the light.
+ *
+ * <p>This represents the physical order of the lights on the device. The exact values are
+ * device-dependent, but for example, if there are lights in a row, sorting the Light objects
+ * by ordinal should match the order in which they appear on the device. If the device has
+ * 4 lights, the ordinals could be [1, 2, 3, 4] or [0, 10, 20, 30] or any other values that
+ * have the same sort order.
+ */
+ public int getOrdinal() {
+ return mOrdinal;
+ }
+
+ /**
+ * Returns the logical type of the light.
+ */
+ public @LightsManager.LightType int getType() {
+ return mType;
+ }
+}
diff --git a/core/java/android/hardware/lights/LightState.aidl b/core/java/android/hardware/lights/LightState.aidl
new file mode 100644
index 0000000..d598336
--- /dev/null
+++ b/core/java/android/hardware/lights/LightState.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.lights;
+
+/** @hide */
+parcelable LightState;
diff --git a/core/java/android/hardware/lights/LightState.java b/core/java/android/hardware/lights/LightState.java
new file mode 100644
index 0000000..e55aa70
--- /dev/null
+++ b/core/java/android/hardware/lights/LightState.java
@@ -0,0 +1,84 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.lights;
+
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents the state of a device light.
+ *
+ * <p>Controlling the color and brightness of a light is done on a best-effort basis. Each of the R,
+ * G and B channels represent the intensities of the respective part of an RGB LED, if that is
+ * supported. For devices that only support on or off lights, everything that's not off will turn
+ * the light on. If the light is monochrome and only the brightness can be controlled, the RGB color
+ * will be converted to only a brightness value and that will be used for the light's single
+ * channel.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class LightState implements Parcelable {
+ private final int mColor;
+
+ /**
+ * Creates a new LightState with the desired color and intensity.
+ *
+ * @param color the desired color and intensity in ARGB format.
+ */
+ public LightState(@ColorInt int color) {
+ mColor = color;
+ }
+
+ private LightState(@NonNull Parcel in) {
+ mColor = in.readInt();
+ }
+
+ /**
+ * Return the color and intensity associated with this LightState.
+ * @return the color and intensity in ARGB format. The A channel is ignored.
+ */
+ public @ColorInt int getColor() {
+ return mColor;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mColor);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Parcelable.Creator<LightState> CREATOR =
+ new Parcelable.Creator<LightState>() {
+ public LightState createFromParcel(Parcel in) {
+ return new LightState(in);
+ }
+
+ public LightState[] newArray(int size) {
+ return new LightState[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/lights/LightsManager.java b/core/java/android/hardware/lights/LightsManager.java
new file mode 100644
index 0000000..1bc051b
--- /dev/null
+++ b/core/java/android/hardware/lights/LightsManager.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.lights;
+
+import android.Manifest;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.CloseGuard;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.Reference;
+import java.util.List;
+
+/**
+ * The LightsManager class allows control over device lights.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+@SystemService(Context.LIGHTS_SERVICE)
+public final class LightsManager {
+ private static final String TAG = "LightsManager";
+
+ // These enum values copy the values from {@link com.android.server.lights.LightsManager}
+ // and the light HAL. Since 0-7 are lights reserved for system use, only the microphone light
+ // is available through this API.
+ /** Type for lights that indicate microphone usage */
+ public static final int LIGHT_TYPE_MICROPHONE = 8;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"LIGHT_TYPE_"},
+ value = {
+ LIGHT_TYPE_MICROPHONE,
+ })
+ public @interface LightType {}
+
+ @NonNull private final Context mContext;
+ @NonNull private final ILightsManager mService;
+
+ /**
+ * Creates a LightsManager.
+ *
+ * @hide
+ */
+ public LightsManager(@NonNull Context context) throws ServiceNotFoundException {
+ this(context, ILightsManager.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.LIGHTS_SERVICE)));
+ }
+
+ /**
+ * Creates a LightsManager with a provided service implementation.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public LightsManager(@NonNull Context context, @NonNull ILightsManager service) {
+ mContext = Preconditions.checkNotNull(context);
+ mService = Preconditions.checkNotNull(service);
+ }
+
+ /**
+ * Returns the lights available on the device.
+ *
+ * @return A list of available lights
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+ public @NonNull List<Light> getLights() {
+ try {
+ return mService.getLights();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the state of a specified light.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+ @TestApi
+ public @NonNull LightState getLightState(@NonNull Light light) {
+ Preconditions.checkNotNull(light);
+ try {
+ return mService.getLightState(light.getId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Creates a new LightsSession that can be used to control the device lights.
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+ public @NonNull LightsSession openSession() {
+ try {
+ final LightsSession session = new LightsSession();
+ mService.openSession(session.mToken);
+ return session;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Encapsulates a session that can be used to control device lights and represents the lifetime
+ * of the requests.
+ */
+ public final class LightsSession implements AutoCloseable {
+
+ private final IBinder mToken = new Binder();
+
+ private final CloseGuard mCloseGuard = new CloseGuard();
+ private boolean mClosed = false;
+
+ /**
+ * Instantiated by {@link LightsManager#openSession()}.
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+ private LightsSession() {
+ mCloseGuard.open("close");
+ }
+
+ /**
+ * Sends a request to modify the states of multiple lights.
+ *
+ * <p>This method only controls lights that aren't overridden by higher-priority sessions.
+ * Additionally, lights not controlled by this session can be controlled by lower-priority
+ * sessions.
+ *
+ * @param request the settings for lights that should change
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+ public void setLights(@NonNull LightsRequest request) {
+ Preconditions.checkNotNull(request);
+ if (!mClosed) {
+ try {
+ mService.setLightStates(mToken, request.mLightIds, request.mLightStates);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Closes the session, reverting all changes made through it.
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+ @Override
+ public void close() {
+ if (!mClosed) {
+ try {
+ mService.closeSession(mToken);
+ mClosed = true;
+ mCloseGuard.close();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ Reference.reachabilityFence(this);
+ }
+
+ /** @hide */
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ mCloseGuard.warnIfOpen();
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+ }
+}
diff --git a/core/java/android/hardware/lights/LightsRequest.java b/core/java/android/hardware/lights/LightsRequest.java
new file mode 100644
index 0000000..a36da4c
--- /dev/null
+++ b/core/java/android/hardware/lights/LightsRequest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.lights;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.util.SparseArray;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Encapsulates a request to modify the state of multiple lights.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class LightsRequest {
+
+ /** Visible to {@link LightsManager.Session}. */
+ final int[] mLightIds;
+
+ /** Visible to {@link LightsManager.Session}. */
+ final LightState[] mLightStates;
+
+ /**
+ * Can only be constructed via {@link LightsRequest.Builder#build()}.
+ */
+ private LightsRequest(SparseArray<LightState> changes) {
+ final int n = changes.size();
+ mLightIds = new int[n];
+ mLightStates = new LightState[n];
+ for (int i = 0; i < n; i++) {
+ mLightIds[i] = changes.keyAt(i);
+ mLightStates[i] = changes.valueAt(i);
+ }
+ }
+
+ /**
+ * Builder for creating device light change requests.
+ */
+ public static final class Builder {
+
+ private final SparseArray<LightState> mChanges = new SparseArray<>();
+
+ /**
+ * Overrides the color and intensity of a given light.
+ *
+ * @param light the light to modify
+ * @param state the desired color and intensity of the light
+ */
+ public @NonNull Builder setLight(@NonNull Light light, @NonNull LightState state) {
+ Preconditions.checkNotNull(light);
+ Preconditions.checkNotNull(state);
+ mChanges.put(light.getId(), state);
+ return this;
+ }
+
+ /**
+ * Removes the override for the color and intensity of a given light.
+ *
+ * @param light the light to modify
+ */
+ public @NonNull Builder clearLight(@NonNull Light light) {
+ Preconditions.checkNotNull(light);
+ mChanges.put(light.getId(), null);
+ return this;
+ }
+
+ /**
+ * Create a LightsRequest object used to override lights on the device.
+ *
+ * <p>The generated {@link LightsRequest} should be used in
+ * {@link LightsManager.Session#setLights(LightsLightsRequest).
+ */
+ public @NonNull LightsRequest build() {
+ return new LightsRequest(mChanges);
+ }
+ }
+}
diff --git a/core/java/android/hardware/location/ContextHubInfo.java b/core/java/android/hardware/location/ContextHubInfo.java
index a11f2e9..6d56d2d 100644
--- a/core/java/android/hardware/location/ContextHubInfo.java
+++ b/core/java/android/hardware/location/ContextHubInfo.java
@@ -21,6 +21,7 @@
import android.hardware.contexthub.V1_0.ContextHub;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.proto.ProtoOutputStream;
import java.util.Arrays;
@@ -270,6 +271,30 @@
return retVal;
}
+ /**
+ * Dump the internal state as a ContextHubInfoProto to the given ProtoOutputStream.
+ *
+ * If the output belongs to a sub message, the caller is responsible for wrapping this function
+ * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}.
+ *
+ * @hide
+ */
+ public void dump(ProtoOutputStream proto) {
+ proto.write(ContextHubInfoProto.ID, mId);
+ proto.write(ContextHubInfoProto.NAME, mName);
+ proto.write(ContextHubInfoProto.VENDOR, mVendor);
+ proto.write(ContextHubInfoProto.TOOLCHAIN, mToolchain);
+ proto.write(ContextHubInfoProto.PLATFORM_VERSION, mPlatformVersion);
+ proto.write(ContextHubInfoProto.STATIC_SW_VERSION, getStaticSwVersion());
+ proto.write(ContextHubInfoProto.TOOLCHAIN_VERSION, mToolchainVersion);
+ proto.write(ContextHubInfoProto.CHRE_PLATFORM_ID, mChrePlatformId);
+ proto.write(ContextHubInfoProto.PEAK_MIPS, mPeakMips);
+ proto.write(ContextHubInfoProto.STOPPED_POWER_DRAW_MW, mStoppedPowerDrawMw);
+ proto.write(ContextHubInfoProto.SLEEP_POWER_DRAW_MW, mSleepPowerDrawMw);
+ proto.write(ContextHubInfoProto.PEAK_POWER_DRAW_MW, mPeakPowerDrawMw);
+ proto.write(ContextHubInfoProto.MAX_PACKET_LENGTH_BYTES, mMaxPacketLengthBytes);
+ }
+
@Override
public boolean equals(@Nullable Object object) {
if (object == this) {
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index d43a619..a30fd6b 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -130,7 +130,7 @@
aidlPhrase.id = apiPhrase.id;
aidlPhrase.recognitionModes = api2aidlRecognitionModes(apiPhrase.recognitionModes);
aidlPhrase.users = Arrays.copyOf(apiPhrase.users, apiPhrase.users.length);
- aidlPhrase.locale = apiPhrase.locale;
+ aidlPhrase.locale = apiPhrase.locale.toLanguageTag();
aidlPhrase.text = apiPhrase.text;
return aidlPhrase;
}
diff --git a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
index eb5d0cb..ef76c62 100644
--- a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
+++ b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
@@ -17,6 +17,7 @@
package android.hardware.soundtrigger;
import android.Manifest;
+import android.annotation.IntDef;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -24,7 +25,6 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
-import android.service.voice.AlwaysOnHotwordDetector;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.AttributeSet;
@@ -35,6 +35,8 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
@@ -66,9 +68,10 @@
"com.android.intent.action.MANAGE_VOICE_KEYPHRASES";
/**
* Intent extra: The intent extra for the specific manage action that needs to be performed.
- * Possible values are {@link AlwaysOnHotwordDetector#MANAGE_ACTION_ENROLL},
- * {@link AlwaysOnHotwordDetector#MANAGE_ACTION_RE_ENROLL}
- * or {@link AlwaysOnHotwordDetector#MANAGE_ACTION_UN_ENROLL}.
+ *
+ * @see #MANAGE_ACTION_ENROLL
+ * @see #MANAGE_ACTION_RE_ENROLL
+ * @see #MANAGE_ACTION_UN_ENROLL
*/
public static final String EXTRA_VOICE_KEYPHRASE_ACTION =
"com.android.intent.extra.VOICE_KEYPHRASE_ACTION";
@@ -86,6 +89,31 @@
"com.android.intent.extra.VOICE_KEYPHRASE_LOCALE";
/**
+ * Keyphrase management actions used with the {@link #EXTRA_VOICE_KEYPHRASE_ACTION} intent extra
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "MANAGE_ACTION_" }, value = {
+ MANAGE_ACTION_ENROLL,
+ MANAGE_ACTION_RE_ENROLL,
+ MANAGE_ACTION_UN_ENROLL
+ })
+ public @interface ManageActions {}
+
+ /**
+ * Indicates desired action to enroll keyphrase model
+ */
+ public static final int MANAGE_ACTION_ENROLL = 0;
+ /**
+ * Indicates desired action to re-enroll keyphrase model
+ */
+ public static final int MANAGE_ACTION_RE_ENROLL = 1;
+ /**
+ * Indicates desired action to un-enroll keyphrase model
+ */
+ public static final int MANAGE_ACTION_UN_ENROLL = 2;
+
+ /**
* List of available keyphrases.
*/
final private KeyphraseMetadata[] mKeyphrases;
@@ -294,15 +322,13 @@
* for the locale.
*
* @param action The enrollment related action that this intent is supposed to perform.
- * This can be one of {@link AlwaysOnHotwordDetector#MANAGE_ACTION_ENROLL},
- * {@link AlwaysOnHotwordDetector#MANAGE_ACTION_RE_ENROLL}
- * or {@link AlwaysOnHotwordDetector#MANAGE_ACTION_UN_ENROLL}
* @param keyphrase The keyphrase that the user needs to be enrolled to.
* @param locale The locale for which the enrollment needs to be performed.
* @return An {@link Intent} to manage the keyphrase. This can be null if managing the
* given keyphrase/locale combination isn't possible.
*/
- public Intent getManageKeyphraseIntent(int action, String keyphrase, Locale locale) {
+ public Intent getManageKeyphraseIntent(@ManageActions int action, String keyphrase,
+ Locale locale) {
if (mKeyphrasePackageMap == null || mKeyphrasePackageMap.isEmpty()) {
Slog.w(TAG, "No enrollment application exists");
return null;
diff --git a/core/java/android/hardware/soundtrigger/KeyphraseMetadata.aidl b/core/java/android/hardware/soundtrigger/KeyphraseMetadata.aidl
new file mode 100644
index 0000000..7a5e932
--- /dev/null
+++ b/core/java/android/hardware/soundtrigger/KeyphraseMetadata.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.soundtrigger;
+
+parcelable KeyphraseMetadata;
diff --git a/core/java/android/hardware/soundtrigger/KeyphraseMetadata.java b/core/java/android/hardware/soundtrigger/KeyphraseMetadata.java
index ed8c296..15462de 100644
--- a/core/java/android/hardware/soundtrigger/KeyphraseMetadata.java
+++ b/core/java/android/hardware/soundtrigger/KeyphraseMetadata.java
@@ -16,8 +16,13 @@
package android.hardware.soundtrigger;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
import android.util.ArraySet;
+import com.android.internal.util.DataClass;
+
import java.util.Locale;
/**
@@ -25,37 +30,168 @@
*
* @hide
*/
-public class KeyphraseMetadata {
+@DataClass(
+ genEqualsHashCode = true,
+ genToString = true,
+ genConstructor = false,
+ genHiddenConstDefs = true)
+public final class KeyphraseMetadata implements Parcelable {
public final int id;
+ @NonNull
public final String keyphrase;
+ @NonNull
public final ArraySet<Locale> supportedLocales;
public final int recognitionModeFlags;
- public KeyphraseMetadata(int id, String keyphrase, ArraySet<Locale> supportedLocales,
- int recognitionModeFlags) {
+ public KeyphraseMetadata(int id, @NonNull String keyphrase,
+ @NonNull ArraySet<Locale> supportedLocales, int recognitionModeFlags) {
this.id = id;
this.keyphrase = keyphrase;
this.supportedLocales = supportedLocales;
this.recognitionModeFlags = recognitionModeFlags;
}
- @Override
- public String toString() {
- return "id=" + id + ", keyphrase=" + keyphrase + ", supported-locales=" + supportedLocales
- + ", recognition-modes=" + recognitionModeFlags;
- }
-
/**
* @return Indicates if we support the given phrase.
*/
- public boolean supportsPhrase(String phrase) {
+ public boolean supportsPhrase(@Nullable String phrase) {
return keyphrase.isEmpty() || keyphrase.equalsIgnoreCase(phrase);
}
/**
* @return Indicates if we support the given locale.
*/
- public boolean supportsLocale(Locale locale) {
+ public boolean supportsLocale(@Nullable Locale locale) {
return supportedLocales.isEmpty() || supportedLocales.contains(locale);
}
+
+
+
+
+ // Code below generated by codegen v1.0.14.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/soundtrigger/KeyphraseMetadata.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "KeyphraseMetadata { " +
+ "id = " + id + ", " +
+ "keyphrase = " + keyphrase + ", " +
+ "supportedLocales = " + supportedLocales + ", " +
+ "recognitionModeFlags = " + recognitionModeFlags +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(KeyphraseMetadata other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ KeyphraseMetadata that = (KeyphraseMetadata) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && id == that.id
+ && java.util.Objects.equals(keyphrase, that.keyphrase)
+ && java.util.Objects.equals(supportedLocales, that.supportedLocales)
+ && recognitionModeFlags == that.recognitionModeFlags;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + id;
+ _hash = 31 * _hash + java.util.Objects.hashCode(keyphrase);
+ _hash = 31 * _hash + java.util.Objects.hashCode(supportedLocales);
+ _hash = 31 * _hash + recognitionModeFlags;
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(id);
+ dest.writeString(keyphrase);
+ dest.writeArraySet(supportedLocales);
+ dest.writeInt(recognitionModeFlags);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ KeyphraseMetadata(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int _id = in.readInt();
+ String _keyphrase = in.readString();
+ ArraySet<Locale> _supportedLocales = (ArraySet) in.readArraySet(null);
+ int _recognitionModeFlags = in.readInt();
+
+ this.id = _id;
+ this.keyphrase = _keyphrase;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, keyphrase);
+ this.supportedLocales = _supportedLocales;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, supportedLocales);
+ this.recognitionModeFlags = _recognitionModeFlags;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<KeyphraseMetadata> CREATOR
+ = new Parcelable.Creator<KeyphraseMetadata>() {
+ @Override
+ public KeyphraseMetadata[] newArray(int size) {
+ return new KeyphraseMetadata[size];
+ }
+
+ @Override
+ public KeyphraseMetadata createFromParcel(@NonNull android.os.Parcel in) {
+ return new KeyphraseMetadata(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1579290593964L,
+ codegenVersion = "1.0.14",
+ sourceFile = "frameworks/base/core/java/android/hardware/soundtrigger/KeyphraseMetadata.java",
+ inputSignatures = "public final int id\npublic final @android.annotation.NonNull java.lang.String keyphrase\npublic final @android.annotation.NonNull android.util.ArraySet<java.util.Locale> supportedLocales\npublic final int recognitionModeFlags\npublic boolean supportsPhrase(java.lang.String)\npublic boolean supportsLocale(java.util.Locale)\nclass KeyphraseMetadata extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genConstructor=false, genHiddenConstDefs=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
}
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 1932f46..d505ae5 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -49,6 +49,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Locale;
import java.util.UUID;
/**
@@ -148,6 +149,7 @@
public final int maxUsers;
/** Supported recognition modes (bit field, RECOGNITION_MODE_VOICE_TRIGGER ...) */
+ @RecognitionModes
public final int recognitionModes;
/** Supports seamless transition to capture mode after recognition */
@@ -175,9 +177,9 @@
ModuleProperties(int id, @NonNull String implementor, @NonNull String description,
@NonNull String uuid, int version, @NonNull String supportedModelArch,
- int maxSoundModels, int maxKeyphrases, int maxUsers, int recognitionModes,
- boolean supportsCaptureTransition, int maxBufferMs,
- boolean supportsConcurrentCapture, int powerConsumptionMw,
+ int maxSoundModels, int maxKeyphrases, int maxUsers,
+ @RecognitionModes int recognitionModes, boolean supportsCaptureTransition,
+ int maxBufferMs, boolean supportsConcurrentCapture, int powerConsumptionMw,
boolean returnsTriggerInEvent, int audioCapabilities) {
this.id = id;
this.implementor = requireNonNull(implementor);
@@ -271,16 +273,27 @@
}
}
- /*****************************************************************************
+ /**
* A SoundModel describes the attributes and contains the binary data used by the hardware
* implementation to detect a particular sound pattern.
* A specialized version {@link KeyphraseSoundModel} is defined for key phrase
* sound models.
- *
- * @hide
- ****************************************************************************/
+ */
public static class SoundModel {
- /** Undefined sound model type */
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ TYPE_GENERIC_SOUND,
+ TYPE_KEYPHRASE,
+ TYPE_UNKNOWN,
+ })
+ public @interface SoundModelType {}
+
+ /**
+ * Undefined sound model type
+ * @hide
+ */
public static final int TYPE_UNKNOWN = -1;
/** Keyphrase sound model */
@@ -293,15 +306,14 @@
public static final int TYPE_GENERIC_SOUND = 1;
/** Unique sound model identifier */
- @UnsupportedAppUsage
@NonNull
public final UUID uuid;
/** Sound model type (e.g. TYPE_KEYPHRASE); */
+ @SoundModelType
public final int type;
/** Unique sound model vendor identifier */
- @UnsupportedAppUsage
@NonNull
public final UUID vendorUuid;
@@ -309,11 +321,11 @@
public final int version;
/** Opaque data. For use by vendor implementation and enrollment application */
- @UnsupportedAppUsage
@NonNull
public final byte[] data;
- public SoundModel(@NonNull UUID uuid, @Nullable UUID vendorUuid, int type,
+ /** @hide */
+ public SoundModel(@NonNull UUID uuid, @Nullable UUID vendorUuid, @SoundModelType int type,
@Nullable byte[] data, int version) {
this.uuid = requireNonNull(uuid);
this.vendorUuid = vendorUuid != null ? vendorUuid : new UUID(0, 0);
@@ -336,67 +348,90 @@
@Override
public boolean equals(Object obj) {
- if (this == obj)
+ if (this == obj) {
return true;
- if (obj == null)
+ }
+ if (obj == null) {
return false;
- if (!(obj instanceof SoundModel))
+ }
+ if (!(obj instanceof SoundModel)) {
return false;
+ }
SoundModel other = (SoundModel) obj;
- if (type != other.type)
+ if (type != other.type) {
return false;
+ }
if (uuid == null) {
- if (other.uuid != null)
+ if (other.uuid != null) {
return false;
- } else if (!uuid.equals(other.uuid))
+ }
+ } else if (!uuid.equals(other.uuid)) {
return false;
+ }
if (vendorUuid == null) {
- if (other.vendorUuid != null)
+ if (other.vendorUuid != null) {
return false;
- } else if (!vendorUuid.equals(other.vendorUuid))
+ }
+ } else if (!vendorUuid.equals(other.vendorUuid)) {
return false;
- if (!Arrays.equals(data, other.data))
+ }
+ if (!Arrays.equals(data, other.data)) {
return false;
- if (version != other.version)
+ }
+ if (version != other.version) {
return false;
+ }
return true;
}
}
- /*****************************************************************************
+ /**
* A Keyphrase describes a key phrase that can be detected by a
* {@link KeyphraseSoundModel}
- *
- * @hide
- ****************************************************************************/
- public static class Keyphrase implements Parcelable {
+ */
+ public static final class Keyphrase implements Parcelable {
/** Unique identifier for this keyphrase */
- @UnsupportedAppUsage
public final int id;
- /** Recognition modes supported for this key phrase in the model */
- @UnsupportedAppUsage
+ /**
+ * Recognition modes supported for this key phrase in the model
+ *
+ * @see #RECOGNITION_MODE_VOICE_TRIGGER
+ * @see #RECOGNITION_MODE_USER_IDENTIFICATION
+ * @see #RECOGNITION_MODE_USER_AUTHENTICATION
+ * @see #RECOGNITION_MODE_GENERIC
+ */
+ @RecognitionModes
public final int recognitionModes;
- /** Locale of the keyphrase. JAVA Locale string e.g en_US */
- @UnsupportedAppUsage
+ /** Locale of the keyphrase. */
@NonNull
- public final String locale;
+ public final Locale locale;
/** Key phrase text */
- @UnsupportedAppUsage
@NonNull
public final String text;
- /** Users this key phrase has been trained for. countains sound trigger specific user IDs
- * derived from system user IDs {@link android.os.UserHandle#getIdentifier()}. */
- @UnsupportedAppUsage
+ /**
+ * Users this key phrase has been trained for. countains sound trigger specific user IDs
+ * derived from system user IDs {@link android.os.UserHandle#getIdentifier()}.
+ */
@NonNull
public final int[] users;
- @UnsupportedAppUsage
- public Keyphrase(int id, int recognitionModes, @NonNull String locale, @NonNull String text,
- @Nullable int[] users) {
+ /**
+ * Constructor for Keyphrase describes a key phrase that can be detected by a
+ * {@link KeyphraseSoundModel}
+ *
+ * @param id Unique keyphrase identifier for this keyphrase
+ * @param recognitionModes Bit field representation of recognition modes this keyphrase
+ * supports
+ * @param locale Locale of the keyphrase
+ * @param text Key phrase text
+ * @param users Users this key phrase has been trained for.
+ */
+ public Keyphrase(int id, @RecognitionModes int recognitionModes, @NonNull Locale locale,
+ @NonNull String text, @Nullable int[] users) {
this.id = id;
this.recognitionModes = recognitionModes;
this.locale = requireNonNull(locale);
@@ -404,21 +439,27 @@
this.users = users != null ? users : new int[0];
}
- public static final @android.annotation.NonNull Parcelable.Creator<Keyphrase> CREATOR
- = new Parcelable.Creator<Keyphrase>() {
- public Keyphrase createFromParcel(Parcel in) {
- return Keyphrase.fromParcel(in);
+ public static final @NonNull Parcelable.Creator<Keyphrase> CREATOR =
+ new Parcelable.Creator<Keyphrase>() {
+ @NonNull
+ public Keyphrase createFromParcel(@NonNull Parcel in) {
+ return Keyphrase.readFromParcel(in);
}
+ @NonNull
public Keyphrase[] newArray(int size) {
return new Keyphrase[size];
}
};
- private static Keyphrase fromParcel(Parcel in) {
+ /**
+ * Read from Parcel to generate keyphrase
+ */
+ @NonNull
+ public static Keyphrase readFromParcel(@NonNull Parcel in) {
int id = in.readInt();
int recognitionModes = in.readInt();
- String locale = in.readString();
+ Locale locale = Locale.forLanguageTag(in.readString());
String text = in.readString();
int[] users = null;
int numUsers = in.readInt();
@@ -430,10 +471,10 @@
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(id);
dest.writeInt(recognitionModes);
- dest.writeString(locale);
+ dest.writeString(locale.toLanguageTag());
dest.writeString(text);
if (users != null) {
dest.writeInt(users.length);
@@ -443,6 +484,7 @@
}
}
+ /** @hide */
@Override
public int describeContents() {
return 0;
@@ -462,49 +504,57 @@
@Override
public boolean equals(Object obj) {
- if (this == obj)
+ if (this == obj) {
return true;
- if (obj == null)
+ }
+ if (obj == null) {
return false;
- if (getClass() != obj.getClass())
+ }
+ if (getClass() != obj.getClass()) {
return false;
+ }
Keyphrase other = (Keyphrase) obj;
if (text == null) {
- if (other.text != null)
+ if (other.text != null) {
return false;
- } else if (!text.equals(other.text))
+ }
+ } else if (!text.equals(other.text)) {
return false;
- if (id != other.id)
+ }
+ if (id != other.id) {
return false;
+ }
if (locale == null) {
- if (other.locale != null)
+ if (other.locale != null) {
return false;
- } else if (!locale.equals(other.locale))
+ }
+ } else if (!locale.equals(other.locale)) {
return false;
- if (recognitionModes != other.recognitionModes)
+ }
+ if (recognitionModes != other.recognitionModes) {
return false;
- if (!Arrays.equals(users, other.users))
+ }
+ if (!Arrays.equals(users, other.users)) {
return false;
+ }
return true;
}
@Override
public String toString() {
- return "Keyphrase [id=" + id + ", recognitionModes=" + recognitionModes + ", locale="
- + locale + ", text=" + text + ", users=" + Arrays.toString(users) + "]";
+ return "Keyphrase [id=" + id + ", recognitionModes=" + recognitionModes
+ + ", locale=" + locale.toLanguageTag() + ", text=" + text
+ + ", users=" + Arrays.toString(users) + "]";
}
}
- /*****************************************************************************
+ /**
* A KeyphraseSoundModel is a specialized {@link SoundModel} for key phrases.
* It contains data needed by the hardware to detect a certain number of key phrases
* and the list of corresponding {@link Keyphrase} descriptors.
- *
- * @hide
- ****************************************************************************/
- public static class KeyphraseSoundModel extends SoundModel implements Parcelable {
+ */
+ public static final class KeyphraseSoundModel extends SoundModel implements Parcelable {
/** Key phrases in this sound model */
- @UnsupportedAppUsage
@NonNull
public final Keyphrase[] keyphrases; // keyword phrases in model
@@ -515,24 +565,29 @@
this.keyphrases = keyphrases != null ? keyphrases : new Keyphrase[0];
}
- @UnsupportedAppUsage
public KeyphraseSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
@Nullable byte[] data, @Nullable Keyphrase[] keyphrases) {
this(uuid, vendorUuid, data, keyphrases, -1);
}
- public static final @android.annotation.NonNull Parcelable.Creator<KeyphraseSoundModel> CREATOR
- = new Parcelable.Creator<KeyphraseSoundModel>() {
- public KeyphraseSoundModel createFromParcel(Parcel in) {
- return KeyphraseSoundModel.fromParcel(in);
+ public static final @NonNull Parcelable.Creator<KeyphraseSoundModel> CREATOR =
+ new Parcelable.Creator<KeyphraseSoundModel>() {
+ @NonNull
+ public KeyphraseSoundModel createFromParcel(@NonNull Parcel in) {
+ return KeyphraseSoundModel.readFromParcel(in);
}
+ @NonNull
public KeyphraseSoundModel[] newArray(int size) {
return new KeyphraseSoundModel[size];
}
};
- private static KeyphraseSoundModel fromParcel(Parcel in) {
+ /**
+ * Read from Parcel to generate KeyphraseSoundModel
+ */
+ @NonNull
+ public static KeyphraseSoundModel readFromParcel(@NonNull Parcel in) {
UUID uuid = UUID.fromString(in.readString());
UUID vendorUuid = null;
int length = in.readInt();
@@ -545,13 +600,14 @@
return new KeyphraseSoundModel(uuid, vendorUuid, data, keyphrases, version);
}
+ /** @hide */
@Override
public int describeContents() {
return 0;
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(uuid.toString());
if (vendorUuid == null) {
dest.writeInt(-1);
@@ -583,15 +639,19 @@
@Override
public boolean equals(Object obj) {
- if (this == obj)
+ if (this == obj) {
return true;
- if (!super.equals(obj))
+ }
+ if (!super.equals(obj)) {
return false;
- if (!(obj instanceof KeyphraseSoundModel))
+ }
+ if (!(obj instanceof KeyphraseSoundModel)) {
return false;
+ }
KeyphraseSoundModel other = (KeyphraseSoundModel) obj;
- if (!Arrays.equals(keyphrases, other.keyphrases))
+ if (!Arrays.equals(keyphrases, other.keyphrases)) {
return false;
+ }
return true;
}
}
@@ -760,31 +820,32 @@
}
/**
- * Modes for key phrase recognition
+ * Modes for key phrase recognition
+ * @hide
*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "RECOGNITION_MODE_" }, value = {
+ RECOGNITION_MODE_VOICE_TRIGGER,
+ RECOGNITION_MODE_USER_IDENTIFICATION,
+ RECOGNITION_MODE_USER_AUTHENTICATION,
+ RECOGNITION_MODE_GENERIC
+ })
+ public @interface RecognitionModes {}
/**
- * Simple recognition of the key phrase
- *
- * @hide
+ * Trigger on recognition of a key phrase
*/
public static final int RECOGNITION_MODE_VOICE_TRIGGER = 0x1;
/**
* Trigger only if one user is identified
- *
- * @hide
*/
public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 0x2;
/**
* Trigger only if one user is authenticated
- *
- * @hide
*/
public static final int RECOGNITION_MODE_USER_AUTHENTICATION = 0x4;
/**
* Generic (non-speech) recognition.
- *
- * @hide
*/
public static final int RECOGNITION_MODE_GENERIC = 0x8;
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index c8dbd16..e32865e 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -126,6 +126,9 @@
/* Gets the current screen unlocked functions. */
long getScreenUnlockedFunctions();
+ /* Resets the USB gadget. */
+ void resetUsbGadget();
+
/* Get the functionfs control handle for the given function. Usb
* descriptors will already be written, and the handle will be
* ready to use.
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 67fdda3..827353b 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -794,6 +794,25 @@
}
/**
+ * Resets the USB Gadget.
+ * <p>
+ * Performs USB data stack reset through USB Gadget HAL.
+ * It will force USB data connection reset. The connection will disconnect and reconnect.
+ * </p>
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ public void resetUsbGadget() {
+ try {
+ mService.resetUsbGadget();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns a list of physical USB ports on the device.
* <p>
* This list is guaranteed to contain all dual-role USB Type C ports but it might
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 81a0d62..92047dc 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -20,8 +20,8 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
+import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ONLY_DRAW_BOTTOM_BAR_BACKGROUND;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -1240,16 +1240,16 @@
Context.LAYOUT_INFLATER_SERVICE);
mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
- mWindow.getWindow().setFitWindowInsetsTypes(WindowInsets.Type.systemBars());
- mWindow.getWindow().addPrivateFlags(PRIVATE_FLAG_ONLY_DRAW_BOTTOM_BAR_BACKGROUND);
+ mWindow.getWindow().getAttributes().setFitInsetsTypes(WindowInsets.Type.statusBars());
+
+ // IME layout should always be inset by navigation bar, no matter it's current visibility.
mWindow.getWindow().getDecorView().setOnApplyWindowInsetsListener(
(v, insets) -> v.onApplyWindowInsets(
- new WindowInsets.Builder(insets).setSystemWindowInsets(
- android.graphics.Insets.of(
- insets.getSystemWindowInsetLeft(),
- insets.getSystemWindowInsetTop(),
- insets.getSystemWindowInsetRight(),
- insets.getStableInsetBottom())).build()));
+ new WindowInsets.Builder(insets).setInsets(
+ navigationBars(),
+ insets.getInsetsIgnoringVisibility(navigationBars()))
+ .build()));
+
// For ColorView in DecorView to work, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS needs to be set
// by default (but IME developers can opt this out later if they want a new behavior).
mWindow.getWindow().setFlags(
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.aidl b/core/java/android/net/ConnectivityDiagnosticsManager.aidl
new file mode 100644
index 0000000..82ba0ca
--- /dev/null
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.aidl
@@ -0,0 +1,21 @@
+/**
+ *
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+parcelable ConnectivityDiagnosticsManager.ConnectivityReport;
+parcelable ConnectivityDiagnosticsManager.DataStallReport;
\ No newline at end of file
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index 6afdb5e..a6f9b96 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -18,10 +18,18 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.PersistableBundle;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -47,38 +55,47 @@
* </ul>
*/
public class ConnectivityDiagnosticsManager {
- public static final int DETECTION_METHOD_DNS_EVENTS = 1;
- public static final int DETECTION_METHOD_TCP_METRICS = 2;
+ private final Context mContext;
+ private final IConnectivityManager mService;
/** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(
- prefix = {"DETECTION_METHOD_"},
- value = {DETECTION_METHOD_DNS_EVENTS, DETECTION_METHOD_TCP_METRICS})
- public @interface DetectionMethod {}
+ public ConnectivityDiagnosticsManager(Context context, IConnectivityManager service) {
+ mContext = Preconditions.checkNotNull(context, "missing context");
+ mService = Preconditions.checkNotNull(service, "missing IConnectivityManager");
+ }
/** @hide */
- public ConnectivityDiagnosticsManager() {}
+ @VisibleForTesting
+ public static boolean persistableBundleEquals(
+ @Nullable PersistableBundle a, @Nullable PersistableBundle b) {
+ if (a == b) return true;
+ if (a == null || b == null) return false;
+ if (!Objects.equals(a.keySet(), b.keySet())) return false;
+ for (String key : a.keySet()) {
+ if (!Objects.equals(a.get(key), b.get(key))) return false;
+ }
+ return true;
+ }
/** Class that includes connectivity information for a specific Network at a specific time. */
- public static class ConnectivityReport {
+ public static final class ConnectivityReport implements Parcelable {
/** The Network for which this ConnectivityReport applied */
- @NonNull public final Network network;
+ @NonNull private final Network mNetwork;
/**
* The timestamp for the report. The timestamp is taken from {@link
* System#currentTimeMillis}.
*/
- public final long reportTimestamp;
+ private final long mReportTimestamp;
/** LinkProperties available on the Network at the reported timestamp */
- @NonNull public final LinkProperties linkProperties;
+ @NonNull private final LinkProperties mLinkProperties;
/** NetworkCapabilities available on the Network at the reported timestamp */
- @NonNull public final NetworkCapabilities networkCapabilities;
+ @NonNull private final NetworkCapabilities mNetworkCapabilities;
/** PersistableBundle that may contain additional info about the report */
- @NonNull public final PersistableBundle additionalInfo;
+ @NonNull private final PersistableBundle mAdditionalInfo;
/**
* Constructor for ConnectivityReport.
@@ -101,30 +118,148 @@
@NonNull LinkProperties linkProperties,
@NonNull NetworkCapabilities networkCapabilities,
@NonNull PersistableBundle additionalInfo) {
- this.network = network;
- this.reportTimestamp = reportTimestamp;
- this.linkProperties = linkProperties;
- this.networkCapabilities = networkCapabilities;
- this.additionalInfo = additionalInfo;
+ mNetwork = network;
+ mReportTimestamp = reportTimestamp;
+ mLinkProperties = linkProperties;
+ mNetworkCapabilities = networkCapabilities;
+ mAdditionalInfo = additionalInfo;
}
+
+ /**
+ * Returns the Network for this ConnectivityReport.
+ *
+ * @return The Network for which this ConnectivityReport applied
+ */
+ @NonNull
+ public Network getNetwork() {
+ return mNetwork;
+ }
+
+ /**
+ * Returns the epoch timestamp (milliseconds) for when this report was taken.
+ *
+ * @return The timestamp for the report. Taken from {@link System#currentTimeMillis}.
+ */
+ public long getReportTimestamp() {
+ return mReportTimestamp;
+ }
+
+ /**
+ * Returns the LinkProperties available when this report was taken.
+ *
+ * @return LinkProperties available on the Network at the reported timestamp
+ */
+ @NonNull
+ public LinkProperties getLinkProperties() {
+ return new LinkProperties(mLinkProperties);
+ }
+
+ /**
+ * Returns the NetworkCapabilities when this report was taken.
+ *
+ * @return NetworkCapabilities available on the Network at the reported timestamp
+ */
+ @NonNull
+ public NetworkCapabilities getNetworkCapabilities() {
+ return new NetworkCapabilities(mNetworkCapabilities);
+ }
+
+ /**
+ * Returns a PersistableBundle with additional info for this report.
+ *
+ * @return PersistableBundle that may contain additional info about the report
+ */
+ @NonNull
+ public PersistableBundle getAdditionalInfo() {
+ return new PersistableBundle(mAdditionalInfo);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ConnectivityReport)) return false;
+ final ConnectivityReport that = (ConnectivityReport) o;
+
+ // PersistableBundle is optimized to avoid unparcelling data unless fields are
+ // referenced. Because of this, use {@link ConnectivityDiagnosticsManager#equals} over
+ // {@link PersistableBundle#kindofEquals}.
+ return mReportTimestamp == that.mReportTimestamp
+ && mNetwork.equals(that.mNetwork)
+ && mLinkProperties.equals(that.mLinkProperties)
+ && mNetworkCapabilities.equals(that.mNetworkCapabilities)
+ && persistableBundleEquals(mAdditionalInfo, that.mAdditionalInfo);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mNetwork,
+ mReportTimestamp,
+ mLinkProperties,
+ mNetworkCapabilities,
+ mAdditionalInfo);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mNetwork, flags);
+ dest.writeLong(mReportTimestamp);
+ dest.writeParcelable(mLinkProperties, flags);
+ dest.writeParcelable(mNetworkCapabilities, flags);
+ dest.writeParcelable(mAdditionalInfo, flags);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final @NonNull Creator<ConnectivityReport> CREATOR =
+ new Creator<>() {
+ public ConnectivityReport createFromParcel(Parcel in) {
+ return new ConnectivityReport(
+ in.readParcelable(null),
+ in.readLong(),
+ in.readParcelable(null),
+ in.readParcelable(null),
+ in.readParcelable(null));
+ }
+
+ public ConnectivityReport[] newArray(int size) {
+ return new ConnectivityReport[size];
+ }
+ };
}
/** Class that includes information for a suspected data stall on a specific Network */
- public static class DataStallReport {
+ public static final class DataStallReport implements Parcelable {
+ public static final int DETECTION_METHOD_DNS_EVENTS = 1;
+ public static final int DETECTION_METHOD_TCP_METRICS = 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"DETECTION_METHOD_"},
+ value = {DETECTION_METHOD_DNS_EVENTS, DETECTION_METHOD_TCP_METRICS})
+ public @interface DetectionMethod {}
+
/** The Network for which this DataStallReport applied */
- @NonNull public final Network network;
+ @NonNull private final Network mNetwork;
/**
* The timestamp for the report. The timestamp is taken from {@link
* System#currentTimeMillis}.
*/
- public final long reportTimestamp;
+ private long mReportTimestamp;
/** The detection method used to identify the suspected data stall */
- @DetectionMethod public final int detectionMethod;
+ @DetectionMethod private final int mDetectionMethod;
/** PersistableBundle that may contain additional information on the suspected data stall */
- @NonNull public final PersistableBundle stallDetails;
+ @NonNull private final PersistableBundle mStallDetails;
/**
* Constructor for DataStallReport.
@@ -143,11 +278,101 @@
long reportTimestamp,
@DetectionMethod int detectionMethod,
@NonNull PersistableBundle stallDetails) {
- this.network = network;
- this.reportTimestamp = reportTimestamp;
- this.detectionMethod = detectionMethod;
- this.stallDetails = stallDetails;
+ mNetwork = network;
+ mReportTimestamp = reportTimestamp;
+ mDetectionMethod = detectionMethod;
+ mStallDetails = stallDetails;
}
+
+ /**
+ * Returns the Network for this DataStallReport.
+ *
+ * @return The Network for which this DataStallReport applied
+ */
+ @NonNull
+ public Network getNetwork() {
+ return mNetwork;
+ }
+
+ /**
+ * Returns the epoch timestamp (milliseconds) for when this report was taken.
+ *
+ * @return The timestamp for the report. Taken from {@link System#currentTimeMillis}.
+ */
+ public long getReportTimestamp() {
+ return mReportTimestamp;
+ }
+
+ /**
+ * Returns the detection method used to identify this suspected data stall.
+ *
+ * @return The detection method used to identify the suspected data stall
+ */
+ public int getDetectionMethod() {
+ return mDetectionMethod;
+ }
+
+ /**
+ * Returns a PersistableBundle with additional info for this report.
+ *
+ * @return PersistableBundle that may contain additional information on the suspected data
+ * stall
+ */
+ @NonNull
+ public PersistableBundle getStallDetails() {
+ return new PersistableBundle(mStallDetails);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (!(o instanceof DataStallReport)) return false;
+ final DataStallReport that = (DataStallReport) o;
+
+ // PersistableBundle is optimized to avoid unparcelling data unless fields are
+ // referenced. Because of this, use {@link ConnectivityDiagnosticsManager#equals} over
+ // {@link PersistableBundle#kindofEquals}.
+ return mReportTimestamp == that.mReportTimestamp
+ && mDetectionMethod == that.mDetectionMethod
+ && mNetwork.equals(that.mNetwork)
+ && persistableBundleEquals(mStallDetails, that.mStallDetails);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mNetwork, mReportTimestamp, mDetectionMethod, mStallDetails);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mNetwork, flags);
+ dest.writeLong(mReportTimestamp);
+ dest.writeInt(mDetectionMethod);
+ dest.writeParcelable(mStallDetails, flags);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final @NonNull Creator<DataStallReport> CREATOR =
+ new Creator<DataStallReport>() {
+ public DataStallReport createFromParcel(Parcel in) {
+ return new DataStallReport(
+ in.readParcelable(null),
+ in.readLong(),
+ in.readInt(),
+ in.readParcelable(null));
+ }
+
+ public DataStallReport[] newArray(int size) {
+ return new DataStallReport[size];
+ }
+ };
}
/**
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 8ba3131..753e754 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -33,6 +33,7 @@
import android.content.Intent;
import android.net.IpSecManager.UdpEncapsulationSocket;
import android.net.SocketKeepalive.Callback;
+import android.net.TetheringManager.TetheringEventCallback;
import android.os.Binder;
import android.os.Build;
import android.os.Build.VERSION_CODES;
@@ -58,6 +59,7 @@
import android.util.Log;
import android.util.SparseIntArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.internal.util.Protocol;
@@ -75,6 +77,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -484,34 +487,35 @@
* enable if any.
* @hide
*/
- public static final String EXTRA_ADD_TETHER_TYPE = TetheringManager.EXTRA_ADD_TETHER_TYPE;
+ public static final String EXTRA_ADD_TETHER_TYPE = TetheringConstants.EXTRA_ADD_TETHER_TYPE;
/**
* Extra used for communicating with the TetherService. Includes the type of tethering for
* which to cancel provisioning.
* @hide
*/
- public static final String EXTRA_REM_TETHER_TYPE = TetheringManager.EXTRA_REM_TETHER_TYPE;
+ public static final String EXTRA_REM_TETHER_TYPE = TetheringConstants.EXTRA_REM_TETHER_TYPE;
/**
* Extra used for communicating with the TetherService. True to schedule a recheck of tether
* provisioning.
* @hide
*/
- public static final String EXTRA_SET_ALARM = TetheringManager.EXTRA_SET_ALARM;
+ public static final String EXTRA_SET_ALARM = TetheringConstants.EXTRA_SET_ALARM;
/**
* Tells the TetherService to run a provision check now.
* @hide
*/
- public static final String EXTRA_RUN_PROVISION = TetheringManager.EXTRA_RUN_PROVISION;
+ public static final String EXTRA_RUN_PROVISION = TetheringConstants.EXTRA_RUN_PROVISION;
/**
* Extra used for communicating with the TetherService. Contains the {@link ResultReceiver}
* which will receive provisioning results. Can be left empty.
* @hide
*/
- public static final String EXTRA_PROVISION_CALLBACK = TetheringManager.EXTRA_PROVISION_CALLBACK;
+ public static final String EXTRA_PROVISION_CALLBACK =
+ TetheringConstants.EXTRA_PROVISION_CALLBACK;
/**
* The absence of a connection type.
@@ -2368,10 +2372,12 @@
*
* @return an array of 0 or more Strings of tetherable interface names.
*
+ * @deprecated Use {@link TetheringEventCallback#onTetherableInterfacesChanged(List)} instead.
* {@hide}
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@UnsupportedAppUsage
+ @Deprecated
public String[] getTetherableIfaces() {
return getTetheringManager().getTetherableIfaces();
}
@@ -2381,10 +2387,12 @@
*
* @return an array of 0 or more String of currently tethered interface names.
*
+ * @deprecated Use {@link TetheringEventCallback#onTetherableInterfacesChanged(List)} instead.
* {@hide}
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@UnsupportedAppUsage
+ @Deprecated
public String[] getTetheredIfaces() {
return getTetheringManager().getTetheredIfaces();
}
@@ -2400,10 +2408,12 @@
* @return an array of 0 or more String indicating the interface names
* which failed to tether.
*
+ * @deprecated Use {@link TetheringEventCallback#onError(String, int)} instead.
* {@hide}
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@UnsupportedAppUsage
+ @Deprecated
public String[] getTetheringErroredIfaces() {
return getTetheringManager().getTetheringErroredIfaces();
}
@@ -2412,9 +2422,11 @@
* Get the set of tethered dhcp ranges.
*
* @return an array of 0 or more {@code String} of tethered dhcp ranges.
+ * @deprecated This API just return the default value which is not used in DhcpServer.
* {@hide}
*/
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ @Deprecated
public String[] getTetheredDhcpRanges() {
return getTetheringManager().getTetheredDhcpRanges();
}
@@ -2467,6 +2479,7 @@
* {@hide}
*/
@UnsupportedAppUsage
+ @Deprecated
public int untether(String iface) {
return getTetheringManager().untether(iface);
}
@@ -2487,6 +2500,7 @@
*
* @return a boolean - {@code true} indicating Tethering is supported.
*
+ * @deprecated Use {@link TetheringEventCallback#onTetheringSupported(boolean)} instead.
* {@hide}
*/
@SystemApi
@@ -2573,9 +2587,12 @@
* {@link ConnectivityManager.TETHERING_WIFI},
* {@link ConnectivityManager.TETHERING_USB}, or
* {@link ConnectivityManager.TETHERING_BLUETOOTH}.
+ *
+ * @deprecated Use {@link TetheringManager#stopTethering} instead.
* @hide
*/
@SystemApi
+ @Deprecated
@RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
public void stopTethering(int type) {
getTetheringManager().stopTethering(type);
@@ -2585,9 +2602,11 @@
* Callback for use with {@link registerTetheringEventCallback} to find out tethering
* upstream status.
*
- *@hide
+ * @deprecated Use {@line TetheringManager#OnTetheringEventCallback} instead.
+ * @hide
*/
@SystemApi
+ @Deprecated
public abstract static class OnTetheringEventCallback {
/**
@@ -2600,6 +2619,10 @@
public void onUpstreamChanged(@Nullable Network network) {}
}
+ @GuardedBy("mTetheringEventCallbacks")
+ private final ArrayMap<OnTetheringEventCallback, TetheringEventCallback>
+ mTetheringEventCallbacks = new ArrayMap<>();
+
/**
* Start listening to tethering change events. Any new added callback will receive the last
* tethering status right away. If callback is registered when tethering has no upstream or
@@ -2608,16 +2631,30 @@
*
* @param executor the executor on which callback will be invoked.
* @param callback the callback to be called when tethering has change events.
+ *
+ * @deprecated Use {@line TetheringManager#registerTetheringEventCallback} instead.
* @hide
*/
@SystemApi
+ @Deprecated
@RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
public void registerTetheringEventCallback(
@NonNull @CallbackExecutor Executor executor,
@NonNull final OnTetheringEventCallback callback) {
Preconditions.checkNotNull(callback, "OnTetheringEventCallback cannot be null.");
- getTetheringManager().registerTetheringEventCallback(executor, callback);
+ final TetheringEventCallback tetherCallback =
+ new TetheringEventCallback() {
+ @Override
+ public void onUpstreamChanged(@Nullable Network network) {
+ callback.onUpstreamChanged(network);
+ }
+ };
+
+ synchronized (mTetheringEventCallbacks) {
+ mTetheringEventCallbacks.put(callback, tetherCallback);
+ getTetheringManager().registerTetheringEventCallback(executor, tetherCallback);
+ }
}
/**
@@ -2625,13 +2662,21 @@
* {@link #registerTetheringEventCallback}.
*
* @param callback previously registered callback.
+ *
+ * @deprecated Use {@link TetheringManager#unregisterTetheringEventCallback} instead.
* @hide
*/
@SystemApi
+ @Deprecated
@RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
public void unregisterTetheringEventCallback(
@NonNull final OnTetheringEventCallback callback) {
- getTetheringManager().unregisterTetheringEventCallback(callback);
+ Objects.requireNonNull(callback, "The callback must be non-null");
+ synchronized (mTetheringEventCallbacks) {
+ final TetheringEventCallback tetherCallback =
+ mTetheringEventCallbacks.remove(callback);
+ getTetheringManager().unregisterTetheringEventCallback(tetherCallback);
+ }
}
@@ -2643,10 +2688,12 @@
* @return an array of 0 or more regular expression Strings defining
* what interfaces are considered tetherable usb interfaces.
*
+ * @deprecated Use {@link TetheringEventCallback#onTetherableInterfaceRegexpsChanged} instead.
* {@hide}
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@UnsupportedAppUsage
+ @Deprecated
public String[] getTetherableUsbRegexs() {
return getTetheringManager().getTetherableUsbRegexs();
}
@@ -2659,10 +2706,12 @@
* @return an array of 0 or more regular expression Strings defining
* what interfaces are considered tetherable wifi interfaces.
*
+ * @deprecated Use {@link TetheringEventCallback#onTetherableInterfaceRegexpsChanged} instead.
* {@hide}
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@UnsupportedAppUsage
+ @Deprecated
public String[] getTetherableWifiRegexs() {
return getTetheringManager().getTetherableWifiRegexs();
}
@@ -2675,10 +2724,13 @@
* @return an array of 0 or more regular expression Strings defining
* what interfaces are considered tetherable bluetooth interfaces.
*
+ * @deprecated Use {@link TetheringEventCallback#onTetherableInterfaceRegexpsChanged(
+ *TetheringManager.TetheringInterfaceRegexps)} instead.
* {@hide}
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@UnsupportedAppUsage
+ @Deprecated
public String[] getTetherableBluetoothRegexs() {
return getTetheringManager().getTetherableBluetoothRegexs();
}
@@ -2705,37 +2757,104 @@
return getTetheringManager().setUsbTethering(enable);
}
- /** {@hide} */
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_NO_ERROR}.
+ * {@hide}
+ */
@SystemApi
- public static final int TETHER_ERROR_NO_ERROR = 0;
- /** {@hide} */
- public static final int TETHER_ERROR_UNKNOWN_IFACE = 1;
- /** {@hide} */
- public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2;
- /** {@hide} */
- public static final int TETHER_ERROR_UNSUPPORTED = 3;
- /** {@hide} */
- public static final int TETHER_ERROR_UNAVAIL_IFACE = 4;
- /** {@hide} */
- public static final int TETHER_ERROR_MASTER_ERROR = 5;
- /** {@hide} */
- public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6;
- /** {@hide} */
- public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7;
- /** {@hide} */
- public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8;
- /** {@hide} */
- public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9;
- /** {@hide} */
- public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10;
- /** {@hide} */
+ @Deprecated
+ public static final int TETHER_ERROR_NO_ERROR = TetheringManager.TETHER_ERROR_NO_ERROR;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_UNKNOWN_IFACE}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_UNKNOWN_IFACE =
+ TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_SERVICE_UNAVAIL}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_SERVICE_UNAVAIL =
+ TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_UNSUPPORTED}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_UNSUPPORTED = TetheringManager.TETHER_ERROR_UNSUPPORTED;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_UNAVAIL_IFACE}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_UNAVAIL_IFACE =
+ TetheringManager.TETHER_ERROR_UNAVAIL_IFACE;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_MASTER_ERROR}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_MASTER_ERROR = TetheringManager.TETHER_ERROR_MASTER_ERROR;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_TETHER_IFACE_ERROR}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_TETHER_IFACE_ERROR =
+ TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_UNTETHER_IFACE_ERROR}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR =
+ TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_ENABLE_NAT_ERROR}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_ENABLE_NAT_ERROR =
+ TetheringManager.TETHER_ERROR_ENABLE_NAT_ERROR;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_DISABLE_NAT_ERROR}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_DISABLE_NAT_ERROR =
+ TetheringManager.TETHER_ERROR_DISABLE_NAT_ERROR;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_IFACE_CFG_ERROR}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_IFACE_CFG_ERROR =
+ TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_PROVISION_FAILED}.
+ * {@hide}
+ */
@SystemApi
- public static final int TETHER_ERROR_PROVISION_FAILED = 11;
- /** {@hide} */
- public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12;
- /** {@hide} */
+ @Deprecated
+ public static final int TETHER_ERROR_PROVISION_FAILED =
+ TetheringManager.TETHER_ERROR_PROVISION_FAILED;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_DHCPSERVER_ERROR}.
+ * {@hide}
+ */
+ @Deprecated
+ public static final int TETHER_ERROR_DHCPSERVER_ERROR =
+ TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR;
+ /**
+ * @deprecated Use {@link TetheringManager#TETHER_ERROR_ENTITLEMENT_UNKNOWN}.
+ * {@hide}
+ */
@SystemApi
- public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13;
+ @Deprecated
+ public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN =
+ TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
/**
* Get a more detailed error code after a Tethering or Untethering
@@ -2745,10 +2864,12 @@
* @return error The error code of the last error tethering or untethering the named
* interface
*
+ * @deprecated Use {@link TetheringEventCallback#onError(String, int)} instead.
* {@hide}
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@UnsupportedAppUsage
+ @Deprecated
public int getLastTetherError(String iface) {
return getTetheringManager().getLastTetherError(iface);
}
@@ -2766,9 +2887,12 @@
/**
* Callback for use with {@link #getLatestTetheringEntitlementResult} to find out whether
* entitlement succeeded.
+ *
+ * @deprecated Use {@link TetheringManager#OnTetheringEntitlementResultListener} instead.
* @hide
*/
@SystemApi
+ @Deprecated
public interface OnTetheringEntitlementResultListener {
/**
* Called to notify entitlement result.
@@ -2798,9 +2922,11 @@
* @param listener an {@link OnTetheringEntitlementResultListener} which will be called to
* notify the caller of the result of entitlement check. The listener may be called zero
* or one time.
+ * @deprecated Use {@link TetheringManager#requestLatestTetheringEntitlementResult} instead.
* {@hide}
*/
@SystemApi
+ @Deprecated
@RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
public void getLatestTetheringEntitlementResult(int type, boolean showEntitlementUi,
@NonNull @CallbackExecutor Executor executor,
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index bf8b38f..8d9f0d0 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -19,6 +19,7 @@
import static android.system.OsConstants.IFA_F_DADFAILED;
import static android.system.OsConstants.IFA_F_DEPRECATED;
import static android.system.OsConstants.IFA_F_OPTIMISTIC;
+import static android.system.OsConstants.IFA_F_PERMANENT;
import static android.system.OsConstants.IFA_F_TENTATIVE;
import static android.system.OsConstants.RT_SCOPE_HOST;
import static android.system.OsConstants.RT_SCOPE_LINK;
@@ -34,6 +35,7 @@
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.SystemClock;
import android.util.Pair;
import java.net.Inet4Address;
@@ -41,6 +43,7 @@
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.UnknownHostException;
+import java.util.Objects;
/**
* Identifies an IP address on a network link.
@@ -58,6 +61,21 @@
* </ul>
*/
public class LinkAddress implements Parcelable {
+
+ /**
+ * Indicates the deprecation or expiration time is unknown
+ * @hide
+ */
+ @SystemApi
+ public static final long LIFETIME_UNKNOWN = -1;
+
+ /**
+ * Indicates this address is permanent.
+ * @hide
+ */
+ @SystemApi
+ public static final long LIFETIME_PERMANENT = Long.MAX_VALUE;
+
/**
* IPv4 or IPv6 address.
*/
@@ -71,7 +89,9 @@
private int prefixLength;
/**
- * Address flags. A bitmask of IFA_F_* values.
+ * Address flags. A bitmask of {@code IFA_F_*} values. Note that {@link #getFlags()} may not
+ * return these exact values. For example, it may set or clear the {@code IFA_F_DEPRECATED}
+ * flag depending on the current preferred lifetime.
*/
private int flags;
@@ -81,6 +101,23 @@
private int scope;
/**
+ * The time, as reported by {@link SystemClock#elapsedRealtime}, when this LinkAddress will be
+ * or was deprecated. {@link #LIFETIME_UNKNOWN} indicates this information is not available. At
+ * the time existing connections can still use this address until it expires, but new
+ * connections should use the new address. {@link #LIFETIME_PERMANENT} indicates this
+ * {@link LinkAddress} will never be deprecated.
+ */
+ private long deprecationTime;
+
+ /**
+ * The time, as reported by {@link SystemClock#elapsedRealtime}, when this {@link LinkAddress}
+ * will expire and be removed from the interface. {@link #LIFETIME_UNKNOWN} indicates this
+ * information is not available. {@link #LIFETIME_PERMANENT} indicates this {@link LinkAddress}
+ * will never expire.
+ */
+ private long expirationTime;
+
+ /**
* Utility function to determines the scope of a unicast address. Per RFC 4291 section 2.5 and
* RFC 6724 section 3.2.
* @hide
@@ -152,7 +189,8 @@
/**
* Utility function for the constructors.
*/
- private void init(InetAddress address, int prefixLength, int flags, int scope) {
+ private void init(InetAddress address, int prefixLength, int flags, int scope,
+ long deprecationTime, long expirationTime) {
if (address == null ||
address.isMulticastAddress() ||
prefixLength < 0 ||
@@ -161,15 +199,42 @@
throw new IllegalArgumentException("Bad LinkAddress params " + address +
"/" + prefixLength);
}
+
+ // deprecation time and expiration time must be both provided, or neither.
+ if ((deprecationTime == LIFETIME_UNKNOWN) != (expirationTime == LIFETIME_UNKNOWN)) {
+ throw new IllegalArgumentException(
+ "Must not specify only one of deprecation time and expiration time");
+ }
+
+ // deprecation time needs to be a positive value.
+ if (deprecationTime != LIFETIME_UNKNOWN && deprecationTime < 0) {
+ throw new IllegalArgumentException("invalid deprecation time " + deprecationTime);
+ }
+
+ // expiration time needs to be a positive value.
+ if (expirationTime != LIFETIME_UNKNOWN && expirationTime < 0) {
+ throw new IllegalArgumentException("invalid expiration time " + expirationTime);
+ }
+
+ // expiration time can't be earlier than deprecation time
+ if (deprecationTime != LIFETIME_UNKNOWN && expirationTime != LIFETIME_UNKNOWN
+ && expirationTime < deprecationTime) {
+ throw new IllegalArgumentException("expiration earlier than deprecation ("
+ + deprecationTime + ", " + expirationTime + ")");
+ }
+
this.address = address;
this.prefixLength = prefixLength;
this.flags = flags;
this.scope = scope;
+ this.deprecationTime = deprecationTime;
+ this.expirationTime = expirationTime;
}
/**
* Constructs a new {@code LinkAddress} from an {@code InetAddress} and prefix length, with
* the specified flags and scope. Flags and scope are not checked for validity.
+ *
* @param address The IP address.
* @param prefixLength The prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6).
* @param flags A bitmask of {@code IFA_F_*} values representing properties of the address.
@@ -181,7 +246,39 @@
@TestApi
public LinkAddress(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength,
int flags, int scope) {
- init(address, prefixLength, flags, scope);
+ init(address, prefixLength, flags, scope, LIFETIME_UNKNOWN, LIFETIME_UNKNOWN);
+ }
+
+ /**
+ * Constructs a new {@code LinkAddress} from an {@code InetAddress}, prefix length, with
+ * the specified flags, scope, deprecation time, and expiration time. Flags and scope are not
+ * checked for validity. The value of the {@code IFA_F_DEPRECATED} and {@code IFA_F_PERMANENT}
+ * flag will be adjusted based on the passed-in lifetimes.
+ *
+ * @param address The IP address.
+ * @param prefixLength The prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6).
+ * @param flags A bitmask of {@code IFA_F_*} values representing properties of the address.
+ * @param scope An integer defining the scope in which the address is unique (e.g.,
+ * {@link OsConstants#RT_SCOPE_LINK} or {@link OsConstants#RT_SCOPE_SITE}).
+ * @param deprecationTime The time, as reported by {@link SystemClock#elapsedRealtime}, when
+ * this {@link LinkAddress} will be or was deprecated.
+ * {@link #LIFETIME_UNKNOWN} indicates this information is not available.
+ * At the time existing connections can still use this address until it
+ * expires, but new connections should use the new address.
+ * {@link #LIFETIME_PERMANENT} indicates this {@link LinkAddress} will
+ * never be deprecated.
+ * @param expirationTime The time, as reported by {@link SystemClock#elapsedRealtime}, when this
+ * {@link LinkAddress} will expire and be removed from the interface.
+ * {@link #LIFETIME_UNKNOWN} indicates this information is not available.
+ * {@link #LIFETIME_PERMANENT} indicates this {@link LinkAddress} will
+ * never expire.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public LinkAddress(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength,
+ int flags, int scope, long deprecationTime, long expirationTime) {
+ init(address, prefixLength, flags, scope, deprecationTime, expirationTime);
}
/**
@@ -237,7 +334,7 @@
// This may throw an IllegalArgumentException; catching it is the caller's responsibility.
// TODO: consider rejecting mapped IPv4 addresses such as "::ffff:192.0.2.5/24".
Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(address);
- init(ipAndMask.first, ipAndMask.second, flags, scope);
+ init(ipAndMask.first, ipAndMask.second, flags, scope, LIFETIME_UNKNOWN, LIFETIME_UNKNOWN);
}
/**
@@ -265,10 +362,12 @@
return false;
}
LinkAddress linkAddress = (LinkAddress) obj;
- return this.address.equals(linkAddress.address) &&
- this.prefixLength == linkAddress.prefixLength &&
- this.flags == linkAddress.flags &&
- this.scope == linkAddress.scope;
+ return this.address.equals(linkAddress.address)
+ && this.prefixLength == linkAddress.prefixLength
+ && this.flags == linkAddress.flags
+ && this.scope == linkAddress.scope
+ && this.deprecationTime == linkAddress.deprecationTime
+ && this.expirationTime == linkAddress.expirationTime;
}
/**
@@ -276,7 +375,7 @@
*/
@Override
public int hashCode() {
- return address.hashCode() + 11 * prefixLength + 19 * flags + 43 * scope;
+ return Objects.hash(address, prefixLength, flags, scope, deprecationTime, expirationTime);
}
/**
@@ -329,6 +428,25 @@
* Returns the flags of this {@code LinkAddress}.
*/
public int getFlags() {
+ int flags = this.flags;
+ if (deprecationTime != LIFETIME_UNKNOWN) {
+ if (SystemClock.elapsedRealtime() >= deprecationTime) {
+ flags |= IFA_F_DEPRECATED;
+ } else {
+ // If deprecation time is in the future, or permanent.
+ flags &= ~IFA_F_DEPRECATED;
+ }
+ }
+
+ if (expirationTime == LIFETIME_PERMANENT) {
+ flags |= IFA_F_PERMANENT;
+ } else if (expirationTime != LIFETIME_UNKNOWN) {
+ // If we know this address expired or will expire in the future or, then this address
+ // should not be permanent.
+ flags &= ~IFA_F_PERMANENT;
+ }
+
+ // Do no touch the original flags. Return the adjusted flags here.
return flags;
}
@@ -340,7 +458,35 @@
}
/**
- * Returns true if this {@code LinkAddress} is global scope and preferred.
+ * @return The time that this address will be deprecated. At the time the existing connection
+ * can still use this address until it expires, but the new connection should use the new
+ * address. This is the EPOCH time in milliseconds. 0 indicates this information is not
+ * available.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public long getDeprecationTime() {
+ return deprecationTime;
+ }
+
+ /**
+ * @return The time that this address will expire and will be no longer valid. This is the EPOCH
+ * time in milliseconds. 0 indicates this information is not available.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public long getExpirationTime() {
+ return expirationTime;
+ }
+
+ /**
+ * Returns true if this {@code LinkAddress} is global scope and preferred (i.e., not currently
+ * deprecated).
+ *
* @hide
*/
@TestApi
@@ -352,6 +498,7 @@
* state has cleared either DAD has succeeded or failed, and both
* flags are cleared regardless).
*/
+ int flags = getFlags();
return (scope == RT_SCOPE_UNIVERSE
&& !isIpv6ULA()
&& (flags & (IFA_F_DADFAILED | IFA_F_DEPRECATED)) == 0L
@@ -373,6 +520,8 @@
dest.writeInt(prefixLength);
dest.writeInt(this.flags);
dest.writeInt(scope);
+ dest.writeLong(deprecationTime);
+ dest.writeLong(expirationTime);
}
/**
@@ -392,7 +541,10 @@
int prefixLength = in.readInt();
int flags = in.readInt();
int scope = in.readInt();
- return new LinkAddress(address, prefixLength, flags, scope);
+ long deprecationTime = in.readLong();
+ long expirationTime = in.readLong();
+ return new LinkAddress(address, prefixLength, flags, scope, deprecationTime,
+ expirationTime);
}
public LinkAddress[] newArray(int size) {
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 7cc78f7..7cc569a 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -304,7 +304,10 @@
private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
// The subtype can be changed with (TODO) setLegacySubtype, but it starts
// with the type and an empty description.
- return new NetworkInfo(config.legacyType, config.legacyType, config.legacyTypeName, "");
+ final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacyType,
+ config.legacyTypeName, "");
+ ni.setIsAvailable(true);
+ return ni;
}
/**
diff --git a/core/java/android/net/NetworkKey.java b/core/java/android/net/NetworkKey.java
index 47c08a4..4469d7d 100644
--- a/core/java/android/net/NetworkKey.java
+++ b/core/java/android/net/NetworkKey.java
@@ -73,13 +73,14 @@
/**
* Constructs a new NetworkKey for the given wifi {@link ScanResult}.
*
- * @return A new {@link NetworkKey} instance or <code>null</code> if the given
- * {@link ScanResult} instance is malformed.
+ * @return A new {@link NetworkKey} instance or <code>null</code> if the given
+ * {@link ScanResult} instance is malformed.
+ * @throws IllegalArgumentException
*/
@Nullable
- public static NetworkKey createFromScanResult(@Nullable ScanResult result) {
+ public static NetworkKey createFromScanResult(@NonNull ScanResult result) {
if (result == null) {
- return null;
+ throw new IllegalArgumentException("ScanResult cannot be null");
}
final String ssid = result.SSID;
if (TextUtils.isEmpty(ssid) || ssid.equals(WifiManager.UNKNOWN_SSID)) {
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index de962f8..01800c6 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -18,6 +18,10 @@
import static android.content.pm.PackageManager.GET_SIGNATURES;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.ActivityManager;
import android.compat.annotation.UnsupportedAppUsage;
@@ -38,24 +42,38 @@
import com.google.android.collect.Sets;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.time.ZonedDateTime;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
/**
* Manager for creating and modifying network policy rules.
*
- * {@hide}
+ * @hide
*/
@SystemService(Context.NETWORK_POLICY_SERVICE)
+@SystemApi
public class NetworkPolicyManager {
/* POLICY_* are masks and can be ORed, although currently they are not.*/
- /** No specific network policy, use system default. */
+ /**
+ * No specific network policy, use system default.
+ * @hide
+ */
public static final int POLICY_NONE = 0x0;
- /** Reject network usage on metered networks when application in background. */
+ /**
+ * Reject network usage on metered networks when application in background.
+ * @hide
+ */
public static final int POLICY_REJECT_METERED_BACKGROUND = 0x1;
- /** Allow metered network use in the background even when in data usage save mode. */
+ /**
+ * Allow metered network use in the background even when in data usage save mode.
+ * @hide
+ */
public static final int POLICY_ALLOW_METERED_BACKGROUND = 0x4;
/*
@@ -74,49 +92,102 @@
*
* See network-policy-restrictions.md for more info.
*/
- /** No specific rule was set */
+
+ /**
+ * No specific rule was set
+ * @hide
+ */
public static final int RULE_NONE = 0;
- /** Allow traffic on metered networks. */
+ /**
+ * Allow traffic on metered networks.
+ * @hide
+ */
public static final int RULE_ALLOW_METERED = 1 << 0;
- /** Temporarily allow traffic on metered networks because app is on foreground. */
+ /**
+ * Temporarily allow traffic on metered networks because app is on foreground.
+ * @hide
+ */
public static final int RULE_TEMPORARY_ALLOW_METERED = 1 << 1;
- /** Reject traffic on metered networks. */
+ /**
+ * Reject traffic on metered networks.
+ * @hide
+ */
public static final int RULE_REJECT_METERED = 1 << 2;
- /** Network traffic should be allowed on all networks (metered or non-metered), although
- * metered-network restrictions could still apply. */
+ /**
+ * Network traffic should be allowed on all networks (metered or non-metered), although
+ * metered-network restrictions could still apply.
+ * @hide
+ */
public static final int RULE_ALLOW_ALL = 1 << 5;
- /** Reject traffic on all networks. */
+ /**
+ * Reject traffic on all networks.
+ * @hide
+ */
public static final int RULE_REJECT_ALL = 1 << 6;
- /** Mask used to get the {@code RULE_xxx_METERED} rules */
+
+ /**
+ * Mask used to get the {@code RULE_xxx_METERED} rules
+ * @hide
+ */
public static final int MASK_METERED_NETWORKS = 0b00001111;
- /** Mask used to get the {@code RULE_xxx_ALL} rules */
+ /**
+ * Mask used to get the {@code RULE_xxx_ALL} rules
+ * @hide
+ */
public static final int MASK_ALL_NETWORKS = 0b11110000;
+ /** @hide */
public static final int FIREWALL_RULE_DEFAULT = 0;
-
+ /** @hide */
public static final String FIREWALL_CHAIN_NAME_NONE = "none";
+ /** @hide */
public static final String FIREWALL_CHAIN_NAME_DOZABLE = "dozable";
+ /** @hide */
public static final String FIREWALL_CHAIN_NAME_STANDBY = "standby";
+ /** @hide */
public static final String FIREWALL_CHAIN_NAME_POWERSAVE = "powersave";
private static final boolean ALLOW_PLATFORM_APP_POLICY = true;
+ /** @hide */
public static final int FOREGROUND_THRESHOLD_STATE =
ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
/**
* {@link Intent} extra that indicates which {@link NetworkTemplate} rule it
* applies to.
+ * @hide
*/
public static final String EXTRA_NETWORK_TEMPLATE = "android.net.NETWORK_TEMPLATE";
- public static final int OVERRIDE_UNMETERED = 1 << 0;
- public static final int OVERRIDE_CONGESTED = 1 << 1;
+ /**
+ * Mask used to check if an override value is marked as unmetered.
+ */
+ public static final int SUBSCRIPTION_OVERRIDE_UNMETERED = 1 << 0;
+
+ /**
+ * Mask used to check if an override value is marked as congested.
+ */
+ public static final int SUBSCRIPTION_OVERRIDE_CONGESTED = 1 << 1;
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "SUBSCRIPTION_OVERRIDE_" }, value = {
+ SUBSCRIPTION_OVERRIDE_UNMETERED,
+ SUBSCRIPTION_OVERRIDE_CONGESTED
+ })
+ public @interface SubscriptionOverrideMask {}
private final Context mContext;
@UnsupportedAppUsage
private INetworkPolicyManager mService;
+ private final Map<SubscriptionCallback, SubscriptionCallbackProxy>
+ mCallbackMap = new ConcurrentHashMap<>();
+
+ /** @hide */
public NetworkPolicyManager(Context context, INetworkPolicyManager service) {
if (service == null) {
throw new IllegalArgumentException("missing INetworkPolicyManager");
@@ -125,6 +196,7 @@
mService = service;
}
+ /** @hide */
@UnsupportedAppUsage
public static NetworkPolicyManager from(Context context) {
return (NetworkPolicyManager) context.getSystemService(Context.NETWORK_POLICY_SERVICE);
@@ -135,6 +207,7 @@
*
* @param policy should be {@link #POLICY_NONE} or any combination of {@code POLICY_} flags,
* although it is not validated.
+ * @hide
*/
@UnsupportedAppUsage
public void setUidPolicy(int uid, int policy) {
@@ -152,6 +225,7 @@
*
* @param policy should be {@link #POLICY_NONE} or any combination of {@code POLICY_} flags,
* although it is not validated.
+ * @hide
*/
public void addUidPolicy(int uid, int policy) {
try {
@@ -168,6 +242,7 @@
*
* @param policy should be {@link #POLICY_NONE} or any combination of {@code POLICY_} flags,
* although it is not validated.
+ * @hide
*/
public void removeUidPolicy(int uid, int policy) {
try {
@@ -177,6 +252,7 @@
}
}
+ /** @hide */
@UnsupportedAppUsage
public int getUidPolicy(int uid) {
try {
@@ -186,6 +262,7 @@
}
}
+ /** @hide */
@UnsupportedAppUsage
public int[] getUidsWithPolicy(int policy) {
try {
@@ -195,6 +272,7 @@
}
}
+ /** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public void registerListener(INetworkPolicyListener listener) {
try {
@@ -204,6 +282,7 @@
}
}
+ /** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public void unregisterListener(INetworkPolicyListener listener) {
try {
@@ -213,6 +292,36 @@
}
}
+ /** @hide */
+ @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY)
+ @SystemApi
+ public void registerSubscriptionCallback(@NonNull SubscriptionCallback callback) {
+ if (callback == null) {
+ throw new NullPointerException("Callback cannot be null.");
+ }
+
+ final SubscriptionCallbackProxy callbackProxy = new SubscriptionCallbackProxy(callback);
+ if (null != mCallbackMap.putIfAbsent(callback, callbackProxy)) {
+ throw new IllegalArgumentException("Callback is already registered.");
+ }
+ registerListener(callbackProxy);
+ }
+
+ /** @hide */
+ @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY)
+ @SystemApi
+ public void unregisterSubscriptionCallback(@NonNull SubscriptionCallback callback) {
+ if (callback == null) {
+ throw new NullPointerException("Callback cannot be null.");
+ }
+
+ final SubscriptionCallbackProxy callbackProxy = mCallbackMap.remove(callback);
+ if (callbackProxy == null) return;
+
+ unregisterListener(callbackProxy);
+ }
+
+ /** @hide */
public void setNetworkPolicies(NetworkPolicy[] policies) {
try {
mService.setNetworkPolicies(policies);
@@ -221,6 +330,7 @@
}
}
+ /** @hide */
@UnsupportedAppUsage
public NetworkPolicy[] getNetworkPolicies() {
try {
@@ -230,6 +340,7 @@
}
}
+ /** @hide */
@UnsupportedAppUsage
public void setRestrictBackground(boolean restrictBackground) {
try {
@@ -239,6 +350,7 @@
}
}
+ /** @hide */
@UnsupportedAppUsage
public boolean getRestrictBackground() {
try {
@@ -249,6 +361,62 @@
}
/**
+ * Override connections to be temporarily marked as either unmetered or congested,
+ * along with automatic timeouts if desired.
+ *
+ * @param subId the subscriber ID this override applies to.
+ * @param overrideMask the bitmask that specifies which of the overrides is being
+ * set or cleared.
+ * @param overrideValue the override values to set or clear.
+ * @param timeoutMillis the timeout after which the requested override will
+ * be automatically cleared, or {@code 0} to leave in the
+ * requested state until explicitly cleared, or the next reboot,
+ * whichever happens first
+ * @param callingPackage the name of the package making the call.
+ */
+ public void setSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask,
+ @SubscriptionOverrideMask int overrideValue, long timeoutMillis,
+ @NonNull String callingPackage) {
+ try {
+ mService.setSubscriptionOverride(subId, overrideMask, overrideValue, timeoutMillis,
+ callingPackage);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set the subscription plans for a specific subscriber.
+ *
+ * @param subId the subscriber this relationship applies to.
+ * @param plans the list of plans.
+ * @param callingPackage the name of the package making the call
+ */
+ public void setSubscriptionPlans(int subId, @NonNull SubscriptionPlan[] plans,
+ @NonNull String callingPackage) {
+ try {
+ mService.setSubscriptionPlans(subId, plans, callingPackage);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get subscription plans for the given subscription id.
+ *
+ * @param subId the subscriber to get the subscription plans for.
+ * @param callingPackage the name of the package making the call.
+ */
+ @NonNull
+ public SubscriptionPlan[] getSubscriptionPlans(int subId, @NonNull String callingPackage) {
+ try {
+ return mService.getSubscriptionPlans(subId, callingPackage);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Resets network policy settings back to factory defaults.
*
* @hide
@@ -286,6 +454,7 @@
/**
* Check if given UID can have a {@link #setUidPolicy(int, int)} defined,
* usually to protect critical system services.
+ * @hide
*/
@Deprecated
public static boolean isUidValidForPolicy(Context context, int uid) {
@@ -353,6 +522,7 @@
/**
* Returns true if {@param procState} is considered foreground and as such will be allowed
* to access network when the device is idle or in battery saver mode. Otherwise, false.
+ * @hide
*/
public static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(int procState) {
return procState <= FOREGROUND_THRESHOLD_STATE;
@@ -361,20 +531,68 @@
/**
* Returns true if {@param procState} is considered foreground and as such will be allowed
* to access network when the device is in data saver mode. Otherwise, false.
+ * @hide
*/
public static boolean isProcStateAllowedWhileOnRestrictBackground(int procState) {
return procState <= FOREGROUND_THRESHOLD_STATE;
}
+ /** @hide */
public static String resolveNetworkId(WifiConfiguration config) {
return WifiInfo.removeDoubleQuotes(config.isPasspoint()
? config.providerFriendlyName : config.SSID);
}
+ /** @hide */
public static String resolveNetworkId(String ssid) {
return WifiInfo.removeDoubleQuotes(ssid);
}
+ /** @hide */
+ @SystemApi
+ public static class SubscriptionCallback {
+ /**
+ * Notify clients of a new override about a given subscription.
+ *
+ * @param subId the subscriber this override applies to.
+ * @param overrideMask a bitmask that specifies which of the overrides is set.
+ * @param overrideValue a bitmask that specifies the override values.
+ */
+ public void onSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask,
+ @SubscriptionOverrideMask int overrideValue) {}
+
+ /**
+ * Notify of subscription plans change about a given subscription.
+ *
+ * @param subId the subscriber id that got subscription plans change.
+ * @param plans the list of subscription plans.
+ */
+ public void onSubscriptionPlansChanged(int subId, @NonNull SubscriptionPlan[] plans) {}
+ }
+
+ /**
+ * SubscriptionCallback proxy for SubscriptionCallback object.
+ * @hide
+ */
+ public class SubscriptionCallbackProxy extends Listener {
+ private final SubscriptionCallback mCallback;
+
+ SubscriptionCallbackProxy(SubscriptionCallback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void onSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask,
+ @SubscriptionOverrideMask int overrideValue) {
+ mCallback.onSubscriptionOverride(subId, overrideMask, overrideValue);
+ }
+
+ @Override
+ public void onSubscriptionPlansChanged(int subId, SubscriptionPlan[] plans) {
+ mCallback.onSubscriptionPlansChanged(subId, plans);
+ }
+ }
+
/** {@hide} */
public static class Listener extends INetworkPolicyListener.Stub {
@Override public void onUidRulesChanged(int uid, int uidRules) { }
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index c233ec0..a190c47 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -35,6 +35,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
@@ -385,7 +386,6 @@
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES)
public boolean requestScores(@NonNull NetworkKey[] networks) throws SecurityException {
try {
@@ -396,6 +396,28 @@
}
/**
+ * Request scoring for networks.
+ *
+ * <p>
+ * Note: The results (i.e scores) for these networks, when available will be provided via the
+ * callback registered with {@link #registerNetworkScoreCallback(int, int, Executor,
+ * NetworkScoreCallback)}. The calling module is responsible for registering a callback to
+ * receive the results before requesting new scores via this API.
+ *
+ * @return true if the request was successfully sent, or false if there is no active scorer.
+ * @throws SecurityException if the caller does not hold the
+ * {@link permission#REQUEST_NETWORK_SCORES} permission.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES)
+ public boolean requestScores(@NonNull Collection<NetworkKey> networks)
+ throws SecurityException {
+ return requestScores(networks.toArray(new NetworkKey[0]));
+ }
+
+ /**
* Register a network score cache.
*
* @param networkType the type of network this cache can handle. See {@link NetworkKey#type}.
@@ -454,26 +476,25 @@
/**
* Base class for network score cache callback. Should be extended by applications and set
- * when calling {@link #registerNetworkScoreCallback(int, int, NetworkScoreCallback,
- * Executor)}
+ * when calling {@link #registerNetworkScoreCallback(int, int, Executor, NetworkScoreCallback)}.
*
* @hide
*/
@SystemApi
- public interface NetworkScoreCallback {
+ public abstract static class NetworkScoreCallback {
/**
* Called when a new set of network scores are available.
* This is triggered in response when the client invokes
- * {@link #requestScores(NetworkKey[])} to score a new set of networks.
+ * {@link #requestScores(Collection)} to score a new set of networks.
*
* @param networks List of {@link ScoredNetwork} containing updated scores.
*/
- void updateScores(@NonNull List<ScoredNetwork> networks);
+ public abstract void onScoresUpdated(@NonNull Collection<ScoredNetwork> networks);
/**
* Invokes when all the previously provided scores are no longer valid.
*/
- void clearScores();
+ public abstract void onScoresInvalidated();
}
/**
@@ -492,7 +513,7 @@
public void updateScores(@NonNull List<ScoredNetwork> networks) {
Binder.clearCallingIdentity();
mExecutor.execute(() -> {
- mCallback.updateScores(networks);
+ mCallback.onScoresUpdated(networks);
});
}
@@ -500,7 +521,7 @@
public void clearScores() {
Binder.clearCallingIdentity();
mExecutor.execute(() -> {
- mCallback.clearScores();
+ mCallback.onScoresInvalidated();
});
}
}
diff --git a/core/java/android/net/StringNetworkSpecifier.java b/core/java/android/net/StringNetworkSpecifier.java
index 83dbc63..6ae5971 100644
--- a/core/java/android/net/StringNetworkSpecifier.java
+++ b/core/java/android/net/StringNetworkSpecifier.java
@@ -17,7 +17,6 @@
package android.net;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -27,7 +26,6 @@
import java.util.Objects;
/** @hide */
-@SystemApi
public final class StringNetworkSpecifier extends NetworkSpecifier implements Parcelable {
/**
* Arbitrary string used to pass (additional) information to the network factory.
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index 0545666..f2e16b4 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -42,7 +42,7 @@
*/
@SystemApi
@SystemService(Context.BATTERY_STATS_SERVICE)
-public class BatteryStatsManager {
+public final class BatteryStatsManager {
/**
* Wifi states.
*
@@ -166,7 +166,7 @@
* @param newRssi The new RSSI value.
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiRssiChanged(@IntRange(from = -127, to = 0) int newRssi) {
+ public void reportWifiRssiChanged(@IntRange(from = -127, to = 0) int newRssi) {
try {
mBatteryStats.noteWifiRssiChanged(newRssi);
} catch (RemoteException e) {
@@ -178,7 +178,7 @@
* Indicates that wifi was toggled on.
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiOn() {
+ public void reportWifiOn() {
try {
mBatteryStats.noteWifiOn();
} catch (RemoteException e) {
@@ -190,7 +190,7 @@
* Indicates that wifi was toggled off.
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiOff() {
+ public void reportWifiOff() {
try {
mBatteryStats.noteWifiOff();
} catch (RemoteException e) {
@@ -205,7 +205,7 @@
* @param accessPoint SSID of the network if wifi is connected to STA, else null.
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiState(@WifiState int newWifiState,
+ public void reportWifiState(@WifiState int newWifiState,
@Nullable String accessPoint) {
try {
mBatteryStats.noteWifiState(newWifiState, accessPoint);
@@ -220,7 +220,7 @@
* @param ws Worksource (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiScanStartedFromSource(@NonNull WorkSource ws) {
+ public void reportWifiScanStartedFromSource(@NonNull WorkSource ws) {
try {
mBatteryStats.noteWifiScanStartedFromSource(ws);
} catch (RemoteException e) {
@@ -234,7 +234,7 @@
* @param ws Worksource (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiScanStoppedFromSource(@NonNull WorkSource ws) {
+ public void reportWifiScanStoppedFromSource(@NonNull WorkSource ws) {
try {
mBatteryStats.noteWifiScanStoppedFromSource(ws);
} catch (RemoteException e) {
@@ -249,7 +249,7 @@
* @param csph Channels scanned per hour.
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiBatchedScanStartedFromSource(@NonNull WorkSource ws,
+ public void reportWifiBatchedScanStartedFromSource(@NonNull WorkSource ws,
@IntRange(from = 0) int csph) {
try {
mBatteryStats.noteWifiBatchedScanStartedFromSource(ws, csph);
@@ -264,7 +264,7 @@
* @param ws Worksource (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiBatchedScanStoppedFromSource(@NonNull WorkSource ws) {
+ public void reportWifiBatchedScanStoppedFromSource(@NonNull WorkSource ws) {
try {
mBatteryStats.noteWifiBatchedScanStoppedFromSource(ws);
} catch (RemoteException e) {
@@ -308,7 +308,7 @@
* @param ws Worksource (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteFullWifiLockAcquiredFromSource(@NonNull WorkSource ws) {
+ public void reportFullWifiLockAcquiredFromSource(@NonNull WorkSource ws) {
try {
mBatteryStats.noteFullWifiLockAcquiredFromSource(ws);
} catch (RemoteException e) {
@@ -322,7 +322,7 @@
* @param ws Worksource (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteFullWifiLockReleasedFromSource(@NonNull WorkSource ws) {
+ public void reportFullWifiLockReleasedFromSource(@NonNull WorkSource ws) {
try {
mBatteryStats.noteFullWifiLockReleasedFromSource(ws);
} catch (RemoteException e) {
@@ -338,7 +338,7 @@
* authentication failure.
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiSupplicantStateChanged(@WifiSupplState int newSupplState,
+ public void reportWifiSupplicantStateChanged(@WifiSupplState int newSupplState,
boolean failedAuth) {
try {
mBatteryStats.noteWifiSupplicantStateChanged(newSupplState, failedAuth);
@@ -353,7 +353,7 @@
* @param uid UID of the app that acquired the wifi lock (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiMulticastEnabled(int uid) {
+ public void reportWifiMulticastEnabled(int uid) {
try {
mBatteryStats.noteWifiMulticastEnabled(uid);
} catch (RemoteException e) {
@@ -367,7 +367,7 @@
* @param uid UID of the app that released the wifi lock (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void noteWifiMulticastDisabled(int uid) {
+ public void reportWifiMulticastDisabled(int uid) {
try {
mBatteryStats.noteWifiMulticastDisabled(uid);
} catch (RemoteException e) {
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index bf13c35..6a80788 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -247,6 +247,19 @@
*/
public static final int BRIGHTNESS_DEFAULT = -1;
+ /**
+ * Brightness value for fully off in float.
+ * TODO: rename this to BRIGHTNES_OFF and remove the integer-based constant.
+ * @hide
+ */
+ public static final float BRIGHTNESS_OFF_FLOAT = -1.0f;
+
+ /**
+ * Invalid brightness value.
+ * @hide
+ */
+ public static final float BRIGHTNESS_INVALID_FLOAT = Float.NaN;
+
// Note: Be sure to update android.os.BatteryStats and PowerManager.h
// if adding or modifying user activity event constants.
diff --git a/core/java/android/os/TelephonyServiceManager.java b/core/java/android/os/TelephonyServiceManager.java
index 064cf7d..c93eba6 100644
--- a/core/java/android/os/TelephonyServiceManager.java
+++ b/core/java/android/os/TelephonyServiceManager.java
@@ -119,14 +119,6 @@
}
/**
- * Returns {@link ServiceRegisterer} for the telephony registry service.
- */
- @NonNull
- public ServiceRegisterer getTelephonyRegistryServiceRegisterer() {
- return new ServiceRegisterer("telephony.registry");
- }
-
- /**
* Returns {@link ServiceRegisterer} for the telephony IMS service.
*/
@NonNull
@@ -151,14 +143,6 @@
}
/**
- * Returns {@link ServiceRegisterer} for the network policy service.
- */
- @NonNull
- public ServiceRegisterer getNetworkPolicyServiceRegisterer() {
- return new ServiceRegisterer(Context.NETWORK_POLICY_SERVICE);
- }
-
- /**
* Returns {@link ServiceRegisterer} for the phone sub service.
*/
@NonNull
@@ -198,6 +182,9 @@
return new ServiceRegisterer("econtroller");
}
+ /**
+ * Returns {@link ServiceRegisterer} for the eUICC card controller service.
+ */
@NonNull
public ServiceRegisterer getEuiccCardControllerServiceRegisterer() {
return new ServiceRegisterer("euicc_card_controller");
@@ -212,26 +199,10 @@
}
/**
- * Returns {@link ServiceRegisterer} for the permission manager service.
- */
- @NonNull
- public ServiceRegisterer getPermissionManagerServiceRegisterer() {
- return new ServiceRegisterer("permissionmgr");
- }
-
- /**
* Returns {@link ServiceRegisterer} for the ICC phone book service.
*/
@NonNull
public ServiceRegisterer getIccPhoneBookServiceRegisterer() {
return new ServiceRegisterer("simphonebook");
}
-
- /**
- * Returns {@link ServiceRegisterer} for the window service.
- */
- @NonNull
- public ServiceRegisterer getWindowServiceRegisterer() {
- return new ServiceRegisterer(Context.WINDOW_SERVICE);
- }
}
diff --git a/core/java/android/os/connectivity/WifiBatteryStats.java b/core/java/android/os/connectivity/WifiBatteryStats.java
index 895d837..3c30f63 100644
--- a/core/java/android/os/connectivity/WifiBatteryStats.java
+++ b/core/java/android/os/connectivity/WifiBatteryStats.java
@@ -15,11 +15,13 @@
*/
package android.os.connectivity;
+import static android.os.BatteryStats.NUM_WIFI_SIGNAL_STRENGTH_BINS;
+import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
+import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.os.BatteryStats;
-import android.os.BatteryStatsManager;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,31 +35,50 @@
*/
@SystemApi
public final class WifiBatteryStats implements Parcelable {
- private long mLoggingDurationMillis = 0;
- private long mKernelActiveTimeMillis = 0;
- private long mNumPacketsTx = 0;
- private long mNumBytesTx = 0;
- private long mNumPacketsRx = 0;
- private long mNumBytesRx = 0;
- private long mSleepTimeMillis = 0;
- private long mScanTimeMillis = 0;
- private long mIdleTimeMillis = 0;
- private long mRxTimeMillis = 0;
- private long mTxTimeMillis = 0;
- private long mEnergyConsumedMaMillis = 0;
- private long mNumAppScanRequest = 0;
- private long[] mTimeInStateMillis =
- new long[BatteryStatsManager.NUM_WIFI_STATES];
- private long[] mTimeInSupplicantStateMillis =
- new long[BatteryStatsManager.NUM_WIFI_SUPPL_STATES];
- private long[] mTimeInRxSignalStrengthLevelMillis =
- new long[BatteryStats.NUM_WIFI_SIGNAL_STRENGTH_BINS];
- private long mMonitoredRailChargeConsumedMaMillis = 0;
+ private final long mLoggingDurationMillis;
+ private final long mKernelActiveTimeMillis;
+ private final long mNumPacketsTx;
+ private final long mNumBytesTx;
+ private final long mNumPacketsRx;
+ private final long mNumBytesRx;
+ private final long mSleepTimeMillis;
+ private final long mScanTimeMillis;
+ private final long mIdleTimeMillis;
+ private final long mRxTimeMillis;
+ private final long mTxTimeMillis;
+ private final long mEnergyConsumedMaMillis;
+ private final long mAppScanRequestCount;
+ private final long[] mTimeInStateMillis;
+ private final long[] mTimeInSupplicantStateMillis;
+ private final long[] mTimeInRxSignalStrengthLevelMillis;
+ private final long mMonitoredRailChargeConsumedMaMillis;
public static final @NonNull Parcelable.Creator<WifiBatteryStats> CREATOR =
new Parcelable.Creator<WifiBatteryStats>() {
public WifiBatteryStats createFromParcel(Parcel in) {
- return new WifiBatteryStats(in);
+ long loggingDurationMillis = in.readLong();
+ long kernelActiveTimeMillis = in.readLong();
+ long numPacketsTx = in.readLong();
+ long numBytesTx = in.readLong();
+ long numPacketsRx = in.readLong();
+ long numBytesRx = in.readLong();
+ long sleepTimeMillis = in.readLong();
+ long scanTimeMillis = in.readLong();
+ long idleTimeMillis = in.readLong();
+ long rxTimeMillis = in.readLong();
+ long txTimeMillis = in.readLong();
+ long energyConsumedMaMillis = in.readLong();
+ long appScanRequestCount = in.readLong();
+ long[] timeInStateMillis = in.createLongArray();
+ long[] timeInRxSignalStrengthLevelMillis = in.createLongArray();
+ long[] timeInSupplicantStateMillis = in.createLongArray();
+ long monitoredRailChargeConsumedMaMillis = in.readLong();
+ return new WifiBatteryStats(loggingDurationMillis, kernelActiveTimeMillis,
+ numPacketsTx, numBytesTx, numPacketsRx, numBytesRx, sleepTimeMillis,
+ scanTimeMillis, idleTimeMillis, rxTimeMillis, txTimeMillis,
+ energyConsumedMaMillis, appScanRequestCount, timeInStateMillis,
+ timeInRxSignalStrengthLevelMillis, timeInSupplicantStateMillis,
+ monitoredRailChargeConsumedMaMillis);
}
public WifiBatteryStats[] newArray(int size) {
@@ -84,7 +105,7 @@
out.writeLong(mRxTimeMillis);
out.writeLong(mTxTimeMillis);
out.writeLong(mEnergyConsumedMaMillis);
- out.writeLong(mNumAppScanRequest);
+ out.writeLong(mAppScanRequestCount);
out.writeLongArray(mTimeInStateMillis);
out.writeLongArray(mTimeInRxSignalStrengthLevelMillis);
out.writeLongArray(mTimeInSupplicantStateMillis);
@@ -108,7 +129,7 @@
&& this.mRxTimeMillis == otherStats.mRxTimeMillis
&& this.mTxTimeMillis == otherStats.mTxTimeMillis
&& this.mEnergyConsumedMaMillis == otherStats.mEnergyConsumedMaMillis
- && this.mNumAppScanRequest == otherStats.mNumAppScanRequest
+ && this.mAppScanRequestCount == otherStats.mAppScanRequestCount
&& Arrays.equals(this.mTimeInStateMillis, otherStats.mTimeInStateMillis)
&& Arrays.equals(this.mTimeInSupplicantStateMillis,
otherStats.mTimeInSupplicantStateMillis)
@@ -123,33 +144,42 @@
return Objects.hash(mLoggingDurationMillis, mKernelActiveTimeMillis, mNumPacketsTx,
mNumBytesTx, mNumPacketsRx, mNumBytesRx, mSleepTimeMillis, mScanTimeMillis,
mIdleTimeMillis, mRxTimeMillis, mTxTimeMillis, mEnergyConsumedMaMillis,
- mNumAppScanRequest, Arrays.hashCode(mTimeInStateMillis),
+ mAppScanRequestCount, Arrays.hashCode(mTimeInStateMillis),
Arrays.hashCode(mTimeInSupplicantStateMillis),
Arrays.hashCode(mTimeInRxSignalStrengthLevelMillis),
mMonitoredRailChargeConsumedMaMillis);
}
/** @hide **/
- public WifiBatteryStats() {}
-
- private void readFromParcel(Parcel in) {
- mLoggingDurationMillis = in.readLong();
- mKernelActiveTimeMillis = in.readLong();
- mNumPacketsTx = in.readLong();
- mNumBytesTx = in.readLong();
- mNumPacketsRx = in.readLong();
- mNumBytesRx = in.readLong();
- mSleepTimeMillis = in.readLong();
- mScanTimeMillis = in.readLong();
- mIdleTimeMillis = in.readLong();
- mRxTimeMillis = in.readLong();
- mTxTimeMillis = in.readLong();
- mEnergyConsumedMaMillis = in.readLong();
- mNumAppScanRequest = in.readLong();
- in.readLongArray(mTimeInStateMillis);
- in.readLongArray(mTimeInRxSignalStrengthLevelMillis);
- in.readLongArray(mTimeInSupplicantStateMillis);
- mMonitoredRailChargeConsumedMaMillis = in.readLong();
+ public WifiBatteryStats(long loggingDurationMillis, long kernelActiveTimeMillis,
+ long numPacketsTx, long numBytesTx, long numPacketsRx, long numBytesRx,
+ long sleepTimeMillis, long scanTimeMillis, long idleTimeMillis, long rxTimeMillis,
+ long txTimeMillis, long energyConsumedMaMillis, long appScanRequestCount,
+ @NonNull long[] timeInStateMillis, @NonNull long [] timeInRxSignalStrengthLevelMillis,
+ @NonNull long[] timeInSupplicantStateMillis, long monitoredRailChargeConsumedMaMillis) {
+ mLoggingDurationMillis = loggingDurationMillis;
+ mKernelActiveTimeMillis = kernelActiveTimeMillis;
+ mNumPacketsTx = numPacketsTx;
+ mNumBytesTx = numBytesTx;
+ mNumPacketsRx = numPacketsRx;
+ mNumBytesRx = numBytesRx;
+ mSleepTimeMillis = sleepTimeMillis;
+ mScanTimeMillis = scanTimeMillis;
+ mIdleTimeMillis = idleTimeMillis;
+ mRxTimeMillis = rxTimeMillis;
+ mTxTimeMillis = txTimeMillis;
+ mEnergyConsumedMaMillis = energyConsumedMaMillis;
+ mAppScanRequestCount = appScanRequestCount;
+ mTimeInStateMillis = Arrays.copyOfRange(
+ timeInStateMillis, 0,
+ Math.min(timeInStateMillis.length, NUM_WIFI_STATES));
+ mTimeInRxSignalStrengthLevelMillis = Arrays.copyOfRange(
+ timeInRxSignalStrengthLevelMillis, 0,
+ Math.min(timeInRxSignalStrengthLevelMillis.length, NUM_WIFI_SIGNAL_STRENGTH_BINS));
+ mTimeInSupplicantStateMillis = Arrays.copyOfRange(
+ timeInSupplicantStateMillis, 0,
+ Math.min(timeInSupplicantStateMillis.length, NUM_WIFI_SUPPL_STATES));
+ mMonitoredRailChargeConsumedMaMillis = monitoredRailChargeConsumedMaMillis;
}
/**
@@ -182,7 +212,7 @@
}
/**
- * Returns the number of packets received over wifi within
+ * Returns the number of bytes transmitted over wifi within
* {@link #getLoggingDurationMillis()}.
*
* @return Number of packets received.
@@ -192,7 +222,7 @@
}
/**
- * Returns the number of bytes transmitted over wifi within
+ * Returns the number of packets received over wifi within
* {@link #getLoggingDurationMillis()}.
*
* @return Number of bytes transmitted.
@@ -262,7 +292,7 @@
}
/**
- * Returns an estimation of energy consumed by wifi chip within
+ * Returns an estimation of energy consumed in millis by wifi chip within
* {@link #getLoggingDurationMillis()}.
*
* @return Energy consumed in millis.
@@ -276,8 +306,8 @@
*
* @return Number of app scans.
*/
- public long getNumAppScanRequest() {
- return mNumAppScanRequest;
+ public long getAppScanRequestCount() {
+ return mAppScanRequestCount;
}
/**
@@ -288,113 +318,4 @@
public long getMonitoredRailChargeConsumedMaMillis() {
return mMonitoredRailChargeConsumedMaMillis;
}
-
- /** @hide */
- public void setLoggingDurationMillis(long t) {
- mLoggingDurationMillis = t;
- return;
- }
-
- /** @hide */
- public void setKernelActiveTimeMillis(long t) {
- mKernelActiveTimeMillis = t;
- return;
- }
-
- /** @hide */
- public void setNumPacketsTx(long n) {
- mNumPacketsTx = n;
- return;
- }
-
- /** @hide */
- public void setNumBytesTx(long b) {
- mNumBytesTx = b;
- return;
- }
-
- /** @hide */
- public void setNumPacketsRx(long n) {
- mNumPacketsRx = n;
- return;
- }
-
- /** @hide */
- public void setNumBytesRx(long b) {
- mNumBytesRx = b;
- return;
- }
-
- /** @hide */
- public void setSleepTimeMillis(long t) {
- mSleepTimeMillis = t;
- return;
- }
-
- /** @hide */
- public void setScanTimeMillis(long t) {
- mScanTimeMillis = t;
- return;
- }
-
- /** @hide */
- public void setIdleTimeMillis(long t) {
- mIdleTimeMillis = t;
- return;
- }
-
- /** @hide */
- public void setRxTimeMillis(long t) {
- mRxTimeMillis = t;
- return;
- }
-
- /** @hide */
- public void setTxTimeMillis(long t) {
- mTxTimeMillis = t;
- return;
- }
-
- /** @hide */
- public void setEnergyConsumedMaMillis(long e) {
- mEnergyConsumedMaMillis = e;
- return;
- }
-
- /** @hide */
- public void setNumAppScanRequest(long n) {
- mNumAppScanRequest = n;
- return;
- }
-
- /** @hide */
- public void setTimeInStateMillis(long[] t) {
- mTimeInStateMillis = Arrays.copyOfRange(t, 0,
- Math.min(t.length, BatteryStatsManager.NUM_WIFI_STATES));
- return;
- }
-
- /** @hide */
- public void setTimeInRxSignalStrengthLevelMillis(long[] t) {
- mTimeInRxSignalStrengthLevelMillis = Arrays.copyOfRange(t, 0,
- Math.min(t.length, BatteryStats.NUM_WIFI_SIGNAL_STRENGTH_BINS));
- return;
- }
-
- /** @hide */
- public void setTimeInSupplicantStateMillis(long[] t) {
- mTimeInSupplicantStateMillis = Arrays.copyOfRange(
- t, 0, Math.min(t.length, BatteryStatsManager.NUM_WIFI_SUPPL_STATES));
- return;
- }
-
- /** @hide */
- public void setMonitoredRailChargeConsumedMaMillis(long monitoredRailEnergyConsumedMaMillis) {
- mMonitoredRailChargeConsumedMaMillis = monitoredRailEnergyConsumedMaMillis;
- return;
- }
-
- private WifiBatteryStats(Parcel in) {
- readFromParcel(in);
- }
}
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 84ceca0..091d78e 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -796,6 +796,14 @@
}
/**
+ * Returns list of namespaces that can be read without READ_DEVICE_CONFIG_PERMISSION;
+ * @hide
+ */
+ public static @NonNull List<String> getPublicNamespaces() {
+ return PUBLIC_NAMESPACES;
+ }
+
+ /**
* Interface for monitoring changes to properties. Implementations will receive callbacks when
* properties change, including a {@link Properties} object which contains a single namespace
* and all of the properties which changed for that namespace. This includes properties which
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c7c3140..00b2feb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -63,6 +63,7 @@
import android.os.LocaleList;
import android.os.PowerManager.AutoPowerSaveModeTriggers;
import android.os.Process;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -2161,6 +2162,11 @@
public static final String CALL_METHOD_PREFIX_KEY = "_prefix";
/**
+ * @hide - RemoteCallback monitor callback argument extra to the fast-path call()-based requests
+ */
+ public static final String CALL_METHOD_MONITOR_CALLBACK_KEY = "_monitor_callback_key";
+
+ /**
* @hide - String argument extra to the fast-path call()-based requests
*/
public static final String CALL_METHOD_FLAGS_KEY = "_flags";
@@ -2218,6 +2224,26 @@
/** @hide - Private call() method to reset to defaults the 'configuration' table */
public static final String CALL_METHOD_LIST_CONFIG = "LIST_config";
+ /** @hide - Private call() method to register monitor callback for 'configuration' table */
+ public static final String CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG =
+ "REGISTER_MONITOR_CALLBACK_config";
+
+ /** @hide - String argument extra to the config monitor callback */
+ public static final String EXTRA_MONITOR_CALLBACK_TYPE = "monitor_callback_type";
+
+ /** @hide - String argument extra to the config monitor callback */
+ public static final String EXTRA_ACCESS_CALLBACK = "access_callback";
+
+ /** @hide - String argument extra to the config monitor callback */
+ public static final String EXTRA_NAMESPACE_UPDATED_CALLBACK =
+ "namespace_updated_callback";
+
+ /** @hide - String argument extra to the config monitor callback */
+ public static final String EXTRA_NAMESPACE = "namespace";
+
+ /** @hide - String argument extra to the config monitor callback */
+ public static final String EXTRA_CALLING_PACKAGE = "calling_package";
+
/**
* Activity Extra: Limit available options in launched activity based on the given authority.
* <p>
@@ -14155,6 +14181,37 @@
}
}
+ /**
+ * Register callback for monitoring Config table.
+ *
+ * @param resolver Handle to the content resolver.
+ * @param callback callback to register
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MONITOR_DEVICE_CONFIG_ACCESS)
+ public static void registerMonitorCallback(@NonNull ContentResolver resolver,
+ @NonNull RemoteCallback callback) {
+ registerMonitorCallbackAsUser(resolver, resolver.getUserId(), callback);
+ }
+
+ private static void registerMonitorCallbackAsUser(
+ @NonNull ContentResolver resolver, @UserIdInt int userHandle,
+ @NonNull RemoteCallback callback) {
+ try {
+ Bundle arg = new Bundle();
+ arg.putInt(CALL_METHOD_USER_KEY, userHandle);
+ arg.putParcelable(CALL_METHOD_MONITOR_CALLBACK_KEY, callback);
+ IContentProvider cp = sProviderHolder.getProvider(resolver);
+ cp.call(resolver.getPackageName(), resolver.getFeatureId(),
+ sProviderHolder.mUri.getAuthority(),
+ CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG, null, arg);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Can't register config monitor callback", e);
+ }
+ }
+
private static String createCompositeName(@NonNull String namespace, @NonNull String name) {
Preconditions.checkNotNull(namespace);
Preconditions.checkNotNull(name);
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 4537281..f25cdf1 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -37,7 +37,6 @@
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
-import android.os.Parcel;
import android.telephony.CarrierConfigManager;
import android.telephony.Rlog;
import android.telephony.ServiceState;
@@ -4559,32 +4558,6 @@
}
/**
- * Used to insert a ServiceState into the ServiceStateProvider as a ContentValues instance.
- *
- * @param state the ServiceState to convert into ContentValues
- * @return the convertedContentValues instance
- * @hide
- */
- public static ContentValues getContentValuesForServiceState(ServiceState state) {
- ContentValues values = new ContentValues();
- final Parcel p = Parcel.obtain();
- state.writeToParcel(p, 0);
- // Turn the parcel to byte array. Safe to do this because the content values were never
- // written into a persistent storage. ServiceStateProvider keeps values in the memory.
- values.put(SERVICE_STATE, p.marshall());
- return values;
- }
-
- /**
- * The current service state.
- *
- * This is the entire {@link ServiceState} object in byte array.
- *
- * @hide
- */
- public static final String SERVICE_STATE = "service_state";
-
- /**
* An integer value indicating the current voice service state.
* <p>
* Valid values: {@link ServiceState#STATE_IN_SERVICE},
@@ -4596,53 +4569,6 @@
public static final String VOICE_REG_STATE = "voice_reg_state";
/**
- * An integer value indicating the current data service state.
- * <p>
- * Valid values: {@link ServiceState#STATE_IN_SERVICE},
- * {@link ServiceState#STATE_OUT_OF_SERVICE}, {@link ServiceState#STATE_EMERGENCY_ONLY},
- * {@link ServiceState#STATE_POWER_OFF}.
- * <p>
- * This is the same as {@link ServiceState#getDataRegState()}.
- * @hide
- */
- public static final String DATA_REG_STATE = "data_reg_state";
-
- /**
- * An integer value indicating the current voice roaming type.
- * <p>
- * This is the same as {@link ServiceState#getVoiceRoamingType()}.
- * @hide
- */
- public static final String VOICE_ROAMING_TYPE = "voice_roaming_type";
-
- /**
- * An integer value indicating the current data roaming type.
- * <p>
- * This is the same as {@link ServiceState#getDataRoamingType()}.
- * @hide
- */
- public static final String DATA_ROAMING_TYPE = "data_roaming_type";
-
- /**
- * The current registered voice network operator name in long alphanumeric format.
- * <p>
- * This is the same as {@link ServiceState#getOperatorAlphaLong()}.
- * @hide
- */
- public static final String VOICE_OPERATOR_ALPHA_LONG = "voice_operator_alpha_long";
-
- /**
- * The current registered operator name in short alphanumeric format.
- * <p>
- * In GSM/UMTS, short format can be up to 8 characters long. The current registered voice
- * network operator name in long alphanumeric format.
- * <p>
- * This is the same as {@link ServiceState#getOperatorAlphaShort()}.
- * @hide
- */
- public static final String VOICE_OPERATOR_ALPHA_SHORT = "voice_operator_alpha_short";
-
- /**
* The current registered operator numeric id.
* <p>
* In GSM/UMTS, numeric format is 3 digit country code plus 2 or 3 digit
@@ -4653,125 +4579,11 @@
public static final String VOICE_OPERATOR_NUMERIC = "voice_operator_numeric";
/**
- * The current registered data network operator name in long alphanumeric format.
- * <p>
- * This is the same as {@link ServiceState#getOperatorAlphaLong()}.
- * @hide
- */
- public static final String DATA_OPERATOR_ALPHA_LONG = "data_operator_alpha_long";
-
- /**
- * The current registered data network operator name in short alphanumeric format.
- * <p>
- * This is the same as {@link ServiceState#getOperatorAlphaShort()}.
- * @hide
- */
- public static final String DATA_OPERATOR_ALPHA_SHORT = "data_operator_alpha_short";
-
- /**
- * The current registered data network operator numeric id.
- * <p>
- * This is the same as {@link ServiceState#getOperatorNumeric()}.
- * @hide
- */
- public static final String DATA_OPERATOR_NUMERIC = "data_operator_numeric";
-
- /**
* The current network selection mode.
* <p>
* This is the same as {@link ServiceState#getIsManualSelection()}.
*/
public static final String IS_MANUAL_NETWORK_SELECTION = "is_manual_network_selection";
-
- /**
- * This is the same as {@link ServiceState#getRilVoiceRadioTechnology()}.
- * @hide
- */
- public static final String RIL_VOICE_RADIO_TECHNOLOGY = "ril_voice_radio_technology";
-
- /**
- * This is the same as {@link ServiceState#getRilDataRadioTechnology()}.
- * @hide
- */
- public static final String RIL_DATA_RADIO_TECHNOLOGY = "ril_data_radio_technology";
-
- /**
- * This is the same as {@link ServiceState#getCssIndicator()}.
- * @hide
- */
- public static final String CSS_INDICATOR = "css_indicator";
-
- /**
- * This is the same as {@link ServiceState#getCdmaNetworkId()}.
- * @hide
- */
- public static final String NETWORK_ID = "network_id";
-
- /**
- * This is the same as {@link ServiceState#getCdmaSystemId()}.
- * @hide
- */
- public static final String SYSTEM_ID = "system_id";
-
- /**
- * This is the same as {@link ServiceState#getCdmaRoamingIndicator()}.
- * @hide
- */
- public static final String CDMA_ROAMING_INDICATOR = "cdma_roaming_indicator";
-
- /**
- * This is the same as {@link ServiceState#getCdmaDefaultRoamingIndicator()}.
- * @hide
- */
- public static final String CDMA_DEFAULT_ROAMING_INDICATOR =
- "cdma_default_roaming_indicator";
-
- /**
- * This is the same as {@link ServiceState#getCdmaEriIconIndex()}.
- * @hide
- */
- public static final String CDMA_ERI_ICON_INDEX = "cdma_eri_icon_index";
-
- /**
- * This is the same as {@link ServiceState#getCdmaEriIconMode()}.
- * @hide
- */
- public static final String CDMA_ERI_ICON_MODE = "cdma_eri_icon_mode";
-
- /**
- * This is the same as {@link ServiceState#isEmergencyOnly()}.
- * @hide
- */
- public static final String IS_EMERGENCY_ONLY = "is_emergency_only";
-
- /**
- * This is the same as {@link ServiceState#getDataRoamingFromRegistration()}.
- * @hide
- */
- public static final String IS_DATA_ROAMING_FROM_REGISTRATION =
- "is_data_roaming_from_registration";
-
- /**
- * This is the same as {@link ServiceState#isUsingCarrierAggregation()}.
- * @hide
- */
- public static final String IS_USING_CARRIER_AGGREGATION = "is_using_carrier_aggregation";
-
- /**
- * The current registered raw data network operator name in long alphanumeric format.
- * <p>
- * This is the same as {@link ServiceState#getOperatorAlphaLongRaw()}.
- * @hide
- */
- public static final String OPERATOR_ALPHA_LONG_RAW = "operator_alpha_long_raw";
-
- /**
- * The current registered raw data network operator name in short alphanumeric format.
- * <p>
- * This is the same as {@link ServiceState#getOperatorAlphaShortRaw()}.
- * @hide
- */
- public static final String OPERATOR_ALPHA_SHORT_RAW = "operator_alpha_short_raw";
}
/**
@@ -5299,6 +5111,12 @@
public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled";
/**
+ * Determines if the user has enabled IMS RCS User Capability Exchange (UCE) for this
+ * subscription.
+ */
+ public static final String IMS_RCS_UCE_ENABLED = "ims_rcs_uce_enabled";
+
+ /**
* TelephonyProvider column name for whether a subscription is opportunistic, that is,
* whether the network it connects to is limited in functionality or coverage.
* For example, CBRS.
diff --git a/core/java/android/security/ConfirmationPrompt.java b/core/java/android/security/ConfirmationPrompt.java
index 5330cff..f67af85 100644
--- a/core/java/android/security/ConfirmationPrompt.java
+++ b/core/java/android/security/ConfirmationPrompt.java
@@ -212,20 +212,16 @@
private int getUiOptionsAsFlags() {
int uiOptionsAsFlags = 0;
- try {
- ContentResolver contentResolver = mContext.getContentResolver();
- int inversionEnabled = Settings.Secure.getInt(contentResolver,
- Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
- if (inversionEnabled == 1) {
- uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_INVERTED_FLAG;
- }
- float fontScale = Settings.System.getFloat(contentResolver,
- Settings.System.FONT_SCALE);
- if (fontScale > 1.0) {
- uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_MAGNIFIED_FLAG;
- }
- } catch (SettingNotFoundException e) {
- Log.w(TAG, "Unexpected SettingNotFoundException");
+ ContentResolver contentResolver = mContext.getContentResolver();
+ int inversionEnabled = Settings.Secure.getInt(contentResolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0);
+ if (inversionEnabled == 1) {
+ uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_INVERTED_FLAG;
+ }
+ float fontScale = Settings.System.getFloat(contentResolver,
+ Settings.System.FONT_SCALE, (float) 1.0);
+ if (fontScale > 1.0) {
+ uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_MAGNIFIED_FLAG;
}
return uiOptionsAsFlags;
}
diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java
index 75f252e..c215778 100644
--- a/core/java/android/service/dataloader/DataLoaderService.java
+++ b/core/java/android/service/dataloader/DataLoaderService.java
@@ -58,6 +58,7 @@
* Managed DataLoader interface. Each instance corresponds to a single installation session.
* @hide
*/
+ @SystemApi
public interface DataLoader {
/**
* A virtual constructor.
@@ -78,8 +79,8 @@
* @param removedFiles list of files removed in this installation session.
* @return false if unable to create and populate all addedFiles.
*/
- boolean onPrepareImage(Collection<InstallationFile> addedFiles,
- Collection<String> removedFiles);
+ boolean onPrepareImage(@NonNull Collection<InstallationFile> addedFiles,
+ @NonNull Collection<String> removedFiles);
}
/**
@@ -88,6 +89,7 @@
* @return An instance of a DataLoader.
* @hide
*/
+ @SystemApi
public @Nullable DataLoader onCreateDataLoader() {
return null;
}
@@ -188,6 +190,7 @@
*
* @hide
*/
+ @SystemApi
public static final class FileSystemConnector {
/**
* Create a wrapper for a native instance.
@@ -211,8 +214,8 @@
* @throws IOException if trouble opening the file for writing, such as lack of disk space
* or unavailable media.
*/
- public void writeData(String name, long offsetBytes, long lengthBytes,
- ParcelFileDescriptor incomingFd) throws IOException {
+ public void writeData(@NonNull String name, long offsetBytes, long lengthBytes,
+ @NonNull ParcelFileDescriptor incomingFd) throws IOException {
try {
nativeWriteData(mNativeInstance, name, offsetBytes, lengthBytes, incomingFd);
} catch (RuntimeException e) {
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index c84fbc7..8464c6d 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -131,6 +131,13 @@
public static final String KEY_RANKING_SCORE = "key_ranking_score";
/**
+ * Data type: boolean, when true it suggests this is NOT a conversation notification.
+ * @hide
+ */
+ @SystemApi
+ public static final String KEY_NOT_CONVERSATION = "key_not_conversation";
+
+ /**
* Create a notification adjustment.
*
* @param pkg The package of the notification.
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index fd04f49..e053ed5 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1539,6 +1539,7 @@
private ArrayList<CharSequence> mSmartReplies;
private boolean mCanBubble;
private boolean mVisuallyInterruptive;
+ private boolean mIsConversation;
private static final int PARCEL_VERSION = 2;
@@ -1571,6 +1572,7 @@
out.writeCharSequenceList(mSmartReplies);
out.writeBoolean(mCanBubble);
out.writeBoolean(mVisuallyInterruptive);
+ out.writeBoolean(mIsConversation);
}
/** @hide */
@@ -1604,6 +1606,7 @@
mSmartReplies = in.readCharSequenceList();
mCanBubble = in.readBoolean();
mVisuallyInterruptive = in.readBoolean();
+ mIsConversation = in.readBoolean();
}
@@ -1801,6 +1804,14 @@
}
/**
+ * Returns whether this notification is a conversation notification.
+ * @hide
+ */
+ public boolean isConversation() {
+ return mIsConversation;
+ }
+
+ /**
* @hide
*/
@VisibleForTesting
@@ -1812,7 +1823,7 @@
int userSentiment, boolean hidden, long lastAudiblyAlertedMs,
boolean noisy, ArrayList<Notification.Action> smartActions,
ArrayList<CharSequence> smartReplies, boolean canBubble,
- boolean visuallyInterruptive) {
+ boolean visuallyInterruptive, boolean isConversation) {
mKey = key;
mRank = rank;
mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1834,6 +1845,7 @@
mSmartReplies = smartReplies;
mCanBubble = canBubble;
mVisuallyInterruptive = visuallyInterruptive;
+ mIsConversation = isConversation;
}
/**
@@ -1859,7 +1871,8 @@
other.mSmartActions,
other.mSmartReplies,
other.mCanBubble,
- other.mVisuallyInterruptive);
+ other.mVisuallyInterruptive,
+ other.mIsConversation);
}
/**
@@ -1912,7 +1925,8 @@
== (other.mSmartActions == null ? 0 : other.mSmartActions.size()))
&& Objects.equals(mSmartReplies, other.mSmartReplies)
&& Objects.equals(mCanBubble, other.mCanBubble)
- && Objects.equals(mVisuallyInterruptive, other.mVisuallyInterruptive);
+ && Objects.equals(mVisuallyInterruptive, other.mVisuallyInterruptive)
+ && Objects.equals(mIsConversation, other.mIsConversation);
}
}
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 0f33998..1966f17 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -29,7 +29,6 @@
import android.hardware.soundtrigger.SoundTrigger.ConfidenceLevel;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra;
-import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.media.AudioFormat;
@@ -67,7 +66,12 @@
/**
* Indicates that recognition for the given keyphrase is not supported.
* No further interaction should be performed with the detector that returns this availability.
+ *
+ * @deprecated This is no longer a valid state. Enrollment can occur outside of
+ * {@link KeyphraseEnrollmentInfo} through another privileged application. We can no longer
+ * determine ahead of time if the keyphrase and locale are unsupported by the system.
*/
+ @Deprecated
public static final int STATE_KEYPHRASE_UNSUPPORTED = -1;
/**
* Indicates that the given keyphrase is not enrolled.
@@ -85,34 +89,6 @@
*/
private static final int STATE_NOT_READY = 0;
- // Keyphrase management actions. Used in getManageIntent() ----//
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "MANAGE_ACTION_" }, value = {
- MANAGE_ACTION_ENROLL,
- MANAGE_ACTION_RE_ENROLL,
- MANAGE_ACTION_UN_ENROLL
- })
- private @interface ManageActions {}
-
- /**
- * Indicates that we need to enroll.
- *
- * @hide
- */
- public static final int MANAGE_ACTION_ENROLL = 0;
- /**
- * Indicates that we need to re-enroll.
- *
- * @hide
- */
- public static final int MANAGE_ACTION_RE_ENROLL = 1;
- /**
- * Indicates that we need to un-enroll.
- *
- * @hide
- */
- public static final int MANAGE_ACTION_UN_ENROLL = 2;
-
//-- Flags for startRecognition ----//
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -248,7 +224,8 @@
* The metadata of the Keyphrase, derived from the enrollment application.
* This may be null if this keyphrase isn't supported by the enrollment application.
*/
- private final KeyphraseMetadata mKeyphraseMetadata;
+ @Nullable
+ private KeyphraseMetadata mKeyphraseMetadata;
private final KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo;
private final IVoiceInteractionService mVoiceInteractionService;
private final IVoiceInteractionManagerService mModelManagementService;
@@ -448,7 +425,6 @@
mText = text;
mLocale = locale;
mKeyphraseEnrollmentInfo = keyphraseEnrollmentInfo;
- mKeyphraseMetadata = mKeyphraseEnrollmentInfo.getKeyphraseMetadata(text, locale);
mExternalCallback = callback;
mHandler = new MyHandler();
mInternalCallback = new SoundTriggerListener(mHandler);
@@ -484,8 +460,7 @@
}
// This method only makes sense if we can actually support a recognition.
- if (mAvailability != STATE_KEYPHRASE_ENROLLED
- && mAvailability != STATE_KEYPHRASE_UNENROLLED) {
+ if (mAvailability != STATE_KEYPHRASE_ENROLLED || mKeyphraseMetadata == null) {
throw new UnsupportedOperationException(
"Getting supported recognition modes for the keyphrase is not supported");
}
@@ -679,7 +654,7 @@
public Intent createEnrollIntent() {
if (DBG) Slog.d(TAG, "createEnrollIntent");
synchronized (mLock) {
- return getManageIntentLocked(MANAGE_ACTION_ENROLL);
+ return getManageIntentLocked(KeyphraseEnrollmentInfo.MANAGE_ACTION_ENROLL);
}
}
@@ -700,7 +675,7 @@
public Intent createUnEnrollIntent() {
if (DBG) Slog.d(TAG, "createUnEnrollIntent");
synchronized (mLock) {
- return getManageIntentLocked(MANAGE_ACTION_UN_ENROLL);
+ return getManageIntentLocked(KeyphraseEnrollmentInfo.MANAGE_ACTION_UN_ENROLL);
}
}
@@ -721,11 +696,11 @@
public Intent createReEnrollIntent() {
if (DBG) Slog.d(TAG, "createReEnrollIntent");
synchronized (mLock) {
- return getManageIntentLocked(MANAGE_ACTION_RE_ENROLL);
+ return getManageIntentLocked(KeyphraseEnrollmentInfo.MANAGE_ACTION_RE_ENROLL);
}
}
- private Intent getManageIntentLocked(int action) {
+ private Intent getManageIntentLocked(@KeyphraseEnrollmentInfo.ManageActions int action) {
if (mAvailability == STATE_INVALID) {
throw new IllegalStateException("getManageIntent called on an invalid detector");
}
@@ -761,8 +736,7 @@
void onSoundModelsChanged() {
synchronized (mLock) {
if (mAvailability == STATE_INVALID
- || mAvailability == STATE_HARDWARE_UNAVAILABLE
- || mAvailability == STATE_KEYPHRASE_UNSUPPORTED) {
+ || mAvailability == STATE_HARDWARE_UNAVAILABLE) {
Slog.w(TAG, "Received onSoundModelsChanged for an unsupported keyphrase/config");
return;
}
@@ -772,7 +746,9 @@
// or was deleted.
// The availability change callback should ensure that the client starts recognition
// again if needed.
- stopRecognitionLocked();
+ if (mAvailability == STATE_KEYPHRASE_ENROLLED) {
+ stopRecognitionLocked();
+ }
// Execute a refresh availability task - which should then notify of a change.
new RefreshAvailabiltyTask().execute();
@@ -955,20 +931,17 @@
@Override
public Void doInBackground(Void... params) {
int availability = internalGetInitialAvailability();
- boolean enrolled = false;
- // Fetch the sound model if the availability is one of the supported ones.
- if (availability == STATE_NOT_READY
- || availability == STATE_KEYPHRASE_UNENROLLED
- || availability == STATE_KEYPHRASE_ENROLLED) {
- enrolled = internalGetIsEnrolled(mKeyphraseMetadata.id, mLocale);
- if (!enrolled) {
- availability = STATE_KEYPHRASE_UNENROLLED;
- } else {
- availability = STATE_KEYPHRASE_ENROLLED;
- }
- }
synchronized (mLock) {
+ if (availability == STATE_NOT_READY) {
+ internalUpdateEnrolledKeyphraseMetadata();
+ if (mKeyphraseMetadata != null) {
+ availability = STATE_KEYPHRASE_ENROLLED;
+ } else {
+ availability = STATE_KEYPHRASE_UNENROLLED;
+ }
+ }
+
if (DBG) {
Slog.d(TAG, "Hotword availability changed from " + mAvailability
+ " -> " + availability);
@@ -997,28 +970,22 @@
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in getDspProperties!", e);
}
+
// No DSP available
if (dspModuleProperties == null) {
return STATE_HARDWARE_UNAVAILABLE;
}
- // No enrollment application supports this keyphrase/locale
- if (mKeyphraseMetadata == null) {
- return STATE_KEYPHRASE_UNSUPPORTED;
- }
+
return STATE_NOT_READY;
}
- /**
- * @return The corresponding {@link KeyphraseSoundModel} or null if none is found.
- */
- private boolean internalGetIsEnrolled(int keyphraseId, Locale locale) {
+ private void internalUpdateEnrolledKeyphraseMetadata() {
try {
- return mModelManagementService.isEnrolledForKeyphrase(
- mVoiceInteractionService, keyphraseId, locale.toLanguageTag());
+ mKeyphraseMetadata = mModelManagementService.getEnrolledKeyphraseMetadata(
+ mVoiceInteractionService, mText, mLocale.toLanguageTag());
} catch (RemoteException e) {
- Slog.w(TAG, "RemoteException in listRegisteredKeyphraseSoundModels!", e);
+ Slog.w(TAG, "RemoteException in internalUpdateEnrolledKeyphraseMetadata", e);
}
- return false;
}
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 36e057f..fc99836 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -16,14 +16,18 @@
package android.service.voice;
+import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
import android.app.Service;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
+import android.media.voice.KeyphraseModelManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -304,6 +308,23 @@
}
/**
+ * Creates an {@link KeyphraseModelManager} to use for enrolling voice models outside of the
+ * pre-bundled system voice models.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES)
+ @NonNull
+ public final KeyphraseModelManager createKeyphraseModelManager() {
+ if (mSystemService == null) {
+ throw new IllegalStateException("Not available until onReady() is called");
+ }
+ synchronized (mLock) {
+ return new KeyphraseModelManager(mSystemService);
+ }
+ }
+
+ /**
* @return Details of keyphrases available for enrollment.
* @hide
*/
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 52b7294..36f2c62 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1137,7 +1137,7 @@
mWindow = new SoftInputWindow(mContext, "VoiceInteractionSession", mTheme,
mCallbacks, this, mDispatcherState,
WindowManager.LayoutParams.TYPE_VOICE_INTERACTION, Gravity.BOTTOM, true);
- mWindow.getWindow().setFitWindowInsetsTypes(0 /* types */);
+ mWindow.getWindow().getAttributes().setFitInsetsTypes(0 /* types */);
mWindow.getWindow().addFlags(
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED |
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
index 00e0b7c..84b6869 100644
--- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
@@ -37,4 +37,5 @@
void requestWallpaperColors();
@UnsupportedAppUsage
void destroy();
+ void setZoomOut(float scale);
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index e3fd8d2..dd78c78 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -16,6 +16,7 @@
package android.service.wallpaper;
+import android.annotation.FloatRange;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -122,6 +123,9 @@
private static final int MSG_WINDOW_MOVED = 10035;
private static final int MSG_TOUCH_EVENT = 10040;
private static final int MSG_REQUEST_WALLPAPER_COLORS = 10050;
+ private static final int MSG_SCALE = 10100;
+
+ private static final float MAX_SCALE = 1.15f;
private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000;
@@ -170,6 +174,7 @@
int mType;
int mCurWidth;
int mCurHeight;
+ float mZoom = 0f;
int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
int mWindowPrivateFlags =
WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
@@ -496,6 +501,15 @@
}
/**
+ * Returns the current scale of the surface
+ * @hide
+ */
+ @VisibleForTesting
+ public float getZoom() {
+ return mZoom;
+ }
+
+ /**
* Called once to initialize the engine. After returning, the
* engine's surface will be created by the framework.
*/
@@ -623,6 +637,16 @@
}
/**
+ * Called when the zoom level of the wallpaper changed.
+ * This method will be called with the initial zoom level when the surface is created.
+ *
+ * @param zoom the zoom level, between 0 indicating fully zoomed in and 1 indicating fully
+ * zoomed out.
+ */
+ public void onZoomChanged(@FloatRange(from = 0f, to = 1f) float zoom) {
+ }
+
+ /**
* Notifies the engine that wallpaper colors changed significantly.
* This will trigger a {@link #onComputeColors()} call.
*/
@@ -706,6 +730,7 @@
out.print(prefix); out.print("mConfiguration=");
out.println(mMergedConfiguration.getMergedConfiguration());
out.print(prefix); out.print("mLayout="); out.println(mLayout);
+ out.print(prefix); out.print("mZoom="); out.println(mZoom);
synchronized (mLock) {
out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset);
out.print(" mPendingXOffset="); out.println(mPendingXOffset);
@@ -721,6 +746,37 @@
}
}
+ /**
+ * Set the wallpaper zoom to the given value. This value will be ignored when in ambient
+ * mode (and zoom will be reset to 0).
+ * @hide
+ * @param zoom between 0 and 1 (inclusive) indicating fully zoomed in to fully zoomed out
+ * respectively.
+ */
+ @VisibleForTesting
+ public void setZoom(float zoom) {
+ if (DEBUG) {
+ Log.v(TAG, "set zoom received: " + zoom);
+ }
+ boolean updated = false;
+ synchronized (mLock) {
+ if (DEBUG) {
+ Log.v(TAG, "mZoom: " + mZoom + " updated: " + zoom);
+ }
+ if (mIsInAmbientMode) {
+ mZoom = 0;
+ }
+ if (Float.compare(zoom, mZoom) != 0) {
+ mZoom = zoom;
+ updated = true;
+ }
+ }
+ if (DEBUG) Log.v(TAG, "setZoom updated? " + updated);
+ if (updated && !mDestroyed) {
+ onZoomChanged(mZoom);
+ }
+ }
+
private void dispatchPointer(MotionEvent event) {
if (event.isTouchEvent()) {
synchronized (mLock) {
@@ -813,7 +869,7 @@
// Add window
mLayout.type = mIWallpaperEngine.mWindowType;
mLayout.gravity = Gravity.START|Gravity.TOP;
- mLayout.setFitWindowInsetsTypes(0 /* types */);
+ mLayout.setFitInsetsTypes(0 /* types */);
mLayout.setTitle(WallpaperService.this.getClass().getName());
mLayout.windowAnimations =
com.android.internal.R.style.Animation_Wallpaper;
@@ -920,6 +976,7 @@
c.surfaceCreated(mSurfaceHolder);
}
}
+ onZoomChanged(0f);
}
redrawNeeded |= creating || (relayoutResult
@@ -1080,6 +1137,7 @@
mIsInAmbientMode = inAmbientMode;
if (mCreated) {
onAmbientModeChanged(inAmbientMode, animationDuration);
+ setZoom(0);
}
}
}
@@ -1354,6 +1412,11 @@
}
}
+ public void setZoomOut(float scale) {
+ Message msg = mCaller.obtainMessageI(MSG_SCALE, Float.floatToIntBits(scale));
+ mCaller.sendMessage(msg);
+ }
+
public void reportShown() {
if (!mShownReported) {
mShownReported = true;
@@ -1426,6 +1489,9 @@
case MSG_UPDATE_SURFACE:
mEngine.updateSurface(true, false, false);
break;
+ case MSG_SCALE:
+ mEngine.setZoom(Float.intBitsToFloat(message.arg1));
+ break;
case MSG_VISIBILITY_CHANGED:
if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine
+ ": " + message.arg1);
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index e25826c..6787c46 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -28,12 +28,15 @@
import android.telephony.Annotation.CallState;
import android.telephony.Annotation.DataActivityType;
import android.telephony.Annotation.DataFailureCause;
+import android.telephony.Annotation.DisconnectCauses;
import android.telephony.Annotation.NetworkType;
import android.telephony.Annotation.PreciseCallStates;
+import android.telephony.Annotation.PreciseDisconnectCauses;
import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SimActivationState;
import android.telephony.Annotation.SrvccState;
import android.telephony.data.ApnSetting;
+import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
import android.util.Log;
@@ -56,6 +59,7 @@
* @hide
*/
@SystemApi
+@TestApi
public class TelephonyRegistryManager {
private static final String TAG = "TelephonyRegistryManager";
@@ -195,6 +199,25 @@
}
/**
+ * Listen for incoming subscriptions
+ * @param subId Subscription ID
+ * @param pkg Package name
+ * @param featureId Feature ID
+ * @param listener Listener providing callback
+ * @param events Events
+ * @param notifyNow Whether to notify instantly
+ */
+ public void listenForSubscriber(int subId, @NonNull String pkg, @NonNull String featureId,
+ @NonNull PhoneStateListener listener, int events, boolean notifyNow) {
+ try {
+ sRegistry.listenForSubscriber(
+ subId, pkg, featureId, listener.callback, events, notifyNow);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Informs the system of an intentional upcoming carrier network change by a carrier app.
* This call only used to allow the system to provide alternative UI while telephony is
* performing an action that may result in intentional, temporary network lack of connectivity.
@@ -225,11 +248,9 @@
* invalid.
* @param state latest call state. e.g, offhook, ringing
* @param incomingNumber incoming phone number.
- *
- * @hide
*/
public void notifyCallStateChanged(int subId, int slotIndex, @CallState int state,
- String incomingNumber) {
+ @Nullable String incomingNumber) {
try {
sRegistry.notifyCallState(slotIndex, subId, state, incomingNumber);
} catch (RemoteException ex) {
@@ -257,16 +278,40 @@
}
/**
+ * Notify {@link SubscriptionInfo} change.
+ * @hide
+ */
+ @SystemApi
+ public void notifySubscriptionInfoChanged() {
+ try {
+ sRegistry.notifySubscriptionInfoChanged();
+ } catch (RemoteException ex) {
+ // system server crash
+ }
+ }
+
+ /**
+ * Notify opportunistic {@link SubscriptionInfo} change.
+ * @hide
+ */
+ @SystemApi
+ public void notifyOpportunisticSubscriptionInfoChanged() {
+ try {
+ sRegistry.notifyOpportunisticSubscriptionInfoChanged();
+ } catch (RemoteException ex) {
+ // system server crash
+ }
+ }
+
+ /**
* Notify {@link ServiceState} update on certain subscription.
*
* @param subId for which the service state changed.
* @param slotIndex for which the service state changed. Can be derived from subId except
* subId is invalid.
* @param state service state e.g, in service, out of service or roaming status.
- *
- * @hide
*/
- public void notifyServiceStateChanged(int subId, int slotIndex, ServiceState state) {
+ public void notifyServiceStateChanged(int subId, int slotIndex, @NonNull ServiceState state) {
try {
sRegistry.notifyServiceStateForPhoneId(slotIndex, subId, state);
} catch (RemoteException ex) {
@@ -281,11 +326,9 @@
* @param slotIndex for which the signalstrength changed. Can be derived from subId except when
* subId is invalid.
* @param signalStrength e.g, signalstrength level {@see SignalStrength#getLevel()}
- *
- * @hide
*/
public void notifySignalStrengthChanged(int subId, int slotIndex,
- SignalStrength signalStrength) {
+ @NonNull SignalStrength signalStrength) {
try {
sRegistry.notifySignalStrengthForPhoneId(slotIndex, subId, signalStrength);
} catch (RemoteException ex) {
@@ -302,8 +345,6 @@
* except when subId is invalid.
* @param msgWaitingInd {@code true} indicates there is message-waiting indicator, {@code false}
* otherwise.
- *
- * @hide
*/
public void notifyMessageWaitingChanged(int subId, int slotIndex, boolean msgWaitingInd) {
try {
@@ -319,8 +360,6 @@
* @param subId for which call forwarding status changed.
* @param callForwardInd {@code true} indicates there is call forwarding, {@code false}
* otherwise.
- *
- * @hide
*/
public void notifyCallForwardingChanged(int subId, boolean callForwardInd) {
try {
@@ -336,8 +375,6 @@
* @param subId for which data activity state changed.
* @param dataActivityType indicates the latest data activity type e.g, {@link
* TelephonyManager#DATA_ACTIVITY_IN}
- *
- * @hide
*/
public void notifyDataActivityChanged(int subId, @DataActivityType int dataActivityType) {
try {
@@ -358,10 +395,9 @@
*
* @see android.telephony.PreciseDataConnection
* @see TelephonyManager#DATA_DISCONNECTED
- * @hide
*/
public void notifyDataConnectionForSubscriber(int slotIndex, int subId,
- @ApnType int apnType, PreciseDataConnectionState preciseState) {
+ @ApnType int apnType, @Nullable PreciseDataConnectionState preciseState) {
try {
sRegistry.notifyDataConnectionForSubscriber(
slotIndex, subId, apnType, preciseState);
@@ -378,10 +414,8 @@
* subId is invalid.
* @param callQuality Information about call quality e.g, call quality level
* @param networkType associated with this data connection. e.g, LTE
- *
- * @hide
*/
- public void notifyCallQualityChanged(int subId, int slotIndex, CallQuality callQuality,
+ public void notifyCallQualityChanged(int subId, int slotIndex, @NonNull CallQuality callQuality,
@NetworkType int networkType) {
try {
sRegistry.notifyCallQualityChanged(callQuality, slotIndex, subId, networkType);
@@ -396,8 +430,6 @@
* @param subId for which emergency number list changed.
* @param slotIndex for which emergency number list changed. Can be derived from subId except
* when subId is invalid.
- *
- * @hide
*/
public void notifyEmergencyNumberList(int subId, int slotIndex) {
try {
@@ -408,14 +440,42 @@
}
/**
+ * Notify outgoing emergency call.
+ * @param phoneId Sender phone ID.
+ * @param subId Sender subscription ID.
+ * @param emergencyNumber Emergency number.
+ */
+ public void notifyOutgoingEmergencyCall(int phoneId, int subId,
+ @NonNull EmergencyNumber emergencyNumber) {
+ try {
+ sRegistry.notifyOutgoingEmergencyCall(phoneId, subId, emergencyNumber);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * Notify outgoing emergency SMS.
+ * @param phoneId Sender phone ID.
+ * @param subId Sender subscription ID.
+ * @param emergencyNumber Emergency number.
+ */
+ public void notifyOutgoingEmergencySms(int phoneId, int subId,
+ @NonNull EmergencyNumber emergencyNumber) {
+ try {
+ sRegistry.notifyOutgoingEmergencySms(phoneId, subId, emergencyNumber);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
* Notify radio power state changed on certain subscription.
*
* @param subId for which radio power state changed.
* @param slotIndex for which radio power state changed. Can be derived from subId except when
* subId is invalid.
* @param radioPowerState the current modem radio state.
- *
- * @hide
*/
public void notifyRadioPowerStateChanged(int subId, int slotIndex,
@RadioPowerState int radioPowerState) {
@@ -430,10 +490,8 @@
* Notify {@link PhoneCapability} changed.
*
* @param phoneCapability the capability of the modem group.
- *
- * @hide
*/
- public void notifyPhoneCapabilityChanged(PhoneCapability phoneCapability) {
+ public void notifyPhoneCapabilityChanged(@NonNull PhoneCapability phoneCapability) {
try {
sRegistry.notifyPhoneCapabilityChanged(phoneCapability);
} catch (RemoteException ex) {
@@ -462,8 +520,6 @@
* @param slotIndex for which data activation state changed. Can be derived from subId except
* when subId is invalid.
* @param activationState sim activation state e.g, activated.
- *
- * @hide
*/
public void notifyDataActivationStateChanged(int subId, int slotIndex,
@SimActivationState int activationState) {
@@ -483,8 +539,6 @@
* @param slotIndex for which voice activation state changed. Can be derived from subId except
* subId is invalid.
* @param activationState sim activation state e.g, activated.
- *
- * @hide
*/
public void notifyVoiceActivationStateChanged(int subId, int slotIndex,
@SimActivationState int activationState) {
@@ -504,8 +558,6 @@
* @param slotIndex for which mobile data state has changed. Can be derived from subId except
* when subId is invalid.
* @param state {@code true} indicates mobile data is enabled/on. {@code false} otherwise.
- *
- * @hide
*/
public void notifyUserMobileDataStateChanged(int slotIndex, int subId, boolean state) {
try {
@@ -516,31 +568,12 @@
}
/**
- * TODO: this is marked as deprecated, can we move this one safely?
- *
- * @param subId
- * @param slotIndex
- * @param rawData
- *
- * @hide
- */
- public void notifyOemHookRawEventForSubscriber(int subId, int slotIndex, byte[] rawData) {
- try {
- sRegistry.notifyOemHookRawEventForSubscriber(slotIndex, subId, rawData);
- } catch (RemoteException ex) {
- // system process is dead
- }
- }
-
- /**
* Notify IMS call disconnect causes which contains {@link android.telephony.ims.ImsReasonInfo}.
*
* @param subId for which ims call disconnect.
* @param imsReasonInfo the reason for ims call disconnect.
- *
- * @hide
*/
- public void notifyImsDisconnectCause(int subId, ImsReasonInfo imsReasonInfo) {
+ public void notifyImsDisconnectCause(int subId, @NonNull ImsReasonInfo imsReasonInfo) {
try {
sRegistry.notifyImsDisconnectCause(subId, imsReasonInfo);
} catch (RemoteException ex) {
@@ -557,11 +590,9 @@
* @param apnType the apn type bitmask, defined with {@code ApnSetting#TYPE_*} flags.
* @param apn the APN {@link ApnSetting#getApnName()} of this data connection.
* @param failCause data fail cause.
- *
- * @hide
*/
public void notifyPreciseDataConnectionFailed(int subId, int slotIndex, @ApnType int apnType,
- String apn, @DataFailureCause int failCause) {
+ @Nullable String apn, @DataFailureCause int failCause) {
try {
sRegistry.notifyPreciseDataConnectionFailed(slotIndex, subId, apnType, apn, failCause);
} catch (RemoteException ex) {
@@ -575,8 +606,6 @@
*
* @param subId for which srvcc state changed.
* @param state srvcc state
- *
- * @hide
*/
public void notifySrvccStateChanged(int subId, @SrvccState int state) {
try {
@@ -596,8 +625,6 @@
* @param ringCallPreciseState ringCall state.
* @param foregroundCallPreciseState foreground call state.
* @param backgroundCallPreciseState background call state.
- *
- * @hide
*/
public void notifyPreciseCallState(int subId, int slotIndex,
@PreciseCallStates int ringCallPreciseState,
@@ -621,10 +648,9 @@
* @param cause {@link DisconnectCause} for the disconnected call.
* @param preciseCause {@link android.telephony.PreciseDisconnectCause} for the disconnected
* call.
- *
- * @hide
*/
- public void notifyDisconnectCause(int slotIndex, int subId, int cause, int preciseCause) {
+ public void notifyDisconnectCause(int slotIndex, int subId, @DisconnectCauses int cause,
+ @PreciseDisconnectCauses int preciseCause) {
try {
sRegistry.notifyDisconnectCause(slotIndex, subId, cause, preciseCause);
} catch (RemoteException ex) {
@@ -637,10 +663,8 @@
*
* <p>To be compatible with {@link TelephonyRegistry}, use {@link CellIdentity} which is
* parcelable, and convert to CellLocation in client code.
- *
- * @hide
*/
- public void notifyCellLocation(int subId, CellIdentity cellLocation) {
+ public void notifyCellLocation(int subId, @NonNull CellIdentity cellLocation) {
try {
sRegistry.notifyCellLocationForSubscriber(subId, cellLocation);
} catch (RemoteException ex) {
@@ -654,10 +678,8 @@
*
* @param subId for which cellinfo changed.
* @param cellInfo A list of cellInfo associated with the given subscription.
- *
- * @hide
*/
- public void notifyCellInfoChanged(int subId, List<CellInfo> cellInfo) {
+ public void notifyCellInfoChanged(int subId, @NonNull List<CellInfo> cellInfo) {
try {
sRegistry.notifyCellInfoForSubscriber(subId, cellInfo);
} catch (RemoteException ex) {
@@ -666,8 +688,8 @@
}
/**
- * @param activeDataSubId
- * @hide
+ * Notify that the active data subscription ID has changed.
+ * @param activeDataSubId The new subscription ID for active data
*/
public void notifyActiveDataSubIdChanged(int activeDataSubId) {
try {
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index eb4af1c..06fccaf 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -44,6 +44,9 @@
public static final String SETTINGS_FUSE_FLAG = "settings_fuse";
public static final String NOTIF_CONVO_BYPASS_SHORTCUT_REQ =
"settings_notif_convo_bypass_shortcut_req";
+ /** @hide */
+ public static final String BACKUP_NO_KV_DATA_CHANGE_CALLS =
+ "backup_enable_no_data_notification_calls";
private static final Map<String, String> DEFAULT_FLAGS;
@@ -62,6 +65,9 @@
DEFAULT_FLAGS.put("settings_controller_loading_enhancement", "false");
DEFAULT_FLAGS.put("settings_conditionals", "false");
DEFAULT_FLAGS.put(NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true");
+
+ // Disabled until backup transports support it.
+ DEFAULT_FLAGS.put(BACKUP_NO_KV_DATA_CHANGE_CALLS, "false");
}
/**
diff --git a/core/java/android/util/SparseSetArray.java b/core/java/android/util/SparseSetArray.java
index 9f0f246..f5025f7 100644
--- a/core/java/android/util/SparseSetArray.java
+++ b/core/java/android/util/SparseSetArray.java
@@ -37,10 +37,10 @@
mData.put(n, set);
}
if (set.contains(value)) {
- return true;
+ return false;
}
set.add(value);
- return false;
+ return true;
}
/**
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 904c510..0304328 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -423,10 +423,14 @@
/**
* Internal method to create a display.
* The display created with this method will have a static {@link DisplayAdjustments} applied.
- * Applications should use {@link android.view.WindowManager#getDefaultDisplay()}
- * or {@link android.hardware.display.DisplayManager#getDisplay}
- * to get a display object.
+ * Applications should use {@link android.content.Context#getDisplay} with
+ * {@link android.app.Activity} or a context associated with a {@link Display} via
+ * {@link android.content.Context#createDisplayContext(Display)}
+ * to get a display object associated with a {@link android.app.Context}, or
+ * {@link android.hardware.display.DisplayManager#getDisplay} to get a display object by id.
*
+ * @see android.content.Context#getDisplay()
+ * @see android.content.Context#createDisplayContext(Display)
* @hide
*/
public Display(DisplayManagerGlobal global, int displayId, /*@NotNull*/ DisplayInfo displayInfo,
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 993bdc4..d9c502e 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -32,6 +32,7 @@
import android.os.Bundle;
import android.os.IRemoteCallback;
import android.os.ParcelFileDescriptor;
+import android.view.DisplayCutout;
import android.view.IApplicationToken;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IDockedStackListener;
@@ -111,6 +112,20 @@
// These can only be called when holding the MANAGE_APP_TOKENS permission.
void setEventDispatching(boolean enabled);
+
+ /** @return {@code true} if this binder is a registered window token. */
+ boolean isWindowToken(in IBinder binder);
+ /**
+ * Adds window token for a given type.
+ *
+ * @param token Token to be registered.
+ * @param type Window type to be used with this token.
+ * @param displayId The ID of the display where this token should be added.
+ * @param packageName The name of package to request to add window token.
+ * @return {@link WindowManagerGlobal#ADD_OKAY} if the addition was successful, an error code
+ * otherwise.
+ */
+ int addWindowContextToken(IBinder token, int type, int displayId, String packageName);
void addWindowToken(IBinder token, int type, int displayId);
void removeWindowToken(IBinder token, int displayId);
void prepareAppTransition(int transit, boolean alwaysKeepCurrent);
@@ -725,4 +740,12 @@
* Called when a remote process modifies insets on a display window container.
*/
void modifyDisplayWindowInsets(int displayId, in InsetsState state);
+
+ /**
+ * Called to get the expected window insets.
+ * TODO(window-context): Remove when new insets flag is available.
+ */
+ void getWindowInsets(in WindowManager.LayoutParams attrs, int displayId,
+ out Rect outContentInsets, out Rect outStableInsets,
+ out DisplayCutout.ParcelableWrapper displayCutout);
}
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index 271566a..8d58ee8 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -45,12 +45,25 @@
mViewRootImpl = viewRootImpl;
}
+ @NonNull
private InputMethodManagerDelegate getImmDelegate() {
- if (mDelegate == null) {
- mDelegate = mViewRootImpl.mContext.getSystemService(
- InputMethodManager.class).getDelegate();
+ InputMethodManagerDelegate delegate = mDelegate;
+ if (delegate != null) {
+ return delegate;
}
- return mDelegate;
+ delegate = mViewRootImpl.mContext.getSystemService(InputMethodManager.class).getDelegate();
+ mDelegate = delegate;
+ return delegate;
+ }
+
+ /** Called when the view root is moved to a different display. */
+ @UiThread
+ void onMovedToDisplay() {
+ // InputMethodManager managed its instances for different displays. So if the associated
+ // display is changed, the delegate also needs to be refreshed (by getImmDelegate).
+ // See the comment in {@link android.app.SystemServiceRegistry} for InputMethodManager
+ // and {@link android.view.inputmethod.InputMethodManager#forContext}.
+ mDelegate = null;
}
@UiThread
@@ -103,7 +116,8 @@
}
boolean forceFocus = false;
- if (getImmDelegate().isRestartOnNextWindowFocus(true /* reset */)) {
+ final InputMethodManagerDelegate immDelegate = getImmDelegate();
+ if (immDelegate.isRestartOnNextWindowFocus(true /* reset */)) {
if (DEBUG) Log.v(TAG, "Restarting due to isRestartOnNextWindowFocus as true");
forceFocus = true;
}
@@ -111,12 +125,13 @@
final View viewForWindowFocus = focusedView != null ? focusedView : mViewRootImpl.mView;
onViewFocusChanged(viewForWindowFocus, true);
- getImmDelegate().startInputAsyncOnWindowFocusGain(viewForWindowFocus,
+ immDelegate.startInputAsyncOnWindowFocusGain(viewForWindowFocus,
windowAttribute.softInputMode, windowAttribute.flags, forceFocus);
}
public boolean checkFocus(boolean forceNewFocus, boolean startInput) {
- if (!getImmDelegate().isCurrentRootView(mViewRootImpl)
+ final InputMethodManagerDelegate immDelegate = getImmDelegate();
+ if (!immDelegate.isCurrentRootView(mViewRootImpl)
|| (mServedView == mNextServedView && !forceNewFocus)) {
return false;
}
@@ -128,15 +143,16 @@
// Close the connection when no next served view coming.
if (mNextServedView == null) {
- getImmDelegate().finishInput();
- getImmDelegate().closeCurrentIme();
+ immDelegate.finishInput();
+ immDelegate.closeCurrentIme();
return false;
}
mServedView = mNextServedView;
- getImmDelegate().finishComposingText();
+ immDelegate.finishComposingText();
if (startInput) {
- getImmDelegate().startInput(StartInputReason.CHECK_FOCUS, null, 0, 0, 0);
+ immDelegate.startInput(StartInputReason.CHECK_FOCUS, null /* focusedView */,
+ 0 /* startInputFlags */, 0 /* softInputMode */, 0 /* windowFlags */);
}
return true;
}
@@ -169,13 +185,14 @@
@UiThread
void onWindowDismissed() {
- if (!getImmDelegate().isCurrentRootView(mViewRootImpl)) {
+ final InputMethodManagerDelegate immDelegate = getImmDelegate();
+ if (!immDelegate.isCurrentRootView(mViewRootImpl)) {
return;
}
if (mServedView != null) {
- getImmDelegate().finishInput();
+ immDelegate.finishInput();
}
- getImmDelegate().setCurrentRootView(null);
+ immDelegate.setCurrentRootView(null);
mHasImeFocus = false;
}
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index f5afd10..405eccd 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -40,6 +40,7 @@
import android.view.WindowInsetsAnimationCallback.AnimationBounds;
import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
import android.view.WindowManager.LayoutParams;
+import android.view.animation.Interpolator;
import com.android.internal.annotations.VisibleForTesting;
@@ -84,8 +85,8 @@
public InsetsAnimationControlImpl(SparseArray<InsetsSourceControl> controls, Rect frame,
InsetsState state, WindowInsetsAnimationControlListener listener,
@InsetsType int types,
- InsetsAnimationControlCallbacks controller, long durationMs, boolean fade,
- @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
+ InsetsAnimationControlCallbacks controller, long durationMs, Interpolator interpolator,
+ boolean fade, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
mControls = controls;
mListener = listener;
mTypes = types;
@@ -101,8 +102,8 @@
mFrame = new Rect(frame);
buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mControls);
- mAnimation = new WindowInsetsAnimationCallback.InsetsAnimation(mTypes,
- InsetsController.INTERPOLATOR, durationMs);
+ mAnimation = new WindowInsetsAnimationCallback.InsetsAnimation(mTypes, interpolator,
+ durationMs);
mAnimation.setAlpha(getCurrentAlpha());
mController.startAnimation(this, listener, types, mAnimation,
new AnimationBounds(mHiddenInsets, mShownInsets), layoutInsetsDuringAnimation);
@@ -196,7 +197,7 @@
state.getSource(control.getType()).setVisible(shown);
}
Insets insets = getInsetsFromState(state, mFrame, null /* typeSideMap */);
- setInsetsAndAlpha(insets, 1f /* alpha */, shown ? 1f : 0f /* fraction */);
+ setInsetsAndAlpha(insets, 1f /* alpha */, 1f /* fraction */);
mFinished = true;
mShownOnFinish = shown;
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 411e910..c6e3835 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -28,6 +28,7 @@
import android.animation.TypeEvaluator;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.RemoteException;
@@ -145,7 +146,7 @@
controller.setInsetsAndAlpha(
value, 1f /* alpha */, (((DefaultAnimationControlListener)
((InsetsAnimationControlImpl) controller).getListener())
- .getRawProgress()));
+ .getRawFraction()));
}
}
@@ -204,9 +205,8 @@
mController.finish(mShow);
}
- protected float getRawProgress() {
- float fraction = (float) mAnimator.getCurrentPlayTime() / mAnimator.getDuration();
- return mShow ? fraction : 1 - fraction;
+ protected float getRawFraction() {
+ return (float) mAnimator.getCurrentPlayTime() / mAnimator.getDuration();
}
protected long getDurationMs() {
@@ -437,27 +437,29 @@
@Override
public void controlWindowInsetsAnimation(@InsetsType int types, long durationMs,
- WindowInsetsAnimationControlListener listener) {
- controlWindowInsetsAnimation(types, listener, false /* fromIme */, durationMs,
+ @Nullable Interpolator interpolator,
+ @NonNull WindowInsetsAnimationControlListener listener) {
+ controlWindowInsetsAnimation(types, listener, false /* fromIme */, durationMs, interpolator,
ANIMATION_TYPE_USER);
}
private void controlWindowInsetsAnimation(@InsetsType int types,
WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs,
- @AnimationType int animationType) {
+ @Nullable Interpolator interpolator, @AnimationType int animationType) {
// If the frame of our window doesn't span the entire display, the control API makes very
// little sense, as we don't deal with negative insets. So just cancel immediately.
if (!mState.getDisplayFrame().equals(mFrame)) {
listener.onCancelled();
return;
}
- controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, false /* fade */,
- animationType, getLayoutInsetsDuringAnimationMode(types));
+ controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, interpolator,
+ false /* fade */, animationType, getLayoutInsetsDuringAnimationMode(types));
}
private void controlAnimationUnchecked(@InsetsType int types,
WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme,
- long durationMs, boolean fade, @AnimationType int animationType,
+ long durationMs, Interpolator interpolator, boolean fade,
+ @AnimationType int animationType,
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
if (types == 0) {
// nothing to animate.
@@ -488,7 +490,7 @@
}
final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(controls,
- frame, mState, listener, typesReady, this, durationMs, fade,
+ frame, mState, listener, typesReady, this, durationMs, interpolator, fade,
layoutInsetsDuringAnimation);
mRunningAnimations.add(new RunningAnimation(controller, animationType));
}
@@ -733,7 +735,7 @@
// and hidden state insets are correct.
controlAnimationUnchecked(
types, listener, mState.getDisplayFrame(), fromIme, listener.getDurationMs(),
- true /* fade */, show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE,
+ INTERPOLATOR, true /* fade */, show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE,
show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
: LAYOUT_INSETS_DURING_ANIMATION_HIDDEN);
}
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 7707ad1..a6b7c33 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
@@ -88,6 +89,8 @@
private static native int nativeSetSharedBufferModeEnabled(long nativeObject, boolean enabled);
private static native int nativeSetAutoRefreshEnabled(long nativeObject, boolean enabled);
+ private static native int nativeSetFrameRate(long nativeObject, float frameRate);
+
public static final @android.annotation.NonNull Parcelable.Creator<Surface> CREATOR =
new Parcelable.Creator<Surface>() {
@Override
@@ -841,6 +844,34 @@
}
/**
+ * Sets the intended frame rate for this surface.
+ *
+ * On devices that are capable of running the display at different refresh rates, the
+ * system may choose a display refresh rate to better match this surface's frame
+ * rate. Usage of this API won't introduce frame rate throttling, or affect other
+ * aspects of the application's frame production pipeline. However, because the system
+ * may change the display refresh rate, calls to this function may result in changes
+ * to Choreographer callback timings, and changes to the time interval at which the
+ * system releases buffers back to the application.
+ *
+ * Note that this only has an effect for surfaces presented on the display. If this
+ * surface is consumed by something other than the system compositor, e.g. a media
+ * codec, this call has no effect.
+ *
+ * @param frameRate The intended frame rate of this surface. 0 is a special value that
+ * indicates the app will accept the system's choice for the display frame rate, which
+ * is the default behavior if this function isn't called. The frameRate param does
+ * *not* need to be a valid refresh rate for this device's display - e.g., it's fine
+ * to pass 30fps to a device that can only run the display at 60fps.
+ */
+ public void setFrameRate(@FloatRange(from = 0.0) float frameRate) {
+ int error = nativeSetFrameRate(mNativeObject, frameRate);
+ if (error != 0) {
+ throw new RuntimeException("Failed to set frame rate on Surface");
+ }
+ }
+
+ /**
* Exception thrown when a Canvas couldn't be locked with {@link Surface#lockCanvas}, or
* when a SurfaceTexture could not successfully be allocated.
*/
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index bcc9e41..f7b87cc 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -211,6 +211,9 @@
private static native void nativeSetGlobalShadowSettings(@Size(4) float[] ambientColor,
@Size(4) float[] spotColor, float lightPosY, float lightPosZ, float lightRadius);
+ private static native void nativeSetFrameRate(
+ long transactionObj, long nativeObject, float frameRate);
+
private final CloseGuard mCloseGuard = CloseGuard.get();
private String mName;
/**
@@ -2787,6 +2790,33 @@
}
/**
+ * Sets the intended frame rate for the surface {@link SurfaceControl}.
+ *
+ * On devices that are capable of running the display at different refresh rates, the system
+ * may choose a display refresh rate to better match this surface's frame rate. Usage of
+ * this API won't directly affect the application's frame production pipeline. However,
+ * because the system may change the display refresh rate, calls to this function may result
+ * in changes to Choreographer callback timings, and changes to the time interval at which
+ * the system releases buffers back to the application.
+ *
+ * @param sc The SurfaceControl to specify the frame rate of.
+ * @param frameRate The intended frame rate for this surface. 0 is a special value that
+ * indicates the app will accept the system's choice for the display frame
+ * rate, which is the default behavior if this function isn't called. The
+ * frameRate param does *not* need to be a valid refresh rate for this
+ * device's display - e.g., it's fine to pass 30fps to a device that can
+ * only run the display at 60fps.
+ * @return This transaction object.
+ */
+ @NonNull
+ public Transaction setFrameRate(
+ @NonNull SurfaceControl sc, @FloatRange(from = 0.0) float frameRate) {
+ checkPreconditions(sc);
+ nativeSetFrameRate(mNativeObject, sc.mNativeObject, frameRate);
+ return this;
+ }
+
+ /**
* Merge the other transaction into this transaction, clearing the
* other transaction as if it had been applied.
*
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 4f8aecd..71cf051 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -20,15 +20,21 @@
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.content.Context;
+import android.graphics.PixelFormat;
import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
/**
- * Utility class for adding a view hierarchy to a SurfaceControl.
- *
- * See WindowlessWmTest for example usage.
- * @hide
+ * Utility class for adding a View hierarchy to a {@link SurfaceControl}. The View hierarchy
+ * will render in to a root SurfaceControl, and receive input based on the SurfaceControl's
+ * placement on-screen. The primary usage of this class is to embed a View hierarchy from
+ * one process in to another. After the SurfaceControlViewHost has been set up in the embedded
+ * content provider, we can send the {@link SurfaceControlViewHost.SurfacePackage}
+ * to the host process. The host process can then attach the hierarchy to a SurfaceView within
+ * its own by calling
+ * {@link SurfaceView#setChildSurfacePackage}.
*/
-@TestApi
public class SurfaceControlViewHost {
private ViewRootImpl mViewRoot;
private WindowlessWindowManager mWm;
@@ -36,20 +42,52 @@
private SurfaceControl mSurfaceControl;
/**
- * @hide
+ * Package encapsulating a Surface hierarchy which contains interactive view
+ * elements. It's expected to get this object from
+ * {@link SurfaceControlViewHost#getSurfacePackage} afterwards it can be embedded within
+ * a SurfaceView by calling {@link SurfaceView#setChildSurfacePackage}.
*/
- @TestApi
- public class SurfacePackage {
- final SurfaceControl mSurfaceControl;
+ public static final class SurfacePackage implements Parcelable {
+ private final SurfaceControl mSurfaceControl;
// TODO: Accessibility ID goes here
SurfacePackage(SurfaceControl sc) {
mSurfaceControl = sc;
}
+ private SurfacePackage(Parcel in) {
+ mSurfaceControl = new SurfaceControl();
+ mSurfaceControl.readFromParcel(in);
+ }
+
+ /**
+ * Use {@link SurfaceView#setChildSurfacePackage} or manually fix
+ * accessibility (see SurfaceView implementation).
+ * @hide
+ */
public @NonNull SurfaceControl getSurfaceControl() {
return mSurfaceControl;
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ mSurfaceControl.writeToParcel(out, flags);
+ }
+
+ public static final @NonNull Creator<SurfacePackage> CREATOR
+ = new Creator<SurfacePackage>() {
+ public SurfacePackage createFromParcel(Parcel in) {
+ return new SurfacePackage(in);
+ }
+ public SurfacePackage[] newArray(int size) {
+ return new SurfacePackage[size];
+ }
+ };
}
/** @hide */
@@ -59,17 +97,36 @@
mViewRoot = new ViewRootImpl(c, d, mWm);
}
- public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d,
- @Nullable IBinder hostInputToken) {
+ /**
+ * Construct a new SurfaceControlViewHost. The root Surface will be
+ * allocated internally and is accessible via getSurfacePackage().
+ *
+ * The {@param hostToken} parameter, primarily used for ANR reporting,
+ * must be obtained from whomever will be hosting the embedded hierarchy.
+ * It's accessible from {@link SurfaceView#getHostToken}.
+ *
+ * @param context The Context object for your activity or application.
+ * @param display The Display the hierarchy will be placed on.
+ * @param hostToken The host token, as discussed above.
+ */
+ public SurfaceControlViewHost(@NonNull Context context, @NonNull Display display,
+ @Nullable IBinder hostToken) {
mSurfaceControl = new SurfaceControl.Builder()
.setContainerLayer()
.setName("SurfaceControlViewHost")
.build();
- mWm = new WindowlessWindowManager(c.getResources().getConfiguration(), mSurfaceControl,
- hostInputToken);
- mViewRoot = new ViewRootImpl(c, d, mWm);
+ mWm = new WindowlessWindowManager(context.getResources().getConfiguration(),
+ mSurfaceControl, hostToken);
+ mViewRoot = new ViewRootImpl(context, display, mWm);
}
+ /**
+ * Return a SurfacePackage for the root SurfaceControl of the embedded hierarchy.
+ * Rather than be directly reparented using {@link SurfaceControl.Transaction} this
+ * SurfacePackage should be passed to {@link SurfaceView#setChildSurfacePackage}
+ * which will not only reparent the Surface, but ensure the accessibility hierarchies
+ * are linked.
+ */
public @Nullable SurfacePackage getSurfacePackage() {
if (mSurfaceControl != null) {
return new SurfacePackage(mSurfaceControl);
@@ -78,10 +135,32 @@
}
}
- public void addView(View view, WindowManager.LayoutParams attrs) {
+ /**
+ * @hide
+ */
+ public void addView(@NonNull View view, WindowManager.LayoutParams attrs) {
mViewRoot.setView(view, attrs, null);
}
+ /**
+ * Set the root view of the SurfaceControlViewHost. This view will render in to
+ * the SurfaceControl, and receive input based on the SurfaceControls positioning on
+ * screen. It will be laid as if it were in a window of the passed in width and height.
+ *
+ * @param view The View to add
+ * @param width The width to layout the View within, in pixels.
+ * @param height The height to layout the View within, in pixels.
+ */
+ public void addView(@NonNull View view, int width, int height) {
+ final WindowManager.LayoutParams lp =
+ new WindowManager.LayoutParams(width, height,
+ WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT);
+ addView(view, lp);
+ }
+
+ /**
+ * @hide
+ */
public void relayout(WindowManager.LayoutParams attrs) {
mViewRoot.setLayoutParams(attrs, false);
mViewRoot.setReportNextDraw();
@@ -90,8 +169,27 @@
});
}
- public void dispose() {
+ /**
+ * Modify the size of the root view.
+ *
+ * @param width Width in pixels
+ * @param height Height in pixels
+ */
+ public void relayout(int width, int height) {
+ final WindowManager.LayoutParams lp =
+ new WindowManager.LayoutParams(width, height,
+ WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT);
+ relayout(width, height);
+ }
+
+ /**
+ * Trigger the tear down of the embedded view hierarchy and release the SurfaceControl.
+ * This will result in onDispatchedFromWindow being dispatched to the embedded view hierarchy
+ * and render the object unusable.
+ */
+ public void release() {
mViewRoot.dispatchDetachedFromWindow();
+ mSurfaceControl.release();
}
/**
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 0de1a4f..1981bdd 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -20,6 +20,7 @@
import static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_SUBLAYER;
import static android.view.WindowManagerPolicyConstants.APPLICATION_PANEL_SUBLAYER;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -43,6 +44,7 @@
import android.util.Log;
import android.view.SurfaceControl.Transaction;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.SurfaceControlViewHost;
import com.android.internal.view.SurfaceCallbackHelper;
@@ -204,6 +206,7 @@
// The token of embedded windowless view hierarchy.
private IBinder mEmbeddedViewHierarchy;
+ SurfaceControlViewHost.SurfacePackage mSurfacePackage;
public SurfaceView(Context context) {
this(context, null);
@@ -877,6 +880,11 @@
} else {
mTmpTransaction.hide(mSurfaceControl);
}
+
+ if (mSurfacePackage != null) {
+ reparentSurfacePackage(mTmpTransaction, mSurfacePackage);
+ }
+
updateBackgroundVisibility(mTmpTransaction);
if (mUseAlpha) {
mTmpTransaction.setAlpha(mSurfaceControl, alpha);
@@ -1471,11 +1479,12 @@
}
/**
- * @return The token used to identify the windows input channel.
- * @hide
+ * A token used for constructing {@link SurfaceControlViewHost}. This token should
+ * be passed from the host process to the client process.
+ *
+ * @return The token
*/
- @TestApi
- public @Nullable IBinder getInputToken() {
+ public @Nullable IBinder getHostToken() {
final ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot == null) {
return null;
@@ -1537,6 +1546,33 @@
}
/**
+ * Display the view-hierarchy embedded within a {@link SurfaceControlViewHost.SurfacePackage}
+ * within this SurfaceView. If this SurfaceView is above it's host Surface (see
+ * {@link #setZOrderOnTop} then the embedded Surface hierarchy will be able to receive
+ * input.
+ *
+ * @param p The SurfacePackage to embed.
+ */
+ public void setChildSurfacePackage(@NonNull SurfaceControlViewHost.SurfacePackage p) {
+ final SurfaceControl sc = p != null ? p.getSurfaceControl() : null;
+ final SurfaceControl lastSc = mSurfacePackage != null ?
+ mSurfacePackage.getSurfaceControl() : null;
+ if (mSurfaceControl != null && lastSc != null) {
+ mTmpTransaction.reparent(lastSc, null).apply();
+ } else if (mSurfaceControl != null) {
+ reparentSurfacePackage(mTmpTransaction, p);
+ mTmpTransaction.apply();
+ }
+ mSurfacePackage = p;
+ }
+
+ private void reparentSurfacePackage(SurfaceControl.Transaction t,
+ SurfaceControlViewHost.SurfacePackage p) {
+ // TODO: Link accessibility IDs here.
+ t.reparent(p.getSurfaceControl(), mSurfaceControl);
+ }
+
+ /**
* Add the token of embedded view hierarchy. Set {@code null} to clear the embedded view
* hierarchy.
*
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 377a764..0f2d2c2 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -22,7 +22,10 @@
import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP;
import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
-import static android.view.WindowInsetsAnimationCallback.DISPATCH_MODE_CONTINUE_ON_SUBTREE;
+import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowInsets.Type.systemBars;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
import static java.lang.Math.max;
@@ -97,6 +100,7 @@
import android.util.LayoutDirection;
import android.util.Log;
import android.util.LongSparseLongArray;
+import android.util.Pair;
import android.util.Pools.SynchronizedPool;
import android.util.Property;
import android.util.SparseArray;
@@ -110,9 +114,11 @@
import android.view.AccessibilityIterators.TextSegmentIterator;
import android.view.AccessibilityIterators.WordTextSegmentIterator;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.Window.OnContentApplyWindowInsetsListener;
+import android.view.WindowInsets.Type;
import android.view.WindowInsetsAnimationCallback.AnimationBounds;
import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
-import android.view.WindowInsetsAnimationCallback.DispatchMode;
+import android.view.WindowManager.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityEventSource;
import android.view.accessibility.AccessibilityManager;
@@ -140,6 +146,7 @@
import android.widget.ScrollBarDrawable;
import com.android.internal.R;
+import com.android.internal.policy.DecorView;
import com.android.internal.view.TooltipPopup;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.widget.ScrollBarUtils;
@@ -1510,6 +1517,10 @@
* Set for framework elements that use FITS_SYSTEM_WINDOWS, to indicate
* that they are optional and should be skipped if the window has
* requested system UI flags that ignore those insets for layout.
+ * <p>
+ * This is only used for support library as of Android R. The framework now uses
+ * {@link #PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS} such that it can skip the legacy
+ * insets path that loses insets information.
*/
static final int OPTIONAL_FITS_SYSTEM_WINDOWS = 0x00000800;
@@ -2258,7 +2269,7 @@
* be extended in the future to hold our own class with more than just
* a Rect. :)
*/
- static final ThreadLocal<Rect> sThreadLocal = new ThreadLocal<Rect>();
+ static final ThreadLocal<Rect> sThreadLocal = ThreadLocal.withInitial(Rect::new);
/**
* Map used to store views' tags.
@@ -3420,6 +3431,7 @@
* 1 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED
* 1 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE
* 11 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK
+ * 1 PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS
* |-------|-------|-------|-------|
*/
@@ -3457,6 +3469,11 @@
PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED
| PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE;
+ /**
+ * @see #OPTIONAL_FITS_SYSTEM_WINDOWS
+ */
+ static final int PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS = 0x000000100;
+
/* End of masks for mPrivateFlags4 */
/** @hide */
@@ -3506,7 +3523,10 @@
* requested the system UI (status bar) to be visible (the default).
*
* @see #setSystemUiVisibility(int)
+ * @deprecated SystemUiVisibility flags are deprecated. Use {@link WindowInsetsController}
+ * instead.
*/
+ @Deprecated
public static final int SYSTEM_UI_FLAG_VISIBLE = 0;
/**
@@ -3519,7 +3539,11 @@
* <p>In low profile mode, the status bar and/or navigation icons may dim.
*
* @see #setSystemUiVisibility(int)
+ * @deprecated Low profile mode is deprecated. Hide the system bars instead if the application
+ * needs to be in a unobtrusive mode. Use {@link WindowInsetsController#hide(int)} with
+ * {@link Type#systemBars()}.
*/
+ @Deprecated
public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 0x00000001;
/**
@@ -3540,7 +3564,10 @@
* so that both elements reappear at the same time.
*
* @see #setSystemUiVisibility(int)
+ * @deprecated Use {@link WindowInsetsController#hide(int)} with {@link Type#navigationBars()}
+ * instead.
*/
+ @Deprecated
public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 0x00000002;
/**
@@ -3576,7 +3603,10 @@
* the book.
*
* @see #setSystemUiVisibility(int)
+ * @deprecated Use {@link WindowInsetsController#hide(int)} with {@link Type#statusBars()}
+ * instead.
*/
+ @Deprecated
public static final int SYSTEM_UI_FLAG_FULLSCREEN = 0x00000004;
/**
@@ -3610,7 +3640,11 @@
* overlay mode with {@link Window#FEATURE_ACTION_BAR_OVERLAY
* Window.FEATURE_ACTION_BAR_OVERLAY}, this flag will also impact the
* insets it adds to those given to the application.
+ *
+ * @deprecated Use {@link WindowInsets#getInsetsIgnoringVisibility(int)} instead to retrieve
+ * insets that don't change when system bars change visibility state.
*/
+ @Deprecated
public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 0x00000100;
/**
@@ -3622,6 +3656,11 @@
* decorations when they are shown. You can perform layout of your inner
* UI elements to account for the navigation system UI through the
* {@link #fitSystemWindows(Rect)} method.
+ *
+ * @deprecated For floating windows, use {@link LayoutParams#setFitInsetsTypes(int)} with
+ * {@link Type#navigationBars()}. For non-floating windows that fill the screen, call
+ * {@link Window#setOnContentApplyWindowInsets} with {@code null} or a listener that doesn't
+ * fit the navigation bar on the window content level.
*/
public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 0x00000200;
@@ -3646,7 +3685,13 @@
* @see WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
* @see WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
* @see WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
+ *
+ * @deprecated For floating windows, use {@link LayoutParams#setFitInsetsTypes(int)} with
+ * {@link Type#statusBars()} ()}. For non-floating windows that fill the screen, call
+ * {@link Window#setOnContentApplyWindowInsets} with {@code null} or a listener that doesn't
+ * fit the status bar on the window content level.
*/
+ @Deprecated
public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400;
/**
@@ -3656,7 +3701,10 @@
* user interaction.
* <p>Since this flag is a modifier for {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, it only
* has an effect when used in combination with that flag.</p>
+ *
+ * @deprecated Use {@link WindowInsetsController#BEHAVIOR_SHOW_BARS_BY_SWIPE} instead.
*/
+ @Deprecated
public static final int SYSTEM_UI_FLAG_IMMERSIVE = 0x00000800;
/**
@@ -3674,7 +3722,10 @@
* </p><p>Since this flag is a modifier for {@link #SYSTEM_UI_FLAG_FULLSCREEN} and
* {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, it only has an effect when used in combination
* with one or both of those flags.</p>
+ *
+ * @deprecated Use {@link WindowInsetsController#BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE} instead.
*/
+ @Deprecated
public static final int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 0x00001000;
/**
@@ -3688,7 +3739,9 @@
* FLAG_TRANSLUCENT_STATUS}.
*
* @see android.R.attr#windowLightStatusBar
+ * @deprecated Use {@link WindowInsetsController#APPEARANCE_LIGHT_STATUS_BARS} instead.
*/
+ @Deprecated
public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0x00002000;
/**
@@ -3714,7 +3767,9 @@
* FLAG_TRANSLUCENT_NAVIGATION}.
*
* @see android.R.attr#windowLightNavigationBar
+ * @deprecated Use {@link WindowInsetsController#APPEARANCE_LIGHT_NAVIGATION_BARS} instead.
*/
+ @Deprecated
public static final int SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR = 0x00000010;
/**
@@ -3942,7 +3997,10 @@
/**
* Flags that can impact the layout in relation to system UI.
+ *
+ * @deprecated System UI layout flags are deprecated.
*/
+ @Deprecated
public static final int SYSTEM_UI_LAYOUT_FLAGS =
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
@@ -11020,23 +11078,22 @@
private boolean fitSystemWindowsInt(Rect insets) {
if ((mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS) {
- mUserPaddingStart = UNDEFINED_PADDING;
- mUserPaddingEnd = UNDEFINED_PADDING;
Rect localInsets = sThreadLocal.get();
- if (localInsets == null) {
- localInsets = new Rect();
- sThreadLocal.set(localInsets);
- }
boolean res = computeFitSystemWindows(insets, localInsets);
- mUserPaddingLeftInitial = localInsets.left;
- mUserPaddingRightInitial = localInsets.right;
- internalSetPadding(localInsets.left, localInsets.top,
- localInsets.right, localInsets.bottom);
+ applyInsets(localInsets);
return res;
}
return false;
}
+ private void applyInsets(Rect insets) {
+ mUserPaddingStart = UNDEFINED_PADDING;
+ mUserPaddingEnd = UNDEFINED_PADDING;
+ mUserPaddingLeftInitial = insets.left;
+ mUserPaddingRightInitial = insets.right;
+ internalSetPadding(insets.left, insets.top, insets.right, insets.bottom);
+ }
+
/**
* Called when the view should apply {@link WindowInsets} according to its internal policy.
*
@@ -11063,6 +11120,10 @@
* @return The supplied insets with any applied insets consumed
*/
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ if ((mPrivateFlags4 & PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS) != 0
+ && (mViewFlags & FITS_SYSTEM_WINDOWS) != 0) {
+ return onApplyFrameworkOptionalFitSystemWindows(insets);
+ }
if ((mPrivateFlags3 & PFLAG3_FITTING_SYSTEM_WINDOWS) == 0) {
// We weren't called from within a direct call to fitSystemWindows,
// call into it as a fallback in case we're in a class that overrides it
@@ -11079,6 +11140,13 @@
return insets;
}
+ private WindowInsets onApplyFrameworkOptionalFitSystemWindows(WindowInsets insets) {
+ Rect localInsets = sThreadLocal.get();
+ WindowInsets result = computeSystemWindowInsets(insets, localInsets);
+ applyInsets(localInsets);
+ return result;
+ }
+
/**
* Set an {@link OnApplyWindowInsetsListener} to take over the policy for applying
* window insets to this view. The listener's
@@ -11369,16 +11437,23 @@
* @return Insets that should be passed along to views under this one
*/
public WindowInsets computeSystemWindowInsets(WindowInsets in, Rect outLocalInsets) {
- if ((mViewFlags & OPTIONAL_FITS_SYSTEM_WINDOWS) == 0
- || mAttachInfo == null
- || ((mAttachInfo.mSystemUiVisibility & SYSTEM_UI_LAYOUT_FLAGS) == 0)) {
+ boolean isOptionalFitSystemWindows = (mViewFlags & OPTIONAL_FITS_SYSTEM_WINDOWS) != 0
+ || (mPrivateFlags4 & PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS) != 0;
+ if (isOptionalFitSystemWindows && mAttachInfo != null) {
+ OnContentApplyWindowInsetsListener listener =
+ mAttachInfo.mContentOnApplyWindowInsetsListener;
+ if (listener == null) {
+ // The application wants to take care of fitting system window for
+ // the content.
+ outLocalInsets.setEmpty();
+ return in;
+ }
+ Pair<Insets, WindowInsets> result = listener.onContentApplyWindowInsets(in);
+ outLocalInsets.set(result.first.toRect());
+ return result.second;
+ } else {
outLocalInsets.set(in.getSystemWindowInsetsAsRect());
return in.consumeSystemWindowInsets().inset(outLocalInsets);
- } else {
- // The application wants to take care of fitting system window for
- // the content.
- outLocalInsets.setEmpty();
- return in;
}
}
@@ -11449,7 +11524,7 @@
}
/**
- * For use by PhoneWindow to make its own system window fitting optional.
+ * @see #OPTIONAL_FITS_SYSTEM_WINDOWS
* @hide
*/
@UnsupportedAppUsage
@@ -11458,6 +11533,14 @@
}
/**
+ * @see #PFLAG4_OPTIONAL_FITS_SYSTEM_WINDOWS
+ * @hide
+ */
+ public void makeFrameworkOptionalFitsSystemWindows() {
+ mPrivateFlags4 |= PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS;
+ }
+
+ /**
* Returns the visibility status for this view.
*
* @return One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
@@ -25743,7 +25826,11 @@
* {@link #SYSTEM_UI_FLAG_LAYOUT_STABLE}, {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION},
* {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}, {@link #SYSTEM_UI_FLAG_IMMERSIVE},
* and {@link #SYSTEM_UI_FLAG_IMMERSIVE_STICKY}.
+ *
+ * @deprecated SystemUiVisibility flags are deprecated. Use {@link WindowInsetsController}
+ * instead.
*/
+ @Deprecated
public void setSystemUiVisibility(int visibility) {
if (visibility != mSystemUiVisibility) {
mSystemUiVisibility = visibility;
@@ -25760,7 +25847,11 @@
* {@link #SYSTEM_UI_FLAG_LAYOUT_STABLE}, {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION},
* {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}, {@link #SYSTEM_UI_FLAG_IMMERSIVE},
* and {@link #SYSTEM_UI_FLAG_IMMERSIVE_STICKY}.
+ *
+ * @deprecated SystemUiVisibility flags are deprecated. Use {@link WindowInsetsController}
+ * instead.
*/
+ @Deprecated
public int getSystemUiVisibility() {
return mSystemUiVisibility;
}
@@ -25770,7 +25861,11 @@
* the entire window. This is the combination of the
* {@link #setSystemUiVisibility(int)} values supplied by all of the
* views in the window.
+ *
+ * @deprecated SystemUiVisibility flags are deprecated. Use {@link WindowInsetsController}
+ * instead.
*/
+ @Deprecated
public int getWindowSystemUiVisibility() {
return mAttachInfo != null ? mAttachInfo.mSystemUiVisibility : 0;
}
@@ -25782,14 +25877,22 @@
* {@link #setOnSystemUiVisibilityChangeListener(OnSystemUiVisibilityChangeListener)}
* in that this is only telling you about the local request of the window,
* not the actual values applied by the system.
+ *
+ * @deprecated SystemUiVisibility flags are deprecated. Use {@link WindowInsetsController}
+ * instead.
*/
+ @Deprecated
public void onWindowSystemUiVisibilityChanged(int visible) {
}
/**
* Dispatch callbacks to {@link #onWindowSystemUiVisibilityChanged(int)} down
* the view hierarchy.
+ *
+ * @deprecated SystemUiVisibility flags are deprecated. Use {@link WindowInsetsController}
+ * instead.
*/
+ @Deprecated
public void dispatchWindowSystemUiVisiblityChanged(int visible) {
onWindowSystemUiVisibilityChanged(visible);
}
@@ -25797,7 +25900,11 @@
/**
* Set a listener to receive callbacks when the visibility of the system bar changes.
* @param l The {@link OnSystemUiVisibilityChangeListener} to receive callbacks.
+ *
+ * @deprecated Use {@link WindowInsets#isVisible(int)} to find out about system bar visibilities
+ * by setting a {@link OnApplyWindowInsetsListener} on this view.
*/
+ @Deprecated
public void setOnSystemUiVisibilityChangeListener(OnSystemUiVisibilityChangeListener l) {
getListenerInfo().mOnSystemUiVisibilityChangeListener = l;
if (mParent != null && mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
@@ -25808,7 +25915,11 @@
/**
* Dispatch callbacks to {@link #setOnSystemUiVisibilityChangeListener} down
* the view hierarchy.
+ *
+ * @deprecated Use {@link WindowInsets#isVisible(int)} to find out about system bar visibilities
+ * by setting a {@link OnApplyWindowInsetsListener} on this view.
*/
+ @Deprecated
public void dispatchSystemUiVisibilityChanged(int visibility) {
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnSystemUiVisibilityChangeListener != null) {
@@ -28249,7 +28360,11 @@
* state, not what the application is requesting.
*
* @see View#setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener)
+ *
+ * @deprecated Use {@link WindowInsets#isVisible(int)} to find out about system bar visibilities
+ * by setting a {@link OnApplyWindowInsetsListener} on this view.
*/
+ @Deprecated
public interface OnSystemUiVisibilityChangeListener {
/**
* Called when the status bar changes visibility because of a call to
@@ -28415,6 +28530,7 @@
* window.
*/
final static class AttachInfo {
+
interface Callbacks {
void playSoundEffect(int effectId);
boolean performHapticFeedback(int effectId, boolean always);
@@ -28854,6 +28970,11 @@
ContentCaptureManager mContentCaptureManager;
/**
+ * Listener used to fit content on window level.
+ */
+ OnContentApplyWindowInsetsListener mContentOnApplyWindowInsetsListener;
+
+ /**
* Creates a new set of attachment information with the specified
* events handler and thread.
*
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index e6470a7..4f03ca1 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -55,6 +55,7 @@
import android.util.Pools.SynchronizedPool;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.view.Window.OnContentApplyWindowInsetsListener;
import android.view.WindowInsetsAnimationCallback.AnimationBounds;
import android.view.WindowInsetsAnimationCallback.DispatchMode;
import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
@@ -1527,6 +1528,19 @@
}
}
+ /**
+ * @hide
+ */
+ @Override
+ public void makeFrameworkOptionalFitsSystemWindows() {
+ super.makeFrameworkOptionalFitsSystemWindows();
+ final int count = mChildrenCount;
+ final View[] children = mChildren;
+ for (int i = 0; i < count; i++) {
+ children[i].makeFrameworkOptionalFitsSystemWindows();
+ }
+ }
+
@Override
public void dispatchDisplayHint(int hint) {
super.dispatchDisplayHint(hint);
@@ -1871,6 +1885,7 @@
}
@Override
+ @Deprecated
public void dispatchWindowSystemUiVisiblityChanged(int visible) {
super.dispatchWindowSystemUiVisiblityChanged(visible);
@@ -1883,6 +1898,7 @@
}
@Override
+ @Deprecated
public void dispatchSystemUiVisibilityChanged(int visible) {
super.dispatchSystemUiVisibilityChanged(visible);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 09ebd00..f5cfbec 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -122,6 +122,7 @@
import android.view.View.AttachInfo;
import android.view.View.FocusDirection;
import android.view.View.MeasureSpec;
+import android.view.Window.OnContentApplyWindowInsetsListener;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
@@ -755,6 +756,11 @@
mActivityConfigCallback = callback;
}
+ public void setOnContentApplyWindowInsetsListener(OnContentApplyWindowInsetsListener listener) {
+ mAttachInfo.mContentOnApplyWindowInsetsListener = listener;
+ requestFitSystemWindows();
+ }
+
public void addWindowCallbacks(WindowCallbacks callback) {
synchronized (mWindowCallbacks) {
mWindowCallbacks.add(callback);
@@ -1442,6 +1448,7 @@
// Get new instance of display based on current display adjustments. It may be updated later
// if moving between the displays also involved a configuration change.
updateInternalDisplay(displayId, mView.getResources());
+ mImeFocusController.onMovedToDisplay();
mAttachInfo.mDisplayState = mDisplay.getState();
// Internal state updated, now notify the view hierarchy.
mView.dispatchMovedToDisplay(mDisplay, config);
@@ -1978,9 +1985,9 @@
return;
}
- int types = inOutParams.getFitWindowInsetsTypes();
- int sides = inOutParams.getFitWindowInsetsSides();
- boolean ignoreVis = inOutParams.getFitIgnoreVisibility();
+ int types = inOutParams.getFitInsetsTypes();
+ int sides = inOutParams.getFitInsetsSides();
+ boolean ignoreVis = inOutParams.isFitInsetsIgnoringVisibility();
if (((sysUiVis & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0
|| (flags & FLAG_LAYOUT_IN_SCREEN) != 0)
@@ -1997,9 +2004,9 @@
&& adjust == SOFT_INPUT_ADJUST_RESIZE) {
types |= Type.ime();
}
- inOutParams.setFitWindowInsetsTypes(types);
- inOutParams.setFitWindowInsetsSides(sides);
- inOutParams.setFitIgnoreVisibility(ignoreVis);
+ inOutParams.setFitInsetsTypes(types);
+ inOutParams.setFitInsetsSides(sides);
+ inOutParams.setFitInsetsIgnoringVisibility(ignoreVis);
// The fitting of insets are not really controlled by the clients, so we remove the flag.
inOutParams.privateFlags &= ~PRIVATE_FLAG_FIT_INSETS_CONTROLLED;
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index a1894f3..0ef4e33 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -33,6 +33,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -46,6 +47,9 @@
import android.transition.Scene;
import android.transition.Transition;
import android.transition.TransitionManager;
+import android.util.Pair;
+import android.view.View.OnApplyWindowInsetsListener;
+import android.view.ViewGroup.LayoutParams;
import android.view.WindowInsets.Side.InsetsSide;
import android.view.WindowInsets.Type.InsetsType;
import android.view.accessibility.AccessibilityEvent;
@@ -692,6 +696,32 @@
int dropCountSinceLastInvocation);
}
+ /**
+ * Listener for applying window insets on the content of a window in a custom way.
+ *
+ * <p>Apps may choose to implement this interface if they want to apply custom policy
+ * to the way that window insets are treated for fitting root-level content views.
+ *
+ * @see Window#setOnContentApplyWindowInsetsListener(OnContentApplyWindowInsetsListener)
+ */
+ public interface OnContentApplyWindowInsetsListener {
+
+ /**
+ * Called when the window needs to apply insets on the container of its content view which
+ * are set by calling {@link #setContentView}. The method should determine what insets to
+ * apply on the container of the root level content view and what should be dispatched to
+ * the content view's
+ * {@link View#setOnApplyWindowInsetsListener(OnApplyWindowInsetsListener)} through the view
+ * hierarchy.
+ *
+ * @param insets The root level insets that are about to be dispatched
+ * @return A pair, with the first element containing the insets to apply as margin to the
+ * root-level content views, and the second element determining what should be
+ * dispatched to the content view.
+ */
+ @NonNull Pair<Insets, WindowInsets> onContentApplyWindowInsets(
+ @NonNull WindowInsets insets);
+ }
public Window(Context context) {
mContext = context;
@@ -1281,57 +1311,33 @@
}
/**
- * A shortcut for {@link WindowManager.LayoutParams#setFitWindowInsetsTypes(int)}
- * @hide pending unhide
+ * Sets the listener to be invoked when fitting root-level content views.
+ * <p>
+ * By default, a listener that inspects the now deprecated {@link View#SYSTEM_UI_LAYOUT_FLAGS}
+ * as well the {@link WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE} flag is installed and
+ * fits content according to these flags.
+ * </p>
+ * @param contentOnApplyWindowInsetsListener The listener to use for fitting root-level content
+ * views, or {@code null} to disable any kind of
+ * content fitting on the window level and letting the
+ * {@link WindowInsets} pass through to the content
+ * view.
+ * @see OnContentApplyWindowInsetsListener
*/
- public void setFitWindowInsetsTypes(@InsetsType int types) {
- final WindowManager.LayoutParams attrs = getAttributes();
- attrs.setFitWindowInsetsTypes(types);
- dispatchWindowAttributesChanged(attrs);
+ public void setOnContentApplyWindowInsetsListener(
+ @Nullable OnContentApplyWindowInsetsListener contentOnApplyWindowInsetsListener) {
}
/**
- * A shortcut for {@link WindowManager.LayoutParams#setFitWindowInsetsSides(int)}
- * @hide pending unhide
+ * Resets the listener set via {@link #setOnContentApplyWindowInsetsListener} to the default
+ * state.
+ * <p>
+ * By default, a listener that inspects the now deprecated {@link View#SYSTEM_UI_LAYOUT_FLAGS}
+ * as well the {@link WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE} flag is installed and
+ * fits content according to these flags.
+ * </p>
*/
- public void setFitWindowInsetsSides(@InsetsSide int sides) {
- final WindowManager.LayoutParams attrs = getAttributes();
- attrs.setFitWindowInsetsSides(sides);
- dispatchWindowAttributesChanged(attrs);
- }
-
- /**
- * A shortcut for {@link WindowManager.LayoutParams#setFitIgnoreVisibility(boolean)}
- * @hide pending unhide
- */
- public void setFitIgnoreVisibility(boolean ignore) {
- final WindowManager.LayoutParams attrs = getAttributes();
- attrs.setFitIgnoreVisibility(ignore);
- dispatchWindowAttributesChanged(attrs);
- }
-
- /**
- * A shortcut for {@link WindowManager.LayoutParams#getFitWindowInsetsTypes}
- * @hide pending unhide
- */
- public @InsetsType int getFitWindowInsetsTypes() {
- return getAttributes().getFitWindowInsetsTypes();
- }
-
- /**
- * A shortcut for {@link WindowManager.LayoutParams#getFitWindowInsetsSides()}
- * @hide pending unhide
- */
- public @InsetsSide int getFitWindowInsetsSides() {
- return getAttributes().getFitWindowInsetsSides();
- }
-
- /**
- * A shortcut for {@link WindowManager.LayoutParams#getFitIgnoreVisibility()}
- * @hide pending unhide
- */
- public boolean getFitIgnoreVisibility() {
- return getAttributes().getFitIgnoreVisibility();
+ public void resetOnContentApplyWindowInsetsListener() {
}
/**
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 0a2a45b..a6c311e 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -301,11 +301,14 @@
* </p>
*
* @return The system window insets
+ * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()}
+ * instead.
*/
+ @Deprecated
@NonNull
public Insets getSystemWindowInsets() {
Insets result = mCompatIgnoreVisibility
- ? getMaxInsets(mCompatInsetTypes & ~ime())
+ ? getInsetsIgnoringVisibility(mCompatInsetTypes & ~ime())
: getInsets(mCompatInsetTypes);
// We can't query max insets for IME, so we need to add it manually after.
@@ -328,25 +331,26 @@
}
/**
- * Returns the maximum amount of insets a specific set of windows can cause, denoted by the
- * {@code typeMask} bit mask of {@link InsetsType}s.
+ * Returns the insets a specific set of windows can cause, denoted by the
+ * {@code typeMask} bit mask of {@link InsetsType}s, regardless of whether that type is
+ * currently visible or not.
*
- * <p>The maximum insets represents the area of a a window that that <b>may</b> be partially
+ * <p>The insets represents the area of a a window that that <b>may</b> be partially
* or fully obscured by the system window identified by {@code type}. This value does not
- * change based on the visibility state of those elements. for example, if the status bar is
- * normally shown, but temporarily hidden, the maximum inset will still provide the inset
+ * change based on the visibility state of those elements. For example, if the status bar is
+ * normally shown, but temporarily hidden, the inset returned here will still provide the inset
* associated with the status bar being shown.</p>
*
* @param typeMask Bit mask of {@link InsetsType}s to query the insets for.
* @return The insets.
*
- * @throws IllegalArgumentException If the caller tries to query {@link Type#ime()}. Maximum
- * insets are not available for this type as the height of the
+ * @throws IllegalArgumentException If the caller tries to query {@link Type#ime()}. Insets are
+ * not available if the IME isn't visible as the height of the
* IME is dynamic depending on the {@link EditorInfo} of the
* currently focused view, as well as the UI state of the IME.
*/
@NonNull
- public Insets getMaxInsets(@InsetsType int typeMask) throws IllegalArgumentException {
+ public Insets getInsetsIgnoringVisibility(@InsetsType int typeMask) {
if ((typeMask & IME) != 0) {
throw new IllegalArgumentException("Unable to query the maximum insets for IME");
}
@@ -381,7 +385,10 @@
* </p>
*
* @return The left system window inset
+ * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()}
+ * instead.
*/
+ @Deprecated
public int getSystemWindowInsetLeft() {
return getSystemWindowInsets().left;
}
@@ -394,7 +401,10 @@
* </p>
*
* @return The top system window inset
+ * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()}
+ * instead.
*/
+ @Deprecated
public int getSystemWindowInsetTop() {
return getSystemWindowInsets().top;
}
@@ -407,7 +417,10 @@
* </p>
*
* @return The right system window inset
+ * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()}
+ * instead.
*/
+ @Deprecated
public int getSystemWindowInsetRight() {
return getSystemWindowInsets().right;
}
@@ -420,7 +433,10 @@
* </p>
*
* @return The bottom system window inset
+ * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()}
+ * instead.
*/
+ @Deprecated
public int getSystemWindowInsetBottom() {
return getSystemWindowInsets().bottom;
}
@@ -433,7 +449,10 @@
* </p>
*
* @return true if any of the system window inset values are nonzero
+ * @deprecated Use {@link #getInsets(int)} with {@link Type#systemBars()}
+ * instead.
*/
+ @Deprecated
public boolean hasSystemWindowInsets() {
return !getSystemWindowInsets().equals(Insets.NONE);
}
@@ -594,7 +613,10 @@
* associated with the status bar being shown.</p>
*
* @return The stable insets
+ * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()}
+ * instead.
*/
+ @Deprecated
@NonNull
public Insets getStableInsets() {
return getInsets(mTypeMaxInsetsMap, mCompatInsetTypes);
@@ -610,7 +632,10 @@
* associated with the status bar being shown.</p>
*
* @return The top stable inset
+ * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()}
+ * instead.
*/
+ @Deprecated
public int getStableInsetTop() {
return getStableInsets().top;
}
@@ -625,7 +650,10 @@
* associated with the status bar being shown.</p>
*
* @return The left stable inset
+ * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()}
+ * instead.
*/
+ @Deprecated
public int getStableInsetLeft() {
return getStableInsets().left;
}
@@ -640,7 +668,10 @@
* associated with the status bar being shown.</p>
*
* @return The right stable inset
+ * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()}
+ * instead.
*/
+ @Deprecated
public int getStableInsetRight() {
return getStableInsets().right;
}
@@ -655,7 +686,10 @@
* associated with the status bar being shown.</p>
*
* @return The bottom stable inset
+ * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()}
+ * instead.
*/
+ @Deprecated
public int getStableInsetBottom() {
return getStableInsets().bottom;
}
@@ -670,7 +704,10 @@
* associated with the status bar being shown.</p>
*
* @return true if any of the stable inset values are nonzero
+ * @deprecated Use {@link #getInsetsIgnoringVisibility(int)} with {@link Type#systemBars()}
+ * instead.
*/
+ @Deprecated
public boolean hasStableInsets() {
return !getStableInsets().equals(Insets.NONE);
}
@@ -706,7 +743,9 @@
* system window insets} by {@link #consumeSystemWindowInsets()}.
*
* @see #getMandatorySystemGestureInsets
+ * @deprecated Use {@link #getInsets(int)} with {@link Type#systemGestures()} instead.
*/
+ @Deprecated
@NonNull
public Insets getSystemGestureInsets() {
return getInsets(mTypeInsetsMap, SYSTEM_GESTURES);
@@ -734,7 +773,9 @@
* system window insets} by {@link #consumeSystemWindowInsets()}.
*
* @see #getSystemGestureInsets
+ * @deprecated Use {@link #getInsets(int)} with {@link Type#mandatorySystemGestures()} instead.
*/
+ @Deprecated
@NonNull
public Insets getMandatorySystemGestureInsets() {
return getInsets(mTypeInsetsMap, MANDATORY_SYSTEM_GESTURES);
@@ -760,7 +801,10 @@
*
* <p>This inset is consumed together with the {@link #getSystemWindowInsets()
* system window insets} by {@link #consumeSystemWindowInsets()}.
+ *
+ * @deprecated Use {@link #getInsets(int)} with {@link Type#tappableElement()} instead.
*/
+ @Deprecated
@NonNull
public Insets getTappableElementInsets() {
return getInsets(mTypeInsetsMap, TAPPABLE_ELEMENT);
@@ -985,7 +1029,9 @@
*
* @see #getSystemWindowInsets()
* @return itself
+ * @deprecated Use {@link #setInsets(int, Insets)} with {@link Type#systemBars()}.
*/
+ @Deprecated
@NonNull
public Builder setSystemWindowInsets(@NonNull Insets systemWindowInsets) {
Preconditions.checkNotNull(systemWindowInsets);
@@ -1003,7 +1049,9 @@
*
* @see #getSystemGestureInsets()
* @return itself
+ * @deprecated Use {@link #setInsets(int, Insets)} with {@link Type#systemGestures()}.
*/
+ @Deprecated
@NonNull
public Builder setSystemGestureInsets(@NonNull Insets insets) {
WindowInsets.setInsets(mTypeInsetsMap, SYSTEM_GESTURES, insets);
@@ -1023,7 +1071,10 @@
*
* @see #getMandatorySystemGestureInsets()
* @return itself
+ * @deprecated Use {@link #setInsets(int, Insets)} with
+ * {@link Type#mandatorySystemGestures()}.
*/
+ @Deprecated
@NonNull
public Builder setMandatorySystemGestureInsets(@NonNull Insets insets) {
WindowInsets.setInsets(mTypeInsetsMap, MANDATORY_SYSTEM_GESTURES, insets);
@@ -1038,7 +1089,9 @@
*
* @see #getTappableElementInsets()
* @return itself
+ * @deprecated Use {@link #setInsets(int, Insets)} with {@link Type#tappableElement()}.
*/
+ @Deprecated
@NonNull
public Builder setTappableElementInsets(@NonNull Insets insets) {
WindowInsets.setInsets(mTypeInsetsMap, TAPPABLE_ELEMENT, insets);
@@ -1068,15 +1121,15 @@
}
/**
- * Sets the maximum amount of insets a specific window type in pixels.
+ * Sets the insets a specific window type in pixels, while ignoring its visibility state.
*
- * <p>The maximum insets represents the area of a a window that that <b>may</b> be partially
- * or fully obscured by the system windows identified by {@code typeMask}. This value does
- * not change based on the visibility state of those elements. for example, if the status
- * bar is normally shown, but temporarily hidden, the maximum inset will still provide the
+ * <p>The insets represents the area of a a window that that <b>may</b> be partially
+ * or fully obscured by the system window identified by {@code type}. This value does not
+ * change based on the visibility state of those elements. For example, if the status bar is
+ * normally shown, but temporarily hidden, the inset returned here will still provide the
* inset associated with the status bar being shown.</p>
*
- * @see #getMaxInsets(int)
+ * @see #getInsetsIgnoringVisibility(int)
*
* @param typeMask The bitmask of {@link InsetsType} to set the insets for.
* @param insets The insets to set.
@@ -1090,7 +1143,7 @@
* state of the IME.
*/
@NonNull
- public Builder setMaxInsets(@InsetsType int typeMask, @NonNull Insets insets)
+ public Builder setInsetsIgnoringVisibility(@InsetsType int typeMask, @NonNull Insets insets)
throws IllegalArgumentException{
if (typeMask == IME) {
throw new IllegalArgumentException("Maximum inset not available for IME");
@@ -1134,7 +1187,10 @@
*
* @see #getStableInsets()
* @return itself
+ * @deprecated Use {@link #setInsetsIgnoringVisibility(int, Insets)} with
+ * {@link Type#systemBars()}.
*/
+ @Deprecated
@NonNull
public Builder setStableInsets(@NonNull Insets stableInsets) {
Preconditions.checkNotNull(stableInsets);
@@ -1267,13 +1323,6 @@
}
/**
- * @return An insets type representing decor that is being app-controlled.
- */
- public static @InsetsType int windowDecor() {
- return WINDOW_DECOR;
- }
-
- /**
* Returns an insets type representing the system gesture insets.
*
* <p>The system gesture insets represent the area of a window where system gestures have
@@ -1309,18 +1358,17 @@
}
/**
- * @return All system bars. Includes {@link #statusBars()} as well as
+ * @return All system bars. Includes {@link #statusBars()}, {@link #captionBar()} as well as
* {@link #navigationBars()}, but not {@link #ime()}.
*/
public static @InsetsType int systemBars() {
- return STATUS_BARS | NAVIGATION_BARS;
+ return STATUS_BARS | NAVIGATION_BARS | CAPTION_BAR;
}
/**
* @return All inset types combined.
*
- * TODO: Figure out if this makes sense at all, mixing e.g {@link #systemGestures()} and
- * {@link #ime()} does not seem very useful.
+ * @hide
*/
public static @InsetsType int all() {
return 0xFFFFFFFF;
@@ -1340,7 +1388,6 @@
/**
* Class that defines different sides for insets.
- * @hide pending unhide
*/
public static final class Side {
diff --git a/core/java/android/view/WindowInsetsAnimationCallback.java b/core/java/android/view/WindowInsetsAnimationCallback.java
index 53d4939..1e04d02 100644
--- a/core/java/android/view/WindowInsetsAnimationCallback.java
+++ b/core/java/android/view/WindowInsetsAnimationCallback.java
@@ -88,7 +88,7 @@
* <ul>
* <li>Application calls {@link WindowInsetsController#hideInputMethod()},
* {@link WindowInsetsController#showInputMethod()},
- * {@link WindowInsetsController#controlInputMethodAnimation(long, WindowInsetsAnimationControlListener)}</li>
+ * {@link WindowInsetsController#controlInputMethodAnimation}</li>
* <li>onPrepare is called on the view hierarchy listeners</li>
* <li>{@link View#onApplyWindowInsets} will be called with the end state of the
* animation</li>
@@ -182,14 +182,26 @@
private final @InsetsType int mTypeMask;
private float mFraction;
@Nullable private final Interpolator mInterpolator;
- private long mDurationMs;
+ private final long mDurationMillis;
private float mAlpha;
+ /**
+ * Creates a new {@link InsetsAnimation} object.
+ * <p>
+ * This should only be used for testing, as usually the system creates this object for the
+ * application to listen to with {@link WindowInsetsAnimationCallback}.
+ * </p>
+ * @param typeMask The bitmask of {@link WindowInsets.Type}s that are animating.
+ * @param interpolator The interpolator of the animation.
+ * @param durationMillis The duration of the animation in
+ * {@link java.util.concurrent.TimeUnit#MILLISECONDS}.
+ */
public InsetsAnimation(
- @InsetsType int typeMask, @Nullable Interpolator interpolator, long durationMs) {
+ @InsetsType int typeMask, @Nullable Interpolator interpolator,
+ long durationMillis) {
mTypeMask = typeMask;
mInterpolator = interpolator;
- mDurationMs = durationMs;
+ mDurationMillis = durationMillis;
}
/**
@@ -201,14 +213,18 @@
/**
* Returns the raw fractional progress of this animation between
- * {@link AnimationBounds#getLowerBound()} and {@link AnimationBounds#getUpperBound()}. Note
+ * start state of the animation and the end state of the animation. Note
* that this progress is the global progress of the animation, whereas
* {@link WindowInsetsAnimationCallback#onProgress} will only dispatch the insets that may
* be inset with {@link WindowInsets#inset} by parents of views in the hierarchy.
* Progress per insets animation is global for the entire animation. One animation animates
* all things together (in, out, ...). If they don't animate together, we'd have
* multiple animations.
- *
+ * <p>
+ * Note: In case the application is controlling the animation, the valued returned here will
+ * be the same as the application passed into
+ * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)}.
+ * </p>
* @return The current progress of this animation.
*/
@FloatRange(from = 0f, to = 1f)
@@ -218,16 +234,27 @@
/**
* Returns the interpolated fractional progress of this animation between
- * {@link AnimationBounds#getLowerBound()} and {@link AnimationBounds#getUpperBound()}. Note
+ * start state of the animation and the end state of the animation. Note
* that this progress is the global progress of the animation, whereas
* {@link WindowInsetsAnimationCallback#onProgress} will only dispatch the insets that may
* be inset with {@link WindowInsets#inset} by parents of views in the hierarchy.
* Progress per insets animation is global for the entire animation. One animation animates
* all things together (in, out, ...). If they don't animate together, we'd have
* multiple animations.
+ * <p>
+ * Note: In case the application is controlling the animation, the valued returned here will
+ * be the same as the application passed into
+ * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)},
+ * interpolated with the interpolator passed into
+ * {@link WindowInsetsController#controlInputMethodAnimation}.
+ * </p>
+ * <p>
+ * Note: For system-initiated animations, this will always return a valid value between 0
+ * and 1.
+ * </p>
* @see #getFraction() for raw fraction.
* @return The current interpolated progress of this animation. -1 if interpolator isn't
- * specified.
+ * specified.
*/
public float getInterpolatedFraction() {
if (mInterpolator != null) {
@@ -236,52 +263,66 @@
return -1;
}
+ /**
+ * Retrieves the interpolator used for this animation, or {@code null} if this animation
+ * doesn't follow an interpolation curved. For system-initiated animations, this will never
+ * return {@code null}.
+ *
+ * @return The interpolator used for this animation.
+ */
@Nullable
public Interpolator getInterpolator() {
return mInterpolator;
}
/**
- * @return duration of animation in {@link java.util.concurrent.TimeUnit#MILLISECONDS}.
+ * @return duration of animation in {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or
+ * -1 if the animation doesn't have a fixed duration.
*/
public long getDurationMillis() {
- return mDurationMs;
+ return mDurationMillis;
}
/**
* Set fraction of the progress if {@link WindowInsets.Type.InsetsType} animation is
- * controlled by the app {@see #getCurrentFraction}.
- * <p>Note: If app didn't create {@link InsetsAnimation}, it shouldn't set progress either.
- * Progress would be set by system with the system-default animation.
+ * controlled by the app.
+ * <p>
+ * Note: This should only be used for testing, as the system fills in the fraction for the
+ * application or the fraction that was passed into
+ * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)} is being
+ * used.
* </p>
* @param fraction fractional progress between 0 and 1 where 0 represents hidden and
* zero progress and 1 represent fully shown final state.
+ * @see #getFraction()
*/
public void setFraction(@FloatRange(from = 0f, to = 1f) float fraction) {
mFraction = fraction;
}
/**
- * Set duration of the animation if {@link WindowInsets.Type.InsetsType} animation is
- * controlled by the app.
- * <p>Note: If app didn't create {@link InsetsAnimation}, it shouldn't set duration either.
- * Duration would be set by system with the system-default animation.
- * </p>
- * @param durationMs in {@link java.util.concurrent.TimeUnit#MILLISECONDS}
- */
- public void setDuration(long durationMs) {
- mDurationMs = durationMs;
- }
-
- /**
- * @return alpha of {@link WindowInsets.Type.InsetsType}.
+ * Retrieves the translucency of the windows that are animating.
+ *
+ * @return Alpha of windows that cause insets of type {@link WindowInsets.Type.InsetsType}.
*/
@FloatRange(from = 0f, to = 1f)
public float getAlpha() {
return mAlpha;
}
- void setAlpha(@FloatRange(from = 0f, to = 1f) float alpha) {
+ /**
+ * Sets the translucency of the windows that are animating.
+ * <p>
+ * Note: This should only be used for testing, as the system fills in the alpha for the
+ * application or the alpha that was passed into
+ * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)} is being
+ * used.
+ * </p>
+ * @param alpha Alpha of windows that cause insets of type
+ * {@link WindowInsets.Type.InsetsType}.
+ * @see #getAlpha()
+ */
+ public void setAlpha(@FloatRange(from = 0f, to = 1f) float alpha) {
mAlpha = alpha;
}
}
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index f292ca4..02323cf 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -20,8 +20,11 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Insets;
import android.view.WindowInsets.Type.InsetsType;
+import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
+import android.view.animation.Interpolator;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -79,7 +82,6 @@
* shown on any user interaction on the corresponding display if navigation bars are hidden by
* {@link #hide(int)} or
* {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)}.
- * @hide
*/
int BEHAVIOR_SHOW_BARS_BY_TOUCH = 0;
@@ -90,7 +92,6 @@
*
* <p>When system bars are hidden in this mode, they can be revealed with system gestures, such
* as swiping from the edge of the screen where the bar is hidden from.</p>
- * @hide
*/
int BEHAVIOR_SHOW_BARS_BY_SWIPE = 1;
@@ -103,7 +104,6 @@
* gestures, such as swiping from the edge of the screen where the bar is hidden from. These
* transient system bars will overlay app’s content, may have some degree of transparency, and
* will automatically hide after a short timeout.</p>
- * @hide
*/
int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2;
@@ -126,7 +126,6 @@
*
* @param types A bitmask of {@link InsetsType} specifying what windows the app
* would like to make appear on screen.
- * @hide
*/
void show(@InsetsType int types);
@@ -139,7 +138,6 @@
*
* @param types A bitmask of {@link InsetsType} specifying what windows the app
* would like to make disappear.
- * @hide
*/
void hide(@InsetsType int types);
@@ -148,29 +146,50 @@
* the position of the windows in the system causing insets directly.
*
* @param types The {@link InsetsType}s the application has requested to control.
- * @param durationMillis duration of animation in
+ * @param durationMillis Duration of animation in
* {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or -1 if the
- * animation doesn't have a predetermined duration.
+ * animation doesn't have a predetermined duration.T his value will be
+ * passed to {@link InsetsAnimation#getDurationMillis()}
+ * @param interpolator The interpolator used for this animation, or {@code null} if this
+ * animation doesn't follow an interpolation curve. This value will be
+ * passed to {@link InsetsAnimation#getInterpolator()} and used to calculate
+ * {@link InsetsAnimation#getInterpolatedFraction()}.
* @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
* windows are ready to be controlled, among other callbacks.
- * @hide
+ *
+ * @see InsetsAnimation#getFraction()
+ * @see InsetsAnimation#getInterpolatedFraction()
+ * @see InsetsAnimation#getInterpolator()
+ * @see InsetsAnimation#getDurationMillis()
*/
void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
+ @Nullable Interpolator interpolator,
@NonNull WindowInsetsAnimationControlListener listener);
/**
* Lets the application control the animation for showing the IME in a frame-by-frame manner by
* modifying the position of the IME when it's causing insets.
*
- * @param durationMillis duration of the animation in
+ * @param durationMillis Duration of the animation in
* {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or -1 if the
- * animation doesn't have a predetermined duration.
+ * animation doesn't have a predetermined duration. This value will be
+ * passed to {@link InsetsAnimation#getDurationMillis()}
+ * @param interpolator The interpolator used for this animation, or {@code null} if this
+ * animation doesn't follow an interpolation curve. This value will be
+ * passed to {@link InsetsAnimation#getInterpolator()} and used to calculate
+ * {@link InsetsAnimation#getInterpolatedFraction()}.
* @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
* IME are ready to be controlled, among other callbacks.
+ *
+ * @see InsetsAnimation#getFraction()
+ * @see InsetsAnimation#getInterpolatedFraction()
+ * @see InsetsAnimation#getInterpolator()
+ * @see InsetsAnimation#getDurationMillis()
*/
default void controlInputMethodAnimation(long durationMillis,
+ @Nullable Interpolator interpolator,
@NonNull WindowInsetsAnimationControlListener listener) {
- controlWindowInsetsAnimation(ime(), durationMillis, listener);
+ controlWindowInsetsAnimation(ime(), durationMillis, interpolator, listener);
}
/**
@@ -181,7 +200,7 @@
* the event by observing {@link View#onApplyWindowInsets} and checking visibility with
* {@link WindowInsets#isVisible}.
*
- * @see #controlInputMethodAnimation(long, WindowInsetsAnimationControlListener)
+ * @see #controlInputMethodAnimation
* @see #hideInputMethod()
*/
default void showInputMethod() {
@@ -196,7 +215,7 @@
* the event by observing {@link View#onApplyWindowInsets} and checking visibility with
* {@link WindowInsets#isVisible}.
*
- * @see #controlInputMethodAnimation(long, WindowInsetsAnimationControlListener)
+ * @see #controlInputMethodAnimation
* @see #showInputMethod()
*/
default void hideInputMethod() {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index cd9dee4..c8dea43 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -421,7 +421,9 @@
* </p>
*
* @return The display that this window manager is managing.
+ * @deprecated Use {@link Context#getDisplay()} instead.
*/
+ @Deprecated
public Display getDefaultDisplay();
/**
@@ -435,6 +437,49 @@
public void removeViewImmediate(View view);
/**
+ * Returns the {@link WindowMetrics} according to the current system state.
+ * <p>
+ * The metrics describe the size of the area the window would occupy with
+ * {@link LayoutParams#MATCH_PARENT MATCH_PARENT} width and height, and the {@link WindowInsets}
+ * such a window would have.
+ * <p>
+ * The value of this is based on the <b>current</b> windowing state of the system.
+ *
+ * For example, for activities in multi-window mode, the metrics returned are based on the
+ * current bounds that the user has selected for the {@link android.app.Activity Activity}'s
+ * task.
+ *
+ * @see #getMaximumWindowMetrics()
+ * @see WindowMetrics
+ */
+ default @NonNull WindowMetrics getCurrentWindowMetrics() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns the largets {@link WindowMetrics} an app may expect in the current system state.
+ * <p>
+ * The metrics describe the size of the largest potential area the window might occupy with
+ * {@link LayoutParams#MATCH_PARENT MATCH_PARENT} width and height, and the {@link WindowInsets}
+ * such a window would have.
+ * <p>
+ * The value of this is based on the largest <b>potential</b> windowing state of the system.
+ *
+ * For example, for activities in multi-window mode, the metrics returned are based on the
+ * what the bounds would be if the user expanded the {@link android.app.Activity Activity}'s
+ * task to cover the entire screen.
+ *
+ * Note that this might still be smaller than the size of the physical display if certain areas
+ * of the display are not available to windows created in this {@link Context}.
+ *
+ * @see #getMaximumWindowMetrics()
+ * @see WindowMetrics
+ */
+ default @NonNull WindowMetrics getMaximumWindowMetrics() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
* Used to asynchronously request Keyboard Shortcuts from the focused window.
*
* @hide
@@ -1243,11 +1288,9 @@
* the device's screen turned on and bright. */
public static final int FLAG_KEEP_SCREEN_ON = 0x00000080;
- /** Window flag: place the window within the entire screen, ignoring
- * decorations around the border (such as the status bar). The
- * window must correctly position its contents to take the screen
- * decoration into account. This flag is normally set for you
- * by Window as described in {@link Window#setFlags}.
+ /**
+ * Window flag for attached windows: Place the window within the entire screen, ignoring
+ * any constraints from the parent window.
*
* <p>Note: on displays that have a {@link DisplayCutout}, the window may be placed
* such that it avoids the {@link DisplayCutout} area if necessary according to the
@@ -1277,11 +1320,21 @@
* {@link android.R.style#Theme_Holo_Light_NoActionBar_Fullscreen},
* {@link android.R.style#Theme_DeviceDefault_NoActionBar_Fullscreen}, and
* {@link android.R.style#Theme_DeviceDefault_Light_NoActionBar_Fullscreen}.</p>
+ *
+ * @deprecated Use {@link WindowInsetsController#hide(int)} with {@link Type#statusBars()}
+ * instead.
*/
+ @Deprecated
public static final int FLAG_FULLSCREEN = 0x00000400;
- /** Window flag: override {@link #FLAG_FULLSCREEN} and force the
- * screen decorations (such as the status bar) to be shown. */
+ /**
+ * Window flag: override {@link #FLAG_FULLSCREEN} and force the
+ * screen decorations (such as the status bar) to be shown.
+ *
+ * @deprecated This value became API "by accident", and shouldn't be used by 3rd party
+ * applications.
+ */
+ @Deprecated
public static final int FLAG_FORCE_NOT_FULLSCREEN = 0x00000800;
/** Window flag: turn on dithering when compositing this window to
@@ -1313,13 +1366,18 @@
* until the finger is released. */
public static final int FLAG_IGNORE_CHEEK_PRESSES = 0x00008000;
- /** Window flag: a special option only for use in combination with
+ /**
+ * Window flag: a special option only for use in combination with
* {@link #FLAG_LAYOUT_IN_SCREEN}. When requesting layout in the
* screen your window may appear on top of or behind screen decorations
* such as the status bar. By also including this flag, the window
* manager will report the inset rectangle needed to ensure your
* content is not covered by screen decorations. This flag is normally
- * set for you by Window as described in {@link Window#setFlags}.*/
+ * set for you by Window as described in {@link Window#setFlags}
+ *
+ * @deprecated Insets will always be delivered to your application.
+ */
+ @Deprecated
public static final int FLAG_LAYOUT_INSET_DECOR = 0x00010000;
/** Window flag: When set, input method can't interact with the focusable window
@@ -1505,7 +1563,11 @@
*
* <p>Note: For devices that support
* {@link android.content.pm.PackageManager#FEATURE_AUTOMOTIVE} this flag may be ignored.
+ *
+ * @deprecated Use {@link Window#setStatusBarColor(int)} with a half-translucent color
+ * instead.
*/
+ @Deprecated
public static final int FLAG_TRANSLUCENT_STATUS = 0x04000000;
/**
@@ -1528,7 +1590,11 @@
* <p>Note: For devices that support
* {@link android.content.pm.PackageManager#FEATURE_AUTOMOTIVE} this flag can be disabled
* by the car manufacturers.
+ *
+ * @deprecated Use {@link Window#setNavigationBarColor(int)} with a half-translucent color
+ * instead.
*/
+ @Deprecated
public static final int FLAG_TRANSLUCENT_NAVIGATION = 0x08000000;
/**
@@ -1558,7 +1624,11 @@
* overlap with the screen decorations of the parent window such as the navigation bar. By
* including this flag, the window manager will layout the attached window within the decor
* frame of the parent window such that it doesn't overlap with screen decorations.
+ *
+ * @deprecated Use {@link #setFitInsetsTypes(int)} to determine whether the attached
+ * window will overlap with system bars.
*/
+ @Deprecated
public static final int FLAG_LAYOUT_ATTACHED_IN_DECOR = 0x40000000;
/**
@@ -1878,13 +1948,6 @@
public static final int PRIVATE_FLAG_FIT_INSETS_CONTROLLED = 0x10000000;
/**
- * Flag to indicate that the window only draws the bottom bar background so that we don't
- * extend it to system bar areas at other sides.
- * @hide
- */
- public static final int PRIVATE_FLAG_ONLY_DRAW_BOTTOM_BAR_BACKGROUND = 0x20000000;
-
- /**
* An internal annotation for flags that can be specified to {@link #softInputMode}.
*
* @hide
@@ -1994,11 +2057,7 @@
@ViewDebug.FlagToString(
mask = PRIVATE_FLAG_FIT_INSETS_CONTROLLED,
equals = PRIVATE_FLAG_FIT_INSETS_CONTROLLED,
- name = "FIT_INSETS_CONTROLLED"),
- @ViewDebug.FlagToString(
- mask = PRIVATE_FLAG_ONLY_DRAW_BOTTOM_BAR_BACKGROUND,
- equals = PRIVATE_FLAG_ONLY_DRAW_BOTTOM_BAR_BACKGROUND,
- name = "ONLY_DRAW_BOTTOM_BAR_BACKGROUND")
+ name = "FIT_INSETS_CONTROLLED")
})
@TestApi
public int privateFlags;
@@ -2098,7 +2157,11 @@
* layout parameter flags include {@link #FLAG_FULLSCREEN}, this
* value for {@link #softInputMode} will be ignored; the window will
* not resize, but will stay fullscreen.
+ *
+ * @deprecated Use {@link Window#setOnContentApplyWindowInsetsListener} instead with a
+ * listener that fits {@link Type#ime()} instead.
*/
+ @Deprecated
public static final int SOFT_INPUT_ADJUST_RESIZE = 0x10;
/** Adjustment option for {@link #softInputMode}: set to have a window
@@ -2393,7 +2456,11 @@
*
* @see View#STATUS_BAR_VISIBLE
* @see View#STATUS_BAR_HIDDEN
+ *
+ * @deprecated SystemUiVisibility flags are deprecated. Use {@link WindowInsetsController}
+ * instead.
*/
+ @Deprecated
public int systemUiVisibility;
/**
@@ -2719,7 +2786,7 @@
equals = WINDOW_DECOR,
name = "WINDOW_DECOR")
})
- private @InsetsType int mFitWindowInsetsTypes = Type.systemBars();
+ private @InsetsType int mFitInsetsTypes = Type.systemBars();
@ViewDebug.ExportedProperty(flagMapping = {
@ViewDebug.FlagToString(
@@ -2739,19 +2806,18 @@
equals = BOTTOM,
name = "BOTTOM")
})
- private @InsetsSide int mFitWindowInsetsSides = Side.all();
+ private @InsetsSide int mFitInsetsSides = Side.all();
- private boolean mFitIgnoreVisibility = false;
+ private boolean mFitInsetsIgnoringVisibility = false;
/**
* Specifies types of insets that this window should avoid overlapping during layout.
*
* @param types which types of insets that this window should avoid. The initial value of
* this object includes all system bars.
- * @hide pending unhide
*/
- public void setFitWindowInsetsTypes(@InsetsType int types) {
- mFitWindowInsetsTypes = types;
+ public void setFitInsetsTypes(@InsetsType int types) {
+ mFitInsetsTypes = types;
privateFlags |= PRIVATE_FLAG_FIT_INSETS_CONTROLLED;
}
@@ -2760,10 +2826,9 @@
*
* @param sides which sides that this window should avoid overlapping with the types
* specified. The initial value of this object includes all sides.
- * @hide pending unhide
*/
- public void setFitWindowInsetsSides(@InsetsSide int sides) {
- mFitWindowInsetsSides = sides;
+ public void setFitInsetsSides(@InsetsSide int sides) {
+ mFitInsetsSides = sides;
privateFlags |= PRIVATE_FLAG_FIT_INSETS_CONTROLLED;
}
@@ -2771,36 +2836,32 @@
* Specifies if this window should fit the window insets no matter they are visible or not.
*
* @param ignore if true, this window will fit the given types even if they are not visible.
- * @hide pending unhide
*/
- public void setFitIgnoreVisibility(boolean ignore) {
- mFitIgnoreVisibility = ignore;
+ public void setFitInsetsIgnoringVisibility(boolean ignore) {
+ mFitInsetsIgnoringVisibility = ignore;
privateFlags |= PRIVATE_FLAG_FIT_INSETS_CONTROLLED;
}
/**
* @return the insets types that this window is avoiding overlapping.
- * @hide pending unhide
*/
- public @InsetsType int getFitWindowInsetsTypes() {
- return mFitWindowInsetsTypes;
+ public @InsetsType int getFitInsetsTypes() {
+ return mFitInsetsTypes;
}
/**
* @return the sides that this window is avoiding overlapping.
- * @hide pending unhide
*/
- public @InsetsSide int getFitWindowInsetsSides() {
- return mFitWindowInsetsSides;
+ public @InsetsSide int getFitInsetsSides() {
+ return mFitInsetsSides;
}
/**
* @return {@code true} if this window fits the window insets no matter they are visible or
* not.
- * @hide pending unhide
*/
- public boolean getFitIgnoreVisibility() {
- return mFitIgnoreVisibility;
+ public boolean isFitInsetsIgnoringVisibility() {
+ return mFitInsetsIgnoringVisibility;
}
public LayoutParams() {
@@ -2966,9 +3027,9 @@
out.writeLong(hideTimeoutMilliseconds);
out.writeInt(insetsFlags.appearance);
out.writeInt(insetsFlags.behavior);
- out.writeInt(mFitWindowInsetsTypes);
- out.writeInt(mFitWindowInsetsSides);
- out.writeBoolean(mFitIgnoreVisibility);
+ out.writeInt(mFitInsetsTypes);
+ out.writeInt(mFitInsetsSides);
+ out.writeBoolean(mFitInsetsIgnoringVisibility);
out.writeBoolean(preferMinimalPostProcessing);
}
@@ -3027,9 +3088,9 @@
hideTimeoutMilliseconds = in.readLong();
insetsFlags.appearance = in.readInt();
insetsFlags.behavior = in.readInt();
- mFitWindowInsetsTypes = in.readInt();
- mFitWindowInsetsSides = in.readInt();
- mFitIgnoreVisibility = in.readBoolean();
+ mFitInsetsTypes = in.readInt();
+ mFitInsetsSides = in.readInt();
+ mFitInsetsIgnoringVisibility = in.readBoolean();
preferMinimalPostProcessing = in.readBoolean();
}
@@ -3276,18 +3337,18 @@
changes |= INSET_FLAGS_CHANGED;
}
- if (mFitWindowInsetsTypes != o.mFitWindowInsetsTypes) {
- mFitWindowInsetsTypes = o.mFitWindowInsetsTypes;
+ if (mFitInsetsTypes != o.mFitInsetsTypes) {
+ mFitInsetsTypes = o.mFitInsetsTypes;
changes |= LAYOUT_CHANGED;
}
- if (mFitWindowInsetsSides != o.mFitWindowInsetsSides) {
- mFitWindowInsetsSides = o.mFitWindowInsetsSides;
+ if (mFitInsetsSides != o.mFitInsetsSides) {
+ mFitInsetsSides = o.mFitInsetsSides;
changes |= LAYOUT_CHANGED;
}
- if (mFitIgnoreVisibility != o.mFitIgnoreVisibility) {
- mFitIgnoreVisibility = o.mFitIgnoreVisibility;
+ if (mFitInsetsIgnoringVisibility != o.mFitInsetsIgnoringVisibility) {
+ mFitInsetsIgnoringVisibility = o.mFitInsetsIgnoringVisibility;
changes |= LAYOUT_CHANGED;
}
@@ -3449,17 +3510,17 @@
sb.append(prefix).append(" bhv=").append(ViewDebug.flagsToString(
InsetsFlags.class, "behavior", insetsFlags.behavior));
}
- if (mFitWindowInsetsTypes != 0) {
+ if (mFitInsetsTypes != 0) {
sb.append(System.lineSeparator());
sb.append(prefix).append(" fitTypes=").append(ViewDebug.flagsToString(
- LayoutParams.class, "mFitWindowInsetsTypes", mFitWindowInsetsTypes));
+ LayoutParams.class, "mFitInsetsTypes", mFitInsetsTypes));
}
- if (mFitWindowInsetsSides != Side.all()) {
+ if (mFitInsetsSides != Side.all()) {
sb.append(System.lineSeparator());
sb.append(prefix).append(" fitSides=").append(ViewDebug.flagsToString(
- LayoutParams.class, "mFitWindowInsetsSides", mFitWindowInsetsSides));
+ LayoutParams.class, "mFitInsetsSides", mFitInsetsSides));
}
- if (mFitIgnoreVisibility) {
+ if (mFitInsetsIgnoringVisibility) {
sb.append(System.lineSeparator());
sb.append(prefix).append(" fitIgnoreVis");
}
@@ -3500,9 +3561,9 @@
proto.write(SUBTREE_SYSTEM_UI_VISIBILITY_FLAGS, subtreeSystemUiVisibility);
proto.write(APPEARANCE, insetsFlags.appearance);
proto.write(BEHAVIOR, insetsFlags.behavior);
- proto.write(FIT_INSETS_TYPES, mFitWindowInsetsTypes);
- proto.write(FIT_INSETS_SIDES, mFitWindowInsetsSides);
- proto.write(FIT_IGNORE_VISIBILITY, mFitIgnoreVisibility);
+ proto.write(FIT_INSETS_TYPES, mFitInsetsTypes);
+ proto.write(FIT_INSETS_SIDES, mFitInsetsSides);
+ proto.write(FIT_IGNORE_VISIBILITY, mFitInsetsIgnoringVisibility);
proto.end(token);
}
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index cdeeaa4..4365d1f 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -17,12 +17,17 @@
package android.view;
import android.annotation.NonNull;
+import android.app.ResourcesManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.graphics.Insets;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Size;
import com.android.internal.os.IResultReceiver;
@@ -62,6 +67,10 @@
private IBinder mDefaultToken;
+ private boolean mIsViewAdded;
+ private View mLastView;
+ private WindowManager.LayoutParams mLastParams;
+
public WindowManagerImpl(Context context) {
this(context, null);
}
@@ -93,6 +102,9 @@
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
+ mIsViewAdded = true;
+ mLastView = view;
+ mLastParams = (WindowManager.LayoutParams) params;
}
@Override
@@ -201,4 +213,71 @@
}
return false;
}
+
+ @Override
+ public WindowMetrics getCurrentWindowMetrics() {
+ final Context context = mParentWindow != null ? mParentWindow.getContext() : mContext;
+ final Rect bound = getCurrentBounds(context);
+
+ return new WindowMetrics(toSize(bound), computeWindowInsets());
+ }
+
+ private static Rect getCurrentBounds(Context context) {
+ synchronized (ResourcesManager.getInstance()) {
+ return context.getResources().getConfiguration().windowConfiguration.getBounds();
+ }
+ }
+
+ @Override
+ public WindowMetrics getMaximumWindowMetrics() {
+ return new WindowMetrics(toSize(getMaximumBounds()), computeWindowInsets());
+ }
+
+ private Size toSize(Rect frame) {
+ return new Size(frame.width(), frame.height());
+ }
+
+ private Rect getMaximumBounds() {
+ // TODO(b/128338354): Current maximum bound is display size, but it should be displayArea
+ // bound after displayArea feature is finished.
+ final Display display = mContext.getDisplay();
+ final Point displaySize = new Point();
+ display.getRealSize(displaySize);
+ return new Rect(0, 0, displaySize.x, displaySize.y);
+ }
+
+ private WindowInsets computeWindowInsets() {
+ // TODO(window-context): This can only be properly implemented
+ // once we flip the new insets mode flag.
+ if (mParentWindow != null) {
+ if (mParentWindow.getDecorView().isAttachedToWindow()) {
+ return mParentWindow.getDecorView().getViewRootImpl()
+ .getWindowInsets(true /* forceConstruct */);
+ }
+ return getWindowInsetsFromServer(mParentWindow.getAttributes());
+ }
+ if (mIsViewAdded) {
+ return mLastView.getViewRootImpl().getWindowInsets(true /* forceConstruct */);
+ } else {
+ return getWindowInsetsFromServer(new WindowManager.LayoutParams());
+ }
+
+ }
+
+ private WindowInsets getWindowInsetsFromServer(WindowManager.LayoutParams attrs) {
+ try {
+ final Rect systemWindowInsets = new Rect();
+ final Rect stableInsets = new Rect();
+ final DisplayCutout.ParcelableWrapper displayCutout =
+ new DisplayCutout.ParcelableWrapper();
+ WindowManagerGlobal.getWindowManagerService().getWindowInsets(attrs,
+ mContext.getDisplayId(), systemWindowInsets, stableInsets, displayCutout);
+ return new WindowInsets.Builder()
+ .setSystemWindowInsets(Insets.of(systemWindowInsets))
+ .setStableInsets(Insets.of(stableInsets))
+ .setDisplayCutout(displayCutout.get()).build();
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
}
diff --git a/core/java/android/view/WindowMetrics.java b/core/java/android/view/WindowMetrics.java
new file mode 100644
index 0000000..8caf5b7
--- /dev/null
+++ b/core/java/android/view/WindowMetrics.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+import android.util.Size;
+
+/**
+ * Metrics about a Window, consisting of the size and {@link WindowInsets}.
+ * <p>
+ * This is usually obtained from {@link WindowManager#getCurrentWindowMetrics()} and
+ * {@link WindowManager#getMaximumWindowMetrics()}.
+ *
+ * @see WindowInsets#getInsets(int)
+ * @see WindowManager#getCurrentWindowMetrics()
+ * @see WindowManager#getMaximumWindowMetrics()
+ */
+public final class WindowMetrics {
+ private final @NonNull Size mSize;
+ private final @NonNull WindowInsets mWindowInsets;
+
+ public WindowMetrics(@NonNull Size size, @NonNull WindowInsets windowInsets) {
+ mSize = size;
+ mWindowInsets = windowInsets;
+ }
+
+ /**
+ * Returns the size of the window.
+ *
+ * @return window size in pixel.
+ */
+ public @NonNull Size getSize() {
+ return mSize;
+ }
+
+ /**
+ * Returns the {@link WindowInsets} of the window.
+ *
+ * @return the {@link WindowInsets} of the window.
+ */
+ public @NonNull WindowInsets getWindowInsets() {
+ return mWindowInsets;
+ }
+}
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index a26243c..c80a1ae 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -18,6 +18,8 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.os.Bundle;
@@ -28,7 +30,13 @@
import android.text.InputType;
import android.text.TextUtils;
import android.util.Printer;
+import android.view.View;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
/**
@@ -491,6 +499,238 @@
@Nullable
public UserHandle targetInputMethodUser = null;
+ @IntDef({TrimPolicy.HEAD, TrimPolicy.TAIL})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface TrimPolicy {
+ int HEAD = 0;
+ int TAIL = 1;
+ }
+
+ /**
+ * The maximum length of initialSurroundingText. When the input text from
+ * {@code setInitialSurroundingText(CharSequence)} is longer than this, trimming shall be
+ * performed to keep memory efficiency.
+ */
+ @VisibleForTesting
+ static final int MEMORY_EFFICIENT_TEXT_LENGTH = 2048;
+ /**
+ * When the input text is longer than {@code #MEMORY_EFFICIENT_TEXT_LENGTH}, we start trimming
+ * the input text into three parts: BeforeCursor, Selection, and AfterCursor. We don't want to
+ * trim the Selection but we also don't want it consumes all available space. Therefore, the
+ * maximum acceptable Selection length is half of {@code #MEMORY_EFFICIENT_TEXT_LENGTH}.
+ */
+ @VisibleForTesting
+ static final int MAX_INITIAL_SELECTION_LENGTH = MEMORY_EFFICIENT_TEXT_LENGTH / 2;
+
+ @NonNull
+ private InitialSurroundingText mInitialSurroundingText = new InitialSurroundingText();
+
+ /**
+ * Editors may use this method to provide initial input text to IMEs. As the surrounding text
+ * could be used to provide various input assistance, we recommend editors to provide the
+ * complete initial input text in its {@link View#onCreateInputConnection(EditorInfo)} callback.
+ * The supplied text will then be processed to serve {@code #getInitialTextBeforeCursor},
+ * {@code #getInitialSelectedText}, and {@code #getInitialTextBeforeCursor}. System is allowed
+ * to trim {@code sourceText} for various reasons while keeping the most valuable data to IMEs.
+ *
+ * <p><strong>Editor authors: </strong>Providing the initial input text helps reducing IPC calls
+ * for IMEs to provide many modern features right after the connection setup. We recommend
+ * calling this method in your implementation.
+ *
+ * @param sourceText The complete input text.
+ */
+ public void setInitialSurroundingText(@NonNull CharSequence sourceText) {
+ setInitialSurroundingSubText(sourceText, /* subTextStart = */ 0);
+ }
+
+ /**
+ * Editors may use this method to provide initial input text to IMEs. As the surrounding text
+ * could be used to provide various input assistance, we recommend editors to provide the
+ * complete initial input text in its {@link View#onCreateInputConnection(EditorInfo)} callback.
+ * When trimming the input text is needed, call this method instead of
+ * {@code setInitialSurroundingText(CharSequence)} and provide the trimmed position info. Always
+ * try to include the selected text within {@code subText} to give the system best flexibility
+ * to choose where and how to trim {@code subText} when necessary.
+ *
+ * @param subText The input text. When it was trimmed, {@code subTextStart} must be provided
+ * correctly.
+ * @param subTextStart The position that the input text got trimmed. For example, when the
+ * editor wants to trim out the first 10 chars, subTextStart should be 10.
+ */
+ public void setInitialSurroundingSubText(@NonNull CharSequence subText, int subTextStart) {
+ Preconditions.checkNotNull(subText);
+
+ // Swap selection start and end if necessary.
+ final int subTextSelStart = initialSelStart > initialSelEnd
+ ? initialSelEnd - subTextStart : initialSelStart - subTextStart;
+ final int subTextSelEnd = initialSelStart > initialSelEnd
+ ? initialSelStart - subTextStart : initialSelEnd - subTextStart;
+
+ final int subTextLength = subText.length();
+ // Unknown or invalid selection.
+ if (subTextStart < 0 || subTextSelStart < 0 || subTextSelEnd > subTextLength) {
+ mInitialSurroundingText = new InitialSurroundingText();
+ return;
+ }
+
+ // For privacy protection reason, we don't carry password inputs to IMEs.
+ if (isPasswordInputType(inputType)) {
+ mInitialSurroundingText = new InitialSurroundingText();
+ return;
+ }
+
+ if (subTextLength <= MEMORY_EFFICIENT_TEXT_LENGTH) {
+ mInitialSurroundingText = new InitialSurroundingText(subText, subTextSelStart,
+ subTextSelEnd);
+ return;
+ }
+
+ // The input text is too long. Let's try to trim it reasonably. Fundamental rules are:
+ // 1. Text before the cursor is the most important information to IMEs.
+ // 2. Text after the cursor is the second important information to IMEs.
+ // 3. Selected text is the least important information but it shall NEVER be truncated.
+ // When it is too long, just drop it.
+ //
+ // Source: <TextBeforeCursor><Selection><TextAfterCursor>
+ // Possible results:
+ // 1. <(maybeTrimmedAtHead)TextBeforeCursor><Selection><TextAfterCursor(maybeTrimmedAtTail)>
+ // 2. <(maybeTrimmedAtHead)TextBeforeCursor><TextAfterCursor(maybeTrimmedAtTail)>
+ //
+ final int sourceSelLength = subTextSelEnd - subTextSelStart;
+ // When the selected text is too long, drop it.
+ final int newSelLength = (sourceSelLength > MAX_INITIAL_SELECTION_LENGTH)
+ ? 0 : sourceSelLength;
+
+ // Distribute rest of length quota to TextBeforeCursor and TextAfterCursor in 4:1 ratio.
+ final int subTextBeforeCursorLength = subTextSelStart;
+ final int subTextAfterCursorLength = subTextLength - subTextSelEnd;
+ final int maxLengthMinusSelection = MEMORY_EFFICIENT_TEXT_LENGTH - newSelLength;
+ final int possibleMaxBeforeCursorLength =
+ Math.min(subTextBeforeCursorLength, (int) (0.8 * maxLengthMinusSelection));
+ int newAfterCursorLength = Math.min(subTextAfterCursorLength,
+ maxLengthMinusSelection - possibleMaxBeforeCursorLength);
+ int newBeforeCursorLength = Math.min(subTextBeforeCursorLength,
+ maxLengthMinusSelection - newAfterCursorLength);
+
+ // As trimming may happen at the head of TextBeforeCursor, calculate new starting position.
+ int newBeforeCursorHead = subTextBeforeCursorLength - newBeforeCursorLength;
+
+ // We don't want to cut surrogate pairs in the middle. Exam that at the new head and tail.
+ if (isCutOnSurrogate(subText,
+ subTextSelStart - newBeforeCursorLength, TrimPolicy.HEAD)) {
+ newBeforeCursorHead = newBeforeCursorHead + 1;
+ newBeforeCursorLength = newBeforeCursorLength - 1;
+ }
+ if (isCutOnSurrogate(subText,
+ subTextSelEnd + newAfterCursorLength - 1, TrimPolicy.TAIL)) {
+ newAfterCursorLength = newAfterCursorLength - 1;
+ }
+
+ // Now we know where to trim, compose the initialSurroundingText.
+ final int newTextLength = newBeforeCursorLength + newSelLength + newAfterCursorLength;
+ CharSequence newInitialSurroundingText;
+ if (newSelLength != sourceSelLength) {
+ final CharSequence beforeCursor = subText.subSequence(newBeforeCursorHead,
+ newBeforeCursorHead + newBeforeCursorLength);
+
+ final CharSequence afterCursor = subText.subSequence(subTextSelEnd,
+ subTextSelEnd + newAfterCursorLength);
+
+ newInitialSurroundingText = TextUtils.concat(beforeCursor, afterCursor);
+ } else {
+ newInitialSurroundingText = subText
+ .subSequence(newBeforeCursorHead, newBeforeCursorHead + newTextLength);
+ }
+
+ // As trimming may happen at the head, adjust cursor position in the initialSurroundingText
+ // obj.
+ newBeforeCursorHead = 0;
+ final int newSelHead = newBeforeCursorHead + newBeforeCursorLength;
+ mInitialSurroundingText = new InitialSurroundingText(
+ newInitialSurroundingText, newSelHead, newSelHead + newSelLength);
+ }
+
+ /**
+ * Get <var>n</var> characters of text before the current cursor position. May be {@code null}
+ * when the protocol is not supported.
+ *
+ * @param length The expected length of the text.
+ * @param flags Supplies additional options controlling how the text is returned. May be
+ * either 0 or {@link InputConnection#GET_TEXT_WITH_STYLES}.
+ * @return the text before the cursor position; the length of the returned text might be less
+ * than <var>n</var>. When there is no text before the cursor, an empty string will be returned.
+ * It could also be {@code null} when the editor or system could not support this protocol.
+ */
+ @Nullable
+ public CharSequence getInitialTextBeforeCursor(int length, int flags) {
+ return mInitialSurroundingText.getInitialTextBeforeCursor(length, flags);
+ }
+
+ /**
+ * Gets the selected text, if any. May be {@code null} when no text is selected or the selected
+ * text is way too long.
+ *
+ * @param flags Supplies additional options controlling how the text is returned. May be
+ * either 0 or {@link InputConnection#GET_TEXT_WITH_STYLES}.
+ * @return the text that is currently selected, if any. It could be an empty string when there
+ * is no text selected. When {@code null} is returned, the selected text might be too long or
+ * this protocol is not supported.
+ */
+ @Nullable
+ public CharSequence getInitialSelectedText(int flags) {
+ // Swap selection start and end if necessary.
+ final int correctedTextSelStart = initialSelStart > initialSelEnd
+ ? initialSelEnd : initialSelStart;
+ final int correctedTextSelEnd = initialSelStart > initialSelEnd
+ ? initialSelStart : initialSelEnd;
+
+ final int sourceSelLength = correctedTextSelEnd - correctedTextSelStart;
+ if (initialSelStart < 0 || initialSelEnd < 0
+ || mInitialSurroundingText.getSelectionLength() != sourceSelLength) {
+ return null;
+ }
+ return mInitialSurroundingText.getInitialSelectedText(flags);
+ }
+
+ /**
+ * Get <var>n</var> characters of text after the current cursor position. May be {@code null}
+ * when the protocol is not supported.
+ *
+ * @param length The expected length of the text.
+ * @param flags Supplies additional options controlling how the text is returned. May be
+ * either 0 or {@link InputConnection#GET_TEXT_WITH_STYLES}.
+ * @return the text after the cursor position; the length of the returned text might be less
+ * than <var>n</var>. When there is no text after the cursor, an empty string will be returned.
+ * It could also be {@code null} when the editor or system could not support this protocol.
+ */
+ @Nullable
+ public CharSequence getInitialTextAfterCursor(int length, int flags) {
+ return mInitialSurroundingText.getInitialTextAfterCursor(length, flags);
+ }
+
+ private static boolean isCutOnSurrogate(CharSequence sourceText, int cutPosition,
+ @TrimPolicy int policy) {
+ switch (policy) {
+ case TrimPolicy.HEAD:
+ return Character.isLowSurrogate(sourceText.charAt(cutPosition));
+ case TrimPolicy.TAIL:
+ return Character.isHighSurrogate(sourceText.charAt(cutPosition));
+ default:
+ return false;
+ }
+ }
+
+ private static boolean isPasswordInputType(int inputType) {
+ final int variation =
+ inputType & (TYPE_MASK_CLASS | TYPE_MASK_VARIATION);
+ return variation
+ == (TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD)
+ || variation
+ == (TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_WEB_PASSWORD)
+ || variation
+ == (TYPE_CLASS_NUMBER | TYPE_NUMBER_VARIATION_PASSWORD);
+ }
+
/**
* Ensure that the data in this EditorInfo is compatible with an application
* that was developed against the given target API version. This can
@@ -573,6 +813,7 @@
dest.writeInt(fieldId);
dest.writeString(fieldName);
dest.writeBundle(extras);
+ mInitialSurroundingText.writeToParcel(dest, flags);
if (hintLocales != null) {
hintLocales.writeToParcel(dest, flags);
} else {
@@ -603,6 +844,9 @@
res.fieldId = source.readInt();
res.fieldName = source.readString();
res.extras = source.readBundle();
+ InitialSurroundingText initialSurroundingText =
+ InitialSurroundingText.CREATOR.createFromParcel(source);
+ res.mInitialSurroundingText = initialSurroundingText;
LocaleList hintLocales = LocaleList.CREATOR.createFromParcel(source);
res.hintLocales = hintLocales.isEmpty() ? null : hintLocales;
res.contentMimeTypes = source.readStringArray();
@@ -619,4 +863,93 @@
return 0;
}
+ // TODO(b/148035211): Unit tests for this class
+ static final class InitialSurroundingText implements Parcelable {
+ @Nullable final CharSequence mSurroundingText;
+ final int mSelectionHead;
+ final int mSelectionEnd;
+
+ InitialSurroundingText() {
+ mSurroundingText = null;
+ mSelectionHead = 0;
+ mSelectionEnd = 0;
+ }
+
+ InitialSurroundingText(@Nullable CharSequence surroundingText, int selectionHead,
+ int selectionEnd) {
+ mSurroundingText = surroundingText;
+ mSelectionHead = selectionHead;
+ mSelectionEnd = selectionEnd;
+ }
+
+ @Nullable
+ private CharSequence getInitialTextBeforeCursor(int n, int flags) {
+ if (mSurroundingText == null) {
+ return null;
+ }
+
+ final int length = Math.min(n, mSelectionHead);
+ return ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0)
+ ? mSurroundingText.subSequence(mSelectionHead - length, mSelectionHead)
+ : TextUtils.substring(mSurroundingText, mSelectionHead - length,
+ mSelectionHead);
+ }
+
+ @Nullable
+ private CharSequence getInitialSelectedText(int flags) {
+ if (mSurroundingText == null) {
+ return null;
+ }
+
+ return ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0)
+ ? mSurroundingText.subSequence(mSelectionHead, mSelectionEnd)
+ : TextUtils.substring(mSurroundingText, mSelectionHead, mSelectionEnd);
+ }
+
+ @Nullable
+ private CharSequence getInitialTextAfterCursor(int n, int flags) {
+ if (mSurroundingText == null) {
+ return null;
+ }
+
+ final int length = Math.min(n, mSurroundingText.length() - mSelectionEnd);
+ return ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0)
+ ? mSurroundingText.subSequence(mSelectionEnd, mSelectionEnd + length)
+ : TextUtils.substring(mSurroundingText, mSelectionEnd, mSelectionEnd + length);
+ }
+
+ private int getSelectionLength() {
+ return mSelectionEnd - mSelectionHead;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ TextUtils.writeToParcel(mSurroundingText, dest, flags);
+ dest.writeInt(mSelectionHead);
+ dest.writeInt(mSelectionEnd);
+ }
+
+ public static final @android.annotation.NonNull Parcelable.Creator<InitialSurroundingText>
+ CREATOR = new Parcelable.Creator<InitialSurroundingText>() {
+ @Override
+ public InitialSurroundingText createFromParcel(Parcel source) {
+ final CharSequence initialText =
+ TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ final int selectionHead = source.readInt();
+ final int selectionEnd = source.readInt();
+
+ return new InitialSurroundingText(initialText, selectionHead, selectionEnd);
+ }
+
+ @Override
+ public InitialSurroundingText[] newArray(int size) {
+ return new InitialSurroundingText[size];
+ }
+ };
+ }
}
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index e554540..4337ed5 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -158,7 +158,11 @@
* trigger an IPC round-trip that will take some time. Assume this
* method consumes a lot of time. Also, please keep in mind the
* Editor may choose to return less characters than requested even
- * if they are available for performance reasons.</p>
+ * if they are available for performance reasons. If you are using
+ * this to get the initial text around the cursor, you may consider
+ * using {@link EditorInfo#getInitialTextBeforeCursor(int, int)},
+ * {@link EditorInfo#getInitialSelectedText(int)}, and
+ * {@link EditorInfo#getInitialTextAfterCursor(int, int)} to prevent IPC costs.</p>
*
* <p><strong>Editor authors:</strong> please be careful of race
* conditions in implementing this call. An IME can make a change
@@ -196,7 +200,11 @@
*
* <p><strong>IME authors:</strong> please consider this will
* trigger an IPC round-trip that will take some time. Assume this
- * method consumes a lot of time.</p>
+ * method consumes a lot of time. If you are using this to get the
+ * initial text around the cursor, you may consider using
+ * {@link EditorInfo#getInitialTextBeforeCursor(int, int)},
+ * {@link EditorInfo#getInitialSelectedText(int)}, and
+ * {@link EditorInfo#getInitialTextAfterCursor(int, int)} to prevent IPC costs.</p>
*
* <p><strong>Editor authors:</strong> please be careful of race
* conditions in implementing this call. An IME can make a change
@@ -234,7 +242,11 @@
*
* <p><strong>IME authors:</strong> please consider this will
* trigger an IPC round-trip that will take some time. Assume this
- * method consumes a lot of time.</p>
+ * method consumes a lot of time. If you are using this to get the
+ * initial text around the cursor, you may consider using
+ * {@link EditorInfo#getInitialTextBeforeCursor(int, int)},
+ * {@link EditorInfo#getInitialSelectedText(int)}, and
+ * {@link EditorInfo#getInitialTextAfterCursor(int, int)} to prevent IPC costs.</p>
*
* <p><strong>Editor authors:</strong> please be careful of race
* conditions in implementing this call. An IME can make a change
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index b10631b..7fd4150 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -391,6 +391,9 @@
// This can only be true if the text view is editable.
private boolean mCursorControlEnabled;
+ // Specifies whether the new magnifier (with fish-eye effect) is enabled.
+ private final boolean mNewMagnifierEnabled;
+
Editor(TextView textView) {
mTextView = textView;
// Synchronize the filter list, which places the undo input filter at the end.
@@ -401,9 +404,13 @@
mCursorControlEnabled = AppGlobals.getIntCoreSetting(
WidgetFlags.KEY_ENABLE_CURSOR_CONTROL , 0) != 0;
+ mNewMagnifierEnabled = AppGlobals.getIntCoreSetting(
+ WidgetFlags.KEY_ENABLE_NEW_MAGNIFIER, 0) != 0;
if (TextView.DEBUG_CURSOR) {
logCursor("Editor", "Cursor control is %s.",
mCursorControlEnabled ? "enabled" : "disabled");
+ logCursor("Editor", "New magnifier is %s.",
+ mNewMagnifierEnabled ? "enabled" : "disabled");
}
}
@@ -422,7 +429,7 @@
if (FLAG_USE_MAGNIFIER && mMagnifierAnimator == null) {
// Lazy creates the magnifier instance because it requires the text height which cannot
// be measured at the time of Editor instance being created.
- final Magnifier.Builder builder = shouldUseNewMagnifier()
+ final Magnifier.Builder builder = mNewMagnifierEnabled
? createBuilderWithInlineMagnifierDefaults()
: Magnifier.createBuilderWithOldMagnifierDefaults(mTextView);
mMagnifierAnimator = new MagnifierMotionAnimator(builder.build());
@@ -430,24 +437,28 @@
return mMagnifierAnimator;
}
- private boolean shouldUseNewMagnifier() {
- // TODO: use a separate flag to enable new magnifier.
- return mCursorControlEnabled;
- }
-
private Magnifier.Builder createBuilderWithInlineMagnifierDefaults() {
final Magnifier.Builder params = new Magnifier.Builder(mTextView);
// TODO: supports changing the height/width dynamically because the text height can be
// dynamically changed.
+ float zoom = AppGlobals.getFloatCoreSetting(
+ WidgetFlags.KEY_MAGNIFIER_ZOOM_FACTOR, 1.5f);
+ float aspectRatio = AppGlobals.getFloatCoreSetting(
+ WidgetFlags.KEY_MAGNIFIER_ASPECT_RATIO, 5.5f);
+ // Avoid invalid/unsupported values.
+ if (zoom < 1.2f || zoom > 1.8f) {
+ zoom = 1.5f;
+ }
+ if (aspectRatio < 3 || aspectRatio > 8) {
+ aspectRatio = 5.5f;
+ }
+
final Paint.FontMetrics fontMetrics = mTextView.getPaint().getFontMetrics();
final float sourceHeight = fontMetrics.descent - fontMetrics.ascent;
- final float zoom = 1.5f;
- final float widthHeightRatio = 5.5f;
// Slightly increase the height to avoid tooLargeTextForMagnifier() returns true.
int height = (int)(sourceHeight * zoom) + 2;
- int width = (int)(widthHeightRatio * height);
-
+ int width = (int)(aspectRatio * height);
params.setFishEyeStyle()
.setSize(width, height)
@@ -4680,11 +4691,11 @@
}
};
- private int getPreferredWidth() {
+ protected final int getPreferredWidth() {
return Math.max(mDrawable.getIntrinsicWidth(), mMinSize);
}
- private int getPreferredHeight() {
+ protected final int getPreferredHeight() {
return Math.max(mDrawable.getIntrinsicHeight(), mMinSize);
}
@@ -5089,14 +5100,14 @@
mTextView.invalidateCursorPath();
suspendBlink();
- if (shouldUseNewMagnifier()) {
+ if (mNewMagnifierEnabled) {
// Calculates the line bounds as the content source bounds to the magnifier.
Layout layout = mTextView.getLayout();
int line = layout.getLineForOffset(getCurrentCursorOffset());
int lineLeft = (int) layout.getLineLeft(line);
lineLeft += mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
int lineRight = (int) layout.getLineRight(line);
- lineRight -= mTextView.getTotalPaddingRight() + mTextView.getScrollX();
+ lineRight += mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
mMagnifierAnimator.mMagnifier.setSourceHorizontalBounds(lineLeft, lineRight);
}
mMagnifierAnimator.show(showPosInView.x, showPosInView.y);
@@ -5218,7 +5229,7 @@
// Whether the popup window is in the invisible state and will be dismissed when finger up.
private boolean mPendingDismissOnUp = false;
// The alpha value of the drawable.
- private final int mDrawableOpacity = 255;
+ private final int mDrawableOpacity;
// Members for toggling the insertion menu in touch through mode.
@@ -5239,8 +5250,29 @@
// of the touch through events.
private float mTextHeight;
- public InsertionHandleView(Drawable drawable) {
+ // The delta height applied to the insertion handle view.
+ private final int mDeltaHeight;
+
+ InsertionHandleView(Drawable drawable) {
super(drawable, drawable, com.android.internal.R.id.insertion_handle);
+
+ int deltaHeight = 0;
+ int opacity = 255;
+ if (mCursorControlEnabled) {
+ deltaHeight = AppGlobals.getIntCoreSetting(
+ WidgetFlags.KEY_INSERTION_HANDLE_DELTA_HEIGHT, 25);
+ opacity = AppGlobals.getIntCoreSetting(
+ WidgetFlags.KEY_INSERTION_HANDLE_OPACITY, 50);
+ // Avoid invalid/unsupported values.
+ if (deltaHeight < -25 || deltaHeight > 50) {
+ deltaHeight = 25;
+ }
+ if (opacity < 10 || opacity > 100) {
+ opacity = 50;
+ }
+ }
+ mDeltaHeight = deltaHeight;
+ mDrawableOpacity = opacity;
}
private void hideAfterDelay() {
@@ -5293,6 +5325,17 @@
}
@Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (mCursorControlEnabled) {
+ final int height = Math.max(
+ getPreferredHeight() + mDeltaHeight, mDrawable.getIntrinsicHeight());
+ setMeasuredDimension(getPreferredWidth(), height);
+ return;
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent ev) {
if (mCursorControlEnabled && FLAG_ENABLE_CURSOR_DRAG) {
// Should only enable touch through when cursor drag is enabled.
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 733a775..970d70c 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -52,7 +52,6 @@
import android.view.ViewDebug;
import android.view.ViewHierarchyEncoder;
import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
@@ -307,6 +306,9 @@
setMax(a.getInt(R.styleable.ProgressBar_max, mMax));
setProgress(a.getInt(R.styleable.ProgressBar_progress, mProgress));
+ // onProgressRefresh() is only called when the progress changes. So we should set
+ // stateDescription during initialization here.
+ super.setStateDescription(formatStateDescription(mProgress));
setSecondaryProgress(a.getInt(
R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress));
@@ -1599,8 +1601,7 @@
}
void onProgressRefresh(float scale, boolean fromUser, int progress) {
- if (AccessibilityManager.getInstance(mContext).isEnabled()
- && mCustomStateDescription == null) {
+ if (mCustomStateDescription == null) {
super.setStateDescription(formatStateDescription(mProgress));
}
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 32d3fef..8ce6ee5 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8668,6 +8668,7 @@
outAttrs.initialSelStart = getSelectionStart();
outAttrs.initialSelEnd = getSelectionEnd();
outAttrs.initialCapsMode = ic.getCursorCapsMode(getInputType());
+ outAttrs.setInitialSurroundingText(mText);
return ic;
}
}
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 969bda9..42d7892 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -456,7 +456,7 @@
params.format = PixelFormat.TRANSLUCENT;
params.windowAnimations = com.android.internal.R.style.Animation_Toast;
params.type = WindowManager.LayoutParams.TYPE_TOAST;
- params.setFitIgnoreVisibility(true);
+ params.setFitInsetsIgnoringVisibility(true);
params.setTitle("Toast");
params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
diff --git a/core/java/android/widget/WidgetFlags.java b/core/java/android/widget/WidgetFlags.java
index fa1e498d..1a8e7a7 100644
--- a/core/java/android/widget/WidgetFlags.java
+++ b/core/java/android/widget/WidgetFlags.java
@@ -35,6 +35,66 @@
*/
public static final String KEY_ENABLE_CURSOR_CONTROL = "widget__enable_cursor_control";
+ /**
+ * The flag of delta height applies to the insertion handle when cursor control flag is enabled.
+ * The default value is 25.
+ */
+ public static final String INSERTION_HANDLE_DELTA_HEIGHT =
+ "CursorControlFeature__insertion_handle_delta_height";
+
+ /**
+ * The key name used in app core settings for {@link #INSERTION_HANDLE_DELTA_HEIGHT}.
+ */
+ public static final String KEY_INSERTION_HANDLE_DELTA_HEIGHT =
+ "widget__insertion_handle_delta_height";
+
+ /**
+ * The flag of opacity applies to the insertion handle when cursor control flag is enabled.
+ * The opacity value is in the range of {0..100}. The default value is 50.
+ */
+ public static final String INSERTION_HANDLE_OPACITY =
+ "CursorControlFeature__insertion_handle_opacity";
+
+ /**
+ * The key name used in app core settings for {@link #INSERTION_HANDLE_OPACITY}.
+ */
+ public static final String KEY_INSERTION_HANDLE_OPACITY =
+ "widget__insertion_handle_opacity";
+
+ /**
+ * The flag of enabling the new magnifier.
+ */
+ public static final String ENABLE_NEW_MAGNIFIER = "CursorControlFeature__enable_new_magnifier";
+
+ /**
+ * The key name used in app core settings for {@link #ENABLE_NEW_MAGNIFIER}.
+ */
+ public static final String KEY_ENABLE_NEW_MAGNIFIER = "widget__enable_new_magnifier";
+
+ /**
+ * The flag of zoom factor applies to the new magnifier.
+ * The default value is 1.5f.
+ */
+ public static final String MAGNIFIER_ZOOM_FACTOR =
+ "CursorControlFeature__magnifier_zoom_factor";
+
+ /**
+ * The key name used in app core settings for {@link #MAGNIFIER_ZOOM_FACTOR}.
+ */
+ public static final String KEY_MAGNIFIER_ZOOM_FACTOR = "widget__magnifier_zoom_factor";
+
+ /**
+ * The flag of aspect ratio (width/height) applies to the new magnifier.
+ * The default value is 5.5f.
+ */
+ public static final String MAGNIFIER_ASPECT_RATIO =
+ "CursorControlFeature__magnifier_aspect_ratio";
+
+ /**
+ * The key name used in app core settings for {@link #MAGNIFIER_ASPECT_RATIO}.
+ */
+ public static final String KEY_MAGNIFIER_ASPECT_RATIO = "widget__magnifier_aspect_ratio";
+
private WidgetFlags() {
}
}
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index 08022e9..b2aa043 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -26,7 +26,9 @@
import com.android.internal.widget.PagerAdapter;
import com.android.internal.widget.ViewPager;
+import java.util.HashSet;
import java.util.Objects;
+import java.util.Set;
/**
* Skeletal {@link PagerAdapter} implementation of a work or personal profile page for
@@ -34,6 +36,7 @@
*/
public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
+ private static final String TAG = "AbstractMultiProfilePagerAdapter";
static final int PROFILE_PERSONAL = 0;
static final int PROFILE_WORK = 1;
@IntDef({PROFILE_PERSONAL, PROFILE_WORK})
@@ -41,10 +44,17 @@
private final Context mContext;
private int mCurrentPage;
+ private OnProfileSelectedListener mOnProfileSelectedListener;
+ private Set<Integer> mLoadedPages;
AbstractMultiProfilePagerAdapter(Context context, int currentPage) {
mContext = Objects.requireNonNull(context);
mCurrentPage = currentPage;
+ mLoadedPages = new HashSet<>();
+ }
+
+ void setOnProfileSelectedListener(OnProfileSelectedListener listener) {
+ mOnProfileSelectedListener = listener;
}
Context getContext() {
@@ -57,15 +67,22 @@
* page and rebuilds the list.
*/
void setupViewPager(ViewPager viewPager) {
- viewPager.setCurrentItem(mCurrentPage);
viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
mCurrentPage = position;
- getActiveListAdapter().rebuildList();
+ if (!mLoadedPages.contains(position)) {
+ getActiveListAdapter().rebuildList();
+ mLoadedPages.add(position);
+ }
+ if (mOnProfileSelectedListener != null) {
+ mOnProfileSelectedListener.onProfileSelected(position);
+ }
}
});
viewPager.setAdapter(this);
+ viewPager.setCurrentItem(mCurrentPage);
+ mLoadedPages.add(mCurrentPage);
}
@Override
@@ -90,7 +107,8 @@
return mCurrentPage;
}
- UserHandle getCurrentUserHandle() {
+ @VisibleForTesting
+ public UserHandle getCurrentUserHandle() {
return getActiveListAdapter().mResolverListController.getUserHandle();
}
@@ -135,7 +153,8 @@
* <p>This method is meant to be implemented with an implementation-specific return type
* depending on the adapter type.
*/
- abstract Object getAdapterForIndex(int pageIndex);
+ @VisibleForTesting
+ public abstract Object getAdapterForIndex(int pageIndex);
@VisibleForTesting
public abstract ResolverListAdapter getActiveListAdapter();
@@ -152,7 +171,9 @@
abstract Object getCurrentRootAdapter();
- abstract ViewGroup getCurrentAdapterView();
+ abstract ViewGroup getActiveAdapterView();
+
+ abstract @Nullable ViewGroup getInactiveAdapterView();
protected class ProfileDescriptor {
final ViewGroup rootView;
@@ -160,4 +181,15 @@
this.rootView = rootView;
}
}
+
+ public interface OnProfileSelectedListener {
+ /**
+ * Callback for when the user changes the active tab from personal to work or vice versa.
+ * <p>This callback is only called when the intent resolver or share sheet shows
+ * the work and personal profiles.
+ * @param profileIndex {@link #PROFILE_PERSONAL} if the personal profile was selected or
+ * {@link #PROFILE_WORK} if the work profile was selected.
+ */
+ void onProfileSelected(int profileIndex);
+ }
}
\ No newline at end of file
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 9532fae..8bbc343 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -508,7 +508,6 @@
protected void onCreate(Bundle savedInstanceState) {
final long intentReceivedTime = System.currentTimeMillis();
// This is the only place this value is being set. Effectively final.
- //TODO(arangelov) - should there be a mIsAppPredictorComponentAvailable flag for work tab?
mIsAppPredictorComponentAvailable = isAppPredictionServiceAvailable();
mIsSuccessfullySelected = false;
@@ -689,29 +688,6 @@
mResolverDrawerLayout.setOnScrollChangeListener(this::handleScroll);
}
- final View chooserHeader = mResolverDrawerLayout.findViewById(R.id.chooser_header);
- final float defaultElevation = chooserHeader.getElevation();
- final float chooserHeaderScrollElevation =
- getResources().getDimensionPixelSize(R.dimen.chooser_header_scroll_elevation);
-
- mChooserMultiProfilePagerAdapter.getCurrentAdapterView().addOnScrollListener(
- new RecyclerView.OnScrollListener() {
- public void onScrollStateChanged(RecyclerView view, int scrollState) {
- }
-
- public void onScrolled(RecyclerView view, int dx, int dy) {
- if (view.getChildCount() > 0) {
- View child = view.getLayoutManager().findViewByPosition(0);
- if (child == null || child.getTop() < 0) {
- chooserHeader.setElevation(chooserHeaderScrollElevation);
- return;
- }
- }
-
- chooserHeader.setElevation(defaultElevation);
- }
- });
-
mResolverDrawerLayout.setOnCollapsedChangedListener(
new ResolverDrawerLayout.OnCollapsedChangedListener() {
@@ -1330,8 +1306,8 @@
}
@Override
- public void onPrepareAdapterView(ResolverListAdapter adapter) {
- mChooserMultiProfilePagerAdapter.getCurrentAdapterView().setVisibility(View.VISIBLE);
+ public void addUseDifferentAppLabelIfNecessary(ResolverListAdapter adapter) {
+ mChooserMultiProfilePagerAdapter.getActiveAdapterView().setVisibility(View.VISIBLE);
if (mCallerChooserTargets != null && mCallerChooserTargets.length > 0) {
mChooserMultiProfilePagerAdapter.getActiveListAdapter().addServiceResults(
/* origTarget */ null,
@@ -2202,7 +2178,7 @@
if (mChooserMultiProfilePagerAdapter == null) {
return;
}
- RecyclerView recyclerView = mChooserMultiProfilePagerAdapter.getCurrentAdapterView();
+ RecyclerView recyclerView = mChooserMultiProfilePagerAdapter.getActiveAdapterView();
ChooserGridAdapter gridAdapter = mChooserMultiProfilePagerAdapter.getCurrentRootAdapter();
if (gridAdapter == null || recyclerView == null) {
return;
@@ -2328,6 +2304,8 @@
@Override
public void onListRebuilt(ResolverListAdapter listAdapter) {
+ setupScrollListener();
+
ChooserListAdapter chooserListAdapter = (ChooserListAdapter) listAdapter;
if (chooserListAdapter.mDisplayList == null
|| chooserListAdapter.mDisplayList.isEmpty()) {
@@ -2368,6 +2346,34 @@
}
}
+ private void setupScrollListener() {
+ if (mResolverDrawerLayout == null) {
+ return;
+ }
+ final View chooserHeader = mResolverDrawerLayout.findViewById(R.id.chooser_header);
+ final float defaultElevation = chooserHeader.getElevation();
+ final float chooserHeaderScrollElevation =
+ getResources().getDimensionPixelSize(R.dimen.chooser_header_scroll_elevation);
+
+ mChooserMultiProfilePagerAdapter.getActiveAdapterView().addOnScrollListener(
+ new RecyclerView.OnScrollListener() {
+ public void onScrollStateChanged(RecyclerView view, int scrollState) {
+ }
+
+ public void onScrolled(RecyclerView view, int dx, int dy) {
+ if (view.getChildCount() > 0) {
+ View child = view.getLayoutManager().findViewByPosition(0);
+ if (child == null || child.getTop() < 0) {
+ chooserHeader.setElevation(chooserHeaderScrollElevation);
+ return;
+ }
+ }
+
+ chooserHeader.setElevation(defaultElevation);
+ }
+ });
+ }
+
@Override // ChooserListCommunicator
public boolean isSendAction(Intent targetIntent) {
if (targetIntent == null) {
@@ -2475,7 +2481,8 @@
* row level by this adapter but not on the item level. Individual targets within the row are
* handled by {@link ChooserListAdapter}
*/
- final class ChooserGridAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+ @VisibleForTesting
+ public final class ChooserGridAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private ChooserListAdapter mChooserListAdapter;
private final LayoutInflater mLayoutInflater;
@@ -2905,7 +2912,7 @@
if (mDirectShareViewHolder != null && canExpandDirectShare) {
mDirectShareViewHolder.handleScroll(
- mChooserMultiProfilePagerAdapter.getCurrentAdapterView(), y, oldy,
+ mChooserMultiProfilePagerAdapter.getActiveAdapterView(), y, oldy,
getMaxTargetsPerRow());
}
}
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index a8a676d..6ff844a 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -246,11 +246,6 @@
}
@Override
- public boolean shouldGetResolvedFilter() {
- return true;
- }
-
- @Override
public int getCount() {
return getRankedTargetCount() + getAlphaTargetCount()
+ getSelectableServiceTargetCount() + getCallerTargetCount();
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index 7d856e1..1c52d0e 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -16,6 +16,7 @@
package com.android.internal.app;
+import android.annotation.Nullable;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.ViewGroup;
@@ -77,7 +78,8 @@
}
@Override
- ChooserActivity.ChooserGridAdapter getAdapterForIndex(int pageIndex) {
+ @VisibleForTesting
+ public ChooserActivity.ChooserGridAdapter getAdapterForIndex(int pageIndex) {
return mItems[pageIndex].chooserGridAdapter;
}
@@ -121,10 +123,19 @@
}
@Override
- RecyclerView getCurrentAdapterView() {
+ RecyclerView getActiveAdapterView() {
return getListViewForIndex(getCurrentPage());
}
+ @Override
+ @Nullable
+ RecyclerView getInactiveAdapterView() {
+ if (getCount() == 1) {
+ return null;
+ }
+ return getListViewForIndex(1 - getCurrentPage());
+ }
+
class ChooserProfileDescriptor extends ProfileDescriptor {
private ChooserActivity.ChooserGridAdapter chooserGridAdapter;
private RecyclerView recyclerView;
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index be2d1d6..f3b6d29 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -26,6 +26,7 @@
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.IVoiceInteractionSessionListener;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
+import android.hardware.soundtrigger.KeyphraseMetadata;
import android.hardware.soundtrigger.ModelParams;
import android.hardware.soundtrigger.SoundTrigger;
import android.service.voice.IVoiceInteractionService;
@@ -72,8 +73,8 @@
*/
SoundTrigger.ModuleProperties getDspModuleProperties(in IVoiceInteractionService service);
/**
- * Indicates if there's a keyphrase sound model available for the given keyphrase ID.
- * This performs the check for the current user.
+ * Indicates if there's a keyphrase sound model available for the given keyphrase ID and the
+ * user ID of the caller.
*
* @param service The current VoiceInteractionService.
* @param keyphraseId The unique identifier for the keyphrase.
@@ -82,6 +83,18 @@
boolean isEnrolledForKeyphrase(IVoiceInteractionService service, int keyphraseId,
String bcp47Locale);
/**
+ * Generates KeyphraseMetadata for an enrolled sound model based on keyphrase string, locale,
+ * and the user ID of the caller.
+ *
+ * @param service The current VoiceInteractionService
+ * @param keyphrase Keyphrase text associated with the enrolled model
+ * @param bcp47Locale The BCP47 language tag for the keyphrase's locale.
+ * @return The metadata for the enrolled voice model bassed on the passed in parameters. Null if
+ * no matching voice model exists.
+ */
+ KeyphraseMetadata getEnrolledKeyphraseMetadata(IVoiceInteractionService service,
+ String keyphrase, String bcp47Locale);
+ /**
* Starts a recognition for the given keyphrase.
*/
int startRecognition(in IVoiceInteractionService service, int keyphraseId,
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index b2417b2..68d6e03 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -59,6 +59,7 @@
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -67,9 +68,12 @@
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.Button;
+import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.Space;
+import android.widget.TabHost;
+import android.widget.TabWidget;
import android.widget.TextView;
import android.widget.Toast;
@@ -82,6 +86,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.widget.ResolverDrawerLayout;
+import com.android.internal.widget.ViewPager;
import java.util.ArrayList;
import java.util.Arrays;
@@ -147,7 +152,10 @@
/**
* TODO(arangelov): Remove a couple of weeks after work/personal tabs are finalized.
*/
- static final boolean ENABLE_TABBED_VIEW = false;
+ @VisibleForTesting
+ public static boolean ENABLE_TABBED_VIEW = false;
+ private static final String TAB_TAG_PERSONAL = "personal";
+ private static final String TAB_TAG_WORK = "work";
private final PackageMonitor mPackageMonitor = createPackageMonitor();
@@ -418,12 +426,16 @@
Intent[] initialIntents,
List<ResolveInfo> rList,
boolean filterLastUsed) {
+ // We only show the default app for the profile of the current user. The filterLastUsed
+ // flag determines whether to show a default app and that app is not shown in the
+ // resolver list. So filterLastUsed should be false for the other profile.
ResolverListAdapter personalAdapter = createResolverListAdapter(
/* context */ this,
/* payloadIntents */ mIntents,
initialIntents,
rList,
- filterLastUsed,
+ (filterLastUsed && UserHandle.myUserId()
+ == getPersonalProfileUserHandle().getIdentifier()),
mUseLayoutForBrowsables,
/* userHandle */ getPersonalProfileUserHandle());
ResolverListAdapter workAdapter = createResolverListAdapter(
@@ -431,7 +443,8 @@
/* payloadIntents */ mIntents,
initialIntents,
rList,
- filterLastUsed,
+ (filterLastUsed && UserHandle.myUserId()
+ == getWorkProfileUserHandle().getIdentifier()),
mUseLayoutForBrowsables,
/* userHandle */ getWorkProfileUserHandle());
return new ResolverMultiProfilePagerAdapter(
@@ -495,12 +508,12 @@
mFooterSpacer = new Space(getApplicationContext());
} else {
((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter)
- .getCurrentAdapterView().removeFooterView(mFooterSpacer);
+ .getActiveAdapterView().removeFooterView(mFooterSpacer);
}
mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
mSystemWindowInsets.bottom));
((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter)
- .getCurrentAdapterView().addFooterView(mFooterSpacer);
+ .getActiveAdapterView().addFooterView(mFooterSpacer);
}
protected WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
@@ -817,7 +830,7 @@
public void onButtonClick(View v) {
final int id = v.getId();
- ListView listView = (ListView) mMultiProfilePagerAdapter.getCurrentAdapterView();
+ ListView listView = (ListView) mMultiProfilePagerAdapter.getActiveAdapterView();
ResolverListAdapter currentListAdapter = mMultiProfilePagerAdapter.getActiveListAdapter();
int which = currentListAdapter.hasFilteredItem()
? currentListAdapter.getFilteredPosition()
@@ -898,7 +911,10 @@
@Override // ResolverListCommunicator
public void onPostListReady(ResolverListAdapter listAdapter) {
- setHeader();
+ if (mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier()
+ == UserHandle.myUserId()) {
+ setHeader();
+ }
resetButtonBar();
onListRebuilt(listAdapter);
}
@@ -913,6 +929,9 @@
finish();
}
}
+
+ final ItemClickListener listener = new ItemClickListener();
+ setupAdapterListView((ListView) mMultiProfilePagerAdapter.getActiveAdapterView(), listener);
}
protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) {
@@ -1094,6 +1113,7 @@
return true;
}
+ @VisibleForTesting
public void safelyStartActivity(TargetInfo cti) {
// We're dispatching intents that might be coming from legacy apps, so
// don't kill ourselves.
@@ -1222,9 +1242,6 @@
+ "cannot be null.");
}
boolean rebuildCompleted = mMultiProfilePagerAdapter.getActiveListAdapter().rebuildList();
- if (mMultiProfilePagerAdapter.getInactiveListAdapter() != null) {
- mMultiProfilePagerAdapter.getInactiveListAdapter().rebuildList();
- }
if (useLayoutWithDefault()) {
mLayoutId = R.layout.resolver_list_with_default;
} else {
@@ -1272,45 +1289,99 @@
}
}
- setupViewVisibilities(count);
+ setupViewVisibilities();
+
+ if (hasWorkProfile() && ENABLE_TABBED_VIEW) {
+ setupProfileTabs();
+ }
+
return false;
}
- private void setupViewVisibilities(int count) {
- if (count == 0
- && mMultiProfilePagerAdapter.getActiveListAdapter().getPlaceholderCount() == 0) {
- final TextView emptyView = findViewById(R.id.empty);
- emptyView.setVisibility(View.VISIBLE);
- findViewById(R.id.profile_pager).setVisibility(View.GONE);
- } else {
- onPrepareAdapterView(mMultiProfilePagerAdapter.getActiveListAdapter());
+ private void setupProfileTabs() {
+ TabHost tabHost = findViewById(R.id.profile_tabhost);
+ tabHost.setup();
+ ViewPager viewPager = findViewById(R.id.profile_pager);
+ TabHost.TabSpec tabSpec = tabHost.newTabSpec(TAB_TAG_PERSONAL)
+ .setContent(R.id.profile_pager)
+ .setIndicator(getString(R.string.resolver_personal_tab));
+ tabHost.addTab(tabSpec);
+
+ tabSpec = tabHost.newTabSpec(TAB_TAG_WORK)
+ .setContent(R.id.profile_pager)
+ .setIndicator(getString(R.string.resolver_work_tab));
+ tabHost.addTab(tabSpec);
+
+ TabWidget tabWidget = tabHost.getTabWidget();
+ tabWidget.setVisibility(View.VISIBLE);
+ resetTabsHeaderStyle(tabWidget);
+ updateActiveTabStyle(tabHost);
+
+ tabHost.setOnTabChangedListener(tabId -> {
+ resetTabsHeaderStyle(tabWidget);
+ updateActiveTabStyle(tabHost);
+ if (TAB_TAG_PERSONAL.equals(tabId)) {
+ viewPager.setCurrentItem(0);
+ } else {
+ viewPager.setCurrentItem(1);
+ }
+ setupViewVisibilities();
+ });
+
+ viewPager.setVisibility(View.VISIBLE);
+ tabHost.setCurrentTab(mMultiProfilePagerAdapter.getCurrentPage());
+ mMultiProfilePagerAdapter.setOnProfileSelectedListener(tabHost::setCurrentTab);
+ }
+
+ private void resetTabsHeaderStyle(TabWidget tabWidget) {
+ for (int i = 0; i < tabWidget.getChildCount(); i++) {
+ TextView title = tabWidget.getChildAt(i).findViewById(android.R.id.title);
+ title.setTextColor(getColor(R.color.resolver_tabs_inactive_color));
+ title.setAllCaps(false);
+ }
+ }
+
+ private void updateActiveTabStyle(TabHost tabHost) {
+ TextView title = tabHost.getTabWidget().getChildAt(tabHost.getCurrentTab())
+ .findViewById(android.R.id.title);
+ title.setTextColor(getColor(R.color.resolver_tabs_active_color));
+ }
+
+ private void setupViewVisibilities() {
+ int count = mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredCount();
+ boolean shouldShowEmptyState = count == 0
+ && mMultiProfilePagerAdapter.getActiveListAdapter().getPlaceholderCount() == 0;
+ //TODO(arangelov): Handle empty state
+ if (!shouldShowEmptyState) {
+ addUseDifferentAppLabelIfNecessary(mMultiProfilePagerAdapter.getActiveListAdapter());
}
}
/**
- * Prepare the scrollable view which consumes data in the list adapter.
+ * Add a label to signify that the user can pick a different app.
* @param adapter The adapter used to provide data to item views.
*/
- public void onPrepareAdapterView(ResolverListAdapter adapter) {
- mMultiProfilePagerAdapter.getCurrentAdapterView().setVisibility(View.VISIBLE);
+ public void addUseDifferentAppLabelIfNecessary(ResolverListAdapter adapter) {
final boolean useHeader = adapter.hasFilteredItem();
- final ListView listView = (ListView) mMultiProfilePagerAdapter.getCurrentAdapterView();
- final ItemClickListener listener = new ItemClickListener();
+ if (useHeader) {
+ FrameLayout stub = findViewById(R.id.stub);
+ stub.setVisibility(View.VISIBLE);
+ TextView textView = (TextView) LayoutInflater.from(this).inflate(
+ R.layout.resolver_different_item_header, null, false);
+ if (ENABLE_TABBED_VIEW) {
+ textView.setGravity(Gravity.CENTER);
+ }
+ stub.addView(textView);
+ }
+ }
+
+ private void setupAdapterListView(ListView listView, ItemClickListener listener) {
listView.setOnItemClickListener(listener);
listView.setOnItemLongClickListener(listener);
if (mSupportsAlwaysUseOption || mUseLayoutForBrowsables) {
listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
}
-
- // In case this method is called again (due to activity recreation), avoid adding a new
- // header if one is already present.
- if (useHeader && listView.getHeaderViewsCount() == 0) {
- listView.setHeaderDividersEnabled(true);
- listView.addHeaderView(LayoutInflater.from(this).inflate(
- R.layout.resolver_different_item_header, listView, false),
- null, false);
- }
}
/**
@@ -1378,7 +1449,7 @@
}
// When the items load in, if an item was already selected, enable the buttons
- ListView currentAdapterView = (ListView) mMultiProfilePagerAdapter.getCurrentAdapterView();
+ ListView currentAdapterView = (ListView) mMultiProfilePagerAdapter.getActiveAdapterView();
if (currentAdapterView != null
&& currentAdapterView.getCheckedItemPosition() != ListView.INVALID_POSITION) {
setAlwaysButtonEnabled(true, currentAdapterView.getCheckedItemPosition(), true);
@@ -1388,8 +1459,18 @@
@Override // ResolverListCommunicator
public boolean useLayoutWithDefault() {
- return mSupportsAlwaysUseOption
- && mMultiProfilePagerAdapter.getActiveListAdapter().hasFilteredItem();
+ // We only use the default app layout when the profile of the active user has a
+ // filtered item. We always show the same default app even in the inactive user profile.
+ boolean currentUserAdapterHasFilteredItem;
+ if (mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier()
+ == UserHandle.myUserId()) {
+ currentUserAdapterHasFilteredItem =
+ mMultiProfilePagerAdapter.getActiveListAdapter().hasFilteredItem();
+ } else {
+ currentUserAdapterHasFilteredItem =
+ mMultiProfilePagerAdapter.getInactiveListAdapter().hasFilteredItem();
+ }
+ return mSupportsAlwaysUseOption && currentUserAdapterHasFilteredItem;
}
/**
@@ -1494,9 +1575,8 @@
.resolveInfoForPosition(position, true) == null) {
return;
}
-
ListView currentAdapterView =
- (ListView) mMultiProfilePagerAdapter.getCurrentAdapterView();
+ (ListView) mMultiProfilePagerAdapter.getActiveAdapterView();
final int checkedPos = currentAdapterView.getCheckedItemPosition();
final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
if (!useLayoutWithDefault()
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index ef7a347..d227ec5 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -193,7 +193,8 @@
mBaseResolveList);
} else {
currentResolveList = mUnfilteredResolveList =
- mResolverListController.getResolversForIntent(shouldGetResolvedFilter(),
+ mResolverListController.getResolversForIntent(
+ /* shouldGetResolvedFilter= */ true,
mResolverListCommunicator.shouldGetActivityMetadata(),
mIntents);
if (currentResolveList == null) {
@@ -363,10 +364,6 @@
}
}
- public boolean shouldGetResolvedFilter() {
- return mFilterLastUsed;
- }
-
private void addResolveInfoWithAlternates(ResolvedComponentInfo rci) {
final int count = rci.getCount();
final Intent intent = rci.getIntentAt(0);
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
index abd3eb2..0bfe9eb 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -133,7 +133,8 @@
return resolvedComponents;
}
- UserHandle getUserHandle() {
+ @VisibleForTesting
+ public UserHandle getUserHandle() {
return mUserHandle;
}
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
index d72c52b..567ed74 100644
--- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -16,6 +16,7 @@
package com.android.internal.app;
+import android.annotation.Nullable;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.ViewGroup;
@@ -81,7 +82,8 @@
}
@Override
- ResolverListAdapter getAdapterForIndex(int pageIndex) {
+ @VisibleForTesting
+ public ResolverListAdapter getAdapterForIndex(int pageIndex) {
return mItems[pageIndex].resolverListAdapter;
}
@@ -106,10 +108,19 @@
}
@Override
- ListView getCurrentAdapterView() {
+ ListView getActiveAdapterView() {
return getListViewForIndex(getCurrentPage());
}
+ @Override
+ @Nullable
+ ViewGroup getInactiveAdapterView() {
+ if (getCount() == 1) {
+ return null;
+ }
+ return getListViewForIndex(1 - getCurrentPage());
+ }
+
class ResolverProfileDescriptor extends ProfileDescriptor {
private ResolverListAdapter resolverListAdapter;
final ListView listView;
diff --git a/core/java/com/android/internal/app/WrapHeightViewPager.java b/core/java/com/android/internal/app/ResolverViewPager.java
similarity index 74%
rename from core/java/com/android/internal/app/WrapHeightViewPager.java
rename to core/java/com/android/internal/app/ResolverViewPager.java
index b017bb4..8ec94f1 100644
--- a/core/java/com/android/internal/app/WrapHeightViewPager.java
+++ b/core/java/com/android/internal/app/ResolverViewPager.java
@@ -18,39 +18,36 @@
import android.content.Context;
import android.util.AttributeSet;
+import android.view.MotionEvent;
import android.view.View;
import com.android.internal.widget.ViewPager;
/**
- * A {@link ViewPager} which wraps around its first child's height.
+ * A {@link ViewPager} which wraps around its first child's height and has swiping disabled.
* <p>Normally {@link ViewPager} instances expand their height to cover all remaining space in
* the layout.
- * <p>This class is used for the intent resolver picker's tabbed view to maintain
- * consistency with the previous behavior.
+ * <p>This class is used for the intent resolver and share sheet's tabbed view.
*/
-public class WrapHeightViewPager extends ViewPager {
+public class ResolverViewPager extends ViewPager {
- public WrapHeightViewPager(Context context) {
+ public ResolverViewPager(Context context) {
super(context);
}
- public WrapHeightViewPager(Context context, AttributeSet attrs) {
+ public ResolverViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
- public WrapHeightViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
+ public ResolverViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
- public WrapHeightViewPager(Context context, AttributeSet attrs,
+ public ResolverViewPager(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
- // TODO(arangelov): When we have multiple pages, the height should wrap to the currently
- // displayed page. Investigate whether onMeasure is called when changing a page, and instead
- // of getChildAt(0), use the currently displayed one.
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -68,4 +65,14 @@
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ return false;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return false;
+ }
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index b3548b8..580c1f0 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -12644,7 +12644,6 @@
/*@hide */
public WifiBatteryStats getWifiBatteryStats() {
- WifiBatteryStats s = new WifiBatteryStats();
final int which = STATS_SINCE_CHARGED;
final long rawRealTime = SystemClock.elapsedRealtime() * 1000;
final ControllerActivityCounter counter = getWifiControllerActivity();
@@ -12675,24 +12674,16 @@
for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
timeSignalStrengthTimeMs[i] = getWifiSignalStrengthTime(i, rawRealTime, which) / 1000;
}
- s.setLoggingDurationMillis(computeBatteryRealtime(rawRealTime, which) / 1000);
- s.setKernelActiveTimeMillis(getWifiActiveTime(rawRealTime, which) / 1000);
- s.setNumPacketsTx(getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which));
- s.setNumBytesTx(getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which));
- s.setNumPacketsRx(getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which));
- s.setNumBytesRx(getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which));
- s.setSleepTimeMillis(sleepTimeMs);
- s.setIdleTimeMillis(idleTimeMs);
- s.setRxTimeMillis(rxTimeMs);
- s.setTxTimeMillis(txTimeMs);
- s.setScanTimeMillis(scanTimeMs);
- s.setEnergyConsumedMaMillis(energyConsumedMaMs);
- s.setNumAppScanRequest(numAppScanRequest);
- s.setTimeInStateMillis(timeInStateMs);
- s.setTimeInSupplicantStateMillis(timeInSupplStateMs);
- s.setTimeInRxSignalStrengthLevelMillis(timeSignalStrengthTimeMs);
- s.setMonitoredRailChargeConsumedMaMillis(monitoredRailChargeConsumedMaMs);
- return s;
+ return new WifiBatteryStats(
+ computeBatteryRealtime(rawRealTime, which) / 1000,
+ getWifiActiveTime(rawRealTime, which) / 1000,
+ getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which),
+ getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which),
+ getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which),
+ getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which),
+ sleepTimeMs, scanTimeMs, idleTimeMs, rxTimeMs, txTimeMs, energyConsumedMaMs,
+ numAppScanRequest, timeInStateMs, timeSignalStrengthTimeMs, timeInSupplStateMs,
+ monitoredRailChargeConsumedMaMs);
}
/*@hide */
diff --git a/core/java/com/android/internal/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java
index 5e3c5df..7a9b659 100644
--- a/core/java/com/android/internal/os/ProcessCpuTracker.java
+++ b/core/java/com/android/internal/os/ProcessCpuTracker.java
@@ -753,6 +753,8 @@
proto.write(CpuUsageProto.Load.LOAD15, mLoad15);
proto.end(currentLoadToken);
+ buildWorkingProcs();
+
proto.write(CpuUsageProto.NOW, now);
proto.write(CpuUsageProto.LAST_SAMPLE_TIME, mLastSampleTime);
proto.write(CpuUsageProto.CURRENT_SAMPLE_TIME, mCurrentSampleTime);
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index a6637a2..8412b84 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -68,6 +68,7 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.InsetDrawable;
import android.graphics.drawable.LayerDrawable;
+import android.os.Build.VERSION_CODES;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
@@ -282,6 +283,11 @@
private Insets mLastBackgroundInsets = Insets.NONE;
private boolean mDrawLegacyNavigationBarBackground;
+ /**
+ * Whether the app targets an SDK that uses the new insets APIs.
+ */
+ private boolean mUseNewInsetsApi;
+
DecorView(Context context, int featureId, PhoneWindow window,
WindowManager.LayoutParams params) {
super(context);
@@ -311,6 +317,7 @@
initResizingPaints();
mLegacyNavigationBarBackgroundPaint.setColor(Color.BLACK);
+ mUseNewInsetsApi = context.getApplicationInfo().targetSdkVersion >= VERSION_CODES.R;
}
void setBackgroundFallback(@Nullable Drawable fallbackDrawable) {
@@ -1174,20 +1181,24 @@
// Note: We don't need to check for IN_SCREEN or INSET_DECOR because unlike the status bar,
// these flags wouldn't make the window draw behind the navigation bar, unless
// LAYOUT_HIDE_NAVIGATION was set.
+ //
+ // Note: Once the app targets R+, we no longer do this logic because we can't rely on
+ // SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION to indicate whether the app wants to handle it by
+ // themselves.
boolean hideNavigation = (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
|| !(state == null || state.getSource(ITYPE_NAVIGATION_BAR).isVisible());
boolean forceConsumingNavBar = (mForceWindowDrawsBarBackgrounds
&& (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
&& (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
- && (attrs.getFitWindowInsetsTypes() & Type.navigationBars()) != 0
&& !hideNavigation)
|| (mLastShouldAlwaysConsumeSystemBars && hideNavigation);
boolean consumingNavBar =
((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
&& (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
- && (attrs.getFitWindowInsetsTypes() & Type.navigationBars()) != 0
- && !hideNavigation)
+ && !hideNavigation
+ // TODO IME wrap_content windows need to have margin to work properly
+ && (!mUseNewInsetsApi || isImeWindow))
|| forceConsumingNavBar;
// If we didn't request fullscreen layout, but we still got it because of the
@@ -1200,16 +1211,14 @@
boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0
&& (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0
&& (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0
- && (attrs.getFitWindowInsetsTypes() & Type.statusBars()) != 0
&& mForceWindowDrawsBarBackgrounds
&& mLastTopInset != 0
|| (mLastShouldAlwaysConsumeSystemBars && fullscreen);
- int sides = attrs.getFitWindowInsetsSides();
- int consumedTop = consumingStatusBar && (sides & Side.TOP) != 0 ? mLastTopInset : 0;
- int consumedRight = consumingNavBar && (sides & Side.RIGHT) != 0 ? mLastRightInset : 0;
- int consumedBottom = consumingNavBar && (sides & Side.BOTTOM) != 0 ? mLastBottomInset : 0;
- int consumedLeft = consumingNavBar && (sides & Side.LEFT) != 0 ? mLastLeftInset : 0;
+ int consumedTop = consumingStatusBar ? mLastTopInset : 0;
+ int consumedRight = consumingNavBar ? mLastRightInset : 0;
+ int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
+ int consumedLeft = consumingNavBar ? mLastLeftInset : 0;
if (mContentRoot != null
&& mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 4abd397..95558c3 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -17,8 +17,11 @@
package com.android.internal.policy;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
+import static android.view.View.SYSTEM_UI_LAYOUT_FLAGS;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
@@ -30,6 +33,8 @@
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import android.annotation.NonNull;
import android.app.ActivityManager;
@@ -43,6 +48,7 @@
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Color;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
@@ -66,6 +72,7 @@
import android.util.AndroidRuntimeException;
import android.util.EventLog;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
@@ -307,6 +314,8 @@
/** @see ViewRootImpl#mActivityConfigCallback */
private ActivityConfigCallback mActivityConfigCallback;
+ private OnContentApplyWindowInsetsListener mPendingOnContentApplyWindowInsetsListener;
+
static class WindowManagerHolder {
static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
@@ -2092,6 +2101,34 @@
/** Notify when decor view is attached to window and {@link ViewRootImpl} is available. */
void onViewRootImplSet(ViewRootImpl viewRoot) {
viewRoot.setActivityConfigCallback(mActivityConfigCallback);
+ if (mPendingOnContentApplyWindowInsetsListener != null) {
+ viewRoot.setOnContentApplyWindowInsetsListener(
+ mPendingOnContentApplyWindowInsetsListener);
+ mPendingOnContentApplyWindowInsetsListener = null;
+ } else {
+ viewRoot.setOnContentApplyWindowInsetsListener(
+ createDefaultContentWindowInsetsListener());
+ }
+ }
+
+ private OnContentApplyWindowInsetsListener createDefaultContentWindowInsetsListener() {
+ return insets -> {
+ if ((getDecorView().getWindowSystemUiVisibility() & SYSTEM_UI_LAYOUT_FLAGS) != 0) {
+ return new Pair<>(Insets.NONE, insets);
+ }
+
+ boolean includeIme = (getAttributes().softInputMode & SOFT_INPUT_MASK_ADJUST)
+ == SOFT_INPUT_ADJUST_RESIZE;
+ Insets insetsToApply;
+ if (ViewRootImpl.sNewInsetsMode == 0) {
+ insetsToApply = insets.getSystemWindowInsets();
+ } else {
+ insetsToApply = insets.getInsets(systemBars() | (includeIme ? ime() : 0));
+ }
+ insets = insets.inset(insetsToApply);
+ return new Pair<>(insetsToApply,
+ insets.inset(insetsToApply).consumeSystemWindowInsets());
+ };
}
static private final String FOCUSED_ID_TAG = "android:focusedViewId";
@@ -2344,6 +2381,8 @@
setFlags(0, flagsToUpdate);
} else {
setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
+ getAttributes().setFitInsetsSides(0);
+ getAttributes().setFitInsetsTypes(0);
}
if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
@@ -2656,7 +2695,7 @@
mContentParent = generateLayout(mDecor);
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
- mDecor.makeOptionalFitsSystemWindows();
+ mDecor.makeFrameworkOptionalFitsSystemWindows();
final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
R.id.decor_content_parent);
@@ -3853,4 +3892,19 @@
public List<Rect> getSystemGestureExclusionRects() {
return getViewRootImpl().getRootSystemGestureExclusionRects();
}
+
+ @Override
+ public void setOnContentApplyWindowInsetsListener(OnContentApplyWindowInsetsListener listener) {
+ ViewRootImpl impl = getViewRootImpl();
+ if (impl != null) {
+ impl.setOnContentApplyWindowInsetsListener(listener);
+ } else {
+ mPendingOnContentApplyWindowInsetsListener = listener;
+ }
+ }
+
+ @Override
+ public void resetOnContentApplyWindowInsetsListener() {
+ setOnContentApplyWindowInsetsListener(createDefaultContentWindowInsetsListener());
+ }
}
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 07d0d7d..f608958 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -22,6 +22,7 @@
import android.annotation.StyleRes;
import android.app.Notification;
import android.app.Person;
+import android.app.RemoteInputHistoryItem;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -161,8 +162,9 @@
if (headerText != null) {
mConversationTitle = headerText.getText();
}
- addRemoteInputHistoryToMessages(newMessages,
- extras.getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY));
+ RemoteInputHistoryItem[] history = (RemoteInputHistoryItem[])
+ extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+ addRemoteInputHistoryToMessages(newMessages, history);
boolean showSpinner =
extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false);
bind(newMessages, newHistoricMessages, showSpinner);
@@ -175,14 +177,18 @@
private void addRemoteInputHistoryToMessages(
List<Notification.MessagingStyle.Message> newMessages,
- CharSequence[] remoteInputHistory) {
+ RemoteInputHistoryItem[] remoteInputHistory) {
if (remoteInputHistory == null || remoteInputHistory.length == 0) {
return;
}
for (int i = remoteInputHistory.length - 1; i >= 0; i--) {
- CharSequence message = remoteInputHistory[i];
- newMessages.add(new Notification.MessagingStyle.Message(
- message, 0, (Person) null, true /* remoteHistory */));
+ RemoteInputHistoryItem historyMessage = remoteInputHistory[i];
+ Notification.MessagingStyle.Message message = new Notification.MessagingStyle.Message(
+ historyMessage.getText(), 0, (Person) null, true /* remoteHistory */);
+ if (historyMessage.getUri() != null) {
+ message.setData(historyMessage.getMimeType(), historyMessage.getUri());
+ }
+ newMessages.add(message);
}
}
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 5937283..32a7cf3 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -243,7 +243,8 @@
return localBitmap.valid() ? &localBitmap->bitmap() : nullptr;
}
-SkImageInfo GraphicsJNI::getBitmapInfo(JNIEnv* env, jobject bitmap, uint32_t* outRowBytes) {
+SkImageInfo GraphicsJNI::getBitmapInfo(JNIEnv* env, jobject bitmap, uint32_t* outRowBytes,
+ bool* isHardware) {
SkASSERT(env);
SkASSERT(bitmap);
SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class));
@@ -252,6 +253,9 @@
if (outRowBytes) {
*outRowBytes = localBitmap->rowBytes();
}
+ if (isHardware) {
+ *isHardware = localBitmap->isHardware();
+ }
return localBitmap->info();
}
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 99034ed..1e49765 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -62,7 +62,8 @@
static android::Canvas* getNativeCanvas(JNIEnv*, jobject canvas);
static android::Bitmap* getNativeBitmap(JNIEnv*, jobject bitmap);
- static SkImageInfo getBitmapInfo(JNIEnv*, jobject bitmap, uint32_t* outRowBytes);
+ static SkImageInfo getBitmapInfo(JNIEnv*, jobject bitmap, uint32_t* outRowBytes,
+ bool* isHardware);
static SkRegion* getNativeRegion(JNIEnv*, jobject region);
/*
diff --git a/core/jni/android/graphics/apex/android_bitmap.cpp b/core/jni/android/graphics/apex/android_bitmap.cpp
index 3c7691b..6628529 100644
--- a/core/jni/android/graphics/apex/android_bitmap.cpp
+++ b/core/jni/android/graphics/apex/android_bitmap.cpp
@@ -78,7 +78,7 @@
}
}
-static uint32_t getInfoFlags(const SkImageInfo& info) {
+static uint32_t getAlphaFlags(const SkImageInfo& info) {
switch (info.alphaType()) {
case kUnknown_SkAlphaType:
LOG_ALWAYS_FATAL("Bitmap has no alpha type");
@@ -92,6 +92,14 @@
}
}
+static uint32_t getInfoFlags(const SkImageInfo& info, bool isHardware) {
+ uint32_t flags = getAlphaFlags(info);
+ if (isHardware) {
+ flags |= ANDROID_BITMAP_FLAGS_IS_HARDWARE;
+ }
+ return flags;
+}
+
ABitmap* ABitmap_copy(ABitmap* srcBitmapHandle, AndroidBitmapFormat dstFormat) {
SkColorType dstColorType = getColorType(dstFormat);
if (srcBitmapHandle && dstColorType != kUnknown_SkColorType) {
@@ -108,19 +116,19 @@
return nullptr;
}
-static AndroidBitmapInfo getInfo(const SkImageInfo& imageInfo, uint32_t rowBytes) {
+static AndroidBitmapInfo getInfo(const SkImageInfo& imageInfo, uint32_t rowBytes, bool isHardware) {
AndroidBitmapInfo info;
info.width = imageInfo.width();
info.height = imageInfo.height();
info.stride = rowBytes;
info.format = getFormat(imageInfo);
- info.flags = getInfoFlags(imageInfo);
+ info.flags = getInfoFlags(imageInfo, isHardware);
return info;
}
AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmapHandle) {
Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle);
- return getInfo(bitmap->info(), bitmap->rowBytes());
+ return getInfo(bitmap->info(), bitmap->rowBytes(), bitmap->isHardware());
}
ADataSpace ABitmap_getDataSpace(ABitmap* bitmapHandle) {
@@ -131,8 +139,9 @@
AndroidBitmapInfo ABitmap_getInfoFromJava(JNIEnv* env, jobject bitmapObj) {
uint32_t rowBytes = 0;
- SkImageInfo imageInfo = GraphicsJNI::getBitmapInfo(env, bitmapObj, &rowBytes);
- return getInfo(imageInfo, rowBytes);
+ bool isHardware = false;
+ SkImageInfo imageInfo = GraphicsJNI::getBitmapInfo(env, bitmapObj, &rowBytes, &isHardware);
+ return getInfo(imageInfo, rowBytes, isHardware);
}
void* ABitmap_getPixels(ABitmap* bitmapHandle) {
@@ -175,7 +184,7 @@
class CompressWriter : public SkWStream {
public:
- CompressWriter(void* userContext, AndroidBitmap_compress_write_fn fn)
+ CompressWriter(void* userContext, AndroidBitmap_CompressWriteFunc fn)
: mUserContext(userContext), mFn(fn), mBytesWritten(0) {}
bool write(const void* buffer, size_t size) override {
@@ -190,7 +199,7 @@
private:
void* mUserContext;
- AndroidBitmap_compress_write_fn mFn;
+ AndroidBitmap_CompressWriteFunc mFn;
size_t mBytesWritten;
};
@@ -198,7 +207,7 @@
int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const void* pixels,
AndroidBitmapCompressFormat inFormat, int32_t quality, void* userContext,
- AndroidBitmap_compress_write_fn fn) {
+ AndroidBitmap_CompressWriteFunc fn) {
Bitmap::JavaCompressFormat format;
switch (inFormat) {
case ANDROID_BITMAP_COMPRESS_FORMAT_JPEG:
@@ -290,3 +299,12 @@
return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
}
}
+
+AHardwareBuffer* ABitmap_getHardwareBuffer(ABitmap* bitmapHandle) {
+ Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle);
+ AHardwareBuffer* buffer = bitmap->hardwareBuffer();
+ if (buffer) {
+ AHardwareBuffer_acquire(buffer);
+ }
+ return buffer;
+}
diff --git a/core/jni/android/graphics/apex/include/android/graphics/bitmap.h b/core/jni/android/graphics/apex/include/android/graphics/bitmap.h
index 683851d..45fec2a 100644
--- a/core/jni/android/graphics/apex/include/android/graphics/bitmap.h
+++ b/core/jni/android/graphics/apex/include/android/graphics/bitmap.h
@@ -21,6 +21,8 @@
#include <jni.h>
#include <sys/cdefs.h>
+struct AHardwareBuffer;
+
__BEGIN_DECLS
/**
@@ -61,7 +63,19 @@
// NDK access
int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const void* pixels,
AndroidBitmapCompressFormat format, int32_t quality, void* userContext,
- AndroidBitmap_compress_write_fn);
+ AndroidBitmap_CompressWriteFunc);
+/**
+ * Retrieve the native object associated with a HARDWARE Bitmap.
+ *
+ * Client must not modify it while a Bitmap is wrapping it.
+ *
+ * @param bitmap Handle to an android.graphics.Bitmap.
+ * @return on success, a pointer to the
+ * AHardwareBuffer associated with bitmap. This acquires
+ * a reference on the buffer, and the client must call
+ * AHardwareBuffer_release when finished with it.
+ */
+AHardwareBuffer* ABitmap_getHardwareBuffer(ABitmap* bitmap);
__END_DECLS
@@ -116,6 +130,7 @@
ADataSpace getDataSpace() const { return ABitmap_getDataSpace(mBitmap); }
void* getPixels() const { return ABitmap_getPixels(mBitmap); }
void notifyPixelsChanged() const { ABitmap_notifyPixelsChanged(mBitmap); }
+ AHardwareBuffer* getHardwareBuffer() const { return ABitmap_getHardwareBuffer(mBitmap); }
private:
// takes ownership of the provided ABitmap
diff --git a/core/jni/android_media_AudioEffectDescriptor.cpp b/core/jni/android_media_AudioEffectDescriptor.cpp
index 5175a05..37d8114 100644
--- a/core/jni/android_media_AudioEffectDescriptor.cpp
+++ b/core/jni/android_media_AudioEffectDescriptor.cpp
@@ -39,17 +39,21 @@
jstring jImplementor;
char str[EFFECT_STRING_LEN_MAX];
- if ((nDescriptor->flags & EFFECT_FLAG_TYPE_MASK)
- == EFFECT_FLAG_TYPE_AUXILIARY) {
- jConnect = env->NewStringUTF("Auxiliary");
- } else if ((nDescriptor->flags & EFFECT_FLAG_TYPE_MASK)
- == EFFECT_FLAG_TYPE_INSERT) {
- jConnect = env->NewStringUTF("Insert");
- } else if ((nDescriptor->flags & EFFECT_FLAG_TYPE_MASK)
- == EFFECT_FLAG_TYPE_PRE_PROC) {
- jConnect = env->NewStringUTF("Pre Processing");
- } else {
- return (jint) AUDIO_JAVA_BAD_VALUE;
+ switch (nDescriptor->flags & EFFECT_FLAG_TYPE_MASK) {
+ case EFFECT_FLAG_TYPE_AUXILIARY:
+ jConnect = env->NewStringUTF("Auxiliary");
+ break;
+ case EFFECT_FLAG_TYPE_INSERT:
+ jConnect = env->NewStringUTF("Insert");
+ break;
+ case EFFECT_FLAG_TYPE_PRE_PROC:
+ jConnect = env->NewStringUTF("Pre Processing");
+ break;
+ case EFFECT_FLAG_TYPE_POST_PROC:
+ jConnect = env->NewStringUTF("Post Processing");
+ break;
+ default:
+ return (jint)AUDIO_JAVA_BAD_VALUE;
}
AudioEffect::guidToString(&nDescriptor->type, str, EFFECT_STRING_LEN_MAX);
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index e266fe6..0156e23 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -307,6 +307,43 @@
return kAudioStatusError;
}
+static jint getVectorOfAudioDeviceTypeAddr(JNIEnv *env, jintArray deviceTypes,
+ jobjectArray deviceAddresses,
+ Vector<AudioDeviceTypeAddr> &audioDeviceTypeAddrVector) {
+ if (deviceTypes == nullptr || deviceAddresses == nullptr) {
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+ jsize deviceCount = env->GetArrayLength(deviceTypes);
+ if (deviceCount == 0 || deviceCount != env->GetArrayLength(deviceAddresses)) {
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+ // retrieve all device types
+ std::vector<audio_devices_t> deviceTypesVector;
+ jint *typesPtr = nullptr;
+ typesPtr = env->GetIntArrayElements(deviceTypes, 0);
+ if (typesPtr == nullptr) {
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+ for (jint i = 0; i < deviceCount; i++) {
+ deviceTypesVector.push_back((audio_devices_t)typesPtr[i]);
+ }
+ // check each address is a string and add device type/address to list
+ jclass stringClass = FindClassOrDie(env, "java/lang/String");
+ for (jint i = 0; i < deviceCount; i++) {
+ jobject addrJobj = env->GetObjectArrayElement(deviceAddresses, i);
+ if (!env->IsInstanceOf(addrJobj, stringClass)) {
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+ const char *address = env->GetStringUTFChars((jstring)addrJobj, NULL);
+ AudioDeviceTypeAddr dev = AudioDeviceTypeAddr(typesPtr[i], address);
+ audioDeviceTypeAddrVector.add(dev);
+ env->ReleaseStringUTFChars((jstring)addrJobj, address);
+ }
+ env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0);
+
+ return (jint)NO_ERROR;
+}
+
static jint
android_media_AudioSystem_muteMicrophone(JNIEnv *env, jobject thiz, jboolean on)
{
@@ -1905,6 +1942,10 @@
nCriterion.mValue.mUid = env->GetIntField(jCriterion,
gAudioMixMatchCriterionFields.mIntProp);
break;
+ case RULE_MATCH_USERID:
+ nCriterion.mValue.mUserId =
+ env->GetIntField(jCriterion, gAudioMixMatchCriterionFields.mIntProp);
+ break;
case RULE_MATCH_ATTRIBUTE_USAGE:
case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET: {
jobject jAttributes = env->GetObjectField(jCriterion, gAudioMixMatchCriterionFields.mAttr);
@@ -1990,39 +2031,11 @@
static jint android_media_AudioSystem_setUidDeviceAffinities(JNIEnv *env, jobject clazz,
jint uid, jintArray deviceTypes, jobjectArray deviceAddresses) {
- if (deviceTypes == nullptr || deviceAddresses == nullptr) {
- return (jint) AUDIO_JAVA_BAD_VALUE;
- }
- jsize nb = env->GetArrayLength(deviceTypes);
- if (nb == 0 || nb != env->GetArrayLength(deviceAddresses)) {
- return (jint) AUDIO_JAVA_BAD_VALUE;
- }
- // retrieve all device types
- std::vector<audio_devices_t> deviceTypesVector;
- jint* typesPtr = nullptr;
- typesPtr = env->GetIntArrayElements(deviceTypes, 0);
- if (typesPtr == nullptr) {
- return (jint) AUDIO_JAVA_BAD_VALUE;
- }
- for (jint i = 0; i < nb; i++) {
- deviceTypesVector.push_back((audio_devices_t) typesPtr[i]);
- }
-
- // check each address is a string and add device type/address to list for device affinity
Vector<AudioDeviceTypeAddr> deviceVector;
- jclass stringClass = FindClassOrDie(env, "java/lang/String");
- for (jint i = 0; i < nb; i++) {
- jobject addrJobj = env->GetObjectArrayElement(deviceAddresses, i);
- if (!env->IsInstanceOf(addrJobj, stringClass)) {
- return (jint) AUDIO_JAVA_BAD_VALUE;
- }
- const char* address = env->GetStringUTFChars((jstring) addrJobj, NULL);
- AudioDeviceTypeAddr dev = AudioDeviceTypeAddr(typesPtr[i], address);
- deviceVector.add(dev);
- env->ReleaseStringUTFChars((jstring) addrJobj, address);
+ jint results = getVectorOfAudioDeviceTypeAddr(env, deviceTypes, deviceAddresses, deviceVector);
+ if (results != NO_ERROR) {
+ return results;
}
- env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0);
-
status_t status = AudioSystem::setUidDeviceAffinities((uid_t) uid, deviceVector);
return (jint) nativeToJavaStatus(status);
}
@@ -2033,6 +2046,23 @@
return (jint) nativeToJavaStatus(status);
}
+static jint android_media_AudioSystem_setUserIdDeviceAffinities(JNIEnv *env, jobject clazz,
+ jint userId, jintArray deviceTypes,
+ jobjectArray deviceAddresses) {
+ Vector<AudioDeviceTypeAddr> deviceVector;
+ jint results = getVectorOfAudioDeviceTypeAddr(env, deviceTypes, deviceAddresses, deviceVector);
+ if (results != NO_ERROR) {
+ return results;
+ }
+ status_t status = AudioSystem::setUserIdDeviceAffinities((int)userId, deviceVector);
+ return (jint)nativeToJavaStatus(status);
+}
+
+static jint android_media_AudioSystem_removeUserIdDeviceAffinities(JNIEnv *env, jobject clazz,
+ jint userId) {
+ status_t status = AudioSystem::removeUserIdDeviceAffinities((int)userId);
+ return (jint)nativeToJavaStatus(status);
+}
static jint
android_media_AudioSystem_systemReady(JNIEnv *env, jobject thiz)
@@ -2463,7 +2493,9 @@
{"setPreferredDeviceForStrategy", "(IILjava/lang/String;)I", (void *)android_media_AudioSystem_setPreferredDeviceForStrategy},
{"removePreferredDeviceForStrategy", "(I)I", (void *)android_media_AudioSystem_removePreferredDeviceForStrategy},
{"getPreferredDeviceForStrategy", "(I[Landroid/media/AudioDeviceAddress;)I", (void *)android_media_AudioSystem_getPreferredDeviceForStrategy},
- {"getDevicesForAttributes", "(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAddress;)I", (void *)android_media_AudioSystem_getDevicesForAttributes}
+ {"getDevicesForAttributes", "(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAddress;)I", (void *)android_media_AudioSystem_getDevicesForAttributes},
+ {"setUserIdDeviceAffinities", "(I[I[Ljava/lang/String;)I", (void *)android_media_AudioSystem_setUserIdDeviceAffinities},
+ {"removeUserIdDeviceAffinities", "(I)I", (void *)android_media_AudioSystem_removeUserIdDeviceAffinities}
};
static const JNINativeMethod gEventHandlerMethods[] = {
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 041019e..a888b43 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -1355,6 +1355,71 @@
lpTrack->setParameters(param.toString());
}
+static jint android_media_AudioTrack_setAudioDescriptionMixLeveldB(JNIEnv *env, jobject thiz,
+ jfloat level) {
+ sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+ if (lpTrack == nullptr) {
+ jniThrowException(env, "java/lang/IllegalStateException", "AudioTrack not initialized");
+ return (jint)AUDIO_JAVA_ERROR;
+ }
+
+ // TODO: replace in r-dev or r-tv-dev with code if HW is able to set audio mix level.
+ return (jint)AUDIO_JAVA_ERROR;
+}
+
+static jint android_media_AudioTrack_getAudioDescriptionMixLeveldB(JNIEnv *env, jobject thiz,
+ jfloatArray level) {
+ sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+ if (lpTrack == nullptr) {
+ ALOGE("%s: AudioTrack not initialized", __func__);
+ return (jint)AUDIO_JAVA_ERROR;
+ }
+ jfloat *nativeLevel = (jfloat *)env->GetPrimitiveArrayCritical(level, NULL);
+ if (nativeLevel == nullptr) {
+ ALOGE("%s: Cannot retrieve level pointer", __func__);
+ return (jint)AUDIO_JAVA_ERROR;
+ }
+
+ // TODO: replace in r-dev or r-tv-dev with code if HW is able to set audio mix level.
+ // By contract we can return -infinity if unsupported.
+ *nativeLevel = -std::numeric_limits<float>::infinity();
+ env->ReleasePrimitiveArrayCritical(level, nativeLevel, 0 /* mode */);
+ nativeLevel = nullptr;
+ return (jint)AUDIO_JAVA_SUCCESS;
+}
+
+static jint android_media_AudioTrack_setDualMonoMode(JNIEnv *env, jobject thiz, jint dualMonoMode) {
+ sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+ if (lpTrack == nullptr) {
+ jniThrowException(env, "java/lang/IllegalStateException", "AudioTrack not initialized");
+ return (jint)AUDIO_JAVA_ERROR;
+ }
+
+ // TODO: replace in r-dev or r-tv-dev with code if HW is able to set audio mix level.
+ return (jint)AUDIO_JAVA_ERROR;
+}
+
+static jint android_media_AudioTrack_getDualMonoMode(JNIEnv *env, jobject thiz,
+ jintArray dualMonoMode) {
+ sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+ if (lpTrack == nullptr) {
+ ALOGE("%s: AudioTrack not initialized", __func__);
+ return (jint)AUDIO_JAVA_ERROR;
+ }
+ jfloat *nativeDualMonoMode = (jfloat *)env->GetPrimitiveArrayCritical(dualMonoMode, NULL);
+ if (nativeDualMonoMode == nullptr) {
+ ALOGE("%s: Cannot retrieve dualMonoMode pointer", __func__);
+ return (jint)AUDIO_JAVA_ERROR;
+ }
+
+ // TODO: replace in r-dev or r-tv-dev with code if HW is able to select dual mono mode.
+ // By contract we can return DUAL_MONO_MODE_OFF if unsupported.
+ *nativeDualMonoMode = 0; // DUAL_MONO_MODE_OFF for now.
+ env->ReleasePrimitiveArrayCritical(dualMonoMode, nativeDualMonoMode, 0 /* mode */);
+ nativeDualMonoMode = nullptr;
+ return (jint)AUDIO_JAVA_SUCCESS;
+}
+
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -1425,6 +1490,12 @@
{"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation},
{"native_getPortId", "()I", (void *)android_media_AudioTrack_get_port_id},
{"native_set_delay_padding", "(II)V", (void *)android_media_AudioTrack_set_delay_padding},
+ {"native_set_audio_description_mix_level_db", "(F)I",
+ (void *)android_media_AudioTrack_setAudioDescriptionMixLeveldB},
+ {"native_get_audio_description_mix_level_db", "([F)I",
+ (void *)android_media_AudioTrack_getAudioDescriptionMixLeveldB},
+ {"native_set_dual_mono_mode", "(I)I", (void *)android_media_AudioTrack_setDualMonoMode},
+ {"native_get_dual_mono_mode", "([I)I", (void *)android_media_AudioTrack_getDualMonoMode},
};
// field names found in android/media/AudioTrack.java
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 566c385..b523811 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -193,7 +193,8 @@
{
int err = memtrack_proc_get(p, pid);
if (err != 0) {
- ALOGW("failed to get memory consumption info: %d", err);
+ // The memtrack HAL may not be available, do not log to avoid flooding
+ // logcat.
return err;
}
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 01b5920..b01083b 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -413,6 +413,12 @@
return anw->perform(surface, NATIVE_WINDOW_SET_AUTO_REFRESH, int(enabled));
}
+static jint nativeSetFrameRate(JNIEnv* env, jclass clazz, jlong nativeObject, jfloat frameRate) {
+ Surface* surface = reinterpret_cast<Surface*>(nativeObject);
+ ANativeWindow* anw = static_cast<ANativeWindow*>(surface);
+ return anw->perform(surface, NATIVE_WINDOW_SET_FRAME_RATE, float(frameRate));
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gSurfaceMethods[] = {
@@ -447,6 +453,7 @@
(void*)nativeAttachAndQueueBufferWithColorSpace},
{"nativeSetSharedBufferModeEnabled", "(JZ)I", (void*)nativeSetSharedBufferModeEnabled},
{"nativeSetAutoRefreshEnabled", "(JZ)I", (void*)nativeSetAutoRefreshEnabled},
+ {"nativeSetFrameRate", "(JF)I", (void*)nativeSetFrameRate},
};
int register_android_view_Surface(JNIEnv* env)
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index e0f9571..2b9d454 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -584,6 +584,14 @@
transaction->setShadowRadius(ctrl, shadowRadius);
}
+static void nativeSetFrameRate(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
+ jfloat frameRate) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+ const auto ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ transaction->setFrameRate(ctrl, frameRate);
+}
+
static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) {
const auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
jlongArray array = env->NewLongArray(displayIds.size());
@@ -1383,6 +1391,8 @@
(void*)nativeSetLayerStack },
{"nativeSetShadowRadius", "(JJF)V",
(void*)nativeSetShadowRadius },
+ {"nativeSetFrameRate", "(JJF)V",
+ (void*)nativeSetFrameRate },
{"nativeGetPhysicalDisplayIds", "()[J",
(void*)nativeGetPhysicalDisplayIds },
{"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;",
diff --git a/core/proto/android/app/job/enums.proto b/core/proto/android/app/job/enums.proto
index f702b3e..d2bf205 100644
--- a/core/proto/android/app/job/enums.proto
+++ b/core/proto/android/app/job/enums.proto
@@ -34,4 +34,5 @@
STOP_REASON_TIMEOUT = 3;
STOP_REASON_DEVICE_IDLE = 4;
STOP_REASON_DEVICE_THERMAL = 5;
+ STOP_REASON_RESTRAINED = 6;
}
diff --git a/core/proto/android/hardware/location/context_hub_info.proto b/core/proto/android/hardware/location/context_hub_info.proto
new file mode 100644
index 0000000..de5cd55
--- /dev/null
+++ b/core/proto/android/hardware/location/context_hub_info.proto
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.hardware.location;
+
+import "frameworks/base/core/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+
+message ContextHubInfoProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+ // Context hub unique identifier
+ optional int32 id = 1;
+ // A name for the hub
+ optional string name = 2;
+ // A name for the vendor
+ optional string vendor = 3;
+ // Description of the tool chain
+ optional string toolchain = 4;
+ optional int32 platform_version = 5;
+ optional int32 static_sw_version = 6;
+ optional int32 toolchain_version = 7;
+ // The CHRE platform ID as defined in chre/version.h
+ optional int64 chre_platform_id = 8;
+ // Peak MIPS that this hub can deliver
+ optional float peak_mips = 9;
+ // Power draw in stopped state in milli watts
+ optional float stopped_power_draw_mw = 10;
+ // Power draw in sleep state in milli watts
+ optional float sleep_power_draw_mw = 11;
+ // Peak power draw in milli watts
+ optional float peak_power_draw_mw = 12;
+ // The maximum number of bytes that can be sent per message to the hub
+ optional int32 max_packet_length_bytes = 13;
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index d08cbed..8adcc9e 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -36,6 +36,7 @@
import "frameworks/base/core/proto/android/server/alarmmanagerservice.proto";
import "frameworks/base/core/proto/android/server/fingerprint.proto";
import "frameworks/base/core/proto/android/server/jobscheduler.proto";
+import "frameworks/base/core/proto/android/server/location/context_hub.proto";
import "frameworks/base/core/proto/android/server/powermanagerservice.proto";
import "frameworks/base/core/proto/android/server/rolemanagerservice.proto";
import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
@@ -486,6 +487,11 @@
(section).args = "connmetrics --proto"
];
+ optional com.android.server.location.ContextHubServiceProto context_hub = 3051 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "contexthub --proto"
+ ];
+
// Reserved for OEMs.
extensions 50000 to 100000;
}
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index b71e539..303d62d 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -282,6 +282,10 @@
// expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
// WINDOW_SIZE_MS.
optional int64 rare_window_size_ms = 6;
+ // The quota window size of the particular standby bucket. Apps in this standby bucket are
+ // expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+ // WINDOW_SIZE_MS.
+ optional int64 restricted_window_size_ms = 20;
// The maximum amount of time an app can have its jobs running within a 24 hour window.
optional int64 max_execution_time_ms = 7;
// The maximum number of jobs an app can run within this particular standby bucket's
@@ -296,6 +300,9 @@
// The maximum number of jobs an app can run within this particular standby bucket's
// window size.
optional int32 max_job_count_rare = 11;
+ // The maximum number of jobs an app can run within this particular standby bucket's
+ // window size.
+ optional int32 max_job_count_restricted = 21;
// The period of time used to rate limit recently run jobs.
optional int32 rate_limiting_window_ms = 19;
// The maximum number of jobs that should be allowed to run in the past
@@ -313,12 +320,17 @@
// The maximum number of timing sessions an app can run within this particular standby
// bucket's window size.
optional int32 max_session_count_rare = 16;
+ // The maximum number of timing sessions an app can run within this particular standby
+ // bucket's window size.
+ optional int32 max_session_count_restricted = 22;
// The maximum number of timing sessions that should be allowed to run in the past
// rate_limiting_window_ms.
optional int32 max_session_count_per_rate_limiting_window = 17;
// Treat two distinct {@link TimingSession}s as the same if they start and end within this
// amount of time of each other.
optional int64 timing_session_coalescing_duration_ms = 18;
+
+ // Next tag: 23
}
optional QuotaController quota_controller = 24;
@@ -943,6 +955,9 @@
optional JobInfo job_info = 6;
repeated ConstraintEnum required_constraints = 7;
+ // Dynamic constraints are additional constraints imposed by the system that MUST be met before
+ // the app can run if the app does not have quota.
+ repeated ConstraintEnum dynamic_constraints = 31;
repeated ConstraintEnum satisfied_constraints = 8;
repeated ConstraintEnum unsatisfied_constraints = 9;
optional bool is_doze_whitelisted = 10;
@@ -956,6 +971,8 @@
// Battery Saver). This implicit constraint must be satisfied for the
// job to run.
optional bool is_not_restricted_in_bg = 2;
+ // True if dynamic constraints have been satisfied.
+ optional bool is_dynamic_satisfied = 3;
}
optional ImplicitConstraints implicit_constraints = 25;
@@ -998,6 +1015,7 @@
FREQUENT = 2;
RARE = 3;
NEVER = 4;
+ RESTRICTED = 5;
}
optional Bucket standby_bucket = 17;
optional bool is_exempted_from_app_standby = 27;
@@ -1028,7 +1046,7 @@
// was no attempt.
optional int64 time_since_first_force_batch_attempt_ms = 29;
- // Next tag: 31
+ // Next tag: 32
}
// Dump from com.android.server.job.JobConcurrencyManager.
diff --git a/core/proto/android/server/location/context_hub.proto b/core/proto/android/server/location/context_hub.proto
new file mode 100644
index 0000000..c87f791
--- /dev/null
+++ b/core/proto/android/server/location/context_hub.proto
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package com.android.server.location;
+
+import "frameworks/base/core/proto/android/hardware/location/context_hub_info.proto";
+import "frameworks/base/core/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+
+message ContextHubServiceProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ repeated .android.hardware.location.ContextHubInfoProto context_hub_info = 1;
+ optional ClientManagerProto client_manager = 2;
+}
+
+message ClientManagerProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ message RegistrationRecord {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int64 timestamp_ms = 1;
+ optional int32 action = 2;
+ // ClientBroker endpoint id, contexthub id and package name
+ optional string broker = 3;
+ }
+
+ repeated ClientBrokerProto client_brokers = 1;
+ repeated RegistrationRecord registration_records = 2;
+}
+
+message ClientBrokerProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 endpoint_id = 1;
+ optional int32 attached_context_hub_id = 2;
+ optional string package = 3;
+ optional int64 nano_app_id = 4;
+ optional bool pending_intent_request_valid = 5;
+ optional bool has_pending_intent = 6;
+ optional bool pending_intent_cancelled = 7;
+ optional bool registered = 8;
+
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3c65753..4595bab 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1616,6 +1616,7 @@
<!-- Allows applications to request network
recommendations and scores from the NetworkScoreService.
+ @SystemApi
<p>Not for use by third-party applications. @hide -->
<permission android:name="android.permission.REQUEST_NETWORK_SCORES"
android:protectionLevel="signature|setup" />
@@ -2253,6 +2254,9 @@
<eat-comment />
<!-- @SystemApi @TestApi Allows an application to write to internal media storage
+ @deprecated This permission is no longer honored in the system and no longer adds
+ the media_rw gid as a supplementary gid to the holder. Use the
+ android.permission.MANAGE_EXTERNAL_STORAGE instead.
@hide -->
<permission android:name="android.permission.WRITE_MEDIA_STORAGE"
android:protectionLevel="signature|privileged" />
@@ -2531,6 +2535,14 @@
android:description="@string/permdesc_useDataInBackground"
android:protectionLevel="normal" />
+ <!-- Allows a companion app to associate to Wi-Fi.
+ <p>Only for use by a single pre-approved app.
+ @hide
+ @SystemApi
+ -->
+ <permission android:name="android.permission.COMPANION_APPROVE_WIFI_CONNECTIONS"
+ android:protectionLevel="signature|privileged" />
+
<!-- ================================== -->
<!-- Permissions affecting the system wallpaper -->
@@ -2709,6 +2721,11 @@
<permission android:name="android.permission.READ_DEVICE_CONFIG"
android:protectionLevel="signature|preinstalled" />
+ <!-- @SystemApi @hide Allows an application to monitor config settings access.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.MONITOR_DEVICE_CONFIG_ACCESS"
+ android:protectionLevel="signature"/>
+
<!-- @SystemApi @TestApi Allows an application to call
{@link android.app.ActivityManager#forceStopPackage}.
@hide -->
@@ -3715,6 +3732,13 @@
<permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_MODE"
android:protectionLevel="signature" />
+ <!-- Allows an application to control the lights on the device.
+ @hide
+ @SystemApi
+ @TestApi -->
+ <permission android:name="android.permission.CONTROL_DEVICE_LIGHTS"
+ android:protectionLevel="signature|privileged" />
+
<!-- Allows an application to control the color saturation of the display.
@hide
@SystemApi -->
@@ -3793,6 +3817,24 @@
<permission android:name="android.permission.CAPTURE_MEDIA_OUTPUT"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows an application to capture the audio played by other apps
+ with the {@code USAGE_VOICE_COMMUNICATION} usage.
+
+ The application may opt out of capturing by setting an allow capture policy of
+ {@link android.media.AudioAttributes#ALLOW_CAPTURE_BY_NONE}.
+
+ There are strong restriction listed at
+ {@link android.media.AudioAttributes#ALLOW_CAPTURE_BY_SYSTEM}
+ on what an app can do with the captured audio.
+
+ See {@code CAPTURE_AUDIO_OUTPUT} and {@code CAPTURE_MEDIA_OUTPUT} for capturing
+ audio use cases other than voice communication playback.
+
+ <p>Not for use by third-party applications.</p>
+ @hide -->
+ <permission android:name="android.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi Allows an application to capture audio for hotword detection.
<p>Not for use by third-party applications.</p>
@hide -->
diff --git a/core/res/res/layout/autofill_inline_suggestion.xml b/core/res/res/layout/autofill_inline_suggestion.xml
index f7ac164..27faea4 100644
--- a/core/res/res/layout/autofill_inline_suggestion.xml
+++ b/core/res/res/layout/autofill_inline_suggestion.xml
@@ -16,19 +16,20 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
+ style="?android:attr/autofillInlineSuggestionChip"
android:layout_width="wrap_content"
- android:layout_height="56dp"
- android:background="@color/white"
- android:orientation="horizontal"
- android:paddingStart="12dp"
- android:paddingEnd="12dp">
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:orientation="horizontal">
<ImageView
android:id="@+id/autofill_inline_suggestion_start_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
android:layout_gravity="center"
+ android:scaleType="fitCenter"
android:contentDescription="autofill_inline_suggestion_start_icon" />
<LinearLayout
@@ -36,32 +37,33 @@
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_weight="1"
- android:layout_marginStart="12dp"
- android:layout_marginEnd="12dp"
+ android:paddingStart="4dp"
+ android:paddingEnd="4dp"
android:orientation="vertical"
- android:gravity="center_vertical">
+ android:gravity="center">
<TextView
+ style="?android:attr/autofillInlineSuggestionTitle"
android:id="@+id/autofill_inline_suggestion_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
- android:maxLines="1"
- tools:text="username1"/>
+ android:maxLines="1"/>
<TextView
+ style="?android:attr/autofillInlineSuggestionSubtitle"
android:id="@+id/autofill_inline_suggestion_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
- android:maxLines="1"
- tools:text="inline fill service"/>
+ android:maxLines="1"/>
</LinearLayout>
<ImageView
android:id="@+id/autofill_inline_suggestion_end_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
android:layout_gravity="center"
+ android:scaleType="fitCenter"
android:contentDescription="autofill_inline_suggestion_end_icon" />
</LinearLayout>
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 0c45e45..4e8a41f 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -61,10 +61,33 @@
android:layout_height="wrap_content"
android:visibility="gone" />
- <com.android.internal.widget.ViewPager
- android:id="@+id/profile_pager"
+ <TabHost
+ android:id="@+id/profile_tabhost"
android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:background="?attr/colorBackgroundFloating">
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TabWidget
+ android:id="@android:id/tabs"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ </TabWidget>
+ <FrameLayout
+ android:id="@android:id/tabcontent"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <com.android.internal.app.ResolverViewPager
+ android:id="@+id/profile_pager"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ </FrameLayout>
+ </LinearLayout>
+ </TabHost>
<TextView android:id="@+id/empty"
android:layout_width="match_parent"
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index c5d8912..757cd53 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -70,14 +70,44 @@
android:background="?attr/colorBackgroundFloating"
android:foreground="?attr/dividerVertical" />
- <com.android.internal.app.WrapHeightViewPager
- android:id="@+id/profile_pager"
+ <FrameLayout
+ android:id="@+id/stub"
+ android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:divider="?attr/dividerVertical"
- android:footerDividersEnabled="false"
- android:headerDividersEnabled="false"
- android:dividerHeight="1dp"/>
+ android:background="?attr/colorBackgroundFloating"/>
+
+ <TabHost
+ android:id="@+id/profile_tabhost"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:background="?attr/colorBackgroundFloating">
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TabWidget
+ android:id="@android:id/tabs"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ </TabWidget>
+ <FrameLayout
+ android:id="@android:id/tabcontent"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <com.android.internal.app.ResolverViewPager
+ android:id="@+id/profile_pager"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:divider="?attr/dividerVertical"
+ android:footerDividersEnabled="false"
+ android:headerDividersEnabled="false"
+ android:dividerHeight="1dp"/>
+ </FrameLayout>
+ </LinearLayout>
+ </TabHost>
<View
android:layout_alwaysShow="true"
diff --git a/core/res/res/layout/resolver_list_per_profile.xml b/core/res/res/layout/resolver_list_per_profile.xml
index 68b9917..6d8d348 100644
--- a/core/res/res/layout/resolver_list_per_profile.xml
+++ b/core/res/res/layout/resolver_list_per_profile.xml
@@ -25,7 +25,7 @@
android:nestedScrollingEnabled="true"
android:scrollbarStyle="outsideOverlay"
android:scrollIndicators="top|bottom"
- android:divider="?attr/dividerVertical"
+ android:divider="@null"
android:footerDividersEnabled="false"
android:headerDividersEnabled="false"
- android:dividerHeight="1dp" />
\ No newline at end of file
+ android:dividerHeight="0dp" />
\ No newline at end of file
diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
index 5b3d929..b546738 100644
--- a/core/res/res/layout/resolver_list_with_default.xml
+++ b/core/res/res/layout/resolver_list_with_default.xml
@@ -151,14 +151,46 @@
android:background="?attr/colorBackgroundFloating"
android:foreground="?attr/dividerVertical" />
- <com.android.internal.app.WrapHeightViewPager
- android:id="@+id/profile_pager"
+ <FrameLayout
+ android:id="@+id/stub"
+ android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:dividerHeight="1dp"
- android:divider="?attr/dividerVertical"
- android:footerDividersEnabled="false"
- android:headerDividersEnabled="false"/>
+ android:background="?attr/colorBackgroundFloating"/>
+
+ <TabHost
+ android:id="@+id/profile_tabhost"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:background="?attr/colorBackgroundFloating">
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TabWidget
+ android:id="@android:id/tabs"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+ </TabWidget>
+ <FrameLayout
+ android:id="@android:id/tabcontent"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <com.android.internal.app.ResolverViewPager
+ android:id="@+id/profile_pager"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:dividerHeight="1dp"
+ android:divider="?attr/dividerVertical"
+ android:footerDividersEnabled="false"
+ android:headerDividersEnabled="false"/>
+ </FrameLayout>
+ </LinearLayout>
+ </TabHost>
+
<View
android:layout_alwaysShow="true"
android:layout_width="match_parent"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 940e9f1..c9c47b9 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9197,4 +9197,12 @@
</declare-styleable>
<attr name="autoSizePresetSizes" />
+
+ <declare-styleable name="AutofillInlineSuggestion">
+ <!-- @hide @SystemApi -->
+ <attr name="isAutofillInlineSuggestionTheme" format="boolean" />
+ <attr name="autofillInlineSuggestionChip" format="reference" />
+ <attr name="autofillInlineSuggestionTitle" format="reference" />
+ <attr name="autofillInlineSuggestionSubtitle" format="reference" />
+ </declare-styleable>
</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 0895edc..cfed805 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2260,6 +2260,55 @@
<attr name="name" />
</declare-styleable>
+ <!-- The <code>processes</code> tag specifies the processes the application will run code in
+ and optionally characteristics of those processes. This tag is optional; if not
+ specified, components will simply run in the processes they specify. If supplied,
+ they can only specify processes that are enumerated here, and if they don't this
+ will be treated as a corrupt apk and result in an install failure.
+
+ <p>This appears as a child tag of the
+ {@link #AndroidManifestApplication application} tag. -->
+ <declare-styleable name="AndroidManifestProcesses" parent="AndroidManifestApplication">
+ </declare-styleable>
+
+ <!-- The <code>process</code> tag enumerates one of the available processes under its
+ containing <code>processes</code> tag.
+
+ <p>This appears as a child tag of the
+ {@link #AndroidManifestProcesses processes} tag. -->
+ <declare-styleable name="AndroidManifestProcess" parent="AndroidManifestProcesses">
+ <!-- Required name of the process that is allowed -->
+ <attr name="process" />
+ </declare-styleable>
+
+ <!-- The <code>deny-permission</code> tag specifies that a permission is to be denied
+ for a particular process (if specified under the
+ {@link #AndroidManifestProcess process} tag) or by default for all
+ processes {if specified under the
+ @link #AndroidManifestProcesses processes} tag).
+
+ <p>This appears as a child tag of the
+ {@link #AndroidManifestProcesses processes} and
+ {@link #AndroidManifestProcess process} tags. -->
+ <declare-styleable name="AndroidManifestDenyPermission"
+ parent="AndroidManifestProcesses">
+ <!-- Required name of the permission that is to be denied -->
+ <attr name="name" />
+ </declare-styleable>
+
+ <!-- The <code>allow-permission</code> tag specifies that a permission is to be allowed
+ for a particular process, when it was previously denied for all processes through
+ {@link #AndroidManifestDenyPermission deny-permission}
+
+ <p>This appears as a child tag of the
+ {@link #AndroidManifestProcesses processes} and
+ {@link #AndroidManifestProcess process} tags. -->
+ <declare-styleable name="AndroidManifestAllowPermission"
+ parent="AndroidManifestProcesses">
+ <!-- Required name of the permission that is to be allowed. -->
+ <attr name="name" />
+ </declare-styleable>
+
<!-- The <code>provider</code> tag declares a
{@link android.content.ContentProvider} class that is available
as part of the package's application components, supplying structured
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 1dcd389..731e2ec 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -224,4 +224,6 @@
<!-- Resolver/Chooser -->
<color name="resolver_text_color_secondary_dark">#ffC4C6C6</color>
+ <color name="resolver_tabs_active_color">#FF1A73E8</color>
+ <color name="resolver_tabs_inactive_color">#FF80868B</color>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7fd444a..686bb55 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1628,29 +1628,21 @@
config_timeZoneRulesUpdateTrackingEnabled are true.] -->
<integer name="config_timeZoneRulesCheckRetryCount">5</integer>
- <!-- Whether to enable network location overlay which allows network
- location provider to be replaced by an app at run-time. When disabled,
- only the config_networkLocationProviderPackageName package will be
- searched for network location provider, otherwise packages whose
- signature matches the signatures of config_locationProviderPackageNames
- will be searched, and the service with the highest version number will
- be picked. Anyone who wants to disable the overlay mechanism can set it
- to false.
- -->
+ <!-- Whether to enable network location overlay which allows network location provider to be
+ replaced by an app at run-time. When disabled, only the
+ config_networkLocationProviderPackageName package will be searched for network location
+ provider, otherwise any system package is eligible. Anyone who wants to disable the overlay
+ mechanism can set it to false. -->
<bool name="config_enableNetworkLocationOverlay" translatable="false">true</bool>
<!-- Package name providing network location support. Used only when
config_enableNetworkLocationOverlay is false. -->
<string name="config_networkLocationProviderPackageName" translatable="false">@null</string>
- <!-- Whether to enable fused location provider overlay which allows fused
- location provider to be replaced by an app at run-time. When disabled,
- only the config_fusedLocationProviderPackageName package will be
- searched for fused location provider, otherwise packages whose
- signature matches the signatures of config_locationProviderPackageNames
- will be searched, and the service with the highest version number will
- be picked. Anyone who wants to disable the overlay mechanism can set it
- to false.
- -->
+ <!-- Whether to enable fused location provider overlay which allows fused location provider to
+ be replaced by an app at run-time. When disabled, only the
+ config_fusedLocationProviderPackageName package will be searched for fused location
+ provider, otherwise any system package is eligible. Anyone who wants to disable the overlay
+ mechanism can set it to false. -->
<bool name="config_enableFusedLocationOverlay" translatable="false">true</bool>
<!-- Package name providing fused location support. Used only when
config_enableFusedLocationOverlay is false. -->
@@ -1669,25 +1661,10 @@
-->
<string name="config_defaultNetworkRecommendationProviderPackage" translatable="false"></string>
- <!-- Whether to enable Hardware FLP overlay which allows Hardware FLP to be
- replaced by an app at run-time. When disabled, only the
- config_hardwareFlpPackageName package will be searched for Hardware Flp,
- otherwise packages whose signature matches the signatures of
- config_locationProviderPackageNames will be searched, and the service
- with the highest version number will be picked. Anyone who wants to
- disable the overlay mechanism can set it to false.
- -->
- <bool name="config_enableHardwareFlpOverlay" translatable="false">true</bool>
- <!-- Package name providing Hardware Flp. Used only when
- config_enableHardwareFlpOverlay is false. -->
- <string name="config_hardwareFlpPackageName" translatable="false">com.android.location.fused</string>
-
<!-- Whether to enable geocoder overlay which allows geocoder to be replaced
by an app at run-time. When disabled, only the
config_geocoderProviderPackageName package will be searched for
- geocoder, otherwise packages whose signature matches the signatures of
- config_locationProviderPackageNames will be searched, and the service
- with the highest version number will be picked. Anyone who wants to
+ geocoder, otherwise any system package is eligible. Anyone who wants to
disable the overlay mechanism can set it to false.
-->
<bool name="config_enableGeocoderOverlay" translatable="false">true</bool>
@@ -1698,9 +1675,7 @@
<!-- Whether to enable geofence overlay which allows geofence to be replaced
by an app at run-time. When disabled, only the
config_geofenceProviderPackageName package will be searched for
- geofence implementation, otherwise packages whose signature matches the
- signatures of config_locationProviderPackageNames will be searched, and
- the service with the highest version number will be picked. Anyone who
+ geofence implementation, otherwise any system package is eligible. Anyone who
wants to disable the overlay mechanism can set it to false.
-->
<bool name="config_enableGeofenceOverlay" translatable="false">true</bool>
@@ -1711,9 +1686,7 @@
<!-- Whether to enable Hardware Activity-Recognition overlay which allows Hardware
Activity-Recognition to be replaced by an app at run-time. When disabled, only the
config_activityRecognitionHardwarePackageName package will be searched for
- its implementation, otherwise packages whose signature matches the
- signatures of config_locationProviderPackageNames will be searched, and
- the service with the highest version number will be picked. Anyone who
+ its implementation, otherwise any system package is eligible. Anyone who
wants to disable the overlay mechanism can set it to false.
-->
<bool name="config_enableActivityRecognitionHardwareOverlay" translatable="false">true</bool>
@@ -1721,19 +1694,8 @@
config_enableActivityRecognitionHardwareOverlay is false. -->
<string name="config_activityRecognitionHardwarePackageName" translatable="false">@null</string>
- <!-- Package name(s) containing location provider support.
- These packages can contain services implementing location providers,
- such as the Geocode Provider, Network Location Provider, and
- Fused Location Provider. They will each be searched for
- service components implementing these providers.
- It is strongly recommended that the packages explicitly named
- below are on the system image, so that they will not map to
- a 3rd party application.
- The location framework also has support for installation
- of new location providers at run-time. The new package does not
- have to be explicitly listed here, however it must have a signature
- that matches the signature of at least one package on this list.
- -->
+ <!-- Package name(s) containing location provider support. These packages will be auto-granted
+ several permissions by the system, and should be system packages. -->
<string-array name="config_locationProviderPackageNames" translatable="false">
<!-- The standard AOSP fused location provider -->
<item>com.android.location.fused</item>
@@ -2723,6 +2685,11 @@
<!-- The amount to scale fullscreen snapshots for Overview and snapshot starting windows. -->
<item name="config_fullTaskSnapshotScale" format="float" type="dimen">1.0</item>
+ <!-- The amount to scale reduced scale snapshots for Overview and snapshot starting windows.
+ Reduced scale snapshots are loaded before full screen snapshots to improve load times and
+ minimize the chance the user will see an empty task card. -->
+ <item name="config_reducedTaskSnapshotScale" format="float" type="dimen">0.5</item>
+
<!-- Feature flag to store TaskSnapshot in 16 bit pixel format to save memory. -->
<bool name="config_use16BitTaskSnapshotPixelFormat">false</bool>
@@ -4326,6 +4293,13 @@
generation). -->
<bool name="config_customBugreport">false</bool>
+ <!-- Names of packages that should not be suspended when personal use is blocked by policy. -->
+ <string-array name="config_packagesExemptFromSuspension" translatable="false">
+ <!-- Add packages here, example: -->
+ <!-- <item>com.android.settings</item> -->
+ </string-array>
+
+
<!-- Class name of the custom country detector to be used. -->
<string name="config_customCountryDetector" translatable="false">com.android.server.location.ComprehensiveCountryDetector</string>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 36dbcbd..d43c9fd 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3012,12 +3012,18 @@
<public name="sdkVersion" />
<!-- @hide @SystemApi -->
<public name="minExtensionVersion" />
+ <public name="autofillInlineSuggestionChip" />
+ <public name="autofillInlineSuggestionTitle" />
+ <public name="autofillInlineSuggestionSubtitle" />
+ <!-- @hide @SystemApi -->
+ <public name="isAutofillInlineSuggestionTheme" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
</public-group>
<public-group type="style" first-id="0x010302e5">
+ <public name="Theme.AutofillInlineSuggestion" />
</public-group>
<public-group type="id" first-id="0x0102004a">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6a00ecb..be2b678 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -425,6 +425,12 @@
<!-- A toast message displayed when printing is attempted but disabled by policy. -->
<string name="printing_disabled_by">Printing disabled by <xliff:g id="owner_app">%s</xliff:g>.</string>
+ <!-- Content title for a notification that personal apps are suspended [CHAR LIMIT=NONE] -->
+ <string name="personal_apps_suspended_notification_title">Personal apps have been suspended by an admin</string>
+
+ <!-- Message for a notification about personal apps suspension when work profile is off. [CHAR LIMIT=NONE] -->
+ <string name="personal_apps_suspended_notification_text">Tap here to check policy compliance.</string>
+
<!-- Display name for any time a piece of data refers to the owner of the phone. For example, this could be used in place of the phone's phone number. -->
<string name="me">Me</string>
@@ -5315,4 +5321,12 @@
<!-- Accessibility description of caption view -->
<string name="accessibility_freeform_caption">Caption bar of <xliff:g id="app_name">%1$s</xliff:g>.</string>
+
+ <!-- Text to tell the user that a package has been forced by themselves in the RESTRICTED bucket. [CHAR LIMIT=NONE] -->
+ <string name="as_app_forced_to_restricted_bucket">
+ <xliff:g id="package_name" example="com.android.example">%1$s</xliff:g> has been put into the RESTRICTED bucket</string>
+
+ <!-- ResolverActivity - profile tabs -->
+ <string name="resolver_personal_tab">Personal</string>
+ <string name="resolver_work_tab">Work</string>
</resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index bcce1f0..751eca0 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1482,6 +1482,22 @@
<item name="android:windowExitAnimation">@anim/slide_out_down</item>
</style>
+ <!-- The style for the Autofill inline suggestion chip. -->
+ <!-- @hide -->
+ <style name="AutofillInlineSuggestionChip">
+ <item name="background">@drawable/autofill_dataset_picker_background</item>
+ </style>
+
+ <!-- @hide -->
+ <style name="AutofillInlineSuggestionTitle">
+ <item name="android:textAppearance">@style/TextAppearance</item>
+ </style>
+
+ <!-- @hide -->
+ <style name="AutofillInlineSuggestionSubtitle">
+ <item name="android:textAppearance">@style/TextAppearance.Small</item>
+ </style>
+
<!-- The style for the container of media actions in a notification. -->
<!-- @hide -->
<style name="NotificationMediaActionContainer">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7e6eb5d..d437318 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -249,6 +249,9 @@
<java-symbol type="id" name="app_ops" />
<java-symbol type="id" name="profile_pager" />
<java-symbol type="id" name="content_preview_container" />
+ <java-symbol type="id" name="profile_tabhost" />
+ <java-symbol type="id" name="tabs" />
+ <java-symbol type="id" name="tabcontent" />
<java-symbol type="attr" name="actionModeShareDrawable" />
<java-symbol type="attr" name="alertDialogCenterButtons" />
@@ -357,6 +360,7 @@
<java-symbol type="bool" name="config_enableNewAutoSelectNetworkUI"/>
<java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
<java-symbol type="dimen" name="config_fullTaskSnapshotScale" />
+ <java-symbol type="dimen" name="config_reducedTaskSnapshotScale" />
<java-symbol type="bool" name="config_use16BitTaskSnapshotPixelFormat" />
<java-symbol type="bool" name="config_hasRecents" />
<java-symbol type="string" name="config_recentsComponentName" />
@@ -1209,6 +1213,8 @@
<java-symbol type="string" name="device_ownership_relinquished" />
<java-symbol type="string" name="network_logging_notification_title" />
<java-symbol type="string" name="network_logging_notification_text" />
+ <java-symbol type="string" name="personal_apps_suspended_notification_title" />
+ <java-symbol type="string" name="personal_apps_suspended_notification_text" />
<java-symbol type="string" name="factory_reset_warning" />
<java-symbol type="string" name="factory_reset_message" />
<java-symbol type="string" name="lockscreen_transport_play_description" />
@@ -1869,7 +1875,6 @@
<java-symbol type="bool" name="config_dozeAfterScreenOffByDefault" />
<java-symbol type="bool" name="config_enableActivityRecognitionHardwareOverlay" />
<java-symbol type="bool" name="config_enableFusedLocationOverlay" />
- <java-symbol type="bool" name="config_enableHardwareFlpOverlay" />
<java-symbol type="bool" name="config_enableGeocoderOverlay" />
<java-symbol type="bool" name="config_enableGeofenceOverlay" />
<java-symbol type="bool" name="config_enableNetworkLocationOverlay" />
@@ -2019,7 +2024,6 @@
<java-symbol type="string" name="config_datause_iface" />
<java-symbol type="string" name="config_activityRecognitionHardwarePackageName" />
<java-symbol type="string" name="config_fusedLocationProviderPackageName" />
- <java-symbol type="string" name="config_hardwareFlpPackageName" />
<java-symbol type="string" name="config_geocoderProviderPackageName" />
<java-symbol type="string" name="config_geofenceProviderPackageName" />
<java-symbol type="string" name="config_networkLocationProviderPackageName" />
@@ -3810,6 +3814,9 @@
<java-symbol type="string" name="config_rawContactsLocalAccountName" />
<java-symbol type="string" name="config_rawContactsLocalAccountType" />
+ <!-- For App Standby -->
+ <java-symbol type="string" name="as_app_forced_to_restricted_bucket" />
+
<!-- Assistant handles -->
<java-symbol type="dimen" name="assist_handle_shadow_radius" />
@@ -3819,6 +3826,9 @@
<java-symbol type="dimen" name="waterfall_display_right_edge_size" />
<java-symbol type="dimen" name="waterfall_display_bottom_edge_size" />
+ <!-- For device policy -->
+ <java-symbol type="array" name="config_packagesExemptFromSuspension" />
+
<!-- Accessibility take screenshot -->
<java-symbol type="string" name="capability_desc_canTakeScreenshot" />
<java-symbol type="string" name="capability_title_canTakeScreenshot" />
@@ -3830,4 +3840,12 @@
<java-symbol type="array" name="config_defaultImperceptibleKillingExemptionPkgs" />
<java-symbol type="array" name="config_defaultImperceptibleKillingExemptionProcStates" />
+
+ <!-- Intent resolver and share sheet -->
+ <java-symbol type="color" name="resolver_tabs_active_color" />
+ <java-symbol type="color" name="resolver_tabs_inactive_color" />
+ <java-symbol type="string" name="resolver_personal_tab" />
+ <java-symbol type="string" name="resolver_work_tab" />
+ <java-symbol type="id" name="stub" />
+
</resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index ad38f3d..5e6dd82 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -893,4 +893,11 @@
<item name="windowActivityTransitions">false</item>
</style>
+ <!-- Theme for the Autofill inline suggestion on IME -->
+ <style name="Theme.AutofillInlineSuggestion" parent="Theme.DeviceDefault">
+ <item name="isAutofillInlineSuggestionTheme">true</item>
+ <item name="autofillInlineSuggestionChip">@style/AutofillInlineSuggestionChip</item>
+ <item name="autofillInlineSuggestionTitle">@style/AutofillInlineSuggestionTitle</item>
+ <item name="autofillInlineSuggestionSubtitle">@style/AutofillInlineSuggestionSubtitle</item>
+ </style>
</resources>
diff --git a/core/tests/coretests/src/android/app/appsearch/SnippetTest.java b/core/tests/coretests/src/android/app/appsearch/SnippetTest.java
new file mode 100644
index 0000000..3103708
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/SnippetTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+
+import com.google.android.icing.proto.DocumentProto;
+import com.google.android.icing.proto.PropertyProto;
+import com.google.android.icing.proto.SearchResultProto;
+import com.google.android.icing.proto.SnippetMatchProto;
+import com.google.android.icing.proto.SnippetProto;
+
+import org.junit.Test;
+
+@SmallTest
+public class SnippetTest {
+
+ // TODO(sidchhabra): Add tests for Double and Long Snippets.
+ @Test
+ public void testSingleStringSnippet() {
+
+ final String propertyKeyString = "content";
+ final String propertyValueString = "A commonly used fake word is foo.\n"
+ + " Another nonsense word that’s used a lot\n"
+ + " is bar.\n";
+ final String uri = "uri1";
+ final String schemaType = "schema1";
+ final String searchWord = "foo";
+ final String exactMatch = "foo";
+ final String window = "is foo";
+
+ // Building the SearchResult received from query.
+ PropertyProto property = PropertyProto.newBuilder()
+ .setName(propertyKeyString)
+ .addStringValues(propertyValueString)
+ .build();
+ DocumentProto documentProto = DocumentProto.newBuilder()
+ .setUri(uri)
+ .setSchema(schemaType)
+ .addProperties(property)
+ .build();
+ SnippetProto snippetProto = SnippetProto.newBuilder()
+ .addEntries(SnippetProto.EntryProto.newBuilder()
+ .setPropertyName(propertyKeyString)
+ .addSnippetMatches(SnippetMatchProto.newBuilder()
+ .setValuesIndex(0)
+ .setExactMatchPosition(29)
+ .setExactMatchBytes(3)
+ .setWindowPosition(26)
+ .setWindowBytes(6)
+ .build())
+ .build())
+ .build();
+ SearchResultProto.ResultProto resultProto = SearchResultProto.ResultProto.newBuilder()
+ .setDocument(documentProto)
+ .setSnippet(snippetProto)
+ .build();
+ SearchResultProto searchResultProto = SearchResultProto.newBuilder()
+ .addResults(resultProto)
+ .build();
+ SearchResults searchResults = new SearchResults(searchResultProto);
+
+ // Making ResultReader and getting Snippet values.
+ while (searchResults.hasNext()) {
+ SearchResults.Result result = searchResults.next();
+ MatchInfo match = result.getMatchInfo().get(0);
+ assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString);
+ assertThat(match.getFullText()).isEqualTo(propertyValueString);
+ assertThat(match.getExactMatch()).isEqualTo(exactMatch);
+ assertThat(match.getSnippet()).isEqualTo(window);
+ }
+ }
+
+ // TODO(sidchhabra): Add tests for Double and Long Snippets.
+ @Test
+ public void testNoSnippets() {
+
+ final String propertyKeyString = "content";
+ final String propertyValueString = "A commonly used fake word is foo.\n"
+ + " Another nonsense word that’s used a lot\n"
+ + " is bar.\n";
+ final String uri = "uri1";
+ final String schemaType = "schema1";
+ final String searchWord = "foo";
+ final String exactMatch = "foo";
+ final String window = "is foo";
+
+ // Building the SearchResult received from query.
+ PropertyProto property = PropertyProto.newBuilder()
+ .setName(propertyKeyString)
+ .addStringValues(propertyValueString)
+ .build();
+ DocumentProto documentProto = DocumentProto.newBuilder()
+ .setUri(uri)
+ .setSchema(schemaType)
+ .addProperties(property)
+ .build();
+ SearchResultProto.ResultProto resultProto = SearchResultProto.ResultProto.newBuilder()
+ .setDocument(documentProto)
+ .build();
+ SearchResultProto searchResultProto = SearchResultProto.newBuilder()
+ .addResults(resultProto)
+ .build();
+ SearchResults searchResults = new SearchResults(searchResultProto);
+
+ while (searchResults.hasNext()) {
+ SearchResults.Result result = searchResults.next();
+ assertThat(result.getMatchInfo()).isEqualTo(null);
+ }
+ }
+
+ @Test
+ public void testMultipleStringSnippet() {
+ final String searchWord = "Test";
+
+ // Building the SearchResult received from query.
+ PropertyProto property1 = PropertyProto.newBuilder()
+ .setName("sender.name")
+ .addStringValues("Test Name Jr.")
+ .build();
+ PropertyProto property2 = PropertyProto.newBuilder()
+ .setName("sender.email")
+ .addStringValues("TestNameJr@gmail.com")
+ .build();
+ DocumentProto documentProto = DocumentProto.newBuilder()
+ .setUri("uri1")
+ .setSchema("schema1")
+ .addProperties(property1)
+ .addProperties(property2)
+ .build();
+ SnippetProto snippetProto = SnippetProto.newBuilder()
+ .addEntries(
+ SnippetProto.EntryProto.newBuilder()
+ .setPropertyName("sender.name")
+ .addSnippetMatches(
+ SnippetMatchProto.newBuilder()
+ .setValuesIndex(0)
+ .setExactMatchPosition(0)
+ .setExactMatchBytes(4)
+ .setWindowPosition(0)
+ .setWindowBytes(9)
+ .build())
+ .build())
+ .addEntries(
+ SnippetProto.EntryProto.newBuilder()
+ .setPropertyName("sender.email")
+ .addSnippetMatches(
+ SnippetMatchProto.newBuilder()
+ .setValuesIndex(0)
+ .setExactMatchPosition(0)
+ .setExactMatchBytes(20)
+ .setWindowPosition(0)
+ .setWindowBytes(20)
+ .build())
+ .build()
+ )
+ .build();
+ SearchResultProto.ResultProto resultProto = SearchResultProto.ResultProto.newBuilder()
+ .setDocument(documentProto)
+ .setSnippet(snippetProto)
+ .build();
+ SearchResultProto searchResultProto = SearchResultProto.newBuilder()
+ .addResults(resultProto)
+ .build();
+ SearchResults searchResults = new SearchResults(searchResultProto);
+
+ // Making ResultReader and getting Snippet values.
+ while (searchResults.hasNext()) {
+ SearchResults.Result result = searchResults.next();
+
+ MatchInfo match1 = result.getMatchInfo().get(0);
+ assertThat(match1.getPropertyPath()).isEqualTo("sender.name");
+ assertThat(match1.getFullText()).isEqualTo("Test Name Jr.");
+ assertThat(match1.getExactMatch()).isEqualTo("Test");
+ assertThat(match1.getSnippet()).isEqualTo("Test Name");
+
+ MatchInfo match2 = result.getMatchInfo().get(1);
+ assertThat(match2.getPropertyPath()).isEqualTo("sender.email");
+ assertThat(match2.getFullText()).isEqualTo("TestNameJr@gmail.com");
+ assertThat(match2.getExactMatch()).isEqualTo("TestNameJr@gmail.com");
+ assertThat(match2.getSnippet()).isEqualTo("TestNameJr@gmail.com");
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index f2852fa..bcf0b8c 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -41,6 +41,7 @@
import android.util.SparseArray;
import android.view.SurfaceControl.Transaction;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
+import android.view.animation.LinearInterpolator;
import android.view.test.InsetsModeSession;
import androidx.test.runner.AndroidJUnit4;
@@ -121,7 +122,7 @@
controls.put(ITYPE_NAVIGATION_BAR, navConsumer.getControl());
mController = new InsetsAnimationControlImpl(controls,
new Rect(0, 0, 500, 500), mInsetsState, mMockListener, systemBars(),
- mMockController, 10 /* durationMs */,
+ mMockController, 10 /* durationMs */, new LinearInterpolator(),
false /* fade */, LAYOUT_INSETS_DURING_ANIMATION_SHOWN);
}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 8381903..f720c98 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -44,6 +44,7 @@
import android.view.WindowInsets.Type;
import android.view.WindowManager.BadTokenException;
import android.view.WindowManager.LayoutParams;
+import android.view.animation.LinearInterpolator;
import android.view.test.InsetsModeSession;
import android.widget.TextView;
@@ -148,7 +149,7 @@
WindowInsetsAnimationControlListener mockListener =
mock(WindowInsetsAnimationControlListener.class);
mController.controlWindowInsetsAnimation(statusBars(), 10 /* durationMs */,
- mockListener);
+ new LinearInterpolator(), mockListener);
// Ready gets deferred until next predraw
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -164,7 +165,8 @@
mController.getState().setDisplayFrame(new Rect(0, 0, 200, 200));
WindowInsetsAnimationControlListener controlListener =
mock(WindowInsetsAnimationControlListener.class);
- mController.controlWindowInsetsAnimation(0, 0 /* durationMs */, controlListener);
+ mController.controlWindowInsetsAnimation(0, 0 /* durationMs */, new LinearInterpolator(),
+ controlListener);
verify(controlListener).onCancelled();
verify(controlListener, never()).onReady(any(), anyInt());
}
@@ -375,7 +377,7 @@
WindowInsetsAnimationControlListener mockListener =
mock(WindowInsetsAnimationControlListener.class);
mController.controlWindowInsetsAnimation(statusBars(), 0 /* durationMs */,
- mockListener);
+ new LinearInterpolator(), mockListener);
ArgumentCaptor<WindowInsetsAnimationController> controllerCaptor =
ArgumentCaptor.forClass(WindowInsetsAnimationController.class);
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 9e4b1c5..20be8c2 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -94,7 +94,7 @@
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_RESIZE, 0, null);
assertEquals(100, insets.getStableInsetBottom());
- assertEquals(Insets.of(0, 0, 0, 100), insets.getMaxInsets(Type.systemBars()));
+ assertEquals(Insets.of(0, 0, 0, 100), insets.getInsetsIgnoringVisibility(Type.systemBars()));
assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 0, 0, 200), insets.getInsets(Type.all()));
assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(Type.navigationBars()));
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index cf5d079..df6ed8c 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -109,7 +109,7 @@
attrs.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
ViewRootImpl.adjustLayoutParamsForCompatibility(attrs);
- assertEquals(0, attrs.getFitWindowInsetsTypes() & Type.statusBars());
+ assertEquals(0, attrs.getFitInsetsTypes() & Type.statusBars());
}
@Test
@@ -120,7 +120,7 @@
attrs.flags = FLAG_LAYOUT_IN_SCREEN;
ViewRootImpl.adjustLayoutParamsForCompatibility(attrs);
- assertEquals(0, attrs.getFitWindowInsetsTypes() & Type.statusBars());
+ assertEquals(0, attrs.getFitInsetsTypes() & Type.statusBars());
}
@Test
@@ -131,7 +131,7 @@
attrs.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
ViewRootImpl.adjustLayoutParamsForCompatibility(attrs);
- assertEquals(0, attrs.getFitWindowInsetsTypes() & Type.systemBars());
+ assertEquals(0, attrs.getFitInsetsTypes() & Type.systemBars());
}
@Test
@@ -141,8 +141,8 @@
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_TOAST);
ViewRootImpl.adjustLayoutParamsForCompatibility(attrs);
- assertEquals(Type.systemBars(), attrs.getFitWindowInsetsTypes() & Type.systemBars());
- assertEquals(true, attrs.getFitIgnoreVisibility());
+ assertEquals(Type.systemBars(), attrs.getFitInsetsTypes() & Type.systemBars());
+ assertEquals(true, attrs.isFitInsetsIgnoringVisibility());
}
@Test
@@ -152,8 +152,8 @@
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_SYSTEM_ALERT);
ViewRootImpl.adjustLayoutParamsForCompatibility(attrs);
- assertEquals(Type.systemBars(), attrs.getFitWindowInsetsTypes() & Type.systemBars());
- assertEquals(true, attrs.getFitIgnoreVisibility());
+ assertEquals(Type.systemBars(), attrs.getFitInsetsTypes() & Type.systemBars());
+ assertEquals(true, attrs.isFitInsetsIgnoringVisibility());
}
@Test
@@ -165,14 +165,14 @@
final int sides = Side.TOP | Side.LEFT;
final boolean fitMaxInsets = true;
attrs.flags = FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- attrs.setFitWindowInsetsTypes(types);
- attrs.setFitWindowInsetsSides(sides);
- attrs.setFitIgnoreVisibility(fitMaxInsets);
+ attrs.setFitInsetsTypes(types);
+ attrs.setFitInsetsSides(sides);
+ attrs.setFitInsetsIgnoringVisibility(fitMaxInsets);
ViewRootImpl.adjustLayoutParamsForCompatibility(attrs);
- assertEquals(types, attrs.getFitWindowInsetsTypes());
- assertEquals(sides, attrs.getFitWindowInsetsSides());
- assertEquals(fitMaxInsets, attrs.getFitIgnoreVisibility());
+ assertEquals(types, attrs.getFitInsetsTypes());
+ assertEquals(sides, attrs.getFitInsetsSides());
+ assertEquals(fitMaxInsets, attrs.isFitInsetsIgnoringVisibility());
}
private static class ViewRootImplAccessor {
diff --git a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
index 68099fe..12c057f 100644
--- a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
@@ -18,9 +18,15 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.UserHandle;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -71,4 +77,227 @@
}
}
}
+
+ @Test
+ public void testNullTextInputComposeInitialSurroundingText() {
+ final Spannable testText = null;
+ final EditorInfo editorInfo = new EditorInfo();
+
+ try {
+ editorInfo.setInitialSurroundingText(testText);
+ fail("Shall not take null input");
+ } catch (NullPointerException expected) {
+ // Expected behavior, nothing to do.
+ }
+ }
+
+ @Test
+ public void testNonNullTextInputComposeInitialSurroundingText() {
+ final Spannable testText = createTestText(/* prependLength= */ 0,
+ EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH);
+ final EditorInfo editorInfo = new EditorInfo();
+
+ // Cursor at position 0.
+ int selectionLength = 0;
+ editorInfo.initialSelStart = 0;
+ editorInfo.initialSelEnd = editorInfo.initialSelStart + selectionLength;
+ int expectedTextBeforeCursorLength = 0;
+ int expectedTextAfterCursorLength = testText.length();
+
+ editorInfo.setInitialSurroundingText(testText);
+
+ assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength,
+ expectedTextAfterCursorLength);
+
+ // Cursor at the end.
+ editorInfo.initialSelStart = testText.length() - selectionLength;
+ editorInfo.initialSelEnd = testText.length();
+ expectedTextBeforeCursorLength = testText.length();
+ expectedTextAfterCursorLength = 0;
+
+ editorInfo.setInitialSurroundingText(testText);
+
+ assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength,
+ expectedTextAfterCursorLength);
+
+ // Cursor at the middle.
+ selectionLength = 2;
+ editorInfo.initialSelStart = testText.length() / 2;
+ editorInfo.initialSelEnd = editorInfo.initialSelStart + selectionLength;
+ expectedTextBeforeCursorLength = editorInfo.initialSelStart;
+ expectedTextAfterCursorLength = testText.length() - editorInfo.initialSelEnd;
+
+ editorInfo.setInitialSurroundingText(testText);
+
+ assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength,
+ expectedTextAfterCursorLength);
+
+ // Accidentally swap selection start and end.
+ editorInfo.initialSelEnd = testText.length() / 2;
+ editorInfo.initialSelStart = editorInfo.initialSelEnd + selectionLength;
+
+ editorInfo.setInitialSurroundingText(testText);
+
+ assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength,
+ expectedTextAfterCursorLength);
+
+ // Invalid cursor position.
+ editorInfo.initialSelStart = -1;
+
+ editorInfo.setInitialSurroundingText(testText);
+
+ assertExpectedTextLength(editorInfo,
+ /* expectBeforeCursorLength= */null,
+ /* expectSelectionLength= */null,
+ /* expectAfterCursorLength= */null);
+ }
+
+ @Test
+ public void testTooLongTextInputComposeInitialSurroundingText() {
+ final Spannable testText = createTestText(/* prependLength= */ 0,
+ EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH + 2);
+ final EditorInfo editorInfo = new EditorInfo();
+
+ // Cursor at position 0.
+ int selectionLength = 0;
+ editorInfo.initialSelStart = 0;
+ editorInfo.initialSelEnd = 0 + selectionLength;
+ int expectedTextBeforeCursorLength = 0;
+ int expectedTextAfterCursorLength = editorInfo.MEMORY_EFFICIENT_TEXT_LENGTH;
+
+ editorInfo.setInitialSurroundingText(testText);
+
+ assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength,
+ expectedTextAfterCursorLength);
+
+ // Cursor at the end.
+ editorInfo.initialSelStart = testText.length() - selectionLength;
+ editorInfo.initialSelEnd = testText.length();
+ expectedTextBeforeCursorLength = editorInfo.MEMORY_EFFICIENT_TEXT_LENGTH;
+ expectedTextAfterCursorLength = 0;
+
+ editorInfo.setInitialSurroundingText(testText);
+
+ assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength,
+ expectedTextAfterCursorLength);
+
+ // Cursor at the middle.
+ selectionLength = 2;
+ editorInfo.initialSelStart = testText.length() / 2;
+ editorInfo.initialSelEnd = editorInfo.initialSelStart + selectionLength;
+ expectedTextBeforeCursorLength = Math.min(editorInfo.initialSelStart,
+ (int) (0.8 * (EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH - selectionLength)));
+ expectedTextAfterCursorLength = EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH
+ - expectedTextBeforeCursorLength - selectionLength;
+
+ editorInfo.setInitialSurroundingText(testText);
+
+ assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength,
+ expectedTextAfterCursorLength);
+
+ // Accidentally swap selection start and end.
+ editorInfo.initialSelEnd = testText.length() / 2;
+ editorInfo.initialSelStart = editorInfo.initialSelEnd + selectionLength;
+
+ editorInfo.setInitialSurroundingText(testText);
+
+ assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength,
+ expectedTextAfterCursorLength);
+
+ // Selection too long, selected text should be dropped.
+ selectionLength = EditorInfo.MAX_INITIAL_SELECTION_LENGTH + 1;
+ editorInfo.initialSelStart = testText.length() / 2;
+ editorInfo.initialSelEnd = editorInfo.initialSelStart + selectionLength;
+ expectedTextBeforeCursorLength = Math.min(editorInfo.initialSelStart,
+ (int) (0.8 * EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH));
+ expectedTextAfterCursorLength = testText.length() - editorInfo.initialSelEnd;
+
+ editorInfo.setInitialSurroundingText(testText);
+
+ assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength,
+ /* expectSelectionLength= */null, expectedTextAfterCursorLength);
+ }
+
+ @Test
+ public void testTooLongSubTextInputComposeInitialSurroundingText() {
+ final int prependLength = 5;
+ final int subTextLength = EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH;
+ final Spannable fullText = createTestText(prependLength, subTextLength);
+ final EditorInfo editorInfo = new EditorInfo();
+ // Cursor at the middle.
+ final int selectionLength = 2;
+ editorInfo.initialSelStart = fullText.length() / 2;
+ editorInfo.initialSelEnd = editorInfo.initialSelStart + selectionLength;
+ // #prependLength characters will be trimmed out.
+ final Spannable expectedTextBeforeCursor = createExpectedText(/* startNumber= */0,
+ editorInfo.initialSelStart - prependLength);
+ final Spannable expectedSelectedText = createExpectedText(
+ editorInfo.initialSelStart - prependLength, selectionLength);
+ final Spannable expectedTextAfterCursor = createExpectedText(
+ editorInfo.initialSelEnd - prependLength,
+ fullText.length() - editorInfo.initialSelEnd);
+
+ editorInfo.setInitialSurroundingSubText(fullText.subSequence(prependLength,
+ fullText.length()), prependLength);
+
+ assertTrue(TextUtils.equals(expectedTextBeforeCursor,
+ editorInfo.getInitialTextBeforeCursor(editorInfo.MEMORY_EFFICIENT_TEXT_LENGTH,
+ InputConnection.GET_TEXT_WITH_STYLES)));
+ assertTrue(TextUtils.equals(expectedSelectedText,
+ editorInfo.getInitialSelectedText(InputConnection.GET_TEXT_WITH_STYLES)));
+ assertTrue(TextUtils.equals(expectedTextAfterCursor,
+ editorInfo.getInitialTextAfterCursor(editorInfo.MEMORY_EFFICIENT_TEXT_LENGTH,
+ InputConnection.GET_TEXT_WITH_STYLES)));
+ }
+
+ private static void assertExpectedTextLength(EditorInfo editorInfo,
+ @Nullable Integer expectBeforeCursorLength, @Nullable Integer expectSelectionLength,
+ @Nullable Integer expectAfterCursorLength) {
+ final CharSequence textBeforeCursor =
+ editorInfo.getInitialTextBeforeCursor(editorInfo.MEMORY_EFFICIENT_TEXT_LENGTH,
+ InputConnection.GET_TEXT_WITH_STYLES);
+ final CharSequence selectedText =
+ editorInfo.getInitialSelectedText(InputConnection.GET_TEXT_WITH_STYLES);
+ final CharSequence textAfterCursor =
+ editorInfo.getInitialTextAfterCursor(editorInfo.MEMORY_EFFICIENT_TEXT_LENGTH,
+ InputConnection.GET_TEXT_WITH_STYLES);
+
+ if (expectBeforeCursorLength == null) {
+ assertNull(textBeforeCursor);
+ } else {
+ assertEquals(expectBeforeCursorLength.intValue(), textBeforeCursor.length());
+ }
+
+ if (expectSelectionLength == null) {
+ assertNull(selectedText);
+ } else {
+ assertEquals(expectSelectionLength.intValue(), selectedText.length());
+ }
+
+ if (expectAfterCursorLength == null) {
+ assertNull(textAfterCursor);
+ } else {
+ assertEquals(expectAfterCursorLength.intValue(), textAfterCursor.length());
+ }
+ }
+
+ private static Spannable createTestText(int prependLength, int surroundingLength) {
+ final SpannableStringBuilder builder = new SpannableStringBuilder();
+ for (int i = 0; i < prependLength; i++) {
+ builder.append("a");
+ }
+
+ for (int i = 0; i < surroundingLength; i++) {
+ builder.append(Integer.toString(i % 10));
+ }
+ return builder;
+ }
+
+ private static Spannable createExpectedText(int startNumber, int length) {
+ final SpannableStringBuilder builder = new SpannableStringBuilder();
+ for (int i = startNumber; i < startNumber + length; i++) {
+ builder.append(Integer.toString(i % 10));
+ }
+ return builder;
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index c086421..411868d 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -31,6 +31,7 @@
import static com.android.internal.app.ChooserListAdapter.CALLER_TARGET_SCORE_BOOST;
import static com.android.internal.app.ChooserListAdapter.SHORTCUT_TARGET_SCORE_BOOST;
import static com.android.internal.app.ChooserWrapperActivity.sOverrides;
+import static com.android.internal.app.MatcherUtils.first;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
@@ -63,6 +64,8 @@
import android.graphics.drawable.Icon;
import android.metrics.LogMaker;
import android.net.Uri;
+import android.os.Bundle;
+import android.os.UserHandle;
import android.service.chooser.ChooserTarget;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -74,7 +77,11 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -302,6 +309,7 @@
assertThat(activity.getIsSelected(), is(true));
}
+ @Ignore // b/148158199
@Test
public void noResultsFromPackageManager() {
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
@@ -346,6 +354,9 @@
@Test
public void hasOtherProfileOneOption() throws Exception {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(2);
@@ -372,9 +383,7 @@
// Make a stable copy of the components as the original list may be modified
List<ResolvedComponentInfo> stableCopy =
createResolvedComponentsForTestWithOtherProfile(2);
- // Check that the "Other Profile" activity is put in the right spot
- onView(withId(R.id.profile_button)).check(matches(
- withText(stableCopy.get(0).getResolveInfoAt(0).activityInfo.name)));
+ waitForIdle();
onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
.perform(click());
waitForIdle();
@@ -383,6 +392,9 @@
@Test
public void hasOtherProfileTwoOptionsAndUserSelectsOne() throws Exception {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(3);
@@ -411,9 +423,6 @@
// Make a stable copy of the components as the original list may be modified
List<ResolvedComponentInfo> stableCopy =
createResolvedComponentsForTestWithOtherProfile(3);
- // Check that the "Other Profile" activity is put in the right spot
- onView(withId(R.id.profile_button)).check(matches(
- withText(stableCopy.get(0).getResolveInfoAt(0).activityInfo.name)));
onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
.perform(click());
waitForIdle();
@@ -422,6 +431,9 @@
@Test
public void hasLastChosenActivityAndOtherProfile() throws Exception {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(3);
@@ -448,9 +460,6 @@
// Make a stable copy of the components as the original list may be modified
List<ResolvedComponentInfo> stableCopy =
createResolvedComponentsForTestWithOtherProfile(3);
- // Check that the "Other Profile" activity is put in the right spot
- onView(withId(R.id.profile_button)).check(matches(
- withText(stableCopy.get(0).getResolveInfoAt(0).activityInfo.name)));
onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
.perform(click());
waitForIdle();
@@ -1161,6 +1170,123 @@
.getAllValues().get(2).getTaggedData(MetricsEvent.FIELD_RANKED_POSITION), is(-1));
}
+ @Test
+ public void testWorkTab_displayedWhenWorkProfileUserAvailable() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+ markWorkProfileUserAvailable();
+
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+
+ onView(withId(R.id.tabs)).check(matches(isDisplayed()));
+ }
+
+ @Test
+ public void testWorkTab_hiddenWhenWorkProfileUserNotAvailable() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+
+ onView(withId(R.id.tabs)).check(matches(not(isDisplayed())));
+ }
+
+ @Test
+ public void testWorkTab_eachTabUsesExpectedAdapter() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ int personalProfileTargets = 3;
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTest(personalProfileTargets);
+ int workProfileTargets = 4;
+ List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(
+ workProfileTargets);
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos);
+ when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+ markWorkProfileUserAvailable();
+
+ final ChooserWrapperActivity activity =
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+
+ assertThat(activity.getCurrentUserHandle().getIdentifier(), is(0));
+ // The work list adapter must only be filled when we open the work tab
+ assertThat(activity.getWorkListAdapter().getCount(), is(0));
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10));
+ assertThat(activity.getPersonalListAdapter().getCount(), is(personalProfileTargets));
+ assertThat(activity.getWorkListAdapter().getCount(), is(workProfileTargets));
+ }
+
+ @Test
+ public void testWorkTab_workProfileHasExpectedNumberOfTargets() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ int workProfileTargets = 4;
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(workProfileTargets);
+ when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+
+ final ChooserWrapperActivity activity =
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+
+ assertThat(activity.getWorkListAdapter().getCount(), is(workProfileTargets));
+ }
+
+ @Ignore // b/148156663
+ @Test
+ public void testWorkTab_selectingWorkTabAppOpensAppInWorkProfile() throws InterruptedException {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ int workProfileTargets = 4;
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(workProfileTargets);
+ when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+ Intent sendIntent = createSendTextIntent();
+ sendIntent.setType("TestType");
+ ResolveInfo[] chosen = new ResolveInfo[1];
+ sOverrides.onSafelyStartCallback = targetInfo -> {
+ chosen[0] = targetInfo.getResolveInfo();
+ return true;
+ };
+
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+ waitForIdle();
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+ waitForIdle();
+ // wait for the share sheet to expand
+ Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
+
+ onView(first(withText(workResolvedComponentInfos.get(0)
+ .getResolveInfoAt(0).activityInfo.applicationInfo.name)))
+ .perform(click());
+ waitForIdle();
+ assertThat(chosen[0], is(workResolvedComponentInfos.get(0).getResolveInfoAt(0)));
+ }
+
private Intent createSendTextIntent() {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
@@ -1224,6 +1350,15 @@
return infoList;
}
+ private List<ResolvedComponentInfo> createResolvedComponentsForTestWithUserId(
+ int numberOfResults, int userId) {
+ List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
+ for (int i = 0; i < numberOfResults; i++) {
+ infoList.add(ResolverDataProvider.createResolvedComponentInfoWithOtherId(i, userId));
+ }
+ return infoList;
+ }
+
private List<ChooserTarget> createDirectShareTargets(int numberOfResults, String packageName) {
Icon icon = Icon.createWithBitmap(createBitmap());
String testTitle = "testTitle";
@@ -1308,4 +1443,8 @@
assertEquals(cn.flattenToString(), ct.getComponentName().flattenToString());
}
}
+
+ private void markWorkProfileUserAvailable() {
+ sOverrides.workProfileUserHandle = UserHandle.of(10);
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index 2a10443..eee62bb 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -17,6 +17,7 @@
package com.android.internal.app;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import android.annotation.Nullable;
import android.app.usage.UsageStatsManager;
@@ -29,6 +30,7 @@
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
+import android.os.Bundle;
import android.os.UserHandle;
import android.util.Size;
@@ -51,6 +53,19 @@
return mChooserMultiProfilePagerAdapter.getActiveListAdapter();
}
+ ChooserListAdapter getPersonalListAdapter() {
+ return ((ChooserGridAdapter) mMultiProfilePagerAdapter.getAdapterForIndex(0))
+ .getListAdapter();
+ }
+
+ ChooserListAdapter getWorkListAdapter() {
+ if (mMultiProfilePagerAdapter.getInactiveListAdapter() == null) {
+ return null;
+ }
+ return ((ChooserGridAdapter) mMultiProfilePagerAdapter.getAdapterForIndex(1))
+ .getListAdapter();
+ }
+
boolean getIsSelected() { return mIsSuccessfullySelected; }
UsageStatsManager getUsageStatsManager() {
@@ -79,7 +94,12 @@
@Override
protected ResolverListController createListController(UserHandle userHandle) {
- return sOverrides.resolverListController;
+ if (userHandle == UserHandle.SYSTEM) {
+ when(sOverrides.resolverListController.getUserHandle()).thenReturn(UserHandle.SYSTEM);
+ return sOverrides.resolverListController;
+ }
+ when(sOverrides.workResolverListController.getUserHandle()).thenReturn(userHandle);
+ return sOverrides.workResolverListController;
}
@Override
@@ -144,6 +164,15 @@
resolveInfoPresentationGetter);
}
+ @Override
+ protected UserHandle getWorkProfileUserHandle() {
+ return sOverrides.workProfileUserHandle;
+ }
+
+ protected UserHandle getCurrentUserHandle() {
+ return mMultiProfilePagerAdapter.getCurrentUserHandle();
+ }
+
/**
* We cannot directly mock the activity created since instrumentation creates it.
* <p>
@@ -154,6 +183,7 @@
public Function<PackageManager, PackageManager> createPackageManager;
public Function<TargetInfo, Boolean> onSafelyStartCallback;
public ResolverListController resolverListController;
+ public ResolverListController workResolverListController;
public Boolean isVoiceInteraction;
public boolean isImageType;
public Cursor resolverCursor;
@@ -162,6 +192,7 @@
public MetricsLogger metricsLogger;
public int alternateProfileSetting;
public Resources resources;
+ public UserHandle workProfileUserHandle;
public void reset() {
onSafelyStartCallback = null;
@@ -172,9 +203,11 @@
resolverCursor = null;
resolverForceException = false;
resolverListController = mock(ResolverListController.class);
+ workResolverListController = mock(ResolverListController.class);
metricsLogger = mock(MetricsLogger.class);
alternateProfileSetting = 0;
resources = null;
+ workProfileUserHandle = null;
}
}
}
diff --git a/core/tests/coretests/src/com/android/internal/app/MatcherUtils.java b/core/tests/coretests/src/com/android/internal/app/MatcherUtils.java
new file mode 100644
index 0000000..a476631
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/MatcherUtils.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+
+/**
+ * Utils for helping with more customized matching options, for example matching the first
+ * occurrence of a set criteria.
+ */
+public class MatcherUtils {
+
+ /**
+ * Returns a {@link Matcher} which only matches the first occurrence of a set criteria.
+ */
+ static <T> Matcher<T> first(final Matcher<T> matcher) {
+ return new BaseMatcher<T>() {
+ boolean isFirstMatch = true;
+
+ @Override
+ public boolean matches(final Object item) {
+ if (isFirstMatch && matcher.matches(item)) {
+ isFirstMatch = false;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void describeTo(final Description description) {
+ description.appendText("Returns the first matching item");
+ }
+ };
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 923ce3e..42f7736 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -19,13 +19,17 @@
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static com.android.internal.app.MatcherUtils.first;
import static com.android.internal.app.ResolverDataProvider.createPackageManagerMockedInfo;
import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
+import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -33,6 +37,7 @@
import android.content.Intent;
import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.view.View;
import android.widget.RelativeLayout;
@@ -49,6 +54,9 @@
import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter;
import com.android.internal.widget.ResolverDrawerLayout;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
@@ -212,6 +220,9 @@
@Test
public void hasOtherProfileOneOption() throws Exception {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+
Intent sendIntent = createSendImageIntent();
List<ResolvedComponentInfo> resolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(2);
@@ -237,9 +248,6 @@
// Make a stable copy of the components as the original list may be modified
List<ResolvedComponentInfo> stableCopy =
createResolvedComponentsForTestWithOtherProfile(2);
- // Check that the "Other Profile" activity is put in the right spot
- onView(withId(R.id.profile_button)).check(matches(
- withText(stableCopy.get(0).getResolveInfoAt(0).activityInfo.name)));
onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
.perform(click());
onView(withId(R.id.button_once))
@@ -250,6 +258,9 @@
@Test
public void hasOtherProfileTwoOptionsAndUserSelectsOne() throws Exception {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+
Intent sendIntent = createSendImageIntent();
List<ResolvedComponentInfo> resolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(3);
@@ -279,9 +290,6 @@
List<ResolvedComponentInfo> stableCopy =
createResolvedComponentsForTestWithOtherProfile(2);
- // Check that the "Other Profile" activity is put in the right spot
- onView(withId(R.id.profile_button)).check(matches(
- withText(stableCopy.get(0).getResolveInfoAt(0).activityInfo.name)));
onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
.perform(click());
onView(withId(R.id.button_once)).perform(click());
@@ -292,6 +300,9 @@
@Test
public void hasLastChosenActivityAndOtherProfile() throws Exception {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+
// In this case we prefer the other profile and don't display anything about the last
// chosen activity.
Intent sendIntent = createSendImageIntent();
@@ -325,9 +336,6 @@
List<ResolvedComponentInfo> stableCopy =
createResolvedComponentsForTestWithOtherProfile(2);
- // Check that the "Other Profile" activity is put in the right spot
- onView(withId(R.id.profile_button)).check(matches(
- withText(stableCopy.get(0).getResolveInfoAt(0).activityInfo.name)));
onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
.perform(click());
onView(withId(R.id.button_once)).perform(click());
@@ -379,6 +387,222 @@
TextUtils.isEmpty(pg.getSubLabel()));
}
+ @Test
+ public void testWorkTab_displayedWhenWorkProfileUserAvailable() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ Intent sendIntent = createSendImageIntent();
+ markWorkProfileUserAvailable();
+
+ mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+
+ onView(withId(R.id.tabs)).check(matches(isDisplayed()));
+ }
+
+ @Test
+ public void testWorkTab_hiddenWhenWorkProfileUserNotAvailable() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ Intent sendIntent = createSendImageIntent();
+
+ mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+
+ onView(withId(R.id.tabs)).check(matches(not(isDisplayed())));
+ }
+
+ @Test
+ public void testWorkTab_workTabListEmptyBeforeGoingToTab() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTest(3);
+ List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos);
+ when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+ Intent sendIntent = createSendImageIntent();
+ markWorkProfileUserAvailable();
+
+ final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+
+ assertThat(activity.getCurrentUserHandle().getIdentifier(), is(0));
+ // The work list adapter must only be filled when we open the work tab
+ assertThat(activity.getWorkListAdapter().getCount(), is(0));
+ }
+
+ @Test
+ public void testWorkTab_workTabUsesExpectedAdapter() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTest(3);
+ List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos);
+ when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+ Intent sendIntent = createSendImageIntent();
+ markWorkProfileUserAvailable();
+
+ final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+
+ assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10));
+ assertThat(activity.getWorkListAdapter().getCount(), is(4));
+ }
+
+ @Test
+ public void testWorkTab_personalTabUsesExpectedAdapter() {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTest(3);
+ List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos);
+ when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+ Intent sendIntent = createSendImageIntent();
+ markWorkProfileUserAvailable();
+
+ final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ onView(withText(R.string.resolver_work_tab)).perform(click());
+
+ assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10));
+ assertThat(activity.getPersonalListAdapter().getCount(), is(3));
+ }
+
+ @Test
+ public void testWorkTab_workProfileHasExpectedNumberOfTargets() throws InterruptedException {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTest(3);
+ List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos);
+ when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+ Intent sendIntent = createSendImageIntent();
+
+ final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ onView(withText(R.string.resolver_work_tab))
+ .perform(click());
+
+ waitForIdle();
+ assertThat(activity.getWorkListAdapter().getCount(), is(4));
+ }
+
+ @Test
+ public void testWorkTab_selectingWorkTabAppOpensAppInWorkProfile() throws InterruptedException {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTest(3);
+ List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos);
+ when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+ Intent sendIntent = createSendImageIntent();
+ ResolveInfo[] chosen = new ResolveInfo[1];
+ sOverrides.onSafelyStartCallback = targetInfo -> {
+ chosen[0] = targetInfo.getResolveInfo();
+ return true;
+ };
+
+ mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ onView(withText(R.string.resolver_work_tab))
+ .perform(click());
+ waitForIdle();
+ // wait for the share sheet to expand
+ Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
+ onView(first(allOf(withText(workResolvedComponentInfos.get(0)
+ .getResolveInfoAt(0).activityInfo.applicationInfo.name), isCompletelyDisplayed())))
+ .perform(click());
+ onView(withId(R.id.button_once))
+ .perform(click());
+
+ waitForIdle();
+ assertThat(chosen[0], is(workResolvedComponentInfos.get(0).getResolveInfoAt(0)));
+ }
+
+ @Test
+ public void testWorkTab_noPersonalApps_workTabHasExpectedNumberOfTargets()
+ throws InterruptedException {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
+ when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+ Intent sendIntent = createSendImageIntent();
+
+ final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ onView(withText(R.string.resolver_work_tab))
+ .perform(click());
+
+ waitForIdle();
+ assertThat(activity.getWorkListAdapter().getCount(), is(4));
+ }
+
+ @Ignore // b/148156663
+ @Test
+ public void testWorkTab_noPersonalApps_canStartWorkApps()
+ throws InterruptedException {
+ // enable the work tab feature flag
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
+ when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+ Intent sendIntent = createSendImageIntent();
+ ResolveInfo[] chosen = new ResolveInfo[1];
+ sOverrides.onSafelyStartCallback = targetInfo -> {
+ chosen[0] = targetInfo.getResolveInfo();
+ return true;
+ };
+
+ mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ onView(withText(R.string.resolver_work_tab))
+ .perform(click());
+ waitForIdle();
+ // wait for the share sheet to expand
+ Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
+ onView(first(allOf(withText(workResolvedComponentInfos.get(0)
+ .getResolveInfoAt(0).activityInfo.applicationInfo.name), isCompletelyDisplayed())))
+ .perform(click());
+ onView(withId(R.id.button_once))
+ .perform(click());
+ waitForIdle();
+
+ assertThat(chosen[0], is(workResolvedComponentInfos.get(0).getResolveInfoAt(0)));
+ }
+
private Intent createSendImageIntent() {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
@@ -411,4 +635,8 @@
private void waitForIdle() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
+
+ private void markWorkProfileUserAvailable() {
+ ResolverWrapperActivity.sOverrides.workProfileUserHandle = UserHandle.of(10);
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java b/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java
index 59634f6..d7db5f8 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java
@@ -46,6 +46,12 @@
createResolverIntent(i), createResolveInfo(i, USER_SOMEONE_ELSE));
}
+ static ResolverActivity.ResolvedComponentInfo createResolvedComponentInfoWithOtherId(int i,
+ int userId) {
+ return new ResolverActivity.ResolvedComponentInfo(createComponentName(i),
+ createResolverIntent(i), createResolveInfo(i, userId));
+ }
+
static ComponentName createComponentName(int i) {
final String name = "component" + i;
return new ComponentName("foo.bar." + name, name);
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
index c5d2cfa..36c8724 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
@@ -17,12 +17,14 @@
package com.android.internal.app;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import android.app.usage.UsageStatsManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.os.Bundle;
import android.os.UserHandle;
import com.android.internal.app.chooser.TargetInfo;
@@ -49,6 +51,17 @@
return (ResolverWrapperAdapter) mMultiProfilePagerAdapter.getActiveListAdapter();
}
+ ResolverListAdapter getPersonalListAdapter() {
+ return ((ResolverListAdapter) mMultiProfilePagerAdapter.getAdapterForIndex(0));
+ }
+
+ ResolverListAdapter getWorkListAdapter() {
+ if (mMultiProfilePagerAdapter.getInactiveListAdapter() == null) {
+ return null;
+ }
+ return ((ResolverListAdapter) mMultiProfilePagerAdapter.getAdapterForIndex(1));
+ }
+
@Override
public boolean isVoiceInteraction() {
if (sOverrides.isVoiceInteraction != null) {
@@ -68,7 +81,12 @@
@Override
protected ResolverListController createListController(UserHandle userHandle) {
- return sOverrides.resolverListController;
+ if (userHandle == UserHandle.SYSTEM) {
+ when(sOverrides.resolverListController.getUserHandle()).thenReturn(UserHandle.SYSTEM);
+ return sOverrides.resolverListController;
+ }
+ when(sOverrides.workResolverListController.getUserHandle()).thenReturn(userHandle);
+ return sOverrides.workResolverListController;
}
@Override
@@ -79,6 +97,20 @@
return super.getPackageManager();
}
+ protected UserHandle getCurrentUserHandle() {
+ return mMultiProfilePagerAdapter.getCurrentUserHandle();
+ }
+
+ @Override
+ protected UserHandle getWorkProfileUserHandle() {
+ return sOverrides.workProfileUserHandle;
+ }
+
+ @Override
+ public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
+ super.startActivityAsUser(intent, options, user);
+ }
+
/**
* We cannot directly mock the activity created since instrumentation creates it.
* <p>
@@ -89,13 +121,17 @@
public Function<PackageManager, PackageManager> createPackageManager;
public Function<TargetInfo, Boolean> onSafelyStartCallback;
public ResolverListController resolverListController;
+ public ResolverListController workResolverListController;
public Boolean isVoiceInteraction;
+ public UserHandle workProfileUserHandle;
public void reset() {
onSafelyStartCallback = null;
isVoiceInteraction = null;
createPackageManager = null;
resolverListController = mock(ResolverListController.class);
+ workResolverListController = mock(ResolverListController.class);
+ workProfileUserHandle = null;
}
}
}
\ No newline at end of file
diff --git a/data/etc/com.android.documentsui.xml b/data/etc/com.android.documentsui.xml
index 36b282c..4d98603 100644
--- a/data/etc/com.android.documentsui.xml
+++ b/data/etc/com.android.documentsui.xml
@@ -17,5 +17,6 @@
<permissions>
<privapp-permissions package="com.android.documentsui">
<permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/>
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 877ef26..0541db1 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -60,10 +60,6 @@
<group gid="log" />
</permission>
- <permission name="android.permission.WRITE_MEDIA_STORAGE" >
- <group gid="media_rw" />
- </permission>
-
<permission name="android.permission.ACCESS_MTP" >
<group gid="mtp" />
</permission>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 18b5f5d..3677b8f 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -63,6 +63,7 @@
<privapp-permissions package="com.android.location.fused">
<permission name="android.permission.INSTALL_LOCATION_PROVIDER"/>
+ <permission name="android.permission.UPDATE_DEVICE_STATS"/>
</privapp-permissions>
<privapp-permissions package="com.android.managedprovisioning">
diff --git a/drm/java/android/drm/DrmConvertedStatus.java b/drm/java/android/drm/DrmConvertedStatus.java
index f6e570a..0f7ceb4 100644
--- a/drm/java/android/drm/DrmConvertedStatus.java
+++ b/drm/java/android/drm/DrmConvertedStatus.java
@@ -25,7 +25,9 @@
* An valid offset value is provided only from a success call to
* {@link DrmManagerClient#closeConvertSession DrmManagerClient.closeConvertSession()}.
*
+ * @deprecated Please use {@link android.media.MediaDrm}
*/
+@Deprecated
public class DrmConvertedStatus {
// The following status code constants must be in sync with
// DrmConvertedStatus.cpp. Please also update isValidStatusCode()
diff --git a/drm/java/android/drm/DrmErrorEvent.java b/drm/java/android/drm/DrmErrorEvent.java
index c61819d..f37c8ac 100644
--- a/drm/java/android/drm/DrmErrorEvent.java
+++ b/drm/java/android/drm/DrmErrorEvent.java
@@ -22,7 +22,9 @@
* An entity class that is passed to the
* {@link DrmManagerClient.OnErrorListener#onError onError()} callback.
*
+ * @deprecated Please use {@link android.media.MediaDrm}
*/
+@Deprecated
public class DrmErrorEvent extends DrmEvent {
// Please add newly defined type constants to the end of the list,
diff --git a/drm/java/android/drm/DrmEvent.java b/drm/java/android/drm/DrmEvent.java
index 1a19f5c..e2fe87b 100644
--- a/drm/java/android/drm/DrmEvent.java
+++ b/drm/java/android/drm/DrmEvent.java
@@ -21,7 +21,9 @@
/**
* A base class that is used to send asynchronous event information from the DRM framework.
*
+ * @deprecated Please use {@link android.media.MediaDrm}
*/
+@Deprecated
public class DrmEvent {
// Please do not add type constants in this class. More event type constants
diff --git a/drm/java/android/drm/DrmInfo.java b/drm/java/android/drm/DrmInfo.java
index 8c43252..3240893 100644
--- a/drm/java/android/drm/DrmInfo.java
+++ b/drm/java/android/drm/DrmInfo.java
@@ -30,7 +30,9 @@
* The caller can retrieve the {@link DrmInfo} instance by passing a {@link DrmInfoRequest}
* instance to {@link DrmManagerClient#acquireDrmInfo}.
*
+ * @deprecated Please use {@link android.media.MediaDrm}
*/
+@Deprecated
public class DrmInfo {
private byte[] mData;
private final String mMimeType;
diff --git a/drm/java/android/drm/DrmInfoEvent.java b/drm/java/android/drm/DrmInfoEvent.java
index 2826dce..853f566c 100644
--- a/drm/java/android/drm/DrmInfoEvent.java
+++ b/drm/java/android/drm/DrmInfoEvent.java
@@ -22,7 +22,9 @@
* An entity class that is passed to the
* {@link DrmManagerClient.OnInfoListener#onInfo onInfo()} callback.
*
+ * @deprecated Please use {@link android.media.MediaDrm}
*/
+@Deprecated
public class DrmInfoEvent extends DrmEvent {
// Please add newly defined type constants to the end of the list,
diff --git a/drm/java/android/drm/DrmInfoRequest.java b/drm/java/android/drm/DrmInfoRequest.java
index 621da41..135bbc0 100644
--- a/drm/java/android/drm/DrmInfoRequest.java
+++ b/drm/java/android/drm/DrmInfoRequest.java
@@ -24,7 +24,9 @@
* class is passed to the {@link DrmManagerClient#acquireDrmInfo acquireDrmInfo()} method to get an
* instance of a {@link DrmInfo}.
*
+ * @deprecated Please use {@link android.media.MediaDrm}
*/
+@Deprecated
public class DrmInfoRequest {
// Changes in following constants should be in sync with DrmInfoRequest.h
/**
diff --git a/drm/java/android/drm/DrmInfoStatus.java b/drm/java/android/drm/DrmInfoStatus.java
index 9a3a7df..0fa1a70 100644
--- a/drm/java/android/drm/DrmInfoStatus.java
+++ b/drm/java/android/drm/DrmInfoStatus.java
@@ -25,7 +25,9 @@
* This class contains the {@link ProcessedData} object, which can be used
* to instantiate a {@link DrmRights} object during license acquisition.
*
+ * @deprecated Please use {@link android.media.MediaDrm}
*/
+@Deprecated
public class DrmInfoStatus {
// The following status code constants must be in sync with DrmInfoStatus.cpp
// Please update isValidStatusCode() if more status codes are added.
diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java
index 041300c..ba3ebdd 100644
--- a/drm/java/android/drm/DrmManagerClient.java
+++ b/drm/java/android/drm/DrmManagerClient.java
@@ -47,7 +47,9 @@
* The main programming interface for the DRM framework. An application must instantiate this class
* to access DRM agents through the DRM framework.
*
+ * @deprecated Please use {@link android.media.MediaDrm}
*/
+@Deprecated
public class DrmManagerClient implements AutoCloseable {
/**
* Indicates that a request was successful or that no error occurred.
diff --git a/drm/java/android/drm/DrmOutputStream.java b/drm/java/android/drm/DrmOutputStream.java
index 9c23834..73e7f23 100644
--- a/drm/java/android/drm/DrmOutputStream.java
+++ b/drm/java/android/drm/DrmOutputStream.java
@@ -40,7 +40,9 @@
* writing to disk, similar to a {@link FilterOutputStream}.
*
* @hide
+ * @deprecated Please use {@link android.media.MediaDrm}
*/
+@Deprecated
public class DrmOutputStream extends OutputStream {
private static final String TAG = "DrmOutputStream";
diff --git a/drm/java/android/drm/DrmRights.java b/drm/java/android/drm/DrmRights.java
index 8747f77..0a8df09 100644
--- a/drm/java/android/drm/DrmRights.java
+++ b/drm/java/android/drm/DrmRights.java
@@ -37,7 +37,9 @@
* agent or plugin, they can be either null, or an empty string, or any other don't-care
* string value.
*
+ * @deprecated Please use {@link android.media.MediaDrm}
*/
+@Deprecated
public class DrmRights {
private byte[] mData;
private String mMimeType;
diff --git a/drm/java/android/drm/DrmStore.java b/drm/java/android/drm/DrmStore.java
index 3a77ea1..98d4449 100644
--- a/drm/java/android/drm/DrmStore.java
+++ b/drm/java/android/drm/DrmStore.java
@@ -19,7 +19,9 @@
/**
* Defines constants that are used by the DRM framework.
*
+ * @deprecated Please use {@link android.media.MediaDrm}
*/
+@Deprecated
public class DrmStore {
/**
* Interface definition for the columns that represent DRM constraints.
diff --git a/drm/java/android/drm/DrmSupportInfo.java b/drm/java/android/drm/DrmSupportInfo.java
index 3694ff4..f7e4fbd 100644
--- a/drm/java/android/drm/DrmSupportInfo.java
+++ b/drm/java/android/drm/DrmSupportInfo.java
@@ -26,7 +26,9 @@
* Plug-in developers can expose the capability of their plug-in by passing an instance of this
* class to an application.
*
+ * @deprecated Please use {@link android.media.MediaDrm}
*/
+@Deprecated
public class DrmSupportInfo {
private final ArrayList<String> mFileSuffixList = new ArrayList<String>();
private final ArrayList<String> mMimeTypeList = new ArrayList<String>();
diff --git a/drm/java/android/drm/DrmUtils.java b/drm/java/android/drm/DrmUtils.java
index 60ee6d9..66a60cf 100644
--- a/drm/java/android/drm/DrmUtils.java
+++ b/drm/java/android/drm/DrmUtils.java
@@ -33,7 +33,9 @@
* constraints, the constraints will show up in the
* {@link DrmStore.ConstraintsColumns#EXTENDED_METADATA} key. You can use
* {@link DrmUtils.ExtendedMetadataParser} to iterate over those values.
+ * @deprecated Please use {@link android.media.MediaDrm}
*/
+@Deprecated
public class DrmUtils {
/* Should be used when we need to read from local file */
/* package */ static byte[] readBytes(String path) throws IOException {
diff --git a/drm/java/android/drm/ProcessedData.java b/drm/java/android/drm/ProcessedData.java
index 06e03e7..35b7288 100644
--- a/drm/java/android/drm/ProcessedData.java
+++ b/drm/java/android/drm/ProcessedData.java
@@ -23,7 +23,9 @@
*
* In a license acquisition scenario this class holds the rights information in binary form.
*
+ * @deprecated Please use {@link android.media.MediaDrm}
*/
+@Deprecated
public class ProcessedData {
private final byte[] mData;
private String mAccountId = "_NO_USER";
diff --git a/packages/WindowManager/OWNERS b/libs/WindowManager/OWNERS
similarity index 100%
rename from packages/WindowManager/OWNERS
rename to libs/WindowManager/OWNERS
diff --git a/packages/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
similarity index 100%
rename from packages/WindowManager/Shell/Android.bp
rename to libs/WindowManager/Shell/Android.bp
diff --git a/packages/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
similarity index 100%
rename from packages/WindowManager/Shell/AndroidManifest.xml
rename to libs/WindowManager/Shell/AndroidManifest.xml
diff --git a/packages/WindowManager/Shell/OWNERS b/libs/WindowManager/Shell/OWNERS
similarity index 100%
rename from packages/WindowManager/Shell/OWNERS
rename to libs/WindowManager/Shell/OWNERS
diff --git a/packages/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
similarity index 100%
rename from packages/WindowManager/Shell/res/values/config.xml
rename to libs/WindowManager/Shell/res/values/config.xml
diff --git a/packages/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java
similarity index 100%
rename from packages/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java
diff --git a/packages/WindowManager/Shell/tests/Android.bp b/libs/WindowManager/Shell/tests/Android.bp
similarity index 100%
rename from packages/WindowManager/Shell/tests/Android.bp
rename to libs/WindowManager/Shell/tests/Android.bp
diff --git a/packages/WindowManager/Shell/tests/AndroidManifest.xml b/libs/WindowManager/Shell/tests/AndroidManifest.xml
similarity index 100%
rename from packages/WindowManager/Shell/tests/AndroidManifest.xml
rename to libs/WindowManager/Shell/tests/AndroidManifest.xml
diff --git a/packages/WindowManager/Shell/tests/AndroidTest.xml b/libs/WindowManager/Shell/tests/AndroidTest.xml
similarity index 100%
rename from packages/WindowManager/Shell/tests/AndroidTest.xml
rename to libs/WindowManager/Shell/tests/AndroidTest.xml
diff --git a/packages/WindowManager/Shell/tests/res/values/config.xml b/libs/WindowManager/Shell/tests/res/values/config.xml
similarity index 100%
rename from packages/WindowManager/Shell/tests/res/values/config.xml
rename to libs/WindowManager/Shell/tests/res/values/config.xml
diff --git a/packages/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java
similarity index 100%
rename from packages/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java
rename to libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java
diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in
index 4a252af..4981792 100644
--- a/libs/hwui/DisplayListOps.in
+++ b/libs/hwui/DisplayListOps.in
@@ -14,39 +14,41 @@
* limitations under the License.
*/
-X(Flush)
-X(Save)
-X(Restore)
+X(Flush)
+X(Save)
+X(Restore)
X(SaveLayer)
X(SaveBehind)
-X(Concat)
-X(SetMatrix)
+X(Concat44)
+X(Concat)
+X(SetMatrix)
+X(Scale)
X(Translate)
-X(ClipPath)
-X(ClipRect)
-X(ClipRRect)
+X(ClipPath)
+X(ClipRect)
+X(ClipRRect)
X(ClipRegion)
X(DrawPaint)
X(DrawBehind)
-X(DrawPath)
-X(DrawRect)
-X(DrawRegion)
-X(DrawOval)
+X(DrawPath)
+X(DrawRect)
+X(DrawRegion)
+X(DrawOval)
X(DrawArc)
-X(DrawRRect)
-X(DrawDRRect)
-X(DrawAnnotation)
-X(DrawDrawable)
+X(DrawRRect)
+X(DrawDRRect)
+X(DrawAnnotation)
+X(DrawDrawable)
X(DrawPicture)
-X(DrawImage)
-X(DrawImageNine)
-X(DrawImageRect)
+X(DrawImage)
+X(DrawImageNine)
+X(DrawImageRect)
X(DrawImageLattice)
X(DrawTextBlob)
-X(DrawPatch)
-X(DrawPoints)
-X(DrawVertices)
-X(DrawAtlas)
+X(DrawPatch)
+X(DrawPoints)
+X(DrawVertices)
+X(DrawAtlas)
X(DrawShadowRec)
X(DrawVectorDrawable)
X(DrawWebView)
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index c0df2fa..dc467c4 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -130,6 +130,12 @@
}
};
+struct Concat44 final : Op {
+ static const auto kType = Type::Concat44;
+ Concat44(const SkScalar m[16]) { memcpy(colMajor, m, sizeof(colMajor)); }
+ SkScalar colMajor[16];
+ void draw(SkCanvas* c, const SkMatrix&) const { c->experimental_concat44(colMajor); }
+};
struct Concat final : Op {
static const auto kType = Type::Concat;
Concat(const SkMatrix& matrix) : matrix(matrix) {}
@@ -144,6 +150,12 @@
c->setMatrix(SkMatrix::Concat(original, matrix));
}
};
+struct Scale final : Op {
+ static const auto kType = Type::Scale;
+ Scale(SkScalar sx, SkScalar sy) : sx(sx), sy(sy) {}
+ SkScalar sx, sy;
+ void draw(SkCanvas* c, const SkMatrix&) const { c->scale(sx, sy); }
+};
struct Translate final : Op {
static const auto kType = Type::Translate;
Translate(SkScalar dx, SkScalar dy) : dx(dx), dy(dy) {}
@@ -562,12 +574,18 @@
this->push<SaveBehind>(0, subset);
}
+void DisplayListData::concat44(const SkScalar colMajor[16]) {
+ this->push<Concat44>(0, colMajor);
+}
void DisplayListData::concat(const SkMatrix& matrix) {
this->push<Concat>(0, matrix);
}
void DisplayListData::setMatrix(const SkMatrix& matrix) {
this->push<SetMatrix>(0, matrix);
}
+void DisplayListData::scale(SkScalar sx, SkScalar sy) {
+ this->push<Scale>(0, sx, sy);
+}
void DisplayListData::translate(SkScalar dx, SkScalar dy) {
this->push<Translate>(0, dx, dy);
}
@@ -823,12 +841,18 @@
return false;
}
+void RecordingCanvas::didConcat44(const SkScalar colMajor[16]) {
+ fDL->concat44(colMajor);
+}
void RecordingCanvas::didConcat(const SkMatrix& matrix) {
fDL->concat(matrix);
}
void RecordingCanvas::didSetMatrix(const SkMatrix& matrix) {
fDL->setMatrix(matrix);
}
+void RecordingCanvas::didScale(SkScalar sx, SkScalar sy) {
+ fDL->scale(sx, sy);
+}
void RecordingCanvas::didTranslate(SkScalar dx, SkScalar dy) {
fDL->translate(dx, dy);
}
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 322eff2..7eb1ce3 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -82,8 +82,10 @@
void saveBehind(const SkRect*);
void restore();
+ void concat44(const SkScalar colMajor[16]);
void concat(const SkMatrix&);
void setMatrix(const SkMatrix&);
+ void scale(SkScalar, SkScalar);
void translate(SkScalar, SkScalar);
void translateZ(SkScalar);
@@ -153,8 +155,10 @@
void onFlush() override;
+ void didConcat44(const SkScalar[16]) override;
void didConcat(const SkMatrix&) override;
void didSetMatrix(const SkMatrix&) override;
+ void didScale(SkScalar, SkScalar) override;
void didTranslate(SkScalar, SkScalar) override;
void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override;
diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
index a6c4e9d..4b2857f 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -31,7 +31,7 @@
, mDecodeSize(mTargetSize)
, mOutColorType(mCodec->computeOutputColorType(kN32_SkColorType))
, mUnpremultipliedRequired(false)
- , mOutColorSpace(mCodec->getInfo().refColorSpace())
+ , mOutColorSpace(mCodec->computeOutputColorSpace(mOutColorType, nullptr))
, mSampleSize(1)
{
}
@@ -111,7 +111,6 @@
if (!gray()) {
return false;
}
- mOutColorSpace = nullptr;
break;
case kN32_SkColorType:
break;
@@ -137,9 +136,15 @@
mOutColorSpace = std::move(colorSpace);
}
+sk_sp<SkColorSpace> ImageDecoder::getOutputColorSpace() const {
+ // kGray_8 is used for ALPHA_8, which ignores the color space.
+ return mOutColorType == kGray_8_SkColorType ? nullptr : mOutColorSpace;
+}
+
+
SkImageInfo ImageDecoder::getOutputInfo() const {
SkISize size = mCropRect ? mCropRect->size() : mTargetSize;
- return SkImageInfo::Make(size, mOutColorType, getOutAlphaType(), mOutColorSpace);
+ return SkImageInfo::Make(size, mOutColorType, getOutAlphaType(), getOutputColorSpace());
}
bool ImageDecoder::opaque() const {
@@ -154,7 +159,7 @@
void* decodePixels = pixels;
size_t decodeRowBytes = rowBytes;
auto decodeInfo = SkImageInfo::Make(mDecodeSize, mOutColorType, getOutAlphaType(),
- mOutColorSpace);
+ getOutputColorSpace());
// Used if we need a temporary before scaling or subsetting.
// FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380.
SkBitmap tmp;
diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h
index 96f97e5..0c99f84 100644
--- a/libs/hwui/hwui/ImageDecoder.h
+++ b/libs/hwui/hwui/ImageDecoder.h
@@ -66,6 +66,7 @@
ImageDecoder& operator=(const ImageDecoder&) = delete;
SkAlphaType getOutAlphaType() const;
+ sk_sp<SkColorSpace> getOutputColorSpace() const;
};
} // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index a1b2b18..aa8849b 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -26,6 +26,7 @@
#include "SkAndroidFrameworkUtils.h"
#include "SkClipStack.h"
#include "SkRect.h"
+#include "include/private/SkM44.h"
namespace android {
namespace uirenderer {
@@ -92,7 +93,7 @@
SkIRect surfaceBounds = canvas->internal_private_getTopLayerBounds();
SkIRect clipBounds = canvas->getDeviceClipBounds();
- SkMatrix44 mat4(canvas->getTotalMatrix());
+ SkM44 mat4(canvas->experimental_getLocalToDevice());
SkRegion clipRegion;
canvas->temporary_internal_getRgnClip(&clipRegion);
@@ -118,7 +119,7 @@
// update the matrix and clip that we pass to the WebView to match the coordinates of
// the offscreen layer
- mat4.preTranslate(-clipBounds.fLeft, -clipBounds.fTop, 0);
+ mat4.preTranslate(-clipBounds.fLeft, -clipBounds.fTop);
clipBounds.offsetTo(0, 0);
clipRegion.translate(-surfaceBounds.fLeft, -surfaceBounds.fTop);
@@ -126,7 +127,7 @@
// we are drawing into a (clipped) offscreen layer so we must update the clip and matrix
// from device coordinates to the layer's coordinates
clipBounds.offset(-surfaceBounds.fLeft, -surfaceBounds.fTop);
- mat4.preTranslate(-surfaceBounds.fLeft, -surfaceBounds.fTop, 0);
+ mat4.preTranslate(-surfaceBounds.fLeft, -surfaceBounds.fTop);
}
DrawGlInfo info;
@@ -137,7 +138,7 @@
info.isLayer = fboID != 0;
info.width = fboSize.width();
info.height = fboSize.height();
- mat4.asColMajorf(&info.transform[0]);
+ mat4.getColMajor(&info.transform[0]);
info.color_space_ptr = canvas->imageInfo().colorSpace();
// ensure that the framebuffer that the webview will render into is bound before we clear
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
index 1127926..68f1117 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -20,6 +20,7 @@
#include <GrBackendDrawableInfo.h>
#include <SkAndroidFrameworkUtils.h>
#include <SkImage.h>
+#include "include/private/SkM44.h"
#include <utils/Color.h>
#include <utils/Trace.h>
#include <utils/TraceUtils.h>
@@ -62,7 +63,7 @@
renderthread::RenderThread::getInstance().vulkanManager();
mFunctorHandle->initVk(vk_manager.getVkFunctorInitParams());
- SkMatrix44 mat4(mMatrix);
+ SkM44 mat4(mMatrix);
VkFunctorDrawParams params{
.width = mImageInfo.width(),
.height = mImageInfo.height(),
@@ -72,7 +73,7 @@
.clip_right = mClip.fRight,
.clip_bottom = mClip.fBottom,
};
- mat4.asColMajorf(¶ms.transform[0]);
+ mat4.getColMajor(¶ms.transform[0]);
params.secondary_command_buffer = vulkan_info.fSecondaryCommandBuffer;
params.color_attachment_index = vulkan_info.fColorAttachmentIndex;
params.compatible_render_pass = vulkan_info.fCompatibleRenderPass;
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index 706325f..241d370 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -121,7 +121,7 @@
glBindTexture(GL_TEXTURE_2D, 0);
DrawGlInfo info;
- SkMatrix44 mat4(canvas->getTotalMatrix());
+ SkM44 mat4(canvas->experimental_getLocalToDevice());
SkIRect clipBounds = canvas->getDeviceClipBounds();
info.clipLeft = clipBounds.fLeft;
@@ -131,7 +131,7 @@
info.isLayer = true;
info.width = mFBInfo.width();
info.height = mFBInfo.height();
- mat4.asColMajorf(&info.transform[0]);
+ mat4.getColMajor(&info.transform[0]);
info.color_space_ptr = canvas->imageInfo().colorSpace();
glViewport(0, 0, info.width, info.height);
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 52305b8..ebc622b 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -100,6 +100,7 @@
virtual int32_t getDisplayId() const;
virtual void fade(Transition transition);
virtual void unfade(Transition transition);
+ virtual void setDisplayViewport(const DisplayViewport& viewport);
virtual void setPresentation(Presentation presentation);
virtual void setSpots(const PointerCoords* spotCoords,
@@ -108,7 +109,6 @@
void updatePointerIcon(int32_t iconId);
void setCustomPointerIcon(const SpriteIcon& icon);
- void setDisplayViewport(const DisplayViewport& viewport);
void setInactivityTimeout(InactivityTimeout inactivityTimeout);
void reloadPointerResources();
diff --git a/location/java/android/location/GnssClock.java b/location/java/android/location/GnssClock.java
index 8a7878b..ed4bf1b 100644
--- a/location/java/android/location/GnssClock.java
+++ b/location/java/android/location/GnssClock.java
@@ -17,6 +17,7 @@
package android.location;
import android.annotation.FloatRange;
+import android.annotation.NonNull;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -39,6 +40,9 @@
private static final int HAS_DRIFT_UNCERTAINTY = (1<<6);
private static final int HAS_ELAPSED_REALTIME_NANOS = (1 << 7);
private static final int HAS_ELAPSED_REALTIME_UNCERTAINTY_NANOS = (1 << 8);
+ private static final int HAS_REFERENCE_CONSTELLATION_TYPE_FOR_ISB = (1 << 9);
+ private static final int HAS_REFERENCE_CARRIER_FREQUENCY_FOR_ISB = (1 << 10);
+ private static final int HAS_REFERENCE_CODE_TYPE_FOR_ISB = (1 << 11);
// End enumerations in sync with gps.h
@@ -54,6 +58,9 @@
private int mHardwareClockDiscontinuityCount;
private long mElapsedRealtimeNanos;
private double mElapsedRealtimeUncertaintyNanos;
+ private int mReferenceConstellationTypeForIsb;
+ private double mReferenceCarrierFrequencyHzForIsb;
+ private String mReferenceCodeTypeForIsb;
/**
* @hide
@@ -81,6 +88,9 @@
mHardwareClockDiscontinuityCount = clock.mHardwareClockDiscontinuityCount;
mElapsedRealtimeNanos = clock.mElapsedRealtimeNanos;
mElapsedRealtimeUncertaintyNanos = clock.mElapsedRealtimeUncertaintyNanos;
+ mReferenceConstellationTypeForIsb = clock.mReferenceConstellationTypeForIsb;
+ mReferenceCarrierFrequencyHzForIsb = clock.mReferenceCarrierFrequencyHzForIsb;
+ mReferenceCodeTypeForIsb = clock.mReferenceCodeTypeForIsb;
}
/**
@@ -196,7 +206,6 @@
@TestApi
public void resetTimeUncertaintyNanos() {
resetFlag(HAS_TIME_UNCERTAINTY);
- mTimeUncertaintyNanos = Double.NaN;
}
/**
@@ -286,7 +295,6 @@
@TestApi
public void resetBiasNanos() {
resetFlag(HAS_BIAS);
- mBiasNanos = Double.NaN;
}
/**
@@ -327,7 +335,6 @@
@TestApi
public void resetBiasUncertaintyNanos() {
resetFlag(HAS_BIAS_UNCERTAINTY);
- mBiasUncertaintyNanos = Double.NaN;
}
/**
@@ -371,7 +378,6 @@
@TestApi
public void resetDriftNanosPerSecond() {
resetFlag(HAS_DRIFT);
- mDriftNanosPerSecond = Double.NaN;
}
/**
@@ -411,7 +417,6 @@
@TestApi
public void resetDriftUncertaintyNanosPerSecond() {
resetFlag(HAS_DRIFT_UNCERTAINTY);
- mDriftUncertaintyNanosPerSecond = Double.NaN;
}
/**
@@ -495,7 +500,128 @@
@TestApi
public void resetElapsedRealtimeUncertaintyNanos() {
resetFlag(HAS_ELAPSED_REALTIME_UNCERTAINTY_NANOS);
- mElapsedRealtimeUncertaintyNanos = Double.NaN;
+ }
+
+ /**
+ * Returns {@code true} if {@link #getReferenceConstellationTypeForIsb()} is available,
+ * {@code false} otherwise.
+ */
+ public boolean hasReferenceConstellationTypeForIsb() {
+ return isFlagSet(HAS_REFERENCE_CONSTELLATION_TYPE_FOR_ISB);
+ }
+
+ /**
+ * Returns the reference constellation type for inter-signal bias.
+ *
+ * <p>The value is only available if {@link #hasReferenceConstellationTypeForIsb()} is
+ * {@code true}.
+ *
+ * <p>The return value is one of those constants with {@code CONSTELLATION_} prefix in
+ * {@link GnssStatus}.
+ */
+ @GnssStatus.ConstellationType
+ public int getReferenceConstellationTypeForIsb() {
+ return mReferenceConstellationTypeForIsb;
+ }
+
+ /**
+ * Sets the reference constellation type for inter-signal bias.
+ * @hide
+ */
+ @TestApi
+ public void setReferenceConstellationTypeForIsb(@GnssStatus.ConstellationType int value) {
+ setFlag(HAS_REFERENCE_CONSTELLATION_TYPE_FOR_ISB);
+ mReferenceConstellationTypeForIsb = value;
+ }
+
+ /**
+ * Resets the reference constellation type for inter-signal bias.
+ * @hide
+ */
+ @TestApi
+ public void resetReferenceConstellationTypeForIsb() {
+ resetFlag(HAS_REFERENCE_CONSTELLATION_TYPE_FOR_ISB);
+ mReferenceConstellationTypeForIsb = GnssStatus.CONSTELLATION_UNKNOWN;
+ }
+
+ /**
+ * Returns {@code true} if {@link #getReferenceCarrierFrequencyHzForIsb()} is available, {@code
+ * false} otherwise.
+ */
+ public boolean hasReferenceCarrierFrequencyHzForIsb() {
+ return isFlagSet(HAS_REFERENCE_CARRIER_FREQUENCY_FOR_ISB);
+ }
+
+ /**
+ * Returns the reference carrier frequency in Hz for inter-signal bias.
+ *
+ * <p>The value is only available if {@link #hasReferenceCarrierFrequencyHzForIsb()} is
+ * {@code true}.
+ */
+ @FloatRange(from = 0.0)
+ public double getReferenceCarrierFrequencyHzForIsb() {
+ return mReferenceCarrierFrequencyHzForIsb;
+ }
+
+ /**
+ * Sets the reference carrier frequency in Hz for inter-signal bias.
+ * @hide
+ */
+ @TestApi
+ public void setReferenceCarrierFrequencyHzForIsb(@FloatRange(from = 0.0) double value) {
+ setFlag(HAS_REFERENCE_CARRIER_FREQUENCY_FOR_ISB);
+ mReferenceCarrierFrequencyHzForIsb = value;
+ }
+
+ /**
+ * Resets the reference carrier frequency in Hz for inter-signal bias.
+ * @hide
+ */
+ @TestApi
+ public void resetReferenceCarrierFrequencyHzForIsb() {
+ resetFlag(HAS_REFERENCE_CARRIER_FREQUENCY_FOR_ISB);
+ }
+
+ /**
+ * Returns {@code true} if {@link #getReferenceCodeTypeForIsb()} is available, {@code
+ * false} otherwise.
+ */
+ public boolean hasReferenceCodeTypeForIsb() {
+ return isFlagSet(HAS_REFERENCE_CODE_TYPE_FOR_ISB);
+ }
+
+ /**
+ * Returns the reference code type for inter-signal bias.
+ *
+ * <p>The value is only available if {@link #hasReferenceCodeTypeForIsb()} is
+ * {@code true}.
+ *
+ * <p>The return value is one of those constants defined in
+ * {@link GnssMeasurement#getCodeType()}.
+ */
+ @NonNull
+ public String getReferenceCodeTypeForIsb() {
+ return mReferenceCodeTypeForIsb;
+ }
+
+ /**
+ * Sets the reference code type for inter-signal bias.
+ * @hide
+ */
+ @TestApi
+ public void setReferenceCodeTypeForIsb(@NonNull String codeType) {
+ setFlag(HAS_REFERENCE_CODE_TYPE_FOR_ISB);
+ mReferenceCodeTypeForIsb = codeType;
+ }
+
+ /**
+ * Resets the reference code type for inter-signal bias.
+ * @hide
+ */
+ @TestApi
+ public void resetReferenceCodeTypeForIsb() {
+ resetFlag(HAS_REFERENCE_CODE_TYPE_FOR_ISB);
+ mReferenceCodeTypeForIsb = "UNKNOWN";
}
/**
@@ -543,6 +669,9 @@
gpsClock.mHardwareClockDiscontinuityCount = parcel.readInt();
gpsClock.mElapsedRealtimeNanos = parcel.readLong();
gpsClock.mElapsedRealtimeUncertaintyNanos = parcel.readDouble();
+ gpsClock.mReferenceConstellationTypeForIsb = parcel.readInt();
+ gpsClock.mReferenceCarrierFrequencyHzForIsb = parcel.readDouble();
+ gpsClock.mReferenceCodeTypeForIsb = parcel.readString();
return gpsClock;
}
@@ -567,6 +696,9 @@
parcel.writeInt(mHardwareClockDiscontinuityCount);
parcel.writeLong(mElapsedRealtimeNanos);
parcel.writeDouble(mElapsedRealtimeUncertaintyNanos);
+ parcel.writeInt(mReferenceConstellationTypeForIsb);
+ parcel.writeDouble(mReferenceCarrierFrequencyHzForIsb);
+ parcel.writeString(mReferenceCodeTypeForIsb);
}
@Override
@@ -580,7 +712,9 @@
final String formatWithUncertainty = " %-15s = %-25s %-26s = %s\n";
StringBuilder builder = new StringBuilder("GnssClock:\n");
- builder.append(String.format(format, "LeapSecond", hasLeapSecond() ? mLeapSecond : null));
+ if (hasLeapSecond()) {
+ builder.append(String.format(format, "LeapSecond", mLeapSecond));
+ }
builder.append(String.format(
formatWithUncertainty,
@@ -589,39 +723,57 @@
"TimeUncertaintyNanos",
hasTimeUncertaintyNanos() ? mTimeUncertaintyNanos : null));
- builder.append(String.format(
- format,
- "FullBiasNanos",
- hasFullBiasNanos() ? mFullBiasNanos : null));
+ if (hasFullBiasNanos()) {
+ builder.append(String.format(format, "FullBiasNanos", mFullBiasNanos));
+ }
- builder.append(String.format(
- formatWithUncertainty,
- "BiasNanos",
- hasBiasNanos() ? mBiasNanos : null,
- "BiasUncertaintyNanos",
- hasBiasUncertaintyNanos() ? mBiasUncertaintyNanos : null));
+ if (hasBiasNanos() || hasBiasUncertaintyNanos()) {
+ builder.append(String.format(
+ formatWithUncertainty,
+ "BiasNanos",
+ hasBiasNanos() ? mBiasNanos : null,
+ "BiasUncertaintyNanos",
+ hasBiasUncertaintyNanos() ? mBiasUncertaintyNanos : null));
+ }
- builder.append(String.format(
- formatWithUncertainty,
- "DriftNanosPerSecond",
- hasDriftNanosPerSecond() ? mDriftNanosPerSecond : null,
- "DriftUncertaintyNanosPerSecond",
- hasDriftUncertaintyNanosPerSecond() ? mDriftUncertaintyNanosPerSecond : null));
+ if (hasDriftNanosPerSecond() || hasDriftUncertaintyNanosPerSecond()) {
+ builder.append(String.format(
+ formatWithUncertainty,
+ "DriftNanosPerSecond",
+ hasDriftNanosPerSecond() ? mDriftNanosPerSecond : null,
+ "DriftUncertaintyNanosPerSecond",
+ hasDriftUncertaintyNanosPerSecond() ? mDriftUncertaintyNanosPerSecond : null));
+ }
builder.append(String.format(
format,
"HardwareClockDiscontinuityCount",
mHardwareClockDiscontinuityCount));
- builder.append(String.format(
- format,
- "ElapsedRealtimeNanos",
- hasElapsedRealtimeNanos() ? mElapsedRealtimeNanos : null));
+ if (hasElapsedRealtimeNanos() || hasElapsedRealtimeUncertaintyNanos()) {
+ builder.append(String.format(
+ formatWithUncertainty,
+ "ElapsedRealtimeNanos",
+ hasElapsedRealtimeNanos() ? mElapsedRealtimeNanos : null,
+ "ElapsedRealtimeUncertaintyNanos",
+ hasElapsedRealtimeUncertaintyNanos() ? mElapsedRealtimeUncertaintyNanos
+ : null));
+ }
- builder.append(String.format(
- format,
- "ElapsedRealtimeUncertaintyNanos",
- hasElapsedRealtimeUncertaintyNanos() ? mElapsedRealtimeUncertaintyNanos : null));
+ if (hasReferenceConstellationTypeForIsb()) {
+ builder.append(String.format(format, "ReferenceConstellationTypeForIsb",
+ mReferenceConstellationTypeForIsb));
+ }
+
+ if (hasReferenceCarrierFrequencyHzForIsb()) {
+ builder.append(String.format(format, "ReferenceCarrierFrequencyHzForIsb",
+ mReferenceCarrierFrequencyHzForIsb));
+ }
+
+ if (hasReferenceCodeTypeForIsb()) {
+ builder.append(
+ String.format(format, "ReferenceCodeTypeForIsb", mReferenceCodeTypeForIsb));
+ }
return builder.toString();
}
@@ -639,6 +791,9 @@
setHardwareClockDiscontinuityCount(Integer.MIN_VALUE);
resetElapsedRealtimeNanos();
resetElapsedRealtimeUncertaintyNanos();
+ resetReferenceConstellationTypeForIsb();
+ resetReferenceCarrierFrequencyHzForIsb();
+ resetReferenceCodeTypeForIsb();
}
private void setFlag(int flag) {
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index 70abbb3..83a8995 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -16,11 +16,21 @@
package android.location;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_AUTOMATIC_GAIN_CONTROL;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_CARRIER_CYCLES;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_CARRIER_FREQUENCY;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_CARRIER_PHASE;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_CARRIER_PHASE_UNCERTAINTY;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_RECEIVER_ISB;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_RECEIVER_ISB_UNCERTAINTY;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_SATELLITE_ISB;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_SATELLITE_ISB_UNCERTAINTY;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_SNR;
+
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.TestApi;
-import android.hardware.gnss.V1_0.IGnssMeasurementCallback.GnssMeasurementFlags;
import android.os.Parcel;
import android.os.Parcelable;
@@ -53,19 +63,14 @@
private double mSnrInDb;
private double mAutomaticGainControlLevelInDb;
@NonNull private String mCodeType;
+ private double mReceiverInterSignalBiasNanos;
+ private double mReceiverInterSignalBiasUncertaintyNanos;
+ private double mSatelliteInterSignalBiasNanos;
+ private double mSatelliteInterSignalBiasUncertaintyNanos;
// The following enumerations must be in sync with the values declared in GNSS HAL.
private static final int HAS_NO_FLAGS = 0;
- private static final int HAS_SNR = GnssMeasurementFlags.HAS_SNR;
- private static final int HAS_CARRIER_FREQUENCY = GnssMeasurementFlags.HAS_CARRIER_FREQUENCY;
- private static final int HAS_CARRIER_CYCLES = GnssMeasurementFlags.HAS_CARRIER_CYCLES;
- private static final int HAS_CARRIER_PHASE = GnssMeasurementFlags.HAS_CARRIER_PHASE;
- private static final int HAS_CARRIER_PHASE_UNCERTAINTY =
- GnssMeasurementFlags.HAS_CARRIER_PHASE_UNCERTAINTY;
- private static final int HAS_AUTOMATIC_GAIN_CONTROL =
- GnssMeasurementFlags.HAS_AUTOMATIC_GAIN_CONTROL;
-
private static final int HAS_CODE_TYPE = (1 << 14);
private static final int HAS_BASEBAND_CN0 = (1 << 15);
@@ -263,6 +268,12 @@
mSnrInDb = measurement.mSnrInDb;
mAutomaticGainControlLevelInDb = measurement.mAutomaticGainControlLevelInDb;
mCodeType = measurement.mCodeType;
+ mReceiverInterSignalBiasNanos = measurement.mReceiverInterSignalBiasNanos;
+ mReceiverInterSignalBiasUncertaintyNanos =
+ measurement.mReceiverInterSignalBiasUncertaintyNanos;
+ mSatelliteInterSignalBiasNanos = measurement.mSatelliteInterSignalBiasNanos;
+ mSatelliteInterSignalBiasUncertaintyNanos =
+ measurement.mSatelliteInterSignalBiasUncertaintyNanos;
}
/**
@@ -778,10 +789,12 @@
/**
* Gets the Carrier-to-noise density in dB-Hz.
*
- * <p>Typical range: 10-50 db-Hz.
+ * <p>Typical range: 10-50 dB-Hz. The range of possible C/N0 values is 0-63 dB-Hz to handle
+ * some edge cases.
*
* <p>The value contains the measured C/N0 for the signal at the antenna input.
*/
+ @FloatRange(from = 0, to = 63)
public double getCn0DbHz() {
return mCn0DbHz;
}
@@ -805,13 +818,14 @@
/**
* Gets the baseband carrier-to-noise density in dB-Hz.
*
- * <p>Typical range: 0-50 dB-Hz.
+ * <p>Typical range: 10-50 dB-Hz. The range of possible baseband C/N0 values is 0-63 dB-Hz to
+ * handle some edge cases.
*
* <p>The value contains the measured C/N0 for the signal at the baseband. This is typically
* a few dB weaker than the value estimated for C/N0 at the antenna port, which is reported
* in {@link #getCn0DbHz()}.
*/
- @FloatRange(from = 0, to = 50)
+ @FloatRange(from = 0, to = 63)
public double getBasebandCn0DbHz() {
return mBasebandCn0DbHz;
}
@@ -835,7 +849,6 @@
@TestApi
public void resetBasebandCn0DbHz() {
resetFlag(HAS_BASEBAND_CN0);
- mBasebandCn0DbHz = Double.NaN;
}
/**
@@ -1166,7 +1179,6 @@
@Deprecated
public void resetCarrierPhase() {
resetFlag(HAS_CARRIER_PHASE);
- mCarrierPhase = Double.NaN;
}
/**
@@ -1221,7 +1233,6 @@
@Deprecated
public void resetCarrierPhaseUncertainty() {
resetFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
- mCarrierPhaseUncertainty = Double.NaN;
}
/**
@@ -1292,7 +1303,6 @@
@TestApi
public void resetSnrInDb() {
resetFlag(HAS_SNR);
- mSnrInDb = Double.NaN;
}
/**
@@ -1340,7 +1350,6 @@
@TestApi
public void resetAutomaticGainControlLevel() {
resetFlag(HAS_AUTOMATIC_GAIN_CONTROL);
- mAutomaticGainControlLevelInDb = Double.NaN;
}
/**
@@ -1425,7 +1434,200 @@
mCodeType = "UNKNOWN";
}
- public static final @android.annotation.NonNull Creator<GnssMeasurement> CREATOR = new Creator<GnssMeasurement>() {
+ /**
+ * Returns {@code true} if {@link #getReceiverInterSignalBiasNanos()} is available,
+ * {@code false} otherwise.
+ */
+ public boolean hasReceiverInterSignalBiasNanos() {
+ return isFlagSet(HAS_RECEIVER_ISB);
+ }
+
+ /**
+ * Gets the GNSS measurement's receiver inter-signal bias in nanoseconds with sub-nanosecond
+ * accuracy.
+ *
+ * <p>This value is the estimated receiver-side inter-system (different from the
+ * constellation in {@link GnssClock#getReferenceConstellationTypeForIsb()} bias and
+ * inter-frequency (different from the carrier frequency in
+ * {@link GnssClock#getReferenceCarrierFrequencyHzForIsb()}) bias. The reported receiver
+ * inter-signal bias must include signal delays caused by:
+ *
+ * <ul>
+ * <li>Receiver inter-constellation bias</li>
+ * <li>Receiver inter-frequency bias</li>
+ * <li>Receiver inter-code bias</li>
+ * </ul>
+ *
+ * <p>The value does not include the inter-frequency Ionospheric bias.
+ *
+ * <p>The value is only available if {@link #hasReceiverInterSignalBiasNanos()} is {@code true}.
+ */
+ public double getReceiverInterSignalBiasNanos() {
+ return mReceiverInterSignalBiasNanos;
+ }
+
+ /**
+ * Sets the GNSS measurement's receiver inter-signal bias in nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void setReceiverInterSignalBiasNanos(double receiverInterSignalBiasNanos) {
+ setFlag(HAS_RECEIVER_ISB);
+ mReceiverInterSignalBiasNanos = receiverInterSignalBiasNanos;
+ }
+
+ /**
+ * Resets the GNSS measurement's receiver inter-signal bias in nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void resetReceiverInterSignalBiasNanos() {
+ resetFlag(HAS_RECEIVER_ISB);
+ }
+
+ /**
+ * Returns {@code true} if {@link #getReceiverInterSignalBiasUncertaintyNanos()} is available,
+ * {@code false} otherwise.
+ */
+ public boolean hasReceiverInterSignalBiasUncertaintyNanos() {
+ return isFlagSet(HAS_RECEIVER_ISB_UNCERTAINTY);
+ }
+
+ /**
+ * Gets the GNSS measurement's receiver inter-signal bias uncertainty (1 sigma) in
+ * nanoseconds with sub-nanosecond accuracy.
+ *
+ * <p>The value is only available if {@link #hasReceiverInterSignalBiasUncertaintyNanos()} is
+ * {@code true}.
+ */
+ @FloatRange(from = 0.0)
+ public double getReceiverInterSignalBiasUncertaintyNanos() {
+ return mReceiverInterSignalBiasUncertaintyNanos;
+ }
+
+ /**
+ * Sets the GNSS measurement's receiver inter-signal bias uncertainty (1 sigma) in nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void setReceiverInterSignalBiasUncertaintyNanos(@FloatRange(from = 0.0)
+ double receiverInterSignalBiasUncertaintyNanos) {
+ setFlag(HAS_RECEIVER_ISB_UNCERTAINTY);
+ mReceiverInterSignalBiasUncertaintyNanos = receiverInterSignalBiasUncertaintyNanos;
+ }
+
+ /**
+ * Resets the GNSS measurement's receiver inter-signal bias uncertainty (1 sigma) in
+ * nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void resetReceiverInterSignalBiasUncertaintyNanos() {
+ resetFlag(HAS_RECEIVER_ISB_UNCERTAINTY);
+ }
+
+ /**
+ * Returns {@code true} if {@link #getSatelliteInterSignalBiasNanos()} is available,
+ * {@code false} otherwise.
+ */
+ public boolean hasSatelliteInterSignalBiasNanos() {
+ return isFlagSet(HAS_SATELLITE_ISB);
+ }
+
+ /**
+ * Gets the GNSS measurement's satellite inter-signal bias in nanoseconds with sub-nanosecond
+ * accuracy.
+ *
+ * <p>This value is the satellite-and-control-segment-side inter-system (different from the
+ * constellation in {@link GnssClock#getReferenceConstellationTypeForIsb()}) bias and
+ * inter-frequency (different from the carrier frequency in
+ * {@link GnssClock#getReferenceCarrierFrequencyHzForIsb()}) bias, including:
+ *
+ * <ul>
+ * <li>Master clock bias (e.g., GPS-GAL Time Offset (GGTO), GPT-UTC Time Offset (TauGps),
+ * BDS-GLO Time Offset (BGTO))</li>
+ * <li>Group delay (e.g., Total Group Delay (TGD))</li>
+ * <li>Satellite inter-signal bias, which includes satellite inter-frequency bias (GLO only),
+ * and satellite inter-code bias (e.g., Differential Code Bias (DCB)).</li>
+ * </ul>
+ *
+ * <p>The value is only available if {@link #hasSatelliteInterSignalBiasNanos()} is {@code
+ * true}.
+ */
+ public double getSatelliteInterSignalBiasNanos() {
+ return mSatelliteInterSignalBiasNanos;
+ }
+
+ /**
+ * Sets the GNSS measurement's satellite inter-signal bias in nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void setSatelliteInterSignalBiasNanos(double satelliteInterSignalBiasNanos) {
+ setFlag(HAS_SATELLITE_ISB);
+ mSatelliteInterSignalBiasNanos = satelliteInterSignalBiasNanos;
+ }
+
+ /**
+ * Resets the GNSS measurement's satellite inter-signal bias in nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void resetSatelliteInterSignalBiasNanos() {
+ resetFlag(HAS_SATELLITE_ISB);
+ }
+
+ /**
+ * Returns {@code true} if {@link #getSatelliteInterSignalBiasUncertaintyNanos()} is available,
+ * {@code false} otherwise.
+ */
+ public boolean hasSatelliteInterSignalBiasUncertaintyNanos() {
+ return isFlagSet(HAS_SATELLITE_ISB_UNCERTAINTY);
+ }
+
+ /**
+ * Gets the GNSS measurement's satellite inter-signal bias uncertainty (1 sigma) in
+ * nanoseconds with sub-nanosecond accuracy.
+ *
+ * <p>The value is only available if {@link #hasSatelliteInterSignalBiasUncertaintyNanos()} is
+ * {@code true}.
+ */
+ @FloatRange(from = 0.0)
+ public double getSatelliteInterSignalBiasUncertaintyNanos() {
+ return mSatelliteInterSignalBiasUncertaintyNanos;
+ }
+
+ /**
+ * Sets the GNSS measurement's satellite inter-signal bias uncertainty (1 sigma) in nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void setSatelliteInterSignalBiasUncertaintyNanos(@FloatRange(from = 0.0)
+ double satelliteInterSignalBiasUncertaintyNanos) {
+ setFlag(HAS_SATELLITE_ISB_UNCERTAINTY);
+ mSatelliteInterSignalBiasUncertaintyNanos = satelliteInterSignalBiasUncertaintyNanos;
+ }
+
+ /**
+ * Resets the GNSS measurement's satellite inter-signal bias uncertainty (1 sigma) in
+ * nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void resetSatelliteInterSignalBiasUncertaintyNanos() {
+ resetFlag(HAS_SATELLITE_ISB_UNCERTAINTY);
+ }
+
+
+ public static final @NonNull Creator<GnssMeasurement> CREATOR = new Creator<GnssMeasurement>() {
@Override
public GnssMeasurement createFromParcel(Parcel parcel) {
GnssMeasurement gnssMeasurement = new GnssMeasurement();
@@ -1452,6 +1654,10 @@
gnssMeasurement.mAutomaticGainControlLevelInDb = parcel.readDouble();
gnssMeasurement.mCodeType = parcel.readString();
gnssMeasurement.mBasebandCn0DbHz = parcel.readDouble();
+ gnssMeasurement.mReceiverInterSignalBiasNanos = parcel.readDouble();
+ gnssMeasurement.mReceiverInterSignalBiasUncertaintyNanos = parcel.readDouble();
+ gnssMeasurement.mSatelliteInterSignalBiasNanos = parcel.readDouble();
+ gnssMeasurement.mSatelliteInterSignalBiasUncertaintyNanos = parcel.readDouble();
return gnssMeasurement;
}
@@ -1486,6 +1692,10 @@
parcel.writeDouble(mAutomaticGainControlLevelInDb);
parcel.writeString(mCodeType);
parcel.writeDouble(mBasebandCn0DbHz);
+ parcel.writeDouble(mReceiverInterSignalBiasNanos);
+ parcel.writeDouble(mReceiverInterSignalBiasUncertaintyNanos);
+ parcel.writeDouble(mSatelliteInterSignalBiasNanos);
+ parcel.writeDouble(mSatelliteInterSignalBiasUncertaintyNanos);
}
@Override
@@ -1514,8 +1724,9 @@
builder.append(String.format(format, "Cn0DbHz", mCn0DbHz));
- builder.append(String.format(format, "BasebandCn0DbHz",
- hasBasebandCn0DbHz() ? mBasebandCn0DbHz : null));
+ if (hasBasebandCn0DbHz()) {
+ builder.append(String.format(format, "BasebandCn0DbHz", mBasebandCn0DbHz));
+ }
builder.append(String.format(
formatWithUncertainty,
@@ -1536,37 +1747,57 @@
"AccumulatedDeltaRangeUncertaintyMeters",
mAccumulatedDeltaRangeUncertaintyMeters));
- builder.append(String.format(
- format,
- "CarrierFrequencyHz",
- hasCarrierFrequencyHz() ? mCarrierFrequencyHz : null));
+ if (hasCarrierFrequencyHz()) {
+ builder.append(String.format(format, "CarrierFrequencyHz", mCarrierFrequencyHz));
+ }
- builder.append(String.format(
- format,
- "CarrierCycles",
- hasCarrierCycles() ? mCarrierCycles : null));
+ if (hasCarrierCycles()) {
+ builder.append(String.format(format, "CarrierCycles", mCarrierCycles));
+ }
- builder.append(String.format(
- formatWithUncertainty,
- "CarrierPhase",
- hasCarrierPhase() ? mCarrierPhase : null,
- "CarrierPhaseUncertainty",
- hasCarrierPhaseUncertainty() ? mCarrierPhaseUncertainty : null));
+ if (hasCarrierPhase() || hasCarrierPhaseUncertainty()) {
+ builder.append(String.format(
+ formatWithUncertainty,
+ "CarrierPhase",
+ hasCarrierPhase() ? mCarrierPhase : null,
+ "CarrierPhaseUncertainty",
+ hasCarrierPhaseUncertainty() ? mCarrierPhaseUncertainty : null));
+ }
builder.append(String.format(format, "MultipathIndicator", getMultipathIndicatorString()));
- builder.append(String.format(
- format,
- "SnrInDb",
- hasSnrInDb() ? mSnrInDb : null));
- builder.append(String.format(
- format,
- "AgcLevelDb",
- hasAutomaticGainControlLevelDb() ? mAutomaticGainControlLevelInDb : null));
- builder.append(String.format(
- format,
- "CodeType",
- hasCodeType() ? mCodeType : null));
+ if (hasSnrInDb()) {
+ builder.append(String.format(format, "SnrInDb", mSnrInDb));
+ }
+
+ if (hasAutomaticGainControlLevelDb()) {
+ builder.append(String.format(format, "AgcLevelDb", mAutomaticGainControlLevelInDb));
+ }
+
+ if (hasCodeType()) {
+ builder.append(String.format(format, "CodeType", mCodeType));
+ }
+
+ if (hasReceiverInterSignalBiasNanos() || hasReceiverInterSignalBiasUncertaintyNanos()) {
+ builder.append(String.format(
+ formatWithUncertainty,
+ "ReceiverInterSignalBiasNs",
+ hasReceiverInterSignalBiasNanos() ? mReceiverInterSignalBiasNanos : null,
+ "ReceiverInterSignalBiasUncertaintyNs",
+ hasReceiverInterSignalBiasUncertaintyNanos()
+ ? mReceiverInterSignalBiasUncertaintyNanos : null));
+ }
+
+ if (hasSatelliteInterSignalBiasNanos() || hasSatelliteInterSignalBiasUncertaintyNanos()) {
+ builder.append(String.format(
+ formatWithUncertainty,
+ "SatelliteInterSignalBiasNs",
+ hasSatelliteInterSignalBiasNanos() ? mSatelliteInterSignalBiasNanos : null,
+ "SatelliteInterSignalBiasUncertaintyNs",
+ hasSatelliteInterSignalBiasUncertaintyNanos()
+ ? mSatelliteInterSignalBiasUncertaintyNanos
+ : null));
+ }
return builder.toString();
}
@@ -1593,6 +1824,10 @@
resetAutomaticGainControlLevel();
resetCodeType();
resetBasebandCn0DbHz();
+ resetReceiverInterSignalBiasNanos();
+ resetReceiverInterSignalBiasUncertaintyNanos();
+ resetSatelliteInterSignalBiasNanos();
+ resetSatelliteInterSignalBiasUncertaintyNanos();
}
private void setFlag(int flag) {
diff --git a/location/java/android/location/GnssNavigationMessage.java b/location/java/android/location/GnssNavigationMessage.java
index a83db3f..ca0bfb1 100644
--- a/location/java/android/location/GnssNavigationMessage.java
+++ b/location/java/android/location/GnssNavigationMessage.java
@@ -16,9 +16,9 @@
package android.location;
-import android.annotation.TestApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -39,7 +39,8 @@
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({TYPE_UNKNOWN, TYPE_GPS_L1CA, TYPE_GPS_L2CNAV, TYPE_GPS_L5CNAV, TYPE_GPS_CNAV2,
- TYPE_GLO_L1CA, TYPE_BDS_D1, TYPE_BDS_D2, TYPE_GAL_I, TYPE_GAL_F})
+ TYPE_SBS, TYPE_GLO_L1CA, TYPE_QZS_L1CA, TYPE_BDS_D1, TYPE_BDS_D2, TYPE_BDS_CNAV1,
+ TYPE_BDS_CNAV2, TYPE_GAL_I, TYPE_GAL_F, TYPE_IRN_L5CA})
public @interface GnssNavigationMessageType {}
// The following enumerations must be in sync with the values declared in gps.h
@@ -54,16 +55,26 @@
public static final int TYPE_GPS_L5CNAV = 0x0103;
/** GPS CNAV-2 message contained in the structure. */
public static final int TYPE_GPS_CNAV2 = 0x0104;
+ /** SBAS message contained in the structure. */
+ public static final int TYPE_SBS = 0x0201;
/** Glonass L1 CA message contained in the structure. */
public static final int TYPE_GLO_L1CA = 0x0301;
+ /** QZSS L1 C/A message contained in the structure. */
+ public static final int TYPE_QZS_L1CA = 0x0401;
/** Beidou D1 message contained in the structure. */
public static final int TYPE_BDS_D1 = 0x0501;
/** Beidou D2 message contained in the structure. */
public static final int TYPE_BDS_D2 = 0x0502;
+ /** Beidou CNAV1 message contained in the structure. */
+ public static final int TYPE_BDS_CNAV1 = 0x0503;
+ /** Beidou CNAV2 message contained in the structure. */
+ public static final int TYPE_BDS_CNAV2 = 0x0504;
/** Galileo I/NAV message contained in the structure. */
public static final int TYPE_GAL_I = 0x0601;
/** Galileo F/NAV message contained in the structure. */
public static final int TYPE_GAL_F = 0x0602;
+ /** IRNSS L5 C/A message contained in the structure. */
+ public static final int TYPE_IRN_L5CA = 0x0701;
/**
* The Navigation Message Status is 'unknown'.
@@ -199,16 +210,26 @@
return "GPS L5-CNAV";
case TYPE_GPS_CNAV2:
return "GPS CNAV2";
+ case TYPE_SBS:
+ return "SBS";
case TYPE_GLO_L1CA:
return "Glonass L1 C/A";
+ case TYPE_QZS_L1CA:
+ return "QZSS L1 C/A";
case TYPE_BDS_D1:
return "Beidou D1";
case TYPE_BDS_D2:
return "Beidou D2";
+ case TYPE_BDS_CNAV1:
+ return "Beidou CNAV1";
+ case TYPE_BDS_CNAV2:
+ return "Beidou CNAV2";
case TYPE_GAL_I:
return "Galileo I";
case TYPE_GAL_F:
return "Galileo F";
+ case TYPE_IRN_L5CA:
+ return "IRNSS L5 C/A";
default:
return "<Invalid:" + mType + ">";
}
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index 89a3bc0..f17fa39 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -189,6 +189,7 @@
* <li>QZSS: 193-200</li>
* <li>Galileo: 1-36</li>
* <li>Beidou: 1-37</li>
+ * <li>IRNSS: 1-14</li>
* </ul>
*
* @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
diff --git a/location/java/com/android/internal/location/ProviderRequest.java b/location/java/com/android/internal/location/ProviderRequest.java
index 8d8df45..572fbc3 100644
--- a/location/java/com/android/internal/location/ProviderRequest.java
+++ b/location/java/com/android/internal/location/ProviderRequest.java
@@ -23,11 +23,10 @@
import android.os.WorkSource;
import android.util.TimeUtils;
-import com.android.internal.util.Preconditions;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/** @hide */
public final class ProviderRequest implements Parcelable {
@@ -76,8 +75,8 @@
this.interval = interval;
this.lowPowerMode = lowPowerMode;
this.locationSettingsIgnored = locationSettingsIgnored;
- this.locationRequests = Preconditions.checkNotNull(locationRequests);
- this.workSource = Preconditions.checkNotNull(workSource);
+ this.locationRequests = Objects.requireNonNull(locationRequests);
+ this.workSource = Objects.requireNonNull(workSource);
}
public static final Parcelable.Creator<ProviderRequest> CREATOR =
@@ -119,6 +118,7 @@
for (LocationRequest request : locationRequests) {
request.writeToParcel(parcel, flags);
}
+ parcel.writeParcelable(workSource, flags);
}
@Override
@@ -155,40 +155,50 @@
return mInterval;
}
- public void setInterval(long interval) {
+ /** Sets the request interval. */
+ public Builder setInterval(long interval) {
this.mInterval = interval;
+ return this;
}
public boolean isLowPowerMode() {
return mLowPowerMode;
}
- public void setLowPowerMode(boolean lowPowerMode) {
+ /** Sets whether low power mode is enabled. */
+ public Builder setLowPowerMode(boolean lowPowerMode) {
this.mLowPowerMode = lowPowerMode;
+ return this;
}
public boolean isLocationSettingsIgnored() {
return mLocationSettingsIgnored;
}
- public void setLocationSettingsIgnored(boolean locationSettingsIgnored) {
+ /** Sets whether location settings should be ignored. */
+ public Builder setLocationSettingsIgnored(boolean locationSettingsIgnored) {
this.mLocationSettingsIgnored = locationSettingsIgnored;
+ return this;
}
public List<LocationRequest> getLocationRequests() {
return mLocationRequests;
}
- public void setLocationRequests(List<LocationRequest> locationRequests) {
- this.mLocationRequests = Preconditions.checkNotNull(locationRequests);
+ /** Sets the {@link LocationRequest}s associated with this request. */
+ public Builder setLocationRequests(List<LocationRequest> locationRequests) {
+ this.mLocationRequests = Objects.requireNonNull(locationRequests);
+ return this;
}
public WorkSource getWorkSource() {
return mWorkSource;
}
- public void setWorkSource(WorkSource workSource) {
- mWorkSource = Preconditions.checkNotNull(workSource);
+ /** Sets the work source. */
+ public Builder setWorkSource(WorkSource workSource) {
+ mWorkSource = Objects.requireNonNull(workSource);
+ return this;
}
/**
diff --git a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
index d12d6b7..b650efc 100644
--- a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
@@ -34,6 +34,7 @@
* of this package for more information.
*/
public final class ProviderRequestUnbundled {
+
private final ProviderRequest mRequest;
/** @hide */
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index cb132f5..6e63d17 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -422,6 +422,40 @@
return AudioFormat.filterPublicFormats(mPort.formats());
}
+ /**
+ * Returns an array of supported encapsulation modes for the device.
+ *
+ * The array can include any of
+ * {@link AudioTrack#ENCAPSULATION_MODE_ELEMENTARY_STREAM},
+ * {@link AudioTrack#ENCAPSULATION_MODE_HANDLE}.
+ *
+ * @return An array of supported encapsulation modes for the device. This
+ * may be an empty array if no encapsulation modes are supported.
+ */
+ public @NonNull int[] getEncapsulationModes() {
+ // Implement a getter in r-dev or r-tv-dev as needed.
+ return new int[0]; // be careful of returning a copy of any internal data.
+ }
+
+ /**
+ * Returns an array of supported encapsulation metadata types for the device.
+ *
+ * The metadata type returned should be allowed for all encapsulation modes supported
+ * by the device. Some metadata types may apply only to certain
+ * compressed stream formats, the returned list is the union of subsets.
+ *
+ * The array can include any of
+ * {@link AudioTrack#ENCAPSULATION_METADATA_TYPE_FRAMEWORK_TUNER},
+ * {@link AudioTrack#ENCAPSULATION_METADATA_TYPE_DVB_AD_DESCRIPTOR}.
+ *
+ * @return An array of supported encapsulation metadata types for the device. This
+ * may be an empty array if no metadata types are supported.
+ */
+ public @NonNull int[] getEncapsulationMetadataTypes() {
+ // Implement a getter in r-dev or r-tv-dev as needed.
+ return new int[0]; // be careful of returning a copy of any internal data.
+ }
+
/**
* @return The device type identifier of the audio device (i.e. TYPE_BUILTIN_SPEAKER).
*/
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 8ad5c04..861b76d 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4567,6 +4567,70 @@
}
/**
+ * @hide
+ * Sets an additional audio output device delay in milliseconds.
+ *
+ * The additional output delay is a request to the output device to
+ * delay audio presentation (generally with respect to video presentation for better
+ * synchronization).
+ * It may not be supported by all output devices,
+ * and typically increases the audio latency by the amount of additional
+ * audio delay requested.
+ *
+ * If additional audio delay is supported by an audio output device,
+ * it is expected to be supported for all output streams (and configurations)
+ * opened on that device.
+ *
+ * @param device an instance of {@link AudioDeviceInfo} returned from {@link getDevices()}.
+ * @param delayMs delay in milliseconds desired. This should be in range of {@code 0}
+ * to the value returned by {@link #getMaxAdditionalOutputDeviceDelay()}.
+ * @return true if successful, false if the device does not support output device delay
+ * or the delay is not in range of {@link #getMaxAdditionalOutputDeviceDelay()}.
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public boolean setAdditionalOutputDeviceDelay(
+ @NonNull AudioDeviceInfo device, @IntRange(from = 0) int delayMs) {
+ Objects.requireNonNull(device);
+ // Implement the setter in r-dev or r-tv-dev as needed.
+ return false;
+ }
+
+ /**
+ * @hide
+ * Returns the current additional audio output device delay in milliseconds.
+ *
+ * @param device an instance of {@link AudioDeviceInfo} returned from {@link getDevices()}.
+ * @return the additional output device delay. This is a non-negative number.
+ * {@code 0} is returned if unsupported.
+ */
+ @SystemApi
+ @IntRange(from = 0)
+ public int getAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) {
+ Objects.requireNonNull(device);
+ // Implement the getter in r-dev or r-tv-dev as needed.
+ return 0;
+ }
+
+ /**
+ * @hide
+ * Returns the maximum additional audio output device delay in milliseconds.
+ *
+ * @param device an instance of {@link AudioDeviceInfo} returned from {@link getDevices()}.
+ * @return the maximum output device delay in milliseconds that can be set.
+ * This is a non-negative number
+ * representing the additional audio delay supported for the device.
+ * {@code 0} is returned if unsupported.
+ */
+ @SystemApi
+ @IntRange(from = 0)
+ public int getMaxAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) {
+ Objects.requireNonNull(device);
+ // Implement the getter in r-dev or r-tv-dev as needed.
+ return 0;
+ }
+
+ /**
* Returns the estimated latency for the given stream type in milliseconds.
*
* DO NOT UNHIDE. The existing approach for doing A/V sync has too many problems. We need
diff --git a/media/java/android/media/AudioPlaybackCaptureConfiguration.java b/media/java/android/media/AudioPlaybackCaptureConfiguration.java
index 453704e..65f2f17 100644
--- a/media/java/android/media/AudioPlaybackCaptureConfiguration.java
+++ b/media/java/android/media/AudioPlaybackCaptureConfiguration.java
@@ -102,6 +102,12 @@
criterion -> criterion.getIntProp());
}
+ /** @return the userId's passed to {@link Builder#addMatchingUserId(int)}. */
+ public @NonNull int[] getMatchingUserIds() {
+ return getIntPredicates(AudioMixingRule.RULE_MATCH_USERID,
+ criterion -> criterion.getIntProp());
+ }
+
/** @return the usages passed to {@link Builder#excludeUsage(int)}. */
@AttributeUsage
public @NonNull int[] getExcludeUsages() {
@@ -115,6 +121,12 @@
criterion -> criterion.getIntProp());
}
+ /** @return the userId's passed to {@link Builder#excludeUserId(int)}. */
+ public @NonNull int[] getExcludeUserIds() {
+ return getIntPredicates(AudioMixingRule.RULE_EXCLUDE_USERID,
+ criterion -> criterion.getIntProp());
+ }
+
private int[] getIntPredicates(int rule,
ToIntFunction<AudioMixMatchCriterion> getPredicate) {
return mAudioMixingRule.getCriteria().stream()
@@ -153,6 +165,7 @@
private final MediaProjection mProjection;
private int mUsageMatchType = MATCH_TYPE_UNSPECIFIED;
private int mUidMatchType = MATCH_TYPE_UNSPECIFIED;
+ private int mUserIdMatchType = MATCH_TYPE_UNSPECIFIED;
/** @param projection A MediaProjection that supports audio projection. */
public Builder(@NonNull MediaProjection projection) {
@@ -202,6 +215,23 @@
}
/**
+ * Only capture audio output by app with the matching {@code userId}.
+ *
+ * <p>If called multiple times, will capture audio output by apps whose userId is any of the
+ * given userId's.
+ *
+ * @throws IllegalStateException if called in conjunction with {@link #excludeUserId(int)}.
+ */
+ public @NonNull Builder addMatchingUserId(int userId) {
+ Preconditions.checkState(
+ mUserIdMatchType != MATCH_TYPE_EXCLUSIVE,
+ ERROR_MESSAGE_MISMATCHED_RULES);
+ mAudioMixingRuleBuilder.addMixRule(AudioMixingRule.RULE_MATCH_USERID, userId);
+ mUserIdMatchType = MATCH_TYPE_INCLUSIVE;
+ return this;
+ }
+
+ /**
* Only capture audio output that does not match the given {@link AudioAttributes}.
*
* <p>If called multiple times, will capture audio output that does not match any of the
@@ -238,6 +268,24 @@
}
/**
+ * Only capture audio output by apps that do not have the matching {@code userId}.
+ *
+ * <p>If called multiple times, will capture audio output by apps whose userId is not any of
+ * the given userId's.
+ *
+ * @throws IllegalStateException if called in conjunction with
+ * {@link #addMatchingUserId(int)}.
+ */
+ public @NonNull Builder excludeUserId(int userId) {
+ Preconditions.checkState(
+ mUserIdMatchType != MATCH_TYPE_INCLUSIVE,
+ ERROR_MESSAGE_MISMATCHED_RULES);
+ mAudioMixingRuleBuilder.excludeMixRule(AudioMixingRule.RULE_MATCH_USERID, userId);
+ mUserIdMatchType = MATCH_TYPE_EXCLUSIVE;
+ return this;
+ }
+
+ /**
* Builds the configuration instance.
*
* @throws UnsupportedOperationException if the parameters set are incompatible.
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 48d27fa..02cb8aa 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1169,6 +1169,13 @@
/** see AudioPolicy.removeUidDeviceAffinities() */
public static native int removeUidDeviceAffinities(int uid);
+ /** see AudioPolicy.setUserIdDeviceAffinities() */
+ public static native int setUserIdDeviceAffinities(int userId, @NonNull int[] types,
+ @NonNull String[] addresses);
+
+ /** see AudioPolicy.removeUserIdDeviceAffinities() */
+ public static native int removeUserIdDeviceAffinities(int userId);
+
public static native int systemReady();
public static native float getStreamVolumeDB(int stream, int index, int device);
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index f566f64..81275f6 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -256,6 +256,103 @@
*/
public static final int ENCAPSULATION_MODE_HANDLE = 2;
+ /* Enumeration of metadata types permitted for use by
+ * encapsulation mode audio streams.
+ */
+ /** @hide */
+ @IntDef(prefix = { "ENCAPSULATION_METADATA_TYPE_" }, value = {
+ ENCAPSULATION_METADATA_TYPE_NONE, /* reserved */
+ ENCAPSULATION_METADATA_TYPE_FRAMEWORK_TUNER,
+ ENCAPSULATION_METADATA_TYPE_DVB_AD_DESCRIPTOR,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EncapsulationMetadataType {}
+
+ /**
+ * Reserved do not use.
+ * @hide
+ */
+ public static final int ENCAPSULATION_METADATA_TYPE_NONE = 0; // reserved
+
+ /**
+ * Encapsulation metadata type for framework tuner information.
+ *
+ * TODO(b/147778408) Link: Fill in Tuner API info.
+ */
+ public static final int ENCAPSULATION_METADATA_TYPE_FRAMEWORK_TUNER = 1;
+
+ /**
+ * Encapsulation metadata type for DVB AD descriptor.
+ *
+ * This metadata is formatted per ETSI TS 101 154 Table E.1: AD_descriptor.
+ */
+ public static final int ENCAPSULATION_METADATA_TYPE_DVB_AD_DESCRIPTOR = 2;
+
+ /* Dual Mono handling is used when a stereo audio stream
+ * contains separate audio content on the left and right channels.
+ * Such information about the content of the stream may be found, for example, in
+ * ITU T-REC-J.94-201610 A.6.2.3 Component descriptor.
+ */
+ /** @hide */
+ @IntDef({
+ DUAL_MONO_MODE_OFF,
+ DUAL_MONO_MODE_LR,
+ DUAL_MONO_MODE_LL,
+ DUAL_MONO_MODE_RR,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DualMonoMode {}
+ // Important: The DUAL_MONO_MODE values must be kept in sync with native header files.
+ /**
+ * This mode disables any Dual Mono presentation effect.
+ *
+ */
+ public static final int DUAL_MONO_MODE_OFF = 0;
+
+ /**
+ * This mode indicates that a stereo stream should be presented
+ * with the left and right audio channels blended together
+ * and delivered to both channels.
+ *
+ * Behavior for non-stereo streams is implementation defined.
+ * A suggested guideline is that the left-right stereo symmetric
+ * channels are pairwise blended;
+ * the other channels such as center are left alone.
+ *
+ * The Dual Mono effect occurs before volume scaling.
+ */
+ public static final int DUAL_MONO_MODE_LR = 1;
+
+ /**
+ * This mode indicates that a stereo stream should be presented
+ * with the left audio channel replicated into the right audio channel.
+ *
+ * Behavior for non-stereo streams is implementation defined.
+ * A suggested guideline is that all channels with left-right
+ * stereo symmetry will have the left channel position replicated
+ * into the right channel position.
+ * The center channels (with no left/right symmetry) or unbalanced
+ * channels are left alone.
+ *
+ * The Dual Mono effect occurs before volume scaling.
+ */
+ public static final int DUAL_MONO_MODE_LL = 2;
+
+ /**
+ * This mode indicates that a stereo stream should be presented
+ * with the right audio channel replicated into the left audio channel.
+ *
+ * Behavior for non-stereo streams is implementation defined.
+ * A suggested guideline is that all channels with left-right
+ * stereo symmetry will have the right channel position replicated
+ * into the left channel position.
+ * The center channels (with no left/right symmetry) or unbalanced
+ * channels are left alone.
+ *
+ * The Dual Mono effect occurs before volume scaling.
+ */
+ public static final int DUAL_MONO_MODE_RR = 3;
+
/** @hide */
@IntDef({
WRITE_BLOCKING,
@@ -1355,6 +1452,140 @@
attributes.getContentType(), attributes.getUsage(), attributes.getFlags());
}
+ /*
+ * The MAX_LEVEL should be exactly representable by an IEEE 754-2008 base32 float.
+ * This means fractions must be divisible by a power of 2. For example,
+ * 10.25f is OK as 0.25 is 1/4, but 10.1f is NOT OK as 1/10 is not expressable by
+ * a finite binary fraction.
+ *
+ * 48.f is the nominal max for API level {@link android os.Build.VERSION_CODES#R}.
+ * We use this to suggest a baseline range for implementation.
+ *
+ * The API contract specification allows increasing this value in a future
+ * API release, but not decreasing this value.
+ */
+ private static final float MAX_AUDIO_DESCRIPTION_MIX_LEVEL = 48.f;
+
+ private static boolean isValidAudioDescriptionMixLevel(float level) {
+ return !(Float.isNaN(level) || level > MAX_AUDIO_DESCRIPTION_MIX_LEVEL);
+ }
+
+ /**
+ * Sets the Audio Description mix level in dB.
+ *
+ * For AudioTracks incorporating a secondary Audio Description stream
+ * (where such contents may be sent through an Encapsulation Mode
+ * {@link #ENCAPSULATION_MODE_ELEMENTARY_STREAM} or {@link #ENCAPSULATION_MODE_HANDLE}
+ * or internally by a HW channel),
+ * the level of mixing of the Audio Description to the Main Audio stream
+ * is controlled by this method.
+ *
+ * Such mixing occurs <strong>prior</strong> to overall volume scaling.
+ *
+ * @param level a floating point value between
+ * {@code Float.NEGATIVE_INFINITY} to {@code +48.f},
+ * where {@code Float.NEGATIVE_INFINITY} means the Audio Description is not mixed
+ * and a level of {@code 0.f} means the Audio Description is mixed without scaling.
+ * @return true on success, false on failure.
+ */
+ public boolean setAudioDescriptionMixLeveldB(
+ @FloatRange(to = 48.f, toInclusive = true) float level) {
+ if (!isValidAudioDescriptionMixLevel(level)) {
+ throw new IllegalArgumentException("level is out of range" + level);
+ }
+ return native_set_audio_description_mix_level_db(level) == SUCCESS;
+ }
+
+ /**
+ * Returns the Audio Description mix level in dB.
+ *
+ * If Audio Description mixing is unavailable from the hardware device,
+ * a value of {@code Float.NEGATIVE_INFINITY} is returned.
+ *
+ * @return the current Audio Description Mix Level in dB.
+ * A value of {@code Float.NEGATIVE_INFINITY} means
+ * that the audio description is not mixed or
+ * the hardware is not available.
+ * This should reflect the <strong>true</strong> internal device mix level;
+ * hence the application might receive any floating value
+ * except {@code Float.NaN}.
+ */
+ public float getAudioDescriptionMixLeveldB() {
+ float[] level = { Float.NEGATIVE_INFINITY };
+ try {
+ final int status = native_get_audio_description_mix_level_db(level);
+ if (status != SUCCESS || Float.isNaN(level[0])) {
+ return Float.NEGATIVE_INFINITY;
+ }
+ } catch (Exception e) {
+ return Float.NEGATIVE_INFINITY;
+ }
+ return level[0];
+ }
+
+ private static boolean isValidDualMonoMode(@DualMonoMode int dualMonoMode) {
+ switch (dualMonoMode) {
+ case DUAL_MONO_MODE_OFF:
+ case DUAL_MONO_MODE_LR:
+ case DUAL_MONO_MODE_LL:
+ case DUAL_MONO_MODE_RR:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Sets the Dual Mono mode presentation on the output device.
+ *
+ * The Dual Mono mode is generally applied to stereo audio streams
+ * where the left and right channels come from separate sources.
+ *
+ * For compressed audio, where the decoding is done in hardware,
+ * Dual Mono presentation needs to be performed
+ * by the hardware output device
+ * as the PCM audio is not available to the framework.
+ *
+ * @param dualMonoMode one of {@link #DUAL_MONO_MODE_OFF},
+ * {@link #DUAL_MONO_MODE_LR},
+ * {@link #DUAL_MONO_MODE_LL},
+ * {@link #DUAL_MONO_MODE_RR}.
+ *
+ * @return true on success, false on failure if the output device
+ * does not support Dual Mono mode.
+ */
+ public boolean setDualMonoMode(@DualMonoMode int dualMonoMode) {
+ if (!isValidDualMonoMode(dualMonoMode)) {
+ throw new IllegalArgumentException(
+ "Invalid Dual Mono mode " + dualMonoMode);
+ }
+ return native_set_dual_mono_mode(dualMonoMode) == SUCCESS;
+ }
+
+ /**
+ * Returns the Dual Mono mode presentation setting.
+ *
+ * If no Dual Mono presentation is available for the output device,
+ * then {@link #DUAL_MONO_MODE_OFF} is returned.
+ *
+ * @return one of {@link #DUAL_MONO_MODE_OFF},
+ * {@link #DUAL_MONO_MODE_LR},
+ * {@link #DUAL_MONO_MODE_LL},
+ * {@link #DUAL_MONO_MODE_RR}.
+ */
+ public @DualMonoMode int getDualMonoMode() {
+ int[] dualMonoMode = { DUAL_MONO_MODE_OFF };
+ try {
+ final int status = native_get_dual_mono_mode(dualMonoMode);
+ if (status != SUCCESS || !isValidDualMonoMode(dualMonoMode[0])) {
+ return DUAL_MONO_MODE_OFF;
+ }
+ } catch (Exception e) {
+ return DUAL_MONO_MODE_OFF;
+ }
+ return dualMonoMode[0];
+ }
+
// mask of all the positional channels supported, however the allowed combinations
// are further restricted by the matching left/right rule and
// AudioSystem.OUT_CHANNEL_COUNT_MAX
@@ -3947,6 +4178,11 @@
private native void native_set_delay_padding(int delayInFrames, int paddingInFrames);
+ private native int native_set_audio_description_mix_level_db(float level);
+ private native int native_get_audio_description_mix_level_db(float[] level);
+ private native int native_set_dual_mono_mode(int dualMonoMode);
+ private native int native_get_dual_mono_mode(int[] dualMonoMode);
+
//---------------------------------------------------------
// Utility methods
//------------------
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 7f1c692..1f97be5 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -266,6 +266,10 @@
int removeUidDeviceAffinity(in IAudioPolicyCallback pcb, in int uid);
+ int setUserIdDeviceAffinity(in IAudioPolicyCallback pcb, in int userId, in int[] deviceTypes,
+ in String[] deviceAddresses);
+ int removeUserIdDeviceAffinity(in IAudioPolicyCallback pcb, in int userId);
+
boolean hasHapticChannels(in Uri uri);
boolean isCallScreeningModeSupported();
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index f780d40..abc7e0b 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -23,6 +23,7 @@
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
+import android.hardware.HardwareBuffer;
import android.media.MediaCodecInfo.CodecCapabilities;
import android.os.Build;
import android.os.Bundle;
@@ -39,9 +40,13 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ReadOnlyBufferException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -1736,7 +1741,25 @@
{
int index = msg.arg2;
synchronized(mBufferLock) {
- validateInputByteBuffer(mCachedInputBuffers, index);
+ switch (mBufferMode) {
+ case BUFFER_MODE_LEGACY:
+ validateInputByteBuffer(mCachedInputBuffers, index);
+ break;
+ case BUFFER_MODE_BLOCK:
+ while (mQueueRequests.size() <= index) {
+ mQueueRequests.add(null);
+ }
+ QueueRequest request = mQueueRequests.get(index);
+ if (request == null) {
+ request = new QueueRequest(mCodec, index);
+ mQueueRequests.set(index, request);
+ }
+ request.setAccessible(true);
+ break;
+ default:
+ throw new IllegalStateException(
+ "Unrecognized buffer mode: " + mBufferMode);
+ }
}
mCallback.onInputBufferAvailable(mCodec, index);
break;
@@ -1747,7 +1770,26 @@
int index = msg.arg2;
BufferInfo info = (MediaCodec.BufferInfo) msg.obj;
synchronized(mBufferLock) {
- validateOutputByteBuffer(mCachedOutputBuffers, index, info);
+ switch (mBufferMode) {
+ case BUFFER_MODE_LEGACY:
+ validateOutputByteBuffer(mCachedOutputBuffers, index, info);
+ break;
+ case BUFFER_MODE_BLOCK:
+ while (mOutputFrames.size() <= index) {
+ mOutputFrames.add(null);
+ }
+ OutputFrame frame = mOutputFrames.get(index);
+ if (frame == null) {
+ frame = new OutputFrame(index);
+ mOutputFrames.set(index, frame);
+ }
+ frame.setBufferInfo(info);
+ frame.setAccessible(true);
+ break;
+ default:
+ throw new IllegalStateException(
+ "Unrecognized buffer mode: " + mBufferMode);
+ }
}
mCallback.onOutputBufferAvailable(
mCodec, index, info);
@@ -1913,8 +1955,33 @@
*/
public static final int CONFIGURE_FLAG_ENCODE = 1;
+ /**
+ * If this codec is to be used with {@link LinearBlock} and/or {@link
+ * GraphicBlock}, pass this flag.
+ * <p>
+ * When this flag is set, the following APIs throw IllegalStateException.
+ * <ul>
+ * <li>{@link #getInputBuffer}
+ * <li>{@link #getInputImage}
+ * <li>{@link #getInputBuffers}
+ * <li>{@link #getOutputBuffer}
+ * <li>{@link #getOutputImage}
+ * <li>{@link #getOutputBuffers}
+ * <li>{@link #queueInputBuffer}
+ * <li>{@link #queueSecureInputBuffer}
+ * <li>{@link #dequeueInputBuffer}
+ * <li>{@link #dequeueOutputBuffer}
+ * </ul>
+ */
+ public static final int CONFIGURE_FLAG_USE_BLOCK_MODEL = 2;
+
/** @hide */
- @IntDef(flag = true, value = { CONFIGURE_FLAG_ENCODE })
+ @IntDef(
+ flag = true,
+ value = {
+ CONFIGURE_FLAG_ENCODE,
+ CONFIGURE_FLAG_USE_BLOCK_MODEL,
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface ConfigureFlag {}
@@ -1984,6 +2051,11 @@
descrambler != null ? descrambler.getBinder() : null, flags);
}
+ private static final int BUFFER_MODE_INVALID = -1;
+ private static final int BUFFER_MODE_LEGACY = 0;
+ private static final int BUFFER_MODE_BLOCK = 1;
+ private int mBufferMode = BUFFER_MODE_INVALID;
+
private void configure(
@Nullable MediaFormat format, @Nullable Surface surface,
@Nullable MediaCrypto crypto, @Nullable IHwBinder descramblerBinder,
@@ -2022,6 +2094,13 @@
mHasSurface = surface != null;
mCrypto = crypto;
+ synchronized (mBufferLock) {
+ if ((flags & CONFIGURE_FLAG_USE_BLOCK_MODEL) != 0) {
+ mBufferMode = BUFFER_MODE_BLOCK;
+ } else {
+ mBufferMode = BUFFER_MODE_LEGACY;
+ }
+ }
native_configure(keys, values, surface, crypto, descramblerBinder, flags);
}
@@ -2446,6 +2525,9 @@
int offset, int size, long presentationTimeUs, int flags)
throws CryptoException {
synchronized(mBufferLock) {
+ if (mBufferMode != BUFFER_MODE_LEGACY) {
+ throw new IllegalStateException();
+ }
invalidateByteBuffer(mCachedInputBuffers, index);
mDequeuedInputBuffers.remove(index);
}
@@ -2695,6 +2777,9 @@
long presentationTimeUs,
int flags) throws CryptoException {
synchronized(mBufferLock) {
+ if (mBufferMode != BUFFER_MODE_LEGACY) {
+ throw new IllegalStateException();
+ }
invalidateByteBuffer(mCachedInputBuffers, index);
mDequeuedInputBuffers.remove(index);
}
@@ -2726,6 +2811,11 @@
* @throws MediaCodec.CodecException upon codec error.
*/
public final int dequeueInputBuffer(long timeoutUs) {
+ synchronized (mBufferLock) {
+ if (mBufferMode != BUFFER_MODE_LEGACY) {
+ throw new IllegalStateException();
+ }
+ }
int res = native_dequeueInputBuffer(timeoutUs);
if (res >= 0) {
synchronized(mBufferLock) {
@@ -2738,6 +2828,654 @@
private native final int native_dequeueInputBuffer(long timeoutUs);
/**
+ * Section of memory that represents a linear block. Applications may
+ * acquire a block via {@link LinearBlock#obtain} and queue all or part
+ * of the block as an input buffer to a codec, or get a block allocated by
+ * codec as an output buffer from {@link OutputFrame}.
+ *
+ * {@see QueueRequest#setLinearBlock}
+ * {@see QueueRequest#setEncryptedLinearBlock}
+ * {@see OutputFrame#getLinearBlock}
+ */
+ public static final class LinearBlock {
+ // No public constructors.
+ private LinearBlock() {}
+
+ /**
+ * Returns true if the buffer is mappable.
+ * @throws IllegalStateException if invalid
+ */
+ public boolean isMappable() {
+ synchronized (mLock) {
+ if (!mValid) {
+ throw new IllegalStateException();
+ }
+ return mMappable;
+ }
+ }
+
+ /**
+ * Map the memory and return the mapped region.
+ * <p>
+ * The returned memory region becomes inaccessible after
+ * {@link #recycle}, or the buffer is queued to the codecs and not
+ * returned to the client yet.
+ *
+ * @return mapped memory region as {@link ByteBuffer} object
+ * @throws IllegalStateException if not mappable or invalid
+ */
+ public @NonNull ByteBuffer map() {
+ synchronized (mLock) {
+ if (!mValid) {
+ throw new IllegalStateException();
+ }
+ if (!mMappable) {
+ throw new IllegalStateException();
+ }
+ if (mMapped == null) {
+ mMapped = native_map();
+ }
+ return mMapped;
+ }
+ }
+
+ private native ByteBuffer native_map();
+
+ /**
+ * Mark this block as ready to be recycled by the framework once it is
+ * no longer in use. All operations to this object after
+ * this call will cause exceptions, as well as attempt to access the
+ * previously mapped memory region. Caller should clear all references
+ * to this object after this call.
+ * <p>
+ * To avoid excessive memory consumption, it is recommended that callers
+ * recycle buffers as soon as they no longer need the buffers
+ *
+ * @throws IllegalStateException if invalid
+ */
+ public void recycle() {
+ synchronized (mLock) {
+ if (!mValid) {
+ throw new IllegalStateException();
+ }
+ if (mMapped != null) {
+ mMapped.setAccessible(false);
+ mMapped = null;
+ }
+ native_recycle();
+ mValid = false;
+ mNativeContext = 0;
+ }
+ sPool.offer(this);
+ }
+
+ private native void native_recycle();
+
+ private native void native_obtain(int capacity, String[] codecNames);
+
+ @Override
+ protected void finalize() {
+ native_recycle();
+ }
+
+ /**
+ * Returns true if it is possible to allocate a linear block that can be
+ * passed to all listed codecs as input buffers without copying the
+ * content.
+ * <p>
+ * Note that even if this function returns true, {@link #obtain} may
+ * still throw due to invalid arguments or allocation failure.
+ *
+ * @param codecNames list of codecs that the client wants to use a
+ * linear block without copying. Null entries are
+ * ignored.
+ */
+ public static boolean isCodecCopyFreeCompatible(@NonNull String[] codecNames) {
+ return native_checkCompatible(codecNames);
+ }
+
+ private static native boolean native_checkCompatible(@NonNull String[] codecNames);
+
+ /**
+ * Obtain a linear block object no smaller than {@code capacity}.
+ * If {@link #isCodecCopyFreeCompatible} with the same
+ * {@code codecNames} returned true, the returned
+ * {@link LinearBlock} object can be queued to the listed codecs without
+ * copying. The returned {@link LinearBlock} object is always
+ * read/write mappable.
+ *
+ * @param capacity requested capacity of the linear block in bytes
+ * @param codecNames list of codecs that the client wants to use this
+ * linear block without copying. Null entries are
+ * ignored.
+ * @return a linear block object.
+ * @throws IllegalArgumentException if the capacity is invalid or
+ * codecNames contains invalid name
+ * @throws IOException if an error occurred while allocating a buffer
+ */
+ public static @Nullable LinearBlock obtain(
+ int capacity, @NonNull String[] codecNames) {
+ LinearBlock buffer = sPool.poll();
+ if (buffer == null) {
+ buffer = new LinearBlock();
+ }
+ synchronized (buffer.mLock) {
+ buffer.native_obtain(capacity, codecNames);
+ }
+ return buffer;
+ }
+
+ // Called from native
+ private void setInternalStateLocked(long context, boolean isMappable) {
+ mNativeContext = context;
+ mMappable = isMappable;
+ mValid = (context != 0);
+ }
+
+ private static final BlockingQueue<LinearBlock> sPool =
+ new LinkedBlockingQueue<>();
+
+ private final Object mLock = new Object();
+ private boolean mValid = false;
+ private boolean mMappable = false;
+ private ByteBuffer mMapped = null;
+ private long mNativeContext = 0;
+ }
+
+ /**
+ * Section of memory that represents a graphic block. Applications may
+ * acquire a block via {@link GraphicBlock#obtain} and queue
+ * the block as an input buffer to a codec, or get a block allocated by
+ * codec as an output buffer from {@link OutputFrame}.
+ *
+ * {@see QueueRequest#setGraphicBlock}
+ * {@see OutputFrame#getGraphicBlock}
+ */
+ public static final class GraphicBlock {
+ // No public constructors.
+ private GraphicBlock() {}
+
+ /**
+ * Returns true if the buffer is mappable.
+ * @throws IllegalStateException if invalid
+ */
+ public boolean isMappable() {
+ synchronized (mLock) {
+ if (!mValid) {
+ throw new IllegalStateException();
+ }
+ return mMappable;
+ }
+ }
+
+ /**
+ * Map the memory and return the mapped region.
+ * <p>
+ * Calling {@link #recycle} or
+ * {@link QueueRequest#setGraphicBlock} causes the returned
+ * {@link Image} object to be closed, if not already.
+ *
+ * @return mapped memory region as {@link Image} object
+ * @throws IllegalStateException if not mappable or invalid
+ */
+ public @NonNull Image map() {
+ synchronized (mLock) {
+ if (!mValid) {
+ throw new IllegalStateException();
+ }
+ if (!mMappable) {
+ throw new IllegalStateException();
+ }
+ if (mMapped == null) {
+ mMapped = native_map();
+ }
+ return mMapped;
+ }
+ }
+
+ private native Image native_map();
+
+ /**
+ * Mark this block as ready to be recycled by the framework once it is
+ * no longer in use. All operations to this object after
+ * this call will cause exceptions, as well as attempt to access the
+ * previously mapped memory region. Caller should clear all references
+ * to this object after this call.
+ * <p>
+ * To avoid excessive memory consumption, it is recommended that callers
+ * recycle buffers as soon as they no longer need the buffers.
+ *
+ * @throws IllegalStateException if invalid
+ */
+ public void recycle() {
+ synchronized (mLock) {
+ if (!mValid) {
+ throw new IllegalStateException();
+ }
+ if (mMapped != null) {
+ mMapped.close();
+ mMapped = null;
+ }
+ native_recycle();
+ mValid = false;
+ mNativeContext = 0;
+ }
+ sPool.offer(this);
+ }
+
+ private native void native_recycle();
+
+ /**
+ * Returns true if it is possible to allocate a graphic block that
+ * can be passed to all listed codecs as an input buffer without
+ * copying.
+ * <p>
+ * Note that even if this function returns true, {@link #obtain}
+ * may still throw due to invalid arguments or allocation failure.
+ * In addition, choosing a format that is not natively supported by the
+ * codec may cause color conversion.
+ *
+ * @param codecNames list of codecs that the client wants to use a
+ * graphic block without copying. Null entries are
+ * ignored.
+ */
+ public static boolean isCodecCopyFreeCompatible(@NonNull String[] codecNames) {
+ return native_checkCompatible(codecNames);
+ }
+
+ private static native boolean native_checkCompatible(@NonNull String[] codecNames);
+
+ // Called from native
+ private void setInternalStateLocked(long context, boolean isMappable) {
+ mNativeContext = context;
+ mMappable = isMappable;
+ mValid = (context != 0);
+ }
+
+ private static final BlockingQueue<GraphicBlock> sPool =
+ new LinkedBlockingQueue<>();
+
+ /**
+ * Obtain a graphic block object of dimension
+ * {@code width}x{@code height}.
+ * If {@link #isCodecCopyFreeCompatible} with the same
+ * {@code codecNames} returned true, the returned
+ * {@link GraphicBlock} object can be queued to the listed codecs
+ * without copying. The returned {@link GraphicBlock} object is always
+ * read/write mappable.
+ *
+ * @param width requested width of the graphic block
+ * @param height requested height of the graphic block
+ * @param format the format of pixels. One of the {@code COLOR_Format}
+ * values from {@link MediaCodecInfo.CodecCapabilities}.
+ * @param usage the usage of the buffer. @HardwareBuffer.Usage
+ * @param codecNames list of codecs that the client wants to use this
+ * graphic block without copying. Null entries are
+ * ignored.
+ * @return a graphic block object.
+ * @throws IllegalArgumentException if the parameters are invalid or
+ * not supported
+ * @throws IOException if an error occurred while allocating a buffer
+ */
+ public static @NonNull GraphicBlock obtain(
+ int width,
+ int height,
+ int format,
+ @HardwareBuffer.Usage long usage,
+ @NonNull String[] codecNames) {
+ GraphicBlock buffer = sPool.poll();
+ if (buffer == null) {
+ buffer = new GraphicBlock();
+ }
+ if (width < 0 || height < 0) {
+ throw new IllegalArgumentException();
+ }
+ synchronized (buffer.mLock) {
+ buffer.native_obtain(width, height, format, usage, codecNames);
+ }
+ return buffer;
+ }
+
+ private native void native_obtain(
+ int width,
+ int height,
+ int format,
+ @HardwareBuffer.Usage long usage,
+ @NonNull String[] codecNames);
+
+ @Override
+ protected void finalize() {
+ native_recycle();
+ }
+
+ private final Object mLock = new Object();
+ private boolean mValid = false;
+ private boolean mMappable = false;
+ private Image mMapped = null;
+ private long mNativeContext = 0;
+ }
+
+ /**
+ * Builder-like class for queue requests. Use this class to prepare a
+ * queue request and send it.
+ */
+ public final class QueueRequest {
+ // No public constructor
+ private QueueRequest(@NonNull MediaCodec codec, int index) {
+ mCodec = codec;
+ mIndex = index;
+ }
+
+ /**
+ * Set a linear block to this queue request. Exactly one buffer must be
+ * set for a queue request before calling {@link #queue}. It is possible
+ * to use the same {@link LinearBlock} object for multiple queue
+ * requests. The behavior is undefined if the range of the buffer
+ * overlaps for multiple requests, or the application writes into the
+ * region being processed by the codec.
+ *
+ * @param block The linear block object
+ * @param offset The byte offset into the input buffer at which the data starts.
+ * @param size The number of bytes of valid input data.
+ * @param presentationTimeUs The presentation timestamp in microseconds for this
+ * buffer. This is normally the media time at which this
+ * buffer should be presented (rendered). When using an output
+ * surface, this will be propagated as the {@link
+ * SurfaceTexture#getTimestamp timestamp} for the frame (after
+ * conversion to nanoseconds).
+ * @param flags A bitmask of flags
+ * {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}.
+ * While not prohibited, most codecs do not use the
+ * {@link #BUFFER_FLAG_KEY_FRAME} flag for input buffers.
+ * @return this object
+ * @throws IllegalStateException if a buffer is already set
+ */
+ public @NonNull QueueRequest setLinearBlock(
+ @NonNull LinearBlock block,
+ int offset,
+ int size,
+ long presentationTimeUs,
+ @BufferFlag int flags) {
+ if (!isAccessible()) {
+ throw new IllegalStateException();
+ }
+ if (mLinearBlock != null || mGraphicBlock != null) {
+ throw new IllegalStateException();
+ }
+ mLinearBlock = block;
+ mOffset = offset;
+ mSize = size;
+ mPresentationTimeUs = presentationTimeUs;
+ mFlags = flags;
+ return this;
+ }
+
+ /**
+ * Set an encrypted linear block to this queue request. Exactly one
+ * buffer must be set for a queue request before calling {@link #queue}.
+ *
+ * @param block The linear block object
+ * @param offset The byte offset into the input buffer at which the data starts.
+ * @param presentationTimeUs The presentation timestamp in microseconds for this
+ * buffer. This is normally the media time at which this
+ * buffer should be presented (rendered). When using an output
+ * surface, this will be propagated as the {@link
+ * SurfaceTexture#getTimestamp timestamp} for the frame (after
+ * conversion to nanoseconds).
+ * @param cryptoInfo Metadata describing the structure of the encrypted input sample.
+ * @param flags A bitmask of flags
+ * {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}.
+ * While not prohibited, most codecs do not use the
+ * {@link #BUFFER_FLAG_KEY_FRAME} flag for input buffers.
+ * @return this object
+ * @throws IllegalStateException if a buffer is already set
+ */
+ public @NonNull QueueRequest setEncryptedLinearBlock(
+ @NonNull LinearBlock block,
+ int offset,
+ @NonNull MediaCodec.CryptoInfo cryptoInfo,
+ long presentationTimeUs,
+ @BufferFlag int flags) {
+ if (!isAccessible()) {
+ throw new IllegalStateException();
+ }
+ if (mLinearBlock != null || mGraphicBlock != null) {
+ throw new IllegalStateException();
+ }
+ mLinearBlock = block;
+ mOffset = offset;
+ mCryptoInfo = cryptoInfo;
+ mPresentationTimeUs = presentationTimeUs;
+ mFlags = flags;
+ return this;
+ }
+
+ /**
+ * Set a graphic block to this queue request. Exactly one buffer must
+ * be set for a queue request before calling {@link #queue}.
+ *
+ * @param block The graphic block object
+ * @param presentationTimeUs The presentation timestamp in microseconds for this
+ * buffer. This is normally the media time at which this
+ * buffer should be presented (rendered). When using an output
+ * surface, this will be propagated as the {@link
+ * SurfaceTexture#getTimestamp timestamp} for the frame (after
+ * conversion to nanoseconds).
+ * @param flags A bitmask of flags
+ * {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}.
+ * While not prohibited, most codecs do not use the
+ * {@link #BUFFER_FLAG_KEY_FRAME} flag for input buffers.
+ * @return this object
+ * @throws IllegalStateException if a buffer is already set
+ */
+ public @NonNull QueueRequest setGraphicBlock(
+ @NonNull GraphicBlock block,
+ long presentationTimeUs,
+ @BufferFlag int flags) {
+ if (!isAccessible()) {
+ throw new IllegalStateException();
+ }
+ if (mLinearBlock != null || mGraphicBlock != null) {
+ throw new IllegalStateException();
+ }
+ mGraphicBlock = block;
+ mPresentationTimeUs = presentationTimeUs;
+ mFlags = flags;
+ return this;
+ }
+
+ /**
+ * Add a integer parameter. See {@link MediaFormat} for the list of
+ * supported tunings. If there was {@link MediaCodec#setParameters}
+ * call with the same key which is not processed by the codec yet, the
+ * value set from this method will override the unprocessed value.
+ */
+ public @NonNull QueueRequest setIntegerParameter(
+ @NonNull String key, int value) {
+ if (!isAccessible()) {
+ throw new IllegalStateException();
+ }
+ mTuningKeys.add(key);
+ mTuningValues.add(Integer.valueOf(value));
+ return this;
+ }
+
+ /**
+ * Add a long parameter. See {@link MediaFormat} for the list of
+ * supported tunings. If there was {@link MediaCodec#setParameters}
+ * call with the same key which is not processed by the codec yet, the
+ * value set from this method will override the unprocessed value.
+ */
+ public @NonNull QueueRequest setLongParameter(
+ @NonNull String key, long value) {
+ if (!isAccessible()) {
+ throw new IllegalStateException();
+ }
+ mTuningKeys.add(key);
+ mTuningValues.add(Long.valueOf(value));
+ return this;
+ }
+
+ /**
+ * Add a float parameter. See {@link MediaFormat} for the list of
+ * supported tunings. If there was {@link MediaCodec#setParameters}
+ * call with the same key which is not processed by the codec yet, the
+ * value set from this method will override the unprocessed value.
+ */
+ public @NonNull QueueRequest setFloatParameter(
+ @NonNull String key, float value) {
+ if (!isAccessible()) {
+ throw new IllegalStateException();
+ }
+ mTuningKeys.add(key);
+ mTuningValues.add(Float.valueOf(value));
+ return this;
+ }
+
+ /**
+ * Add a {@link ByteBuffer} parameter. See {@link MediaFormat} for the list of
+ * supported tunings. If there was {@link MediaCodec#setParameters}
+ * call with the same key which is not processed by the codec yet, the
+ * value set from this method will override the unprocessed value.
+ */
+ public @NonNull QueueRequest setByteBufferParameter(
+ @NonNull String key, @NonNull ByteBuffer value) {
+ if (!isAccessible()) {
+ throw new IllegalStateException();
+ }
+ mTuningKeys.add(key);
+ mTuningValues.add(value);
+ return this;
+ }
+
+ /**
+ * Add a string parameter. See {@link MediaFormat} for the list of
+ * supported tunings. If there was {@link MediaCodec#setParameters}
+ * call with the same key which is not processed by the codec yet, the
+ * value set from this method will override the unprocessed value.
+ */
+ public @NonNull QueueRequest setStringParameter(
+ @NonNull String key, @NonNull String value) {
+ if (!isAccessible()) {
+ throw new IllegalStateException();
+ }
+ mTuningKeys.add(key);
+ mTuningValues.add(value);
+ return this;
+ }
+
+ /**
+ * Finish building a queue request and queue the buffers with tunings.
+ */
+ public void queue() {
+ if (!isAccessible()) {
+ throw new IllegalStateException();
+ }
+ if (mLinearBlock == null && mGraphicBlock == null) {
+ throw new IllegalStateException();
+ }
+ setAccessible(false);
+ if (mLinearBlock != null) {
+ mCodec.native_queueLinearBlock(
+ mIndex, mLinearBlock, mOffset, mSize, mCryptoInfo,
+ mPresentationTimeUs, mFlags,
+ mTuningKeys, mTuningValues);
+ } else if (mGraphicBlock != null) {
+ mCodec.native_queueGraphicBlock(
+ mIndex, mGraphicBlock, mPresentationTimeUs, mFlags,
+ mTuningKeys, mTuningValues);
+ }
+ clear();
+ }
+
+ @NonNull QueueRequest clear() {
+ mLinearBlock = null;
+ mOffset = 0;
+ mSize = 0;
+ mCryptoInfo = null;
+ mGraphicBlock = null;
+ mPresentationTimeUs = 0;
+ mFlags = 0;
+ mTuningKeys.clear();
+ mTuningValues.clear();
+ return this;
+ }
+
+ boolean isAccessible() {
+ return mAccessible;
+ }
+
+ @NonNull QueueRequest setAccessible(boolean accessible) {
+ mAccessible = accessible;
+ return this;
+ }
+
+ private final MediaCodec mCodec;
+ private final int mIndex;
+ private LinearBlock mLinearBlock = null;
+ private int mOffset = 0;
+ private int mSize = 0;
+ private MediaCodec.CryptoInfo mCryptoInfo = null;
+ private GraphicBlock mGraphicBlock = null;
+ private long mPresentationTimeUs = 0;
+ private @BufferFlag int mFlags = 0;
+ private final ArrayList<String> mTuningKeys = new ArrayList<>();
+ private final ArrayList<Object> mTuningValues = new ArrayList<>();
+
+ private boolean mAccessible = false;
+ }
+
+ private native void native_queueLinearBlock(
+ int index,
+ @NonNull LinearBlock block,
+ int offset,
+ int size,
+ @Nullable CryptoInfo cryptoInfo,
+ long presentationTimeUs,
+ int flags,
+ @NonNull ArrayList<String> keys,
+ @NonNull ArrayList<Object> values);
+
+ private native void native_queueGraphicBlock(
+ int index,
+ @NonNull GraphicBlock block,
+ long presentationTimeUs,
+ int flags,
+ @NonNull ArrayList<String> keys,
+ @NonNull ArrayList<Object> values);
+
+ private final ArrayList<QueueRequest> mQueueRequests = new ArrayList<>();
+
+ /**
+ * Return a clear {@link QueueRequest} object for an input slot index.
+ *
+ * @param index input slot index from
+ * {@link Callback#onInputBufferAvailable}
+ * @return queue request object
+ * @throws IllegalStateException if not using block model
+ * @throws IllegalArgumentException if the input slot is not available or
+ * the index is out of range
+ */
+ public @NonNull QueueRequest getQueueRequest(int index) {
+ synchronized (mBufferLock) {
+ if (mBufferMode != BUFFER_MODE_BLOCK) {
+ throw new IllegalStateException();
+ }
+ if (index < 0 || index >= mQueueRequests.size()) {
+ throw new IllegalArgumentException();
+ }
+ QueueRequest request = mQueueRequests.get(index);
+ if (request == null) {
+ throw new IllegalArgumentException();
+ }
+ if (!request.isAccessible()) {
+ throw new IllegalArgumentException();
+ }
+ return request.clear();
+ }
+ }
+
+ /**
* If a non-negative timeout had been specified in the call
* to {@link #dequeueOutputBuffer}, indicates that the call timed out.
*/
@@ -2789,8 +3527,13 @@
@OutputBufferInfo
public final int dequeueOutputBuffer(
@NonNull BufferInfo info, long timeoutUs) {
+ synchronized (mBufferLock) {
+ if (mBufferMode != BUFFER_MODE_LEGACY) {
+ throw new IllegalStateException();
+ }
+ }
int res = native_dequeueOutputBuffer(info, timeoutUs);
- synchronized(mBufferLock) {
+ synchronized (mBufferLock) {
if (res == INFO_OUTPUT_BUFFERS_CHANGED) {
cacheBuffers(false /* input */);
} else if (res >= 0) {
@@ -2826,15 +3569,7 @@
* @throws MediaCodec.CodecException upon codec error.
*/
public final void releaseOutputBuffer(int index, boolean render) {
- BufferInfo info = null;
- synchronized(mBufferLock) {
- invalidateByteBuffer(mCachedOutputBuffers, index);
- mDequeuedOutputBuffers.remove(index);
- if (mHasSurface) {
- info = mDequeuedOutputInfos.remove(index);
- }
- }
- releaseOutputBuffer(index, render, false /* updatePTS */, 0 /* dummy */);
+ releaseOutputBufferInternal(index, render, false /* updatePTS */, 0 /* dummy */);
}
/**
@@ -2887,16 +3622,33 @@
* @throws MediaCodec.CodecException upon codec error.
*/
public final void releaseOutputBuffer(int index, long renderTimestampNs) {
+ releaseOutputBufferInternal(
+ index, true /* render */, true /* updatePTS */, renderTimestampNs);
+ }
+
+ private void releaseOutputBufferInternal(
+ int index, boolean render, boolean updatePts, long renderTimestampNs) {
BufferInfo info = null;
synchronized(mBufferLock) {
- invalidateByteBuffer(mCachedOutputBuffers, index);
- mDequeuedOutputBuffers.remove(index);
- if (mHasSurface) {
- info = mDequeuedOutputInfos.remove(index);
+ switch (mBufferMode) {
+ case BUFFER_MODE_LEGACY:
+ invalidateByteBuffer(mCachedOutputBuffers, index);
+ mDequeuedOutputBuffers.remove(index);
+ if (mHasSurface) {
+ info = mDequeuedOutputInfos.remove(index);
+ }
+ break;
+ case BUFFER_MODE_BLOCK:
+ OutputFrame frame = mOutputFrames.get(index);
+ frame.setAccessible(false);
+ frame.clear();
+ break;
+ default:
+ throw new IllegalStateException();
}
}
releaseOutputBuffer(
- index, true /* render */, true /* updatePTS */, renderTimestampNs);
+ index, render, updatePts, renderTimestampNs);
}
@UnsupportedAppUsage
@@ -3116,6 +3868,8 @@
mCachedOutputBuffers = null;
mDequeuedInputBuffers.clear();
mDequeuedOutputBuffers.clear();
+ mQueueRequests.clear();
+ mOutputFrames.clear();
}
}
@@ -3154,11 +3908,16 @@
*/
@NonNull
public ByteBuffer[] getInputBuffers() {
- if (mCachedInputBuffers == null) {
- throw new IllegalStateException();
+ synchronized (mBufferLock) {
+ if (mBufferMode != BUFFER_MODE_LEGACY) {
+ throw new IllegalStateException();
+ }
+ if (mCachedInputBuffers == null) {
+ throw new IllegalStateException();
+ }
+ // FIXME: check codec status
+ return mCachedInputBuffers;
}
- // FIXME: check codec status
- return mCachedInputBuffers;
}
/**
@@ -3185,11 +3944,16 @@
*/
@NonNull
public ByteBuffer[] getOutputBuffers() {
- if (mCachedOutputBuffers == null) {
- throw new IllegalStateException();
+ synchronized (mBufferLock) {
+ if (mBufferMode != BUFFER_MODE_LEGACY) {
+ throw new IllegalStateException();
+ }
+ if (mCachedOutputBuffers == null) {
+ throw new IllegalStateException();
+ }
+ // FIXME: check codec status
+ return mCachedOutputBuffers;
}
- // FIXME: check codec status
- return mCachedOutputBuffers;
}
/**
@@ -3212,8 +3976,13 @@
*/
@Nullable
public ByteBuffer getInputBuffer(int index) {
+ synchronized (mBufferLock) {
+ if (mBufferMode != BUFFER_MODE_LEGACY) {
+ throw new IllegalStateException();
+ }
+ }
ByteBuffer newBuffer = getBuffer(true /* input */, index);
- synchronized(mBufferLock) {
+ synchronized (mBufferLock) {
invalidateByteBuffer(mCachedInputBuffers, index);
mDequeuedInputBuffers.put(index, newBuffer);
}
@@ -3241,8 +4010,13 @@
*/
@Nullable
public Image getInputImage(int index) {
+ synchronized (mBufferLock) {
+ if (mBufferMode != BUFFER_MODE_LEGACY) {
+ throw new IllegalStateException();
+ }
+ }
Image newImage = getImage(true /* input */, index);
- synchronized(mBufferLock) {
+ synchronized (mBufferLock) {
invalidateByteBuffer(mCachedInputBuffers, index);
mDequeuedInputBuffers.put(index, newImage);
}
@@ -3270,8 +4044,13 @@
*/
@Nullable
public ByteBuffer getOutputBuffer(int index) {
+ synchronized (mBufferLock) {
+ if (mBufferMode != BUFFER_MODE_LEGACY) {
+ throw new IllegalStateException();
+ }
+ }
ByteBuffer newBuffer = getBuffer(false /* input */, index);
- synchronized(mBufferLock) {
+ synchronized (mBufferLock) {
invalidateByteBuffer(mCachedOutputBuffers, index);
mDequeuedOutputBuffers.put(index, newBuffer);
}
@@ -3298,8 +4077,13 @@
*/
@Nullable
public Image getOutputImage(int index) {
+ synchronized (mBufferLock) {
+ if (mBufferMode != BUFFER_MODE_LEGACY) {
+ throw new IllegalStateException();
+ }
+ }
Image newImage = getImage(false /* input */, index);
- synchronized(mBufferLock) {
+ synchronized (mBufferLock) {
invalidateByteBuffer(mCachedOutputBuffers, index);
mDequeuedOutputBuffers.put(index, newImage);
}
@@ -3307,6 +4091,149 @@
}
/**
+ * A single output frame and its associated metadata.
+ */
+ public static final class OutputFrame {
+ // No public constructor
+ OutputFrame(int index) {
+ mIndex = index;
+ }
+
+ /**
+ * Returns the output linear block, or null if this frame is empty.
+ *
+ * @throws IllegalStateException if this output frame is not linear.
+ */
+ public @Nullable LinearBlock getLinearBlock() {
+ if (mGraphicBlock != null) {
+ throw new IllegalStateException();
+ }
+ return mLinearBlock;
+ }
+
+ /**
+ * Returns the output graphic block, or null if this frame is empty.
+ *
+ * @throws IllegalStateException if this output frame is not graphic.
+ */
+ public @Nullable GraphicBlock getGraphicBlock() {
+ if (mLinearBlock != null) {
+ throw new IllegalStateException();
+ }
+ return mGraphicBlock;
+ }
+
+ /**
+ * Returns the presentation timestamp in microseconds.
+ */
+ public long getPresentationTimeUs() {
+ return mPresentationTimeUs;
+ }
+
+ /**
+ * Returns the buffer flags.
+ */
+ public @BufferFlag int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Returns a read-only {@link MediaFormat} for this frame. The returned
+ * object is valid only while the client is holding the output frame.
+ */
+ public @NonNull MediaFormat getFormat() {
+ return mFormat;
+ }
+
+ /**
+ * Populate {@code keys} with the name of entries that has changed from
+ * the previous frame. The entries may have been removed/changed/added.
+ * Client can find out what the change is by querying {@link MediaFormat}
+ * object returned from {@link #getFormat}.
+ */
+ public void getChangedKeys(@NonNull Set<String> keys) {
+ keys.clear();
+ keys.addAll(mChangedKeys);
+ }
+
+ void clear() {
+ mLinearBlock = null;
+ mGraphicBlock = null;
+ mFormat = null;
+ mChangedKeys.clear();
+ mLoaded = false;
+ }
+
+ boolean isAccessible() {
+ return mAccessible;
+ }
+
+ void setAccessible(boolean accessible) {
+ mAccessible = accessible;
+ }
+
+ void setBufferInfo(MediaCodec.BufferInfo info) {
+ mPresentationTimeUs = info.presentationTimeUs;
+ mFlags = info.flags;
+ }
+
+ boolean isLoaded() {
+ return mLoaded;
+ }
+
+ void setLoaded(boolean loaded) {
+ mLoaded = loaded;
+ }
+
+ private final int mIndex;
+ private LinearBlock mLinearBlock = null;
+ private GraphicBlock mGraphicBlock = null;
+ private long mPresentationTimeUs = 0;
+ private @BufferFlag int mFlags = 0;
+ private MediaFormat mFormat = null;
+ private final ArrayList<String> mChangedKeys = new ArrayList<>();
+ private boolean mAccessible = false;
+ private boolean mLoaded = false;
+ }
+
+ private final ArrayList<OutputFrame> mOutputFrames = new ArrayList<>();
+
+ /**
+ * Returns an {@link OutputFrame} object.
+ *
+ * @param index output buffer index from
+ * {@link Callback#onOutputBufferAvailable}
+ * @return {@link OutputFrame} object describing the output buffer
+ * @throws IllegalStateException if not using block model
+ * @throws IllegalArgumentException if the output buffer is not available or
+ * the index is out of range
+ */
+ public @NonNull OutputFrame getOutputFrame(int index) {
+ synchronized (mBufferLock) {
+ if (mBufferMode != BUFFER_MODE_BLOCK) {
+ throw new IllegalStateException();
+ }
+ if (index < 0 || index >= mOutputFrames.size()) {
+ throw new IllegalArgumentException();
+ }
+ OutputFrame frame = mOutputFrames.get(index);
+ if (frame == null) {
+ throw new IllegalArgumentException();
+ }
+ if (!frame.isAccessible()) {
+ throw new IllegalArgumentException();
+ }
+ if (!frame.isLoaded()) {
+ native_getOutputFrame(frame, index);
+ frame.setLoaded(true);
+ }
+ return frame;
+ }
+ }
+
+ private native void native_getOutputFrame(OutputFrame frame, int index);
+
+ /**
* The content is scaled to the surface dimensions
*/
public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1;
@@ -3889,7 +4816,9 @@
@Override
public void close() {
if (mIsImageValid) {
- java.nio.NioUtils.freeDirectBuffer(mBuffer);
+ if (mBuffer != null) {
+ java.nio.NioUtils.freeDirectBuffer(mBuffer);
+ }
mIsImageValid = false;
}
}
@@ -3908,7 +4837,6 @@
super.setCropRect(cropRect);
}
-
public MediaImage(
@NonNull ByteBuffer buffer, @NonNull ByteBuffer info, boolean readOnly,
long timestamp, int xOffset, int yOffset, @Nullable Rect cropRect) {
@@ -3963,7 +4891,6 @@
throw new UnsupportedOperationException("unexpected strides: "
+ colInc + " pixel, " + rowInc + " row on plane " + ix);
}
-
buffer.clear();
buffer.position(mBuffer.position() + planeOffset
+ (xOffset / horiz) * colInc + (yOffset / vert) * rowInc);
@@ -3983,6 +4910,30 @@
super.setCropRect(cropRect);
}
+ public MediaImage(
+ @NonNull Image.Plane[] planes, int width, int height, int format, boolean readOnly,
+ long timestamp, int xOffset, int yOffset, @Nullable Rect cropRect) {
+ mWidth = width;
+ mHeight = height;
+ mFormat = format;
+ mTimestamp = timestamp;
+ mIsImageValid = true;
+ mIsReadOnly = readOnly;
+ mBuffer = null;
+ mInfo = null;
+ mPlanes = planes;
+
+ // save offsets and info
+ mXOffset = xOffset;
+ mYOffset = yOffset;
+
+ if (cropRect == null) {
+ cropRect = new Rect(0, 0, mWidth, mHeight);
+ }
+ cropRect.offset(-xOffset, -yOffset);
+ super.setCropRect(cropRect);
+ }
+
private class MediaPlane extends Plane {
public MediaPlane(@NonNull ByteBuffer buffer, int rowInc, int colInc) {
mData = buffer;
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 1a0f139..f408ac3 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -294,6 +294,14 @@
public static final String KEY_BIT_RATE = "bitrate";
/**
+ * A key describing the hardware AV sync id.
+ * The associated value is an integer
+ *
+ * @see android.media.tv.tuner.Tuner#getAvSyncHwId
+ */
+ public static final String KEY_HARDWARE_AV_SYNC_ID = "hw-av-sync-id";
+
+ /**
* A key describing the max bitrate in bits/sec.
* This is usually over a one-second sliding window (e.g. over any window of one second).
* The associated value is an integer
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index cad5aa6..c25a533 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -356,10 +356,14 @@
public static final String EFFECT_AUXILIARY = "Auxiliary";
/**
* Effect connection mode is pre processing.
- * The audio pre processing effects are attached to an audio input (AudioRecord).
- * @hide
+ * The audio pre processing effects are attached to an audio input stream or device
*/
public static final String EFFECT_PRE_PROCESSING = "Pre Processing";
+ /**
+ * Effect connection mode is post processing.
+ * The audio post processing effects are attached to an audio output stream or device
+ */
+ public static final String EFFECT_POST_PROCESSING = "Post Processing";
// --------------------------------------------------------------------------
// Member variables
diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java
index 8c204d2..bca3fa7 100644
--- a/media/java/android/media/audiopolicy/AudioMixingRule.java
+++ b/media/java/android/media/audiopolicy/AudioMixingRule.java
@@ -73,6 +73,12 @@
* parameter is an instance of {@link java.lang.Integer}.
*/
public static final int RULE_MATCH_UID = 0x1 << 2;
+ /**
+ * A rule requiring the userId of the audio stream to match that specified.
+ * This mixing rule can be added with {@link Builder#addMixRule(int, Object)} where the Object
+ * parameter is an instance of {@link java.lang.Integer}.
+ */
+ public static final int RULE_MATCH_USERID = 0x1 << 3;
private final static int RULE_EXCLUSION_MASK = 0x8000;
/**
@@ -94,6 +100,13 @@
public static final int RULE_EXCLUDE_UID =
RULE_EXCLUSION_MASK | RULE_MATCH_UID;
+ /**
+ * @hide
+ * A rule requiring the userId information to differ.
+ */
+ public static final int RULE_EXCLUDE_USERID =
+ RULE_EXCLUSION_MASK | RULE_MATCH_USERID;
+
/** @hide */
public static final class AudioMixMatchCriterion {
@UnsupportedAppUsage
@@ -125,19 +138,20 @@
dest.writeInt(mRule);
final int match_rule = mRule & ~RULE_EXCLUSION_MASK;
switch (match_rule) {
- case RULE_MATCH_ATTRIBUTE_USAGE:
- dest.writeInt(mAttr.getUsage());
- break;
- case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
- dest.writeInt(mAttr.getCapturePreset());
- break;
- case RULE_MATCH_UID:
- dest.writeInt(mIntProp);
- break;
- default:
- Log.e("AudioMixMatchCriterion", "Unknown match rule" + match_rule
- + " when writing to Parcel");
- dest.writeInt(-1);
+ case RULE_MATCH_ATTRIBUTE_USAGE:
+ dest.writeInt(mAttr.getUsage());
+ break;
+ case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
+ dest.writeInt(mAttr.getCapturePreset());
+ break;
+ case RULE_MATCH_UID:
+ case RULE_MATCH_USERID:
+ dest.writeInt(mIntProp);
+ break;
+ default:
+ Log.e("AudioMixMatchCriterion", "Unknown match rule" + match_rule
+ + " when writing to Parcel");
+ dest.writeInt(-1);
}
}
@@ -203,6 +217,7 @@
case RULE_MATCH_ATTRIBUTE_USAGE:
case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
case RULE_MATCH_UID:
+ case RULE_MATCH_USERID:
return true;
default:
return false;
@@ -225,6 +240,7 @@
case RULE_MATCH_ATTRIBUTE_USAGE:
case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
case RULE_MATCH_UID:
+ case RULE_MATCH_USERID:
return true;
default:
return false;
@@ -234,11 +250,12 @@
private static boolean isPlayerRule(int rule) {
final int match_rule = rule & ~RULE_EXCLUSION_MASK;
switch (match_rule) {
- case RULE_MATCH_ATTRIBUTE_USAGE:
- case RULE_MATCH_UID:
- return true;
- default:
- return false;
+ case RULE_MATCH_ATTRIBUTE_USAGE:
+ case RULE_MATCH_UID:
+ case RULE_MATCH_USERID:
+ return true;
+ default:
+ return false;
}
}
@@ -319,7 +336,8 @@
* property to match against.
* @param rule one of {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_USAGE},
* {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET} or
- * {@link AudioMixingRule#RULE_MATCH_UID}.
+ * {@link AudioMixingRule#RULE_MATCH_UID} or
+ * {@link AudioMixingRule#RULE_MATCH_USERID}.
* @param property see the definition of each rule for the type to use (either an
* {@link AudioAttributes} or an {@link java.lang.Integer}).
* @return the same Builder instance.
@@ -349,7 +367,8 @@
* coming from the specified UID.
* @param rule one of {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_USAGE},
* {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET} or
- * {@link AudioMixingRule#RULE_MATCH_UID}.
+ * {@link AudioMixingRule#RULE_MATCH_UID} or
+ * {@link AudioMixingRule#RULE_MATCH_USERID}.
* @param property see the definition of each rule for the type to use (either an
* {@link AudioAttributes} or an {@link java.lang.Integer}).
* @return the same Builder instance.
@@ -425,6 +444,8 @@
* {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET} or
* {@link AudioMixingRule#RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET},
* {@link AudioMixingRule#RULE_MATCH_UID}, {@link AudioMixingRule#RULE_EXCLUDE_UID}.
+ * {@link AudioMixingRule#RULE_MATCH_USERID},
+ * {@link AudioMixingRule#RULE_EXCLUDE_USERID}.
* @return the same Builder instance.
* @throws IllegalArgumentException
*/
@@ -495,6 +516,20 @@
}
}
break;
+ case RULE_MATCH_USERID:
+ // "userid"-based rule
+ if (criterion.mIntProp == intProp.intValue()) {
+ if (criterion.mRule == rule) {
+ // rule already exists, we're done
+ return this;
+ } else {
+ // criterion already exists with a another rule,
+ // it is incompatible
+ throw new IllegalArgumentException("Contradictory rule exists"
+ + " for userId " + intProp);
+ }
+ }
+ break;
}
}
// rule didn't exist, add it
@@ -504,6 +539,7 @@
mCriteria.add(new AudioMixMatchCriterion(attrToMatch, rule));
break;
case RULE_MATCH_UID:
+ case RULE_MATCH_USERID:
mCriteria.add(new AudioMixMatchCriterion(intProp, rule));
break;
default:
@@ -530,6 +566,7 @@
.setInternalCapturePreset(preset).build();
break;
case RULE_MATCH_UID:
+ case RULE_MATCH_USERID:
intProp = new Integer(in.readInt());
break;
default:
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 27f02fe..32a4a4f 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -51,6 +51,7 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* @hide
@@ -404,7 +405,7 @@
/**
* @hide
- * Configures the audio framework so that all audio stream originating from the given UID
+ * Configures the audio framework so that all audio streams originating from the given UID
* can only come from a set of audio devices.
* For this routing to be operational, a number of {@link AudioMix} instances must have been
* previously registered on this policy, and routed to a super-set of the given audio devices
@@ -476,6 +477,78 @@
}
}
+ /**
+ * @hide
+ * Removes audio device affinity previously set by
+ * {@link #setUserIdDeviceAffinity(int, java.util.List)}.
+ * @param userId userId of the application affected.
+ * @return true if the change was successful, false otherwise.
+ */
+ @TestApi
+ @SystemApi
+ public boolean removeUserIdDeviceAffinity(int userId) {
+ synchronized (mLock) {
+ if (mStatus != POLICY_STATUS_REGISTERED) {
+ throw new IllegalStateException("Cannot use unregistered AudioPolicy");
+ }
+ final IAudioService service = getService();
+ try {
+ final int status = service.removeUserIdDeviceAffinity(this.cb(), userId);
+ return (status == AudioManager.SUCCESS);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in removeUserIdDeviceAffinity", e);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * @hide
+ * Configures the audio framework so that all audio streams originating from the given user
+ * can only come from a set of audio devices.
+ * For this routing to be operational, a number of {@link AudioMix} instances must have been
+ * previously registered on this policy, and routed to a super-set of the given audio devices
+ * with {@link AudioMix.Builder#setDevice(android.media.AudioDeviceInfo)}. Note that having
+ * multiple devices in the list doesn't imply the signals will be duplicated on the different
+ * audio devices, final routing will depend on the {@link AudioAttributes} of the sounds being
+ * played.
+ * @param userId Android user id to affect.
+ * @param devices list of devices to which the audio stream of the application may be routed.
+ * @return true if the change was successful, false otherwise.
+ */
+ @TestApi
+ @SystemApi
+ public boolean setUserIdDeviceAffinity(int userId, @NonNull List<AudioDeviceInfo> devices) {
+ Objects.requireNonNull(devices, "Illegal null list of audio devices");
+ synchronized (mLock) {
+ if (mStatus != POLICY_STATUS_REGISTERED) {
+ throw new IllegalStateException("Cannot use unregistered AudioPolicy");
+ }
+ final int[] deviceTypes = new int[devices.size()];
+ final String[] deviceAddresses = new String[devices.size()];
+ int i = 0;
+ for (AudioDeviceInfo device : devices) {
+ if (device == null) {
+ throw new IllegalArgumentException(
+ "Illegal null AudioDeviceInfo in setUserIdDeviceAffinity");
+ }
+ deviceTypes[i] =
+ AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType());
+ deviceAddresses[i] = device.getAddress();
+ i++;
+ }
+ final IAudioService service = getService();
+ try {
+ final int status = service.setUserIdDeviceAffinity(this.cb(),
+ userId, deviceTypes, deviceAddresses);
+ return (status == AudioManager.SUCCESS);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in setUserIdDeviceAffinity", e);
+ return false;
+ }
+ }
+ }
+
public void setRegistration(String regId) {
synchronized (mLock) {
mRegistrationId = regId;
diff --git a/media/java/android/media/audiopolicy/AudioPolicyConfig.java b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
index c4ba0c1..b048158 100644
--- a/media/java/android/media/audiopolicy/AudioPolicyConfig.java
+++ b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
@@ -197,6 +197,14 @@
textDump += " exclude UID ";
textDump += criterion.mIntProp;
break;
+ case AudioMixingRule.RULE_MATCH_USERID:
+ textDump += " match userId ";
+ textDump += criterion.mIntProp;
+ break;
+ case AudioMixingRule.RULE_EXCLUDE_USERID:
+ textDump += " exclude userId ";
+ textDump += criterion.mIntProp;
+ break;
default:
textDump += "invalid rule!";
}
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 9953626..870c1b4 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -146,6 +146,8 @@
* the system but will not be published until {@link #setActive(boolean)
* setActive(true)} is called. You must call {@link #release()} when
* finished with the session.
+ * <p>
+ * Note that {@link RuntimeException} will be thrown if an app creates too many sessions.
*
* @param context The context to use to create the session.
* @param tag A short name for debugging purposes.
@@ -163,6 +165,8 @@
* The {@code sessionInfo} can include additional unchanging information about this session.
* For example, it can include the version of the application, or the list of the custom
* commands that this session supports.
+ * <p>
+ * Note that {@link RuntimeException} will be thrown if an app creates too many sessions.
*
* @param context The context to use to create the session.
* @param tag A short name for debugging purposes.
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index 5e0b1ea..b40d43a 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -353,8 +353,7 @@
if (!TextUtils.equals(mId, obj.mId) || mType != obj.mType
|| !TextUtils.equals(mLanguage, obj.mLanguage)
|| !TextUtils.equals(mDescription, obj.mDescription)
- || mEncrypted != obj.mEncrypted
- || !Objects.equals(mExtra, obj.mExtra)) {
+ || mEncrypted != obj.mEncrypted) {
return false;
}
@@ -382,7 +381,16 @@
@Override
public int hashCode() {
- return Objects.hashCode(mId);
+ int result = Objects.hash(mId, mType, mLanguage, mDescription);
+
+ if (mType == TYPE_AUDIO) {
+ result = Objects.hash(result, mAudioChannelCount, mAudioSampleRate);
+ } else if (mType == TYPE_VIDEO) {
+ result = Objects.hash(result, mVideoWidth, mVideoHeight, mVideoFrameRate,
+ mVideoPixelAspectRatio);
+ }
+
+ return result;
}
public static final @android.annotation.NonNull Parcelable.Creator<TvTrackInfo> CREATOR =
diff --git a/media/java/android/media/tv/tuner/DemuxCapabilities.java b/media/java/android/media/tv/tuner/DemuxCapabilities.java
index 2c08e5b..364516c 100644
--- a/media/java/android/media/tv/tuner/DemuxCapabilities.java
+++ b/media/java/android/media/tv/tuner/DemuxCapabilities.java
@@ -58,11 +58,13 @@
private final long mSectionFilterLength;
private final int mFilterCaps;
private final int[] mLinkCaps;
+ private final boolean mSupportTimeFilter;
// Used by JNI
private DemuxCapabilities(int demuxCount, int recordCount, int playbackCount, int tsFilterCount,
int sectionFilterCount, int audioFilterCount, int videoFilterCount, int pesFilterCount,
- int pcrFilterCount, long sectionFilterLength, int filterCaps, int[] linkCaps) {
+ int pcrFilterCount, long sectionFilterLength, int filterCaps, int[] linkCaps,
+ boolean timeFilter) {
mDemuxCount = demuxCount;
mRecordCount = recordCount;
mPlaybackCount = playbackCount;
@@ -75,6 +77,7 @@
mSectionFilterLength = sectionFilterLength;
mFilterCaps = filterCaps;
mLinkCaps = linkCaps;
+ mSupportTimeFilter = timeFilter;
}
/**
@@ -161,4 +164,10 @@
public int[] getLinkCapabilities() {
return mLinkCaps;
}
+ /**
+ * Is {@link android.media.tv.tuner.filter.TimeFilter} supported.
+ */
+ public boolean isTimeFilterSupported() {
+ return mSupportTimeFilter;
+ }
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index bac425b..5e01244 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -105,16 +105,33 @@
*
* @param tuner the Tuner instance to share frontend resource with.
*/
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public void shareFrontendFromTuner(@NonNull Tuner tuner) {
// TODO: implementation.
}
+ /**
+ * Updates client priority with an arbitrary value along with a nice value.
+ *
+ * <p>Tuner resource manager (TRM) uses the client priority value to decide whether it is able
+ * to reclaim insufficient resources from another client.
+ * <p>The nice value represents how much the client intends to give up the resource when an
+ * insufficient resource situation happens.
+ *
+ * @param priority the new priority.
+ * @param niceValue the nice value.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ public void updateResourcePriority(int priority, int niceValue) {
+ // TODO: implementation.
+ }
private long mNativeContext; // used by native jMediaTuner
/**
* Releases the Tuner instance.
*/
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Override
public void close() {
// TODO: implementation.
diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java
index eb2f4a9..b6bd86b 100644
--- a/media/java/android/media/tv/tuner/filter/MediaEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java
@@ -19,6 +19,7 @@
import android.annotation.BytesLong;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.media.MediaCodec.LinearBlock;
/**
* Filter event sent from {@link Filter} objects with media type.
@@ -32,7 +33,7 @@
private final long mPts;
private final long mDataLength;
private final long mOffset;
- private final Object mLinearBuffer;
+ private final LinearBlock mLinearBlock;
private final boolean mIsSecureMemory;
private final long mDataId;
private final int mMpuSequenceNumber;
@@ -41,14 +42,14 @@
// This constructor is used by JNI code only
private MediaEvent(int streamId, boolean isPtsPresent, long pts, long dataLength, long offset,
- Object buffer, boolean isSecureMemory, long dataId, int mpuSequenceNumber,
+ LinearBlock buffer, boolean isSecureMemory, long dataId, int mpuSequenceNumber,
boolean isPrivateData, AudioDescriptor extraMetaData) {
mStreamId = streamId;
mIsPtsPresent = isPtsPresent;
mPts = pts;
mDataLength = dataLength;
mOffset = offset;
- mLinearBuffer = buffer;
+ mLinearBlock = buffer;
mIsSecureMemory = isSecureMemory;
mDataId = dataId;
mMpuSequenceNumber = mpuSequenceNumber;
@@ -96,13 +97,11 @@
}
/**
- * Gets a linear buffer associated to the memory where audio or video data stays.
- * TODO: use LinearBuffer when it's ready.
- *
- * @hide
+ * Gets a linear block associated to the memory where audio or video data stays.
*/
- public Object getLinearBuffer() {
- return mLinearBuffer;
+ @Nullable
+ public LinearBlock getLinearBlock() {
+ return mLinearBlock;
}
/**
@@ -125,6 +124,21 @@
}
/**
+ * Gets the audio handle.
+ *
+ * <p>Client gets audio handle from {@link MediaEvent}, and queues it to
+ * {@link android.media.AudioTrack} in
+ * {@link android.media.AudioTrack#ENCAPSULATION_MODE_HANDLE} format.
+ *
+ * @return the audio handle.
+ * @see android.media.AudioTrack#ENCAPSULATION_MODE_HANDLE
+ */
+ public long getAudioHandle() {
+ // TODO: implement
+ return mDataId;
+ }
+
+ /**
* Gets MPU sequence number of filtered data.
*/
public int getMpuSequenceNumber() {
diff --git a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
index 093dc6f..466fa3e 100644
--- a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
@@ -18,6 +18,7 @@
import android.annotation.BytesLong;
import android.annotation.SystemApi;
+import android.media.tv.tuner.filter.RecordSettings.ScHevcIndex;
/**
* Filter event sent from {@link Filter} objects with MMTP type.
@@ -38,6 +39,7 @@
/**
* Gets indexes which can be tagged by NAL unit group in HEVC according to ISO/IEC 23008-2.
*/
+ @ScHevcIndex
public int getScHevcIndexMask() {
return mScHevcIndexMask;
}
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettings.java b/media/java/android/media/tv/tuner/filter/SectionSettings.java
index 70788a7..9470138 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettings.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettings.java
@@ -16,6 +16,7 @@
package android.media.tv.tuner.filter;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.media.tv.tuner.TunerUtils;
@@ -25,9 +26,81 @@
* @hide
*/
@SystemApi
-public class SectionSettings extends Settings {
+public abstract class SectionSettings extends Settings {
+ final boolean mCrcEnabled;
+ final boolean mIsRepeat;
+ final boolean mIsRaw;
- SectionSettings(int mainType) {
+ SectionSettings(int mainType, boolean crcEnabled, boolean isRepeat, boolean isRaw) {
super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_SECTION));
+ mCrcEnabled = crcEnabled;
+ mIsRepeat = isRepeat;
+ mIsRaw = isRaw;
+ }
+
+ /**
+ * Returns whether the filter enables CRC (Cyclic redundancy check) and discards data which
+ * doesn't pass the check.
+ */
+ public boolean isCrcEnabled() {
+ return mCrcEnabled;
+ }
+ /**
+ * Returns whether the filter repeats the data with the same version.
+ */
+ public boolean isRepeat() {
+ return mIsRepeat;
+ }
+ /**
+ * Returns whether the filter sends {@link FilterCallback#onFilterStatusChanged} instead of
+ * {@link FilterCallback#onFilterEvent}.
+ */
+ public boolean isRaw() {
+ return mIsRaw;
+ }
+
+ /**
+ * Builder for {@link SectionSettings}.
+ *
+ * @param <T> The subclass to be built.
+ */
+ public abstract static class Builder<T extends Builder<T>>
+ extends Settings.Builder<Builder<T>> {
+ boolean mCrcEnabled;
+ boolean mIsRepeat;
+ boolean mIsRaw;
+
+ Builder(int mainType) {
+ super(mainType);
+ }
+
+ /**
+ * Sets whether the filter enables CRC (Cyclic redundancy check) and discards data which
+ * doesn't pass the check.
+ */
+ @NonNull
+ public T setCrcEnabled(boolean crcEnabled) {
+ mCrcEnabled = crcEnabled;
+ return self();
+ }
+ /**
+ * Sets whether the filter repeats the data with the same version.
+ */
+ @NonNull
+ public T setRepeat(boolean isRepeat) {
+ mIsRepeat = isRepeat;
+ return self();
+ }
+ /**
+ * Sets whether the filter send onFilterStatus instead of
+ * {@link FilterCallback#onFilterEvent}.
+ */
+ @NonNull
+ public T setRaw(boolean isRaw) {
+ mIsRaw = isRaw;
+ return self();
+ }
+
+ /* package */ abstract T self();
}
}
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
index eeeabdf..cb547ec 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
@@ -34,8 +34,9 @@
private final byte[] mMode;
- private SectionSettingsWithSectionBits(int mainType, byte[] filter, byte[] mask, byte[] mode) {
- super(mainType);
+ private SectionSettingsWithSectionBits(int mainType, boolean isCheckCrc, boolean isRepeat,
+ boolean isRaw, byte[] filter, byte[] mask, byte[] mode) {
+ super(mainType, isCheckCrc, isRepeat, isRaw);
mFilter = filter;
mMask = mask;
mMode = mode;
@@ -86,7 +87,7 @@
/**
* Builder for {@link SectionSettingsWithSectionBits}.
*/
- public static class Builder extends Settings.Builder<Builder> {
+ public static class Builder extends SectionSettings.Builder<Builder> {
private byte[] mFilter;
private byte[] mMask;
private byte[] mMode;
@@ -125,7 +126,8 @@
*/
@NonNull
public SectionSettingsWithSectionBits build() {
- return new SectionSettingsWithSectionBits(mMainType, mFilter, mMask, mMode);
+ return new SectionSettingsWithSectionBits(
+ mMainType, mCrcEnabled, mIsRepeat, mIsRaw, mFilter, mMask, mMode);
}
@Override
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
index c5ff45c..09d1dae9 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
@@ -32,8 +32,9 @@
private final int mTableId;
private final int mVersion;
- private SectionSettingsWithTableInfo(int mainType, int tableId, int version) {
- super(mainType);
+ private SectionSettingsWithTableInfo(int mainType, boolean isCheckCrc, boolean isRepeat,
+ boolean isRaw, int tableId, int version) {
+ super(mainType, isCheckCrc, isRepeat, isRaw);
mTableId = tableId;
mVersion = version;
}
@@ -67,7 +68,7 @@
/**
* Builder for {@link SectionSettingsWithTableInfo}.
*/
- public static class Builder extends Settings.Builder<Builder> {
+ public static class Builder extends SectionSettings.Builder<Builder> {
private int mTableId;
private int mVersion;
@@ -97,7 +98,8 @@
*/
@NonNull
public SectionSettingsWithTableInfo build() {
- return new SectionSettingsWithTableInfo(mMainType, mTableId, mVersion);
+ return new SectionSettingsWithTableInfo(
+ mMainType, mCrcEnabled, mIsRepeat, mIsRaw, mTableId, mVersion);
}
@Override
diff --git a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
index b6878e6..3d83a74 100644
--- a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
@@ -106,7 +106,7 @@
* Sets whether the data is compressed IP packet.
*/
@NonNull
- public Builder setIsCompressedIpPacket(boolean isCompressedIpPacket) {
+ public Builder setCompressedIpPacket(boolean isCompressedIpPacket) {
mIsCompressedIpPacket = isCompressedIpPacket;
return this;
}
diff --git a/media/java/android/media/voice/KeyphraseModelManager.java b/media/java/android/media/voice/KeyphraseModelManager.java
new file mode 100644
index 0000000..3fa38e0
--- /dev/null
+++ b/media/java/android/media/voice/KeyphraseModelManager.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.voice;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.hardware.soundtrigger.SoundTrigger;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.util.Slog;
+
+import com.android.internal.app.IVoiceInteractionManagerService;
+
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * This class provides management of voice based sound recognition models. Usage of this class is
+ * restricted to system or signature applications only. This allows OEMs to write apps that can
+ * manage voice based sound trigger models.
+ * Callers of this class are expected to have whitelist manifest permission MANAGE_VOICE_KEYPHRASES.
+ * Callers of this class are expected to be the designated voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
+ * @hide
+ */
+@SystemApi
+public final class KeyphraseModelManager {
+ private static final boolean DBG = false;
+ private static final String TAG = "KeyphraseModelManager";
+
+ private final IVoiceInteractionManagerService mVoiceInteractionManagerService;
+
+ /**
+ * @hide
+ */
+ public KeyphraseModelManager(
+ IVoiceInteractionManagerService voiceInteractionManagerService) {
+ if (DBG) {
+ Slog.i(TAG, "KeyphraseModelManager created.");
+ }
+ mVoiceInteractionManagerService = voiceInteractionManagerService;
+ }
+
+
+ /**
+ * Gets the registered sound model for keyphrase detection for the current user.
+ * The keyphraseId and locale passed must match a supported model passed in via
+ * {@link #updateKeyphraseSoundModel}.
+ * If the active voice interaction service changes from the current user, all requests will be
+ * rejected, and any registered models will be unregistered.
+ *
+ * @param keyphraseId The unique identifier for the keyphrase.
+ * @param locale The locale language tag supported by the desired model.
+ * @return Registered keyphrase sound model matching the keyphrase ID and locale. May be null if
+ * no matching sound model exists.
+ * @throws SecurityException Thrown when caller does not have MANAGE_VOICE_KEYPHRASES permission
+ * or if the caller is not the active voice interaction service.
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES)
+ @Nullable
+ public SoundTrigger.KeyphraseSoundModel getKeyphraseSoundModel(int keyphraseId,
+ @NonNull Locale locale) {
+ Objects.requireNonNull(locale);
+ try {
+ return mVoiceInteractionManagerService.getKeyphraseSoundModel(keyphraseId,
+ locale.toLanguageTag());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Add or update the given keyphrase sound model to the registered models pool for the current
+ * user.
+ * If a model exists with the same Keyphrase ID, locale, and user list. The registered model
+ * will be overwritten with the new model.
+ * If the active voice interaction service changes from the current user, all requests will be
+ * rejected, and any registered models will be unregistered.
+ *
+ * @param model Keyphrase sound model to be updated.
+ * @throws ServiceSpecificException Thrown with error code if failed to update the keyphrase
+ * sound model.
+ * @throws SecurityException Thrown when caller does not have MANAGE_VOICE_KEYPHRASES permission
+ * or if the caller is not the active voice interaction service.
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES)
+ public void updateKeyphraseSoundModel(@NonNull SoundTrigger.KeyphraseSoundModel model) {
+ Objects.requireNonNull(model);
+ try {
+ int status = mVoiceInteractionManagerService.updateKeyphraseSoundModel(model);
+ if (status != SoundTrigger.STATUS_OK) {
+ throw new ServiceSpecificException(status);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Delete keyphrase sound model from the registered models pool for the current user matching\
+ * the keyphrase ID and locale.
+ * The keyphraseId and locale passed must match a supported model passed in via
+ * {@link #updateKeyphraseSoundModel}.
+ * If the active voice interaction service changes from the current user, all requests will be
+ * rejected, and any registered models will be unregistered.
+ *
+ * @param keyphraseId The unique identifier for the keyphrase.
+ * @param locale The locale language tag supported by the desired model.
+ * @throws ServiceSpecificException Thrown with error code if failed to delete the keyphrase
+ * sound model.
+ * @throws SecurityException Thrown when caller does not have MANAGE_VOICE_KEYPHRASES permission
+ * or if the caller is not the active voice interaction service.
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES)
+ public void deleteKeyphraseSoundModel(int keyphraseId, @NonNull Locale locale) {
+ Objects.requireNonNull(locale);
+ try {
+ int status = mVoiceInteractionManagerService.deleteKeyphraseSoundModel(keyphraseId,
+ locale.toLanguageTag());
+ if (status != SoundTrigger.STATUS_OK) {
+ throw new ServiceSpecificException(status);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index aeacd8f..c17b1b7 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -1,6 +1,8 @@
cc_library_shared {
name: "libmedia_jni",
+ defaults: ["libcodec2-internal-defaults"],
+
srcs: [
"android_media_ImageWriter.cpp",
"android_media_ImageReader.cpp",
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 20980a9..ab6966d 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -18,6 +18,8 @@
#define LOG_TAG "MediaCodec-JNI"
#include <utils/Log.h>
+#include <type_traits>
+
#include "android_media_MediaCodec.h"
#include "android_media_MediaCrypto.h"
@@ -31,13 +33,20 @@
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h>
+#include <C2Buffer.h>
+
#include <android/hardware/cas/native/1.0/IDescrambler.h>
+#include <binder/MemoryHeapBase.h>
+
#include <cutils/compiler.h>
#include <gui/Surface.h>
+#include <hidlmemory/FrameworkUtils.h>
+
#include <media/MediaCodecBuffer.h>
+#include <media/hardware/VideoAPI.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -47,7 +56,6 @@
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/PersistentSurface.h>
#include <mediadrm/ICrypto.h>
-#include <nativehelper/ScopedLocalRef.h>
#include <system/window.h>
@@ -110,6 +118,39 @@
jfieldID levelField;
} gCodecInfo;
+static struct {
+ jclass clazz;
+ jobject nativeByteOrder;
+ jmethodID orderId;
+ jmethodID asReadOnlyBufferId;
+ jmethodID positionId;
+ jmethodID limitId;
+} gByteBufferInfo;
+
+static struct {
+ jmethodID sizeId;
+ jmethodID getId;
+ jmethodID addId;
+} gArrayListInfo;
+
+static struct {
+ jclass clazz;
+ jmethodID ctorId;
+ jmethodID setInternalStateId;
+ jfieldID contextId;
+ jfieldID validId;
+ jfieldID lockId;
+} gLinearBlockInfo;
+
+static struct {
+ jclass clazz;
+ jmethodID ctorId;
+ jmethodID setInternalStateId;
+ jfieldID contextId;
+ jfieldID validId;
+ jfieldID lockId;
+} gGraphicBlockInfo;
+
struct fields_t {
jmethodID postEventFromNativeID;
jmethodID lockAndGetContextID;
@@ -123,11 +164,65 @@
jfieldID cryptoInfoPatternID;
jfieldID patternEncryptBlocksID;
jfieldID patternSkipBlocksID;
+ jfieldID queueRequestIndexID;
+ jfieldID outputFrameLinearBlockID;
+ jfieldID outputFrameGraphicBlockID;
+ jfieldID outputFrameChangedKeysID;
+ jfieldID outputFrameFormatID;
};
static fields_t gFields;
static const void *sRefBaseOwner;
+struct JMediaCodecLinearBlock {
+ std::shared_ptr<C2Buffer> mBuffer;
+ std::shared_ptr<C2ReadView> mReadonlyMapping;
+
+ std::shared_ptr<C2LinearBlock> mBlock;
+ std::shared_ptr<C2WriteView> mReadWriteMapping;
+
+ sp<IMemoryHeap> mHeap;
+ sp<hardware::HidlMemory> mMemory;
+
+ sp<MediaCodecBuffer> mLegacyBuffer;
+
+ std::once_flag mCopyWarningFlag;
+
+ std::shared_ptr<C2Buffer> toC2Buffer(size_t offset, size_t size) {
+ if (mBuffer) {
+ if (mBuffer->data().type() != C2BufferData::LINEAR) {
+ return nullptr;
+ }
+ C2ConstLinearBlock block = mBuffer->data().linearBlocks().front();
+ if (offset == 0 && size == block.capacity()) {
+ return mBuffer;
+ }
+ return C2Buffer::CreateLinearBuffer(block.subBlock(offset, size));
+ }
+ if (mBlock) {
+ return C2Buffer::CreateLinearBuffer(mBlock->share(offset, size, C2Fence{}));
+ }
+ return nullptr;
+ }
+
+ sp<hardware::HidlMemory> toHidlMemory() {
+ if (mMemory) {
+ return mMemory;
+ }
+ return nullptr;
+ }
+};
+
+struct JMediaCodecGraphicBlock {
+ std::shared_ptr<C2Buffer> mBuffer;
+ std::shared_ptr<const C2GraphicView> mReadonlyMapping;
+
+ std::shared_ptr<C2GraphicBlock> mBlock;
+ std::shared_ptr<C2GraphicView> mReadWriteMapping;
+
+ sp<MediaCodecBuffer> mLegacyBuffer;
+};
+
////////////////////////////////////////////////////////////////////////////////
JMediaCodec::JMediaCodec(
@@ -141,8 +236,6 @@
mClass = (jclass)env->NewGlobalRef(clazz);
mObject = env->NewWeakGlobalRef(thiz);
- cacheJavaObjects(env);
-
mLooper = new ALooper;
mLooper->setName("MediaCodec_looper");
@@ -163,45 +256,6 @@
CHECK((mCodec != NULL) != (mInitStatus != OK));
}
-void JMediaCodec::cacheJavaObjects(JNIEnv *env) {
- jclass clazz = (jclass)env->FindClass("java/nio/ByteBuffer");
- mByteBufferClass = (jclass)env->NewGlobalRef(clazz);
- CHECK(mByteBufferClass != NULL);
-
- ScopedLocalRef<jclass> byteOrderClass(
- env, env->FindClass("java/nio/ByteOrder"));
- CHECK(byteOrderClass.get() != NULL);
-
- jmethodID nativeOrderID = env->GetStaticMethodID(
- byteOrderClass.get(), "nativeOrder", "()Ljava/nio/ByteOrder;");
- CHECK(nativeOrderID != NULL);
-
- jobject nativeByteOrderObj =
- env->CallStaticObjectMethod(byteOrderClass.get(), nativeOrderID);
- mNativeByteOrderObj = env->NewGlobalRef(nativeByteOrderObj);
- CHECK(mNativeByteOrderObj != NULL);
- env->DeleteLocalRef(nativeByteOrderObj);
- nativeByteOrderObj = NULL;
-
- mByteBufferOrderMethodID = env->GetMethodID(
- mByteBufferClass,
- "order",
- "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;");
- CHECK(mByteBufferOrderMethodID != NULL);
-
- mByteBufferAsReadOnlyBufferMethodID = env->GetMethodID(
- mByteBufferClass, "asReadOnlyBuffer", "()Ljava/nio/ByteBuffer;");
- CHECK(mByteBufferAsReadOnlyBufferMethodID != NULL);
-
- mByteBufferPositionMethodID = env->GetMethodID(
- mByteBufferClass, "position", "(I)Ljava/nio/Buffer;");
- CHECK(mByteBufferPositionMethodID != NULL);
-
- mByteBufferLimitMethodID = env->GetMethodID(
- mByteBufferClass, "limit", "(I)Ljava/nio/Buffer;");
- CHECK(mByteBufferLimitMethodID != NULL);
-}
-
status_t JMediaCodec::initCheck() const {
return mInitStatus;
}
@@ -247,19 +301,6 @@
mObject = NULL;
env->DeleteGlobalRef(mClass);
mClass = NULL;
- deleteJavaObjects(env);
-}
-
-void JMediaCodec::deleteJavaObjects(JNIEnv *env) {
- env->DeleteGlobalRef(mByteBufferClass);
- mByteBufferClass = NULL;
- env->DeleteGlobalRef(mNativeByteOrderObj);
- mNativeByteOrderObj = NULL;
-
- mByteBufferOrderMethodID = NULL;
- mByteBufferAsReadOnlyBufferMethodID = NULL;
- mByteBufferPositionMethodID = NULL;
- mByteBufferLimitMethodID = NULL;
}
status_t JMediaCodec::enableOnFrameRenderedListener(jboolean enable) {
@@ -300,6 +341,12 @@
mSurfaceTextureClient.clear();
}
+ constexpr int32_t CONFIGURE_FLAG_ENCODE = 1;
+ AString mime;
+ CHECK(format->findString("mime", &mime));
+ mGraphicOutput = (mime.startsWithIgnoreCase("video/") || mime.startsWithIgnoreCase("image/"))
+ && !(flags & CONFIGURE_FLAG_ENCODE);
+
return mCodec->configure(
format, mSurfaceTextureClient, crypto, descrambler, flags);
}
@@ -370,6 +417,32 @@
presentationTimeUs, flags, errorDetailMsg);
}
+status_t JMediaCodec::queueBuffer(
+ size_t index, const std::shared_ptr<C2Buffer> &buffer, int64_t timeUs,
+ uint32_t flags, const sp<AMessage> &tunings, AString *errorDetailMsg) {
+ return mCodec->queueBuffer(
+ index, buffer, timeUs, flags, tunings, errorDetailMsg);
+}
+
+status_t JMediaCodec::queueEncryptedLinearBlock(
+ size_t index,
+ const sp<hardware::HidlMemory> &buffer,
+ size_t offset,
+ const CryptoPlugin::SubSample *subSamples,
+ size_t numSubSamples,
+ const uint8_t key[16],
+ const uint8_t iv[16],
+ CryptoPlugin::Mode mode,
+ const CryptoPlugin::Pattern &pattern,
+ int64_t presentationTimeUs,
+ uint32_t flags,
+ const sp<AMessage> &tunings,
+ AString *errorDetailMsg) {
+ return mCodec->queueEncryptedBuffer(
+ index, buffer, offset, subSamples, numSubSamples, key, iv, mode, pattern,
+ presentationTimeUs, flags, tunings, errorDetailMsg);
+}
+
status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
return mCodec->dequeueInputBuffer(index, timeoutUs);
}
@@ -444,7 +517,7 @@
}
*bufArray = (jobjectArray)env->NewObjectArray(
- buffers.size(), mByteBufferClass, NULL);
+ buffers.size(), gByteBufferInfo.clazz, NULL);
if (*bufArray == NULL) {
return NO_MEMORY;
}
@@ -470,6 +543,39 @@
return OK;
}
+template <typename T>
+static jobject CreateByteBuffer(
+ JNIEnv *env, T *base, size_t capacity, size_t offset, size_t size,
+ bool readOnly, bool clearBuffer) {
+ jobject byteBuffer =
+ env->NewDirectByteBuffer(
+ const_cast<typename std::remove_const<T>::type *>(base),
+ capacity);
+ if (readOnly && byteBuffer != NULL) {
+ jobject readOnlyBuffer = env->CallObjectMethod(
+ byteBuffer, gByteBufferInfo.asReadOnlyBufferId);
+ env->DeleteLocalRef(byteBuffer);
+ byteBuffer = readOnlyBuffer;
+ }
+ if (byteBuffer == NULL) {
+ return nullptr;
+ }
+ jobject me = env->CallObjectMethod(
+ byteBuffer, gByteBufferInfo.orderId, gByteBufferInfo.nativeByteOrder);
+ env->DeleteLocalRef(me);
+ me = env->CallObjectMethod(
+ byteBuffer, gByteBufferInfo.limitId,
+ clearBuffer ? capacity : offset + size);
+ env->DeleteLocalRef(me);
+ me = env->CallObjectMethod(
+ byteBuffer, gByteBufferInfo.positionId,
+ clearBuffer ? 0 : offset);
+ env->DeleteLocalRef(me);
+ me = NULL;
+ return byteBuffer;
+}
+
+
// static
template <typename T>
status_t JMediaCodec::createByteBufferFromABuffer(
@@ -488,29 +594,9 @@
return OK;
}
- jobject byteBuffer =
- env->NewDirectByteBuffer(buffer->base(), buffer->capacity());
- if (readOnly && byteBuffer != NULL) {
- jobject readOnlyBuffer = env->CallObjectMethod(
- byteBuffer, mByteBufferAsReadOnlyBufferMethodID);
- env->DeleteLocalRef(byteBuffer);
- byteBuffer = readOnlyBuffer;
- }
- if (byteBuffer == NULL) {
- return NO_MEMORY;
- }
- jobject me = env->CallObjectMethod(
- byteBuffer, mByteBufferOrderMethodID, mNativeByteOrderObj);
- env->DeleteLocalRef(me);
- me = env->CallObjectMethod(
- byteBuffer, mByteBufferLimitMethodID,
- clearBuffer ? buffer->capacity() : (buffer->offset() + buffer->size()));
- env->DeleteLocalRef(me);
- me = env->CallObjectMethod(
- byteBuffer, mByteBufferPositionMethodID,
- clearBuffer ? 0 : buffer->offset());
- env->DeleteLocalRef(me);
- me = NULL;
+ jobject byteBuffer = CreateByteBuffer(
+ env, buffer->base(), buffer->capacity(), buffer->offset(), buffer->size(),
+ readOnly, clearBuffer);
*buf = byteBuffer;
return OK;
@@ -628,6 +714,92 @@
return OK;
}
+status_t JMediaCodec::getOutputFrame(
+ JNIEnv *env, jobject frame, size_t index) const {
+ sp<MediaCodecBuffer> buffer;
+
+ status_t err = mCodec->getOutputBuffer(index, &buffer);
+ if (err != OK) {
+ return err;
+ }
+
+ if (buffer->size() > 0) {
+ // asC2Buffer clears internal reference, so set the reference again.
+ std::shared_ptr<C2Buffer> c2Buffer = buffer->asC2Buffer();
+ buffer->copy(c2Buffer);
+ if (c2Buffer) {
+ switch (c2Buffer->data().type()) {
+ case C2BufferData::LINEAR: {
+ std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
+ context->mBuffer = c2Buffer;
+ ScopedLocalRef<jobject> linearBlock{env, env->NewObject(
+ gLinearBlockInfo.clazz, gLinearBlockInfo.ctorId)};
+ env->SetLongField(
+ linearBlock.get(), gLinearBlockInfo.contextId, (jlong)context.release());
+ env->SetObjectField(frame, gFields.outputFrameLinearBlockID, linearBlock.get());
+ break;
+ }
+ case C2BufferData::GRAPHIC: {
+ std::unique_ptr<JMediaCodecGraphicBlock> context{new JMediaCodecGraphicBlock};
+ context->mBuffer = c2Buffer;
+ ScopedLocalRef<jobject> graphicBlock{env, env->NewObject(
+ gGraphicBlockInfo.clazz, gGraphicBlockInfo.ctorId)};
+ env->SetLongField(
+ graphicBlock.get(), gGraphicBlockInfo.contextId, (jlong)context.release());
+ env->SetObjectField(frame, gFields.outputFrameGraphicBlockID, graphicBlock.get());
+ break;
+ }
+ case C2BufferData::LINEAR_CHUNKS: [[fallthrough]];
+ case C2BufferData::GRAPHIC_CHUNKS: [[fallthrough]];
+ case C2BufferData::INVALID: [[fallthrough]];
+ default:
+ return INVALID_OPERATION;
+ }
+ } else {
+ if (!mGraphicOutput) {
+ std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
+ context->mLegacyBuffer = buffer;
+ ScopedLocalRef<jobject> linearBlock{env, env->NewObject(
+ gLinearBlockInfo.clazz, gLinearBlockInfo.ctorId)};
+ env->SetLongField(
+ linearBlock.get(), gLinearBlockInfo.contextId, (jlong)context.release());
+ env->SetObjectField(frame, gFields.outputFrameLinearBlockID, linearBlock.get());
+ } else {
+ std::unique_ptr<JMediaCodecGraphicBlock> context{new JMediaCodecGraphicBlock};
+ context->mLegacyBuffer = buffer;
+ ScopedLocalRef<jobject> graphicBlock{env, env->NewObject(
+ gGraphicBlockInfo.clazz, gGraphicBlockInfo.ctorId)};
+ env->SetLongField(
+ graphicBlock.get(), gGraphicBlockInfo.contextId, (jlong)context.release());
+ env->SetObjectField(frame, gFields.outputFrameGraphicBlockID, graphicBlock.get());
+ }
+ }
+ }
+
+ jobject format;
+ err = getOutputFormat(env, index, &format);
+ if (err != OK) {
+ return err;
+ }
+ env->SetObjectField(frame, gFields.outputFrameFormatID, format);
+ env->DeleteLocalRef(format);
+ format = nullptr;
+
+ sp<RefBase> obj;
+ if (buffer->meta()->findObject("changedKeys", &obj) && obj) {
+ sp<MediaCodec::WrapperObject<std::set<std::string>>> changedKeys{
+ (decltype(changedKeys.get()))obj.get()};
+ ScopedLocalRef<jobject> changedKeysObj{env, env->GetObjectField(
+ frame, gFields.outputFrameChangedKeysID)};
+ for (const std::string &key : changedKeys->value) {
+ ScopedLocalRef<jstring> keyStr{env, env->NewStringUTF(key.c_str())};
+ (void)env->CallBooleanMethod(changedKeysObj.get(), gArrayListInfo.addId, keyStr.get());
+ }
+ }
+ return OK;
+}
+
+
status_t JMediaCodec::getName(JNIEnv *env, jstring *nameStr) const {
AString name;
@@ -1428,6 +1600,139 @@
env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
}
+struct NativeCryptoInfo {
+ NativeCryptoInfo(JNIEnv *env, jobject cryptoInfoObj)
+ : mEnv{env},
+ mIvObj{env, (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID)},
+ mKeyObj{env, (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID)} {
+ mNumSubSamples = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID);
+
+ ScopedLocalRef<jintArray> numBytesOfClearDataObj{env, (jintArray)env->GetObjectField(
+ cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID)};
+
+ ScopedLocalRef<jintArray> numBytesOfEncryptedDataObj{env, (jintArray)env->GetObjectField(
+ cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID)};
+
+ jint jmode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
+ if (jmode == gCryptoModes.Unencrypted) {
+ mMode = CryptoPlugin::kMode_Unencrypted;
+ } else if (jmode == gCryptoModes.AesCtr) {
+ mMode = CryptoPlugin::kMode_AES_CTR;
+ } else if (jmode == gCryptoModes.AesCbc) {
+ mMode = CryptoPlugin::kMode_AES_CBC;
+ } else {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return;
+ }
+
+ ScopedLocalRef<jobject> patternObj{
+ env, env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoPatternID)};
+
+ CryptoPlugin::Pattern pattern;
+ if (patternObj.get() == nullptr) {
+ pattern.mEncryptBlocks = 0;
+ pattern.mSkipBlocks = 0;
+ } else {
+ pattern.mEncryptBlocks = env->GetIntField(
+ patternObj.get(), gFields.patternEncryptBlocksID);
+ pattern.mSkipBlocks = env->GetIntField(
+ patternObj.get(), gFields.patternSkipBlocksID);
+ }
+
+ mErr = OK;
+ if (mNumSubSamples <= 0) {
+ mErr = -EINVAL;
+ } else if (numBytesOfClearDataObj == nullptr
+ && numBytesOfEncryptedDataObj == nullptr) {
+ mErr = -EINVAL;
+ } else if (numBytesOfEncryptedDataObj != nullptr
+ && env->GetArrayLength(numBytesOfEncryptedDataObj.get()) < mNumSubSamples) {
+ mErr = -ERANGE;
+ } else if (numBytesOfClearDataObj != nullptr
+ && env->GetArrayLength(numBytesOfClearDataObj.get()) < mNumSubSamples) {
+ mErr = -ERANGE;
+ // subSamples array may silently overflow if number of samples are too large. Use
+ // INT32_MAX as maximum allocation size may be less than SIZE_MAX on some platforms
+ } else if (CC_UNLIKELY(mNumSubSamples >= (signed)(INT32_MAX / sizeof(*mSubSamples))) ) {
+ mErr = -EINVAL;
+ } else {
+ jint *numBytesOfClearData =
+ (numBytesOfClearDataObj == nullptr)
+ ? nullptr
+ : env->GetIntArrayElements(numBytesOfClearDataObj.get(), nullptr);
+
+ jint *numBytesOfEncryptedData =
+ (numBytesOfEncryptedDataObj == nullptr)
+ ? nullptr
+ : env->GetIntArrayElements(numBytesOfEncryptedDataObj.get(), nullptr);
+
+ mSubSamples = new CryptoPlugin::SubSample[mNumSubSamples];
+
+ for (jint i = 0; i < mNumSubSamples; ++i) {
+ mSubSamples[i].mNumBytesOfClearData =
+ (numBytesOfClearData == nullptr) ? 0 : numBytesOfClearData[i];
+
+ mSubSamples[i].mNumBytesOfEncryptedData =
+ (numBytesOfEncryptedData == nullptr) ? 0 : numBytesOfEncryptedData[i];
+ }
+
+ if (numBytesOfEncryptedData != nullptr) {
+ env->ReleaseIntArrayElements(
+ numBytesOfEncryptedDataObj.get(), numBytesOfEncryptedData, 0);
+ numBytesOfEncryptedData = nullptr;
+ }
+
+ if (numBytesOfClearData != nullptr) {
+ env->ReleaseIntArrayElements(
+ numBytesOfClearDataObj.get(), numBytesOfClearData, 0);
+ numBytesOfClearData = nullptr;
+ }
+ }
+
+ if (mErr == OK && mKeyObj.get() != nullptr) {
+ if (env->GetArrayLength(mKeyObj.get()) != 16) {
+ mErr = -EINVAL;
+ } else {
+ mKey = env->GetByteArrayElements(mKeyObj.get(), nullptr);
+ }
+ }
+
+ if (mErr == OK && mIvObj.get() != nullptr) {
+ if (env->GetArrayLength(mIvObj.get()) != 16) {
+ mErr = -EINVAL;
+ } else {
+ mIv = env->GetByteArrayElements(mIvObj.get(), nullptr);
+ }
+ }
+ }
+
+ ~NativeCryptoInfo() {
+ if (mIv != nullptr) {
+ mEnv->ReleaseByteArrayElements(mIvObj.get(), mIv, 0);
+ }
+
+ if (mKey != nullptr) {
+ mEnv->ReleaseByteArrayElements(mKeyObj.get(), mKey, 0);
+ }
+
+ if (mSubSamples != nullptr) {
+ delete[] mSubSamples;
+ }
+ }
+
+ JNIEnv *mEnv{nullptr};
+ ScopedLocalRef<jbyteArray> mIvObj;
+ ScopedLocalRef<jbyteArray> mKeyObj;
+ status_t mErr{OK};
+
+ CryptoPlugin::SubSample *mSubSamples{nullptr};
+ int32_t mNumSubSamples{0};
+ jbyte *mIv{nullptr};
+ jbyte *mKey{nullptr};
+ enum CryptoPlugin::Mode mMode;
+ CryptoPlugin::Pattern mPattern;
+};
+
static void android_media_MediaCodec_queueSecureInputBuffer(
JNIEnv *env,
jobject thiz,
@@ -1445,154 +1750,276 @@
return;
}
- jint numSubSamples =
- env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID);
-
- jintArray numBytesOfClearDataObj =
- (jintArray)env->GetObjectField(
- cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID);
-
- jintArray numBytesOfEncryptedDataObj =
- (jintArray)env->GetObjectField(
- cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID);
-
- jbyteArray keyObj =
- (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID);
-
- jbyteArray ivObj =
- (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID);
-
- jint jmode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
- enum CryptoPlugin::Mode mode;
- if (jmode == gCryptoModes.Unencrypted) {
- mode = CryptoPlugin::kMode_Unencrypted;
- } else if (jmode == gCryptoModes.AesCtr) {
- mode = CryptoPlugin::kMode_AES_CTR;
- } else if (jmode == gCryptoModes.AesCbc) {
- mode = CryptoPlugin::kMode_AES_CBC;
- } else {
- throwExceptionAsNecessary(env, INVALID_OPERATION);
- return;
- }
-
- jobject patternObj = env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoPatternID);
-
- CryptoPlugin::Pattern pattern;
- if (patternObj == NULL) {
- pattern.mEncryptBlocks = 0;
- pattern.mSkipBlocks = 0;
- } else {
- pattern.mEncryptBlocks = env->GetIntField(patternObj, gFields.patternEncryptBlocksID);
- pattern.mSkipBlocks = env->GetIntField(patternObj, gFields.patternSkipBlocksID);
- }
-
- status_t err = OK;
-
- CryptoPlugin::SubSample *subSamples = NULL;
- jbyte *key = NULL;
- jbyte *iv = NULL;
-
- if (numSubSamples <= 0) {
- err = -EINVAL;
- } else if (numBytesOfClearDataObj == NULL
- && numBytesOfEncryptedDataObj == NULL) {
- err = -EINVAL;
- } else if (numBytesOfEncryptedDataObj != NULL
- && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) {
- err = -ERANGE;
- } else if (numBytesOfClearDataObj != NULL
- && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) {
- err = -ERANGE;
- // subSamples array may silently overflow if number of samples are too large. Use
- // INT32_MAX as maximum allocation size may be less than SIZE_MAX on some platforms
- } else if ( CC_UNLIKELY(numSubSamples >= (signed)(INT32_MAX / sizeof(*subSamples))) ) {
- err = -EINVAL;
- } else {
- jboolean isCopy;
-
- jint *numBytesOfClearData =
- (numBytesOfClearDataObj == NULL)
- ? NULL
- : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
-
- jint *numBytesOfEncryptedData =
- (numBytesOfEncryptedDataObj == NULL)
- ? NULL
- : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
-
- subSamples = new CryptoPlugin::SubSample[numSubSamples];
-
- for (jint i = 0; i < numSubSamples; ++i) {
- subSamples[i].mNumBytesOfClearData =
- (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
-
- subSamples[i].mNumBytesOfEncryptedData =
- (numBytesOfEncryptedData == NULL)
- ? 0 : numBytesOfEncryptedData[i];
- }
-
- if (numBytesOfEncryptedData != NULL) {
- env->ReleaseIntArrayElements(
- numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
- numBytesOfEncryptedData = NULL;
- }
-
- if (numBytesOfClearData != NULL) {
- env->ReleaseIntArrayElements(
- numBytesOfClearDataObj, numBytesOfClearData, 0);
- numBytesOfClearData = NULL;
- }
- }
-
- if (err == OK && keyObj != NULL) {
- if (env->GetArrayLength(keyObj) != 16) {
- err = -EINVAL;
- } else {
- jboolean isCopy;
- key = env->GetByteArrayElements(keyObj, &isCopy);
- }
- }
-
- if (err == OK && ivObj != NULL) {
- if (env->GetArrayLength(ivObj) != 16) {
- err = -EINVAL;
- } else {
- jboolean isCopy;
- iv = env->GetByteArrayElements(ivObj, &isCopy);
- }
- }
-
+ NativeCryptoInfo cryptoInfo{env, cryptoInfoObj};
AString errorDetailMsg;
+ status_t err = cryptoInfo.mErr;
if (err == OK) {
err = codec->queueSecureInputBuffer(
index, offset,
- subSamples, numSubSamples,
- (const uint8_t *)key, (const uint8_t *)iv,
- mode,
- pattern,
+ cryptoInfo.mSubSamples, cryptoInfo.mNumSubSamples,
+ (const uint8_t *)cryptoInfo.mKey, (const uint8_t *)cryptoInfo.mIv,
+ cryptoInfo.mMode,
+ cryptoInfo.mPattern,
timestampUs,
flags,
&errorDetailMsg);
}
- if (iv != NULL) {
- env->ReleaseByteArrayElements(ivObj, iv, 0);
- iv = NULL;
- }
-
- if (key != NULL) {
- env->ReleaseByteArrayElements(keyObj, key, 0);
- key = NULL;
- }
-
- delete[] subSamples;
- subSamples = NULL;
-
throwExceptionAsNecessary(
env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
}
+static status_t ConvertKeyValueListsToAMessage(
+ JNIEnv *env, jobject keys, jobject values, sp<AMessage> *msg) {
+ static struct Fields {
+ explicit Fields(JNIEnv *env) {
+ ScopedLocalRef<jclass> clazz{env, env->FindClass("java/lang/String")};
+ CHECK(clazz.get() != NULL);
+ mStringClass = (jclass)env->NewGlobalRef(clazz.get());
+
+ clazz.reset(env->FindClass("java/lang/Integer"));
+ CHECK(clazz.get() != NULL);
+ mIntegerClass = (jclass)env->NewGlobalRef(clazz.get());
+
+ mIntegerValueId = env->GetMethodID(clazz.get(), "intValue", "()I");
+ CHECK(mIntegerValueId != NULL);
+
+ clazz.reset(env->FindClass("java/lang/Long"));
+ CHECK(clazz.get() != NULL);
+ mLongClass = (jclass)env->NewGlobalRef(clazz.get());
+
+ mLongValueId = env->GetMethodID(clazz.get(), "longValue", "()J");
+ CHECK(mLongValueId != NULL);
+
+ clazz.reset(env->FindClass("java/lang/Float"));
+ CHECK(clazz.get() != NULL);
+ mFloatClass = (jclass)env->NewGlobalRef(clazz.get());
+
+ mFloatValueId = env->GetMethodID(clazz.get(), "floatValue", "()F");
+ CHECK(mFloatValueId != NULL);
+
+ clazz.reset(env->FindClass("java/util/ArrayList"));
+ CHECK(clazz.get() != NULL);
+
+ mByteBufferArrayId = env->GetMethodID(gByteBufferInfo.clazz, "array", "()[B");
+ CHECK(mByteBufferArrayId != NULL);
+ }
+
+ jclass mStringClass;
+ jclass mIntegerClass;
+ jmethodID mIntegerValueId;
+ jclass mLongClass;
+ jmethodID mLongValueId;
+ jclass mFloatClass;
+ jmethodID mFloatValueId;
+ jmethodID mByteBufferArrayId;
+ } sFields{env};
+
+ jint size = env->CallIntMethod(keys, gArrayListInfo.sizeId);
+ if (size != env->CallIntMethod(values, gArrayListInfo.sizeId)) {
+ return BAD_VALUE;
+ }
+
+ sp<AMessage> result{new AMessage};
+ for (jint i = 0; i < size; ++i) {
+ ScopedLocalRef<jstring> jkey{
+ env, (jstring)env->CallObjectMethod(keys, gArrayListInfo.getId, i)};
+ const char *tmp = env->GetStringUTFChars(jkey.get(), nullptr);
+ AString key;
+ if (tmp) {
+ key.setTo(tmp);
+ }
+ env->ReleaseStringUTFChars(jkey.get(), tmp);
+ if (key.empty()) {
+ return NO_MEMORY;
+ }
+
+ ScopedLocalRef<jobject> jvalue{
+ env, env->CallObjectMethod(values, gArrayListInfo.getId, i)};
+
+ if (env->IsInstanceOf(jvalue.get(), sFields.mStringClass)) {
+ const char *tmp = env->GetStringUTFChars((jstring)jvalue.get(), nullptr);
+ AString value;
+ if (tmp) {
+ value.setTo(tmp);
+ }
+ env->ReleaseStringUTFChars((jstring)jvalue.get(), tmp);
+ if (value.empty()) {
+ return NO_MEMORY;
+ }
+ result->setString(key.c_str(), value);
+ } else if (env->IsInstanceOf(jvalue.get(), sFields.mIntegerClass)) {
+ jint value = env->CallIntMethod(jvalue.get(), sFields.mIntegerValueId);
+ result->setInt32(key.c_str(), value);
+ } else if (env->IsInstanceOf(jvalue.get(), sFields.mLongClass)) {
+ jlong value = env->CallLongMethod(jvalue.get(), sFields.mLongValueId);
+ result->setInt64(key.c_str(), value);
+ } else if (env->IsInstanceOf(jvalue.get(), sFields.mFloatClass)) {
+ jfloat value = env->CallFloatMethod(jvalue.get(), sFields.mFloatValueId);
+ result->setFloat(key.c_str(), value);
+ } else if (env->IsInstanceOf(jvalue.get(), gByteBufferInfo.clazz)) {
+ jint position = env->CallIntMethod(jvalue.get(), gByteBufferInfo.positionId);
+ jint limit = env->CallIntMethod(jvalue.get(), gByteBufferInfo.limitId);
+ sp<ABuffer> buffer{new ABuffer(limit - position)};
+ void *data = env->GetDirectBufferAddress(jvalue.get());
+ if (data != nullptr) {
+ memcpy(buffer->data(),
+ static_cast<const uint8_t *>(data) + position,
+ buffer->size());
+ } else {
+ ScopedLocalRef<jbyteArray> byteArray{env, (jbyteArray)env->CallObjectMethod(
+ jvalue.get(), sFields.mByteBufferArrayId)};
+ env->GetByteArrayRegion(byteArray.get(), position, buffer->size(),
+ reinterpret_cast<jbyte *>(buffer->data()));
+ }
+ result->setBuffer(key.c_str(), buffer);
+ }
+ }
+
+ *msg = result;
+ return OK;
+}
+
+static void android_media_MediaCodec_native_queueLinearBlock(
+ JNIEnv *env, jobject thiz, jint index, jobject bufferObj,
+ jint offset, jint size, jobject cryptoInfoObj,
+ jlong presentationTimeUs, jint flags, jobject keys, jobject values) {
+ ALOGV("android_media_MediaCodec_native_queueLinearBlock");
+
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+ if (codec == nullptr) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return;
+ }
+
+ sp<AMessage> tunings;
+ status_t err = ConvertKeyValueListsToAMessage(env, keys, values, &tunings);
+ if (err != OK) {
+ throwExceptionAsNecessary(env, err);
+ return;
+ }
+
+ std::shared_ptr<C2Buffer> buffer;
+ sp<hardware::HidlMemory> memory;
+ ScopedLocalRef<jobject> lock{env, env->GetObjectField(bufferObj, gLinearBlockInfo.lockId)};
+ if (env->MonitorEnter(lock.get()) == JNI_OK) {
+ if (env->GetBooleanField(bufferObj, gLinearBlockInfo.validId)) {
+ JMediaCodecLinearBlock *context =
+ (JMediaCodecLinearBlock *)env->GetLongField(bufferObj, gLinearBlockInfo.contextId);
+ if (cryptoInfoObj != nullptr) {
+ memory = context->toHidlMemory();
+ } else {
+ buffer = context->toC2Buffer(offset, size);
+ }
+ }
+ env->MonitorExit(lock.get());
+ } else {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return;
+ }
+
+ AString errorDetailMsg;
+ if (cryptoInfoObj != nullptr) {
+ if (!memory) {
+ throwExceptionAsNecessary(env, BAD_VALUE);
+ return;
+ }
+
+ NativeCryptoInfo cryptoInfo{env, cryptoInfoObj};
+ size_t cryptoSize = 0;
+ for (int i = 0; i < cryptoInfo.mNumSubSamples; ++i) {
+ cryptoSize += cryptoInfo.mSubSamples[i].mNumBytesOfClearData;
+ cryptoSize += cryptoInfo.mSubSamples[i].mNumBytesOfEncryptedData;
+ }
+ err = codec->queueEncryptedLinearBlock(
+ index,
+ memory,
+ offset,
+ cryptoInfo.mSubSamples, cryptoInfo.mNumSubSamples,
+ (const uint8_t *)cryptoInfo.mKey, (const uint8_t *)cryptoInfo.mIv,
+ cryptoInfo.mMode,
+ cryptoInfo.mPattern,
+ presentationTimeUs,
+ flags,
+ tunings,
+ &errorDetailMsg);
+ } else {
+ if (!buffer) {
+ throwExceptionAsNecessary(env, BAD_VALUE);
+ return;
+ }
+ err = codec->queueBuffer(
+ index, buffer, presentationTimeUs, flags, tunings, &errorDetailMsg);
+ }
+ throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, errorDetailMsg.c_str());
+}
+
+static void android_media_MediaCodec_native_queueGraphicBlock(
+ JNIEnv *env, jobject thiz, jint index, jobject bufferObj,
+ jlong presentationTimeUs, jint flags, jobject keys, jobject values) {
+ ALOGV("android_media_MediaCodec_native_queueGraphicBlock");
+
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+ if (codec == NULL) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return;
+ }
+
+ sp<AMessage> tunings;
+ status_t err = ConvertKeyValueListsToAMessage(env, keys, values, &tunings);
+ if (err != OK) {
+ throwExceptionAsNecessary(env, err);
+ return;
+ }
+
+ std::shared_ptr<C2Buffer> buffer;
+ std::shared_ptr<C2GraphicBlock> block;
+ ScopedLocalRef<jobject> lock{env, env->GetObjectField(bufferObj, gGraphicBlockInfo.lockId)};
+ if (env->MonitorEnter(lock.get()) == JNI_OK) {
+ if (env->GetBooleanField(bufferObj, gGraphicBlockInfo.validId)) {
+ JMediaCodecGraphicBlock *context = (JMediaCodecGraphicBlock *)env->GetLongField(
+ bufferObj, gGraphicBlockInfo.contextId);
+ buffer = context->mBuffer;
+ block = context->mBlock;
+ }
+ env->MonitorExit(lock.get());
+ } else {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return;
+ }
+
+ if (!block && !buffer) {
+ throwExceptionAsNecessary(env, BAD_VALUE);
+ return;
+ }
+ if (!buffer) {
+ buffer = C2Buffer::CreateGraphicBuffer(block->share(block->crop(), C2Fence{}));
+ }
+ AString errorDetailMsg;
+ err = codec->queueBuffer(index, buffer, presentationTimeUs, flags, tunings, &errorDetailMsg);
+ throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, errorDetailMsg.c_str());
+}
+
+static void android_media_MediaCodec_native_getOutputFrame(
+ JNIEnv *env, jobject thiz, jobject frame, jint index) {
+ ALOGV("android_media_MediaCodec_native_getOutputFrame");
+
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+ if (codec == NULL) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return;
+ }
+
+ status_t err = codec->getOutputFrame(env, frame, index);
+ if (err != OK) {
+ throwExceptionAsNecessary(env, err);
+ }
+}
+
static jint android_media_MediaCodec_dequeueInputBuffer(
JNIEnv *env, jobject thiz, jlong timeoutUs) {
ALOGV("android_media_MediaCodec_dequeueInputBuffer");
@@ -1991,6 +2418,31 @@
gFields.patternSkipBlocksID = env->GetFieldID(clazz.get(), "mSkipBlocks", "I");
CHECK(gFields.patternSkipBlocksID != NULL);
+ clazz.reset(env->FindClass("android/media/MediaCodec$QueueRequest"));
+ CHECK(clazz.get() != NULL);
+
+ gFields.queueRequestIndexID = env->GetFieldID(clazz.get(), "mIndex", "I");
+ CHECK(gFields.queueRequestIndexID != NULL);
+
+ clazz.reset(env->FindClass("android/media/MediaCodec$OutputFrame"));
+ CHECK(clazz.get() != NULL);
+
+ gFields.outputFrameLinearBlockID =
+ env->GetFieldID(clazz.get(), "mLinearBlock", "Landroid/media/MediaCodec$LinearBlock;");
+ CHECK(gFields.outputFrameLinearBlockID != NULL);
+
+ gFields.outputFrameGraphicBlockID =
+ env->GetFieldID(clazz.get(), "mGraphicBlock", "Landroid/media/MediaCodec$GraphicBlock;");
+ CHECK(gFields.outputFrameGraphicBlockID != NULL);
+
+ gFields.outputFrameChangedKeysID =
+ env->GetFieldID(clazz.get(), "mChangedKeys", "Ljava/util/ArrayList;");
+ CHECK(gFields.outputFrameChangedKeysID != NULL);
+
+ gFields.outputFrameFormatID =
+ env->GetFieldID(clazz.get(), "mFormat", "Landroid/media/MediaFormat;");
+ CHECK(gFields.outputFrameFormatID != NULL);
+
clazz.reset(env->FindClass("android/media/MediaCodec$CryptoException"));
CHECK(clazz.get() != NULL);
@@ -2105,6 +2557,96 @@
field = env->GetFieldID(clazz.get(), "level", "I");
CHECK(field != NULL);
gCodecInfo.levelField = field;
+
+ clazz.reset(env->FindClass("java/nio/ByteBuffer"));
+ CHECK(clazz.get() != NULL);
+ gByteBufferInfo.clazz = (jclass)env->NewGlobalRef(clazz.get());
+
+ ScopedLocalRef<jclass> byteOrderClass(
+ env, env->FindClass("java/nio/ByteOrder"));
+ CHECK(byteOrderClass.get() != NULL);
+
+ jmethodID nativeOrderID = env->GetStaticMethodID(
+ byteOrderClass.get(), "nativeOrder", "()Ljava/nio/ByteOrder;");
+ CHECK(nativeOrderID != NULL);
+
+ ScopedLocalRef<jobject> nativeByteOrderObj{
+ env, env->CallStaticObjectMethod(byteOrderClass.get(), nativeOrderID)};
+ gByteBufferInfo.nativeByteOrder = env->NewGlobalRef(nativeByteOrderObj.get());
+ CHECK(gByteBufferInfo.nativeByteOrder != NULL);
+ nativeByteOrderObj.reset();
+
+ gByteBufferInfo.orderId = env->GetMethodID(
+ clazz.get(),
+ "order",
+ "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;");
+ CHECK(gByteBufferInfo.orderId != NULL);
+
+ gByteBufferInfo.asReadOnlyBufferId = env->GetMethodID(
+ clazz.get(), "asReadOnlyBuffer", "()Ljava/nio/ByteBuffer;");
+ CHECK(gByteBufferInfo.asReadOnlyBufferId != NULL);
+
+ gByteBufferInfo.positionId = env->GetMethodID(
+ clazz.get(), "position", "(I)Ljava/nio/Buffer;");
+ CHECK(gByteBufferInfo.positionId != NULL);
+
+ gByteBufferInfo.limitId = env->GetMethodID(
+ clazz.get(), "limit", "(I)Ljava/nio/Buffer;");
+ CHECK(gByteBufferInfo.limitId != NULL);
+
+ clazz.reset(env->FindClass("java/util/ArrayList"));
+ CHECK(clazz.get() != NULL);
+
+ gArrayListInfo.sizeId = env->GetMethodID(clazz.get(), "size", "()I");
+ CHECK(gArrayListInfo.sizeId != NULL);
+
+ gArrayListInfo.getId = env->GetMethodID(clazz.get(), "get", "(I)Ljava/lang/Object;");
+ CHECK(gArrayListInfo.getId != NULL);
+
+ gArrayListInfo.addId = env->GetMethodID(clazz.get(), "add", "(Ljava/lang/Object;)Z");
+ CHECK(gArrayListInfo.addId != NULL);
+
+ clazz.reset(env->FindClass("android/media/MediaCodec$LinearBlock"));
+ CHECK(clazz.get() != NULL);
+
+ gLinearBlockInfo.clazz = (jclass)env->NewGlobalRef(clazz.get());
+
+ gLinearBlockInfo.ctorId = env->GetMethodID(clazz.get(), "<init>", "()V");
+ CHECK(gLinearBlockInfo.ctorId != NULL);
+
+ gLinearBlockInfo.setInternalStateId = env->GetMethodID(
+ clazz.get(), "setInternalStateLocked", "(JZ)V");
+ CHECK(gLinearBlockInfo.setInternalStateId != NULL);
+
+ gLinearBlockInfo.contextId = env->GetFieldID(clazz.get(), "mNativeContext", "J");
+ CHECK(gLinearBlockInfo.contextId != NULL);
+
+ gLinearBlockInfo.validId = env->GetFieldID(clazz.get(), "mValid", "Z");
+ CHECK(gLinearBlockInfo.validId != NULL);
+
+ gLinearBlockInfo.lockId = env->GetFieldID(clazz.get(), "mLock", "Ljava/lang/Object;");
+ CHECK(gLinearBlockInfo.lockId != NULL);
+
+ clazz.reset(env->FindClass("android/media/MediaCodec$GraphicBlock"));
+ CHECK(clazz.get() != NULL);
+
+ gGraphicBlockInfo.clazz = (jclass)env->NewGlobalRef(clazz.get());
+
+ gGraphicBlockInfo.ctorId = env->GetMethodID(clazz.get(), "<init>", "()V");
+ CHECK(gGraphicBlockInfo.ctorId != NULL);
+
+ gGraphicBlockInfo.setInternalStateId = env->GetMethodID(
+ clazz.get(), "setInternalStateLocked", "(JZ)V");
+ CHECK(gGraphicBlockInfo.setInternalStateId != NULL);
+
+ gGraphicBlockInfo.contextId = env->GetFieldID(clazz.get(), "mNativeContext", "J");
+ CHECK(gGraphicBlockInfo.contextId != NULL);
+
+ gGraphicBlockInfo.validId = env->GetFieldID(clazz.get(), "mValid", "Z");
+ CHECK(gGraphicBlockInfo.validId != NULL);
+
+ gGraphicBlockInfo.lockId = env->GetFieldID(clazz.get(), "mLock", "Ljava/lang/Object;");
+ CHECK(gGraphicBlockInfo.lockId != NULL);
}
static void android_media_MediaCodec_native_setup(
@@ -2155,6 +2697,345 @@
android_media_MediaCodec_release(env, thiz);
}
+// MediaCodec.LinearBlock
+
+static jobject android_media_MediaCodec_LinearBlock_native_map(
+ JNIEnv *env, jobject thiz) {
+ JMediaCodecLinearBlock *context =
+ (JMediaCodecLinearBlock *)env->GetLongField(thiz, gLinearBlockInfo.contextId);
+ if (context->mBuffer) {
+ std::shared_ptr<C2Buffer> buffer = context->mBuffer;
+ if (!context->mReadonlyMapping) {
+ const C2BufferData data = buffer->data();
+ if (data.type() != C2BufferData::LINEAR) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return nullptr;
+ }
+ if (data.linearBlocks().size() != 1u) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return nullptr;
+ }
+ C2ConstLinearBlock block = data.linearBlocks().front();
+ context->mReadonlyMapping =
+ std::make_shared<C2ReadView>(block.map().get());
+ }
+ return CreateByteBuffer(
+ env,
+ context->mReadonlyMapping->data(), // base
+ context->mReadonlyMapping->capacity(), // capacity
+ 0u, // offset
+ context->mReadonlyMapping->capacity(), // size
+ true, // readOnly
+ true /* clearBuffer */);
+ } else if (context->mBlock) {
+ std::shared_ptr<C2LinearBlock> block = context->mBlock;
+ if (!context->mReadWriteMapping) {
+ context->mReadWriteMapping =
+ std::make_shared<C2WriteView>(block->map().get());
+ }
+ return CreateByteBuffer(
+ env,
+ context->mReadWriteMapping->base(),
+ context->mReadWriteMapping->capacity(),
+ context->mReadWriteMapping->offset(),
+ context->mReadWriteMapping->size(),
+ false, // readOnly
+ true /* clearBuffer */);
+ } else if (context->mLegacyBuffer) {
+ return CreateByteBuffer(
+ env,
+ context->mLegacyBuffer->base(),
+ context->mLegacyBuffer->capacity(),
+ context->mLegacyBuffer->offset(),
+ context->mLegacyBuffer->size(),
+ true, // readOnly
+ true /* clearBuffer */);
+ } else if (context->mHeap) {
+ return CreateByteBuffer(
+ env,
+ static_cast<uint8_t *>(context->mHeap->getBase()) + context->mHeap->getOffset(),
+ context->mHeap->getSize(),
+ 0,
+ context->mHeap->getSize(),
+ false, // readOnly
+ true /* clearBuffer */);
+ }
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return nullptr;
+}
+
+static void android_media_MediaCodec_LinearBlock_native_recycle(
+ JNIEnv *env, jobject thiz) {
+ JMediaCodecLinearBlock *context =
+ (JMediaCodecLinearBlock *)env->GetLongField(thiz, gLinearBlockInfo.contextId);
+ env->CallVoidMethod(thiz, gLinearBlockInfo.setInternalStateId, 0, false);
+ delete context;
+}
+
+static void PopulateNamesVector(
+ JNIEnv *env, jobjectArray codecNames, std::vector<std::string> *names) {
+ jsize length = env->GetArrayLength(codecNames);
+ for (jsize i = 0; i < length; ++i) {
+ jstring jstr = static_cast<jstring>(env->GetObjectArrayElement(codecNames, i));
+ if (jstr == nullptr) {
+ // null entries are ignored
+ continue;
+ }
+ const char *cstr = env->GetStringUTFChars(jstr, nullptr);
+ if (cstr == nullptr) {
+ throwExceptionAsNecessary(env, BAD_VALUE);
+ return;
+ }
+ names->emplace_back(cstr);
+ env->ReleaseStringUTFChars(jstr, cstr);
+ }
+}
+
+static void android_media_MediaCodec_LinearBlock_native_obtain(
+ JNIEnv *env, jobject thiz, jint capacity, jobjectArray codecNames) {
+ std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
+ std::vector<std::string> names;
+ PopulateNamesVector(env, codecNames, &names);
+ bool hasSecure = false;
+ bool hasNonSecure = false;
+ for (const std::string &name : names) {
+ if (name.length() >= 7 && name.substr(name.length() - 7) == ".secure") {
+ hasSecure = true;
+ } else {
+ hasNonSecure = true;
+ }
+ }
+ if (hasSecure && !hasNonSecure) {
+ context->mHeap = new MemoryHeapBase(capacity);
+ context->mMemory = hardware::fromHeap(context->mHeap);
+ } else {
+ context->mBlock = MediaCodec::FetchLinearBlock(capacity, names);
+ if (!context->mBlock) {
+ jniThrowException(env, "java/io/IOException", nullptr);
+ return;
+ }
+ }
+ env->CallVoidMethod(
+ thiz,
+ gLinearBlockInfo.setInternalStateId,
+ (jlong)context.release(),
+ true /* isMappable */);
+}
+
+static jboolean android_media_MediaCodec_LinearBlock_checkCompatible(
+ JNIEnv *env, jobjectArray codecNames) {
+ std::vector<std::string> names;
+ PopulateNamesVector(env, codecNames, &names);
+ bool isCompatible = false;
+ bool hasSecure = false;
+ bool hasNonSecure = false;
+ for (const std::string &name : names) {
+ if (name.length() >= 7 && name.substr(name.length() - 7) == ".secure") {
+ hasSecure = true;
+ } else {
+ hasNonSecure = true;
+ }
+ }
+ if (hasSecure && hasNonSecure) {
+ return false;
+ }
+ status_t err = MediaCodec::CanFetchLinearBlock(names, &isCompatible);
+ if (err != OK) {
+ throwExceptionAsNecessary(env, err);
+ }
+ return isCompatible;
+}
+
+// MediaCodec.GraphicBlock
+
+template <class T>
+static jobject CreateImage(JNIEnv *env, const std::shared_ptr<T> &view) {
+ bool readOnly = std::is_const<T>::value;
+ const C2PlanarLayout layout = view->layout();
+ jint format = HAL_PIXEL_FORMAT_YCBCR_420_888;
+ switch (layout.type) {
+ case C2PlanarLayout::TYPE_YUV: {
+ if (layout.numPlanes != 3) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return nullptr;
+ }
+ const C2PlaneInfo & yPlane = layout.planes[C2PlanarLayout::PLANE_Y];
+ const C2PlaneInfo & uPlane = layout.planes[C2PlanarLayout::PLANE_U];
+ const C2PlaneInfo & vPlane = layout.planes[C2PlanarLayout::PLANE_V];
+ if (yPlane.rowSampling != 1 || yPlane.colSampling != 1) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return nullptr;
+ }
+ if (uPlane.rowSampling != vPlane.rowSampling
+ || uPlane.colSampling != vPlane.colSampling) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return nullptr;
+ }
+ if (uPlane.rowSampling == 2 && uPlane.colSampling == 2) {
+ format = HAL_PIXEL_FORMAT_YCBCR_420_888;
+ break;
+ } else if (uPlane.rowSampling == 1 && uPlane.colSampling == 2) {
+ format = HAL_PIXEL_FORMAT_YCBCR_422_888;
+ break;
+ } else if (uPlane.rowSampling == 1 && uPlane.colSampling == 1) {
+ format = HAL_PIXEL_FORMAT_YCBCR_444_888;
+ break;
+ }
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return nullptr;
+ }
+ case C2PlanarLayout::TYPE_RGB: {
+ if (layout.numPlanes != 3) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return nullptr;
+ }
+ format = HAL_PIXEL_FORMAT_FLEX_RGB_888;
+ break;
+ }
+ case C2PlanarLayout::TYPE_RGBA: {
+ if (layout.numPlanes != 4) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return nullptr;
+ }
+ format = HAL_PIXEL_FORMAT_FLEX_RGBA_8888;
+ break;
+ }
+ case C2PlanarLayout::TYPE_YUVA:
+ [[fallthrough]];
+ default:
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return nullptr;
+ }
+
+ ScopedLocalRef<jclass> planeClazz(
+ env, env->FindClass("android/media/MediaCodec$MediaImage$MediaPlane"));
+ ScopedLocalRef<jobjectArray> planeArray{
+ env, env->NewObjectArray(layout.numPlanes, planeClazz.get(), NULL)};
+ CHECK(planeClazz.get() != NULL);
+ jmethodID planeConstructID = env->GetMethodID(planeClazz.get(), "<init>",
+ "([Ljava/nio/ByteBuffer;IIIII)V");
+
+ // plane indices are happened to be Y-U-V and R-G-B(-A) order.
+ for (uint32_t i = 0; i < layout.numPlanes; ++i) {
+ const C2PlaneInfo &plane = layout.planes[i];
+ if (plane.rowInc < 0 || plane.colInc < 0) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return nullptr;
+ }
+ ssize_t minOffset = plane.minOffset(view->width(), view->height());
+ ssize_t maxOffset = plane.maxOffset(view->width(), view->height());
+ ScopedLocalRef<jobject> byteBuffer{env, CreateByteBuffer(
+ env,
+ view->data()[plane.rootIx] + plane.offset + minOffset,
+ maxOffset - minOffset + 1,
+ 0,
+ maxOffset - minOffset + 1,
+ readOnly,
+ true)};
+
+ ScopedLocalRef<jobject> jPlane{env, env->NewObject(
+ planeClazz.get(), planeConstructID,
+ byteBuffer.get(), plane.rowInc, plane.colInc)};
+ }
+
+ ScopedLocalRef<jclass> imageClazz(
+ env, env->FindClass("android/media/MediaCodec$MediaImage"));
+ CHECK(imageClazz.get() != NULL);
+
+ jmethodID imageConstructID = env->GetMethodID(imageClazz.get(), "<init>",
+ "([Landroid/media/Image$Plane;IIIZJIILandroid/graphics/Rect;)V");
+
+ jobject img = env->NewObject(imageClazz.get(), imageConstructID,
+ planeArray.get(),
+ view->width(),
+ view->height(),
+ format,
+ (jboolean)readOnly /* readOnly */,
+ (jlong)0 /* timestamp */,
+ (jint)0 /* xOffset */, (jint)0 /* yOffset */, nullptr /* cropRect */);
+
+ // if MediaImage creation fails, return null
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ return nullptr;
+ }
+
+ return img;
+}
+
+static jobject android_media_MediaCodec_GraphicBlock_native_map(
+ JNIEnv *env, jobject thiz) {
+ JMediaCodecGraphicBlock *context =
+ (JMediaCodecGraphicBlock *)env->GetLongField(thiz, gGraphicBlockInfo.contextId);
+ if (context->mBuffer) {
+ std::shared_ptr<C2Buffer> buffer = context->mBuffer;
+ if (!context->mReadonlyMapping) {
+ const C2BufferData data = buffer->data();
+ if (data.type() != C2BufferData::GRAPHIC) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return nullptr;
+ }
+ if (data.graphicBlocks().size() != 1u) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return nullptr;
+ }
+ C2ConstGraphicBlock block = data.graphicBlocks().front();
+ context->mReadonlyMapping =
+ std::make_shared<const C2GraphicView>(block.map().get());
+ }
+ return CreateImage(env, context->mReadonlyMapping);
+ } else if (context->mBlock) {
+ std::shared_ptr<C2GraphicBlock> block = context->mBlock;
+ if (!context->mReadWriteMapping) {
+ context->mReadWriteMapping =
+ std::make_shared<C2GraphicView>(block->map().get());
+ }
+ return CreateImage(env, context->mReadWriteMapping);
+ } else if (context->mLegacyBuffer) {
+ }
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return nullptr;
+}
+
+static void android_media_MediaCodec_GraphicBlock_native_recycle(
+ JNIEnv *env, jobject thiz) {
+ JMediaCodecGraphicBlock *context =
+ (JMediaCodecGraphicBlock *)env->GetLongField(thiz, gGraphicBlockInfo.contextId);
+ env->CallVoidMethod(thiz, gGraphicBlockInfo.setInternalStateId, 0, false /* isMappable */);
+ delete context;
+}
+
+static void android_media_MediaCodec_GraphicBlock_native_obtain(
+ JNIEnv *env, jobject thiz,
+ jint width, jint height, jint format, jlong usage, jobjectArray codecNames) {
+ std::unique_ptr<JMediaCodecGraphicBlock> context{new JMediaCodecGraphicBlock};
+ std::vector<std::string> names;
+ PopulateNamesVector(env, codecNames, &names);
+ context->mBlock = MediaCodec::FetchGraphicBlock(width, height, format, usage, names);
+ if (!context->mBlock) {
+ jniThrowException(env, "java/io/IOException", nullptr);
+ return;
+ }
+ env->CallVoidMethod(
+ thiz,
+ gGraphicBlockInfo.setInternalStateId,
+ (jlong)context.release(),
+ true /*isMappable */);
+}
+
+static jboolean android_media_MediaCodec_GraphicBlock_checkCompatible(
+ JNIEnv *env, jobjectArray codecNames) {
+ std::vector<std::string> names;
+ PopulateNamesVector(env, codecNames, &names);
+ bool isCompatible = false;
+ status_t err = MediaCodec::CanFetchGraphicBlock(names, &isCompatible);
+ if (err != OK) {
+ throwExceptionAsNecessary(env, err);
+ }
+ return isCompatible;
+}
+
static const JNINativeMethod gMethods[] = {
{ "native_release", "()V", (void *)android_media_MediaCodec_release },
@@ -2200,6 +3081,19 @@
{ "native_queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V",
(void *)android_media_MediaCodec_queueSecureInputBuffer },
+ { "native_queueLinearBlock",
+ "(ILandroid/media/MediaCodec$LinearBlock;IILandroid/media/MediaCodec$CryptoInfo;JI"
+ "Ljava/util/ArrayList;Ljava/util/ArrayList;)V",
+ (void *)android_media_MediaCodec_native_queueLinearBlock },
+
+ { "native_queueGraphicBlock",
+ "(ILandroid/media/MediaCodec$GraphicBlock;JILjava/util/ArrayList;Ljava/util/ArrayList;)V",
+ (void *)android_media_MediaCodec_native_queueGraphicBlock },
+
+ { "native_getOutputFrame",
+ "(Landroid/media/MediaCodec$OutputFrame;I)V",
+ (void *)android_media_MediaCodec_native_getOutputFrame },
+
{ "native_dequeueInputBuffer", "(J)I",
(void *)android_media_MediaCodec_dequeueInputBuffer },
@@ -2254,7 +3148,50 @@
(void *)android_media_MediaCodec_native_finalize },
};
+static const JNINativeMethod gLinearBlockMethods[] = {
+ { "native_map", "()Ljava/nio/ByteBuffer;",
+ (void *)android_media_MediaCodec_LinearBlock_native_map },
+
+ { "native_recycle", "()V",
+ (void *)android_media_MediaCodec_LinearBlock_native_recycle },
+
+ { "native_obtain", "(I[Ljava/lang/String;)V",
+ (void *)android_media_MediaCodec_LinearBlock_native_obtain },
+
+ { "native_checkCompatible", "([Ljava/lang/String;)Z",
+ (void *)android_media_MediaCodec_LinearBlock_checkCompatible },
+};
+
+static const JNINativeMethod gGraphicBlockMethods[] = {
+ { "native_map", "()Landroid/media/Image;",
+ (void *)android_media_MediaCodec_GraphicBlock_native_map },
+
+ { "native_recycle", "()V",
+ (void *)android_media_MediaCodec_GraphicBlock_native_recycle },
+
+ { "native_obtain", "(IIIJ[Ljava/lang/String;)V",
+ (void *)android_media_MediaCodec_GraphicBlock_native_obtain },
+
+ { "native_checkCompatible", "([Ljava/lang/String;)Z",
+ (void *)android_media_MediaCodec_GraphicBlock_checkCompatible },
+};
+
int register_android_media_MediaCodec(JNIEnv *env) {
- return AndroidRuntime::registerNativeMethods(env,
+ int result = AndroidRuntime::registerNativeMethods(env,
"android/media/MediaCodec", gMethods, NELEM(gMethods));
+ if (result != JNI_OK) {
+ return result;
+ }
+ result = AndroidRuntime::registerNativeMethods(env,
+ "android/media/MediaCodec$LinearBlock",
+ gLinearBlockMethods,
+ NELEM(gLinearBlockMethods));
+ if (result != JNI_OK) {
+ return result;
+ }
+ result = AndroidRuntime::registerNativeMethods(env,
+ "android/media/MediaCodec$GraphicBlock",
+ gGraphicBlockMethods,
+ NELEM(gGraphicBlockMethods));
+ return result;
}
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index ce1c805..1d12e77 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -27,6 +27,8 @@
#include <media/stagefright/foundation/AHandler.h>
#include <utils/Errors.h>
+class C2Buffer;
+
namespace android {
struct ABuffer;
@@ -39,6 +41,7 @@
struct PersistentSurface;
class Surface;
namespace hardware {
+class HidlMemory;
namespace cas {
namespace native {
namespace V1_0 {
@@ -97,6 +100,26 @@
uint32_t flags,
AString *errorDetailMsg);
+ status_t queueBuffer(
+ size_t index, const std::shared_ptr<C2Buffer> &buffer,
+ int64_t timeUs, uint32_t flags, const sp<AMessage> &tunings,
+ AString *errorDetailMsg);
+
+ status_t queueEncryptedLinearBlock(
+ size_t index,
+ const sp<hardware::HidlMemory> &buffer,
+ size_t offset,
+ const CryptoPlugin::SubSample *subSamples,
+ size_t numSubSamples,
+ const uint8_t key[16],
+ const uint8_t iv[16],
+ CryptoPlugin::Mode mode,
+ const CryptoPlugin::Pattern &pattern,
+ int64_t presentationTimeUs,
+ uint32_t flags,
+ const sp<AMessage> &tunings,
+ AString *errorDetailMsg);
+
status_t dequeueInputBuffer(size_t *index, int64_t timeoutUs);
status_t dequeueOutputBuffer(
@@ -120,6 +143,9 @@
status_t getImage(
JNIEnv *env, bool input, size_t index, jobject *image) const;
+ status_t getOutputFrame(
+ JNIEnv *env, jobject frame, size_t index) const;
+
status_t getName(JNIEnv *env, jstring *name) const;
status_t getCodecInfo(JNIEnv *env, jobject *codecInfo) const;
@@ -147,17 +173,10 @@
jweak mObject;
sp<Surface> mSurfaceTextureClient;
- // java objects cached
- jclass mByteBufferClass;
- jobject mNativeByteOrderObj;
- jmethodID mByteBufferOrderMethodID;
- jmethodID mByteBufferPositionMethodID;
- jmethodID mByteBufferLimitMethodID;
- jmethodID mByteBufferAsReadOnlyBufferMethodID;
-
sp<ALooper> mLooper;
sp<MediaCodec> mCodec;
AString mNameAtCreation;
+ bool mGraphicOutput{false};
std::once_flag mReleaseFlag;
sp<AMessage> mCallbackNotification;
@@ -170,8 +189,6 @@
JNIEnv *env, bool readOnly, bool clearBuffer, const sp<T> &buffer,
jobject *buf) const;
- void cacheJavaObjects(JNIEnv *env);
- void deleteJavaObjects(JNIEnv *env);
void handleCallback(const sp<AMessage> &msg);
void handleFrameRenderedNotification(const sp<AMessage> &msg);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index f979fdd..c529952 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -311,6 +311,12 @@
cameraId, status));
}
@Override
+ public void onPhysicalCameraStatusChanged(int status, String cameraId,
+ String physicalCameraId) throws RemoteException {
+ Log.v(TAG, String.format("Camera %s : %s has status changed to 0x%x",
+ cameraId, physicalCameraId, status));
+ }
+ @Override
public void onCameraAccessPrioritiesChanged() {
Log.v(TAG, "Camera access permission change");
}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 203adfc..97b861b 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -240,6 +240,7 @@
ASurfaceTransaction_setColor; # introduced=29
ASurfaceTransaction_setDamageRegion; # introduced=29
ASurfaceTransaction_setDesiredPresentTime; # introduced=29
+ ASurfaceTransaction_setFrameRate; # introduced=30
ASurfaceTransaction_setGeometry; # introduced=29
ASurfaceTransaction_setHdrMetadata_cta861_3; # introduced=29
ASurfaceTransaction_setHdrMetadata_smpte2086; # introduced=29
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index b34b31a..392c9f6 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -545,3 +545,18 @@
transaction->setBackgroundColor(surfaceControl, color, alpha, static_cast<ui::Dataspace>(dataspace));
}
+
+void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* aSurfaceTransaction,
+ ASurfaceControl* aSurfaceControl, float frameRate) {
+ CHECK_NOT_NULL(aSurfaceTransaction);
+ CHECK_NOT_NULL(aSurfaceControl);
+
+ sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+ if (frameRate < 0) {
+ ALOGE("Failed to set frame ate - invalid frame rate");
+ return;
+ }
+
+ Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+ transaction->setFrameRate(surfaceControl, frameRate);
+}
diff --git a/native/graphics/jni/bitmap.cpp b/native/graphics/jni/bitmap.cpp
index ea8a521..b8eb543 100644
--- a/native/graphics/jni/bitmap.cpp
+++ b/native/graphics/jni/bitmap.cpp
@@ -33,10 +33,14 @@
int32_t AndroidBitmap_getDataSpace(JNIEnv* env, jobject jbitmap) {
if (NULL == env || NULL == jbitmap) {
- return ADATASPACE_UNKNOWN; // Or return a real error?
+ return ADATASPACE_UNKNOWN;
}
android::graphics::Bitmap bitmap(env, jbitmap);
+ if (!bitmap.isValid()) {
+ return ADATASPACE_UNKNOWN;
+ }
+
return bitmap.getDataSpace();
}
@@ -76,12 +80,27 @@
return ANDROID_BITMAP_RESULT_SUCCESS;
}
+int AndroidBitmap_getHardwareBuffer(JNIEnv* env, jobject jbitmap, AHardwareBuffer** outBuffer) {
+ if (NULL == env || NULL == jbitmap || NULL == outBuffer) {
+ return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
+ }
+
+ android::graphics::Bitmap bitmap(env, jbitmap);
+
+ if (!bitmap.isValid()) {
+ return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
+ }
+
+ *outBuffer = bitmap.getHardwareBuffer();
+ return *outBuffer == NULL ? ANDROID_BITMAP_RESULT_BAD_PARAMETER : ANDROID_BITMAP_RESULT_SUCCESS;
+}
+
int AndroidBitmap_compress(const AndroidBitmapInfo* info,
int32_t dataSpace,
const void* pixels,
int32_t format, int32_t quality,
void* userContext,
- AndroidBitmap_compress_write_fn fn) {
+ AndroidBitmap_CompressWriteFunc fn) {
if (NULL == info || NULL == pixels || NULL == fn) {
return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
}
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index 5143967..79bcc15 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -18,12 +18,14 @@
#include <android/asset_manager.h>
#include <android/bitmap.h>
+#include <android/data_space.h>
#include <android/imagedecoder.h>
#include <android/graphics/MimeType.h>
#include <android/rect.h>
#include <hwui/ImageDecoder.h>
#include <log/log.h>
#include <SkAndroidCodec.h>
+#include <utils/Color.h>
#include <fcntl.h>
#include <optional>
@@ -131,6 +133,10 @@
return reinterpret_cast<ImageDecoder*>(d);
}
+static const ImageDecoder* toDecoder(const AImageDecoder* d) {
+ return reinterpret_cast<const ImageDecoder*>(d);
+}
+
// Note: This differs from the version in android_bitmap.cpp in that this
// version returns kGray_8_SkColorType for ANDROID_BITMAP_FORMAT_A_8. SkCodec
// allows decoding single channel images to gray, which Android then treats
@@ -161,6 +167,18 @@
? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_CONVERSION;
}
+int AImageDecoder_setDataSpace(AImageDecoder* decoder, int32_t dataspace) {
+ sk_sp<SkColorSpace> cs = uirenderer::DataSpaceToColorSpace((android_dataspace)dataspace);
+ // 0 is ADATASPACE_UNKNOWN. We need an explicit request for an ADataSpace.
+ if (!decoder || !dataspace || !cs) {
+ return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+ }
+
+ ImageDecoder* imageDecoder = toDecoder(decoder);
+ imageDecoder->setOutColorSpace(std::move(cs));
+ return ANDROID_IMAGE_DECODER_SUCCESS;
+}
+
const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo(const AImageDecoder* decoder) {
return reinterpret_cast<const AImageDecoderHeaderInfo*>(decoder);
}
@@ -190,11 +208,18 @@
return getMimeType(toDecoder(info)->mCodec->getEncodedFormat());
}
-bool AImageDecoderHeaderInfo_isAnimated(const AImageDecoderHeaderInfo* info) {
+int32_t AImageDecoderHeaderInfo_getDataSpace(const AImageDecoderHeaderInfo* info) {
if (!info) {
- return false;
+ return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
- return toDecoder(info)->mCodec->codec()->getFrameCount() > 1;
+
+ // Note: This recomputes the data space because it's possible the client has
+ // changed the output color space, so we cannot rely on it. Alternatively,
+ // we could store the ADataSpace in the ImageDecoder.
+ const ImageDecoder* imageDecoder = toDecoder(info);
+ SkColorType colorType = imageDecoder->mCodec->computeOutputColorType(kN32_SkColorType);
+ sk_sp<SkColorSpace> colorSpace = imageDecoder->mCodec->computeOutputColorSpace(colorType);
+ return uirenderer::ColorSpaceToADataSpace(colorSpace.get(), colorType);
}
// FIXME: Share with getFormat in android_bitmap.cpp?
@@ -215,8 +240,7 @@
}
}
-AndroidBitmapFormat AImageDecoderHeaderInfo_getAndroidBitmapFormat(
- const AImageDecoderHeaderInfo* info) {
+int32_t AImageDecoderHeaderInfo_getAndroidBitmapFormat(const AImageDecoderHeaderInfo* info) {
if (!info) {
return ANDROID_BITMAP_FORMAT_NONE;
}
@@ -249,7 +273,7 @@
? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_CONVERSION;
}
-int AImageDecoder_setTargetSize(AImageDecoder* decoder, int width, int height) {
+int AImageDecoder_setTargetSize(AImageDecoder* decoder, int32_t width, int32_t height) {
if (!decoder) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
@@ -258,6 +282,18 @@
? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_SCALE;
}
+int AImageDecoder_computeSampledSize(const AImageDecoder* decoder, int sampleSize,
+ int32_t* width, int32_t* height) {
+ if (!decoder || !width || !height || sampleSize < 1) {
+ return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+ }
+
+ SkISize size = toDecoder(decoder)->mCodec->getSampledDimensions(sampleSize);
+ *width = size.width();
+ *height = size.height();
+ return ANDROID_IMAGE_DECODER_SUCCESS;
+}
+
int AImageDecoder_setCrop(AImageDecoder* decoder, ARect crop) {
if (!decoder) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
diff --git a/native/graphics/jni/libjnigraphics.map.txt b/native/graphics/jni/libjnigraphics.map.txt
index 6843e7a..01c1477 100644
--- a/native/graphics/jni/libjnigraphics.map.txt
+++ b/native/graphics/jni/libjnigraphics.map.txt
@@ -6,22 +6,25 @@
AImageDecoder_delete; # introduced=30
AImageDecoder_setAndroidBitmapFormat; # introduced=30
AImageDecoder_setUnpremultipliedRequired; # introduced=30
+ AImageDecoder_setDataSpace; # introduced=30
AImageDecoder_getHeaderInfo; # introduced=30
AImageDecoder_getMinimumStride; # introduced=30
AImageDecoder_decodeImage; # introduced=30
AImageDecoder_setTargetSize; # introduced=30
+ AImageDecoder_computeSampledSize; # introduced=30
AImageDecoder_setCrop; # introduced=30
AImageDecoderHeaderInfo_getWidth; # introduced=30
AImageDecoderHeaderInfo_getHeight; # introduced=30
AImageDecoderHeaderInfo_getMimeType; # introduced=30
AImageDecoderHeaderInfo_getAlphaFlags; # introduced=30
- AImageDecoderHeaderInfo_isAnimated; # introduced=30
AImageDecoderHeaderInfo_getAndroidBitmapFormat; # introduced=30
+ AImageDecoderHeaderInfo_getDataSpace; # introduced=30
AndroidBitmap_getInfo;
AndroidBitmap_getDataSpace;
AndroidBitmap_lockPixels;
AndroidBitmap_unlockPixels;
AndroidBitmap_compress; # introduced=30
+ AndroidBitmap_getHardwareBuffer; #introduced=30
local:
*;
};
diff --git a/opengl/java/com/google/android/gles_jni/EGLImpl.java b/opengl/java/com/google/android/gles_jni/EGLImpl.java
index f94f69f..b4ea0a6 100644
--- a/opengl/java/com/google/android/gles_jni/EGLImpl.java
+++ b/opengl/java/com/google/android/gles_jni/EGLImpl.java
@@ -16,13 +16,12 @@
package com.google.android.gles_jni;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.SurfaceTexture;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
diff --git a/opengl/java/com/google/android/gles_jni/GLImpl.java b/opengl/java/com/google/android/gles_jni/GLImpl.java
index 2a8d07f..3c808a6 100644
--- a/opengl/java/com/google/android/gles_jni/GLImpl.java
+++ b/opengl/java/com/google/android/gles_jni/GLImpl.java
@@ -20,14 +20,13 @@
package com.google.android.gles_jni;
import android.app.AppGlobals;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.os.Build;
import android.os.UserHandle;
import android.util.Log;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import java.nio.Buffer;
import javax.microedition.khronos.opengles.GL10;
diff --git a/packages/CarSystemUI/res/layout/super_notification_shade.xml b/packages/CarSystemUI/res/layout/super_notification_shade.xml
index 3fe1ea3..cb65045 100644
--- a/packages/CarSystemUI/res/layout/super_notification_shade.xml
+++ b/packages/CarSystemUI/res/layout/super_notification_shade.xml
@@ -59,24 +59,6 @@
sysui:ignoreRightInset="true"
/>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="@dimen/status_bar_height"
- android:orientation="vertical"
- >
- <FrameLayout
- android:id="@+id/status_bar_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="gone"
- />
-
- <FrameLayout
- android:id="@+id/car_top_navigation_bar_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- </LinearLayout>
-
<include layout="@layout/brightness_mirror"/>
<ViewStub android:id="@+id/fullscreen_user_switcher_stub"
diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml
index c7b22f8..d93f62f 100644
--- a/packages/CarSystemUI/res/layout/super_status_bar.xml
+++ b/packages/CarSystemUI/res/layout/super_status_bar.xml
@@ -25,9 +25,22 @@
android:layout_height="match_parent"
android:fitsSystemWindows="true">
- <FrameLayout
- android:id="@+id/status_bar_container"
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ >
+ <FrameLayout
+ android:id="@+id/status_bar_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ />
+
+ <FrameLayout
+ android:id="@+id/car_top_navigation_bar_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
</com.android.systemui.statusbar.phone.StatusBarWindowView>
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/SystemUIPrimaryWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/SystemUIPrimaryWindowController.java
index c7e14d6..3f55ac8 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/SystemUIPrimaryWindowController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/SystemUIPrimaryWindowController.java
@@ -113,7 +113,7 @@
PixelFormat.TRANSLUCENT);
mLp.token = new Binder();
mLp.gravity = Gravity.TOP;
- mLp.setFitWindowInsetsTypes(/* types= */ 0);
+ mLp.setFitInsetsTypes(/* types= */ 0);
mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
mLp.setTitle("SystemUIPrimaryWindow");
mLp.packageName = mContext.getPackageName();
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
index 78764dd..3a52015 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
@@ -235,7 +235,7 @@
private void buildNavBarWindows() {
mTopNavigationBarWindow = mSuperStatusBarViewFactory
- .getNotificationShadeWindowView()
+ .getStatusBarWindowView()
.findViewById(R.id.car_top_navigation_bar_container);
mBottomNavigationBarWindow = mCarNavigationBarController.getBottomWindow();
mLeftNavigationBarWindow = mCarNavigationBarController.getLeftWindow();
@@ -296,7 +296,7 @@
leftlp.windowAnimations = 0;
leftlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
leftlp.gravity = Gravity.LEFT;
- leftlp.setFitWindowInsetsTypes(0 /* types */);
+ leftlp.setFitInsetsTypes(0 /* types */);
mWindowManager.addView(mLeftNavigationBarWindow, leftlp);
}
if (mRightNavigationBarWindow != null) {
@@ -314,7 +314,7 @@
rightlp.windowAnimations = 0;
rightlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
rightlp.gravity = Gravity.RIGHT;
- rightlp.setFitWindowInsetsTypes(0 /* types */);
+ rightlp.setFitInsetsTypes(0 /* types */);
mWindowManager.addView(mRightNavigationBarWindow, rightlp);
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java
index b2f8aad..07dbd66 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java
@@ -249,7 +249,7 @@
| WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
PixelFormat.TRANSLUCENT
);
- attrs.setFitWindowInsetsTypes(0 /* types */);
+ attrs.setFitInsetsTypes(0 /* types */);
return attrs;
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index 8c756ec..7dd3be4 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -20,6 +20,7 @@
import static android.content.DialogInterface.BUTTON_POSITIVE;
import static android.os.UserManager.DISALLOW_ADD_USER;
import static android.os.UserManager.SWITCHABILITY_STATUS_OK;
+import static android.view.WindowInsets.Type.statusBars;
import android.annotation.IntDef;
import android.annotation.Nullable;
@@ -367,8 +368,8 @@
window.setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
- window.setFitWindowInsetsTypes(
- window.getFitWindowInsetsTypes() & ~WindowInsets.Type.statusBars());
+ window.getAttributes().setFitInsetsTypes(
+ window.getAttributes().getFitInsetsTypes() & ~statusBars());
}
private void notifyUserSelected(UserRecord userRecord) {
diff --git a/packages/FusedLocation/Android.bp b/packages/FusedLocation/Android.bp
index e794f72..c70ab71 100644
--- a/packages/FusedLocation/Android.bp
+++ b/packages/FusedLocation/Android.bp
@@ -14,9 +14,33 @@
android_app {
name: "FusedLocation",
- srcs: ["**/*.java"],
+ srcs: ["src/**/*.java"],
libs: ["com.android.location.provider"],
platform_apis: true,
certificate: "platform",
privileged: true,
}
+
+android_test {
+ name: "FusedLocationTests",
+ manifest: "test/AndroidManifest.xml",
+ test_config: "test/AndroidTest.xml",
+ srcs: [
+ "test/src/**/*.java",
+ "src/**/*.java", // include real sources because we're forced to test this directly
+ ],
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ "com.android.location.provider",
+ ],
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "androidx.test.ext.truth",
+ "mockito-target-minus-junit4",
+ "truth-prebuilt",
+ ],
+ test_suites: ["device-tests"]
+}
\ No newline at end of file
diff --git a/packages/FusedLocation/AndroidManifest.xml b/packages/FusedLocation/AndroidManifest.xml
index a8319ab..bad0497 100644
--- a/packages/FusedLocation/AndroidManifest.xml
+++ b/packages/FusedLocation/AndroidManifest.xml
@@ -23,6 +23,8 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+ <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
<uses-permission android:name="android.permission.INSTALL_LOCATION_PROVIDER" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
diff --git a/packages/FusedLocation/TEST_MAPPING b/packages/FusedLocation/TEST_MAPPING
new file mode 100644
index 0000000..e810d6a
--- /dev/null
+++ b/packages/FusedLocation/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "FusedLocationTests"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
index be817d6..fb7dbc8 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
@@ -16,70 +16,307 @@
package com.android.location.fused;
+import static android.content.Intent.ACTION_USER_SWITCHED;
+import static android.location.LocationManager.GPS_PROVIDER;
+import static android.location.LocationManager.NETWORK_PROVIDER;
+
+import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.Criteria;
-import android.os.Handler;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.location.LocationRequest;
+import android.os.Bundle;
import android.os.Looper;
-import android.os.UserHandle;
+import android.os.Parcelable;
import android.os.WorkSource;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.location.ProviderRequest;
import com.android.location.provider.LocationProviderBase;
+import com.android.location.provider.LocationRequestUnbundled;
import com.android.location.provider.ProviderPropertiesUnbundled;
import com.android.location.provider.ProviderRequestUnbundled;
-import java.io.FileDescriptor;
import java.io.PrintWriter;
-class FusedLocationProvider extends LocationProviderBase implements FusionEngine.Callback {
+/** Basic fused location provider implementation. */
+public class FusedLocationProvider extends LocationProviderBase {
+
private static final String TAG = "FusedLocationProvider";
- private static ProviderPropertiesUnbundled PROPERTIES = ProviderPropertiesUnbundled.create(
- false, false, false, false, true, true, true, Criteria.POWER_LOW,
- Criteria.ACCURACY_FINE);
+ private static final ProviderPropertiesUnbundled PROPERTIES =
+ ProviderPropertiesUnbundled.create(
+ /* requiresNetwork = */ false,
+ /* requiresSatellite = */ false,
+ /* requiresCell = */ false,
+ /* hasMonetaryCost = */ false,
+ /* supportsAltitude = */ true,
+ /* supportsSpeed = */ true,
+ /* supportsBearing = */ true,
+ Criteria.POWER_LOW,
+ Criteria.ACCURACY_FINE
+ );
+
+ private static final long MAX_LOCATION_COMPARISON_NS = 11 * 1000000000L; // 11 seconds
+
+ private final Object mLock = new Object();
private final Context mContext;
- private final Handler mHandler;
- private final FusionEngine mEngine;
+ private final LocationManager mLocationManager;
+ private final LocationListener mGpsListener;
+ private final LocationListener mNetworkListener;
+ private final BroadcastReceiver mUserChangeReceiver;
- private final BroadcastReceiver mUserSwitchReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- mEngine.switchUser();
+ @GuardedBy("mLock")
+ private ProviderRequestUnbundled mRequest;
+ @GuardedBy("mLock")
+ private WorkSource mWorkSource;
+ @GuardedBy("mLock")
+ private long mGpsInterval;
+ @GuardedBy("mLock")
+ private long mNetworkInterval;
+
+ @GuardedBy("mLock")
+ @Nullable private Location mFusedLocation;
+ @GuardedBy("mLock")
+ @Nullable private Location mGpsLocation;
+ @GuardedBy("mLock")
+ @Nullable private Location mNetworkLocation;
+
+ public FusedLocationProvider(Context context) {
+ super(TAG, PROPERTIES);
+ mContext = context;
+ mLocationManager = context.getSystemService(LocationManager.class);
+
+ mGpsListener = new LocationListener() {
+ @Override
+ public void onLocationChanged(Location location) {
+ synchronized (mLock) {
+ mGpsLocation = location;
+ reportBestLocationLocked();
+ }
+ }
+
+ @Override
+ public void onProviderDisabled(String provider) {
+ synchronized (mLock) {
+ // if satisfying a bypass request, don't clear anything
+ if (mRequest.getReportLocation() && mRequest.isLocationSettingsIgnored()) {
+ return;
+ }
+
+ mGpsLocation = null;
+ }
+ }
+ };
+
+ mNetworkListener = new LocationListener() {
+ @Override
+ public void onLocationChanged(Location location) {
+ synchronized (mLock) {
+ mNetworkLocation = location;
+ reportBestLocationLocked();
+ }
+ }
+
+ @Override
+ public void onProviderDisabled(String provider) {
+ synchronized (mLock) {
+ // if satisfying a bypass request, don't clear anything
+ if (mRequest.getReportLocation() && mRequest.isLocationSettingsIgnored()) {
+ return;
+ }
+
+ mNetworkLocation = null;
+ }
+ }
+ };
+
+ mUserChangeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!ACTION_USER_SWITCHED.equals(intent.getAction())) {
+ return;
+ }
+
+ onUserChanged();
+ }
+ };
+
+ mRequest = new ProviderRequestUnbundled(ProviderRequest.EMPTY_REQUEST);
+ mWorkSource = new WorkSource();
+ mGpsInterval = Long.MAX_VALUE;
+ mNetworkInterval = Long.MAX_VALUE;
+ }
+
+ void start() {
+ mContext.registerReceiver(mUserChangeReceiver, new IntentFilter(ACTION_USER_SWITCHED));
+ }
+
+ void stop() {
+ mContext.unregisterReceiver(mUserChangeReceiver);
+
+ synchronized (mLock) {
+ mRequest = new ProviderRequestUnbundled(ProviderRequest.EMPTY_REQUEST);
+ updateRequirementsLocked();
+ }
+ }
+
+ @Override
+ public void onSetRequest(ProviderRequestUnbundled request, WorkSource workSource) {
+ synchronized (mLock) {
+ mRequest = request;
+ mWorkSource = workSource;
+ updateRequirementsLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void updateRequirementsLocked() {
+ long gpsInterval = Long.MAX_VALUE;
+ long networkInterval = Long.MAX_VALUE;
+ if (mRequest.getReportLocation()) {
+ for (LocationRequestUnbundled request : mRequest.getLocationRequests()) {
+ switch (request.getQuality()) {
+ case LocationRequestUnbundled.ACCURACY_FINE:
+ case LocationRequestUnbundled.POWER_HIGH:
+ if (request.getInterval() < gpsInterval) {
+ gpsInterval = request.getInterval();
+ }
+ if (request.getInterval() < networkInterval) {
+ networkInterval = request.getInterval();
+ }
+ break;
+ case LocationRequestUnbundled.ACCURACY_BLOCK:
+ case LocationRequestUnbundled.ACCURACY_CITY:
+ case LocationRequestUnbundled.POWER_LOW:
+ if (request.getInterval() < networkInterval) {
+ networkInterval = request.getInterval();
+ }
+ break;
+ }
}
}
- };
- FusedLocationProvider(Context context) {
- super(TAG, PROPERTIES);
-
- mContext = context;
- mHandler = new Handler(Looper.myLooper());
- mEngine = new FusionEngine(context, Looper.myLooper(), this);
+ if (gpsInterval != mGpsInterval) {
+ resetProviderRequestLocked(GPS_PROVIDER, mGpsInterval, gpsInterval, mGpsListener);
+ mGpsInterval = gpsInterval;
+ }
+ if (networkInterval != mNetworkInterval) {
+ resetProviderRequestLocked(NETWORK_PROVIDER, mNetworkInterval, networkInterval,
+ mNetworkListener);
+ mNetworkInterval = networkInterval;
+ }
}
- void init() {
- // listen for user change
- mContext.registerReceiverAsUser(mUserSwitchReceiver, UserHandle.ALL,
- new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
+ @GuardedBy("mLock")
+ private void resetProviderRequestLocked(String provider, long oldInterval, long newInterval,
+ LocationListener listener) {
+ if (oldInterval != Long.MAX_VALUE) {
+ mLocationManager.removeUpdates(listener);
+ }
+ if (newInterval != Long.MAX_VALUE) {
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, newInterval, 0, false);
+ if (mRequest.isLocationSettingsIgnored()) {
+ request.setLocationSettingsIgnored(true);
+ }
+ request.setWorkSource(mWorkSource);
+ mLocationManager.requestLocationUpdates(request, listener, Looper.getMainLooper());
+ }
}
- void destroy() {
- mContext.unregisterReceiver(mUserSwitchReceiver);
- mHandler.post(() -> mEngine.setRequest(null));
+ @GuardedBy("mLock")
+ private void reportBestLocationLocked() {
+ Location bestLocation = chooseBestLocation(mGpsLocation, mNetworkLocation);
+ if (bestLocation == mFusedLocation) {
+ return;
+ }
+
+ mFusedLocation = bestLocation;
+ if (mFusedLocation == null) {
+ return;
+ }
+
+ // copy NO_GPS_LOCATION extra from mNetworkLocation into mFusedLocation
+ if (mNetworkLocation != null) {
+ Bundle srcExtras = mNetworkLocation.getExtras();
+ if (srcExtras != null) {
+ Parcelable srcParcelable =
+ srcExtras.getParcelable(LocationProviderBase.EXTRA_NO_GPS_LOCATION);
+ if (srcParcelable instanceof Location) {
+ Bundle dstExtras = mFusedLocation.getExtras();
+ if (dstExtras == null) {
+ dstExtras = new Bundle();
+ mFusedLocation.setExtras(dstExtras);
+ }
+ dstExtras.putParcelable(LocationProviderBase.EXTRA_NO_GPS_LOCATION,
+ srcParcelable);
+ }
+ }
+ }
+
+ reportLocation(mFusedLocation);
}
- @Override
- public void onSetRequest(ProviderRequestUnbundled request, WorkSource source) {
- mHandler.post(() -> mEngine.setRequest(request));
+ private void onUserChanged() {
+ // clear cached locations when the user changes to prevent leaking user information
+ synchronized (mLock) {
+ mFusedLocation = null;
+ mGpsLocation = null;
+ mNetworkLocation = null;
+ }
}
- @Override
- public void onDump(FileDescriptor fd, PrintWriter pw, String[] args) {
- mEngine.dump(fd, pw, args);
+ void dump(PrintWriter writer) {
+ synchronized (mLock) {
+ writer.println("request: " + mRequest);
+ if (mGpsInterval != Long.MAX_VALUE) {
+ writer.println(" gps interval: " + mGpsInterval);
+ }
+ if (mNetworkInterval != Long.MAX_VALUE) {
+ writer.println(" network interval: " + mNetworkInterval);
+ }
+ if (mGpsLocation != null) {
+ writer.println(" last gps location: " + mGpsLocation);
+ }
+ if (mNetworkLocation != null) {
+ writer.println(" last network location: " + mNetworkLocation);
+ }
+ }
+ }
+
+ @Nullable
+ private static Location chooseBestLocation(
+ @Nullable Location locationA,
+ @Nullable Location locationB) {
+ if (locationA == null) {
+ return locationB;
+ }
+ if (locationB == null) {
+ return locationA;
+ }
+
+ if (locationA.getElapsedRealtimeNanos()
+ > locationB.getElapsedRealtimeNanos() + MAX_LOCATION_COMPARISON_NS) {
+ return locationA;
+ }
+ if (locationB.getElapsedRealtimeNanos()
+ > locationA.getElapsedRealtimeNanos() + MAX_LOCATION_COMPARISON_NS) {
+ return locationB;
+ }
+
+ if (!locationA.hasAccuracy()) {
+ return locationB;
+ }
+ if (!locationB.hasAccuracy()) {
+ return locationA;
+ }
+ return locationA.getAccuracy() < locationB.getAccuracy() ? locationA : locationB;
}
}
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java
index 75bb5ec..1fa3824 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java
@@ -16,19 +16,23 @@
package com.android.location.fused;
+import android.annotation.Nullable;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
public class FusedLocationService extends Service {
- private FusedLocationProvider mProvider;
+ @Nullable private FusedLocationProvider mProvider;
@Override
public IBinder onBind(Intent intent) {
if (mProvider == null) {
mProvider = new FusedLocationProvider(this);
- mProvider.init();
+ mProvider.start();
}
return mProvider.getBinder();
@@ -37,8 +41,15 @@
@Override
public void onDestroy() {
if (mProvider != null) {
- mProvider.destroy();
+ mProvider.stop();
mProvider = null;
}
}
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ if (mProvider != null) {
+ mProvider.dump(writer);
+ }
+ }
}
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
deleted file mode 100644
index e4610cf..0000000
--- a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.location.fused;
-
-import android.content.Context;
-import android.location.Location;
-import android.location.LocationListener;
-import android.location.LocationManager;
-import android.os.Bundle;
-import android.os.Looper;
-import android.os.Parcelable;
-import android.os.SystemClock;
-import android.util.Log;
-
-import com.android.location.provider.LocationProviderBase;
-import com.android.location.provider.LocationRequestUnbundled;
-import com.android.location.provider.ProviderRequestUnbundled;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.HashMap;
-
-public class FusionEngine implements LocationListener {
- public interface Callback {
- void reportLocation(Location location);
- }
-
- private static final String TAG = "FusedLocation";
- private static final String NETWORK = LocationManager.NETWORK_PROVIDER;
- private static final String GPS = LocationManager.GPS_PROVIDER;
- private static final String FUSED = LocationProviderBase.FUSED_PROVIDER;
-
- public static final long SWITCH_ON_FRESHNESS_CLIFF_NS = 11 * 1000000000L; // 11 seconds
-
- private final LocationManager mLocationManager;
- private final Looper mLooper;
- private final Callback mCallback;
-
- // all fields are only used on mLooper thread. except for in dump() which is not thread-safe
- private Location mFusedLocation;
- private Location mGpsLocation;
- private Location mNetworkLocation;
-
- private ProviderRequestUnbundled mRequest;
-
- private final HashMap<String, ProviderStats> mStats = new HashMap<>();
-
- FusionEngine(Context context, Looper looper, Callback callback) {
- mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
- mNetworkLocation = new Location("");
- mNetworkLocation.setAccuracy(Float.MAX_VALUE);
- mGpsLocation = new Location("");
- mGpsLocation.setAccuracy(Float.MAX_VALUE);
- mLooper = looper;
- mCallback = callback;
-
- mStats.put(GPS, new ProviderStats());
- mStats.put(NETWORK, new ProviderStats());
- }
-
- /** Called on mLooper thread */
- public void setRequest(ProviderRequestUnbundled request) {
- mRequest = request;
- updateRequirements();
- }
-
- private static class ProviderStats {
- public boolean requested;
- public long requestTime;
- public long minTime;
-
- @Override
- public String toString() {
- return (requested ? " REQUESTED" : " ---");
- }
- }
-
- private void enableProvider(String name, long minTime) {
- ProviderStats stats = mStats.get(name);
- if (stats == null) return;
-
- if (mLocationManager.isProviderEnabled(name)) {
- if (!stats.requested) {
- stats.requestTime = SystemClock.elapsedRealtime();
- stats.requested = true;
- stats.minTime = minTime;
- mLocationManager.requestLocationUpdates(name, minTime, 0, this, mLooper);
- } else if (stats.minTime != minTime) {
- stats.minTime = minTime;
- mLocationManager.requestLocationUpdates(name, minTime, 0, this, mLooper);
- }
- }
- }
-
- private void disableProvider(String name) {
- ProviderStats stats = mStats.get(name);
- if (stats == null) return;
-
- if (stats.requested) {
- stats.requested = false;
- mLocationManager.removeUpdates(this); //TODO GLOBAL
- }
- }
-
- private void updateRequirements() {
- if (mRequest == null || !mRequest.getReportLocation()) {
- mRequest = null;
- disableProvider(NETWORK);
- disableProvider(GPS);
- return;
- }
-
- long networkInterval = Long.MAX_VALUE;
- long gpsInterval = Long.MAX_VALUE;
- for (LocationRequestUnbundled request : mRequest.getLocationRequests()) {
- switch (request.getQuality()) {
- case LocationRequestUnbundled.ACCURACY_FINE:
- case LocationRequestUnbundled.POWER_HIGH:
- if (request.getInterval() < gpsInterval) {
- gpsInterval = request.getInterval();
- }
- if (request.getInterval() < networkInterval) {
- networkInterval = request.getInterval();
- }
- break;
- case LocationRequestUnbundled.ACCURACY_BLOCK:
- case LocationRequestUnbundled.ACCURACY_CITY:
- case LocationRequestUnbundled.POWER_LOW:
- if (request.getInterval() < networkInterval) {
- networkInterval = request.getInterval();
- }
- break;
- }
- }
-
- if (gpsInterval < Long.MAX_VALUE) {
- enableProvider(GPS, gpsInterval);
- } else {
- disableProvider(GPS);
- }
- if (networkInterval < Long.MAX_VALUE) {
- enableProvider(NETWORK, networkInterval);
- } else {
- disableProvider(NETWORK);
- }
- }
-
- /**
- * Test whether one location (a) is better to use than another (b).
- */
- private static boolean isBetterThan(Location locationA, Location locationB) {
- if (locationA == null) {
- return false;
- }
- if (locationB == null) {
- return true;
- }
- // A provider is better if the reading is sufficiently newer. Heading
- // underground can cause GPS to stop reporting fixes. In this case it's
- // appropriate to revert to cell, even when its accuracy is less.
- if (locationA.getElapsedRealtimeNanos()
- > locationB.getElapsedRealtimeNanos() + SWITCH_ON_FRESHNESS_CLIFF_NS) {
- return true;
- }
-
- // A provider is better if it has better accuracy. Assuming both readings
- // are fresh (and by that accurate), choose the one with the smaller
- // accuracy circle.
- if (!locationA.hasAccuracy()) {
- return false;
- }
- if (!locationB.hasAccuracy()) {
- return true;
- }
- return locationA.getAccuracy() < locationB.getAccuracy();
- }
-
- private void updateFusedLocation() {
- // may the best location win!
- if (isBetterThan(mGpsLocation, mNetworkLocation)) {
- mFusedLocation = new Location(mGpsLocation);
- } else {
- mFusedLocation = new Location(mNetworkLocation);
- }
- mFusedLocation.setProvider(FUSED);
- if (mNetworkLocation != null) {
- // copy NO_GPS_LOCATION extra from mNetworkLocation into mFusedLocation
- Bundle srcExtras = mNetworkLocation.getExtras();
- if (srcExtras != null) {
- Parcelable srcParcelable =
- srcExtras.getParcelable(LocationProviderBase.EXTRA_NO_GPS_LOCATION);
- if (srcParcelable instanceof Location) {
- Bundle dstExtras = mFusedLocation.getExtras();
- if (dstExtras == null) {
- dstExtras = new Bundle();
- mFusedLocation.setExtras(dstExtras);
- }
- dstExtras.putParcelable(LocationProviderBase.EXTRA_NO_GPS_LOCATION,
- srcParcelable);
- }
- }
- }
-
- if (mCallback != null) {
- mCallback.reportLocation(mFusedLocation);
- } else {
- Log.w(TAG, "Location updates received while fusion engine not started");
- }
- }
-
- /** Called on mLooper thread */
- @Override
- public void onLocationChanged(Location location) {
- if (GPS.equals(location.getProvider())) {
- mGpsLocation = location;
- updateFusedLocation();
- } else if (NETWORK.equals(location.getProvider())) {
- mNetworkLocation = location;
- updateFusedLocation();
- }
- }
-
- /** Called on mLooper thread */
- @Override
- public void onStatusChanged(String provider, int status, Bundle extras) {
- }
-
- /** Called on mLooper thread */
- @Override
- public void onProviderEnabled(String provider) {
- }
-
- /** Called on mLooper thread */
- @Override
- public void onProviderDisabled(String provider) {
- }
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- StringBuilder s = new StringBuilder();
- s.append(mRequest).append('\n');
- s.append("fused=").append(mFusedLocation).append('\n');
- s.append(String.format("gps %s\n", mGpsLocation));
- s.append(" ").append(mStats.get(GPS)).append('\n');
- s.append(String.format("net %s\n", mNetworkLocation));
- s.append(" ").append(mStats.get(NETWORK)).append('\n');
- pw.append(s);
- }
-
- /** Called on mLooper thread */
- public void switchUser() {
- // reset state to prevent location data leakage
- mFusedLocation = null;
- mGpsLocation = null;
- mNetworkLocation = null;
- }
-}
diff --git a/packages/FusedLocation/test/AndroidManifest.xml b/packages/FusedLocation/test/AndroidManifest.xml
new file mode 100644
index 0000000..d6c4107
--- /dev/null
+++ b/packages/FusedLocation/test/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.location.fused.tests">
+
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+ <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
+
+ <application android:label="FusedLocation Tests">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.location.fused.tests"
+ android:label="FusedLocation Tests" />
+</manifest>
diff --git a/packages/FusedLocation/test/AndroidTest.xml b/packages/FusedLocation/test/AndroidTest.xml
new file mode 100644
index 0000000..f88e595
--- /dev/null
+++ b/packages/FusedLocation/test/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<configuration description="FusedLocation Tests">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="FusedLocationTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="framework-base-presubmit" />
+ <option name="test-tag" value="FusedLocationTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.location.fused.tests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
new file mode 100644
index 0000000..3312651
--- /dev/null
+++ b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.location.fused.tests;
+
+import static android.location.LocationManager.FUSED_PROVIDER;
+import static android.location.LocationManager.GPS_PROVIDER;
+import static android.location.LocationManager.NETWORK_PROVIDER;
+
+import static androidx.test.ext.truth.location.LocationSubject.assertThat;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationRequest;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.os.WorkSource;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.location.ILocationProvider;
+import com.android.internal.location.ILocationProviderManager;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+import com.android.location.fused.FusedLocationProvider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class FusedLocationServiceTest {
+
+ private static final String TAG = "FusedLocationServiceTest";
+
+ private static final long TIMEOUT_MS = 5000;
+
+ private Context mContext;
+ private Random mRandom;
+ private LocationManager mLocationManager;
+
+ private ILocationProvider mProvider;
+ private LocationProviderManagerCapture mManager;
+
+ @Before
+ public void setUp() throws Exception {
+ long seed = System.currentTimeMillis();
+ Log.i(TAG, "location seed: " + seed);
+
+ mContext = InstrumentationRegistry.getTargetContext();
+ mRandom = new Random(seed);
+ mLocationManager = mContext.getSystemService(LocationManager.class);
+
+ setMockLocation(true);
+
+ mManager = new LocationProviderManagerCapture();
+ mProvider = ILocationProvider.Stub.asInterface(
+ new FusedLocationProvider(mContext).getBinder());
+ mProvider.setLocationProviderManager(mManager);
+
+ mLocationManager.addTestProvider(NETWORK_PROVIDER,
+ true,
+ false,
+ true,
+ false,
+ false,
+ false,
+ false,
+ Criteria.POWER_MEDIUM,
+ Criteria.ACCURACY_FINE);
+ mLocationManager.setTestProviderEnabled(NETWORK_PROVIDER, true);
+ mLocationManager.addTestProvider(GPS_PROVIDER,
+ true,
+ false,
+ true,
+ false,
+ false,
+ false,
+ false,
+ Criteria.POWER_MEDIUM,
+ Criteria.ACCURACY_FINE);
+ mLocationManager.setTestProviderEnabled(GPS_PROVIDER, true);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ for (String provider : mLocationManager.getAllProviders()) {
+ mLocationManager.removeTestProvider(provider);
+ }
+
+ setMockLocation(false);
+ }
+
+ @Test
+ public void testNetworkRequest() throws Exception {
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(FUSED_PROVIDER, 1000,
+ 0, false);
+
+ mProvider.setRequest(
+ new ProviderRequest.Builder()
+ .setInterval(1000)
+ .setLocationRequests(Collections.singletonList(request))
+ .build(),
+ new WorkSource());
+
+ Location location = createLocation(NETWORK_PROVIDER, mRandom);
+ mLocationManager.setTestProviderLocation(NETWORK_PROVIDER, location);
+
+ assertThat(mManager.getNextLocation(TIMEOUT_MS)).isEqualTo(location);
+ }
+
+ @Test
+ public void testGpsRequest() throws Exception {
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(FUSED_PROVIDER, 1000,
+ 0, false).setQuality(LocationRequest.POWER_HIGH);
+
+ mProvider.setRequest(
+ new ProviderRequest.Builder()
+ .setInterval(1000)
+ .setLocationRequests(Collections.singletonList(request))
+ .build(),
+ new WorkSource());
+
+ Location location = createLocation(GPS_PROVIDER, mRandom);
+ mLocationManager.setTestProviderLocation(GPS_PROVIDER, location);
+
+ assertThat(mManager.getNextLocation(TIMEOUT_MS)).isEqualTo(location);
+ }
+
+ @Test
+ public void testBypassRequest() throws Exception {
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(FUSED_PROVIDER, 1000,
+ 0, false).setQuality(LocationRequest.POWER_HIGH).setLocationSettingsIgnored(true);
+
+ mProvider.setRequest(
+ new ProviderRequest.Builder()
+ .setInterval(1000)
+ .setLocationSettingsIgnored(true)
+ .setLocationRequests(Collections.singletonList(request))
+ .build(),
+ new WorkSource());
+
+ boolean containsNetworkBypass = false;
+ for (LocationRequest iRequest : mLocationManager.getTestProviderCurrentRequests(
+ NETWORK_PROVIDER)) {
+ if (iRequest.isLocationSettingsIgnored()) {
+ containsNetworkBypass = true;
+ break;
+ }
+ }
+
+ boolean containsGpsBypass = false;
+ for (LocationRequest iRequest : mLocationManager.getTestProviderCurrentRequests(
+ GPS_PROVIDER)) {
+ if (iRequest.isLocationSettingsIgnored()) {
+ containsGpsBypass = true;
+ break;
+ }
+ }
+
+ assertThat(containsNetworkBypass).isTrue();
+ assertThat(containsGpsBypass).isTrue();
+ }
+
+ private static class LocationProviderManagerCapture extends ILocationProviderManager.Stub {
+
+ private final LinkedBlockingQueue<Location> mLocations;
+
+ private LocationProviderManagerCapture() {
+ mLocations = new LinkedBlockingQueue<>();
+ }
+
+ @Override
+ public void onSetAdditionalProviderPackages(List<String> packageNames) {
+
+ }
+
+ @Override
+ public void onSetEnabled(boolean enabled) {
+
+ }
+
+ @Override
+ public void onSetProperties(ProviderProperties properties) {
+
+ }
+
+ @Override
+ public void onReportLocation(Location location) {
+ mLocations.add(location);
+ }
+
+ public Location getNextLocation(long timeoutMs) throws InterruptedException {
+ return mLocations.poll(timeoutMs, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ private static final double MIN_LATITUDE = -90D;
+ private static final double MAX_LATITUDE = 90D;
+ private static final double MIN_LONGITUDE = -180D;
+ private static final double MAX_LONGITUDE = 180D;
+
+ private static final float MIN_ACCURACY = 1;
+ private static final float MAX_ACCURACY = 100;
+
+ private static Location createLocation(String provider, Random random) {
+ return createLocation(provider,
+ MIN_LATITUDE + random.nextDouble() * (MAX_LATITUDE - MIN_LATITUDE),
+ MIN_LONGITUDE + random.nextDouble() * (MAX_LONGITUDE - MIN_LONGITUDE),
+ MIN_ACCURACY + random.nextFloat() * (MAX_ACCURACY - MIN_ACCURACY));
+ }
+
+ private static Location createLocation(String provider, double latitude, double longitude,
+ float accuracy) {
+ Location location = new Location(provider);
+ location.setLatitude(latitude);
+ location.setLongitude(longitude);
+ location.setAccuracy(accuracy);
+ location.setTime(System.currentTimeMillis());
+ location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
+ return location;
+ }
+
+ private static void setMockLocation(boolean allowed) throws IOException {
+ ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .executeShellCommand("appops set "
+ + InstrumentationRegistry.getTargetContext().getPackageName()
+ + " android:mock_location " + (allowed ? "allow" : "deny"));
+ try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ byte[] buffer = new byte[32768];
+ int count;
+ try {
+ while ((count = fis.read(buffer)) != -1) {
+ os.write(buffer, 0, count);
+ }
+ fis.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ Log.e(TAG, new String(os.toByteArray()));
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
index 2d7d59c..4ebb102 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
@@ -199,8 +199,7 @@
return;
}
if ((mWifiEntry.getSecurity() != WifiEntry.SECURITY_NONE)
- && (mWifiEntry.getSecurity() != WifiEntry.SECURITY_OWE)
- && (mWifiEntry.getSecurity() != WifiEntry.SECURITY_OWE_TRANSITION)) {
+ && (mWifiEntry.getSecurity() != WifiEntry.SECURITY_OWE)) {
mFrictionSld.setState(STATE_SECURED);
} else if (mWifiEntry.isMetered()) {
mFrictionSld.setState(STATE_METERED);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index c913999..486386f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -62,6 +62,7 @@
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SELinux;
import android.os.ServiceManager;
@@ -275,6 +276,9 @@
private final Object mLock = new Object();
@GuardedBy("mLock")
+ private RemoteCallback mConfigMonitorCallback;
+
+ @GuardedBy("mLock")
private SettingsRegistry mSettingsRegistry;
@GuardedBy("mLock")
@@ -450,8 +454,17 @@
case Settings.CALL_METHOD_LIST_CONFIG: {
String prefix = getSettingPrefix(args);
- return packageValuesForCallResult(getAllConfigFlags(prefix),
+ Bundle result = packageValuesForCallResult(getAllConfigFlags(prefix),
isTrackingGeneration(args));
+ reportDeviceConfigAccess(prefix);
+ return result;
+ }
+
+ case Settings.CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG: {
+ RemoteCallback callback = args.getParcelable(
+ Settings.CALL_METHOD_MONITOR_CALLBACK_KEY);
+ setMonitorCallback(callback);
+ break;
}
case Settings.CALL_METHOD_LIST_GLOBAL: {
@@ -1052,8 +1065,9 @@
enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
synchronized (mLock) {
- return mSettingsRegistry.setSettingsLocked(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM,
- prefix, keyValues, resolveCallingPackage());
+ final int key = makeKey(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM);
+ return mSettingsRegistry.setConfigSettingsLocked(key, prefix, keyValues,
+ resolveCallingPackage());
}
}
@@ -2155,6 +2169,59 @@
return result;
}
+ private void setMonitorCallback(RemoteCallback callback) {
+ if (callback == null) {
+ return;
+ }
+ getContext().enforceCallingOrSelfPermission(
+ Manifest.permission.MONITOR_DEVICE_CONFIG_ACCESS,
+ "Permission denial: registering for config access requires: "
+ + Manifest.permission.MONITOR_DEVICE_CONFIG_ACCESS);
+ synchronized (mLock) {
+ mConfigMonitorCallback = callback;
+ }
+ }
+
+ private void reportDeviceConfigAccess(@Nullable String prefix) {
+ if (prefix == null) {
+ return;
+ }
+ String callingPackage = getCallingPackage();
+ String namespace = prefix.replace("/", "");
+ if (DeviceConfig.getPublicNamespaces().contains(namespace)) {
+ return;
+ }
+ synchronized (mLock) {
+ if (mConfigMonitorCallback != null) {
+ Bundle callbackResult = new Bundle();
+ callbackResult.putString(Settings.EXTRA_MONITOR_CALLBACK_TYPE,
+ Settings.EXTRA_ACCESS_CALLBACK);
+ callbackResult.putString(Settings.EXTRA_CALLING_PACKAGE, callingPackage);
+ callbackResult.putString(Settings.EXTRA_NAMESPACE, namespace);
+ mConfigMonitorCallback.sendResult(callbackResult);
+ }
+ }
+ }
+
+ private void reportDeviceConfigUpdate(@Nullable String prefix) {
+ if (prefix == null) {
+ return;
+ }
+ String namespace = prefix.replace("/", "");
+ if (DeviceConfig.getPublicNamespaces().contains(namespace)) {
+ return;
+ }
+ synchronized (mLock) {
+ if (mConfigMonitorCallback != null) {
+ Bundle callbackResult = new Bundle();
+ callbackResult.putString(Settings.EXTRA_MONITOR_CALLBACK_TYPE,
+ Settings.EXTRA_NAMESPACE_UPDATED_CALLBACK);
+ callbackResult.putString(Settings.EXTRA_NAMESPACE, namespace);
+ mConfigMonitorCallback.sendResult(callbackResult);
+ }
+ }
+ }
+
private static int getRequestingUserId(Bundle args) {
final int callingUserId = UserHandle.getCallingUserId();
return (args != null) ? args.getInt(Settings.CALL_METHOD_USER_KEY, callingUserId)
@@ -2715,22 +2782,20 @@
}
/**
- * Set Settings using consumed keyValues, returns true if the keyValues can be set, false
- * otherwise.
+ * Set Config Settings using consumed keyValues, returns true if the keyValues can be set,
+ * false otherwise.
*/
- public boolean setSettingsLocked(int type, int userId, String prefix,
+ public boolean setConfigSettingsLocked(int key, String prefix,
Map<String, String> keyValues, String packageName) {
- final int key = makeKey(type, userId);
-
SettingsState settingsState = peekSettingsStateLocked(key);
if (settingsState != null) {
- if (SETTINGS_TYPE_CONFIG == type && settingsState.isNewConfigBannedLocked(prefix,
- keyValues)) {
+ if (settingsState.isNewConfigBannedLocked(prefix, keyValues)) {
return false;
}
List<String> changedSettings =
settingsState.setSettingsLocked(prefix, keyValues, packageName);
if (!changedSettings.isEmpty()) {
+ reportDeviceConfigUpdate(prefix);
notifyForConfigSettingsChangeLocked(key, prefix, changedSettings);
}
}
diff --git a/packages/SystemUI/res/drawable/ic_screenrecord.xml b/packages/SystemUI/res/drawable/ic_screenrecord.xml
new file mode 100644
index 0000000..6d8bd0d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_screenrecord.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:pathData="M12,12m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"
+ android:fillColor="#FFFFFFFF"/>
+</vector>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 639005b..44a7fda 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -194,6 +194,10 @@
<!-- Power menu item for taking a screenshot [CHAR LIMIT=20]-->
<string name="global_action_screenshot">Screenshot</string>
+ <!-- text to show in place of RemoteInput images when they cannot be shown.
+ [CHAR LIMIT=50] -->
+ <string name="remote_input_image_insertion_text">Image inserted</string>
+
<!-- Notification ticker displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=30] -->
<string name="screenshot_saving_ticker">Saving screenshot\u2026</string>
<!-- Notification title displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=50] -->
@@ -222,6 +226,8 @@
<string name="screenrecord_mic_label">Record voiceover</string>
<!-- Label for the checkbox to enable showing location of touches during screen recording [CHAR LIMIT=NONE]-->
<string name="screenrecord_taps_label">Show taps</string>
+ <!-- Label for notification that the user can tap to stop and save the screen recording [CHAR LIMIT=NONE] -->
+ <string name="screenrecord_stop_text">Tap to stop</string>
<!-- Label for notification action to stop and save the screen recording [CHAR LIMIT=35] -->
<string name="screenrecord_stop_label">Stop</string>
<!-- Label for notification action to pause screen recording [CHAR LIMIT=35] -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/UniversalSmartspaceUtils.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/UniversalSmartspaceUtils.java
index 70a464d..871cae3 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/UniversalSmartspaceUtils.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/UniversalSmartspaceUtils.java
@@ -37,7 +37,7 @@
Intent intent = new Intent(ACTION_REQUEST_SMARTSPACE_VIEW);
Bundle inputBundle = new Bundle();
- inputBundle.putBinder(BUNDLE_KEY_INPUT_TOKEN, surfaceView.getInputToken());
+ inputBundle.putBinder(BUNDLE_KEY_INPUT_TOKEN, surfaceView.getHostToken());
return intent
.putExtra(INTENT_KEY_SURFACE_CONTROL, surfaceView.getSurfaceControl())
.putExtra(INTENT_KEY_INPUT_BUNDLE, inputBundle)
diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
index 2f8ef2d..b2423b9 100644
--- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
@@ -172,7 +172,7 @@
private void onSurfaceReady() {
try {
- mClient.onSurfaceReady(mView.getInputToken(), mCallback);
+ mClient.onSurfaceReady(mView.getHostToken(), mCallback);
} catch (RemoteException e) {
Log.e(TAG, "Error in onSurfaceReady", e);
dismiss(KeyguardUpdateMonitor.getCurrentUser());
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
index 4a5bc2a..0106609 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
@@ -39,7 +39,6 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import com.android.internal.telephony.TelephonyIntents;
import com.android.settingslib.WirelessUtils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -337,15 +336,15 @@
CharSequence text =
getContext().getText(com.android.internal.R.string.emergency_calls_only);
Intent i = getContext().registerReceiver(null,
- new IntentFilter(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION));
+ new IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED));
if (i != null) {
String spn = "";
String plmn = "";
- if (i.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false)) {
- spn = i.getStringExtra(TelephonyIntents.EXTRA_SPN);
+ if (i.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false)) {
+ spn = i.getStringExtra(TelephonyManager.EXTRA_SPN);
}
- if (i.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false)) {
- plmn = i.getStringExtra(TelephonyIntents.EXTRA_PLMN);
+ if (i.getBooleanExtra(TelephonyManager.EXTRA_SHOW_PLMN, false)) {
+ plmn = i.getStringExtra(TelephonyManager.EXTRA_PLMN);
}
if (DEBUG) Log.d(TAG, "Getting plmn/spn sticky brdcst " + plmn + "/" + spn);
if (Objects.equals(plmn, spn)) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 4e7956d..571c4ae 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -290,7 +290,7 @@
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
- getWindow().setFitWindowInsetsTypes(0 /* types */);
+ getWindow().getAttributes().setFitInsetsTypes(0 /* types */);
getWindow().setNavigationBarContrastEnforced(false);
getWindow().setNavigationBarColor(Color.TRANSPARENT);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index 5d35169..f61f585 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -140,6 +140,13 @@
mLayoutTransition.setAnimateParentHierarchy(false);
}
+ // Temporary workaround to allow KeyguardStatusView to inflate a copy for Universal Smartspace.
+ // Eventually the existing copy will be reparented instead, and we won't need this.
+ public KeyguardSliceView(Context context, AttributeSet attributeSet) {
+ this(context, attributeSet, Dependency.get(ActivityStarter.class),
+ Dependency.get(ConfigurationController.class));
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 5a1c997..61caf3b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -18,10 +18,15 @@
import android.app.ActivityManager;
import android.app.IActivityManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.Color;
+import android.graphics.PixelFormat;
import android.os.Handler;
+import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -30,7 +35,11 @@
import android.util.Log;
import android.util.Slog;
import android.util.TypedValue;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
import android.widget.GridLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -40,6 +49,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.shared.system.UniversalSmartspaceUtils;
import com.android.systemui.statusbar.policy.ConfigurationController;
import java.io.FileDescriptor;
@@ -76,6 +86,7 @@
private int mIconTopMargin;
private int mIconTopMarginWithHeader;
private boolean mShowingHeader;
+ private SurfaceControlViewHost mUniversalSmartspaceViewHost;
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
@@ -122,6 +133,38 @@
}
};
+ private BroadcastReceiver mUniversalSmartspaceBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent i) {
+ // TODO(b/148159743): Restrict to Pixel Launcher.
+ if (UniversalSmartspaceUtils.ACTION_REQUEST_SMARTSPACE_VIEW.equals(i.getAction())) {
+ if (mUniversalSmartspaceViewHost != null) {
+ mUniversalSmartspaceViewHost.die();
+ }
+ SurfaceControl surfaceControl = UniversalSmartspaceUtils.getSurfaceControl(i);
+ if (surfaceControl != null) {
+ IBinder input = UniversalSmartspaceUtils.getInputToken(i);
+
+ WindowlessWindowManager windowlessWindowManager =
+ new WindowlessWindowManager(context.getResources().getConfiguration(),
+ surfaceControl, input);
+ mUniversalSmartspaceViewHost = new SurfaceControlViewHost(context,
+ context.getDisplay(), windowlessWindowManager);
+ WindowManager.LayoutParams layoutParams =
+ new WindowManager.LayoutParams(
+ surfaceControl.getWidth(),
+ surfaceControl.getHeight(),
+ WindowManager.LayoutParams.TYPE_APPLICATION,
+ 0,
+ PixelFormat.TRANSPARENT);
+
+ mUniversalSmartspaceViewHost.addView(
+ inflate(context, R.layout.keyguard_status_area, null), layoutParams);
+ }
+ }
+ }
+ };;
+
public KeyguardStatusView(Context context) {
this(context, null, 0);
}
@@ -316,6 +359,8 @@
super.onAttachedToWindow();
Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mInfoCallback);
Dependency.get(ConfigurationController.class).addCallback(this);
+ getContext().registerReceiver(mUniversalSmartspaceBroadcastReceiver,
+ new IntentFilter(UniversalSmartspaceUtils.ACTION_REQUEST_SMARTSPACE_VIEW));
}
@Override
@@ -323,6 +368,7 @@
super.onDetachedFromWindow();
Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mInfoCallback);
Dependency.get(ConfigurationController.class).removeCallback(this);
+ getContext().unregisterReceiver(mUniversalSmartspaceBroadcastReceiver);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f03648a..58a6c17 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -92,7 +92,6 @@
import android.util.SparseBooleanArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.widget.LockPatternUtils;
import com.android.settingslib.WirelessUtils;
import com.android.systemui.DejankUtils;
@@ -1083,13 +1082,13 @@
MSG_BATTERY_UPDATE, new BatteryStatus(status, level, plugged, health,
maxChargingMicroWatt));
mHandler.sendMessage(msg);
- } else if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) {
+ } else if (Intent.ACTION_SIM_STATE_CHANGED.equals(action)) {
SimData args = SimData.fromIntent(intent);
// ACTION_SIM_STATE_CHANGED is rebroadcast after unlocking the device to
// keep compatibility with apps that aren't direct boot aware.
// SysUI should just ignore this broadcast because it was already received
// and processed previously.
- if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
+ if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
// Guarantee mTelephonyCapable state after SysUI crash and restart
if (args.simState == TelephonyManager.SIM_STATE_ABSENT) {
mHandler.obtainMessage(MSG_TELEPHONY_CAPABLE, true).sendToTarget();
@@ -1122,7 +1121,7 @@
}
mHandler.sendMessage(
mHandler.obtainMessage(MSG_SERVICE_STATE_CHANGE, subId, 0, serviceState));
- } else if (TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED.equals(action)) {
+ } else if (TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED.equals(action)) {
mHandler.sendEmptyMessage(MSG_SIM_SUBSCRIPTION_INFO_CHANGED);
} else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(
action)) {
@@ -1273,7 +1272,7 @@
static SimData fromIntent(Intent intent) {
int state;
- if (!TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) {
+ if (!Intent.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) {
throw new IllegalArgumentException("only handles intent ACTION_SIM_STATE_CHANGED");
}
String stateExtra = intent.getStringExtra(Intent.EXTRA_SIM_STATE);
@@ -1673,7 +1672,7 @@
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
filter.addAction(Intent.ACTION_SERVICE_STATE);
filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index db8b583..e66b9f2 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -503,7 +503,7 @@
lp.gravity = Gravity.TOP | Gravity.LEFT;
}
lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- lp.setFitWindowInsetsTypes(0 /* types */);
+ lp.setFitInsetsTypes(0 /* types */);
if (isLandscape(mRotation)) {
lp.width = WRAP_CONTENT;
lp.height = MATCH_PARENT;
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
index 659629b..5532a04 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
@@ -70,7 +70,7 @@
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
PixelFormat.TRANSLUCENT);
lp.setTitle("AssistDisclosure");
- lp.setFitWindowInsetsTypes(0 /* types */);
+ lp.setFitInsetsTypes(0 /* types */);
mWm.addView(mView, lp);
mViewAdded = true;
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
index eb615a0..f201a6f 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
@@ -85,7 +85,7 @@
PixelFormat.TRANSLUCENT);
mLayoutParams.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
mLayoutParams.gravity = Gravity.BOTTOM;
- mLayoutParams.setFitWindowInsetsTypes(0 /* types */);
+ mLayoutParams.setFitInsetsTypes(0 /* types */);
mLayoutParams.setTitle("Assist");
mInvocationLightsView = (InvocationLightsView)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 89446ad..b8d32ae 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -592,7 +592,7 @@
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("BiometricPrompt");
lp.token = windowToken;
- lp.setFitWindowInsetsTypes(lp.getFitWindowInsetsTypes() & ~Type.statusBars());
+ lp.setFitInsetsTypes(lp.getFitInsetsTypes() & ~Type.statusBars());
return lp;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
index ccfd3a5..82c8a46 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -72,10 +72,10 @@
}
// Wait a bit to focus the field so the focusable flag on the window is already set then.
- post(() -> {
+ postDelayed(() -> {
mPasswordField.requestFocus();
mImm.showSoftInput(mPasswordField, InputMethodManager.SHOW_IMPLICIT);
- });
+ }, 100);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
index e705584..6e23777 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
@@ -157,6 +157,7 @@
PackageManager pm = c.getPackageManager();
ApplicationInfo appInfo;
Drawable badgedIcon;
+ Drawable appIcon;
try {
appInfo = pm.getApplicationInfo(
packageName,
@@ -167,7 +168,7 @@
if (appInfo != null) {
info.appName = String.valueOf(pm.getApplicationLabel(appInfo));
}
- Drawable appIcon = pm.getApplicationIcon(packageName);
+ appIcon = pm.getApplicationIcon(packageName);
badgedIcon = pm.getUserBadgedIcon(appIcon, sbn.getUser());
} catch (PackageManager.NameNotFoundException exception) {
// If we can't find package... don't think we should show the bubble.
@@ -178,6 +179,11 @@
// Badged bubble image
Drawable bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo,
b.getEntry().getBubbleMetadata());
+ if (bubbleDrawable == null) {
+ // Default to app icon
+ bubbleDrawable = appIcon;
+ }
+
BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon);
info.badgedBubbleImage = iconFactory.getBubbleBitmap(bubbleDrawable,
badgeBitmapInfo).icon;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 3735198..83f6d45 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -1577,7 +1577,7 @@
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
- window.setFitWindowInsetsTypes(0 /* types */);
+ window.getAttributes().setFitInsetsTypes(0 /* types */);
setTitle(R.string.global_actions);
mPanelController = plugin;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index c911bf2..dd1856a 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -126,7 +126,7 @@
window.getAttributes().height = ViewGroup.LayoutParams.MATCH_PARENT;
window.getAttributes().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
- window.setFitWindowInsetsTypes(0 /* types */);
+ window.getAttributes().setFitInsetsTypes(0 /* types */);
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
window.addFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
index 750cc60..b725811 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
@@ -109,7 +109,7 @@
lp.setTitle("pip-dismiss-overlay");
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
- lp.setFitWindowInsetsTypes(0 /* types */);
+ lp.setFitInsetsTypes(0 /* types */);
mWindowManager.addView(mDismissView, lp);
}
mDismissView.animate().cancel();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index f30c181..79a7df2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -111,7 +111,9 @@
final ArrayList<QSTile> tilesToAdd = new ArrayList<>();
for (String spec : possibleTiles) {
- // Only add current and stock tiles that can be created from QSFactoryImpl
+ // Only add current and stock tiles that can be created from QSFactoryImpl.
+ // Do not include CustomTile. Those will be created by `addPackageTiles`.
+ if (spec.startsWith(CustomTile.PREFIX)) continue;
final QSTile tile = host.createTile(spec);
if (tile == null) {
continue;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 1d649ee..fe84d818 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -138,7 +138,7 @@
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("ScreenPinningConfirmation");
lp.gravity = Gravity.FILL;
- lp.setFitWindowInsetsTypes(0 /* types */);
+ lp.setFitInsetsTypes(0 /* types */);
return lp;
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 1b32168..b091ad8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -34,6 +34,7 @@
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.net.Uri;
+import android.os.Bundle;
import android.os.IBinder;
import android.provider.MediaStore;
import android.provider.Settings;
@@ -72,9 +73,6 @@
private static final String ACTION_START = "com.android.systemui.screenrecord.START";
private static final String ACTION_STOP = "com.android.systemui.screenrecord.STOP";
- private static final String ACTION_PAUSE = "com.android.systemui.screenrecord.PAUSE";
- private static final String ACTION_RESUME = "com.android.systemui.screenrecord.RESUME";
- private static final String ACTION_CANCEL = "com.android.systemui.screenrecord.CANCEL";
private static final String ACTION_SHARE = "com.android.systemui.screenrecord.SHARE";
private static final String ACTION_DELETE = "com.android.systemui.screenrecord.DELETE";
@@ -94,6 +92,7 @@
private boolean mUseAudio;
private boolean mShowTaps;
+ private boolean mOriginalShowTaps;
private File mTempFile;
@Inject
@@ -145,38 +144,11 @@
}
break;
- case ACTION_CANCEL:
- stopRecording();
-
- // Delete temp file
- if (!mTempFile.delete()) {
- Log.e(TAG, "Error canceling screen recording!");
- Toast.makeText(this, R.string.screenrecord_delete_error, Toast.LENGTH_LONG)
- .show();
- } else {
- Toast.makeText(this, R.string.screenrecord_cancel_success, Toast.LENGTH_LONG)
- .show();
- }
-
- // Close quick shade
- sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
- break;
-
case ACTION_STOP:
stopRecording();
saveRecording(notificationManager);
break;
- case ACTION_PAUSE:
- mMediaRecorder.pause();
- setNotificationActions(true, notificationManager);
- break;
-
- case ACTION_RESUME:
- mMediaRecorder.resume();
- setNotificationActions(false, notificationManager);
- break;
-
case ACTION_SHARE:
Uri shareUri = Uri.parse(intent.getStringExtra(EXTRA_PATH));
@@ -233,9 +205,14 @@
*/
private void startRecording() {
try {
- mTempFile = File.createTempFile("temp", ".mp4");
+ File cacheDir = getCacheDir();
+ cacheDir.mkdirs();
+ mTempFile = File.createTempFile("temp", ".mp4", cacheDir);
Log.d(TAG, "Writing video output to: " + mTempFile.getAbsolutePath());
+ mOriginalShowTaps = 1 == Settings.System.getInt(
+ getApplicationContext().getContentResolver(),
+ Settings.System.SHOW_TOUCHES, 0);
setTapsVisible(mShowTaps);
// Set up media recorder
@@ -295,59 +272,42 @@
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
getString(R.string.screenrecord_name),
- NotificationManager.IMPORTANCE_LOW);
+ NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription(getString(R.string.screenrecord_channel_description));
channel.enableVibration(true);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
+ Bundle extras = new Bundle();
+ extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
+ getResources().getString(R.string.screenrecord_name));
+
mRecordingNotificationBuilder = new Notification.Builder(this, CHANNEL_ID)
- .setSmallIcon(R.drawable.ic_android)
+ .setSmallIcon(R.drawable.ic_screenrecord)
.setContentTitle(getResources().getString(R.string.screenrecord_name))
+ .setContentText(getResources().getString(R.string.screenrecord_stop_text))
.setUsesChronometer(true)
- .setOngoing(true);
- setNotificationActions(false, notificationManager);
+ .setColorized(true)
+ .setColor(getResources().getColor(R.color.GM2_red_700))
+ .setOngoing(true)
+ .setContentIntent(
+ PendingIntent.getService(
+ this, REQUEST_CODE, getStopIntent(this),
+ PendingIntent.FLAG_UPDATE_CURRENT))
+ .addExtras(extras);
+ notificationManager.notify(NOTIFICATION_ID, mRecordingNotificationBuilder.build());
Notification notification = mRecordingNotificationBuilder.build();
startForeground(NOTIFICATION_ID, notification);
}
- private void setNotificationActions(boolean isPaused, NotificationManager notificationManager) {
- String pauseString = getResources()
- .getString(isPaused ? R.string.screenrecord_resume_label
- : R.string.screenrecord_pause_label);
- Intent pauseIntent = isPaused ? getResumeIntent(this) : getPauseIntent(this);
-
- mRecordingNotificationBuilder.setActions(
- new Notification.Action.Builder(
- Icon.createWithResource(this, R.drawable.ic_android),
- getResources().getString(R.string.screenrecord_stop_label),
- PendingIntent
- .getService(this, REQUEST_CODE, getStopIntent(this),
- PendingIntent.FLAG_UPDATE_CURRENT))
- .build(),
- new Notification.Action.Builder(
- Icon.createWithResource(this, R.drawable.ic_android), pauseString,
- PendingIntent.getService(this, REQUEST_CODE, pauseIntent,
- PendingIntent.FLAG_UPDATE_CURRENT))
- .build(),
- new Notification.Action.Builder(
- Icon.createWithResource(this, R.drawable.ic_android),
- getResources().getString(R.string.screenrecord_cancel_label),
- PendingIntent
- .getService(this, REQUEST_CODE, getCancelIntent(this),
- PendingIntent.FLAG_UPDATE_CURRENT))
- .build());
- notificationManager.notify(NOTIFICATION_ID, mRecordingNotificationBuilder.build());
- }
-
private Notification createSaveNotification(Uri uri) {
Intent viewIntent = new Intent(Intent.ACTION_VIEW)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION)
.setDataAndType(uri, "video/mp4");
Notification.Action shareAction = new Notification.Action.Builder(
- Icon.createWithResource(this, R.drawable.ic_android),
+ Icon.createWithResource(this, R.drawable.ic_screenrecord),
getResources().getString(R.string.screenrecord_share_label),
PendingIntent.getService(
this,
@@ -357,7 +317,7 @@
.build();
Notification.Action deleteAction = new Notification.Action.Builder(
- Icon.createWithResource(this, R.drawable.ic_android),
+ Icon.createWithResource(this, R.drawable.ic_screenrecord),
getResources().getString(R.string.screenrecord_delete_label),
PendingIntent.getService(
this,
@@ -366,8 +326,12 @@
PendingIntent.FLAG_UPDATE_CURRENT))
.build();
+ Bundle extras = new Bundle();
+ extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
+ getResources().getString(R.string.screenrecord_name));
+
Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID)
- .setSmallIcon(R.drawable.ic_android)
+ .setSmallIcon(R.drawable.ic_screenrecord)
.setContentTitle(getResources().getString(R.string.screenrecord_name))
.setContentText(getResources().getString(R.string.screenrecord_save_message))
.setContentIntent(PendingIntent.getActivity(
@@ -377,7 +341,8 @@
Intent.FLAG_GRANT_READ_URI_PERMISSION))
.addAction(shareAction)
.addAction(deleteAction)
- .setAutoCancel(true);
+ .setAutoCancel(true)
+ .addExtras(extras);
// Add thumbnail if available
Bitmap thumbnailBitmap = null;
@@ -400,7 +365,7 @@
}
private void stopRecording() {
- setTapsVisible(false);
+ setTapsVisible(mOriginalShowTaps);
mMediaRecorder.stop();
mMediaRecorder.release();
mMediaRecorder = null;
@@ -459,18 +424,6 @@
return new Intent(context, RecordingService.class).setAction(ACTION_STOP);
}
- private static Intent getPauseIntent(Context context) {
- return new Intent(context, RecordingService.class).setAction(ACTION_PAUSE);
- }
-
- private static Intent getResumeIntent(Context context) {
- return new Intent(context, RecordingService.class).setAction(ACTION_RESUME);
- }
-
- private static Intent getCancelIntent(Context context) {
- return new Intent(context, RecordingService.class).setAction(ACTION_CANCEL);
- }
-
private static Intent getShareIntent(Context context, String path) {
return new Intent(context, RecordingService.class).setAction(ACTION_SHARE)
.putExtra(EXTRA_PATH, path);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 50e9a51..99a9dfe 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -240,7 +240,7 @@
PixelFormat.TRANSLUCENT);
mWindowLayoutParams.setTitle("ScreenshotAnimation");
mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- mWindowLayoutParams.setFitWindowInsetsTypes(0 /* types */);
+ mWindowLayoutParams.setFitInsetsTypes(0 /* types */);
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mDisplay = mWindowManager.getDefaultDisplay();
mDisplayMetrics = new DisplayMetrics();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
index 16447fb..a5baa7a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
@@ -156,7 +156,7 @@
PixelFormat.TRANSLUCENT);
mWindowLayoutParams.setTitle("ScreenshotAnimation");
mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- mWindowLayoutParams.setFitWindowInsetsTypes(0 /* types */);
+ mWindowLayoutParams.setFitInsetsTypes(0 /* types */);
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mDisplay = mWindowManager.getDefaultDisplay();
mDisplayMetrics = new DisplayMetrics();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index 083fbc9..ab69d47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -22,6 +22,7 @@
import android.util.MathUtils
import android.view.SurfaceControl
import android.view.ViewRootImpl
+import androidx.annotation.VisibleForTesting
import com.android.internal.util.IndentingPrintWriter
import com.android.systemui.DumpController
import com.android.systemui.Dumpable
@@ -33,7 +34,7 @@
import javax.inject.Singleton
@Singleton
-class BlurUtils @Inject constructor(
+open class BlurUtils @Inject constructor(
@Main private val resources: Resources,
val dumpController: DumpController
) : Dumpable {
@@ -63,22 +64,28 @@
* @param radius blur radius in pixels.
*/
fun applyBlur(viewRootImpl: ViewRootImpl?, radius: Int) {
- if (viewRootImpl == null || !supportsBlursOnWindows()) {
+ if (viewRootImpl == null || !viewRootImpl.surfaceControl.isValid ||
+ !supportsBlursOnWindows()) {
return
}
- SurfaceControl.Transaction().use {
+ createTransaction().use {
it.setBackgroundBlurRadius(viewRootImpl.surfaceControl, radius)
it.apply()
}
}
+ @VisibleForTesting
+ open fun createTransaction(): SurfaceControl.Transaction {
+ return SurfaceControl.Transaction()
+ }
+
/**
* If this device can render blurs.
*
* @see android.view.SurfaceControl.Transaction#setBackgroundBlurRadius(SurfaceControl, int)
* @return {@code true} when supported.
*/
- fun supportsBlursOnWindows(): Boolean {
+ open fun supportsBlursOnWindows(): Boolean {
return blurSysProp && ActivityManager.isHighEndGfx()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index ac05c53..6839921 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -56,7 +56,7 @@
}
public boolean isNewNotifPipelineEnabled() {
- return getDeviceConfigFlag("notification.newpipeline.enabled", false);
+ return getDeviceConfigFlag("notification.newpipeline.enabled", true);
}
public boolean isNewNotifPipelineRenderingEnabled() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 8cc45f2..4d6764e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -195,6 +195,7 @@
new ArrayList<>(),
new ArrayList<>(),
false,
+ false,
false
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 667e721..f3783c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -25,8 +25,10 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.app.RemoteInput;
+import android.app.RemoteInputHistoryItem;
import android.content.Context;
import android.content.Intent;
+import android.net.Uri;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -345,7 +347,8 @@
});
mSmartReplyController.setCallback((entry, reply) -> {
StatusBarNotification newSbn =
- rebuildNotificationWithRemoteInput(entry, reply, true /* showSpinner */);
+ rebuildNotificationWithRemoteInput(entry, reply, true /* showSpinner */,
+ null /* mimeType */, null /* uri */);
mEntryManager.updateNotification(newSbn, null /* ranking */);
});
}
@@ -527,28 +530,36 @@
StatusBarNotification rebuildNotificationForCanceledSmartReplies(
NotificationEntry entry) {
return rebuildNotificationWithRemoteInput(entry, null /* remoteInputTest */,
- false /* showSpinner */);
+ false /* showSpinner */, null /* mimeType */, null /* uri */);
}
@VisibleForTesting
StatusBarNotification rebuildNotificationWithRemoteInput(NotificationEntry entry,
- CharSequence remoteInputText, boolean showSpinner) {
+ CharSequence remoteInputText, boolean showSpinner, String mimeType, Uri uri) {
StatusBarNotification sbn = entry.getSbn();
Notification.Builder b = Notification.Builder
.recoverBuilder(mContext, sbn.getNotification().clone());
- if (remoteInputText != null) {
- CharSequence[] oldHistory = sbn.getNotification().extras
- .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
- CharSequence[] newHistory;
- if (oldHistory == null) {
- newHistory = new CharSequence[1];
+ if (remoteInputText != null || uri != null) {
+ RemoteInputHistoryItem[] oldHistoryItems = (RemoteInputHistoryItem[])
+ sbn.getNotification().extras.getParcelableArray(
+ Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+ RemoteInputHistoryItem[] newHistoryItems;
+
+ if (oldHistoryItems == null) {
+ newHistoryItems = new RemoteInputHistoryItem[1];
} else {
- newHistory = new CharSequence[oldHistory.length + 1];
- System.arraycopy(oldHistory, 0, newHistory, 1, oldHistory.length);
+ newHistoryItems = new RemoteInputHistoryItem[oldHistoryItems.length + 1];
+ System.arraycopy(oldHistoryItems, 0, newHistoryItems, 1, oldHistoryItems.length);
}
- newHistory[0] = String.valueOf(remoteInputText);
- b.setRemoteInputHistory(newHistory);
+ RemoteInputHistoryItem newItem;
+ if (uri != null) {
+ newItem = new RemoteInputHistoryItem(mimeType, uri, remoteInputText);
+ } else {
+ newItem = new RemoteInputHistoryItem(remoteInputText);
+ }
+ newHistoryItems[0] = newItem;
+ b.setRemoteInputHistory(newHistoryItems);
}
b.setShowRemoteInputSpinner(showSpinner);
b.setHideSmartReplies(true);
@@ -631,8 +642,11 @@
if (TextUtils.isEmpty(remoteInputText)) {
remoteInputText = entry.remoteInputTextWhenReset;
}
+ String remoteInputMimeType = entry.remoteInputMimeType;
+ Uri remoteInputUri = entry.remoteInputUri;
StatusBarNotification newSbn = rebuildNotificationWithRemoteInput(entry,
- remoteInputText, false /* showSpinner */);
+ remoteInputText, false /* showSpinner */, remoteInputMimeType,
+ remoteInputUri);
entry.onRemoteInputInserted();
if (newSbn == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 2fcfb8c..1f77ec2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -37,8 +37,10 @@
import android.app.NotificationChannel;
import android.app.NotificationManager.Policy;
import android.app.Person;
+import android.app.RemoteInputHistoryItem;
import android.content.Context;
import android.graphics.drawable.Icon;
+import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
import android.service.notification.NotificationListenerService.Ranking;
@@ -120,6 +122,8 @@
public int targetSdk;
private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
public CharSequence remoteInputText;
+ public String remoteInputMimeType;
+ public Uri remoteInputUri;
private Notification.BubbleMetadata mBubbleMetadata;
/**
@@ -595,8 +599,8 @@
return false;
}
Bundle extras = mSbn.getNotification().extras;
- CharSequence[] replyTexts = extras.getCharSequenceArray(
- Notification.EXTRA_REMOTE_INPUT_HISTORY);
+ RemoteInputHistoryItem[] replyTexts = (RemoteInputHistoryItem[]) extras.getParcelableArray(
+ Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
if (!ArrayUtils.isEmpty(replyTexts)) {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index f7fe064..1eeeab3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.collection
-import android.app.NotificationChannel
import android.app.NotificationManager.IMPORTANCE_HIGH
import android.app.NotificationManager.IMPORTANCE_MIN
import android.service.notification.NotificationListenerService.Ranking
@@ -192,9 +191,7 @@
}
private fun NotificationEntry.isPeopleNotification() =
- sbn.isPeopleNotification(channel)
- private fun StatusBarNotification.isPeopleNotification(channel: NotificationChannel) =
- peopleNotificationIdentifier.isPeopleNotification(this, channel)
+ peopleNotificationIdentifier.isPeopleNotification(sbn, ranking)
private fun NotificationEntry.isHighPriority() =
highPriorityProvider.isHighPriority(this)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
index da119c1..854444f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
import android.app.Notification;
-import android.os.Handler;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
@@ -30,6 +29,8 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.util.Assert;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import java.util.HashMap;
import java.util.Map;
@@ -47,6 +48,8 @@
* frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceController
* frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener
* frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender
+ *
+ * TODO: AppOps stuff should be spun off into its own coordinator
*/
@Singleton
public class ForegroundCoordinator implements Coordinator {
@@ -54,7 +57,7 @@
private final ForegroundServiceController mForegroundServiceController;
private final AppOpsController mAppOpsController;
- private final Handler mMainHandler;
+ private final DelayableExecutor mMainExecutor;
private NotifPipeline mNotifPipeline;
@@ -62,10 +65,10 @@
public ForegroundCoordinator(
ForegroundServiceController foregroundServiceController,
AppOpsController appOpsController,
- @Main Handler mainHandler) {
+ @Main DelayableExecutor mainExecutor) {
mForegroundServiceController = foregroundServiceController;
mAppOpsController = appOpsController;
- mMainHandler = mainHandler;
+ mMainExecutor = mainExecutor;
}
@Override
@@ -78,11 +81,11 @@
// listen for new notifications to add appOps
mNotifPipeline.addCollectionListener(mNotifCollectionListener);
- // when appOps change, update any relevant notifications to update appOps for
- mAppOpsController.addCallback(ForegroundServiceController.APP_OPS, this::onAppOpsChanged);
-
// filter out foreground service notifications that aren't necessary anymore
mNotifPipeline.addPreGroupFilter(mNotifFilter);
+
+ // when appOps change, update any relevant notifications to update appOps for
+ mAppOpsController.addCallback(ForegroundServiceController.APP_OPS, this::onAppOpsChanged);
}
/**
@@ -93,7 +96,8 @@
public boolean shouldFilterOut(NotificationEntry entry, long now) {
StatusBarNotification sbn = entry.getSbn();
if (mForegroundServiceController.isDisclosureNotification(sbn)
- && !mForegroundServiceController.isDisclosureNeededForUser(sbn.getUserId())) {
+ && !mForegroundServiceController.isDisclosureNeededForUser(
+ sbn.getUser().getIdentifier())) {
return true;
}
@@ -102,7 +106,7 @@
Notification.EXTRA_FOREGROUND_APPS);
if (apps != null && apps.length >= 1) {
if (!mForegroundServiceController.isSystemAlertWarningNeeded(
- sbn.getUserId(), apps[0])) {
+ sbn.getUser().getIdentifier(), apps[0])) {
return true;
}
}
@@ -119,7 +123,7 @@
new NotifLifetimeExtender() {
private static final int MIN_FGS_TIME_MS = 5000;
private OnEndLifetimeExtensionCallback mEndCallback;
- private Map<String, Runnable> mEndRunnables = new HashMap<>();
+ private Map<NotificationEntry, Runnable> mEndRunnables = new HashMap<>();
@Override
public String getName() {
@@ -142,16 +146,18 @@
final boolean extendLife = currTime - entry.getSbn().getPostTime() < MIN_FGS_TIME_MS;
if (extendLife) {
- if (!mEndRunnables.containsKey(entry.getKey())) {
- final Runnable runnable = new Runnable() {
- @Override
- public void run() {
- mEndCallback.onEndLifetimeExtension(mForegroundLifetimeExtender, entry);
- }
+ if (!mEndRunnables.containsKey(entry)) {
+ final Runnable endExtensionRunnable = () -> {
+ mEndRunnables.remove(entry);
+ mEndCallback.onEndLifetimeExtension(
+ mForegroundLifetimeExtender,
+ entry);
};
- mEndRunnables.put(entry.getKey(), runnable);
- mMainHandler.postDelayed(runnable,
+
+ final Runnable cancelRunnable = mMainExecutor.executeDelayed(
+ endExtensionRunnable,
MIN_FGS_TIME_MS - (currTime - entry.getSbn().getPostTime()));
+ mEndRunnables.put(entry, cancelRunnable);
}
}
@@ -160,9 +166,9 @@
@Override
public void cancelLifetimeExtension(NotificationEntry entry) {
- if (mEndRunnables.containsKey(entry.getKey())) {
- Runnable endRunnable = mEndRunnables.remove(entry.getKey());
- mMainHandler.removeCallbacks(endRunnable);
+ Runnable cancelRunnable = mEndRunnables.remove(entry);
+ if (cancelRunnable != null) {
+ cancelRunnable.run();
}
}
};
@@ -184,25 +190,32 @@
private void tagForeground(NotificationEntry entry) {
final StatusBarNotification sbn = entry.getSbn();
// note: requires that the ForegroundServiceController is updating their appOps first
- ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(sbn.getUserId(),
- sbn.getPackageName());
+ ArraySet<Integer> activeOps =
+ mForegroundServiceController.getAppOps(
+ sbn.getUser().getIdentifier(),
+ sbn.getPackageName());
if (activeOps != null) {
- synchronized (entry.mActiveAppOps) {
- entry.mActiveAppOps.clear();
- entry.mActiveAppOps.addAll(activeOps);
- }
+ entry.mActiveAppOps.clear();
+ entry.mActiveAppOps.addAll(activeOps);
}
}
};
+ private void onAppOpsChanged(int code, int uid, String packageName, boolean active) {
+ mMainExecutor.execute(() -> handleAppOpsChanged(code, uid, packageName, active));
+ }
+
/**
* Update the appOp for the posted notification associated with the current foreground service
+ *
* @param code code for appOp to add/remove
* @param uid of user the notification is sent to
* @param packageName package that created the notification
* @param active whether the appOpCode is active or not
*/
- private void onAppOpsChanged(int code, int uid, String packageName, boolean active) {
+ private void handleAppOpsChanged(int code, int uid, String packageName, boolean active) {
+ Assert.isMainThread();
+
int userId = UserHandle.getUserId(uid);
// Update appOp if there's an associated posted notification:
@@ -214,15 +227,13 @@
&& uid == entry.getSbn().getUid()
&& packageName.equals(entry.getSbn().getPackageName())) {
boolean changed;
- synchronized (entry.mActiveAppOps) {
- if (active) {
- changed = entry.mActiveAppOps.add(code);
- } else {
- changed = entry.mActiveAppOps.remove(code);
- }
+ if (active) {
+ changed = entry.mActiveAppOps.add(code);
+ } else {
+ changed = entry.mActiveAppOps.remove(code);
}
if (changed) {
- mMainHandler.post(mNotifFilter::invalidateList);
+ mNotifFilter.invalidateList();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
index ccd7fa3..b496116 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
@@ -100,7 +100,7 @@
private boolean isPeopleNotification(NotificationEntry entry) {
return mPeopleNotificationIdentifier.isPeopleNotification(
- entry.getSbn(), entry.getChannel());
+ entry.getSbn(), entry.getRanking());
}
private boolean hasUserSetImportance(NotificationEntry entry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
index 5c90211..4672de0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
@@ -16,31 +16,20 @@
package com.android.systemui.statusbar.notification.people
-import android.app.Notification
-import android.content.Context
-import android.app.NotificationChannel
+import android.service.notification.NotificationListenerService.Ranking
import android.service.notification.StatusBarNotification
-import android.util.FeatureFlagUtils
import javax.inject.Inject
import javax.inject.Singleton
interface PeopleNotificationIdentifier {
- fun isPeopleNotification(sbn: StatusBarNotification, channel: NotificationChannel): Boolean
+ fun isPeopleNotification(sbn: StatusBarNotification, ranking: Ranking): Boolean
}
@Singleton
class PeopleNotificationIdentifierImpl @Inject constructor(
- private val personExtractor: NotificationPersonExtractor,
- private val context: Context
+ private val personExtractor: NotificationPersonExtractor
) : PeopleNotificationIdentifier {
- override fun isPeopleNotification(sbn: StatusBarNotification, channel: NotificationChannel) =
- ((sbn.notification.notificationStyle == Notification.MessagingStyle::class.java &&
- (sbn.notification.shortcutId != null ||
- FeatureFlagUtils.isEnabled(
- context,
- FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ
- ))) ||
- personExtractor.isPersonNotification(sbn)) &&
- !channel.isDemoted
+ override fun isPeopleNotification(sbn: StatusBarNotification, ranking: Ranking) =
+ ranking.isConversation || personExtractor.isPersonNotification(sbn)
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
index 315ea0a..4f27c0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
@@ -35,6 +35,7 @@
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.Window
import android.view.WindowInsets.Type
+import android.view.WindowInsets.Type.statusBars
import android.view.WindowManager
import android.widget.TextView
import com.android.internal.annotations.VisibleForTesting
@@ -288,13 +289,13 @@
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
addFlags(wmFlags)
setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL)
- setFitWindowInsetsTypes(getFitWindowInsetsTypes() and Type.statusBars().inv())
setWindowAnimations(com.android.internal.R.style.Animation_InputMethod)
attributes = attributes.apply {
format = PixelFormat.TRANSLUCENT
title = ChannelEditorDialogController::class.java.simpleName
gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
+ fitInsetsTypes = attributes.fitInsetsTypes and statusBars().inv()
width = MATCH_PARENT
height = WRAP_CONTENT
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
index a3e1305..fa4bc2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
@@ -80,7 +80,15 @@
public Drawable loadImage(Uri uri) {
Drawable result = null;
try {
- result = hasCache() ? mImageCache.get(uri) : resolveImage(uri);
+ if (hasCache()) {
+ // if the uri isn't currently cached, try caching it first
+ if (!mImageCache.hasEntry(uri)) {
+ mImageCache.preload((uri));
+ }
+ result = mImageCache.get(uri);
+ } else {
+ result = resolveImage(uri);
+ }
} catch (IOException | SecurityException ex) {
Log.d(TAG, "loadImage: Can't load image from " + uri, ex);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index ef581db..6bd122d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -304,7 +304,7 @@
layoutParams.setTitle(TAG + mContext.getDisplayId());
layoutParams.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel);
layoutParams.windowAnimations = 0;
- layoutParams.setFitWindowInsetsTypes(0 /* types */);
+ layoutParams.setFitInsetsTypes(0 /* types */);
return layoutParams;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
index 783e7ad..16b5a23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
@@ -84,7 +84,7 @@
PixelFormat.TRANSLUCENT);
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("FloatingRotationButton");
- lp.setFitWindowInsetsTypes(0 /*types */);
+ lp.setFitInsetsTypes(0 /*types */);
switch (mWindowManager.getDefaultDisplay().getRotation()) {
case Surface.ROTATION_0:
lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
index fe4879b..3af8038 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
@@ -175,7 +175,7 @@
PixelFormat.TRANSLUCENT);
mLp.token = new Binder();
mLp.gravity = Gravity.TOP;
- mLp.setFitWindowInsetsTypes(0 /* types */);
+ mLp.setFitInsetsTypes(0 /* types */);
mLp.softInputMode = LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
mLp.setTitle("NotificationShade");
mLp.packageName = mContext.getPackageName();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
index 6979554..7650a3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static android.view.WindowInsets.Type.systemBars;
+
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
import android.annotation.LayoutRes;
@@ -81,7 +83,7 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
- final Insets insets = windowInsets.getMaxInsets(WindowInsets.Type.systemBars());
+ final Insets insets = windowInsets.getInsetsIgnoringVisibility(systemBars());
if (getFitsSystemWindows()) {
boolean paddingChanged = insets.top != getPaddingTop()
|| insets.bottom != getPaddingBottom();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 00e38f8..41d8968 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -37,7 +37,6 @@
import android.text.format.DateFormat;
import android.util.Log;
-import com.android.internal.telephony.TelephonyIntents;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -174,7 +173,7 @@
filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
filter.addAction(AudioManager.ACTION_HEADSET_PLUG);
- filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
@@ -614,10 +613,9 @@
case AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION:
updateVolumeZen();
break;
- case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
+ case Intent.ACTION_SIM_STATE_CHANGED:
// Avoid rebroadcast because SysUI is direct boot aware.
- if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK,
- false)) {
+ if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
break;
}
updateSimState(intent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index fb30bde..e448d0a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -107,6 +107,7 @@
PixelFormat.TRANSLUCENT);
mLp.token = new Binder();
mLp.gravity = Gravity.TOP;
+ mLp.setFitInsetsTypes(0 /* types */);
mLp.setTitle("StatusBar");
mLp.packageName = mContext.getPackageName();
mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 28b6c38..06105f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -106,8 +106,8 @@
if (Dependency.get(KeyguardStateController.class).isShowing()) {
final Window window = dialog.getWindow();
window.setType(LayoutParams.TYPE_STATUS_BAR_PANEL);
- window.setFitWindowInsetsTypes(
- window.getFitWindowInsetsTypes() & ~Type.statusBars());
+ window.getAttributes().setFitInsetsTypes(
+ window.getAttributes().getFitInsetsTypes() & ~Type.statusBars());
} else {
dialog.getWindow().setType(LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
}
@@ -118,8 +118,8 @@
window.setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL);
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
- window.setFitWindowInsetsTypes(
- window.getFitWindowInsetsTypes() & ~Type.statusBars());
+ window.getAttributes().setFitInsetsTypes(
+ window.getAttributes().getFitInsetsTypes() & ~Type.statusBars());
return dialog;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
index e675a7f..5dc9104 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
@@ -29,7 +29,6 @@
import android.util.AttributeSet;
import android.widget.TextView;
-import com.android.internal.telephony.TelephonyIntents;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dependency;
@@ -137,9 +136,9 @@
displayText = getContext().getText(
com.android.internal.R.string.emergency_calls_only);
Intent i = getContext().registerReceiver(null,
- new IntentFilter(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION));
+ new IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED));
if (i != null) {
- displayText = i.getStringExtra(TelephonyIntents.EXTRA_PLMN);
+ displayText = i.getStringExtra(TelephonyManager.EXTRA_PLMN);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index cca100f..3a1feaa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -27,6 +27,7 @@
import android.os.Message;
import android.provider.Settings.Global;
import android.telephony.Annotation;
+import android.telephony.CdmaEriInformation;
import android.telephony.CellSignalStrength;
import android.telephony.CellSignalStrengthCdma;
import android.telephony.NetworkRegistrationInfo;
@@ -40,8 +41,6 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.cdma.EriInfo;
import com.android.settingslib.Utils;
import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.net.SignalStrengthUtil;
@@ -415,10 +414,10 @@
return false;
}
if (isCdma() && mServiceState != null) {
- final int iconMode = mServiceState.getCdmaEriIconMode();
- return mServiceState.getCdmaEriIconIndex() != EriInfo.ROAMING_INDICATOR_OFF
- && (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL
- || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH);
+ final int iconMode = mPhone.getCdmaEriInformation().getEriIconMode();
+ return mPhone.getCdmaEriInformation().getEriIconIndex() != CdmaEriInformation.ERI_OFF
+ && (iconMode == CdmaEriInformation.ERI_ICON_MODE_NORMAL
+ || iconMode == CdmaEriInformation.ERI_ICON_MODE_FLASH);
} else {
return mServiceState != null && mServiceState.getRoaming();
}
@@ -430,12 +429,12 @@
public void handleBroadcast(Intent intent) {
String action = intent.getAction();
- if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) {
- updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false),
- intent.getStringExtra(TelephonyIntents.EXTRA_SPN),
- intent.getStringExtra(TelephonyIntents.EXTRA_DATA_SPN),
- intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false),
- intent.getStringExtra(TelephonyIntents.EXTRA_PLMN));
+ if (action.equals(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED)) {
+ updateNetworkName(intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false),
+ intent.getStringExtra(TelephonyManager.EXTRA_SPN),
+ intent.getStringExtra(TelephonyManager.EXTRA_DATA_SPN),
+ intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_PLMN, false),
+ intent.getStringExtra(TelephonyManager.EXTRA_PLMN));
notifyListenersIfNecessary();
} else if (action.equals(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
updateDataSim();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 6b3c5dc..6dd1133 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -315,11 +315,11 @@
filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
filter.addAction(TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
filter.addAction(Intent.ACTION_SERVICE_STATE);
- filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
+ filter.addAction(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
@@ -524,9 +524,9 @@
mConfig = Config.readConfig(mContext);
mReceiverHandler.post(this::handleConfigurationChanged);
break;
- case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
+ case Intent.ACTION_SIM_STATE_CHANGED:
// Avoid rebroadcast because SysUI is direct boot aware.
- if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
+ if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
break;
}
// Might have different subscriptions now.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 307e3bc..408b3a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -161,6 +161,11 @@
Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent,
results);
+
+ mEntry.remoteInputText = mEditText.getText();
+ mEntry.remoteInputUri = null;
+ mEntry.remoteInputMimeType = null;
+
if (mEntry.editedSuggestionInfo == null) {
RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_FREE_FORM_INPUT);
} else {
@@ -177,6 +182,10 @@
Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
RemoteInput.addDataResultToIntent(mRemoteInput, fillInIntent, results);
+ mEntry.remoteInputText = mContext.getString(R.string.remote_input_image_insertion_text);
+ mEntry.remoteInputMimeType = contentType;
+ mEntry.remoteInputUri = data;
+
return fillInIntent;
}
@@ -184,7 +193,6 @@
mEditText.setEnabled(false);
mSendButton.setVisibility(INVISIBLE);
mProgressBar.setVisibility(VISIBLE);
- mEntry.remoteInputText = mEditText.getText();
mEntry.lastRemoteInputSent = SystemClock.elapsedRealtime();
mController.addSpinning(mEntry.getKey(), mToken);
mController.removeRemoteInput(mEntry, mToken);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 718522c..6baf36c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -54,7 +54,8 @@
mWifiTracker.setListening(true);
mHasMobileData = hasMobileData;
if (wifiManager != null) {
- wifiManager.registerTrafficStateCallback(new WifiTrafficStateCallback());
+ wifiManager.registerTrafficStateCallback(context.getMainExecutor(),
+ new WifiTrafficStateCallback());
}
// WiFi only has one state.
mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index b3c2ba3..59eb6c5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -170,7 +170,7 @@
Assert.assertTrue("onSimStateChanged not called",
mKeyguardUpdateMonitor.hasSimStateJustChanged());
- intent.putExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, true);
+ intent.putExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, true);
mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), intent);
mTestableLooper.processAllMessages();
Assert.assertFalse("onSimStateChanged should have been skipped",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
index 2ecc8ea..b3071f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
@@ -19,8 +19,11 @@
import android.content.Context;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Log;
public class TestableDependency extends Dependency {
+ private static final String TAG = "TestableDependency";
+
private final ArrayMap<Object, Object> mObjs = new ArrayMap<>();
private final ArraySet<Object> mInstantiatedObjects = new ArraySet<>();
@@ -44,7 +47,7 @@
public <T> void injectTestDependency(Class<T> key, T obj) {
if (mInstantiatedObjects.contains(key)) {
- throw new IllegalStateException(key + " was already initialized");
+ Log.d(TAG, key + " was already initialized but overriding with testDependency.");
}
mObjs.put(key, obj);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 8a9a7a2..4c68102 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -77,12 +78,14 @@
private static final Set<String> FACTORY_TILES = new ArraySet<>();
private static final String TEST_PKG = "test_pkg";
private static final String TEST_CLS = "test_cls";
+ private static final String CUSTOM_TILE = "custom(" + TEST_PKG + "/" + TEST_CLS + ")";
static {
FACTORY_TILES.addAll(Arrays.asList(
new String[]{"wifi", "bt", "cell", "dnd", "inversion", "airplane", "work",
"rotation", "flashlight", "location", "cast", "hotspot", "user", "battery",
"saver", "night", "nfc"}));
+ FACTORY_TILES.add(CUSTOM_TILE);
}
@Mock
@@ -223,6 +226,15 @@
}
@Test
+ public void testCustomTileNotCreated() {
+ Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES,
+ CUSTOM_TILE);
+ mTileQueryHelper.queryTiles(mQSTileHost);
+ FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
+ verify(mQSTileHost, never()).createTile(CUSTOM_TILE);
+ }
+
+ @Test
public void testThirdPartyTilesInactive() {
ResolveInfo resolveInfo = new ResolveInfo();
ServiceInfo serviceInfo = mock(ServiceInfo.class, Answers.RETURNS_MOCKS);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt
new file mode 100644
index 0000000..c180a88
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar
+
+import android.content.res.Resources
+import android.view.SurfaceControl
+import android.view.ViewRootImpl
+import androidx.test.filters.SmallTest
+import com.android.systemui.DumpController
+import com.android.systemui.SysuiTestCase
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class BlurUtilsTest : SysuiTestCase() {
+
+ @Mock lateinit var resources: Resources
+ @Mock lateinit var dumpController: DumpController
+ @Mock lateinit var transaction: SurfaceControl.Transaction
+ lateinit var blurUtils: BlurUtils
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ blurUtils = TestableBlurUtils()
+ }
+
+ @Test
+ fun testApplyBlur_noViewRoot_doesntCrash() {
+ blurUtils.applyBlur(null /* viewRootImple */, 10 /* radius */)
+ }
+
+ @Test
+ fun testApplyBlur_invalidSurfaceControl() {
+ val surfaceControl = mock(SurfaceControl::class.java)
+ val viewRootImpl = mock(ViewRootImpl::class.java)
+ `when`(viewRootImpl.surfaceControl).thenReturn(surfaceControl)
+ blurUtils.applyBlur(viewRootImpl, 10 /* radius */)
+ }
+
+ @Test
+ fun testApplyBlur_success() {
+ val radius = 10
+ val surfaceControl = mock(SurfaceControl::class.java)
+ val viewRootImpl = mock(ViewRootImpl::class.java)
+ `when`(viewRootImpl.surfaceControl).thenReturn(surfaceControl)
+ `when`(surfaceControl.isValid).thenReturn(true)
+ blurUtils.applyBlur(viewRootImpl, radius)
+ verify(transaction).setBackgroundBlurRadius(eq(surfaceControl), eq(radius))
+ verify(transaction).apply()
+ }
+
+ inner class TestableBlurUtils() : BlurUtils(resources, dumpController) {
+ override fun supportsBlursOnWindows(): Boolean {
+ return true
+ }
+
+ override fun createTransaction(): SurfaceControl.Transaction {
+ return transaction
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 64b10c8..1117646 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -10,7 +10,9 @@
import static org.mockito.Mockito.when;
import android.app.Notification;
+import android.app.RemoteInputHistoryItem;
import android.content.Context;
+import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
@@ -150,13 +152,30 @@
}
@Test
+ public void testRebuildWithRemoteInput_noExistingInput_image() {
+ Uri uri = mock(Uri.class);
+ String mimeType = "image/jpeg";
+ String text = "image inserted";
+ StatusBarNotification newSbn =
+ mRemoteInputManager.rebuildNotificationWithRemoteInput(
+ mEntry, text, false, mimeType, uri);
+ RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification()
+ .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+ assertEquals(1, messages.length);
+ assertEquals(text, messages[0].getText());
+ assertEquals(mimeType, messages[0].getMimeType());
+ assertEquals(uri, messages[0].getUri());
+ }
+
+ @Test
public void testRebuildWithRemoteInput_noExistingInputNoSpinner() {
StatusBarNotification newSbn =
- mRemoteInputManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", false);
- CharSequence[] messages = newSbn.getNotification().extras
- .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
+ mRemoteInputManager.rebuildNotificationWithRemoteInput(
+ mEntry, "A Reply", false, null, null);
+ RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification()
+ .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
assertEquals(1, messages.length);
- assertEquals("A Reply", messages[0]);
+ assertEquals("A Reply", messages[0].getText());
assertFalse(newSbn.getNotification().extras
.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false));
assertTrue(newSbn.getNotification().extras
@@ -166,11 +185,12 @@
@Test
public void testRebuildWithRemoteInput_noExistingInputWithSpinner() {
StatusBarNotification newSbn =
- mRemoteInputManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", true);
- CharSequence[] messages = newSbn.getNotification().extras
- .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
+ mRemoteInputManager.rebuildNotificationWithRemoteInput(
+ mEntry, "A Reply", true, null, null);
+ RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification()
+ .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
assertEquals(1, messages.length);
- assertEquals("A Reply", messages[0]);
+ assertEquals("A Reply", messages[0].getText());
assertTrue(newSbn.getNotification().extras
.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false));
assertTrue(newSbn.getNotification().extras
@@ -181,18 +201,45 @@
public void testRebuildWithRemoteInput_withExistingInput() {
// Setup a notification entry with 1 remote input.
StatusBarNotification newSbn =
- mRemoteInputManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", false);
+ mRemoteInputManager.rebuildNotificationWithRemoteInput(
+ mEntry, "A Reply", false, null, null);
NotificationEntry entry = new NotificationEntryBuilder()
.setSbn(newSbn)
.build();
// Try rebuilding to add another reply.
- newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInput(entry, "Reply 2", true);
- CharSequence[] messages = newSbn.getNotification().extras
- .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
+ newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInput(
+ entry, "Reply 2", true, null, null);
+ RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification()
+ .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
assertEquals(2, messages.length);
- assertEquals("Reply 2", messages[0]);
- assertEquals("A Reply", messages[1]);
+ assertEquals("Reply 2", messages[0].getText());
+ assertEquals("A Reply", messages[1].getText());
+ }
+
+ @Test
+ public void testRebuildWithRemoteInput_withExistingInput_image() {
+ // Setup a notification entry with 1 remote input.
+ Uri uri = mock(Uri.class);
+ String mimeType = "image/jpeg";
+ String text = "image inserted";
+ StatusBarNotification newSbn =
+ mRemoteInputManager.rebuildNotificationWithRemoteInput(
+ mEntry, text, false, mimeType, uri);
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setSbn(newSbn)
+ .build();
+
+ // Try rebuilding to add another reply.
+ newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInput(
+ entry, "Reply 2", true, null, null);
+ RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification()
+ .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+ assertEquals(2, messages.length);
+ assertEquals("Reply 2", messages[0].getText());
+ assertEquals(text, messages[1].getText());
+ assertEquals(mimeType, messages[1].getMimeType());
+ assertEquals(uri, messages[1].getUri());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
index 5310dd8..16f105d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
@@ -52,6 +52,7 @@
private ArrayList<CharSequence> mSmartReplies = new ArrayList<>();
private boolean mCanBubble = false;
private boolean mIsVisuallyInterruptive = false;
+ private boolean mIsConversation = false;
public RankingBuilder() {
}
@@ -77,6 +78,7 @@
mSmartReplies = copyList(ranking.getSmartReplies());
mCanBubble = ranking.canBubble();
mIsVisuallyInterruptive = ranking.visuallyInterruptive();
+ mIsConversation = ranking.isConversation();
}
public Ranking build() {
@@ -101,7 +103,8 @@
mSmartActions,
mSmartReplies,
mCanBubble,
- mIsVisuallyInterruptive);
+ mIsVisuallyInterruptive,
+ mIsConversation);
return ranking;
}
@@ -181,6 +184,11 @@
return this;
}
+ public RankingBuilder setIsConversation(boolean isConversation) {
+ mIsConversation = isConversation;
+ return this;
+ }
+
public RankingBuilder setImportance(@Importance int importance) {
mImportance = importance;
return this;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 4b2ce01..296d0cef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -45,7 +45,6 @@
import android.content.Intent;
import android.graphics.drawable.Icon;
import android.os.Handler;
-import android.os.Looper;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
@@ -88,7 +87,6 @@
import com.android.systemui.statusbar.notification.row.NotifRemoteViewCache;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -99,6 +97,7 @@
import com.android.systemui.util.Assert;
import com.android.systemui.util.leak.LeakDetector;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -162,7 +161,8 @@
0,
IMPORTANCE_DEFAULT,
null, null,
- null, null, null, true, sentiment, false, -1, false, null, null, false, false);
+ null, null, null, true, sentiment, false, -1, false, null, null, false, false,
+ false);
return true;
}).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
}
@@ -181,7 +181,7 @@
null, null,
null, null, null, true,
Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
- false, smartActions, null, false, false);
+ false, smartActions, null, false, false, false);
return true;
}).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
}
@@ -189,15 +189,14 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- if (!mDependency.hasInstantiatedDependency(SmartReplyController.class)) {
- mDependency.injectMockDependency(SmartReplyController.class);
- }
+ mDependency.injectMockDependency(SmartReplyController.class);
mDependency.injectMockDependency(NotificationMediaManager.class);
mCountDownLatch = new CountDownLatch(1);
+ Assert.sMainLooper = TestableLooper.get(this).getLooper();
mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
- Handler.createAsync(Looper.myLooper()));
+ Handler.createAsync(TestableLooper.get(this).getLooper()));
when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
when(mListContainer.getViewParentForNotification(any())).thenReturn(
new FrameLayout(mContext));
@@ -207,9 +206,10 @@
mEntry.expandedIcon = mock(StatusBarIconView.class);
- NotificationRowContentBinder contentBinder = new NotificationContentInflater(
+ NotificationContentInflater contentBinder = new NotificationContentInflater(
mock(NotifRemoteViewCache.class),
mRemoteInputManager);
+ contentBinder.setInflateSynchronously(true);
when(mNotificationRowComponentBuilder.activatableNotificationView(any()))
.thenReturn(mNotificationRowComponentBuilder);
@@ -262,6 +262,12 @@
mEntry.getKey(), Ranking.USER_SENTIMENT_NEUTRAL);
}
+ @After
+ public void tearDown() {
+ // CLEAN UP inflation tasks so they don't callback in a future test
+ mEntry.abortTask();
+ }
+
@Test
public void testAddNotification() throws Exception {
TestableLooper.get(this).processAllMessages();
@@ -347,9 +353,6 @@
@Test
public void testRemoveNotification() {
- // Row inflation happens off thread, so pretend that this test looper is main
- Assert.sMainLooper = TestableLooper.get(this).getLooper();
-
mEntry.setRow(mRow);
mEntryManager.addActiveNotificationForTest(mEntry);
@@ -467,9 +470,6 @@
@Test
public void testLifetimeExtenders_whenRetentionEndsNotificationIsRemoved() {
- // Row inflation happens off thread, so pretend that this test looper is main
- Assert.sMainLooper = TestableLooper.get(this).getLooper();
-
// GIVEN an entry manager with a notification whose life has been extended
mEntryManager.addActiveNotificationForTest(mEntry);
final FakeNotificationLifetimeExtender extender = new FakeNotificationLifetimeExtender();
@@ -560,9 +560,6 @@
@Test
public void testRemoveInterceptor_notInterceptedGetsRemoved() {
- // Row inflation happens off thread, so pretend that this test looper is main
- Assert.sMainLooper = TestableLooper.get(this).getLooper();
-
// GIVEN an entry manager with a notification
mEntryManager.addActiveNotificationForTest(mEntry);
@@ -625,7 +622,6 @@
@Test
public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications() {
- Assert.sMainLooper = TestableLooper.get(this).getLooper();
Notification.Builder n = new Notification.Builder(mContext, "di")
.setSmallIcon(R.drawable.ic_person)
.setContentTitle("Title")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
index 7c3665b..e4865b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
@@ -60,7 +60,7 @@
final NotificationEntry entry = new NotificationEntryBuilder()
.setImportance(IMPORTANCE_HIGH)
.build();
- when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getRanking()))
.thenReturn(false);
// THEN it has high priority
@@ -76,7 +76,7 @@
.setNotification(notification)
.setImportance(IMPORTANCE_LOW)
.build();
- when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getRanking()))
.thenReturn(true);
// THEN it has high priority
@@ -92,7 +92,7 @@
final NotificationEntry entry = new NotificationEntryBuilder()
.setNotification(notification)
.build();
- when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getRanking()))
.thenReturn(false);
// THEN it has high priority
@@ -109,7 +109,7 @@
.setNotification(notification)
.setImportance(IMPORTANCE_LOW)
.build();
- when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getRanking()))
.thenReturn(false);
// THEN it has high priority
@@ -126,7 +126,7 @@
.setNotification(notification)
.setImportance(IMPORTANCE_MIN)
.build();
- when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getRanking()))
.thenReturn(false);
// THEN it does NOT have high priority
@@ -149,7 +149,7 @@
.setNotification(notification)
.setChannel(channel)
.build();
- when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getRanking()))
.thenReturn(true);
// THEN it does NOT have high priority
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java
index 6cc8dd9..eb1af7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java
@@ -16,20 +16,23 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
+import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
import android.os.Bundle;
-import android.os.Handler;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.ArraySet;
import androidx.test.filters.SmallTest;
@@ -41,36 +44,53 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.util.Assert;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
public class ForegroundCoordinatorTest extends SysuiTestCase {
private static final String TEST_PKG = "test_pkg";
private static final int NOTIF_USER_ID = 0;
- @Mock private Handler mMainHandler;
@Mock private ForegroundServiceController mForegroundServiceController;
@Mock private AppOpsController mAppOpsController;
@Mock private NotifPipeline mNotifPipeline;
+ @Captor private ArgumentCaptor<AppOpsController.Callback> mAppOpsCaptor;
+
private NotificationEntry mEntry;
private Notification mNotification;
private ForegroundCoordinator mForegroundCoordinator;
private NotifFilter mForegroundFilter;
+ private AppOpsController.Callback mAppOpsCallback;
private NotifLifetimeExtender mForegroundNotifLifetimeExtender;
+ private FakeSystemClock mClock = new FakeSystemClock();
+ private FakeExecutor mExecutor = new FakeExecutor(mClock);
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mForegroundCoordinator = new ForegroundCoordinator(
- mForegroundServiceController, mAppOpsController, mMainHandler);
+ Assert.sMainLooper = TestableLooper.get(this).getLooper();
+
+ mForegroundCoordinator =
+ new ForegroundCoordinator(
+ mForegroundServiceController,
+ mAppOpsController,
+ mExecutor);
mNotification = new Notification();
mEntry = new NotificationEntryBuilder()
@@ -86,9 +106,11 @@
verify(mNotifPipeline, times(1)).addPreGroupFilter(filterCaptor.capture());
verify(mNotifPipeline, times(1)).addNotificationLifetimeExtender(
lifetimeExtenderCaptor.capture());
+ verify(mAppOpsController).addCallback(any(int[].class), mAppOpsCaptor.capture());
mForegroundFilter = filterCaptor.getValue();
mForegroundNotifLifetimeExtender = lifetimeExtenderCaptor.getValue();
+ mAppOpsCallback = mAppOpsCaptor.getValue();
}
@Test
@@ -183,4 +205,74 @@
assertFalse(mForegroundNotifLifetimeExtender
.shouldExtendLifetime(mEntry, NotificationListenerService.REASON_CLICK));
}
+
+ @Test
+ public void testAppOpsAreApplied() {
+ // GIVEN Three current notifications, two with the same key but from different users
+ NotificationEntry entry1 = new NotificationEntryBuilder()
+ .setUser(new UserHandle(NOTIF_USER_ID))
+ .setPkg(TEST_PKG)
+ .setId(1)
+ .build();
+ NotificationEntry entry2 = new NotificationEntryBuilder()
+ .setUser(new UserHandle(NOTIF_USER_ID))
+ .setPkg(TEST_PKG)
+ .setId(2)
+ .build();
+ NotificationEntry entry2Other = new NotificationEntryBuilder()
+ .setUser(new UserHandle(NOTIF_USER_ID + 1))
+ .setPkg(TEST_PKG)
+ .setId(2)
+ .build();
+ when(mNotifPipeline.getActiveNotifs()).thenReturn(List.of(entry1, entry2, entry2Other));
+
+ // GIVEN that entry2 is currently associated with a foreground service
+ when(mForegroundServiceController.getStandardLayoutKey(0, TEST_PKG))
+ .thenReturn(entry2.getKey());
+
+ // WHEN a new app ops code comes in
+ mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true);
+ mExecutor.runAllReady();
+
+ // THEN entry2's app ops are updated, but no one else's are
+ assertEquals(
+ new ArraySet<>(),
+ entry1.mActiveAppOps);
+ assertEquals(
+ new ArraySet<>(List.of(47)),
+ entry2.mActiveAppOps);
+ assertEquals(
+ new ArraySet<>(),
+ entry2Other.mActiveAppOps);
+ }
+
+ @Test
+ public void testAppOpsAreRemoved() {
+ // GIVEN One notification which is associated with app ops
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setUser(new UserHandle(NOTIF_USER_ID))
+ .setPkg(TEST_PKG)
+ .setId(2)
+ .build();
+ when(mNotifPipeline.getActiveNotifs()).thenReturn(List.of(entry));
+ when(mForegroundServiceController.getStandardLayoutKey(0, TEST_PKG))
+ .thenReturn(entry.getKey());
+
+ // GIVEN that the notification's app ops are already [47, 33]
+ mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, true);
+ mAppOpsCallback.onActiveStateChanged(33, NOTIF_USER_ID, TEST_PKG, true);
+ mExecutor.runAllReady();
+ assertEquals(
+ new ArraySet<>(List.of(47, 33)),
+ entry.mActiveAppOps);
+
+ // WHEN one of the app ops is removed
+ mAppOpsCallback.onActiveStateChanged(47, NOTIF_USER_ID, TEST_PKG, false);
+ mExecutor.runAllReady();
+
+ // THEN the entry's active app ops are updated as well
+ assertEquals(
+ new ArraySet<>(List.of(33)),
+ entry.mActiveAppOps);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 13f3a5f..cc3c3cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -42,6 +42,7 @@
import android.os.Handler;
import android.provider.Settings;
import android.provider.Settings.Global;
+import android.telephony.CdmaEriInformation;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
@@ -111,6 +112,8 @@
private NetworkCapabilities mNetCapabilities;
private ConnectivityManager.NetworkCallback mNetworkCallback;
+ private CdmaEriInformation mEriInformation;
+
@Rule
public TestWatcher failWatcher = new TestWatcher() {
@Override
@@ -155,6 +158,10 @@
mSignalStrength = mock(SignalStrength.class);
mServiceState = mock(ServiceState.class);
+ mEriInformation = new CdmaEriInformation(CdmaEriInformation.ERI_OFF,
+ CdmaEriInformation.ERI_ICON_MODE_NORMAL);
+ when(mMockTm.getCdmaEriInformation()).thenReturn(mEriInformation);
+
mConfig = new Config();
mConfig.hspaDataDistinguishable = true;
mCallbackHandler = mock(CallbackHandler.class);
@@ -305,11 +312,9 @@
}
public void setCdmaRoaming(boolean isRoaming) {
- when(mServiceState.getCdmaEriIconIndex()).thenReturn(isRoaming ?
- EriInfo.ROAMING_INDICATOR_ON : EriInfo.ROAMING_INDICATOR_OFF);
- when(mServiceState.getCdmaEriIconMode()).thenReturn(isRoaming ?
- EriInfo.ROAMING_ICON_MODE_NORMAL : -1);
- updateServiceState();
+ mEriInformation.setEriIconIndex(isRoaming ?
+ CdmaEriInformation.ERI_ON : CdmaEriInformation.ERI_OFF);
+ when(mMockTm.getCdmaEriInformation()).thenReturn(mEriInformation);
}
public void setVoiceRegState(int voiceRegState) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index cd89d3c..4406248 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -36,7 +36,6 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
-import com.android.internal.telephony.TelephonyIntents;
import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.R;
@@ -411,13 +410,13 @@
boolean showPlmn, String plmn) {
Intent intent = new Intent();
- intent.setAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
+ intent.setAction(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
- intent.putExtra(TelephonyIntents.EXTRA_SHOW_SPN, showSpn);
- intent.putExtra(TelephonyIntents.EXTRA_SPN, spn);
+ intent.putExtra(TelephonyManager.EXTRA_SHOW_SPN, showSpn);
+ intent.putExtra(TelephonyManager.EXTRA_SPN, spn);
- intent.putExtra(TelephonyIntents.EXTRA_SHOW_PLMN, showPlmn);
- intent.putExtra(TelephonyIntents.EXTRA_PLMN, plmn);
+ intent.putExtra(TelephonyManager.EXTRA_SHOW_PLMN, showPlmn);
+ intent.putExtra(TelephonyManager.EXTRA_PLMN, plmn);
SubscriptionManager.putSubscriptionIdExtra(intent, mSubId);
return intent;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/animation/PhysicsAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/animation/PhysicsAnimatorTest.kt
index a785d4b..3f3f4d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/animation/PhysicsAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/animation/PhysicsAnimatorTest.kt
@@ -23,6 +23,7 @@
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertTrue
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyFloat
@@ -446,6 +447,7 @@
}
@Test
+ @Ignore("Sporadically flaking.")
fun testFlingThenSpring() {
PhysicsAnimatorTestUtils.setAllAnimationsBlock(false)
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index 264ce44..a8d1239 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -36,6 +36,7 @@
sdk_version: "system_current",
srcs: [
"src/android/net/TetheringManager.java",
+ "src/android/net/TetheringConstants.java",
":framework-tethering-annotations",
],
static_libs: [
@@ -63,9 +64,11 @@
name: "framework-tethering-srcs",
srcs: [
"src/android/net/TetheringManager.java",
+ "src/android/net/TetheringConstants.java",
"src/android/net/IIntResultListener.aidl",
"src/android/net/ITetheringEventCallback.aidl",
"src/android/net/ITetheringConnector.aidl",
+ "src/android/net/TetheringCallbackStartedParcel.aidl",
"src/android/net/TetheringConfigurationParcel.aidl",
"src/android/net/TetherStatesParcel.aidl",
],
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
index 2836195..28a810d 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
+++ b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
@@ -18,6 +18,7 @@
import android.net.Network;
import android.net.TetheringConfigurationParcel;
+import android.net.TetheringCallbackStartedParcel;
import android.net.TetherStatesParcel;
/**
@@ -26,8 +27,8 @@
*/
oneway interface ITetheringEventCallback
{
- void onCallbackStarted(in Network network, in TetheringConfigurationParcel config,
- in TetherStatesParcel states);
+ /** Called immediately after the callbacks are registered */
+ void onCallbackStarted(in TetheringCallbackStartedParcel parcel);
void onCallbackStopped(int errorCode);
void onUpstreamChanged(in Network network);
void onConfigurationChanged(in TetheringConfigurationParcel config);
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl
new file mode 100644
index 0000000..14ee2d3
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.Network;
+import android.net.TetheringConfigurationParcel;
+import android.net.TetherStatesParcel;
+
+/**
+ * Initial information reported by tethering upon callback registration.
+ * @hide
+ */
+parcelable TetheringCallbackStartedParcel {
+ boolean tetheringSupported;
+ Network upstreamNetwork;
+ TetheringConfigurationParcel config;
+ TetherStatesParcel states;
+}
\ No newline at end of file
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
new file mode 100644
index 0000000..00cf98e
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.os.ResultReceiver;
+
+/**
+ * Collections of constants for internal tethering usage.
+ *
+ * <p>These hidden constants are not in TetheringManager as they are not part of the API stubs
+ * generated for TetheringManager, which prevents the tethering module from linking them at
+ * build time.
+ * TODO: investigate changing the tethering build rules so that Tethering can reference hidden
+ * symbols from framework-tethering even when they are in a non-hidden class.
+ * @hide
+ */
+public class TetheringConstants {
+ /**
+ * Extra used for communicating with the TetherService. Includes the type of tethering to
+ * enable if any.
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
+ /**
+ * Extra used for communicating with the TetherService. Includes the type of tethering for
+ * which to cancel provisioning.
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
+ /**
+ * Extra used for communicating with the TetherService. True to schedule a recheck of tether
+ * provisioning.
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_SET_ALARM = "extraSetAlarm";
+ /**
+ * Tells the TetherService to run a provision check now.
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
+ /**
+ * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver}
+ * which will receive provisioning results. Can be left empty.
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
+}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index 11e5718..e1b9c16 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -16,8 +16,12 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.Context;
-import android.net.ConnectivityManager.OnTetheringEventCallback;
+import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.IBinder;
import android.os.RemoteException;
@@ -25,6 +29,11 @@
import android.util.ArrayMap;
import android.util.Log;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -34,7 +43,8 @@
*
* @hide
*/
-// TODO: make it @SystemApi
+@SystemApi
+@TestApi
public class TetheringManager {
private static final String TAG = TetheringManager.class.getSimpleName();
private static final int DEFAULT_TIMEOUT_MS = 60_000;
@@ -44,7 +54,7 @@
private final ITetheringConnector mConnector;
private final TetheringCallbackInternal mCallback;
private final Context mContext;
- private final ArrayMap<OnTetheringEventCallback, ITetheringEventCallback>
+ private final ArrayMap<TetheringEventCallback, ITetheringEventCallback>
mTetheringEventCallbacks = new ArrayMap<>();
private TetheringConfigurationParcel mTetheringConfiguration;
@@ -72,7 +82,7 @@
* gives a String[] listing all the interfaces currently in local-only
* mode (ie, has DHCPv4+IPv6-ULA support and no packet forwarding)
*/
- public static final String EXTRA_ACTIVE_LOCAL_ONLY = "localOnlyArray";
+ public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
/**
* gives a String[] listing all the interfaces currently tethered
@@ -118,35 +128,6 @@
*/
public static final int TETHERING_WIFI_P2P = 3;
- /**
- * Extra used for communicating with the TetherService. Includes the type of tethering to
- * enable if any.
- */
- public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
-
- /**
- * Extra used for communicating with the TetherService. Includes the type of tethering for
- * which to cancel provisioning.
- */
- public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
-
- /**
- * Extra used for communicating with the TetherService. True to schedule a recheck of tether
- * provisioning.
- */
- public static final String EXTRA_SET_ALARM = "extraSetAlarm";
-
- /**
- * Tells the TetherService to run a provision check now.
- */
- public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
-
- /**
- * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver}
- * which will receive provisioning results. Can be left empty.
- */
- public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
-
public static final int TETHER_ERROR_NO_ERROR = 0;
public static final int TETHER_ERROR_UNKNOWN_IFACE = 1;
public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2;
@@ -160,12 +141,14 @@
public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10;
public static final int TETHER_ERROR_PROVISION_FAILED = 11;
public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12;
- public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13;
+ public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13;
public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14;
public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15;
/**
* Create a TetheringManager object for interacting with the tethering service.
+ *
+ * {@hide}
*/
public TetheringManager(@NonNull final Context context, @NonNull final IBinder service) {
mContext = context;
@@ -229,10 +212,9 @@
private final ConditionVariable mWaitForCallback = new ConditionVariable();
@Override
- public void onCallbackStarted(Network network, TetheringConfigurationParcel config,
- TetherStatesParcel states) {
- mTetheringConfiguration = config;
- mTetherStatesParcel = states;
+ public void onCallbackStarted(TetheringCallbackStartedParcel parcel) {
+ mTetheringConfiguration = parcel.config;
+ mTetherStatesParcel = parcel.states;
mWaitForCallback.open();
}
@@ -275,6 +257,8 @@
*
* @param iface the interface name to tether.
* @return error a {@code TETHER_ERROR} value indicating success or failure type
+ *
+ * {@hide}
*/
@Deprecated
public int tether(@NonNull final String iface) {
@@ -296,6 +280,8 @@
*
* @deprecated The only usages is PanService. It uses this for legacy reasons
* and will migrate away as soon as possible.
+ *
+ * {@hide}
*/
@Deprecated
public int untether(@NonNull final String iface) {
@@ -320,6 +306,8 @@
* #startTethering or #stopTethering which encapsulate proper entitlement logic. If the API is
* used and an entitlement check is needed, downstream USB tethering will be enabled but will
* not have any upstream.
+ *
+ * {@hide}
*/
@Deprecated
public int setUsbTethering(final boolean enable) {
@@ -340,7 +328,7 @@
/**
* Starts tethering and runs tether provisioning for the given type if needed. If provisioning
* fails, stopTethering will be called automatically.
- *
+ * @hide
*/
// TODO: improve the usage of ResultReceiver, b/145096122
public void startTethering(final int type, @NonNull final ResultReceiver receiver,
@@ -375,11 +363,63 @@
}
/**
+ * Callback for use with {@link #getLatestTetheringEntitlementResult} to find out whether
+ * entitlement succeeded.
+ */
+ public interface OnTetheringEntitlementResultListener {
+ /**
+ * Called to notify entitlement result.
+ *
+ * @param resultCode an int value of entitlement result. It may be one of
+ * {@link #TETHER_ERROR_NO_ERROR},
+ * {@link #TETHER_ERROR_PROVISION_FAILED}, or
+ * {@link #TETHER_ERROR_ENTITLEMENT_UNKNOWN}.
+ */
+ void onTetheringEntitlementResult(int resultCode);
+ }
+
+ /**
* Request the latest value of the tethering entitlement check.
*
- * Note: Allow privileged apps who have TETHER_PRIVILEGED permission to access. If it turns
- * out some such apps are observed to abuse this API, change to per-UID limits on this API
- * if it's really needed.
+ * <p>This method will only return the latest entitlement result if it is available. If no
+ * cached entitlement result is available, and {@code showEntitlementUi} is false,
+ * {@link #TETHER_ERROR_ENTITLEMENT_UNKNOWN} will be returned. If {@code showEntitlementUi} is
+ * true, entitlement will be run.
+ *
+ * @param type the downstream type of tethering. Must be one of {@code #TETHERING_*} constants.
+ * @param showEntitlementUi a boolean indicating whether to run UI-based entitlement check.
+ * @param executor the executor on which callback will be invoked.
+ * @param listener an {@link OnTetheringEntitlementResultListener} which will be called to
+ * notify the caller of the result of entitlement check. The listener may be called zero
+ * or one time.
+ */
+ @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
+ public void requestLatestTetheringEntitlementResult(int type, boolean showEntitlementUi,
+ @NonNull Executor executor,
+ @NonNull final OnTetheringEntitlementResultListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException(
+ "OnTetheringEntitlementResultListener cannot be null.");
+ }
+
+ ResultReceiver wrappedListener = new ResultReceiver(null /* handler */) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ executor.execute(() -> {
+ listener.onTetheringEntitlementResult(resultCode);
+ });
+ }
+ };
+
+ requestLatestTetheringEntitlementResult(type, wrappedListener,
+ showEntitlementUi);
+ }
+
+ /**
+ * Helper function of #requestLatestTetheringEntitlementResult to remain backwards compatible
+ * with ConnectivityManager#getLatestTetheringEntitlementResult
+ *
+ * {@hide}
*/
// TODO: improve the usage of ResultReceiver, b/145096122
public void requestLatestTetheringEntitlementResult(final int type,
@@ -396,9 +436,126 @@
}
/**
+ * Callback for use with {@link registerTetheringEventCallback} to find out tethering
+ * upstream status.
+ */
+ public abstract static class TetheringEventCallback {
+ /**
+ * Called when tethering supported status changed.
+ *
+ * <p>This will be called immediately after the callback is registered, and may be called
+ * multiple times later upon changes.
+ *
+ * <p>Tethering may be disabled via system properties, device configuration, or device
+ * policy restrictions.
+ *
+ * @param supported The new supported status
+ */
+ public void onTetheringSupported(boolean supported) {}
+
+ /**
+ * Called when tethering upstream changed.
+ *
+ * <p>This will be called immediately after the callback is registered, and may be called
+ * multiple times later upon changes.
+ *
+ * @param network the {@link Network} of tethering upstream. Null means tethering doesn't
+ * have any upstream.
+ */
+ public void onUpstreamChanged(@Nullable Network network) {}
+
+ /**
+ * Called when there was a change in tethering interface regular expressions.
+ *
+ * <p>This will be called immediately after the callback is registered, and may be called
+ * multiple times later upon changes.
+ * @param reg The new regular expressions.
+ * @deprecated Referencing interfaces by regular expressions is a deprecated mechanism.
+ */
+ @Deprecated
+ public void onTetherableInterfaceRegexpsChanged(@NonNull TetheringInterfaceRegexps reg) {}
+
+ /**
+ * Called when there was a change in the list of tetherable interfaces.
+ *
+ * <p>This will be called immediately after the callback is registered, and may be called
+ * multiple times later upon changes.
+ * @param interfaces The list of tetherable interfaces.
+ */
+ public void onTetherableInterfacesChanged(@NonNull List<String> interfaces) {}
+
+ /**
+ * Called when there was a change in the list of tethered interfaces.
+ *
+ * <p>This will be called immediately after the callback is registered, and may be called
+ * multiple times later upon changes.
+ * @param interfaces The list of tethered interfaces.
+ */
+ public void onTetheredInterfacesChanged(@NonNull List<String> interfaces) {}
+
+ /**
+ * Called when an error occurred configuring tethering.
+ *
+ * <p>This will be called immediately after the callback is registered if the latest status
+ * on the interface is an error, and may be called multiple times later upon changes.
+ * @param ifName Name of the interface.
+ * @param error One of {@code TetheringManager#TETHER_ERROR_*}.
+ */
+ public void onError(@NonNull String ifName, int error) {}
+ }
+
+ /**
+ * Regular expressions used to identify tethering interfaces.
+ * @deprecated Referencing interfaces by regular expressions is a deprecated mechanism.
+ */
+ @Deprecated
+ public static class TetheringInterfaceRegexps {
+ private final String[] mTetherableBluetoothRegexs;
+ private final String[] mTetherableUsbRegexs;
+ private final String[] mTetherableWifiRegexs;
+
+ public TetheringInterfaceRegexps(@NonNull String[] tetherableBluetoothRegexs,
+ @NonNull String[] tetherableUsbRegexs, @NonNull String[] tetherableWifiRegexs) {
+ mTetherableBluetoothRegexs = tetherableBluetoothRegexs.clone();
+ mTetherableUsbRegexs = tetherableUsbRegexs.clone();
+ mTetherableWifiRegexs = tetherableWifiRegexs.clone();
+ }
+
+ @NonNull
+ public List<String> getTetherableBluetoothRegexs() {
+ return Collections.unmodifiableList(Arrays.asList(mTetherableBluetoothRegexs));
+ }
+
+ @NonNull
+ public List<String> getTetherableUsbRegexs() {
+ return Collections.unmodifiableList(Arrays.asList(mTetherableUsbRegexs));
+ }
+
+ @NonNull
+ public List<String> getTetherableWifiRegexs() {
+ return Collections.unmodifiableList(Arrays.asList(mTetherableWifiRegexs));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mTetherableBluetoothRegexs, mTetherableUsbRegexs,
+ mTetherableWifiRegexs);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (!(obj instanceof TetheringInterfaceRegexps)) return false;
+ final TetheringInterfaceRegexps other = (TetheringInterfaceRegexps) obj;
+ return Arrays.equals(mTetherableBluetoothRegexs, other.mTetherableBluetoothRegexs)
+ && Arrays.equals(mTetherableUsbRegexs, other.mTetherableUsbRegexs)
+ && Arrays.equals(mTetherableWifiRegexs, other.mTetherableWifiRegexs);
+ }
+ }
+
+ /**
* Start listening to tethering change events. Any new added callback will receive the last
* tethering status right away. If callback is registered,
- * {@link OnTetheringEventCallback#onUpstreamChanged} will immediately be called. If tethering
+ * {@link TetheringEventCallback#onUpstreamChanged} will immediately be called. If tethering
* has no upstream or disabled, the argument of callback will be null. The same callback object
* cannot be registered twice.
*
@@ -406,15 +563,20 @@
* @param callback the callback to be called when tethering has change events.
*/
public void registerTetheringEventCallback(@NonNull Executor executor,
- @NonNull OnTetheringEventCallback callback) {
+ @NonNull TetheringEventCallback callback) {
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "registerTetheringEventCallback caller:" + callerPkg);
synchronized (mTetheringEventCallbacks) {
- if (!mTetheringEventCallbacks.containsKey(callback)) {
+ if (mTetheringEventCallbacks.containsKey(callback)) {
throw new IllegalArgumentException("callback was already registered.");
}
final ITetheringEventCallback remoteCallback = new ITetheringEventCallback.Stub() {
+ // Only accessed with a lock on this object
+ private final HashMap<String, Integer> mErrorStates = new HashMap<>();
+ private String[] mLastTetherableInterfaces = null;
+ private String[] mLastTetheredInterfaces = null;
+
@Override
public void onUpstreamChanged(Network network) throws RemoteException {
executor.execute(() -> {
@@ -422,11 +584,45 @@
});
}
+ private synchronized void sendErrorCallbacks(final TetherStatesParcel newStates) {
+ for (int i = 0; i < newStates.erroredIfaceList.length; i++) {
+ final String iface = newStates.erroredIfaceList[i];
+ final Integer lastError = mErrorStates.get(iface);
+ final int newError = newStates.lastErrorList[i];
+ if (newError != TETHER_ERROR_NO_ERROR
+ && !Objects.equals(lastError, newError)) {
+ callback.onError(iface, newError);
+ }
+ mErrorStates.put(iface, newError);
+ }
+ }
+
+ private synchronized void maybeSendTetherableIfacesChangedCallback(
+ final TetherStatesParcel newStates) {
+ if (Arrays.equals(mLastTetherableInterfaces, newStates.availableList)) return;
+ mLastTetherableInterfaces = newStates.availableList.clone();
+ callback.onTetherableInterfacesChanged(
+ Collections.unmodifiableList(Arrays.asList(mLastTetherableInterfaces)));
+ }
+
+ private synchronized void maybeSendTetheredIfacesChangedCallback(
+ final TetherStatesParcel newStates) {
+ if (Arrays.equals(mLastTetheredInterfaces, newStates.tetheredList)) return;
+ mLastTetheredInterfaces = newStates.tetheredList.clone();
+ callback.onTetheredInterfacesChanged(
+ Collections.unmodifiableList(Arrays.asList(mLastTetheredInterfaces)));
+ }
+
+ // Called immediately after the callbacks are registered.
@Override
- public void onCallbackStarted(Network network, TetheringConfigurationParcel config,
- TetherStatesParcel states) {
+ public void onCallbackStarted(TetheringCallbackStartedParcel parcel) {
executor.execute(() -> {
- callback.onUpstreamChanged(network);
+ callback.onTetheringSupported(parcel.tetheringSupported);
+ callback.onUpstreamChanged(parcel.upstreamNetwork);
+ sendErrorCallbacks(parcel.states);
+ sendRegexpsChanged(parcel.config);
+ maybeSendTetherableIfacesChangedCallback(parcel.states);
+ maybeSendTetheredIfacesChangedCallback(parcel.states);
});
}
@@ -437,11 +633,26 @@
});
}
- @Override
- public void onConfigurationChanged(TetheringConfigurationParcel config) { }
+ private void sendRegexpsChanged(TetheringConfigurationParcel parcel) {
+ callback.onTetherableInterfaceRegexpsChanged(new TetheringInterfaceRegexps(
+ parcel.tetherableBluetoothRegexs,
+ parcel.tetherableUsbRegexs,
+ parcel.tetherableWifiRegexs));
+ }
@Override
- public void onTetherStatesChanged(TetherStatesParcel states) { }
+ public void onConfigurationChanged(TetheringConfigurationParcel config) {
+ executor.execute(() -> sendRegexpsChanged(config));
+ }
+
+ @Override
+ public void onTetherStatesChanged(TetherStatesParcel states) {
+ executor.execute(() -> {
+ sendErrorCallbacks(states);
+ maybeSendTetherableIfacesChangedCallback(states);
+ maybeSendTetheredIfacesChangedCallback(states);
+ });
+ }
};
try {
mConnector.registerTetheringEventCallback(remoteCallback, callerPkg);
@@ -458,7 +669,7 @@
*
* @param callback previously registered callback.
*/
- public void unregisterTetheringEventCallback(@NonNull final OnTetheringEventCallback callback) {
+ public void unregisterTetheringEventCallback(@NonNull final TetheringEventCallback callback) {
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "unregisterTetheringEventCallback caller:" + callerPkg);
@@ -482,6 +693,7 @@
* @param iface The name of the interface of interest
* @return error The error code of the last error tethering or untethering the named
* interface
+ * @hide
*/
public int getLastTetherError(@NonNull final String iface) {
mCallback.waitForStarted();
@@ -503,6 +715,7 @@
*
* @return an array of 0 or more regular expression Strings defining
* what interfaces are considered tetherable usb interfaces.
+ * @hide
*/
public @NonNull String[] getTetherableUsbRegexs() {
mCallback.waitForStarted();
@@ -516,6 +729,7 @@
*
* @return an array of 0 or more regular expression Strings defining
* what interfaces are considered tetherable wifi interfaces.
+ * @hide
*/
public @NonNull String[] getTetherableWifiRegexs() {
mCallback.waitForStarted();
@@ -529,6 +743,7 @@
*
* @return an array of 0 or more regular expression Strings defining
* what interfaces are considered tetherable bluetooth interfaces.
+ * @hide
*/
public @NonNull String[] getTetherableBluetoothRegexs() {
mCallback.waitForStarted();
@@ -540,6 +755,7 @@
* device configuration and current interface existence.
*
* @return an array of 0 or more Strings of tetherable interface names.
+ * @hide
*/
public @NonNull String[] getTetherableIfaces() {
mCallback.waitForStarted();
@@ -552,6 +768,7 @@
* Get the set of tethered interfaces.
*
* @return an array of 0 or more String of currently tethered interface names.
+ * @hide
*/
public @NonNull String[] getTetheredIfaces() {
mCallback.waitForStarted();
@@ -570,6 +787,7 @@
*
* @return an array of 0 or more String indicating the interface names
* which failed to tether.
+ * @hide
*/
public @NonNull String[] getTetheringErroredIfaces() {
mCallback.waitForStarted();
@@ -582,6 +800,7 @@
* Get the set of tethered dhcp ranges.
*
* @deprecated This API just return the default value which is not used in DhcpServer.
+ * @hide
*/
@Deprecated
public @NonNull String[] getTetheredDhcpRanges() {
@@ -595,6 +814,7 @@
* due to device configuration.
*
* @return a boolean - {@code true} indicating Tethering is supported.
+ * @hide
*/
public boolean isTetheringSupported() {
final String callerPkg = mContext.getOpPackageName();
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
index 1cabc8d..e81d6ac 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -16,14 +16,14 @@
package com.android.server.connectivity.tethering;
-import static android.net.TetheringManager.EXTRA_ADD_TETHER_TYPE;
-import static android.net.TetheringManager.EXTRA_PROVISION_CALLBACK;
-import static android.net.TetheringManager.EXTRA_RUN_PROVISION;
+import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE;
+import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK;
+import static android.net.TetheringConstants.EXTRA_RUN_PROVISION;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_INVALID;
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
-import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKONWN;
+import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED;
@@ -577,7 +577,7 @@
private static String errorString(int value) {
switch (value) {
- case TETHER_ERROR_ENTITLEMENT_UNKONWN: return "TETHER_ERROR_ENTITLEMENT_UNKONWN";
+ case TETHER_ERROR_ENTITLEMENT_UNKNOWN: return "TETHER_ERROR_ENTITLEMENT_UNKONWN";
case TETHER_ERROR_NO_ERROR: return "TETHER_ERROR_NO_ERROR";
case TETHER_ERROR_PROVISION_FAILED: return "TETHER_ERROR_PROVISION_FAILED";
default:
@@ -657,7 +657,7 @@
}
final int cacheValue = mEntitlementCacheValue.get(
- downstream, TETHER_ERROR_ENTITLEMENT_UNKONWN);
+ downstream, TETHER_ERROR_ENTITLEMENT_UNKNOWN);
if (cacheValue == TETHER_ERROR_NO_ERROR || !showEntitlementUi) {
receiver.send(cacheValue, null);
} else {
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index 8d1e0c9..5370145 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -74,6 +74,7 @@
import android.net.Network;
import android.net.NetworkInfo;
import android.net.TetherStatesParcel;
+import android.net.TetheringCallbackStartedParcel;
import android.net.TetheringConfigurationParcel;
import android.net.ip.IpServer;
import android.net.shared.NetdUtils;
@@ -951,6 +952,7 @@
mWrapper.showTetheredNotification(
R.drawable.stat_sys_tether_general, false);
mWrapper.untetherAll();
+ // TODO(b/148139325): send tetheringSupported on restriction change
}
}
}
@@ -1844,9 +1846,13 @@
void registerTetheringEventCallback(ITetheringEventCallback callback) {
mHandler.post(() -> {
mTetheringEventCallbacks.register(callback);
+ final TetheringCallbackStartedParcel parcel = new TetheringCallbackStartedParcel();
+ parcel.tetheringSupported = mDeps.isTetheringSupported();
+ parcel.upstreamNetwork = mTetherUpstream;
+ parcel.config = mConfig.toStableParcelable();
+ parcel.states = mTetherStatesParcel;
try {
- callback.onCallbackStarted(mTetherUpstream, mConfig.toStableParcelable(),
- mTetherStatesParcel);
+ callback.onCallbackStarted(parcel);
} catch (RemoteException e) {
// Not really very much to do here.
}
@@ -1881,6 +1887,7 @@
for (int i = 0; i < length; i++) {
try {
mTetheringEventCallbacks.getBroadcastItem(i).onConfigurationChanged(config);
+ // TODO(b/148139325): send tetheringSupported on configuration change
} catch (RemoteException e) {
// Not really very much to do here.
}
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
index 4f07461..3a1d4a6 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -19,7 +19,7 @@
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
-import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKONWN;
+import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
@@ -110,7 +110,7 @@
}
public class WrappedEntitlementManager extends EntitlementManager {
- public int fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKONWN;
+ public int fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKNOWN;
public int uiProvisionCount = 0;
public int silentProvisionCount = 0;
@@ -120,7 +120,7 @@
}
public void reset() {
- fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKONWN;
+ fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKNOWN;
uiProvisionCount = 0;
silentProvisionCount = 0;
}
@@ -274,7 +274,7 @@
receiver = new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
- assertEquals(TETHER_ERROR_ENTITLEMENT_UNKONWN, resultCode);
+ assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode);
mCallbacklatch.countDown();
}
};
@@ -343,7 +343,7 @@
receiver = new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
- assertEquals(TETHER_ERROR_ENTITLEMENT_UNKONWN, resultCode);
+ assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode);
mCallbacklatch.countDown();
}
};
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index d6afa47..9f0d876 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -86,6 +86,7 @@
import android.net.NetworkRequest;
import android.net.RouteInfo;
import android.net.TetherStatesParcel;
+import android.net.TetheringCallbackStartedParcel;
import android.net.TetheringConfigurationParcel;
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
@@ -1113,11 +1114,10 @@
}
@Override
- public void onCallbackStarted(Network network, TetheringConfigurationParcel config,
- TetherStatesParcel states) {
- mActualUpstreams.add(network);
- mTetheringConfigs.add(config);
- mTetherStates.add(states);
+ public void onCallbackStarted(TetheringCallbackStartedParcel parcel) {
+ mActualUpstreams.add(parcel.upstreamNetwork);
+ mTetheringConfigs.add(parcel.config);
+ mTetherStates.add(parcel.states);
}
@Override
diff --git a/packages/services/PacProcessor/jni/Android.bp b/packages/services/PacProcessor/jni/Android.bp
index 61f8143..ab21a76 100644
--- a/packages/services/PacProcessor/jni/Android.bp
+++ b/packages/services/PacProcessor/jni/Android.bp
@@ -39,8 +39,5 @@
],
sanitize: {
cfi: true,
- diag: {
- cfi: true,
- },
},
}
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index ad802ff..54b4201 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -312,5 +312,9 @@
// Notify the user that data or apps are being moved to external storage.
// Package: com.android.systemui
NOTE_STORAGE_MOVE = 0x534d4f56;
+
+ // Notify the user that the admin suspended personal apps on the device.
+ // Package: android
+ NOTE_PERSONAL_APPS_SUSPENDED = 1003;
}
}
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
index 5e6f97e..331eb37 100644
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
@@ -20,7 +20,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.slice.Slice;
import android.content.Context;
import android.os.RemoteException;
import android.service.autofill.Dataset;
@@ -132,7 +131,7 @@
}
};
final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
- createInlineContentProvider(inlinePresentation.getSlice(), inlineSuggestionUi,
+ createInlineContentProvider(inlinePresentation, inlineSuggestionUi,
onClickListener));
return inlineSuggestion;
}
@@ -151,20 +150,22 @@
client.fill(requestId, fieldIndex, dataset);
};
final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
- createInlineContentProvider(inlinePresentation.getSlice(), inlineSuggestionUi,
+ createInlineContentProvider(inlinePresentation, inlineSuggestionUi,
onClickListener));
return inlineSuggestion;
}
private static IInlineContentProvider.Stub createInlineContentProvider(
- @NonNull Slice slice, @NonNull InlineSuggestionUi inlineSuggestionUi,
+ @NonNull InlinePresentation inlinePresentation,
+ @NonNull InlineSuggestionUi inlineSuggestionUi,
@Nullable View.OnClickListener onClickListener) {
return new IInlineContentProvider.Stub() {
@Override
public void provideContent(int width, int height,
IInlineContentCallback callback) {
UiThread.getHandler().post(() -> {
- SurfaceControl sc = inlineSuggestionUi.inflate(slice, width, height,
+ SurfaceControl sc = inlineSuggestionUi.inflate(inlinePresentation, width,
+ height,
onClickListener);
try {
callback.onContent(sc);
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
index 2460732..2adefea 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
@@ -25,10 +25,17 @@
import android.app.slice.Slice;
import android.app.slice.SliceItem;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.graphics.drawable.Icon;
+import android.graphics.fonts.SystemFonts;
import android.os.IBinder;
+import android.service.autofill.InlinePresentation;
+import android.text.TextUtils;
import android.util.Log;
+import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
@@ -41,6 +48,8 @@
import com.android.internal.R;
import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* This is a temporary inline suggestion UI inflater which will be replaced by the ExtServices
@@ -54,6 +63,10 @@
private static final String TAG = "InlineSuggestionUi";
+ // The pattern to match the value can be obtained by calling {@code Resources#getResourceName
+ // (int)}. This name is a single string of the form "package:type/entry".
+ private static final Pattern RESOURCE_NAME_PATTERN = Pattern.compile("([^:]+):([^/]+)/(\\S+)");
+
private final Context mContext;
public InlineSuggestionUi(Context context) {
@@ -65,28 +78,36 @@
*/
@MainThread
@Nullable
- public SurfaceControl inflate(@NonNull Slice slice, int width, int height,
- @Nullable View.OnClickListener onClickListener) {
+ public SurfaceControl inflate(@NonNull InlinePresentation inlinePresentation, int width,
+ int height, @Nullable View.OnClickListener onClickListener) {
Log.d(TAG, "Inflating the inline suggestion UI");
//TODO(b/137800469): Pass in inputToken from IME.
final SurfaceControlViewHost wvr = new SurfaceControlViewHost(mContext,
mContext.getDisplay(), (IBinder) null);
final SurfaceControl sc = wvr.getSurfacePackage().getSurfaceControl();
- final ViewGroup suggestionView = (ViewGroup) renderSlice(slice);
+
+ Context contextThemeWrapper = getContextThemeWrapper(mContext,
+ inlinePresentation.getInlinePresentationSpec().getStyle());
+ if (contextThemeWrapper == null) {
+ contextThemeWrapper = getDefaultContextThemeWrapper(mContext);
+ }
+ final View suggestionView = renderSlice(inlinePresentation.getSlice(),
+ contextThemeWrapper);
if (onClickListener != null) {
suggestionView.setOnClickListener(onClickListener);
}
WindowManager.LayoutParams lp =
new WindowManager.LayoutParams(width, height,
- WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT);
+ WindowManager.LayoutParams.TYPE_APPLICATION, 0,
+ PixelFormat.TRANSPARENT);
wvr.addView(suggestionView, lp);
return sc;
}
- private View renderSlice(Slice slice) {
- final LayoutInflater inflater = LayoutInflater.from(mContext);
+ private static View renderSlice(Slice slice, Context context) {
+ final LayoutInflater inflater = LayoutInflater.from(context);
final ViewGroup suggestionView =
(ViewGroup) inflater.inflate(R.layout.autofill_inline_suggestion, null);
@@ -137,4 +158,96 @@
return suggestionView;
}
+
+ private Context getDefaultContextThemeWrapper(@NonNull Context context) {
+ Resources.Theme theme = context.getResources().newTheme();
+ theme.applyStyle(android.R.style.Theme_AutofillInlineSuggestion, true);
+ return new ContextThemeWrapper(context, theme);
+ }
+
+ /**
+ * Returns a context wrapping the theme in the provided {@code style}, or null if {@code
+ * style} doesn't pass validation.
+ */
+ @Nullable
+ private static Context getContextThemeWrapper(@NonNull Context context,
+ @Nullable String style) {
+ if (style == null) {
+ return null;
+ }
+ Matcher matcher = RESOURCE_NAME_PATTERN.matcher(style);
+ if (!matcher.matches()) {
+ Log.d(TAG, "Can not parse the style=" + style);
+ return null;
+ }
+ String packageName = matcher.group(1);
+ String type = matcher.group(2);
+ String entry = matcher.group(3);
+ if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(type) || TextUtils.isEmpty(entry)) {
+ Log.d(TAG, "Can not proceed with empty field values in the style=" + style);
+ return null;
+ }
+ Resources resources = null;
+ try {
+ resources = context.getPackageManager().getResourcesForApplication(
+ packageName);
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ int resId = resources.getIdentifier(entry, type, packageName);
+ if (resId == Resources.ID_NULL) {
+ return null;
+ }
+ Resources.Theme theme = resources.newTheme();
+ theme.applyStyle(resId, true);
+ if (!validateBaseTheme(theme, resId)) {
+ Log.d(TAG, "Provided theme is not a child of Theme.InlineSuggestion, ignoring it.");
+ return null;
+ }
+ if (!validateFontFamilyForTextViewStyles(theme)) {
+ Log.d(TAG,
+ "Provided theme specifies a font family that is not system font, ignoring it.");
+ return null;
+ }
+ return new ContextThemeWrapper(context, theme);
+ }
+
+ private static boolean validateFontFamilyForTextViewStyles(Resources.Theme theme) {
+ return validateFontFamily(theme, android.R.attr.autofillInlineSuggestionTitle)
+ && validateFontFamily(theme, android.R.attr.autofillInlineSuggestionSubtitle);
+ }
+
+ private static boolean validateFontFamily(Resources.Theme theme, int styleAttr) {
+ TypedArray ta = null;
+ try {
+ ta = theme.obtainStyledAttributes(null, new int[]{android.R.attr.fontFamily},
+ styleAttr,
+ 0);
+ if (ta.getIndexCount() == 0) {
+ return true;
+ }
+ String fontFamily = ta.getString(ta.getIndex(0));
+ return SystemFonts.getRawSystemFallbackMap().containsKey(fontFamily);
+ } finally {
+ if (ta != null) {
+ ta.recycle();
+ }
+ }
+ }
+
+ private static boolean validateBaseTheme(Resources.Theme theme, int styleAttr) {
+ TypedArray ta = null;
+ try {
+ ta = theme.obtainStyledAttributes(null,
+ new int[]{android.R.attr.isAutofillInlineSuggestionTheme}, styleAttr, 0);
+ if (ta.getIndexCount() == 0) {
+ return false;
+ }
+ return ta.getBoolean(ta.getIndex(0), false);
+ } finally {
+ if (ta != null) {
+ ta.recycle();
+ }
+ }
+ }
}
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index 5e10916..0bcf45d 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -47,6 +47,7 @@
import android.os.SELinux;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -399,6 +400,12 @@
* the transport have no data.
*/
private void informTransportOfUnchangedApps(Set<String> appsBackedUp) {
+ // If the feautre is not enabled then we just exit early.
+ if (!FeatureFlagUtils.isEnabled(mBackupManagerService.getContext(),
+ FeatureFlagUtils.BACKUP_NO_KV_DATA_CHANGE_CALLS)) {
+ return;
+ }
+
String[] succeedingPackages = getSucceedingPackages();
if (succeedingPackages == null) {
// Nothing is succeeding, so end early.
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index e976811..434a97e 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -361,13 +361,18 @@
}
@Override
- public boolean isDeviceAssociated(String packageName, String macAddress, int userId) {
+ public boolean isDeviceAssociatedForWifiConnection(String packageName, String macAddress,
+ int userId) {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.MANAGE_COMPANION_DEVICES, "isDeviceAssociated");
+ boolean bypassMacPermission = getContext().getPackageManager().checkPermission(
+ android.Manifest.permission.COMPANION_APPROVE_WIFI_CONNECTIONS, packageName)
+ == PackageManager.PERMISSION_GRANTED;
+
return CollectionUtils.any(
readAllAssociations(userId, packageName),
- a -> Objects.equals(a.deviceAddress, macAddress));
+ a -> bypassMacPermission || Objects.equals(a.deviceAddress, macAddress));
}
private void checkCanCallNotificationApi(String callingPackage) throws RemoteException {
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
index 9cdb58d..b54ec4e 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
@@ -27,6 +27,7 @@
import android.app.contentsuggestions.ISelectionsCallback;
import android.app.contentsuggestions.SelectionsRequest;
import android.content.Context;
+import android.graphics.Bitmap;
import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
@@ -61,6 +62,10 @@
private static final boolean VERBOSE = false; // TODO: make dynamic
private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+ /**
+ * Key into the extras Bundle passed to {@link #provideContextImage(int, Bundle)}.
+ */
+ private static final String EXTRA_BITMAP = "android.contentsuggestions.extra.BITMAP";
private ActivityTaskManagerInternal mActivityTaskManagerInternal;
@@ -111,6 +116,33 @@
private class ContentSuggestionsManagerStub extends IContentSuggestionsManager.Stub {
@Override
+ public void provideContextBitmap(
+ int userId,
+ @NonNull Bitmap bitmap,
+ @NonNull Bundle imageContextRequestExtras) {
+ if (bitmap == null) {
+ throw new IllegalArgumentException("Expected non-null bitmap");
+ }
+ if (imageContextRequestExtras == null) {
+ throw new IllegalArgumentException("Expected non-null imageContextRequestExtras");
+ }
+ enforceCaller(UserHandle.getCallingUserId(), "provideContextBitmap");
+
+ synchronized (mLock) {
+ final ContentSuggestionsPerUserService service = getServiceForUserLocked(userId);
+ if (service != null) {
+ // TODO(b/147324195): Temporarily pass bitmap until we change the service API.
+ imageContextRequestExtras.putParcelable(EXTRA_BITMAP, bitmap);
+ service.provideContextImageLocked(/* taskId = */ -1, imageContextRequestExtras);
+ } else {
+ if (VERBOSE) {
+ Slog.v(TAG, "provideContextImageLocked: no service for " + userId);
+ }
+ }
+ }
+ }
+
+ @Override
public void provideContextImage(
int userId,
int taskId,
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 02d4f94..a603fa9 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -83,6 +83,7 @@
":storaged_aidl",
":vold_aidl",
":platform-compat-config",
+ ":display-device-config",
"java/com/android/server/EventLogTags.logtags",
"java/com/android/server/am/EventLogTags.logtags",
"java/com/android/server/wm/EventLogTags.logtags",
@@ -112,6 +113,7 @@
"android.hardware.broadcastradio-V2.0-java",
"android.hardware.health-V1.0-java",
"android.hardware.health-V2.0-java",
+ "android.hardware.light-java",
"android.hardware.weaver-V1.0-java",
"android.hardware.biometrics.face-V1.0-java",
"android.hardware.biometrics.fingerprint-V2.1-java",
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 368416b..994c314 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -33,6 +33,7 @@
import android.content.pm.parsing.ComponentParseUtils;
import android.os.Bundle;
import android.os.PersistableBundle;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -169,7 +170,7 @@
* Return a List of all application packages that are installed on the
* device, for a specific user. If flag GET_UNINSTALLED_PACKAGES has been
* set, a list of all applications including those deleted with
- * {@code DONT_DELETE_DATA} (partially installed apps with data directory)
+ * {@code DELETE_KEEP_DATA} (partially installed apps with data directory)
* will be returned.
*
* @param flags Additional option flags to modify the data returned.
@@ -183,7 +184,7 @@
* information is retrieved from the list of uninstalled
* applications (which includes installed applications as well as
* applications with data directory i.e. applications which had been
- * deleted with {@code DONT_DELETE_DATA} flag set).
+ * deleted with {@code DELETE_KEEP_DATA} flag set).
*/
public abstract List<ApplicationInfo> getInstalledApplications(
@ApplicationInfoFlags int flags, @UserIdInt int userId, int callingUid);
@@ -689,6 +690,27 @@
int userId);
/**
+ * Return the processes that have been declared for a uid.
+ *
+ * @param uid The uid to query.
+ *
+ * @return Returns null if there are no declared processes for the uid; otherwise,
+ * returns the set of processes it declared.
+ */
+ public abstract ArrayMap<String, ProcessInfo> getProcessesForUid(int uid);
+
+ /**
+ * Return the gids associated with a particular permission.
+ *
+ * @param permissionName The name of the permission to query.
+ * @param userId The user id the gids will be associated with.
+ *
+ * @return Returns null if there are no gids associated with the permission, otherwise an
+ * array if the gid ints.
+ */
+ public abstract int[] getPermissionGids(String permissionName, int userId);
+
+ /**
* Return if device is currently in a "core" boot environment, typically
* used to support full-disk encryption. Only apps marked with
* {@code coreApp} attribute are available.
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index a33fcd5..8074900 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -66,8 +66,8 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.DumpUtils;
import com.android.server.am.BatteryStatsService;
-import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
+import com.android.server.lights.LogicalLight;
import java.io.File;
import java.io.FileDescriptor;
@@ -1065,7 +1065,7 @@
}
private final class Led {
- private final Light mBatteryLight;
+ private final LogicalLight mBatteryLight;
private final int mBatteryLowARGB;
private final int mBatteryMediumARGB;
@@ -1100,7 +1100,7 @@
mBatteryLight.setColor(mBatteryLowARGB);
} else {
// Flash red when battery is low and not charging
- mBatteryLight.setFlashing(mBatteryLowARGB, Light.LIGHT_FLASH_TIMED,
+ mBatteryLight.setFlashing(mBatteryLowARGB, LogicalLight.LIGHT_FLASH_TIMED,
mBatteryLedOn, mBatteryLedOff);
}
} else if (status == BatteryManager.BATTERY_STATUS_CHARGING
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index e9db9c8..de0b6fc 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -18,6 +18,8 @@
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.location.LocationManager.FUSED_PROVIDER;
import static android.location.LocationManager.GPS_PROVIDER;
@@ -35,14 +37,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.content.pm.Signature;
-import android.content.res.Resources;
-import android.hardware.location.ActivityRecognitionHardware;
import android.location.Address;
import android.location.Criteria;
import android.location.GeocoderParams;
@@ -52,6 +47,7 @@
import android.location.IGnssMeasurementsListener;
import android.location.IGnssNavigationMessageListener;
import android.location.IGnssStatusListener;
+import android.location.IGpsGeofenceHardware;
import android.location.ILocationListener;
import android.location.ILocationManager;
import android.location.Location;
@@ -91,11 +87,11 @@
import com.android.internal.util.Preconditions;
import com.android.server.location.AbstractLocationProvider;
import com.android.server.location.AbstractLocationProvider.State;
-import com.android.server.location.ActivityRecognitionProxy;
import com.android.server.location.CallerIdentity;
import com.android.server.location.GeocoderProxy;
import com.android.server.location.GeofenceManager;
import com.android.server.location.GeofenceProxy;
+import com.android.server.location.HardwareActivityRecognitionProxy;
import com.android.server.location.LocationFudger;
import com.android.server.location.LocationProviderProxy;
import com.android.server.location.LocationRequestStatistics;
@@ -114,7 +110,6 @@
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -123,6 +118,7 @@
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
+import java.util.TreeMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
@@ -543,77 +539,6 @@
}
@GuardedBy("mLock")
- private void ensureFallbackFusedProviderPresentLocked(String[] pkgs) {
- PackageManager pm = mContext.getPackageManager();
- String systemPackageName = mContext.getPackageName();
- ArrayList<HashSet<Signature>> sigSets = ServiceWatcher.getSignatureSets(mContext, pkgs);
-
- List<ResolveInfo> rInfos = pm.queryIntentServicesAsUser(
- new Intent(FUSED_LOCATION_SERVICE_ACTION),
- PackageManager.GET_META_DATA, mUserInfoStore.getCurrentUserId());
- for (ResolveInfo rInfo : rInfos) {
- String packageName = rInfo.serviceInfo.packageName;
-
- // Check that the signature is in the list of supported sigs. If it's not in
- // this list the standard provider binding logic won't bind to it.
- try {
- PackageInfo pInfo;
- pInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
- if (!ServiceWatcher.isSignatureMatch(pInfo.signatures, sigSets)) {
- Log.w(TAG, packageName + " resolves service " + FUSED_LOCATION_SERVICE_ACTION +
- ", but has wrong signature, ignoring");
- continue;
- }
- } catch (NameNotFoundException e) {
- Log.e(TAG, "missing package: " + packageName);
- continue;
- }
-
- // Get the version info
- if (rInfo.serviceInfo.metaData == null) {
- Log.w(TAG, "Found fused provider without metadata: " + packageName);
- continue;
- }
-
- int version = rInfo.serviceInfo.metaData.getInt(
- ServiceWatcher.EXTRA_SERVICE_VERSION, -1);
- if (version == 0) {
- // This should be the fallback fused location provider.
-
- // Make sure it's in the system partition.
- if ((rInfo.serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- if (D) Log.d(TAG, "Fallback candidate not in /system: " + packageName);
- continue;
- }
-
- // Check that the fallback is signed the same as the OS
- // as a proxy for coreApp="true"
- if (pm.checkSignatures(systemPackageName, packageName)
- != PackageManager.SIGNATURE_MATCH) {
- if (D) {
- Log.d(TAG, "Fallback candidate not signed the same as system: "
- + packageName);
- }
- continue;
- }
-
- // Found a valid fallback.
- if (D) Log.d(TAG, "Found fallback provider: " + packageName);
- return;
- } else {
- if (D) Log.d(TAG, "Fallback candidate not version 0: " + packageName);
- }
- }
-
- throw new IllegalStateException("Unable to find a fused location provider that is in the "
- + "system partition with version 0 and signed with the platform certificate. "
- + "Such a package is needed to provide a default fused location provider in the "
- + "event that no other fused location provider has been installed or is currently "
- + "available. For example, coreOnly boot mode when decrypting the data "
- + "partition. The fallback must also be marked coreApp=\"true\" in the manifest");
- }
-
- @GuardedBy("mLock")
private void initializeProvidersLocked() {
if (GnssManagerService.isGnssSupported()) {
mGnssManagerService = new GnssManagerService(this, mContext, mLocationUsageLogger);
@@ -622,33 +547,11 @@
gnssManager.setRealProvider(mGnssManagerService.getGnssLocationProvider());
}
- /*
- Load package name(s) containing location provider support.
- These packages can contain services implementing location providers:
- Geocoder Provider, Network Location Provider, and
- Fused Location Provider. They will each be searched for
- service components implementing these providers.
- The location framework also has support for installation
- of new location providers at run-time. The new package does not
- have to be explicitly listed here, however it must have a signature
- that matches the signature of at least one package on this list.
- */
- Resources resources = mContext.getResources();
- String[] pkgs = resources.getStringArray(
- com.android.internal.R.array.config_locationProviderPackageNames);
- if (D) {
- Log.d(TAG, "certificates for location providers pulled from: " +
- Arrays.toString(pkgs));
- }
-
- ensureFallbackFusedProviderPresentLocked(pkgs);
-
- LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
+ LocationProviderProxy networkProvider = LocationProviderProxy.createAndRegister(
mContext,
NETWORK_LOCATION_SERVICE_ACTION,
com.android.internal.R.bool.config_enableNetworkLocationOverlay,
- com.android.internal.R.string.config_networkLocationProviderPackageName,
- com.android.internal.R.array.config_locationProviderPackageNames);
+ com.android.internal.R.string.config_networkLocationProviderPackageName);
if (networkProvider != null) {
LocationProviderManager networkManager = new LocationProviderManager(NETWORK_PROVIDER);
mProviderManagers.add(networkManager);
@@ -657,13 +560,18 @@
Slog.w(TAG, "no network location provider found");
}
+ // ensure that a fused provider exists which will work in direct boot
+ Preconditions.checkState(!mContext.getPackageManager().queryIntentServicesAsUser(
+ new Intent(FUSED_LOCATION_SERVICE_ACTION),
+ MATCH_DIRECT_BOOT_AWARE | MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM).isEmpty(),
+ "Unable to find a direct boot aware fused location provider");
+
// bind to fused provider
- LocationProviderProxy fusedProvider = LocationProviderProxy.createAndBind(
+ LocationProviderProxy fusedProvider = LocationProviderProxy.createAndRegister(
mContext,
FUSED_LOCATION_SERVICE_ACTION,
com.android.internal.R.bool.config_enableFusedLocationOverlay,
- com.android.internal.R.string.config_fusedLocationProviderPackageName,
- com.android.internal.R.array.config_locationProviderPackageNames);
+ com.android.internal.R.string.config_fusedLocationProviderPackageName);
if (fusedProvider != null) {
LocationProviderManager fusedManager = new LocationProviderManager(FUSED_PROVIDER);
mProviderManagers.add(fusedManager);
@@ -674,47 +582,30 @@
}
// bind to geocoder provider
- mGeocodeProvider = GeocoderProxy.createAndBind(mContext,
- com.android.internal.R.bool.config_enableGeocoderOverlay,
- com.android.internal.R.string.config_geocoderProviderPackageName,
- com.android.internal.R.array.config_locationProviderPackageNames);
+ mGeocodeProvider = GeocoderProxy.createAndRegister(mContext);
if (mGeocodeProvider == null) {
Slog.e(TAG, "no geocoder provider found");
}
+ // bind to geofence proxy
if (mGnssManagerService != null) {
- // bind to geofence provider
- GeofenceProxy provider = GeofenceProxy.createAndBind(
- mContext, com.android.internal.R.bool.config_enableGeofenceOverlay,
- com.android.internal.R.string.config_geofenceProviderPackageName,
- com.android.internal.R.array.config_locationProviderPackageNames,
- mGnssManagerService.getGpsGeofenceProxy(),
- null);
- if (provider == null) {
- Slog.d(TAG, "Unable to bind FLP Geofence proxy.");
+ IGpsGeofenceHardware gpsGeofenceHardware = mGnssManagerService.getGpsGeofenceProxy();
+ if (gpsGeofenceHardware != null) {
+ GeofenceProxy provider = GeofenceProxy.createAndBind(mContext, gpsGeofenceHardware);
+ if (provider == null) {
+ Slog.d(TAG, "unable to bind to GeofenceProxy");
+ }
}
}
// bind to hardware activity recognition
- boolean activityRecognitionHardwareIsSupported = ActivityRecognitionHardware.isSupported();
- ActivityRecognitionHardware activityRecognitionHardware = null;
- if (activityRecognitionHardwareIsSupported) {
- activityRecognitionHardware = ActivityRecognitionHardware.getInstance(mContext);
- } else {
- Slog.d(TAG, "Hardware Activity-Recognition not supported.");
- }
- ActivityRecognitionProxy proxy = ActivityRecognitionProxy.createAndBind(
- mContext,
- activityRecognitionHardwareIsSupported,
- activityRecognitionHardware,
- com.android.internal.R.bool.config_enableActivityRecognitionHardwareOverlay,
- com.android.internal.R.string.config_activityRecognitionHardwarePackageName,
- com.android.internal.R.array.config_locationProviderPackageNames);
- if (proxy == null) {
- Slog.d(TAG, "Unable to bind ActivityRecognitionProxy.");
+ HardwareActivityRecognitionProxy hardwareActivityRecognitionProxy =
+ HardwareActivityRecognitionProxy.createAndRegister(mContext);
+ if (hardwareActivityRecognitionProxy == null) {
+ Log.e(TAG, "unable to bind ActivityRecognitionProxy");
}
- String[] testProviderStrings = resources.getStringArray(
+ String[] testProviderStrings = mContext.getResources().getStringArray(
com.android.internal.R.array.config_testLocationProviders);
for (String testProviderString : testProviderStrings) {
String[] fragments = testProviderString.split(",");
@@ -2996,10 +2887,12 @@
ipw.println("Historical Records by Provider:");
ipw.increaseIndent();
+ TreeMap<PackageProviderKey, PackageStatistics> sorted = new TreeMap<>();
+ sorted.putAll(mRequestStatistics.statistics);
for (Map.Entry<PackageProviderKey, PackageStatistics> entry
- : mRequestStatistics.statistics.entrySet()) {
+ : sorted.entrySet()) {
PackageProviderKey key = entry.getKey();
- ipw.println(key.packageName + ": " + key.providerName + ": " + entry.getValue());
+ ipw.println(key.providerName + ": " + key.packageName + ": " + entry.getValue());
}
ipw.decreaseIndent();
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index e8e3b39d..131a22b 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -18,6 +18,7 @@
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
@@ -25,14 +26,18 @@
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.os.Build;
+import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Process;
import android.os.RecoverySystem;
+import android.os.RemoteCallback;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.provider.DeviceConfig;
import android.provider.Settings;
+import android.util.ArraySet;
import android.util.ExceptionUtils;
import android.util.Log;
import android.util.MathUtils;
@@ -46,10 +51,16 @@
import com.android.server.PackageWatchdog.PackageHealthObserver;
import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
import com.android.server.am.SettingsToPropertiesMapper;
-import com.android.server.utils.FlagNamespaceUtils;
import java.io.File;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
/**
* Utilities to help rescue the system from crash loops. Callers are expected to
@@ -64,8 +75,6 @@
@VisibleForTesting
static final String PROP_ENABLE_RESCUE = "persist.sys.enable_rescue";
@VisibleForTesting
- static final int TRIGGER_COUNT = 5;
- @VisibleForTesting
static final String PROP_RESCUE_LEVEL = "sys.rescue_level";
@VisibleForTesting
static final int LEVEL_NONE = 0;
@@ -81,6 +90,8 @@
static final String PROP_RESCUE_BOOT_COUNT = "sys.rescue_boot_count";
@VisibleForTesting
static final String TAG = "RescueParty";
+ @VisibleForTesting
+ static final long DEFAULT_OBSERVING_DURATION_MS = TimeUnit.DAYS.toMillis(2);
private static final String NAME = "rescue-party-observer";
@@ -139,7 +150,11 @@
*/
public static void onSettingsProviderPublished(Context context) {
handleNativeRescuePartyResets();
- executeRescueLevel(context);
+ executeRescueLevel(context, /*failedPackage=*/ null);
+ ContentResolver contentResolver = context.getContentResolver();
+ Settings.Config.registerMonitorCallback(contentResolver, new RemoteCallback(result -> {
+ handleMonitorCallback(context, result);
+ }));
}
@VisibleForTesting
@@ -147,10 +162,53 @@
return SystemClock.elapsedRealtime();
}
+ private static void handleMonitorCallback(Context context, Bundle result) {
+ String callbackType = result.getString(Settings.EXTRA_MONITOR_CALLBACK_TYPE, "");
+ switch (callbackType) {
+ case Settings.EXTRA_NAMESPACE_UPDATED_CALLBACK:
+ String updatedNamespace = result.getString(Settings.EXTRA_NAMESPACE);
+ if (updatedNamespace != null) {
+ startObservingPackages(context, updatedNamespace);
+ }
+ break;
+ case Settings.EXTRA_ACCESS_CALLBACK:
+ String callingPackage = result.getString(Settings.EXTRA_CALLING_PACKAGE, null);
+ String namespace = result.getString(Settings.EXTRA_NAMESPACE, null);
+ if (namespace != null && callingPackage != null) {
+ RescuePartyObserver.getInstance(context).recordDeviceConfigAccess(
+ callingPackage,
+ namespace);
+ }
+ break;
+ default:
+ Slog.w(TAG, "Unrecognized DeviceConfig callback");
+ break;
+ }
+ }
+
+ private static void startObservingPackages(Context context, @NonNull String updatedNamespace) {
+ RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context);
+ Set<String> callingPackages = rescuePartyObserver.getCallingPackagesSet(updatedNamespace);
+ if (callingPackages == null) {
+ return;
+ }
+ List<String> callingPackageList = new ArrayList<>();
+ callingPackageList.addAll(callingPackages);
+ Slog.i(TAG, "Starting to observe: " + callingPackageList + ", updated namespace: "
+ + updatedNamespace);
+ PackageWatchdog.getInstance(context).startObservingHealth(
+ rescuePartyObserver,
+ callingPackageList,
+ DEFAULT_OBSERVING_DURATION_MS);
+ }
+
private static void handleNativeRescuePartyResets() {
if (SettingsToPropertiesMapper.isNativeFlagsResetPerformed()) {
- FlagNamespaceUtils.resetDeviceConfig(Settings.RESET_MODE_TRUSTED_DEFAULTS,
- Arrays.asList(SettingsToPropertiesMapper.getResetNativeCategories()));
+ String[] resetNativeCategories = SettingsToPropertiesMapper.getResetNativeCategories();
+ for (int i = 0; i < resetNativeCategories.length; i++) {
+ DeviceConfig.resetToDefaults(Settings.RESET_MODE_TRUSTED_DEFAULTS,
+ resetNativeCategories[i]);
+ }
}
}
@@ -164,7 +222,7 @@
/**
* Escalate to the next rescue level. After incrementing the level you'll
- * probably want to call {@link #executeRescueLevel(Context)}.
+ * probably want to call {@link #executeRescueLevel(Context, String)}.
*/
private static void incrementRescueLevel(int triggerUid) {
final int level = MathUtils.constrain(
@@ -177,13 +235,13 @@
+ levelToString(level) + " triggered by UID " + triggerUid);
}
- private static void executeRescueLevel(Context context) {
+ private static void executeRescueLevel(Context context, @Nullable String failedPackage) {
final int level = SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE);
if (level == LEVEL_NONE) return;
Slog.w(TAG, "Attempting rescue level " + levelToString(level));
try {
- executeRescueLevelInternal(context, level);
+ executeRescueLevelInternal(context, level, failedPackage);
EventLogTags.writeRescueSuccess(level);
logCriticalInfo(Log.DEBUG,
"Finished rescue level " + levelToString(level));
@@ -195,24 +253,23 @@
}
}
- private static void executeRescueLevelInternal(Context context, int level) throws Exception {
+ private static void executeRescueLevelInternal(Context context, int level, @Nullable
+ String failedPackage) throws Exception {
StatsLog.write(StatsLog.RESCUE_PARTY_RESET_REPORTED, level);
switch (level) {
case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
- resetAllSettings(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
+ resetAllSettings(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS, failedPackage);
break;
case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
- resetAllSettings(context, Settings.RESET_MODE_UNTRUSTED_CHANGES);
+ resetAllSettings(context, Settings.RESET_MODE_UNTRUSTED_CHANGES, failedPackage);
break;
case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
- resetAllSettings(context, Settings.RESET_MODE_TRUSTED_DEFAULTS);
+ resetAllSettings(context, Settings.RESET_MODE_TRUSTED_DEFAULTS, failedPackage);
break;
case LEVEL_FACTORY_RESET:
RecoverySystem.rebootPromptAndWipeUserData(context, TAG);
break;
}
- FlagNamespaceUtils.addToKnownResetNamespaces(
- FlagNamespaceUtils.NAMESPACE_NO_PACKAGE);
}
private static int mapRescueLevelToUserImpact(int rescueLevel) {
@@ -237,13 +294,14 @@
}
}
- private static void resetAllSettings(Context context, int mode) throws Exception {
+ private static void resetAllSettings(Context context, int mode, @Nullable String failedPackage)
+ throws Exception {
// Try our best to reset all settings possible, and once finished
// rethrow any exception that we encountered
Exception res = null;
final ContentResolver resolver = context.getContentResolver();
try {
- FlagNamespaceUtils.resetDeviceConfig(mode);
+ resetDeviceConfig(context, mode, failedPackage);
} catch (Exception e) {
res = new RuntimeException("Failed to reset config settings", e);
}
@@ -264,6 +322,41 @@
}
}
+ private static void resetDeviceConfig(Context context, int resetMode,
+ @Nullable String failedPackage) {
+ if (!shouldPerformScopedResets() || failedPackage == null) {
+ DeviceConfig.resetToDefaults(resetMode, /*namespace=*/ null);
+ } else {
+ performScopedReset(context, resetMode, failedPackage);
+ }
+ }
+
+ private static boolean shouldPerformScopedResets() {
+ int rescueLevel = MathUtils.constrain(
+ SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE),
+ LEVEL_NONE, LEVEL_FACTORY_RESET);
+ return rescueLevel <= LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES;
+ }
+
+ private static void performScopedReset(Context context, int resetMode,
+ @NonNull String failedPackage) {
+ RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context);
+ Set<String> affectedNamespaces = rescuePartyObserver.getAffectedNamespaceSet(
+ failedPackage);
+ if (affectedNamespaces == null) {
+ DeviceConfig.resetToDefaults(resetMode, /*namespace=*/ null);
+ } else {
+ Slog.w(TAG,
+ "Performing scoped reset for package: " + failedPackage
+ + ", affected namespaces: "
+ + Arrays.toString(affectedNamespaces.toArray()));
+ Iterator<String> it = affectedNamespaces.iterator();
+ while (it.hasNext()) {
+ DeviceConfig.resetToDefaults(resetMode, it.next());
+ }
+ }
+ }
+
/**
* Handle mitigation action for package failures. This observer will be register to Package
* Watchdog and will receive calls about package failures. This observer is persistent so it
@@ -271,7 +364,9 @@
*/
public static class RescuePartyObserver implements PackageHealthObserver {
- private Context mContext;
+ private final Context mContext;
+ private final Map<String, Set<String>> mCallingPackageNamespaceSetMap = new HashMap<>();
+ private final Map<String, Set<String>> mNamespaceCallingPackageSetMap = new HashMap<>();
@GuardedBy("RescuePartyObserver.class")
static RescuePartyObserver sRescuePartyObserver;
@@ -290,6 +385,13 @@
}
}
+ @VisibleForTesting
+ static void reset() {
+ synchronized (RescuePartyObserver.class) {
+ sRescuePartyObserver = null;
+ }
+ }
+
@Override
public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage,
@FailureReasons int failureReason) {
@@ -314,7 +416,8 @@
|| failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {
int triggerUid = getPackageUid(mContext, failedPackage.getPackageName());
incrementRescueLevel(triggerUid);
- executeRescueLevel(mContext);
+ executeRescueLevel(mContext,
+ failedPackage == null ? null : failedPackage.getPackageName());
return true;
} else {
return false;
@@ -355,7 +458,7 @@
return false;
}
incrementRescueLevel(Process.ROOT_UID);
- executeRescueLevel(mContext);
+ executeRescueLevel(mContext, /*failedPackage=*/ null);
return true;
}
@@ -363,6 +466,32 @@
public String getName() {
return NAME;
}
+
+ private synchronized void recordDeviceConfigAccess(@NonNull String callingPackage,
+ @NonNull String namespace) {
+ // Record it in calling packages to namespace map
+ Set<String> namespaceSet = mCallingPackageNamespaceSetMap.get(callingPackage);
+ if (namespaceSet == null) {
+ namespaceSet = new ArraySet<>();
+ mCallingPackageNamespaceSetMap.put(callingPackage, namespaceSet);
+ }
+ namespaceSet.add(namespace);
+ // Record it in namespace to calling packages map
+ Set<String> callingPackageSet = mNamespaceCallingPackageSetMap.get(namespace);
+ if (callingPackageSet == null) {
+ callingPackageSet = new ArraySet<>();
+ }
+ callingPackageSet.add(callingPackage);
+ mNamespaceCallingPackageSetMap.put(namespace, callingPackageSet);
+ }
+
+ private synchronized Set<String> getAffectedNamespaceSet(String failedPackage) {
+ return mCallingPackageNamespaceSetMap.get(failedPackage);
+ }
+
+ private synchronized Set<String> getCallingPackagesSet(String namespace) {
+ return mNamespaceCallingPackageSetMap.get(namespace);
+ }
}
private static int[] getAllUserIds() {
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index 7f51aa9..8564cb4 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -16,7 +16,19 @@
package com.android.server;
+import static android.content.Context.BIND_AUTO_CREATE;
+import static android.content.Context.BIND_NOT_FOREGROUND;
+import static android.content.Context.BIND_NOT_VISIBLE;
+import static android.content.pm.PackageManager.GET_META_DATA;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+
+import android.annotation.BoolRes;
import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -24,11 +36,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
-import android.content.pm.Signature;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
@@ -37,15 +45,10 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
-import android.util.Slog;
import com.android.internal.content.PackageMonitor;
import com.android.internal.util.Preconditions;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Callable;
@@ -55,16 +58,16 @@
import java.util.concurrent.TimeoutException;
/**
- * Find the best Service, and bind to it.
- * Handles run-time package changes.
+ * Maintains a binding to the best service that matches the given intent information. Bind and
+ * unbind callbacks, as well as all binder operations, will all be run on the given handler.
*/
public class ServiceWatcher implements ServiceConnection {
private static final String TAG = "ServiceWatcher";
- private static final boolean D = false;
+ private static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
- public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
- public static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser";
+ private static final String EXTRA_SERVICE_VERSION = "serviceVersion";
+ private static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser";
private static final long BLOCKING_BINDER_TIMEOUT_MS = 30 * 1000;
@@ -83,280 +86,300 @@
T run(IBinder binder) throws RemoteException;
}
- public static ArrayList<HashSet<Signature>> getSignatureSets(Context context,
- String... packageNames) {
- PackageManager pm = context.getPackageManager();
+ /**
+ * Information on the service ServiceWatcher has selected as the best option for binding.
+ */
+ public static final class ServiceInfo implements Comparable<ServiceInfo> {
- ArrayList<HashSet<Signature>> signatureSets = new ArrayList<>(packageNames.length);
- for (String packageName : packageNames) {
- try {
- Signature[] signatures = pm.getPackageInfo(packageName,
- PackageManager.MATCH_SYSTEM_ONLY
- | PackageManager.GET_SIGNATURES).signatures;
+ public static final ServiceInfo NONE = new ServiceInfo(Integer.MIN_VALUE, null,
+ UserHandle.USER_NULL);
- HashSet<Signature> set = new HashSet<>();
- Collections.addAll(set, signatures);
- signatureSets.add(set);
- } catch (NameNotFoundException e) {
- Log.w(TAG, packageName + " not found");
+ public final int version;
+ @Nullable public final ComponentName component;
+ @UserIdInt public final int userId;
+
+ private ServiceInfo(ResolveInfo resolveInfo, int currentUserId) {
+ Preconditions.checkArgument(resolveInfo.serviceInfo.getComponentName() != null);
+
+ Bundle metadata = resolveInfo.serviceInfo.metaData;
+ boolean isMultiuser;
+ if (metadata != null) {
+ version = metadata.getInt(EXTRA_SERVICE_VERSION, Integer.MIN_VALUE);
+ isMultiuser = metadata.getBoolean(EXTRA_SERVICE_IS_MULTIUSER, false);
+ } else {
+ version = Integer.MIN_VALUE;
+ isMultiuser = false;
}
+
+ component = resolveInfo.serviceInfo.getComponentName();
+ userId = isMultiuser ? UserHandle.USER_SYSTEM : currentUserId;
}
- return signatureSets;
- }
- /** Checks if signatures match. */
- public static boolean isSignatureMatch(Signature[] signatures,
- List<HashSet<Signature>> sigSets) {
- if (signatures == null) return false;
+ private ServiceInfo(int version, @Nullable ComponentName component, int userId) {
+ Preconditions.checkArgument(component != null || version == Integer.MIN_VALUE);
+ this.version = version;
+ this.component = component;
+ this.userId = userId;
+ }
- // build hashset of input to test against
- HashSet<Signature> inputSet = new HashSet<>();
- Collections.addAll(inputSet, signatures);
-
- // test input against each of the signature sets
- for (HashSet<Signature> referenceSet : sigSets) {
- if (referenceSet.equals(inputSet)) {
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
return true;
}
+ if (!(o instanceof ServiceInfo)) {
+ return false;
+ }
+ ServiceInfo that = (ServiceInfo) o;
+ return version == that.version && userId == that.userId
+ && Objects.equals(component, that.component);
}
- return false;
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(version, component, userId);
+ }
+
+ @Override
+ public int compareTo(ServiceInfo that) {
+ // ServiceInfos with higher version numbers always win (having a version number >
+ // MIN_VALUE implies having a non-null component). if version numbers are equal, a
+ // non-null component wins over a null component. if the version numbers are equal and
+ // both components exist then we prefer components that work for all users vs components
+ // that only work for a single user at a time. otherwise everything's equal.
+ int ret = Integer.compare(version, that.version);
+ if (ret == 0) {
+ if (component == null && that.component != null) {
+ ret = -1;
+ } else if (component != null && that.component == null) {
+ ret = 1;
+ } else {
+ if (userId != UserHandle.USER_SYSTEM && that.userId == UserHandle.USER_SYSTEM) {
+ ret = -1;
+ } else if (userId == UserHandle.USER_SYSTEM
+ && that.userId != UserHandle.USER_SYSTEM) {
+ ret = 1;
+ }
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ public String toString() {
+ return component + "@" + version + "[u" + userId + "]";
+ }
}
private final Context mContext;
- private final String mTag;
- private final String mAction;
- private final String mServicePackageName;
- private final List<HashSet<Signature>> mSignatureSets;
-
private final Handler mHandler;
+ private final Intent mIntent;
- // read/write from handler thread
- private IBinder mBestService;
+ @Nullable private final BinderRunner mOnBind;
+ @Nullable private final Runnable mOnUnbind;
+
+ // read/write from handler thread only
private int mCurrentUserId;
- // read from any thread, write from handler thread
- private volatile ComponentName mBestComponent;
- private volatile int mBestVersion;
- private volatile int mBestUserId;
+ // write from handler thread only, read anywhere
+ private volatile ServiceInfo mServiceInfo;
- public ServiceWatcher(Context context, String logTag, String action,
- int overlaySwitchResId, int defaultServicePackageNameResId,
- int initialPackageNamesResId, Handler handler) {
- Resources resources = context.getResources();
+ // read/write from handler thread only
+ private IBinder mBinder;
+ public ServiceWatcher(Context context, Handler handler, String action,
+ @Nullable BinderRunner onBind, @Nullable Runnable onUnbind,
+ @BoolRes int enableOverlayResId, @StringRes int nonOverlayPackageResId) {
mContext = context;
- mTag = logTag;
- mAction = action;
+ mHandler = FgThread.getHandler();
+ mIntent = new Intent(Objects.requireNonNull(action));
- boolean enableOverlay = resources.getBoolean(overlaySwitchResId);
- if (enableOverlay) {
- String[] pkgs = resources.getStringArray(initialPackageNamesResId);
- mServicePackageName = null;
- mSignatureSets = getSignatureSets(context, pkgs);
- if (D) Log.d(mTag, "Overlay enabled, packages=" + Arrays.toString(pkgs));
- } else {
- mServicePackageName = resources.getString(defaultServicePackageNameResId);
- mSignatureSets = getSignatureSets(context, mServicePackageName);
- if (D) Log.d(mTag, "Overlay disabled, default package=" + mServicePackageName);
+ Resources resources = context.getResources();
+ boolean enableOverlay = resources.getBoolean(enableOverlayResId);
+ if (!enableOverlay) {
+ mIntent.setPackage(resources.getString(nonOverlayPackageResId));
}
- mHandler = handler;
+ mOnBind = onBind;
+ mOnUnbind = onUnbind;
- mBestComponent = null;
- mBestVersion = Integer.MIN_VALUE;
- mBestUserId = UserHandle.USER_NULL;
+ mCurrentUserId = UserHandle.USER_NULL;
- mBestService = null;
+ mServiceInfo = ServiceInfo.NONE;
+ mBinder = null;
}
- protected void onBind() {}
-
- protected void onUnbind() {}
-
/**
- * Start this watcher, including binding to the current best match and
- * re-binding to any better matches down the road.
- * <p>
- * Note that if there are no matching encryption-aware services, we may not
- * bind to a real service until after the current user is unlocked.
- *
- * @return {@code true} if a potential service implementation was found.
+ * Register this class, which will start the process of determining the best matching service
+ * and maintaining a binding to it. Will return false and fail if there are no possible matching
+ * services at the time this functions is called.
*/
- public final boolean start() {
- // if we have to return false, do it before registering anything
- if (isServiceMissing()) return false;
-
- // listen for relevant package changes if service overlay is enabled on handler
- if (mServicePackageName == null) {
- new PackageMonitor() {
- @Override
- public void onPackageUpdateFinished(String packageName, int uid) {
- bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
- }
-
- @Override
- public void onPackageAdded(String packageName, int uid) {
- bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
- }
-
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
- }
-
- @Override
- public boolean onPackageChanged(String packageName, int uid, String[] components) {
- bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
- return super.onPackageChanged(packageName, uid, components);
- }
- }.register(mContext, UserHandle.ALL, true, mHandler);
+ public boolean register() {
+ if (mContext.getPackageManager().queryIntentServicesAsUser(mIntent,
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | MATCH_SYSTEM_ONLY,
+ UserHandle.USER_SYSTEM).isEmpty()) {
+ return false;
}
- // listen for user change on handler
+ new PackageMonitor() {
+ @Override
+ public void onPackageUpdateFinished(String packageName, int uid) {
+ ServiceWatcher.this.onPackageChanged(packageName);
+ }
+
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ ServiceWatcher.this.onPackageChanged(packageName);
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ ServiceWatcher.this.onPackageChanged(packageName);
+ }
+
+ @Override
+ public boolean onPackageChanged(String packageName, int uid, String[] components) {
+ ServiceWatcher.this.onPackageChanged(packageName);
+ return super.onPackageChanged(packageName, uid, components);
+ }
+ }.register(mContext, UserHandle.ALL, true, mHandler);
+
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
- UserHandle.USER_NULL);
- if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- mCurrentUserId = userId;
- bindBestPackage(false);
- } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
- if (userId == mCurrentUserId) {
- bindBestPackage(false);
- }
+ String action = intent.getAction();
+ if (action == null) {
+ return;
}
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId == UserHandle.USER_NULL) {
+ return;
+ }
+
+ switch (action) {
+ case Intent.ACTION_USER_SWITCHED:
+ onUserSwitched(userId);
+ break;
+ case Intent.ACTION_USER_UNLOCKED:
+ onUserUnlocked(userId);
+ break;
+ default:
+ break;
+ }
+
}
}, UserHandle.ALL, intentFilter, null, mHandler);
mCurrentUserId = ActivityManager.getCurrentUser();
- mHandler.post(() -> bindBestPackage(false));
+ mHandler.post(() -> onBestServiceChanged(false));
return true;
}
- /** Returns the name of the currently connected package or null. */
- @Nullable
- public String getCurrentPackageName() {
- ComponentName bestComponent = mBestComponent;
- return bestComponent == null ? null : bestComponent.getPackageName();
+ /**
+ * Returns information on the currently selected service.
+ */
+ public ServiceInfo getBoundService() {
+ return mServiceInfo;
}
- private boolean isServiceMissing() {
- return mContext.getPackageManager().queryIntentServicesAsUser(new Intent(mAction),
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- UserHandle.USER_SYSTEM).isEmpty();
- }
-
- private void bindBestPackage(boolean forceRebind) {
+ private void onBestServiceChanged(boolean forceRebind) {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- Intent intent = new Intent(mAction);
- if (mServicePackageName != null) {
- intent.setPackage(mServicePackageName);
- }
-
- List<ResolveInfo> rInfos = mContext.getPackageManager().queryIntentServicesAsUser(intent,
- PackageManager.GET_META_DATA | PackageManager.MATCH_DIRECT_BOOT_AUTO,
+ List<ResolveInfo> resolveInfos = mContext.getPackageManager().queryIntentServicesAsUser(
+ mIntent,
+ GET_META_DATA | MATCH_DIRECT_BOOT_AUTO | MATCH_SYSTEM_ONLY,
mCurrentUserId);
- if (rInfos == null) {
- rInfos = Collections.emptyList();
- }
- ComponentName bestComponent = null;
- int bestVersion = Integer.MIN_VALUE;
- boolean bestIsMultiuser = false;
-
- for (ResolveInfo rInfo : rInfos) {
- ComponentName component = rInfo.serviceInfo.getComponentName();
- String packageName = component.getPackageName();
-
- // check signature
- try {
- PackageInfo pInfo = mContext.getPackageManager().getPackageInfo(packageName,
- PackageManager.GET_SIGNATURES
- | PackageManager.MATCH_DIRECT_BOOT_AUTO);
- if (!isSignatureMatch(pInfo.signatures, mSignatureSets)) {
- Log.w(mTag, packageName + " resolves service " + mAction
- + ", but has wrong signature, ignoring");
- continue;
- }
- } catch (NameNotFoundException e) {
- Log.wtf(mTag, e);
- continue;
- }
-
- // check metadata
- Bundle metadata = rInfo.serviceInfo.metaData;
- int version = Integer.MIN_VALUE;
- boolean isMultiuser = false;
- if (metadata != null) {
- version = metadata.getInt(EXTRA_SERVICE_VERSION, Integer.MIN_VALUE);
- isMultiuser = metadata.getBoolean(EXTRA_SERVICE_IS_MULTIUSER, false);
- }
-
- if (version > bestVersion) {
- bestComponent = component;
- bestVersion = version;
- bestIsMultiuser = isMultiuser;
+ ServiceInfo bestServiceInfo = ServiceInfo.NONE;
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ ServiceInfo serviceInfo = new ServiceInfo(resolveInfo, mCurrentUserId);
+ if (serviceInfo.compareTo(bestServiceInfo) > 0) {
+ bestServiceInfo = serviceInfo;
}
}
- if (D) {
- Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction,
- (mServicePackageName == null ? ""
- : "(" + mServicePackageName + ") "), rInfos.size(),
- (bestComponent == null ? "no new best component"
- : "new best component: " + bestComponent)));
+ if (forceRebind || !bestServiceInfo.equals(mServiceInfo)) {
+ rebind(bestServiceInfo);
+ }
+ }
+
+ private void rebind(ServiceInfo newServiceInfo) {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
+ if (!mServiceInfo.equals(ServiceInfo.NONE)) {
+ if (D) {
+ Log.i(TAG, "[" + mIntent.getAction() + "] unbinding from " + mServiceInfo);
+ }
+
+ mContext.unbindService(this);
+ mServiceInfo = ServiceInfo.NONE;
}
- if (bestComponent == null) {
- Slog.w(mTag, "Odd, no component found for service " + mAction);
- unbind();
+ mServiceInfo = newServiceInfo;
+ if (mServiceInfo.equals(ServiceInfo.NONE)) {
return;
}
- int userId = bestIsMultiuser ? UserHandle.USER_SYSTEM : mCurrentUserId;
- boolean alreadyBound = Objects.equals(bestComponent, mBestComponent)
- && bestVersion == mBestVersion && userId == mBestUserId;
- if (forceRebind || !alreadyBound) {
- unbind();
- bind(bestComponent, bestVersion, userId);
+ Preconditions.checkState(mServiceInfo.component != null);
+
+ if (D) {
+ Log.i(TAG, getLogPrefix() + " binding to " + mServiceInfo);
+ }
+
+ Intent bindIntent = new Intent(mIntent).setComponent(mServiceInfo.component);
+ mContext.bindServiceAsUser(bindIntent, this,
+ BIND_AUTO_CREATE | BIND_NOT_FOREGROUND | BIND_NOT_VISIBLE,
+ mHandler, UserHandle.of(mServiceInfo.userId));
+ }
+
+ @Override
+ public final void onServiceConnected(ComponentName component, IBinder binder) {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
+ if (D) {
+ Log.i(TAG, getLogPrefix() + " connected to " + component);
+ }
+
+ mBinder = binder;
+ if (mOnBind != null) {
+ runOnBinder(mOnBind);
}
}
- private void bind(ComponentName component, int version, int userId) {
+ @Override
+ public final void onServiceDisconnected(ComponentName component) {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- Intent intent = new Intent(mAction);
- intent.setComponent(component);
-
- mBestComponent = component;
- mBestVersion = version;
- mBestUserId = userId;
-
- if (D) Log.d(mTag, "binding " + component + " (v" + version + ") (u" + userId + ")");
- mContext.bindServiceAsUser(intent, this,
- Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE,
- UserHandle.of(userId));
- }
-
- private void unbind() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-
- if (mBestComponent != null) {
- if (D) Log.d(mTag, "unbinding " + mBestComponent);
- mContext.unbindService(this);
+ if (D) {
+ Log.i(TAG, getLogPrefix() + " disconnected from " + component);
}
- mBestComponent = null;
- mBestVersion = Integer.MIN_VALUE;
- mBestUserId = UserHandle.USER_NULL;
+ mBinder = null;
+ if (mOnUnbind != null) {
+ mOnUnbind.run();
+ }
+ }
+
+ private void onUserSwitched(@UserIdInt int userId) {
+ mCurrentUserId = userId;
+ onBestServiceChanged(false);
+ }
+
+ private void onUserUnlocked(@UserIdInt int userId) {
+ if (userId == mCurrentUserId) {
+ onBestServiceChanged(false);
+ }
+ }
+
+ private void onPackageChanged(String packageName) {
+ // force a rebind if the changed package was the currently connected package
+ String currentPackageName =
+ mServiceInfo.component != null ? mServiceInfo.component.getPackageName() : null;
+ onBestServiceChanged(packageName.equals(currentPackageName));
}
/**
@@ -365,26 +388,26 @@
*/
public final void runOnBinder(BinderRunner runner) {
runOnHandler(() -> {
- if (mBestService == null) {
+ if (mBinder == null) {
return;
}
try {
- runner.run(mBestService);
- } catch (RuntimeException e) {
- // the code being run is privileged, but may be outside the system server, and thus
- // we cannot allow runtime exceptions to crash the system server
- Log.e(TAG, "exception while while running " + runner + " on " + mBestService
- + " from " + this, e);
- } catch (RemoteException e) {
- // do nothing
+ runner.run(mBinder);
+ } catch (RuntimeException | RemoteException e) {
+ // binders may propagate some specific non-RemoteExceptions from the other side
+ // through the binder as well - we cannot allow those to crash the system server
+ Log.e(TAG, getLogPrefix() + " exception running on " + mServiceInfo, e);
}
});
}
/**
* Runs the given function synchronously if currently connected, and returns the default value
- * if not currently connected or if any exception is thrown.
+ * if not currently connected or if any exception is thrown. Do not obtain any locks within the
+ * BlockingBinderRunner, or risk deadlock. The default value will be returned if there is no
+ * service connection when this is run, if a RemoteException occurs, or if the operation times
+ * out.
*
* @deprecated Using this function is an indication that your AIDL API is broken. Calls from
* system server to outside MUST be one-way, and so cannot return any result, and this
@@ -395,13 +418,16 @@
public final <T> T runOnBinderBlocking(BlockingBinderRunner<T> runner, T defaultValue) {
try {
return runOnHandlerBlocking(() -> {
- if (mBestService == null) {
+ if (mBinder == null) {
return defaultValue;
}
try {
- return runner.run(mBestService);
- } catch (RemoteException e) {
+ return runner.run(mBinder);
+ } catch (RuntimeException | RemoteException e) {
+ // binders may propagate some specific non-RemoteExceptions from the other side
+ // through the binder as well - we cannot allow those to crash the system server
+ Log.e(TAG, getLogPrefix() + " exception running on " + mServiceInfo, e);
return defaultValue;
}
});
@@ -410,30 +436,6 @@
}
}
- @Override
- public final void onServiceConnected(ComponentName component, IBinder binder) {
- runOnHandler(() -> {
- if (D) Log.d(mTag, component + " connected");
- mBestService = binder;
- onBind();
- });
- }
-
- @Override
- public final void onServiceDisconnected(ComponentName component) {
- runOnHandler(() -> {
- if (D) Log.d(mTag, component + " disconnected");
- mBestService = null;
- onUnbind();
- });
- }
-
- @Override
- public String toString() {
- ComponentName bestComponent = mBestComponent;
- return bestComponent == null ? "null" : bestComponent.toShortString() + "@" + mBestVersion;
- }
-
private void runOnHandler(Runnable r) {
if (Looper.myLooper() == mHandler.getLooper()) {
r.run();
@@ -467,4 +469,8 @@
}
}
}
+
+ private String getLogPrefix() {
+ return "[" + mIntent.getAction() + "]";
+ }
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 32830ae..4b4ce34 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1701,8 +1701,15 @@
if (mIsFuseEnabled != settingsFuseFlag) {
Slog.i(TAG, "Toggling persist.sys.fuse to " + settingsFuseFlag);
SystemProperties.set(PROP_FUSE, Boolean.toString(settingsFuseFlag));
- // Perform hard reboot to kick policy into place
- mContext.getSystemService(PowerManager.class).reboot("fuse_prop");
+
+ PowerManager powerManager = mContext.getSystemService(PowerManager.class);
+ if (powerManager.isRebootingUserspaceSupported()) {
+ // Perform userspace reboot to kick policy into place
+ powerManager.reboot(PowerManager.REBOOT_USERSPACE);
+ } else {
+ // Perform hard reboot to kick policy into place
+ powerManager.reboot("fuse_prop");
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 00c0b3e..5596b2f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -214,6 +214,7 @@
import android.content.pm.ParceledListSlice;
import android.content.pm.PathPermission;
import android.content.pm.PermissionInfo;
+import android.content.pm.ProcessInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.SELinuxUtil;
@@ -751,67 +752,23 @@
}
/**
+ * These are the currently running processes for which we have a ProcessInfo.
+ * Note: needs to be static since the permission checking call chain is static. This
+ * all probably should be refactored into a separate permission checking object.
+ */
+ @GuardedBy("sActiveProcessInfoSelfLocked")
+ static final SparseArray<ProcessInfo> sActiveProcessInfoSelfLocked = new SparseArray<>();
+
+ /**
* All of the processes we currently have running organized by pid.
* The keys are the pid running the application.
*
* <p>NOTE: This object is protected by its own lock, NOT the global activity manager lock!
*/
final PidMap mPidsSelfLocked = new PidMap();
- final class PidMap {
+ static final class PidMap {
private final SparseArray<ProcessRecord> mPidMap = new SparseArray<>();
- /**
- * Puts the process record in the map.
- * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
- * method.
- */
- void put(ProcessRecord app) {
- synchronized (this) {
- mPidMap.put(app.pid, app);
- }
- mAtmInternal.onProcessMapped(app.pid, app.getWindowProcessController());
- }
-
- /**
- * Removes the process record from the map.
- * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
- * method.
- */
- void remove(ProcessRecord app) {
- boolean removed = false;
- synchronized (this) {
- final ProcessRecord existingApp = mPidMap.get(app.pid);
- if (existingApp != null && existingApp.startSeq == app.startSeq) {
- mPidMap.remove(app.pid);
- removed = true;
- }
- }
- if (removed) {
- mAtmInternal.onProcessUnMapped(app.pid);
- }
- }
-
- /**
- * Removes the process record from the map if it has a thread.
- * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
- * method.
- */
- boolean removeIfNoThread(ProcessRecord app) {
- boolean removed = false;
- synchronized (this) {
- final ProcessRecord existingApp = get(app.pid);
- if (existingApp != null && existingApp.startSeq == app.startSeq
- && app.thread == null) {
- mPidMap.remove(app.pid);
- removed = true;
- }
- }
- if (removed) {
- mAtmInternal.onProcessUnMapped(app.pid);
- }
- return removed;
- }
-
ProcessRecord get(int pid) {
return mPidMap.get(pid);
}
@@ -831,6 +788,82 @@
int indexOfKey(int key) {
return mPidMap.indexOfKey(key);
}
+
+ void doAddInternal(ProcessRecord app) {
+ mPidMap.put(app.pid, app);
+ }
+
+ boolean doRemoveInternal(ProcessRecord app) {
+ final ProcessRecord existingApp = mPidMap.get(app.pid);
+ if (existingApp != null && existingApp.startSeq == app.startSeq) {
+ mPidMap.remove(app.pid);
+ return true;
+ }
+ return false;
+ }
+
+ boolean doRemoveIfNoThreadInternal(ProcessRecord app) {
+ if (app == null || app.thread != null) {
+ return false;
+ }
+ return doRemoveInternal(app);
+ }
+ }
+
+ /**
+ * Puts the process record in the map.
+ * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
+ * method.
+ */
+ void addPidLocked(ProcessRecord app) {
+ synchronized (mPidsSelfLocked) {
+ mPidsSelfLocked.doAddInternal(app);
+ }
+ synchronized (sActiveProcessInfoSelfLocked) {
+ if (app.processInfo != null) {
+ sActiveProcessInfoSelfLocked.put(app.pid, app.processInfo);
+ } else {
+ sActiveProcessInfoSelfLocked.remove(app.pid);
+ }
+ }
+ mAtmInternal.onProcessMapped(app.pid, app.getWindowProcessController());
+ }
+
+ /**
+ * Removes the process record from the map.
+ * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
+ * method.
+ */
+ void removePidLocked(ProcessRecord app) {
+ final boolean removed;
+ synchronized (mPidsSelfLocked) {
+ removed = mPidsSelfLocked.doRemoveInternal(app);
+ }
+ if (removed) {
+ synchronized (sActiveProcessInfoSelfLocked) {
+ sActiveProcessInfoSelfLocked.remove(app.pid);
+ }
+ mAtmInternal.onProcessUnMapped(app.pid);
+ }
+ }
+
+ /**
+ * Removes the process record from the map if it doesn't have a thread.
+ * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
+ * method.
+ */
+ boolean removePidIfNoThread(ProcessRecord app) {
+ final boolean removed;
+ synchronized (mPidsSelfLocked) {
+ removed = mPidsSelfLocked.doRemoveIfNoThreadInternal(app);
+ }
+ if (removed) {
+ synchronized (sActiveProcessInfoSelfLocked) {
+ sActiveProcessInfoSelfLocked.remove(app.pid);
+ }
+ mAtmInternal.onProcessUnMapped(app.pid);
+ }
+ return removed;
}
/**
@@ -2061,7 +2094,7 @@
app.getWindowProcessController().setPid(MY_PID);
app.maxAdj = ProcessList.SYSTEM_ADJ;
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
- mPidsSelfLocked.put(app);
+ addPidLocked(app);
mProcessList.updateLruProcessLocked(app, false, null);
updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
}
@@ -4723,7 +4756,7 @@
@GuardedBy("this")
private final void processStartTimedOutLocked(ProcessRecord app) {
final int pid = app.pid;
- boolean gone = mPidsSelfLocked.removeIfNoThread(app);
+ boolean gone = removePidIfNoThread(app);
if (gone) {
Slog.w(TAG, "Process " + app + " failed to attach");
@@ -4796,7 +4829,7 @@
// If there is already an app occupying that pid that hasn't been cleaned up
cleanUpApplicationRecordLocked(app, false, false, -1,
true /*replacingPid*/);
- mPidsSelfLocked.remove(app);
+ removePidLocked(app);
app = null;
}
} else {
@@ -5896,6 +5929,21 @@
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
}
+ // If there is an explicit permission being checked, and this is coming from a process
+ // that has been denied access to that permission, then just deny. Ultimately this may
+ // not be quite right -- it means that even if the caller would have access for another
+ // reason (such as being the owner of the component it is trying to access), it would still
+ // fail. This also means the system and root uids would be able to deny themselves
+ // access to permissions, which... well okay. ¯\_(ツ)_/¯
+ if (permission != null) {
+ synchronized (sActiveProcessInfoSelfLocked) {
+ ProcessInfo procInfo = sActiveProcessInfoSelfLocked.get(pid);
+ if (procInfo != null && procInfo.deniedPermissions != null
+ && procInfo.deniedPermissions.contains(permission)) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+ }
+ }
return ActivityManager.checkComponentPermission(permission, uid,
owningUid, exported);
}
@@ -14367,7 +14415,7 @@
return true;
} else if (app.pid > 0 && app.pid != MY_PID) {
// Goodbye!
- mPidsSelfLocked.remove(app);
+ removePidLocked(app);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
if (app.isolated) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 3f0e2ce..53a967b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2391,6 +2391,8 @@
return UsageStatsManager.STANDBY_BUCKET_FREQUENT;
} else if (lower.startsWith("ra")) {
return UsageStatsManager.STANDBY_BUCKET_RARE;
+ } else if (lower.startsWith("re")) {
+ return UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
} else if (lower.startsWith("ne")) {
return UsageStatsManager.STANDBY_BUCKET_NEVER;
} else {
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 6e135d6..bab133f 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -189,12 +189,14 @@
}
void onSystemReady() {
- // Read the sysprop set by lmkd and set this to persist so app could read it.
- SystemProperties.set("persist.sys.lmk.reportkills",
- Boolean.toString(SystemProperties.getBoolean("sys.lmk.reportkills", false)));
registerForUserRemoval();
registerForPackageRemoval();
- IoThread.getHandler().post(this::loadExistingProcessExitInfo);
+ IoThread.getHandler().post(() -> {
+ // Read the sysprop set by lmkd and set this to persist so app could read it.
+ SystemProperties.set("persist.sys.lmk.reportkills",
+ Boolean.toString(SystemProperties.getBoolean("sys.lmk.reportkills", false)));
+ loadExistingProcessExitInfo();
+ });
}
@GuardedBy("mService")
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index fa55701..a03f0bb 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -108,6 +108,21 @@
sDeviceConfigEntries.add(new DeviceConfigEntry(
DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.ENABLE_CURSOR_CONTROL,
WidgetFlags.KEY_ENABLE_CURSOR_CONTROL, boolean.class));
+ sDeviceConfigEntries.add(new DeviceConfigEntry(
+ DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.INSERTION_HANDLE_DELTA_HEIGHT,
+ WidgetFlags.KEY_INSERTION_HANDLE_DELTA_HEIGHT, int.class));
+ sDeviceConfigEntries.add(new DeviceConfigEntry(
+ DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.INSERTION_HANDLE_OPACITY,
+ WidgetFlags.KEY_INSERTION_HANDLE_OPACITY, int.class));
+ sDeviceConfigEntries.add(new DeviceConfigEntry(
+ DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.ENABLE_NEW_MAGNIFIER,
+ WidgetFlags.KEY_ENABLE_NEW_MAGNIFIER, boolean.class));
+ sDeviceConfigEntries.add(new DeviceConfigEntry(
+ DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.MAGNIFIER_ZOOM_FACTOR,
+ WidgetFlags.KEY_MAGNIFIER_ZOOM_FACTOR, float.class));
+ sDeviceConfigEntries.add(new DeviceConfigEntry(
+ DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.MAGNIFIER_ASPECT_RATIO,
+ WidgetFlags.KEY_MAGNIFIER_ASPECT_RATIO, float.class));
// add other device configs here...
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 63331fa..0429782 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -125,7 +125,7 @@
static final String OOM_ADJ_REASON_PROCESS_END = OOM_ADJ_REASON_METHOD + "_processEnd";
/**
- * Flag {@link Context#BIND_INCLUDE_CAPABILITIES} is used
+ * Flag {@link android.content.Context#BIND_INCLUDE_CAPABILITIES} is used
* to pass while-in-use capabilities from client process to bound service. In targetSdkVersion
* R and above, if client is a TOP activity, when this flag is present, bound service gets all
* while-in-use capabilities; when this flag is not present, bound service gets no while-in-use
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 4a89845..38cb501 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1564,7 +1564,7 @@
long startTime = SystemClock.uptimeMillis();
if (app.pid > 0 && app.pid != ActivityManagerService.MY_PID) {
checkSlow(startTime, "startProcess: removing from pids map");
- mService.mPidsSelfLocked.remove(app);
+ mService.removePidLocked(app);
mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
checkSlow(startTime, "startProcess: done removing from pids map");
app.setPid(0);
@@ -1609,10 +1609,28 @@
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
+
+ // Remove any gids needed if the process has been denied permissions.
+ // NOTE: eventually we should probably have the package manager pre-compute
+ // this for us?
+ if (app.processInfo != null && app.processInfo.deniedPermissions != null) {
+ for (int i = app.processInfo.deniedPermissions.size() - 1; i >= 0; i--) {
+ int[] denyGids = mService.mPackageManagerInt.getPermissionGids(
+ app.processInfo.deniedPermissions.valueAt(i), app.userId);
+ if (denyGids != null) {
+ for (int gid : denyGids) {
+ permGids = ArrayUtils.removeInt(permGids, gid);
+ }
+ }
+ }
+ }
+
int numGids = 3;
- if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) {
+ if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE
+ || app.info.packageName.equals("com.android.externalstorage")) {
numGids++;
}
+
/*
* Add shared application and profile GIDs so applications can share some
* resources like shared libraries and access user-wide resources
@@ -1626,8 +1644,14 @@
gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));
+
if (numGids > 3) {
- gids[3] = Process.SDCARD_RW_GID;
+ if (app.info.packageName.equals("com.android.externalstorage")) {
+ // Allows access to 'unreliable' (USB OTG) volumes via SAF
+ gids[3] = Process.MEDIA_RW_GID;
+ } else if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) {
+ gids[3] = Process.SDCARD_RW_GID;
+ }
}
// Replace any invalid GIDs
@@ -2311,7 +2335,7 @@
mService.cleanUpApplicationRecordLocked(oldApp, false, false, -1,
true /*replacingPid*/);
}
- mService.mPidsSelfLocked.put(app);
+ mService.addPidLocked(app);
synchronized (mService.mPidsSelfLocked) {
if (!procAttached) {
Message msg = mService.mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
@@ -2492,7 +2516,7 @@
.pendingStart)) {
int pid = app.pid;
if (pid > 0) {
- mService.mPidsSelfLocked.remove(app);
+ mService.removePidLocked(app);
mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
if (app.isolated) {
@@ -3945,4 +3969,3 @@
}
};
}
-
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 156466c..0e1e0f9 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -35,6 +35,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ProcessInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.VersionedPackage;
import android.content.res.CompatibilityInfo;
@@ -84,6 +85,7 @@
private final ActivityManagerService mService; // where we came from
final ApplicationInfo info; // all about the first app in the process
+ final ProcessInfo processInfo; // if non-null, process-specific manifest info
final boolean isolated; // true if this is a special isolated process
final boolean appZygote; // true if this is forked from the app zygote
final int uid; // uid of process; may be different from 'info' if isolated
@@ -603,6 +605,13 @@
int _uid) {
mService = _service;
info = _info;
+ if (_service.mPackageManagerInt != null) {
+ ArrayMap<String, ProcessInfo> processes =
+ _service.mPackageManagerInt.getProcessesForUid(_uid);
+ processInfo = processes != null ? processes.get(_processName) : null;
+ } else {
+ processInfo = null;
+ }
isolated = _info.uid != _uid;
appZygote = (UserHandle.getAppId(_uid) >= Process.FIRST_APP_ZYGOTE_ISOLATED_UID
&& UserHandle.getAppId(_uid) <= Process.LAST_APP_ZYGOTE_ISOLATED_UID);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 46972d9..8f6bd21 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -21,6 +21,7 @@
import static android.media.AudioManager.RINGER_MODE_SILENT;
import static android.media.AudioManager.RINGER_MODE_VIBRATE;
import static android.media.AudioManager.STREAM_SYSTEM;
+import static android.media.audiopolicy.AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE;
import static android.os.Process.FIRST_APPLICATION_UID;
import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE;
import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
@@ -89,6 +90,7 @@
import android.media.VolumePolicy;
import android.media.audiofx.AudioEffect;
import android.media.audiopolicy.AudioMix;
+import android.media.audiopolicy.AudioMixingRule.AudioMixMatchCriterion;
import android.media.audiopolicy.AudioPolicy;
import android.media.audiopolicy.AudioPolicyConfig;
import android.media.audiopolicy.AudioProductStrategy;
@@ -6774,6 +6776,7 @@
boolean requireValidProjection = false;
boolean requireCaptureAudioOrMediaOutputPerm = false;
+ boolean requireVoiceComunicationOutputPerm = false;
boolean requireModifyRouting = false;
if (hasFocusAccess || isVolumeController) {
@@ -6783,8 +6786,15 @@
requireModifyRouting |= true;
}
for (AudioMix mix : policyConfig.getMixes()) {
- // If mix is requesting a privileged capture
- if (mix.getRule().allowPrivilegedPlaybackCapture()) {
+ // If mix is trying to capture USAGE_VOICE_COMMUNICATION using playback capture
+ if (isVoiceCommunicationPlaybackCaptureMix(mix)) {
+ // then it must have CAPTURE_USAGE_VOICE_COMMUNICATION_OUTPUT permission
+ requireVoiceComunicationOutputPerm |= true;
+ }
+ // If mix is requesting privileged capture and is capturing at
+ // least one usage which is not USAGE_VOICE_COMMUNICATION.
+ if (mix.getRule().allowPrivilegedPlaybackCapture()
+ && isNonVoiceCommunicationCaptureMix(mix)) {
// then it must have CAPTURE_MEDIA_OUTPUT or CAPTURE_AUDIO_OUTPUT permission
requireCaptureAudioOrMediaOutputPerm |= true;
// and its format must be low quality enough
@@ -6812,6 +6822,14 @@
return false;
}
+ if (requireVoiceComunicationOutputPerm
+ && !callerHasPermission(
+ android.Manifest.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT)) {
+ Log.e(TAG, "Privileged audio capture for voice communication requires "
+ + "CAPTURE_VOICE_COMMUNICATION_OUTPUT system permission");
+ return false;
+ }
+
if (requireValidProjection && !canProjectAudio(projection)) {
return false;
}
@@ -6825,6 +6843,41 @@
return true;
}
+ /**
+ * Checks whether a given AudioMix is used for playback capture
+ * (has the ROUTE_FLAG_LOOP_BACK_RENDER flag) and has a matching
+ * criterion for USAGE_VOICE_COMMUNICATION.
+ */
+ private boolean isVoiceCommunicationPlaybackCaptureMix(AudioMix mix) {
+ if (mix.getRouteFlags() != mix.ROUTE_FLAG_LOOP_BACK_RENDER) {
+ return false;
+ }
+
+ for (AudioMixMatchCriterion criterion : mix.getRule().getCriteria()) {
+ if (criterion.getRule() == RULE_MATCH_ATTRIBUTE_USAGE
+ && criterion.getAudioAttributes().getUsage()
+ == AudioAttributes.USAGE_VOICE_COMMUNICATION) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks whether a given AudioMix has a matching
+ * criterion for a usage which is not USAGE_VOICE_COMMUNICATION.
+ */
+ private boolean isNonVoiceCommunicationCaptureMix(AudioMix mix) {
+ for (AudioMixMatchCriterion criterion : mix.getRule().getCriteria()) {
+ if (criterion.getRule() == RULE_MATCH_ATTRIBUTE_USAGE
+ && criterion.getAudioAttributes().getUsage()
+ != AudioAttributes.USAGE_VOICE_COMMUNICATION) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private boolean callerHasPermission(String permission) {
return mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED;
}
@@ -6988,6 +7041,26 @@
}
}
+ /** see AudioPolicy.setUserIdDeviceAffinity() */
+ public int setUserIdDeviceAffinity(IAudioPolicyCallback pcb, int userId,
+ @NonNull int[] deviceTypes, @NonNull String[] deviceAddresses) {
+ if (DEBUG_AP) {
+ Log.d(TAG, "setUserIdDeviceAffinity for " + pcb.asBinder() + " user:" + userId);
+ }
+
+ synchronized (mAudioPolicies) {
+ final AudioPolicyProxy app =
+ checkUpdateForPolicy(pcb, "Cannot change device affinity in audio policy");
+ if (app == null) {
+ return AudioManager.ERROR;
+ }
+ if (!app.hasMixRoutedToDevices(deviceTypes, deviceAddresses)) {
+ return AudioManager.ERROR;
+ }
+ return app.setUserIdDeviceAffinities(userId, deviceTypes, deviceAddresses);
+ }
+ }
+
/** see AudioPolicy.removeUidDeviceAffinity() */
public int removeUidDeviceAffinity(IAudioPolicyCallback pcb, int uid) {
if (DEBUG_AP) {
@@ -7003,6 +7076,22 @@
}
}
+ /** see AudioPolicy.removeUserIdDeviceAffinity() */
+ public int removeUserIdDeviceAffinity(IAudioPolicyCallback pcb, int userId) {
+ if (DEBUG_AP) {
+ Log.d(TAG, "removeUserIdDeviceAffinity for " + pcb.asBinder()
+ + " userId:" + userId);
+ }
+ synchronized (mAudioPolicies) {
+ final AudioPolicyProxy app =
+ checkUpdateForPolicy(pcb, "Cannot remove device affinity in audio policy");
+ if (app == null) {
+ return AudioManager.ERROR;
+ }
+ return app.removeUserIdDeviceAffinities(userId);
+ }
+ }
+
public int setFocusPropertiesForPolicy(int duckingBehavior, IAudioPolicyCallback pcb) {
if (DEBUG_AP) Log.d(TAG, "setFocusPropertiesForPolicy() duck behavior=" + duckingBehavior
+ " policy " + pcb.asBinder());
@@ -7238,6 +7327,9 @@
final HashMap<Integer, AudioDeviceArray> mUidDeviceAffinities =
new HashMap<Integer, AudioDeviceArray>();
+ final HashMap<Integer, AudioDeviceArray> mUserIdDeviceAffinities =
+ new HashMap<>();
+
final IMediaProjection mProjection;
private final class UnregisterOnStopCallback extends IMediaProjectionCallback.Stub {
public void onStop() {
@@ -7429,6 +7521,45 @@
return AudioManager.ERROR;
}
+ int setUserIdDeviceAffinities(int userId,
+ @NonNull int[] types, @NonNull String[] addresses) {
+ final Integer UserId = new Integer(userId);
+ int res;
+ if (mUserIdDeviceAffinities.remove(UserId) != null) {
+ final long identity = Binder.clearCallingIdentity();
+ res = AudioSystem.removeUserIdDeviceAffinities(UserId);
+ Binder.restoreCallingIdentity(identity);
+ if (res != AudioSystem.SUCCESS) {
+ Log.e(TAG, "AudioSystem. removeUserIdDeviceAffinities("
+ + UserId + ") failed, "
+ + " cannot call AudioSystem.setUserIdDeviceAffinities");
+ return AudioManager.ERROR;
+ }
+ }
+ final long identity = Binder.clearCallingIdentity();
+ res = AudioSystem.setUserIdDeviceAffinities(userId, types, addresses);
+ Binder.restoreCallingIdentity(identity);
+ if (res == AudioSystem.SUCCESS) {
+ mUserIdDeviceAffinities.put(UserId, new AudioDeviceArray(types, addresses));
+ return AudioManager.SUCCESS;
+ }
+ Log.e(TAG, "AudioSystem.setUserIdDeviceAffinities(" + userId + ") failed");
+ return AudioManager.ERROR;
+ }
+
+ int removeUserIdDeviceAffinities(int userId) {
+ if (mUserIdDeviceAffinities.remove(new Integer(userId)) != null) {
+ final long identity = Binder.clearCallingIdentity();
+ final int res = AudioSystem.removeUserIdDeviceAffinities(userId);
+ Binder.restoreCallingIdentity(identity);
+ if (res == AudioSystem.SUCCESS) {
+ return AudioManager.SUCCESS;
+ }
+ }
+ Log.e(TAG, "AudioSystem.removeUserIdDeviceAffinities failed");
+ return AudioManager.ERROR;
+ }
+
/** @return human readable debug informations summarizing the state of the object. */
public String toLogFriendlyString() {
String textDump = super.toLogFriendlyString();
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index a0c8e23..a0573db 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -118,14 +118,16 @@
private int mError;
// Only valid if mError is ERROR_VENDOR
private int mVendorError;
+ private int mUser;
AuthenticationEvent(long startTime, long latency, boolean authenticated, int error,
- int vendorError) {
+ int vendorError, int user) {
mStartTime = startTime;
mLatency = latency;
mAuthenticated = authenticated;
mError = error;
mVendorError = vendorError;
+ mUser = user;
}
public String toString(Context context) {
@@ -134,6 +136,7 @@
+ "\tAuthenticated: " + mAuthenticated
+ "\tError: " + mError
+ "\tVendorCode: " + mVendorError
+ + "\tUser: " + mUser
+ "\t" + FaceManager.getErrorString(context, mError, mVendorError);
}
}
@@ -242,7 +245,8 @@
System.currentTimeMillis() - getStartTimeMs() /* latency */,
authenticated,
0 /* error */,
- 0 /* vendorError */));
+ 0 /* vendorError */,
+ getTargetUserId()));
// For face, the authentication lifecycle ends either when
// 1) Authenticated == true
@@ -260,7 +264,8 @@
System.currentTimeMillis() - getStartTimeMs() /* latency */,
false /* authenticated */,
error,
- vendorCode));
+ vendorCode,
+ getTargetUserId()));
return super.onError(deviceId, error, vendorCode);
}
diff --git a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
index 299cb66..4fb6607 100644
--- a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
+++ b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
@@ -82,12 +82,14 @@
}
/**
- * Convert events in the ring buffer to protos and add to the given list
+ * Convert events in the ring buffer to a list of IpConnectivityEvent protos
*/
- public synchronized void listEventsAsProto(List<IpConnectivityEvent> out) {
+ public synchronized List<IpConnectivityEvent> listEventsAsProto() {
+ List<IpConnectivityEvent> list = new ArrayList<>();
for (DefaultNetworkEvent ev : mEventsLog.toArray()) {
- out.add(IpConnectivityEventBuilder.toProto(ev));
+ list.add(IpConnectivityEventBuilder.toProto(ev));
}
+ return list;
}
public synchronized void flushEvents(List<IpConnectivityEvent> out) {
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index 1337a93..2c06d82 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -244,9 +244,9 @@
private List<IpConnectivityEvent> listEventsAsProtos() {
final List<IpConnectivityEvent> events = IpConnectivityEventBuilder.toProto(getEvents());
if (mNetdListener != null) {
- mNetdListener.listAsProtos(events);
+ events.addAll(mNetdListener.listAsProtos());
}
- mDefaultNetworkMetrics.listEventsAsProto(events);
+ events.addAll(mDefaultNetworkMetrics.listEventsAsProto());
return events;
}
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 0d31051..f2892cc 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -367,18 +367,20 @@
}
/**
- * Convert events in the buffer to protos and add to the given list
+ * Convert events in the buffer to a list of IpConnectivityEvent protos
*/
- public synchronized void listAsProtos(List<IpConnectivityEvent> out) {
+ public synchronized List<IpConnectivityEvent> listAsProtos() {
+ List<IpConnectivityEvent> list = new ArrayList<>();
for (int i = 0; i < mNetworkMetrics.size(); i++) {
- out.add(IpConnectivityEventBuilder.toProto(mNetworkMetrics.valueAt(i).connectMetrics));
+ list.add(IpConnectivityEventBuilder.toProto(mNetworkMetrics.valueAt(i).connectMetrics));
}
for (int i = 0; i < mNetworkMetrics.size(); i++) {
- out.add(IpConnectivityEventBuilder.toProto(mNetworkMetrics.valueAt(i).dnsMetrics));
+ list.add(IpConnectivityEventBuilder.toProto(mNetworkMetrics.valueAt(i).dnsMetrics));
}
for (int i = 0; i < mWakeupStats.size(); i++) {
- out.add(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i)));
+ list.add(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i)));
}
+ return list;
}
private long getTransports(int netId) {
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 28f67fe..aa39926 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -109,11 +109,17 @@
return levels;
}
- private static float[] getFloatArray(TypedArray array) {
+ /**
+ * Extracts a float array from the specified {@link TypedArray}.
+ *
+ * @param array The array to convert.
+ * @return the given array as a float array.
+ */
+ public static float[] getFloatArray(TypedArray array) {
final int N = array.length();
float[] vals = new float[N];
for (int i = 0; i < N; i++) {
- vals[i] = array.getFloat(i, -1.0f);
+ vals[i] = array.getFloat(i, PowerManager.BRIGHTNESS_OFF_FLOAT);
}
array.recycle();
return vals;
@@ -335,7 +341,7 @@
}
}
- protected float normalizeAbsoluteBrightness(int brightness) {
+ protected static float normalizeAbsoluteBrightness(int brightness) {
brightness = MathUtils.constrain(brightness,
PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
return (float) brightness / PowerManager.BRIGHTNESS_ON;
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
new file mode 100644
index 0000000..e09cf61
--- /dev/null
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.os.Environment;
+import android.util.Slog;
+
+import com.android.server.display.config.DisplayConfiguration;
+import com.android.server.display.config.NitsMap;
+import com.android.server.display.config.Point;
+import com.android.server.display.config.XmlParser;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.util.List;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+
+/**
+ * Reads and stores display-specific configurations.
+ */
+public class DisplayDeviceConfig {
+ private static final String TAG = "DisplayDeviceConfig";
+
+ public static final float HIGH_BRIGHTNESS_MODE_UNSUPPORTED = Float.NaN;
+
+ private static final String ETC_DIR = "etc";
+ private static final String DISPLAY_CONFIG_DIR = "displayconfig";
+ private static final String CONFIG_FILE_FORMAT = "display_%d.xml";
+
+ private float[] mNits;
+ private float[] mBrightness;
+ private BigDecimal mHighBrightnessModeStart;
+
+ private DisplayDeviceConfig() {
+ }
+
+ /**
+ * Creates an instance for the specified display.
+ *
+ * @param physicalDisplayId The display ID for which to load the configuration.
+ * @return A configuration instance for the specified display.
+ */
+ public static DisplayDeviceConfig create(long physicalDisplayId) {
+ final DisplayDeviceConfig config = new DisplayDeviceConfig();
+ final String filename = String.format(CONFIG_FILE_FORMAT, physicalDisplayId);
+
+ config.initFromFile(Environment.buildPath(
+ Environment.getProductDirectory(), ETC_DIR, DISPLAY_CONFIG_DIR, filename));
+ return config;
+ }
+
+ /**
+ * Return the brightness mapping nits array if one is defined in the configuration file.
+ *
+ * @return The brightness mapping nits array.
+ */
+ public float[] getNits() {
+ return mNits;
+ }
+
+ /**
+ * Return the brightness mapping value array if one is defined in the configuration file.
+ *
+ * @return The brightness mapping value array.
+ */
+ public float[] getBrightness() {
+ return mBrightness;
+ }
+
+ /**
+ * Returns the point along the brightness value range {@link #getBrightness()} that
+ * high-brightness-mode begins. If high-brightness-mode is not supported, then
+ * Float.NaN is returned.
+ *
+ * @return The high brightness mode threshold, or Float.NaN if not supported.
+ */
+ public float getHighBrightnessModeStart() {
+ return mHighBrightnessModeStart != null
+ ? mHighBrightnessModeStart.floatValue() : HIGH_BRIGHTNESS_MODE_UNSUPPORTED;
+ }
+
+ private void initFromFile(File configFile) {
+ if (!configFile.exists()) {
+ // Display configuration files aren't required to exist.
+ return;
+ }
+
+ if (!configFile.isFile()) {
+ Slog.e(TAG, "Display configuration is not a file: " + configFile + ", skipping");
+ return;
+ }
+
+ try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
+ final DisplayConfiguration config = XmlParser.read(in);
+ loadBrightnessMap(config);
+ } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+ Slog.e(TAG, "Encountered an error while reading/parsing display config file: "
+ + configFile, e);
+ }
+ }
+
+ private void loadBrightnessMap(DisplayConfiguration config) {
+ final NitsMap map = config.getScreenBrightnessMap();
+ final List<Point> points = map.getPoint();
+ final int size = points.size();
+
+ float[] nits = new float[size];
+ float[] backlight = new float[size];
+
+ int i = 0;
+ for (Point point : points) {
+ nits[i] = point.getNits().floatValue();
+ backlight[i] = point.getValue().floatValue();
+ if (i > 0) {
+ if (nits[i] < nits[i - 1]) {
+ Slog.e(TAG, "screenBrightnessMap must be non-decreasing, ignoring rest "
+ + " of configuration. Nits: " + nits[i] + " < " + nits[i - 1]);
+ return;
+ }
+
+ if (backlight[i] < backlight[i - 1]) {
+ Slog.e(TAG, "screenBrightnessMap must be non-decreasing, ignoring rest "
+ + " of configuration. Value: " + backlight[i] + " < "
+ + backlight[i - 1]);
+ return;
+ }
+ }
+ ++i;
+ }
+ final BigDecimal hbmStart = map.getHighBrightnessStart();
+
+ mHighBrightnessModeStart = hbmStart;
+ mNits = nits;
+ mBrightness = backlight;
+ }
+}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index fb8a419..7e8fe3a 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -30,6 +30,7 @@
import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.Spline;
import android.view.Display;
import android.view.DisplayAddress;
import android.view.DisplayCutout;
@@ -37,10 +38,11 @@
import android.view.Surface;
import android.view.SurfaceControl;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
-import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
+import com.android.server.lights.LogicalLight;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -162,7 +164,7 @@
private final class LocalDisplayDevice extends DisplayDevice {
private final long mPhysicalDisplayId;
- private final Light mBacklight;
+ private final LogicalLight mBacklight;
private final SparseArray<DisplayModeRecord> mSupportedModes = new SparseArray<>();
private final ArrayList<Integer> mSupportedColorModes = new ArrayList<>();
private final boolean mIsInternal;
@@ -187,8 +189,10 @@
private boolean mGameContentTypeRequested;
private boolean mSidekickActive;
private SidekickInternal mSidekickInternal;
-
private SurfaceControl.PhysicalDisplayInfo[] mDisplayInfos;
+ private Spline mSystemBrightnessToNits;
+ private Spline mNitsToHalBrightness;
+ private boolean mHalBrightnessSupport;
LocalDisplayDevice(IBinder displayToken, long physicalDisplayId,
SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo,
@@ -210,6 +214,11 @@
mHdrCapabilities = SurfaceControl.getHdrCapabilities(displayToken);
mAllmSupported = SurfaceControl.getAutoLowLatencyModeSupport(displayToken);
mGameContentTypeSupported = SurfaceControl.getGameContentTypeSupport(displayToken);
+ mHalBrightnessSupport = SurfaceControl.getDisplayBrightnessSupport(displayToken);
+
+ // Defer configuration file loading
+ BackgroundThread.getHandler().sendMessage(PooledLambda.obtainMessage(
+ LocalDisplayDevice::loadDisplayConfigurationBrightnessMapping, this));
}
@Override
@@ -338,6 +347,41 @@
return true;
}
+ private void loadDisplayConfigurationBrightnessMapping() {
+ Spline nitsToHal = null;
+ Spline sysToNits = null;
+
+ // Load the mapping from nits to HAL brightness range (display-device-config.xml)
+ DisplayDeviceConfig config = DisplayDeviceConfig.create(mPhysicalDisplayId);
+ if (config == null) {
+ return;
+ }
+ final float[] halNits = config.getNits();
+ final float[] halBrightness = config.getBrightness();
+ if (halNits == null || halBrightness == null) {
+ return;
+ }
+ nitsToHal = Spline.createSpline(halNits, halBrightness);
+
+ // Load the mapping from system brightness range to nits (config.xml)
+ final Resources res = getOverlayContext().getResources();
+ final float[] sysNits = BrightnessMappingStrategy.getFloatArray(res.obtainTypedArray(
+ com.android.internal.R.array.config_screenBrightnessNits));
+ final int[] sysBrightness = res.getIntArray(
+ com.android.internal.R.array.config_screenBrightnessBacklight);
+ if (sysNits.length == 0 || sysBrightness.length != sysNits.length) {
+ return;
+ }
+ final float[] sysBrightnessFloat = new float[sysBrightness.length];
+ for (int i = 0; i < sysBrightness.length; i++) {
+ sysBrightnessFloat[i] = sysBrightness[i];
+ }
+ sysToNits = Spline.createSpline(sysBrightnessFloat, sysNits);
+
+ mNitsToHalBrightness = nitsToHal;
+ mSystemBrightnessToNits = sysToNits;
+ }
+
private boolean updateColorModesLocked(int[] colorModes,
int activeColorMode) {
List<Integer> pendingColorModes = new ArrayList<>();
@@ -628,13 +672,37 @@
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness("
+ "id=" + physicalDisplayId + ", brightness=" + brightness + ")");
try {
- mBacklight.setBrightness(brightness);
+ if (mHalBrightnessSupport) {
+ mBacklight.setBrightnessFloat(
+ displayBrightnessToHalBrightness(brightness));
+ } else {
+ mBacklight.setBrightness(brightness);
+ }
Trace.traceCounter(Trace.TRACE_TAG_POWER,
"ScreenBrightness", brightness);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
+
+ /**
+ * Converts brightness range from the framework's brightness space to the
+ * Hal brightness space if the HAL brightness space has been provided via
+ * a display device configuration file.
+ */
+ private float displayBrightnessToHalBrightness(int brightness) {
+ if (mSystemBrightnessToNits == null || mNitsToHalBrightness == null) {
+ return PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ }
+
+ if (brightness == 0) {
+ return PowerManager.BRIGHTNESS_OFF_FLOAT;
+ }
+
+ final float nits = mSystemBrightnessToNits.interpolate(brightness);
+ final float halBrightness = mNitsToHalBrightness.interpolate(nits);
+ return halBrightness;
+ }
};
}
return null;
diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java b/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java
index 5c18f58..5161a77 100644
--- a/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java
+++ b/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java
@@ -16,6 +16,10 @@
package com.android.server.incremental;
+import static android.content.pm.InstallationFile.FILE_TYPE_OBB;
+import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
+import static android.content.pm.PackageInstaller.LOCATION_MEDIA_OBB;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -171,7 +175,9 @@
session = packageInstaller.openSession(sessionId);
for (int i = 0; i < numFiles; i++) {
InstallationFile file = installationFiles.get(i);
- session.addFile(file.getName(), file.getSize(), file.getMetadata());
+ final int location = file.getFileType() == FILE_TYPE_OBB ? LOCATION_MEDIA_OBB
+ : LOCATION_DATA_APP;
+ session.addFile(location, file.getName(), file.getSize(), file.getMetadata(), null);
}
session.commit(localReceiver.getIntentSender());
final Intent result = localReceiver.getResult();
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index b1639a9..2926ec9 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -67,7 +67,9 @@
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/** Implementation of {@link AppIntegrityManagerService}. */
@@ -221,6 +223,15 @@
}
String installerPackageName = getInstallerPackageName(intent);
+
+ // Skip integrity verification if the verifier is doing the install.
+ if (isRuleProvider(installerPackageName)) {
+ Slog.i(TAG, "Verifier doing the install. Skipping integrity check.");
+ mPackageManagerInternal.setIntegrityVerificationResult(
+ verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
+ return;
+ }
+
String appCert = getCertificateFingerprint(packageInfo);
AppInstallMetadata.Builder builder = new AppInstallMetadata.Builder();
@@ -271,7 +282,7 @@
* Verify the UID and return the installer package name.
*
* @return the package name of the installer, or null if it cannot be determined or it is
- * installed via adb.
+ * installed via adb.
*/
@Nullable
private String getInstallerPackageName(Intent intent) {
@@ -518,9 +529,17 @@
}
private String getCallerPackageNameOrThrow() {
- final String[] allowedRuleProviders =
- mContext.getResources()
- .getStringArray(R.array.config_integrityRuleProviderPackages);
+ String callerPackageName = getCallerPackageName();
+ if (callerPackageName == null) {
+ throw new SecurityException(
+ "Only system packages specified in config_integrityRuleProviderPackages are"
+ + " allowed to call this method.");
+ }
+ return callerPackageName;
+ }
+
+ private String getCallerPackageName() {
+ final List<String> allowedRuleProviders = getAllowedRuleProviders();
for (String packageName : allowedRuleProviders) {
try {
// At least in tests, getPackageUid gives "NameNotFound" but getPackagesFromUid
@@ -537,9 +556,7 @@
Slog.i(TAG, "Rule provider package " + packageName + " not installed.");
}
}
- throw new SecurityException(
- "Only system packages specified in config_integrityRuleProviderPackages are"
- + " allowed to call this method.");
+ return null;
}
private boolean isSystemApp(String packageName) {
@@ -552,4 +569,14 @@
return false;
}
}
+
+ private List<String> getAllowedRuleProviders() {
+ return Arrays.asList(mContext.getResources().getStringArray(
+ R.array.config_integrityRuleProviderPackages));
+ }
+
+ private boolean isRuleProvider(String installerPackageName) {
+ return getAllowedRuleProviders().stream().anyMatch(
+ ruleProvider -> ruleProvider.equals(installerPackageName));
+ }
}
diff --git a/services/core/java/com/android/server/lights/LightsManager.java b/services/core/java/com/android/server/lights/LightsManager.java
index be20a44..521913a 100644
--- a/services/core/java/com/android/server/lights/LightsManager.java
+++ b/services/core/java/com/android/server/lights/LightsManager.java
@@ -29,5 +29,8 @@
public static final int LIGHT_ID_WIFI = Type.WIFI;
public static final int LIGHT_ID_COUNT = Type.COUNT;
- public abstract Light getLight(int id);
+ /**
+ * Returns the logical light with the given type.
+ */
+ public abstract LogicalLight getLight(int id);
}
diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java
index eaae2ed..5683e69 100644
--- a/services/core/java/com/android/server/lights/LightsService.java
+++ b/services/core/java/com/android/server/lights/LightsService.java
@@ -15,32 +15,223 @@
package com.android.server.lights;
+import android.Manifest;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
+import android.hardware.light.HwLight;
+import android.hardware.light.HwLightState;
+import android.hardware.light.ILights;
+import android.hardware.lights.ILightsManager;
+import android.hardware.lights.Light;
+import android.hardware.lights.LightState;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Message;
+import android.os.Looper;
import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.Trace;
import android.provider.Settings;
import android.util.Slog;
+import android.util.SparseArray;
import android.view.SurfaceControl;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
import com.android.server.SystemService;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
public class LightsService extends SystemService {
static final String TAG = "LightsService";
static final boolean DEBUG = false;
- final LightImpl mLights[] = new LightImpl[LightsManager.LIGHT_ID_COUNT];
+ private LightImpl[] mLights = null;
+ private SparseArray<LightImpl> mLightsById = null;
- private final class LightImpl extends Light {
+ private ILights mVintfLights = null;
+ @VisibleForTesting
+ final LightsManagerBinderService mManagerService;
+
+ private Handler mH;
+
+ private final class LightsManagerBinderService extends ILightsManager.Stub {
+
+ private final class Session {
+ final IBinder mToken;
+ final SparseArray<LightState> mRequests = new SparseArray<>();
+
+ Session(IBinder token) {
+ mToken = token;
+ }
+
+ void setRequest(int lightId, LightState state) {
+ if (state != null) {
+ mRequests.put(lightId, state);
+ } else {
+ mRequests.remove(lightId);
+ }
+ }
+ }
+
+ @GuardedBy("LightsService.this")
+ private final List<Session> mSessions = new ArrayList<>();
+
+ /**
+ * Returns the lights available for apps to control on the device. Only lights that aren't
+ * reserved for system use are available to apps.
+ */
+ @Override
+ public List<Light> getLights() {
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS,
+ "getLights requires CONTROL_DEVICE_LIGHTS_PERMISSION");
+
+ synchronized (LightsService.this) {
+ final List<Light> lights = new ArrayList<Light>();
+ for (int i = 0; i < mLightsById.size(); i++) {
+ HwLight hwLight = mLightsById.valueAt(i).getHwLight();
+ if (!isSystemLight(hwLight)) {
+ lights.add(new Light(hwLight.id, hwLight.ordinal, hwLight.type));
+ }
+ }
+ return lights;
+ }
+ }
+
+ /**
+ * Updates the set of light requests for {@param token} with additions and removals from
+ * {@param lightIds} and {@param lightStates}.
+ *
+ * <p>Null values mean that the request should be removed, and the light turned off if it
+ * is not being used by anything else.
+ */
+ @Override
+ public void setLightStates(IBinder token, int[] lightIds, LightState[] lightStates) {
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS,
+ "setLightStates requires CONTROL_DEVICE_LIGHTS permission");
+ Preconditions.checkState(lightIds.length == lightStates.length);
+
+ synchronized (LightsService.this) {
+ Session session = getSessionLocked(Preconditions.checkNotNull(token));
+ Preconditions.checkState(session != null, "not registered");
+
+ checkRequestIsValid(lightIds);
+
+ for (int i = 0; i < lightIds.length; i++) {
+ session.setRequest(lightIds[i], lightStates[i]);
+ }
+ invalidateLightStatesLocked();
+ }
+ }
+
+ @Override
+ public @Nullable LightState getLightState(int lightId) {
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS,
+ "getLightState(@TestApi) requires CONTROL_DEVICE_LIGHTS permission");
+
+ synchronized (LightsService.this) {
+ final LightImpl light = mLightsById.get(lightId);
+ if (light == null || isSystemLight(light.getHwLight())) {
+ throw new IllegalArgumentException("Invalid light: " + lightId);
+ }
+ return new LightState(light.getColor());
+ }
+ }
+
+ @Override
+ public void openSession(IBinder token) {
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS,
+ "openSession requires CONTROL_DEVICE_LIGHTS permission");
+ Preconditions.checkNotNull(token);
+
+ synchronized (LightsService.this) {
+ Preconditions.checkState(getSessionLocked(token) == null, "already registered");
+ try {
+ token.linkToDeath(() -> closeSessionInternal(token), 0);
+ mSessions.add(new Session(token));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Couldn't open session, client already died" , e);
+ throw new IllegalArgumentException("Client is already dead.");
+ }
+ }
+ }
+
+ @Override
+ public void closeSession(IBinder token) {
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS,
+ "closeSession requires CONTROL_DEVICE_LIGHTS permission");
+ Preconditions.checkNotNull(token);
+ closeSessionInternal(token);
+ }
+
+ private void closeSessionInternal(IBinder token) {
+ synchronized (LightsService.this) {
+ final Session session = getSessionLocked(token);
+ if (session != null) {
+ mSessions.remove(session);
+ invalidateLightStatesLocked();
+ }
+ }
+ }
+
+ private void checkRequestIsValid(int[] lightIds) {
+ for (int i = 0; i < lightIds.length; i++) {
+ final LightImpl light = mLightsById.get(lightIds[i]);
+ final HwLight hwLight = light.getHwLight();
+ Preconditions.checkState(light != null && !isSystemLight(hwLight),
+ "invalid lightId " + hwLight.id);
+ }
+ }
+
+ /**
+ * Apply light state requests for all light IDs.
+ *
+ * <p>In case of conflict, the session that started earliest wins.
+ */
+ private void invalidateLightStatesLocked() {
+ final Map<Integer, LightState> states = new HashMap<>();
+ for (int i = mSessions.size() - 1; i >= 0; i--) {
+ SparseArray<LightState> requests = mSessions.get(i).mRequests;
+ for (int j = 0; j < requests.size(); j++) {
+ states.put(requests.keyAt(j), requests.valueAt(j));
+ }
+ }
+ for (int i = 0; i < mLightsById.size(); i++) {
+ LightImpl light = mLightsById.valueAt(i);
+ HwLight hwLight = light.getHwLight();
+ if (!isSystemLight(hwLight)) {
+ LightState state = states.get(hwLight.id);
+ if (state != null) {
+ light.setColor(state.getColor());
+ } else {
+ light.turnOff();
+ }
+ }
+ }
+ }
+
+ private @Nullable Session getSessionLocked(IBinder token) {
+ for (int i = 0; i < mSessions.size(); i++) {
+ if (token.equals(mSessions.get(i).mToken)) {
+ return mSessions.get(i);
+ }
+ }
+ return null;
+ }
+ }
+
+ private final class LightImpl extends LogicalLight {
private final IBinder mDisplayToken;
private final int mSurfaceControlMaximumBrightness;
- private LightImpl(Context context, int id) {
- mId = id;
+ private LightImpl(Context context, HwLight hwLight) {
+ mHwLight = hwLight;
mDisplayToken = SurfaceControl.getInternalDisplayToken();
final boolean brightnessSupport = SurfaceControl.getDisplayBrightnessSupport(
mDisplayToken);
@@ -58,17 +249,28 @@
}
@Override
+ public void setBrightnessFloat(float brightness) {
+ if (!Float.isNaN(brightness)) {
+ setBrightness(brightness, 0, BRIGHTNESS_MODE_USER);
+ }
+ }
+
+ @Override
public void setBrightness(int brightness) {
setBrightness(brightness, BRIGHTNESS_MODE_USER);
}
@Override
public void setBrightness(int brightness, int brightnessMode) {
+ setBrightness(Float.NaN, brightness, brightnessMode);
+ }
+
+ private void setBrightness(float brightnessFloat, int brightness, int brightnessMode) {
synchronized (this) {
// LOW_PERSISTENCE cannot be manually set
if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) {
- Slog.w(TAG, "setBrightness with LOW_PERSISTENCE unexpected #" + mId +
- ": brightness=0x" + Integer.toHexString(brightness));
+ Slog.w(TAG, "setBrightness with LOW_PERSISTENCE unexpected #" + mHwLight.id
+ + ": brightness=0x" + Integer.toHexString(brightness));
return;
}
// Ideally, we'd like to set the brightness mode through the SF/HWC as well, but
@@ -84,7 +286,10 @@
if (DEBUG) {
Slog.d(TAG, "Using new setBrightness path!");
}
- if (brightness == 0) {
+
+ if (!Float.isNaN(brightnessFloat)) {
+ SurfaceControl.setDisplayBrightness(mDisplayToken, brightnessFloat);
+ } else if (brightness == 0) {
SurfaceControl.setDisplayBrightness(mDisplayToken, -1.0f);
} else {
SurfaceControl.setDisplayBrightness(mDisplayToken,
@@ -124,7 +329,7 @@
setLightLocked(color, LIGHT_FLASH_HARDWARE, onMS, 1000,
BRIGHTNESS_MODE_USER);
mColor = 0;
- mH.sendMessageDelayed(Message.obtain(mH, 1, this), onMS);
+ mH.postDelayed(this::stopFlashing, onMS);
}
}
}
@@ -171,8 +376,10 @@
if (!mInitialized || color != mColor || mode != mMode || onMS != mOnMS ||
offMS != mOffMS || mBrightnessMode != brightnessMode) {
- if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#"
- + Integer.toHexString(color) + ": brightnessMode=" + brightnessMode);
+ if (DEBUG) {
+ Slog.v(TAG, "setLight #" + mHwLight.id + ": color=#"
+ + Integer.toHexString(color) + ": brightnessMode=" + brightnessMode);
+ }
mInitialized = true;
mLastColor = mColor;
mColor = color;
@@ -180,10 +387,31 @@
mOnMS = onMS;
mOffMS = offMS;
mBrightnessMode = brightnessMode;
- Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLight(" + mId + ", 0x"
- + Integer.toHexString(color) + ")");
+ setLightUnchecked(color, mode, onMS, offMS, brightnessMode);
+ }
+ }
+
+ private void setLightUnchecked(int color, int mode, int onMS, int offMS,
+ int brightnessMode) {
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLightState(" + mHwLight.id + ", 0x"
+ + Integer.toHexString(color) + ")");
+ if (mVintfLights != null) {
+ HwLightState lightState = new HwLightState();
+ lightState.color = color;
+ lightState.flashMode = (byte) mode;
+ lightState.flashOnMs = onMS;
+ lightState.flashOffMs = offMS;
+ lightState.brightnessMode = (byte) brightnessMode;
try {
- setLight_native(mId, color, mode, onMS, offMS, brightnessMode);
+ mVintfLights.setLightState(mHwLight.id, lightState);
+ } catch (RemoteException | UnsupportedOperationException ex) {
+ Slog.e(TAG, "Failed issuing setLightState", ex);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
+ }
+ } else {
+ try {
+ setLight_native(mHwLight.id, color, mode, onMS, offMS, brightnessMode);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
@@ -194,7 +422,15 @@
return mVrModeEnabled && mUseLowPersistenceForVR;
}
- private int mId;
+ private HwLight getHwLight() {
+ return mHwLight;
+ }
+
+ private int getColor() {
+ return mColor;
+ }
+
+ private HwLight mHwLight;
private int mColor;
private int mMode;
private int mOnMS;
@@ -209,16 +445,59 @@
}
public LightsService(Context context) {
- super(context);
+ this(context,
+ ILights.Stub.asInterface(
+ ServiceManager.getService("android.hardware.light.ILights/default")),
+ Looper.myLooper());
+ }
- for (int i = 0; i < LightsManager.LIGHT_ID_COUNT; i++) {
- mLights[i] = new LightImpl(context, i);
+ @VisibleForTesting
+ LightsService(Context context, ILights service, Looper looper) {
+ super(context);
+ mH = new Handler(looper);
+ mVintfLights = service;
+ mManagerService = new LightsManagerBinderService();
+ populateAvailableLights(context);
+ }
+
+ private void populateAvailableLights(Context context) {
+ mLights = new LightImpl[LightsManager.LIGHT_ID_COUNT];
+ mLightsById = new SparseArray<>();
+
+ if (mVintfLights != null) {
+ try {
+ for (HwLight availableLight : mVintfLights.getLights()) {
+ LightImpl light = new LightImpl(context, availableLight);
+ int type = (int) availableLight.type;
+ if (0 <= type && type < mLights.length && mLights[type] == null) {
+ mLights[type] = light;
+ }
+ mLightsById.put(availableLight.id, light);
+ }
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Unable to get lights for initialization", ex);
+ }
+ }
+
+ // In the case where only the old HAL is available, all lights will be initialized here
+ for (int i = 0; i < mLights.length; i++) {
+ if (mLights[i] == null) {
+ // The ordinal can be anything if there is only 1 light of each type. Set it to 1.
+ HwLight light = new HwLight();
+ light.id = (byte) i;
+ light.ordinal = 1;
+ light.type = (byte) i;
+
+ mLights[i] = new LightImpl(context, light);
+ mLightsById.put(i, mLights[i]);
+ }
}
}
@Override
public void onStart() {
publishLocalService(LightsManager.class, mService);
+ publishBinderService(Context.LIGHTS_SERVICE, mManagerService);
}
@Override
@@ -235,22 +514,25 @@
private final LightsManager mService = new LightsManager() {
@Override
- public Light getLight(int id) {
- if (0 <= id && id < LIGHT_ID_COUNT) {
- return mLights[id];
+ public LogicalLight getLight(int lightType) {
+ if (mLights != null && 0 <= lightType && lightType < mLights.length) {
+ return mLights[lightType];
} else {
return null;
}
}
};
- private Handler mH = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- LightImpl light = (LightImpl)msg.obj;
- light.stopFlashing();
- }
- };
+ /**
+ * Returns whether a light is system-use-only or should be accessible to
+ * applications using the {@link android.hardware.lights.LightsManager} API.
+ */
+ private static boolean isSystemLight(HwLight light) {
+ // LIGHT_ID_COUNT comes from the 2.0 HIDL HAL and only contains system
+ // lights. Newly added lights will be made available via the
+ // LightsManager API.
+ return 0 <= light.type && light.type < LightsManager.LIGHT_ID_COUNT;
+ }
static native void setLight_native(int light, int color, int mode,
int onMS, int offMS, int brightnessMode);
diff --git a/services/core/java/com/android/server/lights/Light.java b/services/core/java/com/android/server/lights/LogicalLight.java
similarity index 67%
rename from services/core/java/com/android/server/lights/Light.java
rename to services/core/java/com/android/server/lights/LogicalLight.java
index 717e3da..33dfbb4 100644
--- a/services/core/java/com/android/server/lights/Light.java
+++ b/services/core/java/com/android/server/lights/LogicalLight.java
@@ -19,9 +19,24 @@
import android.hardware.light.V2_0.Brightness;
import android.hardware.light.V2_0.Flash;
-public abstract class Light {
+/**
+ * Allow control over a logical light of a given type. The mapping of logical lights to physical
+ * lights is HAL implementation-dependent.
+ */
+public abstract class LogicalLight {
+ /**
+ * Keep the light steady on or off.
+ */
public static final int LIGHT_FLASH_NONE = Flash.NONE;
+
+ /**
+ * Flash the light at specified rate.
+ */
public static final int LIGHT_FLASH_TIMED = Flash.TIMED;
+
+ /**
+ * Flash the light using hardware assist.
+ */
public static final int LIGHT_FLASH_HARDWARE = Flash.HARDWARE;
/**
@@ -49,10 +64,39 @@
*/
public abstract void setBrightness(int brightness, int brightnessMode);
+ /**
+ * Set the brightness of a display using the brightness range defines in a
+ * display-device-configuration file.
+ */
+ public abstract void setBrightnessFloat(float brightness);
+
+ /**
+ * Set the color of a light.
+ */
public abstract void setColor(int color);
+
+ /**
+ * Set the color of a light and control flashing.
+ */
public abstract void setFlashing(int color, int mode, int onMS, int offMS);
+
+ /**
+ * Pulses the light.
+ */
public abstract void pulse();
+
+ /**
+ * Pulses the light with a specified color for a specified duration.
+ */
public abstract void pulse(int color, int onMS);
+
+ /**
+ * Turns off the light.
+ */
public abstract void turnOff();
+
+ /**
+ * Set the VR mode of a display.
+ */
public abstract void setVrMode(boolean enabled);
}
diff --git a/services/core/java/com/android/server/lights/OWNERS b/services/core/java/com/android/server/lights/OWNERS
index c7c6d56..0e795b9 100644
--- a/services/core/java/com/android/server/lights/OWNERS
+++ b/services/core/java/com/android/server/lights/OWNERS
@@ -1,2 +1,3 @@
michaelwr@google.com
-dangittik@google.com
+santoscordon@google.com
+flc@google.com
diff --git a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
deleted file mode 100644
index 80ab790..0000000
--- a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.location;
-
-import android.content.Context;
-import android.hardware.location.ActivityRecognitionHardware;
-import android.hardware.location.IActivityRecognitionHardwareClient;
-import android.hardware.location.IActivityRecognitionHardwareWatcher;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.server.FgThread;
-import com.android.server.ServiceWatcher;
-
-/**
- * Proxy class to bind GmsCore to the ActivityRecognitionHardware.
- *
- * @hide
- */
-public class ActivityRecognitionProxy {
-
- private static final String TAG = "ActivityRecognitionProxy";
-
- /**
- * Creates an instance of the proxy and binds it to the appropriate FusedProvider.
- *
- * @return An instance of the proxy if it could be bound, null otherwise.
- */
- public static ActivityRecognitionProxy createAndBind(
- Context context,
- boolean activityRecognitionHardwareIsSupported,
- ActivityRecognitionHardware activityRecognitionHardware,
- int overlaySwitchResId,
- int defaultServicePackageNameResId,
- int initialPackageNameResId) {
- ActivityRecognitionProxy activityRecognitionProxy = new ActivityRecognitionProxy(
- context,
- activityRecognitionHardwareIsSupported,
- activityRecognitionHardware,
- overlaySwitchResId,
- defaultServicePackageNameResId,
- initialPackageNameResId);
-
- if (activityRecognitionProxy.mServiceWatcher.start()) {
- return activityRecognitionProxy;
- } else {
- return null;
- }
- }
-
- private final ServiceWatcher mServiceWatcher;
- private final boolean mIsSupported;
- private final ActivityRecognitionHardware mInstance;
-
- private ActivityRecognitionProxy(
- Context context,
- boolean activityRecognitionHardwareIsSupported,
- ActivityRecognitionHardware activityRecognitionHardware,
- int overlaySwitchResId,
- int defaultServicePackageNameResId,
- int initialPackageNameResId) {
- mIsSupported = activityRecognitionHardwareIsSupported;
- mInstance = activityRecognitionHardware;
-
- mServiceWatcher = new ServiceWatcher(
- context,
- TAG,
- "com.android.location.service.ActivityRecognitionProvider",
- overlaySwitchResId,
- defaultServicePackageNameResId,
- initialPackageNameResId,
- FgThread.getHandler()) {
- @Override
- protected void onBind() {
- runOnBinder(ActivityRecognitionProxy.this::initializeService);
- }
- };
- }
-
- private void initializeService(IBinder binder) {
- try {
- String descriptor = binder.getInterfaceDescriptor();
-
- if (IActivityRecognitionHardwareWatcher.class.getCanonicalName().equals(
- descriptor)) {
- IActivityRecognitionHardwareWatcher watcher =
- IActivityRecognitionHardwareWatcher.Stub.asInterface(binder);
- if (mInstance != null) {
- watcher.onInstanceChanged(mInstance);
- }
- } else if (IActivityRecognitionHardwareClient.class.getCanonicalName()
- .equals(descriptor)) {
- IActivityRecognitionHardwareClient client =
- IActivityRecognitionHardwareClient.Stub.asInterface(binder);
- client.onAvailabilityChanged(mIsSupported, mInstance);
- } else {
- Log.e(TAG, "Invalid descriptor found on connection: " + descriptor);
- }
- } catch (RemoteException e) {
- Log.w(TAG, e);
- }
- }
-}
diff --git a/services/core/java/com/android/server/location/ContextHubClientBroker.java b/services/core/java/com/android/server/location/ContextHubClientBroker.java
index 45d9bae..bb96e98 100644
--- a/services/core/java/com/android/server/location/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/ContextHubClientBroker.java
@@ -33,6 +33,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import android.util.proto.ProtoOutputStream;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
@@ -449,6 +450,28 @@
}
}
+ /**
+ * Dump debugging info as ClientBrokerProto
+ *
+ * If the output belongs to a sub message, the caller is responsible for wrapping this function
+ * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}.
+ *
+ * @param proto the ProtoOutputStream to write to
+ */
+ void dump(ProtoOutputStream proto) {
+ proto.write(ClientBrokerProto.ENDPOINT_ID, getHostEndPointId());
+ proto.write(ClientBrokerProto.ATTACHED_CONTEXT_HUB_ID, getAttachedContextHubId());
+ proto.write(ClientBrokerProto.PACKAGE, mPackage);
+ if (mPendingIntentRequest.isValid()) {
+ proto.write(ClientBrokerProto.PENDING_INTENT_REQUEST_VALID, true);
+ proto.write(ClientBrokerProto.NANO_APP_ID, mPendingIntentRequest.getNanoAppId());
+ }
+ proto.write(ClientBrokerProto.HAS_PENDING_INTENT, mPendingIntentRequest.hasPendingIntent());
+ proto.write(ClientBrokerProto.PENDING_INTENT_CANCELLED, isPendingIntentCancelled());
+ proto.write(ClientBrokerProto.REGISTERED, mRegistered);
+
+ }
+
@Override
public String toString() {
String out = "[ContextHubClient ";
diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java
index 46db8dc..0f70bb8 100644
--- a/services/core/java/com/android/server/location/ContextHubClientManager.java
+++ b/services/core/java/com/android/server/location/ContextHubClientManager.java
@@ -27,10 +27,13 @@
import android.hardware.location.NanoAppMessage;
import android.os.RemoteException;
import android.util.Log;
+import android.util.proto.ProtoOutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Calendar;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
@@ -45,6 +48,11 @@
private static final String TAG = "ContextHubClientManager";
/*
+ * The DateFormat for printing RegistrationRecord.
+ */
+ private static final DateFormat DATE_FORMAT = new SimpleDateFormat("MM/dd HH:mm:ss.SSS");
+
+ /*
* The maximum host endpoint ID value that a client can be assigned.
*/
private static final int MAX_CLIENT_ID = 0x7fff;
@@ -123,24 +131,24 @@
private class RegistrationRecord {
private final String mBroker;
private final int mAction;
- private final String mDate;
+ private final long mTimestamp;
RegistrationRecord(String broker, @Action int action) {
mBroker = broker;
mAction = action;
- Calendar instance = Calendar.getInstance();
- mDate = String.format("%02d", instance.get(Calendar.MONTH) + 1) // Jan == 0
- + "/" + String.format("%02d", instance.get(Calendar.DAY_OF_MONTH))
- + " " + String.format("%02d", instance.get(Calendar.HOUR_OF_DAY))
- + ":" + String.format("%02d", instance.get(Calendar.MINUTE))
- + ":" + String.format("%02d", instance.get(Calendar.SECOND))
- + "." + String.format("%03d", instance.get(Calendar.MILLISECOND));
+ mTimestamp = System.currentTimeMillis();
+ }
+
+ void dump(ProtoOutputStream proto) {
+ proto.write(ClientManagerProto.RegistrationRecord.TIMESTAMP_MS, mTimestamp);
+ proto.write(ClientManagerProto.RegistrationRecord.ACTION, mAction);
+ proto.write(ClientManagerProto.RegistrationRecord.BROKER, mBroker);
}
@Override
public String toString() {
String out = "";
- out += mDate + " ";
+ out += DATE_FORMAT.format(new Date(mTimestamp)) + " ";
out += mAction == ACTION_REGISTERED ? "+ " : "- ";
out += mBroker;
if (mAction == ACTION_CANCELLED) {
@@ -376,6 +384,28 @@
return null;
}
+ /**
+ * Dump debugging info as ClientManagerProto
+ *
+ * If the output belongs to a sub message, the caller is responsible for wrapping this function
+ * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}.
+ *
+ * @param proto the ProtoOutputStream to write to
+ */
+ void dump(ProtoOutputStream proto) {
+ for (ContextHubClientBroker broker : mHostEndPointIdToClientMap.values()) {
+ long token = proto.start(ClientManagerProto.CLIENT_BROKERS);
+ broker.dump(proto);
+ proto.end(token);
+ }
+ Iterator<RegistrationRecord> it = mRegistrationRecordDeque.descendingIterator();
+ while (it.hasNext()) {
+ long token = proto.start(ClientManagerProto.REGISTRATION_RECORDS);
+ it.next().dump(proto);
+ proto.end(token);
+ }
+ }
+
@Override
public String toString() {
String out = "";
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index 787a800..e79eddf 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -43,6 +43,7 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.util.DumpUtils;
@@ -782,6 +783,13 @@
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ for (String arg : args) {
+ if ("--proto".equals(arg)) {
+ dump(new ProtoOutputStream(fd));
+ return;
+ }
+ }
+
pw.println("Dumping ContextHub Service");
pw.println("");
@@ -802,6 +810,20 @@
// dump eventLog
}
+ private void dump(ProtoOutputStream proto) {
+ mContextHubIdToInfoMap.values().forEach(hubInfo -> {
+ long token = proto.start(ContextHubServiceProto.CONTEXT_HUB_INFO);
+ hubInfo.dump(proto);
+ proto.end(token);
+ });
+
+ long token = proto.start(ContextHubServiceProto.CLIENT_MANAGER);
+ mClientManager.dump(proto);
+ proto.end(token);
+
+ proto.flush();
+ }
+
private void checkPermissions() {
ContextHubServiceUtil.checkPermissions(mContext);
}
diff --git a/services/core/java/com/android/server/location/GeocoderProxy.java b/services/core/java/com/android/server/location/GeocoderProxy.java
index e6f0ed9..536f95a 100644
--- a/services/core/java/com/android/server/location/GeocoderProxy.java
+++ b/services/core/java/com/android/server/location/GeocoderProxy.java
@@ -16,6 +16,7 @@
package com.android.server.location;
+import android.annotation.Nullable;
import android.content.Context;
import android.location.Address;
import android.location.GeocoderParams;
@@ -28,40 +29,38 @@
/**
* Proxy for IGeocodeProvider implementations.
+ *
+ * @hide
*/
public class GeocoderProxy {
- private static final String TAG = "GeocoderProxy";
private static final String SERVICE_ACTION = "com.android.location.service.GeocodeProvider";
- private final ServiceWatcher mServiceWatcher;
-
- public static GeocoderProxy createAndBind(Context context,
- int overlaySwitchResId, int defaultServicePackageNameResId,
- int initialPackageNamesResId) {
- GeocoderProxy proxy = new GeocoderProxy(context, overlaySwitchResId,
- defaultServicePackageNameResId, initialPackageNamesResId);
- if (proxy.bind()) {
+ /**
+ * Creates and registers this proxy. If no suitable service is available for the proxy, returns
+ * null.
+ */
+ @Nullable
+ public static GeocoderProxy createAndRegister(Context context) {
+ GeocoderProxy proxy = new GeocoderProxy(context);
+ if (proxy.register()) {
return proxy;
} else {
return null;
}
}
- private GeocoderProxy(Context context,
- int overlaySwitchResId, int defaultServicePackageNameResId,
- int initialPackageNamesResId) {
- mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, overlaySwitchResId,
- defaultServicePackageNameResId, initialPackageNamesResId,
- BackgroundThread.getHandler());
+ private final ServiceWatcher mServiceWatcher;
+
+ private GeocoderProxy(Context context) {
+ mServiceWatcher = new ServiceWatcher(context, BackgroundThread.getHandler(), SERVICE_ACTION,
+ null, null,
+ com.android.internal.R.bool.config_enableGeocoderOverlay,
+ com.android.internal.R.string.config_geocoderProviderPackageName);
}
- private boolean bind() {
- return mServiceWatcher.start();
- }
-
- public String getConnectedPackageName() {
- return mServiceWatcher.getCurrentPackageName();
+ private boolean register() {
+ return mServiceWatcher.register();
}
public String getFromLocation(double latitude, double longitude, int maxResults,
@@ -83,5 +82,4 @@
maxResults, params, addrs);
}, "Service not Available");
}
-
}
diff --git a/services/core/java/com/android/server/location/GeofenceProxy.java b/services/core/java/com/android/server/location/GeofenceProxy.java
index ce93661..f006fb1 100644
--- a/services/core/java/com/android/server/location/GeofenceProxy.java
+++ b/services/core/java/com/android/server/location/GeofenceProxy.java
@@ -22,7 +22,6 @@
import android.content.ServiceConnection;
import android.hardware.location.GeofenceHardwareService;
import android.hardware.location.IGeofenceHardware;
-import android.location.IFusedGeofenceHardware;
import android.location.IGeofenceProvider;
import android.location.IGpsGeofenceHardware;
import android.os.IBinder;
@@ -33,6 +32,8 @@
import com.android.server.FgThread;
import com.android.server.ServiceWatcher;
+import java.util.Objects;
+
/**
* @hide
*/
@@ -41,64 +42,41 @@
private static final String TAG = "GeofenceProxy";
private static final String SERVICE_ACTION = "com.android.location.service.GeofenceProvider";
- private final Context mContext;
- private final ServiceWatcher mServiceWatcher;
-
@Nullable
- private final IGpsGeofenceHardware mGpsGeofenceHardware;
- @Nullable
- private final IFusedGeofenceHardware mFusedGeofenceHardware;
-
- private volatile IGeofenceHardware mGeofenceHardware;
-
- private final ServiceWatcher.BinderRunner mUpdateGeofenceHardware = (binder) -> {
- IGeofenceProvider provider = IGeofenceProvider.Stub.asInterface(binder);
- try {
- provider.setGeofenceHardware(mGeofenceHardware);
- } catch (RemoteException e) {
- Log.w(TAG, e);
- }
- };
-
- public static GeofenceProxy createAndBind(Context context,
- int overlaySwitchResId, int defaultServicePackageNameResId,
- int initialPackageNamesResId, @Nullable IGpsGeofenceHardware gpsGeofence,
- @Nullable IFusedGeofenceHardware fusedGeofenceHardware) {
- GeofenceProxy proxy = new GeofenceProxy(context, overlaySwitchResId,
- defaultServicePackageNameResId, initialPackageNamesResId, gpsGeofence,
- fusedGeofenceHardware);
-
- if (proxy.bind()) {
+ public static GeofenceProxy createAndBind(Context context, IGpsGeofenceHardware gpsGeofence) {
+ GeofenceProxy proxy = new GeofenceProxy(context, gpsGeofence);
+ if (proxy.register(context)) {
return proxy;
} else {
return null;
}
}
- private GeofenceProxy(Context context,
- int overlaySwitchResId, int defaultServicePackageNameResId,
- int initialPackageNamesResId, @Nullable IGpsGeofenceHardware gpsGeofence,
- @Nullable IFusedGeofenceHardware fusedGeofenceHardware) {
- mContext = context;
- mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, overlaySwitchResId,
- defaultServicePackageNameResId, initialPackageNamesResId,
- FgThread.getHandler()) {
- @Override
- protected void onBind() {
- runOnBinder(mUpdateGeofenceHardware);
- }
- };
+ private final IGpsGeofenceHardware mGpsGeofenceHardware;
+ private final ServiceWatcher mServiceWatcher;
- mGpsGeofenceHardware = gpsGeofence;
- mFusedGeofenceHardware = fusedGeofenceHardware;
+ private volatile IGeofenceHardware mGeofenceHardware;
+
+ private GeofenceProxy(Context context, IGpsGeofenceHardware gpsGeofence) {
+ mGpsGeofenceHardware = Objects.requireNonNull(gpsGeofence);
+ mServiceWatcher = new ServiceWatcher(context, FgThread.getHandler(), SERVICE_ACTION,
+ this::updateGeofenceHardware, null,
+ com.android.internal.R.bool.config_enableGeofenceOverlay,
+ com.android.internal.R.string.config_geofenceProviderPackageName);
mGeofenceHardware = null;
}
- private boolean bind() {
- if (mServiceWatcher.start()) {
- mContext.bindServiceAsUser(new Intent(mContext, GeofenceHardwareService.class),
- new GeofenceProxyServiceConnection(), Context.BIND_AUTO_CREATE,
+ private void updateGeofenceHardware(IBinder binder) throws RemoteException {
+ IGeofenceProvider.Stub.asInterface(binder).setGeofenceHardware(mGeofenceHardware);
+ }
+
+ private boolean register(Context context) {
+ if (mServiceWatcher.register()) {
+ context.bindServiceAsUser(
+ new Intent(context, GeofenceHardwareService.class),
+ new GeofenceProxyServiceConnection(),
+ Context.BIND_AUTO_CREATE,
UserHandle.SYSTEM);
return true;
}
@@ -113,24 +91,18 @@
IGeofenceHardware geofenceHardware = IGeofenceHardware.Stub.asInterface(service);
try {
- if (mGpsGeofenceHardware != null) {
- geofenceHardware.setGpsGeofenceHardware(mGpsGeofenceHardware);
- }
- if (mFusedGeofenceHardware != null) {
- geofenceHardware.setFusedGeofenceHardware(mFusedGeofenceHardware);
- }
-
+ geofenceHardware.setGpsGeofenceHardware(mGpsGeofenceHardware);
mGeofenceHardware = geofenceHardware;
- mServiceWatcher.runOnBinder(mUpdateGeofenceHardware);
- } catch (Exception e) {
- Log.w(TAG, e);
+ mServiceWatcher.runOnBinder(GeofenceProxy.this::updateGeofenceHardware);
+ } catch (RemoteException e) {
+ Log.w(TAG, "unable to initialize geofence hardware", e);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mGeofenceHardware = null;
- mServiceWatcher.runOnBinder(mUpdateGeofenceHardware);
+ mServiceWatcher.runOnBinder(GeofenceProxy.this::updateGeofenceHardware);
}
}
}
diff --git a/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
new file mode 100644
index 0000000..9d9852b
--- /dev/null
+++ b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.location;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.location.ActivityRecognitionHardware;
+import android.hardware.location.IActivityRecognitionHardwareClient;
+import android.hardware.location.IActivityRecognitionHardwareWatcher;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.server.FgThread;
+import com.android.server.ServiceWatcher;
+
+/**
+ * Proxy class to bind GmsCore to the ActivityRecognitionHardware.
+ *
+ * @hide
+ */
+public class HardwareActivityRecognitionProxy {
+
+ private static final String TAG = "ARProxy";
+ private static final String SERVICE_ACTION =
+ "com.android.location.service.ActivityRecognitionProvider";
+
+ /**
+ * Creates and registers this proxy. If no suitable service is available for the proxy, returns
+ * null.
+ */
+ @Nullable
+ public static HardwareActivityRecognitionProxy createAndRegister(Context context) {
+ HardwareActivityRecognitionProxy arProxy = new HardwareActivityRecognitionProxy(context);
+ if (arProxy.register()) {
+ return arProxy;
+ } else {
+ return null;
+ }
+ }
+
+ private final boolean mIsSupported;
+ private final ActivityRecognitionHardware mInstance;
+
+ private final ServiceWatcher mServiceWatcher;
+
+ private HardwareActivityRecognitionProxy(Context context) {
+ mIsSupported = ActivityRecognitionHardware.isSupported();
+ if (mIsSupported) {
+ mInstance = ActivityRecognitionHardware.getInstance(context);
+ } else {
+ mInstance = null;
+ }
+
+ mServiceWatcher = new ServiceWatcher(context,
+ FgThread.getHandler(),
+ SERVICE_ACTION,
+ this::onBind,
+ null,
+ com.android.internal.R.bool.config_enableActivityRecognitionHardwareOverlay,
+ com.android.internal.R.string.config_activityRecognitionHardwarePackageName);
+ }
+
+ private boolean register() {
+ return mServiceWatcher.register();
+ }
+
+ private void onBind(IBinder binder) throws RemoteException {
+ String descriptor = binder.getInterfaceDescriptor();
+
+ if (IActivityRecognitionHardwareWatcher.class.getCanonicalName().equals(descriptor)) {
+ IActivityRecognitionHardwareWatcher watcher =
+ IActivityRecognitionHardwareWatcher.Stub.asInterface(binder);
+ if (mInstance != null) {
+ watcher.onInstanceChanged(mInstance);
+ }
+ } else if (IActivityRecognitionHardwareClient.class.getCanonicalName().equals(descriptor)) {
+ IActivityRecognitionHardwareClient client =
+ IActivityRecognitionHardwareClient.Stub.asInterface(binder);
+ client.onAvailabilityChanged(mIsSupported, mInstance);
+ } else {
+ Log.e(TAG, "Unknown descriptor: " + descriptor);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index 8a149af..805b018 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -19,12 +19,11 @@
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import android.annotation.Nullable;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArraySet;
@@ -35,7 +34,6 @@
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
import com.android.server.FgThread;
-import com.android.server.LocationManagerService;
import com.android.server.ServiceWatcher;
import java.io.FileDescriptor;
@@ -49,17 +47,31 @@
public class LocationProviderProxy extends AbstractLocationProvider {
private static final String TAG = "LocationProviderProxy";
- private static final boolean D = LocationManagerService.D;
private static final int MAX_ADDITIONAL_PACKAGES = 2;
+ /**
+ * Creates and registers this proxy. If no suitable service is available for the proxy, returns
+ * null.
+ */
+ @Nullable
+ public static LocationProviderProxy createAndRegister(Context context, String action,
+ int enableOverlayResId, int nonOverlayPackageResId) {
+ LocationProviderProxy proxy = new LocationProviderProxy(context, action, enableOverlayResId,
+ nonOverlayPackageResId);
+ if (proxy.register()) {
+ return proxy;
+ } else {
+ return null;
+ }
+ }
+
private final ILocationProviderManager.Stub mManager = new ILocationProviderManager.Stub() {
// executed on binder thread
@Override
public void onSetAdditionalProviderPackages(List<String> packageNames) {
- int maxCount = Math.min(MAX_ADDITIONAL_PACKAGES, packageNames.size()) + 1;
+ int maxCount = Math.min(MAX_ADDITIONAL_PACKAGES, packageNames.size());
ArraySet<String> allPackages = new ArraySet<>(maxCount);
- allPackages.add(mServiceWatcher.getCurrentPackageName());
for (String packageName : packageNames) {
if (packageNames.size() >= maxCount) {
return;
@@ -74,6 +86,12 @@
}
}
+ // add the binder package
+ ComponentName service = mServiceWatcher.getBoundService().component;
+ if (service != null) {
+ allPackages.add(service.getPackageName());
+ }
+
setPackageNames(allPackages);
}
@@ -100,63 +118,39 @@
@Nullable private ProviderRequest mRequest;
- /**
- * Creates a new LocationProviderProxy and immediately begins binding to the best applicable
- * service.
- */
- @Nullable
- public static LocationProviderProxy createAndBind(Context context, String action,
- int overlaySwitchResId, int defaultServicePackageNameResId,
- int initialPackageNamesResId) {
- LocationProviderProxy proxy = new LocationProviderProxy(context, FgThread.getHandler(),
- action, overlaySwitchResId, defaultServicePackageNameResId,
- initialPackageNamesResId);
- if (proxy.bind()) {
- return proxy;
- } else {
- return null;
- }
- }
+ private LocationProviderProxy(Context context, String action, int enableOverlayResId,
+ int nonOverlayPackageResId) {
+ super(context, FgThread.getExecutor());
- private LocationProviderProxy(Context context, Handler handler, String action,
- int overlaySwitchResId, int defaultServicePackageNameResId,
- int initialPackageNamesResId) {
- super(context, new HandlerExecutor(handler), Collections.emptySet());
-
- mServiceWatcher = new ServiceWatcher(context, TAG, action, overlaySwitchResId,
- defaultServicePackageNameResId, initialPackageNamesResId, handler) {
-
- @Override
- protected void onBind() {
- runOnBinder(LocationProviderProxy.this::initializeService);
- }
-
- @Override
- protected void onUnbind() {
- setState(State.EMPTY_STATE);
- }
- };
+ mServiceWatcher = new ServiceWatcher(context, FgThread.getHandler(), action, this::onBind,
+ this::onUnbind, enableOverlayResId, nonOverlayPackageResId);
mRequest = null;
}
- private boolean bind() {
- return mServiceWatcher.start();
+ private boolean register() {
+ return mServiceWatcher.register();
}
- private void initializeService(IBinder binder) throws RemoteException {
- ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
- if (D) Log.d(TAG, "applying state to connected service " + mServiceWatcher);
+ private void onBind(IBinder binder) throws RemoteException {
+ ILocationProvider provider = ILocationProvider.Stub.asInterface(binder);
- setPackageNames(Collections.singleton(mServiceWatcher.getCurrentPackageName()));
+ ComponentName service = mServiceWatcher.getBoundService().component;
+ if (service != null) {
+ setPackageNames(Collections.singleton(service.getPackageName()));
+ }
- service.setLocationProviderManager(mManager);
+ provider.setLocationProviderManager(mManager);
if (mRequest != null) {
- service.setRequest(mRequest, mRequest.workSource);
+ provider.setRequest(mRequest, mRequest.workSource);
}
}
+ private void onUnbind() {
+ setState(State.EMPTY_STATE);
+ }
+
@Override
public void onSetRequest(ProviderRequest request) {
mServiceWatcher.runOnBinder(binder -> {
diff --git a/services/core/java/com/android/server/location/LocationRequestStatistics.java b/services/core/java/com/android/server/location/LocationRequestStatistics.java
index 45c8334..b1913389 100644
--- a/services/core/java/com/android/server/location/LocationRequestStatistics.java
+++ b/services/core/java/com/android/server/location/LocationRequestStatistics.java
@@ -92,7 +92,7 @@
/**
* A key that holds both package and provider names.
*/
- public static class PackageProviderKey {
+ public static class PackageProviderKey implements Comparable<PackageProviderKey> {
/**
* Name of package requesting location.
*/
@@ -108,6 +108,16 @@
}
@Override
+ public int compareTo(PackageProviderKey other) {
+ final int providerCompare = providerName.compareTo(other.providerName);
+ if (providerCompare != 0) {
+ return providerCompare;
+ } else {
+ return packageName.compareTo(other.packageName);
+ }
+ }
+
+ @Override
public boolean equals(Object other) {
if (!(other instanceof PackageProviderKey)) {
return false;
@@ -211,7 +221,7 @@
void dump(IndentingPrintWriter ipw, long systemElapsedOffsetMillis) {
StringBuilder s = new StringBuilder();
long systemTimeMillis = systemElapsedOffsetMillis + mElapsedRealtimeMillis;
- s.append("At ").append(TimeUtils.formatForLogging(systemTimeMillis)).append(": ")
+ s.append("At ").append(TimeUtils.logTimeOfDay(systemTimeMillis)).append(": ")
.append(mIntervalMillis == REQUEST_ENDED_INTERVAL ? "- " : "+ ")
.append(String.format("%7s", mProviderName)).append(" request from ")
.append(mPackageName);
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 4a6fcdf7..a6ad57a 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -103,6 +103,7 @@
private static final int WAKELOCK_TIMEOUT = 5000;
private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
+ private static final int SESSION_CREATION_LIMIT_PER_UID = 100;
private final Context mContext;
private final SessionManagerImpl mSessionManagerImpl;
@@ -428,6 +429,18 @@
Log.d(TAG, "Destroying " + session);
}
FullUserRecord user = getFullUserRecordLocked(session.getUserId());
+
+ if (user != null) {
+ final int uid = session.getUid();
+ final int sessionCount = user.mUidToSessionCount.get(uid, 0);
+ if (sessionCount <= 0) {
+ Log.w(TAG, "destroySessionLocked: sessionCount should be positive. "
+ + "sessionCount=" + sessionCount);
+ } else {
+ user.mUidToSessionCount.put(uid, sessionCount - 1);
+ }
+ }
+
if (mGlobalPrioritySession == session) {
mGlobalPrioritySession = null;
if (session.isActive() && user != null) {
@@ -490,6 +503,20 @@
}
}
+ private boolean hasMediaControlPermission(int pid, int uid) {
+ // Check if it's system server or has MEDIA_CONTENT_CONTROL.
+ // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra
+ // check here.
+ if (uid == Process.SYSTEM_UID || mContext.checkPermission(
+ android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
+ == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ } else if (DEBUG) {
+ Log.d(TAG, "uid(" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL");
+ }
+ return false;
+ }
+
/**
* This checks if the component is an enabled notification listener for the
* specified user. Enabled components may only operate on behalf of the user
@@ -544,6 +571,14 @@
throw new RuntimeException("Media Session owner died prematurely.", e);
}
+ final int sessionCount = user.mUidToSessionCount.get(callerUid, 0);
+ if (sessionCount >= SESSION_CREATION_LIMIT_PER_UID
+ && !hasMediaControlPermission(callerPid, callerUid)) {
+ throw new RuntimeException("Created too many sessions. count="
+ + sessionCount + ")");
+ }
+ user.mUidToSessionCount.put(callerUid, sessionCount + 1);
+
user.mPriorityStack.addSession(session);
mHandler.postSessionsChanged(session);
@@ -723,6 +758,7 @@
mOnMediaKeyEventDispatchedListeners = new HashMap<>();
private final HashMap<IBinder, OnMediaKeyEventSessionChangedListenerRecord>
mOnMediaKeyEventSessionChangedListeners = new HashMap<>();
+ private final SparseIntArray mUidToSessionCount = new SparseIntArray();
private PendingIntent mLastMediaButtonReceiver;
private ComponentName mRestoredMediaButtonReceiver;
@@ -1954,20 +1990,6 @@
return resolvedUserId;
}
- private boolean hasMediaControlPermission(int pid, int uid) {
- // Check if it's system server or has MEDIA_CONTENT_CONTROL.
- // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra
- // check here.
- if (uid == Process.SYSTEM_UID || mContext.checkPermission(
- android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
- == PackageManager.PERMISSION_GRANTED) {
- return true;
- } else if (DEBUG) {
- Log.d(TAG, "uid(" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL");
- }
- return false;
- }
-
private boolean hasEnabledNotificationListener(int resolvedUserId, String packageName)
throws RemoteException {
// You may not access another user's content as an enabled listener.
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b5b3923..7cc6732 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -251,8 +251,8 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
-import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
+import com.android.server.lights.LogicalLight;
import com.android.server.notification.ManagedServices.ManagedServiceInfo;
import com.android.server.notification.ManagedServices.UserProfiles;
import com.android.server.pm.PackageManagerService;
@@ -413,8 +413,8 @@
private final HandlerThread mRankingThread = new HandlerThread("ranker",
Process.THREAD_PRIORITY_BACKGROUND);
- private Light mNotificationLight;
- Light mAttentionLight;
+ private LogicalLight mNotificationLight;
+ LogicalLight mAttentionLight;
private long[] mFallbackVibrationPattern;
private boolean mUseAttentionLight;
@@ -1753,7 +1753,7 @@
}
@VisibleForTesting
- void setLights(Light light) {
+ void setLights(LogicalLight light) {
mNotificationLight = light;
mAttentionLight = light;
mNotificationPulseEnabled = true;
@@ -7875,7 +7875,7 @@
NotificationRecord.Light light = ledNotification.getLight();
if (light != null && mNotificationPulseEnabled) {
// pulse repeatedly
- mNotificationLight.setFlashing(light.color, Light.LIGHT_FLASH_TIMED,
+ mNotificationLight.setFlashing(light.color, LogicalLight.LIGHT_FLASH_TIMED,
light.onMs, light.offMs);
}
}
@@ -8256,7 +8256,8 @@
record.getSystemGeneratedSmartActions(),
record.getSmartReplies(),
record.canBubble(),
- record.isInterruptive()
+ record.isInterruptive(),
+ record.isConversation()
);
rankings.add(ranking);
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index f344a84..2bea218 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -54,6 +54,7 @@
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.ArraySet;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -164,6 +165,7 @@
private boolean mShowBadge;
private boolean mAllowBubble;
private Light mLight;
+ private boolean mIsNotConversationOverride;
/**
* This list contains system generated smart actions from NAS, app-generated smart actions are
* stored in Notification.actions with isContextual() set to true.
@@ -660,6 +662,10 @@
if (signals.containsKey(Adjustment.KEY_RANKING_SCORE)) {
mRankingScore = signals.getFloat(Adjustment.KEY_RANKING_SCORE);
}
+ if (signals.containsKey(Adjustment.KEY_NOT_CONVERSATION)) {
+ mIsNotConversationOverride = signals.getBoolean(
+ Adjustment.KEY_NOT_CONVERSATION);
+ }
if (!signals.isEmpty() && adjustment.getIssuer() != null) {
mAdjustmentIssuer = adjustment.getIssuer();
}
@@ -1310,6 +1316,25 @@
return hasCustomRemoteView && !hasDecoratedStyle;
}
+ /** Whether this notification is a conversation notification. */
+ public boolean isConversation() {
+ Notification notification = getNotification();
+ if (mChannel.isDemoted()
+ || !Notification.MessagingStyle.class.equals(notification.getNotificationStyle())) {
+ return false;
+ }
+ if (notification.getShortcutId() == null
+ && !FeatureFlagUtils.isEnabled(
+ mContext, FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ)) {
+ return false;
+ }
+ if (mIsNotConversationOverride) {
+ return false;
+ }
+ // STOPSHIP b/137397357: Check shortcut to make a further decision
+ return true;
+ }
+
@VisibleForTesting
static final class Light {
public final int color;
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 78e1719..9dff775 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -302,8 +302,9 @@
* initiating uid.
*/
public void grantImplicitAccess(int callingUid, int targetUid) {
- if (mImplicitlyQueryable.add(targetUid, callingUid) && DEBUG_LOGGING) {
- Slog.wtf(TAG, "implicit access granted: " + callingUid + " -> " + targetUid);
+ if (targetUid != callingUid
+ && mImplicitlyQueryable.add(targetUid, callingUid) && DEBUG_LOGGING) {
+ Slog.wtf(TAG, "implicit access granted: " + targetUid + " -> " + callingUid);
}
}
@@ -511,6 +512,10 @@
}
return true;
}
+ if (targetPkg.isStaticSharedLibrary()) {
+ // not an app, this filtering takes place at a higher level
+ return false;
+ }
final String targetName = targetPkg.getPackageName();
Trace.beginSection("getAppId");
final int callingAppId;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index ed955a2..ef28733 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -18,6 +18,7 @@
import static android.content.pm.DataLoaderType.INCREMENTAL;
import static android.content.pm.DataLoaderType.STREAMING;
+import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_SIGNATURE;
import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
@@ -60,6 +61,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.DataLoaderManager;
import android.content.pm.DataLoaderParams;
+import android.content.pm.DataLoaderParamsParcel;
import android.content.pm.FileSystemControlParcel;
import android.content.pm.IDataLoader;
import android.content.pm.IDataLoaderStatusListener;
@@ -203,8 +205,10 @@
private static final String ATTR_DATALOADER_PACKAGE_NAME = "dataLoaderPackageName";
private static final String ATTR_DATALOADER_CLASS_NAME = "dataLoaderClassName";
private static final String ATTR_DATALOADER_ARGUMENTS = "dataLoaderArguments";
+ private static final String ATTR_LOCATION = "location";
private static final String ATTR_LENGTH_BYTES = "lengthBytes";
private static final String ATTR_METADATA = "metadata";
+ private static final String ATTR_SIGNATURE = "signature";
private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
private static final int[] EMPTY_CHILD_SESSION_ARRAY = {};
@@ -303,22 +307,27 @@
private int mParentSessionId;
static class FileInfo {
+ public final int location;
public final String name;
public final Long lengthBytes;
public final byte[] metadata;
+ public final byte[] signature;
- public static FileInfo added(String name, Long lengthBytes, byte[] metadata) {
- return new FileInfo(name, lengthBytes, metadata);
+ public static FileInfo added(int location, String name, Long lengthBytes, byte[] metadata,
+ byte[] signature) {
+ return new FileInfo(location, name, lengthBytes, metadata, signature);
}
- public static FileInfo removed(String name) {
- return new FileInfo(name, -1L, null);
+ public static FileInfo removed(int location, String name) {
+ return new FileInfo(location, name, -1L, null, null);
}
- FileInfo(String name, Long lengthBytes, byte[] metadata) {
+ FileInfo(int location, String name, Long lengthBytes, byte[] metadata, byte[] signature) {
+ this.location = location;
this.name = name;
this.lengthBytes = lengthBytes;
this.metadata = metadata;
+ this.signature = signature;
}
}
@@ -597,6 +606,7 @@
info.isStagedSessionReady = mStagedSessionReady;
info.isStagedSessionFailed = mStagedSessionFailed;
info.setStagedSessionErrorCode(mStagedSessionErrorCode, mStagedSessionErrorMessage);
+ info.createdMillis = createdMillis;
info.updatedMillis = updatedMillis;
}
return info;
@@ -2325,11 +2335,23 @@
}
@Override
- public void addFile(String name, long lengthBytes, byte[] metadata) {
+ public DataLoaderParamsParcel getDataLoaderParams() {
+ return params.dataLoaderParams != null ? params.dataLoaderParams.getData() : null;
+ }
+
+ @Override
+ public void addFile(int location, String name, long lengthBytes, byte[] metadata,
+ byte[] signature) {
if (!isDataLoaderInstallation()) {
throw new IllegalStateException(
"Cannot add files to non-data loader installation session.");
}
+ if (!isIncrementalInstallation()) {
+ if (location != LOCATION_DATA_APP) {
+ throw new IllegalArgumentException(
+ "Non-incremental installation only supports /data/app placement: " + name);
+ }
+ }
// Use installer provided name for now; we always rename later
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid name: " + name);
@@ -2338,12 +2360,12 @@
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotSealedLocked("addFile");
- mFiles.add(FileInfo.added(name, lengthBytes, metadata));
+ mFiles.add(FileInfo.added(location, name, lengthBytes, metadata, signature));
}
}
@Override
- public void removeFile(String name) {
+ public void removeFile(int location, String name) {
if (!isDataLoaderInstallation()) {
throw new IllegalStateException(
"Cannot add files to non-data loader installation session.");
@@ -2356,7 +2378,7 @@
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotSealedLocked("removeFile");
- mFiles.add(FileInfo.removed(getRemoveMarkerName(name)));
+ mFiles.add(FileInfo.removed(location, getRemoveMarkerName(name)));
}
}
@@ -2891,9 +2913,11 @@
}
for (FileInfo fileInfo : mFiles) {
out.startTag(null, TAG_SESSION_FILE);
+ writeIntAttribute(out, ATTR_LOCATION, fileInfo.location);
writeStringAttribute(out, ATTR_NAME, fileInfo.name);
writeLongAttribute(out, ATTR_LENGTH_BYTES, fileInfo.lengthBytes);
writeByteArrayAttribute(out, ATTR_METADATA, fileInfo.metadata);
+ writeByteArrayAttribute(out, ATTR_SIGNATURE, fileInfo.signature);
out.endTag(null, TAG_SESSION_FILE);
}
}
@@ -3024,9 +3048,12 @@
childSessionIds.add(readIntAttribute(in, ATTR_SESSION_ID, SessionInfo.INVALID_ID));
}
if (TAG_SESSION_FILE.equals(in.getName())) {
- files.add(new FileInfo(readStringAttribute(in, ATTR_NAME),
+ files.add(new FileInfo(
+ readIntAttribute(in, ATTR_LOCATION, 0),
+ readStringAttribute(in, ATTR_NAME),
readLongAttribute(in, ATTR_LENGTH_BYTES, -1),
- readByteArrayAttribute(in, ATTR_METADATA)));
+ readByteArrayAttribute(in, ATTR_METADATA),
+ readByteArrayAttribute(in, ATTR_SIGNATURE)));
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3a33313..ca4ae02 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -185,6 +185,7 @@
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
+import android.content.pm.ProcessInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.SELinuxUtil;
@@ -3158,6 +3159,7 @@
// Adjust seInfo to ensure apps which share a sharedUserId are placed in the same
// SELinux domain.
setting.fixSeInfoLocked();
+ setting.updateProcesses();
}
// Now that we know all the packages we are keeping,
@@ -17697,7 +17699,7 @@
}
/*
- * This method deletes the package from internal data structures. If the DONT_DELETE_DATA
+ * This method deletes the package from internal data structures. If the DELETE_KEEP_DATA
* flag is not set, the data directory is removed as well.
* make sure this flag is set for partially installed apps. If not its meaningless to
* delete a partially installed application.
@@ -23487,6 +23489,20 @@
}
@Override
+ public ArrayMap<String, ProcessInfo> getProcessesForUid(int uid) {
+ synchronized (mLock) {
+ return getProcessesForUidLocked(uid);
+ }
+ }
+
+ @Override
+ public int[] getPermissionGids(String permissionName, int userId) {
+ synchronized (mLock) {
+ return getPermissionGidsLocked(permissionName, userId);
+ }
+ }
+
+ @Override
public boolean isOnlyCoreApps() {
return PackageManagerService.this.isOnlyCoreApps();
}
@@ -23741,6 +23757,30 @@
return res != null ? res : EmptyArray.STRING;
}
+ @GuardedBy("mLock")
+ public ArrayMap<String, ProcessInfo> getProcessesForUidLocked(int uid) {
+ final int appId = UserHandle.getAppId(uid);
+ final SettingBase obj = mSettings.getSettingLPr(appId);
+ if (obj instanceof SharedUserSetting) {
+ final SharedUserSetting sus = (SharedUserSetting) obj;
+ return PackageInfoUtils.generateProcessInfo(sus.processes, 0);
+ } else if (obj instanceof PackageSetting) {
+ final PackageSetting ps = (PackageSetting) obj;
+ return PackageInfoUtils.generateProcessInfo(ps.pkg.getProcesses(), 0);
+ }
+ return null;
+ }
+
+ @GuardedBy("mLock")
+ public int[] getPermissionGidsLocked(String permissionName, int userId) {
+ BasePermission perm
+ = mPermissionManager.getPermissionSettings().getPermission(permissionName);
+ if (perm != null) {
+ return perm.computeGids(userId);
+ }
+ return null;
+ }
+
@Override
public int getRuntimePermissionsVersion(@UserIdInt int userId) {
Preconditions.checkArgumentNonnegative(userId);
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 8384006..e7f6b89 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
@@ -2970,7 +2971,7 @@
// 1. Single file from stdin.
if (args.isEmpty() || STDIN_PATH.equals(args.get(0))) {
String name = "base." + (isApex ? "apex" : "apk");
- session.addFile(name, sessionSizeBytes, STDIN_PATH_BYTES);
+ session.addFile(LOCATION_DATA_APP, name, sessionSizeBytes, STDIN_PATH_BYTES, null);
return 0;
}
@@ -2994,7 +2995,7 @@
return 1;
}
- session.addFile(name, sizeBytes, STDIN_PATH_BYTES);
+ session.addFile(LOCATION_DATA_APP, name, sizeBytes, STDIN_PATH_BYTES, null);
continue;
}
@@ -3004,7 +3005,7 @@
String name = new File(inPath).getName();
byte[] metadata = inPath.getBytes(StandardCharsets.UTF_8);
- session.addFile(name, -1, metadata);
+ session.addFile(LOCATION_DATA_APP, name, -1, metadata, null);
}
return 0;
} finally {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
index a814cb8..281c44a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
@@ -115,8 +115,8 @@
}
@Override
- public boolean onPrepareImage(Collection<InstallationFile> addedFiles,
- Collection<String> removedFiles) {
+ public boolean onPrepareImage(@NonNull Collection<InstallationFile> addedFiles,
+ @NonNull Collection<String> removedFiles) {
final int commandId = extractShellCommandId(mParams.getArguments());
if (commandId == INVALID_SHELL_COMMAND_ID) {
return false;
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 0a42ccf..b9bb9e0 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -19,7 +19,9 @@
import android.annotation.Nullable;
import android.content.pm.ApplicationInfo;
import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ComponentParseUtils;
import android.service.pm.PackageServiceDumpProto;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.proto.ProtoOutputStream;
@@ -51,6 +53,8 @@
final PackageSignatures signatures = new PackageSignatures();
Boolean signaturesChanged;
+ ArrayMap<String, ComponentParseUtils.ParsedProcess> processes;
+
SharedUserSetting(String _name, int _pkgFlags, int _pkgPrivateFlags) {
super(_pkgFlags, _pkgPrivateFlags);
uidFlags = _pkgFlags;
@@ -72,6 +76,25 @@
proto.end(token);
}
+ void addProcesses(ArrayMap<String, ComponentParseUtils.ParsedProcess> newProcs) {
+ if (newProcs != null) {
+ final int numProcs = newProcs.size();
+ if (processes == null) {
+ processes = new ArrayMap<>(numProcs);
+ }
+ for (int i = 0; i < numProcs; i++) {
+ ComponentParseUtils.ParsedProcess newProc = newProcs.valueAt(i);
+ ComponentParseUtils.ParsedProcess proc = processes.get(newProc.name);
+ if (proc == null) {
+ proc = new ComponentParseUtils.ParsedProcess(newProc);
+ processes.put(newProc.name, proc);
+ } else {
+ proc.addStateFrom(newProc);
+ }
+ }
+ }
+ }
+
boolean removePackage(PackageSetting packageSetting) {
if (!packages.remove(packageSetting)) {
return false;
@@ -91,6 +114,8 @@
}
setPrivateFlags(aggregatedPrivateFlags);
}
+ // recalculate processes.
+ updateProcesses();
return true;
}
@@ -104,6 +129,9 @@
setFlags(this.pkgFlags | packageSetting.pkgFlags);
setPrivateFlags(this.pkgPrivateFlags | packageSetting.pkgPrivateFlags);
}
+ if (packageSetting.pkg != null) {
+ addProcesses(packageSetting.pkg.getProcesses());
+ }
}
public @Nullable List<AndroidPackage> getPackages() {
@@ -148,6 +176,16 @@
}
}
+ /**
+ * Update tracked data about processes based on all known packages in the shared user ID.
+ */
+ public void updateProcesses() {
+ processes = null;
+ for (int i = packages.size() - 1; i >= 0; i--) {
+ addProcesses(packages.valueAt(i).pkg.getProcesses());
+ }
+ }
+
/** Returns userIds which doesn't have any packages with this sharedUserId */
public int[] getNotInstalledUserIds() {
int[] excludedUserIds = null;
@@ -176,6 +214,17 @@
this.packages.clear();
this.packages.addAll(sharedUser.packages);
this.signaturesChanged = sharedUser.signaturesChanged;
+ if (sharedUser.processes != null) {
+ final int numProcs = sharedUser.processes.size();
+ this.processes = new ArrayMap<>(numProcs);
+ for (int i = 0; i < numProcs; i++) {
+ ComponentParseUtils.ParsedProcess proc =
+ new ComponentParseUtils.ParsedProcess(sharedUser.processes.valueAt(i));
+ this.processes.put(proc.name, proc);
+ }
+ } else {
+ this.processes = null;
+ }
return this;
}
}
diff --git a/services/core/java/com/android/server/policy/GlobalKeyManager.java b/services/core/java/com/android/server/policy/GlobalKeyManager.java
index e08c004..157f825 100644
--- a/services/core/java/com/android/server/policy/GlobalKeyManager.java
+++ b/services/core/java/com/android/server/policy/GlobalKeyManager.java
@@ -74,7 +74,7 @@
Intent intent = new Intent(Intent.ACTION_GLOBAL_BUTTON)
.setComponent(component)
.setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
- .putExtra(Intent.EXTRA_KEY_EVENT, event);
+ .putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(event));
context.sendBroadcastAsUser(intent, UserHandle.CURRENT, null);
return true;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index ec9049e..c3e7f62 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4876,7 +4876,7 @@
mBootMsgDialog.getWindow().setDimAmount(1);
WindowManager.LayoutParams lp = mBootMsgDialog.getWindow().getAttributes();
lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
- lp.setFitWindowInsetsTypes(0 /* types */);
+ lp.setFitInsetsTypes(0 /* types */);
mBootMsgDialog.getWindow().setAttributes(lp);
mBootMsgDialog.setCancelable(false);
mBootMsgDialog.show();
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 8460ede..df8e30f 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -46,6 +46,7 @@
import android.os.Vibrator;
import android.os.WorkSource;
import android.provider.Settings;
+import android.telephony.TelephonyManager;
import android.util.EventLog;
import android.util.Slog;
import android.util.StatsLog;
@@ -671,6 +672,8 @@
}
mUserActivityPending = false;
}
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ tm.notifyUserActivity();
mPolicy.userActivity();
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index ca36869..3f3a133 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -16,8 +16,8 @@
package com.android.server.power;
-import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT;
import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
@@ -96,8 +96,8 @@
import com.android.server.UiThread;
import com.android.server.Watchdog;
import com.android.server.am.BatteryStatsService;
-import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
+import com.android.server.lights.LogicalLight;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.power.batterysaver.BatterySaverController;
import com.android.server.power.batterysaver.BatterySaverPolicy;
@@ -257,7 +257,7 @@
private WirelessChargerDetector mWirelessChargerDetector;
private SettingsObserver mSettingsObserver;
private DreamManagerInternal mDreamManager;
- private Light mAttentionLight;
+ private LogicalLight mAttentionLight;
private InattentiveSleepWarningController mInattentiveSleepWarningOverlayController;
@@ -3347,7 +3347,7 @@
}
private void setAttentionLightInternal(boolean on, int color) {
- Light light;
+ LogicalLight light;
synchronized (mLock) {
if (!mSystemReady) {
return;
@@ -3356,7 +3356,7 @@
}
// Control light outside of lock.
- light.setFlashing(color, Light.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0);
+ light.setFlashing(color, LogicalLight.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0);
}
private void setDozeAfterScreenOffInternal(boolean on) {
diff --git a/services/core/java/com/android/server/utils/FlagNamespaceUtils.java b/services/core/java/com/android/server/utils/FlagNamespaceUtils.java
deleted file mode 100644
index f8c7447..0000000
--- a/services/core/java/com/android/server/utils/FlagNamespaceUtils.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.utils;
-
-import android.annotation.Nullable;
-import android.provider.DeviceConfig;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.RescueParty;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Utilities for interacting with the {@link android.provider.DeviceConfig}.
- *
- * @hide
- */
-public final class FlagNamespaceUtils {
- /**
- * Special String used for communicating through {@link #RESET_PLATFORM_PACKAGE_FLAG} that
- * Settings were reset by the RescueParty, no actual namespace with this name exists in
- * {@link DeviceConfig}.
- */
- public static final String NAMESPACE_NO_PACKAGE = "no_package";
-
- /**
- * Name of the special namespace in DeviceConfig table used for communicating resets.
- */
- @VisibleForTesting
- public static final String NAMESPACE_RESCUE_PARTY = "rescue_party_namespace";
- /**
- * Flag in the {@link DeviceConfig} in {@link #NAMESPACE_RESCUE_PARTY}, holding all known {@link
- * DeviceConfig} namespaces, as a {@link #DELIMITER} separated String. It's updated after the
- * first time flags are written to the new namespace in the {@link DeviceConfig}.
- */
- @VisibleForTesting
- public static final String ALL_KNOWN_NAMESPACES_FLAG = "all_known_namespaces";
- /**
- * Flag in the {@link DeviceConfig} in {@link #NAMESPACE_RESCUE_PARTY} with integer counter
- * suffix added to it, holding {@link DeviceConfig} namespace value whose flags were recently
- * reset by the {@link RescueParty}. It's updated by {@link RescueParty} every time given
- * namespace flags are reset.
- */
- @VisibleForTesting
- public static final String RESET_PLATFORM_PACKAGE_FLAG = "reset_platform_package";
- private static final String DELIMITER = ":";
- /**
- * Maximum value of the counter used in combination with {@link #RESET_PLATFORM_PACKAGE_FLAG}
- * when communicating recently reset by the RescueParty namespace values.
- */
- private static final int MAX_COUNTER_VALUE = 50;
-
- private static int sKnownResetNamespacesFlagCounter = -1;
-
- /**
- * Sets the union of {@link #RESET_PLATFORM_PACKAGE_FLAG} with
- * {@link #sKnownResetNamespacesFlagCounter} in the DeviceConfig for each namespace
- * in the consumed namespacesList. These flags are used for communicating the namespaces
- * (aka platform packages) whose flags in {@link DeviceConfig} were just reset
- * by the RescueParty.
- */
- public static void addToKnownResetNamespaces(@Nullable List<String> namespacesList) {
- if (namespacesList == null) {
- return;
- }
- for (String namespace : namespacesList) {
- addToKnownResetNamespaces(namespace);
- }
- }
-
- /**
- * Sets the union of {@link #RESET_PLATFORM_PACKAGE_FLAG} with
- * {@link #sKnownResetNamespacesFlagCounter} in the DeviceConfig for the consumed namespace.
- * This flag is used for communicating the namespace (aka platform package) whose flags
- * in {@link DeviceConfig} were just reset by the RescueParty.
- */
- public static void addToKnownResetNamespaces(String namespace) {
- int nextFlagCounter = incrementAndRetrieveResetNamespacesFlagCounter();
- DeviceConfig.setProperty(NAMESPACE_RESCUE_PARTY,
- RESET_PLATFORM_PACKAGE_FLAG + nextFlagCounter,
- namespace, /*makeDefault=*/ true);
- }
-
- /**
- * Reset all namespaces in DeviceConfig with consumed resetMode.
- */
- public static void resetDeviceConfig(int resetMode) {
- resetDeviceConfig(resetMode, getAllKnownDeviceConfigNamespacesList());
- }
-
- /**
- * Reset all consumed namespaces in DeviceConfig with consumed resetMode.
- */
- public static void resetDeviceConfig(int resetMode, List<String> namespacesList) {
- for (String namespace : namespacesList) {
- DeviceConfig.resetToDefaults(resetMode, namespace);
- }
- addToKnownResetNamespaces(namespacesList);
- }
-
- /**
- * Resets known reset namespaces flag counter for tests only.
- */
- @VisibleForTesting
- public static void resetKnownResetNamespacesFlagCounterForTest() {
- sKnownResetNamespacesFlagCounter = -1;
- }
-
- /**
- * Returns a list of all known DeviceConfig namespaces, except for the special {@link
- * #NAMESPACE_RESCUE_PARTY}
- */
- private static List<String> getAllKnownDeviceConfigNamespacesList() {
- String namespacesStr = DeviceConfig.getProperty(NAMESPACE_RESCUE_PARTY,
- ALL_KNOWN_NAMESPACES_FLAG);
- List<String> namespacesList = toStringList(namespacesStr);
- namespacesList.remove(NAMESPACE_RESCUE_PARTY);
- return namespacesList;
- }
-
- private static List<String> toStringList(String serialized) {
- if (serialized == null || serialized.length() == 0) {
- return new ArrayList<>();
- }
- return Arrays.asList(serialized.split(DELIMITER));
- }
-
- private static int incrementAndRetrieveResetNamespacesFlagCounter() {
- sKnownResetNamespacesFlagCounter++;
- if (sKnownResetNamespacesFlagCounter == MAX_COUNTER_VALUE) {
- sKnownResetNamespacesFlagCounter = 0;
- }
- return sKnownResetNamespacesFlagCounter;
- }
-}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 03139d2e..2115f7c 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2180,6 +2180,47 @@
}
}
+ /**
+ * Called when the wallpaper needs to zoom out.
+ *
+ * @param zoom from 0 to 1 (inclusive) where 1 means fully zoomed out, 0 means fully zoomed in.
+ * @param callingPackage package name calling this API.
+ * @param displayId id of the display whose zoom is updating.
+ */
+ public void setWallpaperZoomOut(float zoom, String callingPackage, int displayId) {
+ if (!isWallpaperSupported(callingPackage)) {
+ return;
+ }
+ synchronized (mLock) {
+ if (!isValidDisplay(displayId)) {
+ throw new IllegalArgumentException("Cannot find display with id=" + displayId);
+ }
+ int userId = UserHandle.getCallingUserId();
+ if (mCurrentUserId != userId) {
+ return; // Don't change the properties now
+ }
+ WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
+ if (zoom < 0 || zoom > 1f) {
+ throw new IllegalArgumentException("zoom must be between 0 and one: " + zoom);
+ }
+
+ if (wallpaper.connection != null) {
+ final WallpaperConnection.DisplayConnector connector = wallpaper.connection
+ .getDisplayConnectorOrCreate(displayId);
+ final IWallpaperEngine engine = connector != null ? connector.mEngine : null;
+ if (engine != null) {
+ try {
+ engine.setZoomOut(zoom);
+ } catch (RemoteException e) {
+ if (DEBUG) {
+ Slog.w(TAG, "Couldn't set wallpaper zoom", e);
+ }
+ }
+ }
+ }
+ }
+ }
+
@Deprecated
@Override
public ParcelFileDescriptor getWallpaper(String callingPkg, IWallpaperManagerCallback cb,
@@ -3392,6 +3433,9 @@
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ pw.print("mDefaultWallpaperComponent="); pw.println(mDefaultWallpaperComponent);
+ pw.print("mImageWallpaper="); pw.println(mImageWallpaper);
+
synchronized (mLock) {
pw.println("System wallpaper state:");
for (int i = 0; i < mWallpaperMap.size(); i++) {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 4f1d40e..f8df883 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -63,7 +63,6 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ONLY_DRAW_BOTTOM_BAR_BACKGROUND;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
@@ -362,7 +361,6 @@
private static final Rect sTmpRect = new Rect();
private static final Rect sTmpNavFrame = new Rect();
private static final Rect sTmpLastParentFrame = new Rect();
- private static final int[] sTmpTypesAndSides = new int[2];
private WindowState mTopFullscreenOpaqueWindowState;
private WindowState mTopFullscreenOpaqueOrDimmingWindowState;
@@ -888,6 +886,21 @@
// Toasts can't be clickable
attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
break;
+
+ case TYPE_BASE_APPLICATION:
+
+ // A non-translucent main app window isn't allowed to fit insets, as it would create
+ // a hole on the display!
+ if (attrs.isFullscreen() && win.mActivityRecord != null
+ && win.mActivityRecord.fillsParent()
+ && (win.mAttrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0
+ && attrs.getFitInsetsTypes() != 0) {
+ throw new RuntimeException("Illegal attributes: Main activity window that isn't"
+ + " translucent trying to fit insets: "
+ + attrs.getFitInsetsTypes()
+ + " attrs=" + attrs);
+ }
+ break;
}
}
@@ -1247,7 +1260,7 @@
if (layoutInScreenAndInsetDecor && !screenDecor) {
if ((sysUiVis & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
- || (attrs.getFitWindowInsetsTypes() & Type.navigationBars()) == 0) {
+ || (attrs.getFitInsetsTypes() & Type.navigationBars()) == 0) {
outFrame.set(displayFrames.mUnrestricted);
} else {
outFrame.set(displayFrames.mRestricted);
@@ -1300,21 +1313,6 @@
}
}
- private static void getImpliedTypesAndSidesToFit(LayoutParams attrs, int[] typesAndSides) {
- typesAndSides[0] = attrs.getFitWindowInsetsTypes();
- typesAndSides[1] = attrs.getFitWindowInsetsSides();
- final boolean forceDrawsBarBackgrounds =
- (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0
- && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT;
- if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 || forceDrawsBarBackgrounds) {
- if ((attrs.privateFlags & PRIVATE_FLAG_ONLY_DRAW_BOTTOM_BAR_BACKGROUND) != 0) {
- typesAndSides[1] &= ~Side.BOTTOM;
- } else {
- typesAndSides[0] &= ~Type.systemBars();
- }
- }
- }
-
// TODO(b/118118435): remove after migration
private static int getImpliedSysUiFlagsForLayout(LayoutParams attrs) {
int impliedFlags = 0;
@@ -1878,16 +1876,15 @@
sf.set(displayFrames.mStable);
if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
- getImpliedTypesAndSidesToFit(attrs, sTmpTypesAndSides);
- final @InsetsType int typesToFit = sTmpTypesAndSides[0];
- final @InsetsSide int sidesToFit = sTmpTypesAndSides[1];
+ final @InsetsType int typesToFit = attrs.getFitInsetsTypes();
+ final @InsetsSide int sidesToFit = attrs.getFitInsetsSides();
final ArraySet<Integer> types = InsetsState.toInternalType(typesToFit);
final Rect dfu = displayFrames.mUnrestricted;
Insets insets = Insets.of(0, 0, 0, 0);
for (int i = types.size() - 1; i >= 0; i--) {
insets = Insets.max(insets, mDisplayContent.getInsetsPolicy()
.getInsetsForDispatch(win).getSource(types.valueAt(i))
- .calculateInsets(dfu, attrs.getFitIgnoreVisibility()));
+ .calculateInsets(dfu, attrs.isFitInsetsIgnoringVisibility()));
}
final int left = (sidesToFit & Side.LEFT) != 0 ? insets.left : 0;
final int top = (sidesToFit & Side.TOP) != 0 ? insets.top : 0;
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index bef1442..ef6f847 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -190,7 +190,7 @@
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT);
- lp.setFitWindowInsetsTypes(lp.getFitWindowInsetsTypes() & ~Type.statusBars());
+ lp.setFitInsetsTypes(lp.getFitInsetsTypes() & ~Type.statusBars());
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("ImmersiveModeConfirmation");
lp.windowAnimations = com.android.internal.R.style.Animation_ImmersiveModeConfirmation;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d3da500..0b54245 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2004,7 +2004,7 @@
final ActivityStack stack = display.getStackAt(stackNdx);
stack.switchUser(userId);
Task task = stack.getTopMostTask();
- if (task != null) {
+ if (task != null && task != stack) {
stack.positionChildAtTop(task);
}
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 38a7000..828775a 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -52,7 +52,6 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotPersister" : TAG_WM;
private static final String SNAPSHOTS_DIRNAME = "snapshots";
private static final String REDUCED_POSTFIX = "_reduced";
- private static final float REDUCED_SCALE = .5f;
private static final float LOW_RAM_REDUCED_SCALE = .8f;
static final boolean DISABLE_FULL_SIZED_BITMAPS = ActivityManager.isLowRamDeviceStatic();
private static final long DELAY_MS = 100;
@@ -84,8 +83,13 @@
TaskSnapshotPersister(WindowManagerService service, DirectoryResolver resolver) {
mDirectoryResolver = resolver;
- mReducedScale = ActivityManager.isLowRamDeviceStatic()
- ? LOW_RAM_REDUCED_SCALE : REDUCED_SCALE;
+
+ if (ActivityManager.isLowRamDeviceStatic()) {
+ mReducedScale = LOW_RAM_REDUCED_SCALE;
+ } else {
+ mReducedScale = service.mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_reducedTaskSnapshotScale);
+ }
mUse16BitFormat = service.mContext.getResources().getBoolean(
com.android.internal.R.bool.config_use16BitTaskSnapshotPixelFormat);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 81a4c68..8d4ad28 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -71,6 +71,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
+import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
@@ -1367,52 +1368,10 @@
boolean addToastWindowRequiresToken = false;
if (token == null) {
- if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
- ProtoLog.w(WM_ERROR, "Attempted to add application window with unknown token "
- + "%s. Aborting.", attrs.token);
+ if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type,
+ rootType, attrs.token, attrs.packageName)) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
- if (rootType == TYPE_INPUT_METHOD) {
- ProtoLog.w(WM_ERROR, "Attempted to add input method window with unknown token "
- + "%s. Aborting.", attrs.token);
- return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
- }
- if (rootType == TYPE_VOICE_INTERACTION) {
- ProtoLog.w(WM_ERROR,
- "Attempted to add voice interaction window with unknown token "
- + "%s. Aborting.", attrs.token);
- return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
- }
- if (rootType == TYPE_WALLPAPER) {
- ProtoLog.w(WM_ERROR, "Attempted to add wallpaper window with unknown token "
- + "%s. Aborting.", attrs.token);
- return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
- }
- if (rootType == TYPE_DREAM) {
- ProtoLog.w(WM_ERROR, "Attempted to add Dream window with unknown token "
- + "%s. Aborting.", attrs.token);
- return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
- }
- if (rootType == TYPE_QS_DIALOG) {
- ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with unknown token "
- + "%s. Aborting.", attrs.token);
- return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
- }
- if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
- ProtoLog.w(WM_ERROR,
- "Attempted to add Accessibility overlay window with unknown token "
- + "%s. Aborting.", attrs.token);
- return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
- }
- if (type == TYPE_TOAST) {
- // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
- if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
- parentWindow)) {
- ProtoLog.w(WM_ERROR, "Attempted to add a toast window with unknown token "
- + "%s. Aborting.", attrs.token);
- return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
- }
- }
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
final boolean isRoundedCornerOverlay =
(attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
@@ -1697,6 +1656,56 @@
return res;
}
+ private boolean unprivilegedAppCanCreateTokenWith(WindowState parentWindow,
+ int callingUid, int type, int rootType, IBinder tokenForLog, String packageName) {
+ if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
+ ProtoLog.w(WM_ERROR, "Attempted to add application window with unknown token "
+ + "%s. Aborting.", tokenForLog);
+ return false;
+ }
+ if (rootType == TYPE_INPUT_METHOD) {
+ ProtoLog.w(WM_ERROR, "Attempted to add input method window with unknown token "
+ + "%s. Aborting.", tokenForLog);
+ return false;
+ }
+ if (rootType == TYPE_VOICE_INTERACTION) {
+ ProtoLog.w(WM_ERROR,
+ "Attempted to add voice interaction window with unknown token "
+ + "%s. Aborting.", tokenForLog);
+ return false;
+ }
+ if (rootType == TYPE_WALLPAPER) {
+ ProtoLog.w(WM_ERROR, "Attempted to add wallpaper window with unknown token "
+ + "%s. Aborting.", tokenForLog);
+ return false;
+ }
+ if (rootType == TYPE_DREAM) {
+ ProtoLog.w(WM_ERROR, "Attempted to add Dream window with unknown token "
+ + "%s. Aborting.", tokenForLog);
+ return false;
+ }
+ if (rootType == TYPE_QS_DIALOG) {
+ ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with unknown token "
+ + "%s. Aborting.", tokenForLog);
+ return false;
+ }
+ if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
+ ProtoLog.w(WM_ERROR,
+ "Attempted to add Accessibility overlay window with unknown token "
+ + "%s. Aborting.", tokenForLog);
+ return false;
+ }
+ if (type == TYPE_TOAST) {
+ // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
+ if (doesAddToastWindowRequireToken(packageName, callingUid, parentWindow)) {
+ ProtoLog.w(WM_ERROR, "Attempted to add a toast window with unknown token "
+ + "%s. Aborting.", tokenForLog);
+ return false;
+ }
+ }
+ return true;
+ }
+
/**
* Get existing {@link DisplayContent} or create a new one if the display is registered in
* DisplayManager.
@@ -2501,16 +2510,36 @@
@Override
public void addWindowToken(IBinder binder, int type, int displayId) {
- if (!checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()")) {
- throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
+ addWindowContextToken(binder, type, displayId, null);
+ }
+
+ @Override
+ public int addWindowContextToken(IBinder binder, int type, int displayId, String packageName) {
+ final boolean callerCanManageAppTokens =
+ checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()");
+ if (!callerCanManageAppTokens) {
+ // TODO(window-context): refactor checkAddPermission to not take attrs.
+ LayoutParams attrs = new LayoutParams(type);
+ attrs.packageName = packageName;
+ final int res = mPolicy.checkAddPermission(attrs, new int[1]);
+ if (res != ADD_OKAY) {
+ return res;
+ }
}
synchronized (mGlobalLock) {
+ if (!callerCanManageAppTokens) {
+ if (!unprivilegedAppCanCreateTokenWith(null, Binder.getCallingUid(), type, type,
+ null, packageName) || packageName == null) {
+ throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
+ }
+ }
+
final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */);
if (dc == null) {
ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add token: %s"
+ " for non-exiting displayId=%d", binder, displayId);
- return;
+ return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
WindowToken token = dc.getWindowToken(binder);
@@ -2518,15 +2547,28 @@
ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add binder token: %s"
+ " for already created window token: %s"
+ " displayId=%d", binder, token, displayId);
- return;
+ return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
+ // TODO(window-container): Clean up dead tokens
if (type == TYPE_WALLPAPER) {
- new WallpaperWindowToken(this, binder, true, dc,
- true /* ownerCanManageAppTokens */);
+ new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens);
} else {
- new WindowToken(this, binder, type, true, dc, true /* ownerCanManageAppTokens */);
+ new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens);
}
}
+ return WindowManagerGlobal.ADD_OKAY;
+ }
+
+ @Override
+ public boolean isWindowToken(IBinder binder) {
+ synchronized (mGlobalLock) {
+ final WindowToken windowToken = mRoot.getWindowToken(binder);
+ if (windowToken == null) {
+ return false;
+ }
+ // We don't allow activity tokens in WindowContext. TODO(window-context): rename method
+ return windowToken.asActivityRecord() == null;
+ }
}
@Override
@@ -7885,4 +7927,33 @@
return mDisplayWindowSettings.shouldShowImeLocked(displayContent)
|| mForceDesktopModeOnExternalDisplays;
}
+
+ @Override
+ public void getWindowInsets(WindowManager.LayoutParams attrs,
+ int displayId, Rect outContentInsets, Rect outStableInsets,
+ DisplayCutout.ParcelableWrapper displayCutout) {
+ synchronized (mGlobalLock) {
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
+ final WindowToken windowToken = dc.getWindowToken(attrs.token);
+ final ActivityRecord activity;
+ if (windowToken != null && windowToken.asActivityRecord() != null) {
+ activity = windowToken.asActivityRecord();
+ } else {
+ activity = null;
+ }
+ final Rect taskBounds = new Rect();
+ final boolean floatingStack;
+ if (activity != null && activity.getTask() != null) {
+ final Task task = activity.getTask();
+ task.getBounds(taskBounds);
+ floatingStack = task.isFloating();
+ } else {
+ floatingStack = false;
+ }
+ final DisplayFrames displayFrames = dc.mDisplayFrames;
+ final DisplayPolicy policy = dc.getDisplayPolicy();
+ policy.getLayoutHintLw(attrs, taskBounds, displayFrames, floatingStack,
+ new Rect(), outContentInsets, outStableInsets, displayCutout);
+ }
+ }
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index c0891d7..212a3a6 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -312,7 +312,6 @@
void updateInactivityTimeoutLocked();
void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
void ensureSpriteControllerLocked();
- const DisplayViewport* findDisplayViewportLocked(int32_t displayId);
int32_t getPointerDisplayId();
void updatePointerDisplayLocked();
static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
@@ -390,16 +389,6 @@
return false;
}
-const DisplayViewport* NativeInputManager::findDisplayViewportLocked(int32_t displayId)
- REQUIRES(mLock) {
- for (const DisplayViewport& v : mLocked.viewports) {
- if (v.displayId == displayId) {
- return &v;
- }
- }
- return nullptr;
-}
-
void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray) {
std::vector<DisplayViewport> viewports;
@@ -547,6 +536,8 @@
outConfig->setDisplayViewports(mLocked.viewports);
+ outConfig->defaultPointerDisplayId = mLocked.pointerDisplayId;
+
outConfig->disabledDevices = mLocked.disabledInputDevices;
} // release lock
}
@@ -564,8 +555,6 @@
updateInactivityTimeoutLocked();
}
- updatePointerDisplayLocked();
-
return controller;
}
@@ -580,23 +569,6 @@
return pointerDisplayId;
}
-void NativeInputManager::updatePointerDisplayLocked() REQUIRES(mLock) {
- ATRACE_CALL();
-
- sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller != nullptr) {
- const DisplayViewport* viewport = findDisplayViewportLocked(mLocked.pointerDisplayId);
- if (viewport == nullptr) {
- ALOGW("Can't find pointer display viewport, fallback to default display.");
- viewport = findDisplayViewportLocked(ADISPLAY_ID_DEFAULT);
- }
-
- if (viewport != nullptr) {
- controller->setDisplayViewport(*viewport);
- }
- }
-}
-
void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) {
if (mLocked.spriteController == nullptr) {
JNIEnv* env = jniEnv();
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 00436bb..08a7592 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1208,6 +1208,22 @@
translateSingleGnssMeasurement(&(measurement_V2_1->v2_0), object);
SET(BasebandCn0DbHz, measurement_V2_1->basebandCN0DbHz);
+
+ if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_RECEIVER_ISB) {
+ SET(ReceiverInterSignalBiasNs, measurement_V2_1->receiverInterSignalBiasNs);
+ }
+
+ if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_RECEIVER_ISB_UNCERTAINTY) {
+ SET(ReceiverInterSignalBiasUncertaintyNs, measurement_V2_1->receiverInterSignalBiasUncertaintyNs);
+ }
+
+ if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_SATELLITE_ISB) {
+ SET(SatelliteInterSignalBiasNs, measurement_V2_1->satelliteInterSignalBiasNs);
+ }
+
+ if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_SATELLITE_ISB_UNCERTAINTY) {
+ SET(SatelliteInterSignalBiasUncertaintyNs, measurement_V2_1->satelliteInterSignalBiasUncertaintyNs);
+ }
}
template<class T>
@@ -1253,6 +1269,19 @@
template<>
void GnssMeasurementCallback::translateGnssClock(
+ JavaObject& object, const IGnssMeasurementCallback_V2_1::GnssClock& clock) {
+ JNIEnv* env = getJniEnv();
+ SET(ReferenceConstellationTypeForIsb,
+ static_cast<int32_t>(clock.referenceSignalTypeForIsb.constellation));
+ SET(ReferenceCarrierFrequencyHzForIsb, clock.referenceSignalTypeForIsb.carrierFrequencyHz);
+ SET(ReferenceCodeTypeForIsb,
+ env->NewStringUTF(clock.referenceSignalTypeForIsb.codeType.c_str()));
+
+ translateGnssClock(object, clock.v1_0);
+}
+
+template<>
+void GnssMeasurementCallback::translateGnssClock(
JavaObject& object, const IGnssMeasurementCallback_V2_0::GnssData& data) {
auto elapsedRealtime = data.elapsedRealtime;
uint16_t flags = static_cast<uint16_t>(elapsedRealtime.flags);
@@ -1265,6 +1294,20 @@
translateGnssClock(object, data.clock);
}
+template<>
+void GnssMeasurementCallback::translateGnssClock(
+ JavaObject& object, const IGnssMeasurementCallback_V2_1::GnssData& data) {
+ auto elapsedRealtime = data.elapsedRealtime;
+ uint16_t flags = static_cast<uint16_t>(elapsedRealtime.flags);
+ if (flags & ElapsedRealtimeFlags::HAS_TIMESTAMP_NS) {
+ SET(ElapsedRealtimeNanos, static_cast<uint64_t>(elapsedRealtime.timestampNs));
+ }
+ if (flags & ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS) {
+ SET(ElapsedRealtimeUncertaintyNanos, static_cast<double>(elapsedRealtime.timeUncertaintyNs));
+ }
+ translateGnssClock(object, data.clock);
+}
+
template<class T>
jobjectArray GnssMeasurementCallback::translateAllGnssMeasurements(JNIEnv* env,
const T* measurements,
diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp
index 8b2cbbd..b7d6424 100644
--- a/services/core/xsd/Android.bp
+++ b/services/core/xsd/Android.bp
@@ -12,3 +12,11 @@
api_dir: "platform-compat-schema",
package_name: "com.android.server.compat.config",
}
+
+
+xsd_config {
+ name: "display-device-config",
+ srcs: ["display-device-config/display-device-config.xsd"],
+ api_dir: "display-device-config/schema",
+ package_name: "com.android.server.display.config",
+}
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
new file mode 100644
index 0000000..5c7f305
--- /dev/null
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- This defines the format of the XML file generated by
+ ~ com.android.compat.annotation.ChangeIdProcessor annotation processor (from
+ ~ tools/platform-compat), and is parsed in com/android/server/compat/CompatConfig.java.
+-->
+<xs:schema version="2.0"
+ elementFormDefault="qualified"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <xs:element name="displayConfiguration">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element type="nitsMap" name="screenBrightnessMap"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- Type definitions -->
+
+ <xs:complexType name="nitsMap">
+ <xs:sequence>
+ <xs:element name="point" type="point" maxOccurs="unbounded" minOccurs="2"/>
+ <xs:element name="highBrightnessStart" minOccurs="0" type="nonNegativeDecimal"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="point">
+ <xs:sequence>
+ <xs:element type="nonNegativeDecimal" name="value"/>
+ <xs:element type="nonNegativeDecimal" name="nits"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:simpleType name="nonNegativeDecimal">
+ <xs:restriction base="xs:decimal">
+ <xs:minInclusive value="0.0"/>
+ </xs:restriction>
+ </xs:simpleType>
+</xs:schema>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
new file mode 100644
index 0000000..5a9c945
--- /dev/null
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -0,0 +1,33 @@
+// Signature format: 2.0
+package com.android.server.display.config {
+
+ public class DisplayConfiguration {
+ ctor public DisplayConfiguration();
+ method public com.android.server.display.config.NitsMap getScreenBrightnessMap();
+ method public void setScreenBrightnessMap(com.android.server.display.config.NitsMap);
+ }
+
+ public class NitsMap {
+ ctor public NitsMap();
+ method public java.math.BigDecimal getHighBrightnessStart();
+ method public java.util.List<com.android.server.display.config.Point> getPoint();
+ method public void setHighBrightnessStart(java.math.BigDecimal);
+ }
+
+ public class Point {
+ ctor public Point();
+ method public java.math.BigDecimal getNits();
+ method public java.math.BigDecimal getValue();
+ method public void setNits(java.math.BigDecimal);
+ method public void setValue(java.math.BigDecimal);
+ }
+
+ public class XmlParser {
+ ctor public XmlParser();
+ method public static com.android.server.display.config.DisplayConfiguration read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ }
+
+}
+
diff --git a/services/core/xsd/display-device-config/schema/last_current.txt b/services/core/xsd/display-device-config/schema/last_current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/core/xsd/display-device-config/schema/last_current.txt
diff --git a/services/core/xsd/display-device-config/schema/last_removed.txt b/services/core/xsd/display-device-config/schema/last_removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/core/xsd/display-device-config/schema/last_removed.txt
diff --git a/services/core/xsd/display-device-config/schema/removed.txt b/services/core/xsd/display-device-config/schema/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/core/xsd/display-device-config/schema/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 8641059..43ee97d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -68,4 +68,11 @@
public boolean isOrganizationOwnedDeviceWithManagedProfile() {
return false;
}
+
+ public int getPersonalAppsSuspendedReasons(ComponentName admin) {
+ return 0;
+ }
+
+ public void setPersonalAppsSuspended(ComponentName admin, boolean suspended) {
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7830c60..b6953f6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.BIND_DEVICE_ADMIN;
import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
+import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
@@ -126,6 +127,7 @@
import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager.PasswordComplexity;
+import android.app.admin.DevicePolicyManager.PersonalAppSuspensionReason;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DeviceStateCache;
import android.app.admin.FactoryResetProtectionPolicy;
@@ -254,6 +256,7 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.telephony.SmsApplication;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
@@ -375,6 +378,8 @@
private static final String TAG_SECONDARY_LOCK_SCREEN = "secondary-lock-screen";
+ private static final String TAG_PERSONAL_APPS_SUSPENDED = "personal-apps-suspended";
+
private static final int REQUEST_EXPIRE_PASSWORD = 5571;
private static final long MS_PER_DAY = TimeUnit.DAYS.toMillis(1);
@@ -445,6 +450,8 @@
// A collection of user restrictions that are deprecated and should simply be ignored.
private static final Set<String> DEPRECATED_USER_RESTRICTIONS;
private static final String AB_DEVICE_KEY = "ro.build.ab_update";
+ // Permissions related to location which must not be granted automatically
+ private static final Set<String> LOCATION_PERMISSIONS;
static {
SECURE_SETTINGS_WHITELIST = new ArraySet<>();
@@ -489,6 +496,11 @@
DEPRECATED_USER_RESTRICTIONS = Sets.newHashSet(
UserManager.DISALLOW_ADD_MANAGED_PROFILE,
UserManager.DISALLOW_REMOVE_MANAGED_PROFILE);
+
+ LOCATION_PERMISSIONS = Sets.newHashSet(
+ permission.ACCESS_FINE_LOCATION,
+ permission.ACCESS_BACKGROUND_LOCATION,
+ permission.ACCESS_COARSE_LOCATION);
}
/**
@@ -787,6 +799,10 @@
long mPasswordTokenHandle = 0;
+ // Flag reflecting the current state of the personal apps suspension. This flag should
+ // only be written AFTER all the needed apps were suspended or unsuspended.
+ boolean mPersonalAppsSuspended = false;
+
public DevicePolicyData(int userHandle) {
mUserHandle = userHandle;
}
@@ -1016,6 +1032,7 @@
private static final String TAG_CROSS_PROFILE_PACKAGES = "cross-profile-packages";
private static final String TAG_FACTORY_RESET_PROTECTION_POLICY =
"factory_reset_protection_policy";
+ private static final String TAG_SUSPEND_PERSONAL_APPS = "suspend-personal-apps";
DeviceAdminInfo info;
@@ -1138,6 +1155,9 @@
// represented as an empty list.
List<String> mCrossProfilePackages = Collections.emptyList();
+ // Whether the admin explicitly requires personal apps to be suspended
+ boolean mSuspendPersonalApps = false;
+
ActiveAdmin(DeviceAdminInfo _info, boolean parent) {
info = _info;
isParent = parent;
@@ -1347,8 +1367,7 @@
writeTextToXml(out, TAG_ORGANIZATION_NAME, organizationName);
}
if (isLogoutEnabled) {
- writeAttributeValueToXml(
- out, TAG_IS_LOGOUT_ENABLED, isLogoutEnabled);
+ writeAttributeValueToXml(out, TAG_IS_LOGOUT_ENABLED, isLogoutEnabled);
}
if (startUserSessionMessage != null) {
writeTextToXml(out, TAG_START_USER_SESSION_MESSAGE, startUserSessionMessage);
@@ -1369,6 +1388,9 @@
mFactoryResetProtectionPolicy.writeToXml(out);
out.endTag(null, TAG_FACTORY_RESET_PROTECTION_POLICY);
}
+ if (mSuspendPersonalApps) {
+ writeAttributeValueToXml(out, TAG_SUSPEND_PERSONAL_APPS, mSuspendPersonalApps);
+ }
}
void writeTextToXml(XmlSerializer out, String tag, String text) throws IOException {
@@ -1605,6 +1627,9 @@
} else if (TAG_FACTORY_RESET_PROTECTION_POLICY.equals(tag)) {
mFactoryResetProtectionPolicy = FactoryResetProtectionPolicy.readFromXml(
parser);
+ } else if (TAG_SUSPEND_PERSONAL_APPS.equals(tag)) {
+ mSuspendPersonalApps = Boolean.parseBoolean(
+ parser.getAttributeValue(null, ATTR_VALUE));
} else {
Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -3411,6 +3436,13 @@
out.endTag(null, TAG_PROTECTED_PACKAGES);
}
+ if (policy.mPersonalAppsSuspended) {
+ out.startTag(null, TAG_PERSONAL_APPS_SUSPENDED);
+ out.attribute(null, ATTR_VALUE,
+ Boolean.toString(policy.mPersonalAppsSuspended));
+ out.endTag(null, TAG_PERSONAL_APPS_SUSPENDED);
+ }
+
out.endTag(null, "policies");
out.endDocument();
@@ -3627,6 +3659,9 @@
policy.mOwnerInstalledCaCerts.add(parser.getAttributeValue(null, ATTR_ALIAS));
} else if (TAG_PROTECTED_PACKAGES.equals(tag)) {
policy.mProtectedPackages.add(parser.getAttributeValue(null, ATTR_NAME));
+ } else if (TAG_PERSONAL_APPS_SUSPENDED.equals(tag)) {
+ policy.mPersonalAppsSuspended =
+ Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_VALUE));
} else {
Slog.w(LOG_TAG, "Unknown tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -3767,6 +3802,7 @@
synchronized (getLockObject()) {
maybeMigrateToProfileOnOrganizationOwnedDeviceLocked();
}
+ checkPackageSuspensionOnBoot();
break;
case SystemService.PHASE_BOOT_COMPLETED:
ensureDeviceOwnerUserStarted(); // TODO Consider better place to do this.
@@ -3774,6 +3810,34 @@
}
}
+ private void checkPackageSuspensionOnBoot() {
+ int profileUserId = UserHandle.USER_NULL;
+ final boolean shouldSuspend;
+ synchronized (getLockObject()) {
+ for (final int userId : mOwners.getProfileOwnerKeys()) {
+ if (mOwners.isProfileOwnerOfOrganizationOwnedDevice(userId)) {
+ profileUserId = userId;
+ break;
+ }
+ }
+
+ if (profileUserId == UserHandle.USER_NULL) {
+ shouldSuspend = false;
+ } else {
+ shouldSuspend = getProfileOwnerAdminLocked(profileUserId).mSuspendPersonalApps;
+ }
+ }
+
+ final boolean suspended = getUserData(UserHandle.USER_SYSTEM).mPersonalAppsSuspended;
+ if (suspended != shouldSuspend) {
+ suspendPersonalAppsInternal(shouldSuspend, UserHandle.USER_SYSTEM);
+ }
+
+ if (shouldSuspend) {
+ sendPersonalAppsSuspendedNotification(profileUserId);
+ }
+ }
+
private void onLockSettingsReady() {
getUserData(UserHandle.USER_SYSTEM);
loadOwners();
@@ -3886,11 +3950,13 @@
@Override
void handleUnlockUser(int userId) {
startOwnerService(userId, "unlock-user");
+ maybeUpdatePersonalAppsSuspendedNotification(userId);
}
@Override
void handleStopUser(int userId) {
stopOwnerService(userId, "stop-user");
+ maybeUpdatePersonalAppsSuspendedNotification(userId);
}
private void startOwnerService(int userId, String actionForLog) {
@@ -9749,7 +9815,7 @@
}
AccessibilityManager accessibilityManager = getAccessibilityManagerForUser(userId);
enabledServices = accessibilityManager.getEnabledAccessibilityServiceList(
- AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
+ FEEDBACK_ALL_MASK);
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -10671,30 +10737,35 @@
@Override
public boolean setApplicationHidden(ComponentName who, String callerPackage, String packageName,
- boolean hidden) {
- int callingUserId = UserHandle.getCallingUserId();
- boolean result = false;
+ boolean hidden, boolean parent) {
+ final int userId = parent ? getProfileParentId(UserHandle.getCallingUserId())
+ : UserHandle.getCallingUserId();
+ boolean result;
+
synchronized (getLockObject()) {
// Ensure the caller is a DO/PO or a package access delegate.
enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
DELEGATION_PACKAGE_ACCESS);
- long id = mInjector.binderClearCallingIdentity();
- try {
- result = mIPackageManager
- .setApplicationHiddenSettingAsUser(packageName, hidden, callingUserId);
- } catch (RemoteException re) {
- // shouldn't happen
- Slog.e(LOG_TAG, "Failed to setApplicationHiddenSetting", re);
- } finally {
- mInjector.binderRestoreCallingIdentity(id);
+ if (parent) {
+ getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER, parent);
+ // Ensure the package provided is a system package, this is to ensure that this
+ // API cannot be used to leak if certain non-system package exists in the person
+ // profile.
+ mInjector.binderWithCleanCallingIdentity(() ->
+ enforcePackageIsSystemPackage(packageName, hidden, userId));
}
+
+ result = mInjector.binderWithCleanCallingIdentity(() -> mIPackageManager
+ .setApplicationHiddenSettingAsUser(packageName, hidden, userId));
}
final boolean isDelegate = (who == null);
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_APPLICATION_HIDDEN)
.setAdmin(callerPackage)
.setBoolean(isDelegate)
+ .setBoolean(parent)
.setStrings(packageName, hidden ? "hidden" : "not_hidden")
.write();
return result;
@@ -10702,24 +10773,40 @@
@Override
public boolean isApplicationHidden(ComponentName who, String callerPackage,
- String packageName) {
- int callingUserId = UserHandle.getCallingUserId();
+ String packageName, boolean parent) {
+ final int userId = parent ? getProfileParentId(UserHandle.getCallingUserId())
+ : UserHandle.getCallingUserId();
+
synchronized (getLockObject()) {
// Ensure the caller is a DO/PO or a package access delegate.
enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
DELEGATION_PACKAGE_ACCESS);
- long id = mInjector.binderClearCallingIdentity();
- try {
- return mIPackageManager.getApplicationHiddenSettingAsUser(
- packageName, callingUserId);
- } catch (RemoteException re) {
- // shouldn't happen
- Slog.e(LOG_TAG, "Failed to getApplicationHiddenSettingAsUser", re);
- } finally {
- mInjector.binderRestoreCallingIdentity(id);
+ if (parent) {
+ getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER, parent);
+ // Ensure the package provided is a system package.
+ mInjector.binderWithCleanCallingIdentity(() ->
+ enforcePackageIsSystemPackage(packageName, false, userId));
}
- return false;
+
+ return mInjector.binderWithCleanCallingIdentity(
+ () -> mIPackageManager.getApplicationHiddenSettingAsUser(packageName, userId));
+ }
+ }
+
+ private void enforcePackageIsSystemPackage(String packageName, boolean hidden, int userId)
+ throws RemoteException {
+ int flags = PackageManager.MATCH_SYSTEM_ONLY;
+ // If the package is currently hidden then it is considered uninstalled and
+ // the MATCH_UNINSTALLED_PACKAGES flag has to be added.
+ if (!hidden) {
+ flags |= PackageManager.MATCH_UNINSTALLED_PACKAGES;
+ }
+ PackageInfo packageInfo = mIPackageManager.getPackageInfo(packageName, flags, userId);
+ if (packageInfo == null || !packageInfo.applicationInfo.isSystemApp()) {
+ throw new IllegalArgumentException(
+ "The provided package is not a system package");
}
}
@@ -12124,7 +12211,8 @@
LocalDate.now());
}
synchronized (getLockObject()) {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER);
if (policy == null) {
mOwners.clearSystemUpdatePolicy();
} else {
@@ -12133,9 +12221,9 @@
}
mOwners.writeDeviceOwner();
}
- mContext.sendBroadcastAsUser(
+ mInjector.binderWithCleanCallingIdentity(() -> mContext.sendBroadcastAsUser(
new Intent(DevicePolicyManager.ACTION_SYSTEM_UPDATE_POLICY_CHANGED),
- UserHandle.SYSTEM);
+ UserHandle.SYSTEM));
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_SYSTEM_UPDATE_POLICY)
.setAdmin(who)
@@ -12390,6 +12478,14 @@
true);
}
+ // Prevent granting location-related permissions without user consent.
+ if (LOCATION_PERMISSIONS.contains(permission)
+ && grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED
+ && !isUnattendedManagedKioskUnchecked()) {
+ callback.sendResult(null);
+ return;
+ }
+
if (grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED
|| grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED
|| grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT) {
@@ -14789,7 +14885,7 @@
.setAdmin(admin)
.setBoolean(isDeviceAB())
.write();
- enforceDeviceOwner(admin);
+ enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(admin);
mInjector.binderWithCleanCallingIdentity(() -> {
UpdateInstaller updateInstaller;
if (isDeviceAB()) {
@@ -14981,21 +15077,22 @@
}
}
+ private boolean isUnattendedManagedKioskUnchecked() {
+ try {
+ return isManagedKioskInternal()
+ && getPowerManagerInternal().wasDeviceIdleFor(UNATTENDED_MANAGED_KIOSK_MS);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
@Override
public boolean isUnattendedManagedKiosk() {
if (!mHasFeature) {
return false;
}
enforceManageUsers();
- long id = mInjector.binderClearCallingIdentity();
- try {
- return isManagedKioskInternal()
- && getPowerManagerInternal().wasDeviceIdleFor(UNATTENDED_MANAGED_KIOSK_MS);
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
- } finally {
- mInjector.binderRestoreCallingIdentity(id);
- }
+ return mInjector.binderWithCleanCallingIdentity(() -> isUnattendedManagedKioskUnchecked());
}
/**
@@ -15166,4 +15263,121 @@
}
return mInjector.settingsGlobalGetInt(Settings.Global.COMMON_CRITERIA_MODE, 0) != 0;
}
+
+ @Override
+ public @PersonalAppSuspensionReason int getPersonalAppsSuspendedReasons(ComponentName who) {
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER,
+ false /* parent */);
+ // DO shouldn't be able to use this method.
+ enforceProfileOwnerOfOrganizationOwnedDevice(admin);
+ if (admin.mSuspendPersonalApps) {
+ return DevicePolicyManager.PERSONAL_APPS_SUSPENDED_EXPLICITLY;
+ } else {
+ return DevicePolicyManager.PERSONAL_APPS_NOT_SUSPENDED;
+ }
+ }
+ }
+
+ @Override
+ public void setPersonalAppsSuspended(ComponentName who, boolean suspended) {
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER,
+ false /* parent */);
+ // DO shouldn't be able to use this method.
+ enforceProfileOwnerOfOrganizationOwnedDevice(admin);
+ if (admin.mSuspendPersonalApps != suspended) {
+ admin.mSuspendPersonalApps = suspended;
+ saveSettingsLocked(callingUserId);
+ }
+ }
+
+ if (getUserData(UserHandle.USER_SYSTEM).mPersonalAppsSuspended == suspended) {
+ // Admin request matches current state, nothing to do.
+ return;
+ }
+
+ suspendPersonalAppsInternal(suspended, UserHandle.USER_SYSTEM);
+
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ if (suspended) {
+ sendPersonalAppsSuspendedNotification(callingUserId);
+ } else {
+ clearPersonalAppsSuspendedNotification(callingUserId);
+ }
+ });
+ }
+
+ private void suspendPersonalAppsInternal(boolean suspended, int userId) {
+ Slog.i(LOG_TAG, String.format("%s personal apps for user %d",
+ suspended ? "Suspending" : "Unsuspending", userId));
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ try {
+ final String[] appsToSuspend =
+ new PersonalAppsSuspensionHelper(mContext, mInjector.getPackageManager())
+ .getPersonalAppsForSuspension(userId);
+ final String[] failedPackages = mIPackageManager.setPackagesSuspendedAsUser(
+ appsToSuspend, suspended, null, null, null, PLATFORM_PACKAGE_NAME, userId);
+ if (!ArrayUtils.isEmpty(failedPackages)) {
+ Slog.wtf(LOG_TAG, String.format("Failed to %s packages: %s",
+ suspended ? "suspend" : "unsuspend", String.join(",", failedPackages)));
+ }
+ } catch (RemoteException re) {
+ // Shouldn't happen.
+ Slog.e(LOG_TAG, "Failed talking to the package manager", re);
+ }
+ });
+
+ synchronized (getLockObject()) {
+ getUserData(userId).mPersonalAppsSuspended = suspended;
+ saveSettingsLocked(userId);
+ }
+ }
+
+ private void maybeUpdatePersonalAppsSuspendedNotification(int profileUserId) {
+ // TODO(b/147414651): Unless updated, the notification stops working after turning the
+ // profile off and back on, so it has to be updated more often than necessary.
+ if (getUserData(UserHandle.USER_SYSTEM).mPersonalAppsSuspended
+ && getProfileParentId(profileUserId) == UserHandle.USER_SYSTEM) {
+ sendPersonalAppsSuspendedNotification(profileUserId);
+ }
+ }
+
+ private void clearPersonalAppsSuspendedNotification(int userId) {
+ mInjector.binderWithCleanCallingIdentity(() ->
+ mInjector.getNotificationManager().cancel(
+ SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED));
+ }
+
+ private void sendPersonalAppsSuspendedNotification(int userId) {
+ final String profileOwnerPackageName;
+ synchronized (getLockObject()) {
+ profileOwnerPackageName = mOwners.getProfileOwnerComponent(userId).getPackageName();
+ }
+
+ final Intent intent = new Intent(DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE);
+ intent.setPackage(profileOwnerPackageName);
+
+ final PendingIntent pendingIntent = mInjector.pendingIntentGetActivityAsUser(mContext,
+ 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT, null /* options */,
+ UserHandle.of(userId));
+
+ final Notification notification =
+ new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN)
+ .setSmallIcon(android.R.drawable.stat_sys_warning)
+ .setOngoing(true)
+ .setContentTitle(
+ mContext.getString(
+ R.string.personal_apps_suspended_notification_title))
+ .setContentText(mContext.getString(
+ R.string.personal_apps_suspended_notification_text))
+ .setColor(mContext.getColor(R.color.system_notification_accent_color))
+ .setContentIntent(pendingIntent)
+ .build();
+ mInjector.getNotificationManager().notify(
+ SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED, notification);
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
new file mode 100644
index 0000000..180acc8
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.devicepolicy;
+
+import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Slog;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManager;
+import android.view.inputmethod.InputMethodInfo;
+
+import com.android.internal.R;
+import com.android.server.inputmethod.InputMethodManagerInternal;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Utility class to find what personal apps should be suspended to limit personal device use.
+ */
+public class PersonalAppsSuspensionHelper {
+ private static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG;
+
+ private final Context mContext;
+ private final PackageManager mPackageManager;
+
+ public PersonalAppsSuspensionHelper(Context context, PackageManager packageManager) {
+ mContext = context;
+ mPackageManager = packageManager;
+ }
+
+ /**
+ * @return List of packages that should be suspended to limit personal use.
+ */
+ String[] getPersonalAppsForSuspension(@UserIdInt int userId) {
+ final List<PackageInfo> installedPackageInfos =
+ mPackageManager.getInstalledPackagesAsUser(0 /* flags */, userId);
+ final Set<String> result = new HashSet<>();
+ for (final PackageInfo packageInfo : installedPackageInfos) {
+ final ApplicationInfo info = packageInfo.applicationInfo;
+ if ((!info.isSystemApp() && !info.isUpdatedSystemApp())
+ || hasLauncherIntent(packageInfo.packageName)) {
+ result.add(packageInfo.packageName);
+ }
+ }
+ result.removeAll(getCriticalPackages());
+ result.removeAll(getSystemLauncherPackages());
+ result.removeAll(getAccessibilityServices(userId));
+ result.removeAll(getInputMethodPackages(userId));
+ result.remove(getActiveLauncherPackages(userId));
+ result.remove(getDialerPackage(userId));
+ result.remove(getSettingsPackageName(userId));
+
+ Slog.i(LOG_TAG, "Packages subject to suspension: " + String.join(",", result));
+ return result.toArray(new String[0]);
+ }
+
+ private List<String> getSystemLauncherPackages() {
+ final List<String> result = new ArrayList<>();
+ final Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_HOME);
+ final List<ResolveInfo> matchingActivities =
+ mPackageManager.queryIntentActivities(intent, 0);
+ for (final ResolveInfo resolveInfo : matchingActivities) {
+ if (resolveInfo.activityInfo == null
+ || TextUtils.isEmpty(resolveInfo.activityInfo.packageName)) {
+ Slog.wtf(LOG_TAG, "Could not find package name for launcher app" + resolveInfo);
+ continue;
+ }
+ final String packageName = resolveInfo.activityInfo.packageName;
+ try {
+ final ApplicationInfo applicationInfo =
+ mPackageManager.getApplicationInfo(packageName, 0);
+ if (applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp()) {
+ Log.d(LOG_TAG, "Not suspending system launcher package: " + packageName);
+ result.add(packageName);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(LOG_TAG, "Could not find application info for launcher app: " + packageName);
+ }
+ }
+ return result;
+ }
+
+ private List<String> getAccessibilityServices(int userId) {
+ final List<AccessibilityServiceInfo> accessibilityServiceInfos =
+ getAccessibilityManagerForUser(userId)
+ .getEnabledAccessibilityServiceList(FEEDBACK_ALL_MASK);
+ final List<String> result = new ArrayList<>();
+ for (final AccessibilityServiceInfo serviceInfo : accessibilityServiceInfos) {
+ final ComponentName componentName =
+ ComponentName.unflattenFromString(serviceInfo.getId());
+ if (componentName != null) {
+ final String packageName = componentName.getPackageName();
+ Slog.d(LOG_TAG, "Not suspending a11y service: " + packageName);
+ result.add(packageName);
+ }
+ }
+ return result;
+ }
+
+ private List<String> getInputMethodPackages(int userId) {
+ final List<InputMethodInfo> enabledImes =
+ InputMethodManagerInternal.get().getEnabledInputMethodListAsUser(userId);
+ final List<String> result = new ArrayList<>();
+ for (final InputMethodInfo info : enabledImes) {
+ Slog.d(LOG_TAG, "Not suspending IME: " + info.getPackageName());
+ result.add(info.getPackageName());
+ }
+ return result;
+ }
+
+ @Nullable
+ private String getActiveLauncherPackages(int userId) {
+ final Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_HOME);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ return getPackageNameForIntent("active launcher", intent, userId);
+ }
+
+ @Nullable
+ private String getSettingsPackageName(int userId) {
+ final Intent intent = new Intent(Settings.ACTION_SETTINGS);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ return getPackageNameForIntent("settings", intent, userId);
+ }
+
+ @Nullable
+ private String getDialerPackage(int userId) {
+ final Intent intent = new Intent(Intent.ACTION_DIAL);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ return getPackageNameForIntent("dialer", intent, userId);
+ }
+
+ @Nullable
+ private String getPackageNameForIntent(String name, Intent intent, int userId) {
+ final ResolveInfo resolveInfo =
+ mPackageManager.resolveActivityAsUser(intent, /* flags= */ 0, userId);
+ if (resolveInfo != null) {
+ final String packageName = resolveInfo.activityInfo.packageName;
+ Slog.d(LOG_TAG, "Not suspending " + name + " package: " + packageName);
+ return packageName;
+ }
+ return null;
+ }
+
+ private List<String> getCriticalPackages() {
+ final List<String> result = Arrays.asList(mContext.getResources()
+ .getStringArray(R.array.config_packagesExemptFromSuspension));
+ Slog.d(LOG_TAG, "Not suspending critical packages: " + String.join(",", result));
+ return result;
+ }
+
+ private boolean hasLauncherIntent(String packageName) {
+ final Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
+ intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
+ intentToResolve.setPackage(packageName);
+ final List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(
+ intentToResolve, PackageManager.GET_UNINSTALLED_PACKAGES);
+ return resolveInfos != null && !resolveInfos.isEmpty();
+ }
+
+ private AccessibilityManager getAccessibilityManagerForUser(int userId) {
+ final IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
+ final IAccessibilityManager service =
+ iBinder == null ? null : IAccessibilityManager.Stub.asInterface(iBinder);
+ return new AccessibilityManager(mContext, service, userId);
+ }
+}
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index ec56e1e..62ff3a1 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -96,6 +96,7 @@
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.util.FeatureFlagUtils;
import android.util.Pair;
import com.android.internal.backup.IBackupTransport;
@@ -258,6 +259,9 @@
public void tearDown() throws Exception {
ShadowBackupDataInput.reset();
ShadowApplicationPackageManager.reset();
+ // False by default.
+ FeatureFlagUtils.setEnabled(
+ mContext, FeatureFlagUtils.BACKUP_NO_KV_DATA_CHANGE_CALLS, false);
}
@Test
@@ -2344,6 +2348,9 @@
@Test
public void testRunTask_whenNoDataToBackupOnFirstBackup_doesNotTellTransportOfBackup()
throws Exception {
+ FeatureFlagUtils.setEnabled(
+ mContext, FeatureFlagUtils.BACKUP_NO_KV_DATA_CHANGE_CALLS, true);
+
TransportMock transportMock = setUpInitializedTransport(mTransport);
mBackupManagerService.setCurrentToken(0L);
when(transportMock.transport.getCurrentRestoreSet()).thenReturn(1234L);
@@ -2361,6 +2368,9 @@
@Test
public void testRunTask_whenBackupHasCompletedAndThenNoDataChanges_transportGetsNotified()
throws Exception {
+ FeatureFlagUtils.setEnabled(
+ mContext, FeatureFlagUtils.BACKUP_NO_KV_DATA_CHANGE_CALLS, true);
+
TransportMock transportMock = setUpInitializedTransport(mTransport);
when(transportMock.transport.getCurrentRestoreSet()).thenReturn(1234L);
when(transportMock.transport.isAppEligibleForBackup(
diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml
index b3b5af0..0e24b03 100644
--- a/services/tests/mockingservicestests/AndroidManifest.xml
+++ b/services/tests/mockingservicestests/AndroidManifest.xml
@@ -21,6 +21,7 @@
<uses-permission android:name="android.permission.HARDWARE_TEST"/>
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.MANAGE_APPOPS"/>
+ <uses-permission android:name="android.permission.MONITOR_DEVICE_CONFIG_ACCESS"/>
<application android:testOnly="true"
android:debuggable="true">
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index c3602f8..2080fdf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -30,11 +30,14 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.times;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.VersionedPackage;
+import android.os.Bundle;
import android.os.RecoverySystem;
+import android.os.RemoteCallback;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.DeviceConfig;
@@ -44,18 +47,21 @@
import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
import com.android.server.RescueParty.RescuePartyObserver;
import com.android.server.am.SettingsToPropertiesMapper;
-import com.android.server.utils.FlagNamespaceUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
/**
* Test RescueParty.
@@ -69,16 +75,25 @@
private static VersionedPackage sFailingPackage = new VersionedPackage("com.package.name", 1);
private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue";
+ private static final String CALLING_PACKAGE1 = "com.package.name1";
+ private static final String CALLING_PACKAGE2 = "com.package.name2";
+ private static final String NAMESPACE1 = "namespace1";
+ private static final String NAMESPACE2 = "namespace2";
private MockitoSession mSession;
+ private HashMap<String, String> mSystemSettingsMap;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mMockContext;
-
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private PackageWatchdog mMockPackageWatchdog;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private ContentResolver mMockContentResolver;
- private HashMap<String, String> mSystemSettingsMap;
+ @Captor
+ private ArgumentCaptor<RemoteCallback> mMonitorCallbackCaptor;
+ @Captor
+ private ArgumentCaptor<List<String>> mPackageListCaptor;
@Before
public void setUp() throws Exception {
@@ -90,14 +105,17 @@
.spyStatic(SystemProperties.class)
.spyStatic(Settings.Global.class)
.spyStatic(Settings.Secure.class)
+ .spyStatic(Settings.Config.class)
.spyStatic(SettingsToPropertiesMapper.class)
.spyStatic(RecoverySystem.class)
.spyStatic(RescueParty.class)
+ .spyStatic(PackageWatchdog.class)
.startMocking();
mSystemSettingsMap = new HashMap<>();
when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
-
+ // Reset observer instance to get new mock context on every run
+ RescuePartyObserver.reset();
// Mock SystemProperties setter and various getters
doAnswer((Answer<Void>) invocationOnMock -> {
@@ -143,9 +161,11 @@
doAnswer((Answer<Void>) invocationOnMock -> null)
.when(() -> DeviceConfig.resetToDefaults(anyInt(), anyString()));
+ // Mock PackageWatchdog
+ doAnswer((Answer<PackageWatchdog>) invocationOnMock -> mMockPackageWatchdog)
+ .when(() -> PackageWatchdog.getInstance(mMockContext));
doReturn(CURRENT_NETWORK_TIME_MILLIS).when(() -> RescueParty.getElapsedRealtime());
- FlagNamespaceUtils.resetKnownResetNamespacesFlagCounterForTest();
SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL,
Integer.toString(RescueParty.LEVEL_NONE));
@@ -162,19 +182,19 @@
public void testBootLoopDetectionWithExecutionForAllRescueLevels() {
noteBoot();
- verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
+ verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null);
assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
noteBoot();
- verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES);
+ verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null);
assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
noteBoot();
- verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS);
+ verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null);
assertEquals(RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
@@ -189,19 +209,19 @@
public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevels() {
notePersistentAppCrash();
- verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
+ verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null);
assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
notePersistentAppCrash();
- verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES);
+ verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, /*resetNamespaces=*/ null);
assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
notePersistentAppCrash();
- verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS);
+ verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/ null);
assertEquals(RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
@@ -213,6 +233,54 @@
}
@Test
+ public void testNonPersistentAppCrashDetectionWithScopedResets() {
+ RescueParty.onSettingsProviderPublished(mMockContext);
+ verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver),
+ mMonitorCallbackCaptor.capture()));
+
+ // Record DeviceConfig accesses
+ RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
+ RemoteCallback monitorCallback = mMonitorCallbackCaptor.getValue();
+ monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE1));
+ monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE2));
+ monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE2));
+ // Fake DeviceConfig value changes
+ monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE1));
+ verify(mMockPackageWatchdog).startObservingHealth(observer,
+ Arrays.asList(CALLING_PACKAGE1), RescueParty.DEFAULT_OBSERVING_DURATION_MS);
+ monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE2));
+ verify(mMockPackageWatchdog, times(2)).startObservingHealth(eq(observer),
+ mPackageListCaptor.capture(),
+ eq(RescueParty.DEFAULT_OBSERVING_DURATION_MS));
+ assertTrue(mPackageListCaptor.getValue().containsAll(
+ Arrays.asList(CALLING_PACKAGE1, CALLING_PACKAGE2)));
+ // Perform and verify scoped resets
+ final String[] expectedResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2};
+ observer.execute(new VersionedPackage(
+ CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+ verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, expectedResetNamespaces);
+ assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
+ SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
+
+ observer.execute(new VersionedPackage(
+ CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING);
+ verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, expectedResetNamespaces);
+ assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES,
+ SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
+
+ observer.execute(new VersionedPackage(
+ CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING);
+ verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, /*resetNamespaces=*/null);
+ assertEquals(RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS,
+ SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
+
+ observer.execute(new VersionedPackage(
+ CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+ verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
+ assertTrue(RescueParty.isAttemptingFactoryReset());
+ }
+
+ @Test
public void testIsAttemptingFactoryReset() {
for (int i = 0; i < LEVEL_FACTORY_RESET; i++) {
noteBoot();
@@ -227,7 +295,7 @@
RescueParty.onSettingsProviderPublished(mMockContext);
- verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
+ verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null);
assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
}
@@ -244,15 +312,6 @@
FAKE_NATIVE_NAMESPACE1));
verify(() -> DeviceConfig.resetToDefaults(Settings.RESET_MODE_TRUSTED_DEFAULTS,
FAKE_NATIVE_NAMESPACE2));
-
- ExtendedMockito.verify(
- () -> DeviceConfig.setProperty(FlagNamespaceUtils.NAMESPACE_RESCUE_PARTY,
- FlagNamespaceUtils.RESET_PLATFORM_PACKAGE_FLAG + 0,
- FAKE_NATIVE_NAMESPACE1, /*makeDefault=*/true));
- ExtendedMockito.verify(
- () -> DeviceConfig.setProperty(FlagNamespaceUtils.NAMESPACE_RESCUE_PARTY,
- FlagNamespaceUtils.RESET_PLATFORM_PACKAGE_FLAG + 1,
- FAKE_NATIVE_NAMESPACE2, /*makeDefault=*/true));
}
@Test
@@ -326,11 +385,19 @@
RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS);
}
- private void verifySettingsResets(int resetMode) {
+ private void verifySettingsResets(int resetMode, String[] resetNamespaces) {
verify(() -> Settings.Global.resetToDefaultsAsUser(mMockContentResolver, null,
resetMode, UserHandle.USER_SYSTEM));
verify(() -> Settings.Secure.resetToDefaultsAsUser(eq(mMockContentResolver), isNull(),
eq(resetMode), anyInt()));
+ // Verify DeviceConfig resets
+ if (resetNamespaces == null) {
+ verify(() -> DeviceConfig.resetToDefaults(resetMode, /*namespace=*/ null));
+ } else {
+ for (String namespace : resetNamespaces) {
+ verify(() -> DeviceConfig.resetToDefaults(resetMode, namespace));
+ }
+ }
}
private void noteBoot() {
@@ -339,6 +406,22 @@
private void notePersistentAppCrash() {
RescuePartyObserver.getInstance(mMockContext).execute(new VersionedPackage(
- "com.package.name", 1), PackageWatchdog.FAILURE_REASON_UNKNOWN);
+ "com.package.name", 1), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+ }
+
+ private Bundle getConfigAccessBundle(String callingPackage, String namespace) {
+ Bundle result = new Bundle();
+ result.putString(Settings.EXTRA_MONITOR_CALLBACK_TYPE, Settings.EXTRA_ACCESS_CALLBACK);
+ result.putString(Settings.EXTRA_CALLING_PACKAGE, callingPackage);
+ result.putString(Settings.EXTRA_NAMESPACE, namespace);
+ return result;
+ }
+
+ private Bundle getConfigNamespaceUpdateBundle(String updatedNamespace) {
+ Bundle result = new Bundle();
+ result.putString(Settings.EXTRA_MONITOR_CALLBACK_TYPE,
+ Settings.EXTRA_NAMESPACE_UPDATED_CALLBACK);
+ result.putString(Settings.EXTRA_NAMESPACE, updatedNamespace);
+ return result;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java b/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java
index 6f2de7f..07b6556 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java
@@ -22,6 +22,7 @@
import com.google.android.icing.proto.DocumentProto;
import com.google.android.icing.proto.PropertyProto;
import com.google.android.icing.proto.SearchResultProto;
+import com.google.android.icing.proto.StatusProto;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -117,6 +118,7 @@
private static List<String> queryGetUris(FakeIcing icing, String term) {
List<String> uris = new ArrayList<>();
SearchResultProto results = icing.query(term);
+ assertThat(results.getStatus().getCode()).isEqualTo(StatusProto.Code.OK);
for (SearchResultProto.ResultProto result : results.getResultsList()) {
uris.add(result.getDocument().getUri());
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 8f1d0f7..def5b61 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2183,6 +2183,63 @@
assertThat(actualAccounts).containsExactlyElementsIn(expectedAccounts);
}
+ public void testSetApplicationHiddenWithDO() throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ mContext.packageName = admin1.getPackageName();
+ setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
+
+ String packageName = "com.google.android.test";
+
+ dpm.setApplicationHidden(admin1, packageName, true);
+ verify(getServices().ipackageManager).setApplicationHiddenSettingAsUser(packageName,
+ true, UserHandle.USER_SYSTEM);
+
+ dpm.setApplicationHidden(admin1, packageName, false);
+ verify(getServices().ipackageManager).setApplicationHiddenSettingAsUser(packageName,
+ false, UserHandle.USER_SYSTEM);
+
+ verify(getServices().ipackageManager, never()).getPackageInfo(packageName,
+ PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
+ verify(getServices().ipackageManager, never()).getPackageInfo(packageName,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.MATCH_SYSTEM_ONLY,
+ UserHandle.USER_SYSTEM);
+ }
+
+ public void testSetApplicationHiddenWithPOOfOrganizationOwnedDevice() throws Exception {
+ final int MANAGED_PROFILE_USER_ID = DpmMockContext.CALLER_USER_HANDLE;
+ final int MANAGED_PROFILE_ADMIN_UID =
+ UserHandle.getUid(MANAGED_PROFILE_USER_ID, DpmMockContext.SYSTEM_UID);
+ mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+
+ addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
+ configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
+ mContext.packageName = admin1.getPackageName();
+ setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
+
+ String packageName = "com.google.android.test";
+
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+ when(getServices().userManager.getProfileParent(MANAGED_PROFILE_USER_ID))
+ .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
+ when(getServices().ipackageManager.getPackageInfo(packageName,
+ PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM)).thenReturn(
+ packageInfo);
+ when(getServices().ipackageManager.getPackageInfo(packageName,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.MATCH_SYSTEM_ONLY,
+ UserHandle.USER_SYSTEM)).thenReturn(packageInfo);
+
+ parentDpm.setApplicationHidden(admin1, packageName, true);
+ verify(getServices().ipackageManager).setApplicationHiddenSettingAsUser(packageName,
+ true, UserHandle.USER_SYSTEM);
+
+ parentDpm.setApplicationHidden(admin1, packageName, false);
+ verify(getServices().ipackageManager).setApplicationHiddenSettingAsUser(packageName,
+ false, UserHandle.USER_SYSTEM);
+ }
+
public void testGetMacAddress() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index 604efc4..a6af9a9 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -92,7 +92,7 @@
private static final String PACKAGE_NAME = "com.test.app";
private static final int VERSION_CODE = 100;
- private static final String INSTALLER = TEST_FRAMEWORK_PACKAGE;
+ private static final String INSTALLER = "com.long.random.test.installer.name";
// These are obtained by running the test and checking logcat.
private static final String APP_CERT =
"301AA3CB081134501C45F1422ABC66C24224FD5DED5FDC8F17E697176FD866AA";
@@ -100,7 +100,7 @@
"301AA3CB081134501C45F1422ABC66C24224FD5DED5FDC8F17E697176FD866AA";
// We use SHA256 for package names longer than 32 characters.
private static final String INSTALLER_SHA256 =
- "786933C28839603EB48C50B2A688DC6BE52C833627CB2731FF8466A2AE9F94CD";
+ "30F41A7CBF96EE736A54DD6DF759B50ED3CC126ABCEF694E167C324F5976C227";
private static final String PLAY_STORE_PKG = "com.android.vending";
private static final String ADB_INSTALLER = "adb";
@@ -140,7 +140,7 @@
// setup mocks to prevent NPE
when(mMockContext.getPackageManager()).thenReturn(mSpyPackageManager);
when(mMockContext.getResources()).thenReturn(mMockResources);
- when(mMockResources.getStringArray(anyInt())).thenReturn(new String[] {});
+ when(mMockResources.getStringArray(anyInt())).thenReturn(new String[]{});
when(mIntegrityFileManager.initialized()).thenReturn(true);
}
@@ -267,6 +267,8 @@
@Test
public void broadcastReceiverRegistration() throws Exception {
+ whitelistUsAsRuleProvider();
+ makeUsSystemApp();
ArgumentCaptor<IntentFilter> intentFilterCaptor =
ArgumentCaptor.forClass(IntentFilter.class);
@@ -281,6 +283,8 @@
@Test
public void handleBroadcast_correctArgs() throws Exception {
+ whitelistUsAsRuleProvider();
+ makeUsSystemApp();
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
verify(mMockContext)
@@ -314,6 +318,8 @@
@Test
public void handleBroadcast_allow() throws Exception {
+ whitelistUsAsRuleProvider();
+ makeUsSystemApp();
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
verify(mMockContext)
@@ -331,6 +337,8 @@
@Test
public void handleBroadcast_reject() throws Exception {
+ whitelistUsAsRuleProvider();
+ makeUsSystemApp();
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
verify(mMockContext)
@@ -354,6 +362,8 @@
@Test
public void handleBroadcast_notInitialized() throws Exception {
+ whitelistUsAsRuleProvider();
+ makeUsSystemApp();
when(mIntegrityFileManager.initialized()).thenReturn(false);
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
@@ -371,10 +381,30 @@
verify(mSpyPackageManager, never()).getPackageArchiveInfo(any(), anyInt());
}
+ @Test
+ public void verifierAsInstaller_skipIntegrityVerification() throws Exception {
+ whitelistUsAsRuleProvider();
+ makeUsSystemApp();
+ ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(mMockContext)
+ .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
+ Intent intent = makeVerificationIntent(TEST_FRAMEWORK_PACKAGE);
+ when(mRuleEvaluationEngine.evaluate(any(), any())).thenReturn(
+ IntegrityCheckResult.deny(/* rule= */ null));
+
+ broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
+ runJobInHandler();
+
+ verify(mPackageManagerInternal)
+ .setIntegrityVerificationResult(
+ 1, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
+ }
+
private void whitelistUsAsRuleProvider() {
Resources mockResources = mock(Resources.class);
when(mockResources.getStringArray(R.array.config_integrityRuleProviderPackages))
- .thenReturn(new String[] {TEST_FRAMEWORK_PACKAGE});
+ .thenReturn(new String[]{TEST_FRAMEWORK_PACKAGE});
when(mMockContext.getResources()).thenReturn(mockResources);
}
@@ -395,15 +425,28 @@
}
private Intent makeVerificationIntent() throws Exception {
+ PackageInfo packageInfo =
+ mRealContext.getPackageManager().getPackageInfo(TEST_FRAMEWORK_PACKAGE,
+ PackageManager.GET_SIGNATURES);
+ doReturn(packageInfo)
+ .when(mSpyPackageManager)
+ .getPackageInfo(eq(INSTALLER), anyInt());
+ doReturn(1)
+ .when(mSpyPackageManager)
+ .getPackageUid(eq(INSTALLER), anyInt());
+ return makeVerificationIntent(INSTALLER);
+ }
+
+ private Intent makeVerificationIntent(String installer) throws Exception {
Intent intent = new Intent();
intent.setDataAndType(Uri.fromFile(mTestApk), PACKAGE_MIME_TYPE);
intent.setAction(Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION);
intent.putExtra(EXTRA_VERIFICATION_ID, 1);
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, PACKAGE_NAME);
- intent.putExtra(EXTRA_VERIFICATION_INSTALLER_PACKAGE, INSTALLER);
+ intent.putExtra(EXTRA_VERIFICATION_INSTALLER_PACKAGE, installer);
intent.putExtra(
EXTRA_VERIFICATION_INSTALLER_UID,
- mRealContext.getPackageManager().getPackageUid(INSTALLER, /* flags= */ 0));
+ mMockContext.getPackageManager().getPackageUid(installer, /* flags= */ 0));
intent.putExtra(Intent.EXTRA_VERSION_CODE, VERSION_CODE);
return intent;
}
diff --git a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java
new file mode 100644
index 0000000..b0def60
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.lights;
+
+import static android.hardware.lights.LightsRequest.Builder;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.hardware.light.HwLight;
+import android.hardware.light.HwLightState;
+import android.hardware.light.ILights;
+import android.hardware.lights.Light;
+import android.hardware.lights.LightState;
+import android.hardware.lights.LightsManager;
+import android.os.Looper;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LightsServiceTest {
+
+ private final ILights mHal = new ILights.Stub() {
+ @Override
+ public void setLightState(int id, HwLightState state) {
+ return;
+ }
+
+ @Override
+ public HwLight[] getLights() {
+ return new HwLight[] {
+ fakeHwLight(101, 3, 1),
+ fakeHwLight(102, LightsManager.LIGHT_TYPE_MICROPHONE, 4),
+ fakeHwLight(103, LightsManager.LIGHT_TYPE_MICROPHONE, 3),
+ fakeHwLight(104, LightsManager.LIGHT_TYPE_MICROPHONE, 1),
+ fakeHwLight(105, LightsManager.LIGHT_TYPE_MICROPHONE, 2)
+ };
+ }
+ };
+
+ private static HwLight fakeHwLight(int id, int type, int ordinal) {
+ HwLight light = new HwLight();
+ light.id = id;
+ light.type = (byte) type;
+ light.ordinal = ordinal;
+ return light;
+ }
+
+ @Mock
+ Context mContext;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testGetLights_filtersSystemLights() {
+ LightsService service = new LightsService(mContext, mHal, Looper.getMainLooper());
+ LightsManager manager = new LightsManager(mContext, service.mManagerService);
+
+ // When lights are listed, only the 4 MICROPHONE lights should be visible.
+ assertThat(manager.getLights().size()).isEqualTo(4);
+ }
+
+ @Test
+ public void testControlMultipleLights() {
+ LightsService service = new LightsService(mContext, mHal, Looper.getMainLooper());
+ LightsManager manager = new LightsManager(mContext, service.mManagerService);
+
+ // When the session requests to turn 3/4 lights on:
+ LightsManager.LightsSession session = manager.openSession();
+ session.setLights(new Builder()
+ .setLight(manager.getLights().get(0), new LightState(0xf1))
+ .setLight(manager.getLights().get(1), new LightState(0xf2))
+ .setLight(manager.getLights().get(2), new LightState(0xf3))
+ .build());
+
+ // Then all 3 should turn on.
+ assertThat(manager.getLightState(manager.getLights().get(0)).getColor()).isEqualTo(0xf1);
+ assertThat(manager.getLightState(manager.getLights().get(1)).getColor()).isEqualTo(0xf2);
+ assertThat(manager.getLightState(manager.getLights().get(2)).getColor()).isEqualTo(0xf3);
+
+ // And the 4th should remain off.
+ assertThat(manager.getLightState(manager.getLights().get(3)).getColor()).isEqualTo(0x00);
+ }
+
+ @Test
+ public void testControlLights_onlyEffectiveForLifetimeOfClient() {
+ LightsService service = new LightsService(mContext, mHal, Looper.getMainLooper());
+ LightsManager manager = new LightsManager(mContext, service.mManagerService);
+ Light micLight = manager.getLights().get(0);
+
+ // The light should begin by being off.
+ assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0x00000000);
+
+ // When a session commits changes:
+ LightsManager.LightsSession session = manager.openSession();
+ session.setLights(new Builder().setLight(micLight, new LightState(0xff00ff00)).build());
+ // Then the light should turn on.
+ assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0xff00ff00);
+
+ // When the session goes away:
+ session.close();
+ // Then the light should turn off.
+ assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0x00000000);
+ }
+
+ @Test
+ public void testControlLights_firstCallerWinsContention() {
+ LightsService service = new LightsService(mContext, mHal, Looper.getMainLooper());
+ LightsManager manager = new LightsManager(mContext, service.mManagerService);
+ Light micLight = manager.getLights().get(0);
+
+ LightsManager.LightsSession session1 = manager.openSession();
+ LightsManager.LightsSession session2 = manager.openSession();
+
+ // When session1 and session2 both request the same light:
+ session1.setLights(new Builder().setLight(micLight, new LightState(0xff0000ff)).build());
+ session2.setLights(new Builder().setLight(micLight, new LightState(0xffffffff)).build());
+ // Then session1 should win because it was created first.
+ assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0xff0000ff);
+
+ // When session1 goes away:
+ session1.close();
+ // Then session2 should have its request go into effect.
+ assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0xffffffff);
+
+ // When session2 goes away:
+ session2.close();
+ // Then the light should turn off because there are no more sessions.
+ assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0);
+ }
+
+ @Test
+ public void testClearLight() {
+ LightsService service = new LightsService(mContext, mHal, Looper.getMainLooper());
+ LightsManager manager = new LightsManager(mContext, service.mManagerService);
+ Light micLight = manager.getLights().get(0);
+
+ // When the session turns a light on:
+ LightsManager.LightsSession session = manager.openSession();
+ session.setLights(new Builder().setLight(micLight, new LightState(0xffffffff)).build());
+
+ // And then the session clears it again:
+ session.setLights(new Builder().clearLight(micLight).build());
+
+ // Then the light should turn back off.
+ assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 355cada..a1baf0e 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -138,6 +138,7 @@
import android.util.RecurrenceRule;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.test.BroadcastInterceptingContext;
@@ -1055,6 +1056,7 @@
computeLastCycleBoundary(parseTime("2013-01-14T15:11:00.000-08:00"), policy));
}
+ @FlakyTest
@Test
public void testNetworkPolicyAppliedCycleLastMonth() throws Exception {
NetworkState[] state = null;
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
index 15327b6..a8674a8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
@@ -309,6 +309,7 @@
actual.getStagedSessionErrorMessage());
assertEquals(expected.isPrepared(), actual.isPrepared());
assertEquals(expected.isCommitted(), actual.isCommitted());
+ assertEquals(expected.createdMillis, actual.createdMillis);
assertEquals(expected.isSealed(), actual.isSealed());
assertEquals(expected.isMultiPackage(), actual.isMultiPackage());
assertEquals(expected.hasParentSessionId(), actual.hasParentSessionId());
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
index 4a13dce..7af3ec6 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -16,18 +16,18 @@
package com.android.server.usage;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_SYSTEM;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
import android.os.FileUtils;
import android.test.AndroidTestCase;
@@ -37,10 +37,12 @@
File mStorageDir;
- final static String PACKAGE_1 = "com.android.testpackage1";
- final static String PACKAGE_2 = "com.android.testpackage2";
+ private static final String PACKAGE_1 = "com.android.testpackage1";
+ private static final String PACKAGE_2 = "com.android.testpackage2";
+ private static final String PACKAGE_3 = "com.android.testpackage3";
+ private static final String PACKAGE_4 = "com.android.testpackage4";
- final static int USER_ID = 0;
+ private static final int USER_ID = 0;
@Override
protected void setUp() throws Exception {
@@ -100,16 +102,27 @@
aih.setAppStandbyBucket(PACKAGE_2, USER_ID, 2000, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_USAGE);
+ aih.setAppStandbyBucket(PACKAGE_3, USER_ID, 2500, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE);
+ aih.setAppStandbyBucket(PACKAGE_4, USER_ID, 2750, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_USER);
aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 3000, STANDBY_BUCKET_RARE,
REASON_MAIN_TIMEOUT);
assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 3000), STANDBY_BUCKET_RARE);
assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 3000), STANDBY_BUCKET_ACTIVE);
assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000), REASON_MAIN_TIMEOUT);
+ assertEquals(aih.getAppStandbyBucket(PACKAGE_3, USER_ID, 3000), STANDBY_BUCKET_RESTRICTED);
+ assertEquals(aih.getAppStandbyReason(PACKAGE_3, USER_ID, 3000),
+ REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE);
+ assertEquals(aih.getAppStandbyReason(PACKAGE_4, USER_ID, 3000),
+ REASON_MAIN_FORCED_BY_USER);
- // RARE is considered idle
+ // RARE and RESTRICTED are considered idle
assertTrue(aih.isIdle(PACKAGE_1, USER_ID, 3000));
assertFalse(aih.isIdle(PACKAGE_2, USER_ID, 3000));
+ assertTrue(aih.isIdle(PACKAGE_3, USER_ID, 3000));
+ assertTrue(aih.isIdle(PACKAGE_4, USER_ID, 3000));
// Check persistence
aih.writeAppIdleDurations();
@@ -118,6 +131,11 @@
assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 5000), STANDBY_BUCKET_RARE);
assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 5000), STANDBY_BUCKET_ACTIVE);
assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_MAIN_TIMEOUT);
+ assertEquals(aih.getAppStandbyBucket(PACKAGE_3, USER_ID, 3000), STANDBY_BUCKET_RESTRICTED);
+ assertEquals(aih.getAppStandbyReason(PACKAGE_3, USER_ID, 3000),
+ REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE);
+ assertEquals(aih.getAppStandbyReason(PACKAGE_4, USER_ID, 3000),
+ REASON_MAIN_FORCED_BY_USER);
assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
assertFalse(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 6aca58f..03dc213 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -28,11 +28,17 @@
import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYNC_ADAPTER;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_INTERACTION;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
import static org.junit.Assert.assertEquals;
@@ -46,6 +52,8 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import android.annotation.NonNull;
+import android.app.ActivityManager;
import android.app.usage.AppStandbyInfo;
import android.app.usage.UsageEvents;
import android.appwidget.AppWidgetManager;
@@ -124,6 +132,13 @@
public PackageManager getPackageManager() {
return mockPm;
}
+
+ public Object getSystemService(@NonNull String name) {
+ if (Context.ACTIVITY_SERVICE.equals(name)) {
+ return mock(ActivityManager.class);
+ }
+ return super.getSystemService(name);
+ }
}
static class MyInjector extends AppStandbyController.Injector {
@@ -253,8 +268,11 @@
doReturn(packages).when(mockPm).getInstalledPackagesAsUser(anyInt(), anyInt());
try {
+ doReturn(UID_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_1), anyInt());
doReturn(UID_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_1), anyInt(), anyInt());
doReturn(UID_EXEMPTED_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_EXEMPTED_1),
+ anyInt());
+ doReturn(UID_EXEMPTED_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_EXEMPTED_1),
anyInt(), anyInt());
doReturn(pi.applicationInfo).when(mockPm).getApplicationInfo(eq(pi.packageName),
anyInt());
@@ -468,7 +486,7 @@
}
@Test
- public void testPredictionTimedout() throws Exception {
+ public void testPredictionTimedOut() throws Exception {
// Set it to timeout or usage, so that prediction can override it
mInjector.mElapsedRealtime = HOUR_MS;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
@@ -532,6 +550,79 @@
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
REASON_MAIN_PREDICTED);
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
+
+ // Prediction can't remove from RESTRICTED
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_USER);
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+ REASON_MAIN_PREDICTED);
+ assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_SYSTEM);
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+ REASON_MAIN_PREDICTED);
+ assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+
+ // Force from user can remove from RESTRICTED
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_USER);
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+ REASON_MAIN_FORCED_BY_USER);
+ assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_SYSTEM);
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+ REASON_MAIN_FORCED_BY_USER);
+ assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+
+ // Force from system can remove from RESTRICTED if it was put it in due to system
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_SYSTEM);
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+ REASON_MAIN_FORCED_BY_SYSTEM);
+ assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_USER);
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+ REASON_MAIN_FORCED_BY_SYSTEM);
+ assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_PREDICTED);
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+ REASON_MAIN_FORCED_BY_SYSTEM);
+ assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+
+ // Non-user usage can't remove from RESTRICTED
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_SYSTEM);
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+ REASON_MAIN_USAGE);
+ assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+ REASON_MAIN_USAGE | REASON_SUB_USAGE_SYSTEM_INTERACTION);
+ assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+ REASON_MAIN_USAGE | REASON_SUB_USAGE_SYNC_ADAPTER);
+ assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+ REASON_MAIN_USAGE | REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE);
+ assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+
+ // Explicit user usage can remove from RESTRICTED
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_USER);
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+ REASON_MAIN_USAGE | REASON_SUB_USAGE_USER_INTERACTION);
+ assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_SYSTEM);
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+ REASON_MAIN_USAGE | REASON_SUB_USAGE_MOVE_TO_FOREGROUND);
+ assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
}
@Test
@@ -556,6 +647,55 @@
assertBucket(STANDBY_BUCKET_RARE);
}
+ /**
+ * Test that setAppStandbyBucket to RESTRICTED doesn't change the bucket until the usage
+ * timeout has passed.
+ */
+ @Test
+ public void testTimeoutBeforeRestricted() throws Exception {
+ reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
+ assertBucket(STANDBY_BUCKET_ACTIVE);
+
+ mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_SYSTEM);
+ // Bucket shouldn't change
+ assertBucket(STANDBY_BUCKET_ACTIVE);
+
+ // bucketing works after timeout
+ mInjector.mElapsedRealtime += DAY_MS;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_SYSTEM);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+
+ // Way past all timeouts. Make sure timeout processing doesn't raise bucket.
+ mInjector.mElapsedRealtime += RARE_THRESHOLD * 4;
+ mController.checkIdleStates(USER_ID);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+ }
+
+ /**
+ * Test that an app is put into the RESTRICTED bucket after enough time has passed.
+ */
+ @Test
+ public void testRestrictedDelay() throws Exception {
+ reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
+ assertBucket(STANDBY_BUCKET_ACTIVE);
+
+ mInjector.mElapsedRealtime += mInjector.getRestrictedBucketDelayMs() - 5000;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_SYSTEM);
+ // Bucket shouldn't change
+ assertBucket(STANDBY_BUCKET_ACTIVE);
+
+ // bucketing works after timeout
+ mInjector.mElapsedRealtime += 6000;
+
+ Thread.sleep(6000);
+ // Enough time has passed. The app should automatically be put into the RESTRICTED bucket.
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+ }
+
@Test
public void testCascadingTimeouts() throws Exception {
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 1e55b15..587cfbf 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -74,7 +74,7 @@
import com.android.internal.util.IntPair;
import com.android.server.UiServiceTestCase;
-import com.android.server.lights.Light;
+import com.android.server.lights.LogicalLight;
import org.junit.Before;
import org.junit.Test;
@@ -91,7 +91,7 @@
@Mock AudioManager mAudioManager;
@Mock Vibrator mVibrator;
@Mock android.media.IRingtonePlayer mRingtonePlayer;
- @Mock Light mLight;
+ @Mock LogicalLight mLight;
@Mock
NotificationManagerService.WorkerHandler mHandler;
@Mock
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index a9fe1a6..da0e03d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -117,6 +117,7 @@
assertEquals(getSmartReplies(key, i), ranking.getSmartReplies());
assertEquals(canBubble(i), ranking.canBubble());
assertEquals(visuallyInterruptive(i), ranking.visuallyInterruptive());
+ assertEquals(isConversation(i), ranking.isConversation());
}
}
@@ -184,7 +185,8 @@
(ArrayList) tweak.getSmartActions(),
(ArrayList) tweak.getSmartReplies(),
tweak.canBubble(),
- tweak.visuallyInterruptive()
+ tweak.visuallyInterruptive(),
+ tweak.isConversation()
);
assertNotEquals(nru, nru2);
}
@@ -261,7 +263,8 @@
getSmartActions(key, i),
getSmartReplies(key, i),
canBubble(i),
- visuallyInterruptive(i)
+ visuallyInterruptive(i),
+ isConversation(i)
);
rankings[i] = ranking;
}
@@ -370,6 +373,10 @@
return index % 4 == 0;
}
+ private boolean isConversation(int index) {
+ return index % 4 == 0;
+ }
+
private void assertActionsEqual(
List<Notification.Action> expecteds, List<Notification.Action> actuals) {
assertEquals(expecteds.size(), actuals.size());
@@ -403,6 +410,7 @@
assertEquals(comment, a.isNoisy(), b.isNoisy());
assertEquals(comment, a.getSmartReplies(), b.getSmartReplies());
assertEquals(comment, a.canBubble(), b.canBubble());
+ assertEquals(comment, a.isConversation(), b.isConversation());
assertActionsEqual(a.getSmartActions(), b.getSmartActions());
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 9260fbf..93e09df 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -153,8 +153,8 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiServiceTestCase;
-import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
+import com.android.server.lights.LogicalLight;
import com.android.server.notification.NotificationManagerService.NotificationAssistants;
import com.android.server.notification.NotificationManagerService.NotificationListeners;
import com.android.server.uri.UriGrantsManagerInternal;
@@ -372,7 +372,7 @@
});
when(mPackageManagerClient.getPackageUidAsUser(any(), anyInt())).thenReturn(mUid);
final LightsManager mockLightsManager = mock(LightsManager.class);
- when(mockLightsManager.getLight(anyInt())).thenReturn(mock(Light.class));
+ when(mockLightsManager.getLight(anyInt())).thenReturn(mock(LogicalLight.class));
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index fab6b7f..2d4b5a7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -20,6 +20,7 @@
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.service.notification.Adjustment.KEY_IMPORTANCE;
+import static android.service.notification.Adjustment.KEY_NOT_CONVERSATION;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
@@ -38,12 +39,15 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.NotificationChannel;
import android.app.NotificationManager;
+import android.app.Person;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -58,6 +62,7 @@
import android.provider.Settings;
import android.service.notification.Adjustment;
import android.service.notification.StatusBarNotification;
+import android.util.FeatureFlagUtils;
import android.widget.RemoteViews;
import androidx.test.filters.SmallTest;
@@ -83,6 +88,7 @@
private final Context mMockContext = mock(Context.class);
@Mock private PackageManager mPm;
+ @Mock private ContentResolver mContentResolver;
private final String pkg = PKG_N_MR1;
private final int uid = 9583;
@@ -116,6 +122,9 @@
when(mMockContext.getResources()).thenReturn(getContext().getResources());
when(mMockContext.getPackageManager()).thenReturn(mPm);
+ when(mMockContext.getContentResolver()).thenReturn(mContentResolver);
+ Settings.Global.putString(mContentResolver,
+ FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false");
ApplicationInfo appInfo = new ApplicationInfo();
appInfo.targetSdkVersion = Build.VERSION_CODES.O;
when(mMockContext.getApplicationInfo()).thenReturn(appInfo);
@@ -194,6 +203,21 @@
return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid);
}
+ private StatusBarNotification getMessagingStyleNotification(@Nullable String shortcutId) {
+ final Builder builder = new Builder(mMockContext)
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+ Person person = new Person.Builder().setName("Bob").build();
+ builder.setStyle(new Notification.MessagingStyle(person));
+ if (shortcutId != null) {
+ builder.setShortcutId(shortcutId);
+ }
+
+ Notification n = builder.build();
+ return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid);
+ }
+
//
// Tests
//
@@ -1095,4 +1119,55 @@
assertTrue("false negative detection", record.hasUndecoratedRemoteView());
}
+
+ @Test
+ public void testIsConversation() {
+ StatusBarNotification sbn = getMessagingStyleNotification("test_shortcut_id");
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+ assertTrue(record.isConversation());
+ }
+
+ @Test
+ public void testIsConversation_nullShortcutId() {
+ StatusBarNotification sbn = getMessagingStyleNotification(null);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+ assertFalse(record.isConversation());
+ }
+
+ @Test
+ public void testIsConversation_bypassShortcutFlagEnabled() {
+ Settings.Global.putString(mContentResolver,
+ FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true");
+ StatusBarNotification sbn = getMessagingStyleNotification(null);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+ assertTrue(record.isConversation());
+ }
+
+ @Test
+ public void testIsConversation_channelDemoted() {
+ StatusBarNotification sbn = getMessagingStyleNotification("test_shortcut_id");
+ channel.setDemoted(true);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+ assertFalse(record.isConversation());
+ }
+
+ @Test
+ public void testIsConversation_withAdjustmentOverride() {
+ StatusBarNotification sbn = getMessagingStyleNotification("test_shortcut_id");
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(KEY_NOT_CONVERSATION, true);
+ Adjustment adjustment = new Adjustment(
+ PKG_O, record.getKey(), bundle, "", record.getUser().getIdentifier());
+
+ record.addAdjustment(adjustment);
+ record.applyAdjustments();
+
+ assertFalse(record.isConversation());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 0527561..5ba676d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -36,7 +36,6 @@
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ONLY_DRAW_BOTTOM_BAR_BACKGROUND;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -141,7 +140,7 @@
public void layoutWindowLw_fitStatusBars() {
assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
- mWindow.mAttrs.setFitWindowInsetsTypes(Type.statusBars());
+ mWindow.mAttrs.setFitInsetsTypes(Type.statusBars());
addWindow(mWindow);
mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -159,7 +158,7 @@
public void layoutWindowLw_fitNavigationBars() {
assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
- mWindow.mAttrs.setFitWindowInsetsTypes(Type.navigationBars());
+ mWindow.mAttrs.setFitInsetsTypes(Type.navigationBars());
addWindow(mWindow);
mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -178,7 +177,7 @@
assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
mWindow.mAttrs.flags = FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- mWindow.mAttrs.setFitWindowInsetsTypes(Type.systemBars());
+ mWindow.mAttrs.setFitInsetsTypes(Type.systemBars());
addWindow(mWindow);
mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -197,7 +196,7 @@
assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
mWindow.mAttrs.privateFlags = PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
- mWindow.mAttrs.setFitWindowInsetsTypes(Type.systemBars());
+ mWindow.mAttrs.setFitInsetsTypes(Type.systemBars());
addWindow(mWindow);
mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -216,8 +215,7 @@
assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
mWindow.mAttrs.flags = FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- mWindow.mAttrs.privateFlags = PRIVATE_FLAG_ONLY_DRAW_BOTTOM_BAR_BACKGROUND;
- mWindow.mAttrs.setFitWindowInsetsTypes(Type.systemBars());
+ mWindow.mAttrs.setFitInsetsTypes(Type.systemBars());
addWindow(mWindow);
mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -235,7 +233,7 @@
public void layoutWindowLw_fitAllSides() {
assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
- mWindow.mAttrs.setFitWindowInsetsSides(Side.all());
+ mWindow.mAttrs.setFitInsetsSides(Side.all());
addWindow(mWindow);
mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -253,7 +251,7 @@
public void layoutWindowLw_fitTopOnly() {
assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
- mWindow.mAttrs.setFitWindowInsetsSides(Side.TOP);
+ mWindow.mAttrs.setFitInsetsSides(Side.TOP);
addWindow(mWindow);
mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -275,7 +273,7 @@
mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow);
state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false);
- mWindow.mAttrs.setFitIgnoreVisibility(true);
+ mWindow.mAttrs.setFitInsetsIgnoringVisibility(true);
addWindow(mWindow);
mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -297,7 +295,7 @@
mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow);
state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false);
- mWindow.mAttrs.setFitIgnoreVisibility(false);
+ mWindow.mAttrs.setFitInsetsIgnoringVisibility(false);
addWindow(mWindow);
mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -374,7 +372,7 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
- mWindow.mAttrs.setFitWindowInsetsTypes(0 /* types */);
+ mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
addWindow(mWindow);
mDisplayPolicy.beginLayoutLw(mFrames, 0 /* uiMode */);
@@ -431,8 +429,8 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- mWindow.mAttrs.setFitWindowInsetsTypes(
- mWindow.mAttrs.getFitWindowInsetsTypes() & ~Type.statusBars());
+ mWindow.mAttrs.setFitInsetsTypes(
+ mWindow.mAttrs.getFitInsetsTypes() & ~Type.statusBars());
addWindow(mWindow);
mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -537,8 +535,8 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- mWindow.mAttrs.setFitWindowInsetsTypes(
- mWindow.mAttrs.getFitWindowInsetsTypes() & ~Type.statusBars());
+ mWindow.mAttrs.setFitInsetsTypes(
+ mWindow.mAttrs.getFitInsetsTypes() & ~Type.statusBars());
addWindow(mWindow);
mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -556,7 +554,7 @@
addDisplayCutout();
mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN;
- mWindow.mAttrs.setFitWindowInsetsTypes(Type.systemBars() & ~Type.statusBars());
+ mWindow.mAttrs.setFitInsetsTypes(Type.systemBars() & ~Type.statusBars());
mWindow.mAttrs.type = TYPE_APPLICATION_OVERLAY;
mWindow.mAttrs.width = DISPLAY_WIDTH;
mWindow.mAttrs.height = DISPLAY_HEIGHT;
@@ -577,8 +575,8 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- mWindow.mAttrs.setFitWindowInsetsTypes(
- mWindow.mAttrs.getFitWindowInsetsTypes() & ~Type.statusBars());
+ mWindow.mAttrs.setFitInsetsTypes(
+ mWindow.mAttrs.getFitInsetsTypes() & ~Type.statusBars());
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
addWindow(mWindow);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index e699b52..c370d6c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -28,6 +28,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -233,6 +234,16 @@
assertNotEquals(0, toast.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED);
}
+ @Test(expected = RuntimeException.class)
+ public void testMainAppWindowDisallowFitSystemWindowTypes() {
+ final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
+ final WindowState activity = createBaseApplicationWindow();
+ activity.mAttrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
+
+ policy.adjustWindowParamsLw(activity, activity.mAttrs, 0 /* callingPid */,
+ 0 /* callingUid */);
+ }
+
private WindowState createToastWindow() {
final WindowState win = createWindow(null, TYPE_TOAST, "Toast");
final WindowManager.LayoutParams attrs = win.mAttrs;
@@ -254,6 +265,17 @@
return win;
}
+ private WindowState createBaseApplicationWindow() {
+ final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "Application");
+ final WindowManager.LayoutParams attrs = win.mAttrs;
+ attrs.width = MATCH_PARENT;
+ attrs.height = MATCH_PARENT;
+ attrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
+ attrs.format = PixelFormat.OPAQUE;
+ win.mHasSurface = true;
+ return win;
+ }
+
@Test
@FlakyTest(bugId = 131005232)
public void testOverlappingWithNavBar() {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index b8cd378..5119e58 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -369,7 +369,7 @@
/**
* Fetches a map (package_name:install_time) of installed packages for the given user. This
* map contains all installed packages, including those packages which have been uninstalled
- * with the DONT_DELETE_DATA flag.
+ * with the DELETE_KEEP_DATA flag.
* This is a helper method which should only be called when the given user's usage stats service
* is initialized; it performs a heavy query to package manager so do not call it otherwise.
* <br/>
diff --git a/services/usb/Android.bp b/services/usb/Android.bp
index d2c973a..a9474c1 100644
--- a/services/usb/Android.bp
+++ b/services/usb/Android.bp
@@ -19,5 +19,6 @@
"android.hardware.usb-V1.1-java",
"android.hardware.usb-V1.2-java",
"android.hardware.usb.gadget-V1.0-java",
+ "android.hardware.usb.gadget-V1.1-java",
],
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 9f3b07b..6407ec7 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -161,6 +161,7 @@
private static final int MSG_GET_CURRENT_USB_FUNCTIONS = 16;
private static final int MSG_FUNCTION_SWITCH_TIMEOUT = 17;
private static final int MSG_GADGET_HAL_REGISTERED = 18;
+ private static final int MSG_RESET_USB_GADGET = 19;
private static final int AUDIO_MODE_SOURCE = 1;
@@ -1846,6 +1847,23 @@
}
}
break;
+ case MSG_RESET_USB_GADGET:
+ synchronized (mGadgetProxyLock) {
+ if (mGadgetProxy == null) {
+ Slog.e(TAG, "reset Usb Gadget mGadgetProxy is null");
+ break;
+ }
+
+ try {
+ android.hardware.usb.gadget.V1_1.IUsbGadget gadgetProxy =
+ android.hardware.usb.gadget.V1_1.IUsbGadget
+ .castFrom(mGadgetProxy);
+ gadgetProxy.reset();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "reset Usb Gadget failed", e);
+ }
+ }
+ break;
default:
super.handleMessage(msg);
}
@@ -2054,6 +2072,17 @@
mHandler.sendMessage(MSG_SET_SCREEN_UNLOCKED_FUNCTIONS, functions);
}
+ /**
+ * Resets the USB Gadget.
+ */
+ public void resetUsbGadget() {
+ if (DEBUG) {
+ Slog.d(TAG, "reset Usb Gadget");
+ }
+
+ mHandler.sendMessage(MSG_RESET_USB_GADGET, null);
+ }
+
private void onAdbEnabled(boolean enabled) {
mHandler.sendMessage(MSG_ENABLE_ADB, enabled);
}
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 0a03f14..61f2c50 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -638,6 +638,19 @@
}
@Override
+ public void resetUsbGadget() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+ Preconditions.checkNotNull(mDeviceManager, "DeviceManager must not be null");
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mDeviceManager.resetUsbGadget();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public List<ParcelableUsbPort> getPorts() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
index c58b6da..af81ab6 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -46,18 +46,21 @@
private static final String NAME = "sound_model.db";
private static final int VERSION = 7;
- public static interface SoundModelContract {
- public static final String TABLE = "sound_model";
- public static final String KEY_MODEL_UUID = "model_uuid";
- public static final String KEY_VENDOR_UUID = "vendor_uuid";
- public static final String KEY_KEYPHRASE_ID = "keyphrase_id";
- public static final String KEY_TYPE = "type";
- public static final String KEY_DATA = "data";
- public static final String KEY_RECOGNITION_MODES = "recognition_modes";
- public static final String KEY_LOCALE = "locale";
- public static final String KEY_HINT_TEXT = "hint_text";
- public static final String KEY_USERS = "users";
- public static final String KEY_MODEL_VERSION = "model_version";
+ /**
+ * Keyphrase sound model database columns
+ */
+ public interface SoundModelContract {
+ String TABLE = "sound_model";
+ String KEY_MODEL_UUID = "model_uuid";
+ String KEY_VENDOR_UUID = "vendor_uuid";
+ String KEY_KEYPHRASE_ID = "keyphrase_id";
+ String KEY_TYPE = "type";
+ String KEY_DATA = "data";
+ String KEY_RECOGNITION_MODES = "recognition_modes";
+ String KEY_LOCALE = "locale";
+ String KEY_HINT_TEXT = "hint_text";
+ String KEY_USERS = "users";
+ String KEY_MODEL_VERSION = "model_version";
}
// Table Create Statement
@@ -173,7 +176,8 @@
soundModel.keyphrases[0].recognitionModes);
values.put(SoundModelContract.KEY_USERS,
getCommaSeparatedString(soundModel.keyphrases[0].users));
- values.put(SoundModelContract.KEY_LOCALE, soundModel.keyphrases[0].locale);
+ values.put(SoundModelContract.KEY_LOCALE,
+ soundModel.keyphrases[0].locale.toLanguageTag());
values.put(SoundModelContract.KEY_HINT_TEXT, soundModel.keyphrases[0].text);
try {
return db.insertWithOnConflict(SoundModelContract.TABLE, null, values,
@@ -190,7 +194,7 @@
* Deletes the sound model and associated keyphrases.
*/
public boolean deleteKeyphraseSoundModel(int keyphraseId, int userHandle, String bcp47Locale) {
- // Sanitize the locale to guard against SQL injection.
+ // Normalize the locale to guard against SQL injection.
bcp47Locale = Locale.forLanguageTag(bcp47Locale).toLanguageTag();
synchronized(this) {
KeyphraseSoundModel soundModel = getKeyphraseSoundModel(keyphraseId, userHandle,
@@ -226,92 +230,119 @@
String selectQuery = "SELECT * FROM " + SoundModelContract.TABLE
+ " WHERE " + SoundModelContract.KEY_KEYPHRASE_ID + "= '" + keyphraseId
+ "' AND " + SoundModelContract.KEY_LOCALE + "='" + bcp47Locale + "'";
- SQLiteDatabase db = getReadableDatabase();
- Cursor c = db.rawQuery(selectQuery, null);
-
- try {
- if (c.moveToFirst()) {
- do {
- int type = c.getInt(c.getColumnIndex(SoundModelContract.KEY_TYPE));
- if (type != SoundTrigger.SoundModel.TYPE_KEYPHRASE) {
- if (DBG) {
- Slog.w(TAG, "Ignoring SoundModel since it's type is incorrect");
- }
- continue;
- }
-
- String modelUuid = c.getString(
- c.getColumnIndex(SoundModelContract.KEY_MODEL_UUID));
- if (modelUuid == null) {
- Slog.w(TAG, "Ignoring SoundModel since it doesn't specify an ID");
- continue;
- }
-
- String vendorUuidString = null;
- int vendorUuidColumn = c.getColumnIndex(SoundModelContract.KEY_VENDOR_UUID);
- if (vendorUuidColumn != -1) {
- vendorUuidString = c.getString(vendorUuidColumn);
- }
- byte[] data = c.getBlob(c.getColumnIndex(SoundModelContract.KEY_DATA));
- int recognitionModes = c.getInt(
- c.getColumnIndex(SoundModelContract.KEY_RECOGNITION_MODES));
- int[] users = getArrayForCommaSeparatedString(
- c.getString(c.getColumnIndex(SoundModelContract.KEY_USERS)));
- String modelLocale = c.getString(
- c.getColumnIndex(SoundModelContract.KEY_LOCALE));
- String text = c.getString(
- c.getColumnIndex(SoundModelContract.KEY_HINT_TEXT));
- int version = c.getInt(
- c.getColumnIndex(SoundModelContract.KEY_MODEL_VERSION));
-
- // Only add keyphrases meant for the current user.
- if (users == null) {
- // No users present in the keyphrase.
- Slog.w(TAG, "Ignoring SoundModel since it doesn't specify users");
- continue;
- }
-
- boolean isAvailableForCurrentUser = false;
- for (int user : users) {
- if (userHandle == user) {
- isAvailableForCurrentUser = true;
- break;
- }
- }
- if (!isAvailableForCurrentUser) {
- if (DBG) {
- Slog.w(TAG, "Ignoring SoundModel since user handles don't match");
- }
- continue;
- } else {
- if (DBG) Slog.d(TAG, "Found a SoundModel for user: " + userHandle);
- }
-
- Keyphrase[] keyphrases = new Keyphrase[1];
- keyphrases[0] = new Keyphrase(
- keyphraseId, recognitionModes, modelLocale, text, users);
- UUID vendorUuid = null;
- if (vendorUuidString != null) {
- vendorUuid = UUID.fromString(vendorUuidString);
- }
- KeyphraseSoundModel model = new KeyphraseSoundModel(
- UUID.fromString(modelUuid), vendorUuid, data, keyphrases, version);
- if (DBG) {
- Slog.d(TAG, "Found SoundModel for the given keyphrase/locale/user: "
- + model);
- }
- return model;
- } while (c.moveToNext());
- }
- Slog.w(TAG, "No SoundModel available for the given keyphrase");
- } finally {
- c.close();
- db.close();
- }
- return null;
+ return getValidKeyphraseSoundModelForUser(selectQuery, userHandle);
}
}
+ /**
+ * Returns a matching {@link KeyphraseSoundModel} for the keyphrase string.
+ * Returns null if a match isn't found.
+ *
+ * TODO: We only support one keyphrase currently.
+ */
+ public KeyphraseSoundModel getKeyphraseSoundModel(String keyphrase, int userHandle,
+ String bcp47Locale) {
+ // Sanitize the locale to guard against SQL injection.
+ bcp47Locale = Locale.forLanguageTag(bcp47Locale).toLanguageTag();
+ synchronized (this) {
+ // Find the corresponding sound model ID for the keyphrase.
+ String selectQuery = "SELECT * FROM " + SoundModelContract.TABLE
+ + " WHERE " + SoundModelContract.KEY_HINT_TEXT + "= '" + keyphrase
+ + "' AND " + SoundModelContract.KEY_LOCALE + "='" + bcp47Locale + "'";
+ return getValidKeyphraseSoundModelForUser(selectQuery, userHandle);
+ }
+ }
+
+ private KeyphraseSoundModel getValidKeyphraseSoundModelForUser(String selectQuery,
+ int userHandle) {
+ SQLiteDatabase db = getReadableDatabase();
+ Cursor c = db.rawQuery(selectQuery, null);
+
+ try {
+ if (c.moveToFirst()) {
+ do {
+ int type = c.getInt(c.getColumnIndex(SoundModelContract.KEY_TYPE));
+ if (type != SoundTrigger.SoundModel.TYPE_KEYPHRASE) {
+ if (DBG) {
+ Slog.w(TAG, "Ignoring SoundModel since its type is incorrect");
+ }
+ continue;
+ }
+
+ String modelUuid = c.getString(
+ c.getColumnIndex(SoundModelContract.KEY_MODEL_UUID));
+ if (modelUuid == null) {
+ Slog.w(TAG, "Ignoring SoundModel since it doesn't specify an ID");
+ continue;
+ }
+
+ String vendorUuidString = null;
+ int vendorUuidColumn = c.getColumnIndex(SoundModelContract.KEY_VENDOR_UUID);
+ if (vendorUuidColumn != -1) {
+ vendorUuidString = c.getString(vendorUuidColumn);
+ }
+ int keyphraseId = c.getInt(
+ c.getColumnIndex(SoundModelContract.KEY_KEYPHRASE_ID));
+ byte[] data = c.getBlob(c.getColumnIndex(SoundModelContract.KEY_DATA));
+ int recognitionModes = c.getInt(
+ c.getColumnIndex(SoundModelContract.KEY_RECOGNITION_MODES));
+ int[] users = getArrayForCommaSeparatedString(
+ c.getString(c.getColumnIndex(SoundModelContract.KEY_USERS)));
+ Locale modelLocale = Locale.forLanguageTag(c.getString(
+ c.getColumnIndex(SoundModelContract.KEY_LOCALE)));
+ String text = c.getString(
+ c.getColumnIndex(SoundModelContract.KEY_HINT_TEXT));
+ int version = c.getInt(
+ c.getColumnIndex(SoundModelContract.KEY_MODEL_VERSION));
+
+ // Only add keyphrases meant for the current user.
+ if (users == null) {
+ // No users present in the keyphrase.
+ Slog.w(TAG, "Ignoring SoundModel since it doesn't specify users");
+ continue;
+ }
+
+ boolean isAvailableForCurrentUser = false;
+ for (int user : users) {
+ if (userHandle == user) {
+ isAvailableForCurrentUser = true;
+ break;
+ }
+ }
+ if (!isAvailableForCurrentUser) {
+ if (DBG) {
+ Slog.w(TAG, "Ignoring SoundModel since user handles don't match");
+ }
+ continue;
+ } else {
+ if (DBG) Slog.d(TAG, "Found a SoundModel for user: " + userHandle);
+ }
+
+ Keyphrase[] keyphrases = new Keyphrase[1];
+ keyphrases[0] = new Keyphrase(
+ keyphraseId, recognitionModes, modelLocale, text, users);
+ UUID vendorUuid = null;
+ if (vendorUuidString != null) {
+ vendorUuid = UUID.fromString(vendorUuidString);
+ }
+ KeyphraseSoundModel model = new KeyphraseSoundModel(
+ UUID.fromString(modelUuid), vendorUuid, data, keyphrases, version);
+ if (DBG) {
+ Slog.d(TAG, "Found SoundModel for the given keyphrase/locale/user: "
+ + model);
+ }
+ return model;
+ } while (c.moveToNext());
+ }
+ Slog.w(TAG, "No SoundModel available for the given keyphrase");
+ } finally {
+ c.close();
+ db.close();
+ }
+
+ return null;
+ }
+
private static String getCommaSeparatedString(int[] users) {
if (users == null) {
return "";
@@ -431,8 +462,11 @@
}
}
+ /**
+ * Dumps contents of database for dumpsys
+ */
public void dump(PrintWriter pw) {
- synchronized(this) {
+ synchronized (this) {
String selectQuery = "SELECT * FROM " + SoundModelContract.TABLE;
SQLiteDatabase db = getReadableDatabase();
Cursor c = db.rawQuery(selectQuery, null);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 506c67e..d5eec33 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -41,7 +41,9 @@
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
+import android.hardware.soundtrigger.KeyphraseMetadata;
import android.hardware.soundtrigger.ModelParams;
+import android.hardware.soundtrigger.SoundTrigger;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
@@ -90,6 +92,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;
+import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -923,6 +926,8 @@
}
//----------------- Model management APIs --------------------------------//
+ // TODO: add check to only allow active voice interaction service or keyphrase enrollment
+ // application to manage voice models
@Override
public KeyphraseSoundModel getKeyphraseSoundModel(int keyphraseId, String bcp47Locale) {
@@ -1022,6 +1027,41 @@
}
}
+ @Nullable
+ public KeyphraseMetadata getEnrolledKeyphraseMetadata(IVoiceInteractionService service,
+ String keyphrase, String bcp47Locale) {
+ synchronized (this) {
+ enforceIsCurrentVoiceInteractionService(service);
+ }
+
+ if (bcp47Locale == null) {
+ throw new IllegalArgumentException("Illegal argument(s) in isEnrolledForKeyphrase");
+ }
+
+ final int callingUid = UserHandle.getCallingUserId();
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ KeyphraseSoundModel model =
+ mDbHelper.getKeyphraseSoundModel(keyphrase, callingUid, bcp47Locale);
+ if (model == null) {
+ return null;
+ }
+
+ for (SoundTrigger.Keyphrase phrase : model.keyphrases) {
+ if (keyphrase.equals(phrase.text)) {
+ ArraySet<Locale> locales = new ArraySet<>();
+ locales.add(phrase.locale);
+ return new KeyphraseMetadata(phrase.id, phrase.text, locales,
+ phrase.recognitionModes);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+
+ return null;
+ }
+
@Override
public ModuleProperties getDspModuleProperties(IVoiceInteractionService service) {
// Allow the call if this is the current voice interaction service.
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 826a89e..acf51f3 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -547,8 +547,14 @@
*/
public static final int PROPERTY_VOIP_AUDIO_MODE = 0x00001000;
+ /**
+ * Indicates that the call is an adhoc conference call. This property can be set for both
+ * incoming and outgoing calls.
+ */
+ public static final int PROPERTY_IS_ADHOC_CONFERENCE = 0x00002000;
+
//******************************************************************************************
- // Next PROPERTY value: 0x00002000
+ // Next PROPERTY value: 0x00004000
//******************************************************************************************
private final String mTelecomCallId;
@@ -726,6 +732,9 @@
if (hasProperty(properties, PROPERTY_VOIP_AUDIO_MODE)) {
builder.append(" PROPERTY_VOIP_AUDIO_MODE");
}
+ if (hasProperty(properties, PROPERTY_IS_ADHOC_CONFERENCE)) {
+ builder.append(" PROPERTY_IS_ADHOC_CONFERENCE");
+ }
builder.append("]");
return builder.toString();
}
diff --git a/telecomm/java/android/telecom/CallerInfoAsyncQuery.java b/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
index 4a50e98..4a81a8e 100644
--- a/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
+++ b/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
@@ -17,6 +17,7 @@
package android.telecom;
import android.app.ActivityManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.Context;
@@ -35,8 +36,6 @@
import android.telephony.SubscriptionManager;
import android.text.TextUtils;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import java.util.ArrayList;
import java.util.List;
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 456290c..6b0845f 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -69,6 +69,7 @@
public void onConnectionEvent(Conference c, String event, Bundle extras) {}
public void onCallerDisplayNameChanged(
Conference c, String callerDisplayName, int presentation) {}
+ public void onRingbackRequested(Conference c, boolean ringback) {}
}
private final Set<Listener> mListeners = new CopyOnWriteArraySet<>();
@@ -97,6 +98,7 @@
private int mAddressPresentation;
private String mCallerDisplayName;
private int mCallerDisplayNamePresentation;
+ private boolean mRingbackRequested = false;
private final Connection.Listener mConnectionDeathListener = new Connection.Listener() {
@Override
@@ -170,6 +172,14 @@
}
/**
+ * Returns whether this conference is requesting that the system play a ringback tone
+ * on its behalf.
+ */
+ public final boolean isRingbackRequested() {
+ return mRingbackRequested;
+ }
+
+ /**
* Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class
* {@link Connection} for valid values.
*
@@ -308,6 +318,35 @@
public void onConnectionAdded(Connection connection) {}
/**
+ * Notifies this Conference, which is in {@code STATE_RINGING}, of
+ * a request to accept.
+ * For managed {@link ConnectionService}s, this will be called when the user answers a call via
+ * the default dialer's {@link InCallService}.
+ *
+ * @param videoState The video state in which to answer the connection.
+ */
+ public void onAnswer(int videoState) {}
+
+ /**
+ * Notifies this Conference, which is in {@code STATE_RINGING}, of
+ * a request to accept.
+ * For managed {@link ConnectionService}s, this will be called when the user answers a call via
+ * the default dialer's {@link InCallService}.
+ * @hide
+ */
+ public final void onAnswer() {
+ onAnswer(VideoProfile.STATE_AUDIO_ONLY);
+ }
+
+ /**
+ * Notifies this Conference, which is in {@code STATE_RINGING}, of
+ * a request to reject.
+ * For managed {@link ConnectionService}s, this will be called when the user rejects a call via
+ * the default dialer's {@link InCallService}.
+ */
+ public void onReject() {}
+
+ /**
* Sets state to be on hold.
*/
public final void setOnHold() {
@@ -322,9 +361,17 @@
}
/**
+ * Sets state to be ringing.
+ */
+ public final void setRinging() {
+ setState(Connection.STATE_RINGING);
+ }
+
+ /**
* Sets state to be active.
*/
public final void setActive() {
+ setRingbackRequested(false);
setState(Connection.STATE_ACTIVE);
}
@@ -436,6 +483,21 @@
}
/**
+ * Requests that the framework play a ringback tone. This is to be invoked by implementations
+ * that do not play a ringback tone themselves in the conference's audio stream.
+ *
+ * @param ringback Whether the ringback tone is to be played.
+ */
+ public final void setRingbackRequested(boolean ringback) {
+ if (mRingbackRequested != ringback) {
+ mRingbackRequested = ringback;
+ for (Listener l : mListeners) {
+ l.onRingbackRequested(this, ringback);
+ }
+ }
+ }
+
+ /**
* Set the video state for the conference.
* Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
* {@link VideoProfile#STATE_BIDIRECTIONAL},
@@ -640,14 +702,6 @@
}
private void setState(int newState) {
- if (newState != Connection.STATE_ACTIVE &&
- newState != Connection.STATE_HOLDING &&
- newState != Connection.STATE_DISCONNECTED) {
- Log.w(this, "Unsupported state transition for Conference call.",
- Connection.stateToString(newState));
- return;
- }
-
if (mState != newState) {
int oldState = mState;
mState = newState;
@@ -657,6 +711,37 @@
}
}
+ private static class FailureSignalingConference extends Conference {
+ private boolean mImmutable = false;
+ public FailureSignalingConference(DisconnectCause disconnectCause,
+ PhoneAccountHandle phoneAccount) {
+ super(phoneAccount);
+ setDisconnected(disconnectCause);
+ mImmutable = true;
+ }
+ public void checkImmutable() {
+ if (mImmutable) {
+ throw new UnsupportedOperationException("Conference is immutable");
+ }
+ }
+ }
+
+ /**
+ * Return a {@code Conference} which represents a failed conference attempt. The returned
+ * {@code Conference} will have a {@link android.telecom.DisconnectCause} and as specified,
+ * and a {@link #getState()} of {@code STATE_DISCONNECTED}.
+ * <p>
+ * The returned {@code Conference} can be assumed to {@link #destroy()} itself when appropriate,
+ * so users of this method need not maintain a reference to its return value to destroy it.
+ *
+ * @param disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}).
+ * @return A {@code Conference} which indicates failure.
+ */
+ public @NonNull static Conference createFailedConference(
+ @NonNull DisconnectCause disconnectCause, @NonNull PhoneAccountHandle phoneAccount) {
+ return new FailureSignalingConference(disconnectCause, phoneAccount);
+ }
+
private final void clearConferenceableList() {
for (Connection c : mConferenceableConnections) {
c.removeConnectionListener(mConnectionDeathListener);
@@ -667,11 +752,13 @@
@Override
public String toString() {
return String.format(Locale.US,
- "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s, ThisObject %s]",
+ "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s,"
+ + "isRingbackRequested: %s, ThisObject %s]",
Connection.stateToString(mState),
Call.Details.capabilitiesToString(mConnectionCapabilities),
getVideoState(),
getVideoProvider(),
+ isRingbackRequested() ? "Y" : "N",
super.toString());
}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index f205ec6..c934625 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -497,8 +497,17 @@
@TestApi
public static final int PROPERTY_REMOTELY_HOSTED = 1 << 11;
+ /**
+ * Set by the framework to indicate that it is an adhoc conference call.
+ * <p>
+ * This is used for Outgoing and incoming conference calls.
+ *
+ */
+ public static final int PROPERTY_IS_ADHOC_CONFERENCE = 1 << 12;
+
+
//**********************************************************************************************
- // Next PROPERTY value: 1<<12
+ // Next PROPERTY value: 1<<13
//**********************************************************************************************
/**
@@ -1018,6 +1027,10 @@
builder.append(isLong ? " PROPERTY_REMOTELY_HOSTED" : " remote_hst");
}
+ if ((properties & PROPERTY_IS_ADHOC_CONFERENCE) == PROPERTY_IS_ADHOC_CONFERENCE) {
+ builder.append(isLong ? " PROPERTY_IS_ADHOC_CONFERENCE" : " adhoc_conf");
+ }
+
builder.append("]");
return builder.toString();
}
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index 221f8f1..6d7ceca 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -26,6 +26,9 @@
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Simple data container encapsulating a request to some entity to
* create a new {@link Connection}.
@@ -46,6 +49,8 @@
private boolean mShouldShowIncomingCallUi = false;
private ParcelFileDescriptor mRttPipeToInCall;
private ParcelFileDescriptor mRttPipeFromInCall;
+ private List<Uri> mParticipants;
+ private boolean mIsAdhocConference = false;
public Builder() { }
@@ -59,6 +64,15 @@
}
/**
+ * Sets the participants for the resulting {@link ConnectionRequest}
+ * @param participants The participants to which the {@link Connection} is to connect.
+ */
+ public @NonNull Builder setParticipants(@Nullable List<Uri> participants) {
+ this.mParticipants = participants;
+ return this;
+ }
+
+ /**
* Sets the address for the resulting {@link ConnectionRequest}
* @param address The address(e.g., phone number) to which the {@link Connection} is to
* connect.
@@ -108,6 +122,16 @@
}
/**
+ * Sets isAdhocConference for the resulting {@link ConnectionRequest}
+ * @param isAdhocConference {@code true} if it is a adhoc conference call
+ * {@code false}, if not a adhoc conference call
+ */
+ public @NonNull Builder setIsAdhocConferenceCall(boolean isAdhocConference) {
+ this.mIsAdhocConference = isAdhocConference;
+ return this;
+ }
+
+ /**
* Sets the RTT pipe for transferring text into the {@link ConnectionService} for the
* resulting {@link ConnectionRequest}
* @param rttPipeFromInCall The data pipe to read from.
@@ -141,7 +165,9 @@
mTelecomCallId,
mShouldShowIncomingCallUi,
mRttPipeFromInCall,
- mRttPipeToInCall);
+ mRttPipeToInCall,
+ mParticipants,
+ mIsAdhocConference);
}
}
@@ -155,6 +181,8 @@
private final ParcelFileDescriptor mRttPipeFromInCall;
// Cached return value of getRttTextStream -- we don't want to wrap it more than once.
private Connection.RttTextStream mRttTextStream;
+ private List<Uri> mParticipants;
+ private final boolean mIsAdhocConference;
/**
* @param accountHandle The accountHandle which should be used to place the call.
@@ -214,6 +242,21 @@
boolean shouldShowIncomingCallUi,
ParcelFileDescriptor rttPipeFromInCall,
ParcelFileDescriptor rttPipeToInCall) {
+ this(accountHandle, handle, extras, videoState, telecomCallId,
+ shouldShowIncomingCallUi, rttPipeFromInCall, rttPipeToInCall, null, false);
+ }
+
+ private ConnectionRequest(
+ PhoneAccountHandle accountHandle,
+ Uri handle,
+ Bundle extras,
+ int videoState,
+ String telecomCallId,
+ boolean shouldShowIncomingCallUi,
+ ParcelFileDescriptor rttPipeFromInCall,
+ ParcelFileDescriptor rttPipeToInCall,
+ List<Uri> participants,
+ boolean isAdhocConference) {
mAccountHandle = accountHandle;
mAddress = handle;
mExtras = extras;
@@ -222,6 +265,8 @@
mShouldShowIncomingCallUi = shouldShowIncomingCallUi;
mRttPipeFromInCall = rttPipeFromInCall;
mRttPipeToInCall = rttPipeToInCall;
+ mParticipants = participants;
+ mIsAdhocConference = isAdhocConference;
}
private ConnectionRequest(Parcel in) {
@@ -233,6 +278,11 @@
mShouldShowIncomingCallUi = in.readInt() == 1;
mRttPipeFromInCall = in.readParcelable(getClass().getClassLoader());
mRttPipeToInCall = in.readParcelable(getClass().getClassLoader());
+
+ mParticipants = new ArrayList<Uri>();
+ in.readList(mParticipants, getClass().getClassLoader());
+
+ mIsAdhocConference = in.readInt() == 1;
}
/**
@@ -246,6 +296,11 @@
public Uri getAddress() { return mAddress; }
/**
+ * The participants to which the {@link Connection} is to connect.
+ */
+ public @Nullable List<Uri> getParticipants() { return mParticipants; }
+
+ /**
* Application-specific extra data. Used for passing back information from an incoming
* call {@code Intent}, and for any proprietary extensions arranged between a client
* and servant {@code ConnectionService} which agree on a vocabulary for such data.
@@ -290,6 +345,13 @@
}
/**
+ * @return {@code true} if the call is a adhoc conference call else @return {@code false}
+ */
+ public boolean isAdhocConferenceCall() {
+ return mIsAdhocConference;
+ }
+
+ /**
* Gets the {@link ParcelFileDescriptor} that is used to send RTT text from the connection
* service to the in-call UI. In order to obtain an
* {@link java.io.InputStream} from this {@link ParcelFileDescriptor}, use
@@ -345,11 +407,12 @@
@Override
public String toString() {
- return String.format("ConnectionRequest %s %s",
+ return String.format("ConnectionRequest %s %s isAdhocConf: %s",
mAddress == null
? Uri.EMPTY
: Connection.toLogSafePhoneNumber(mAddress.toString()),
- bundleToString(mExtras));
+ bundleToString(mExtras),
+ isAdhocConferenceCall() ? "Y" : "N");
}
private static String bundleToString(Bundle extras){
@@ -406,5 +469,7 @@
destination.writeInt(mShouldShowIncomingCallUi ? 1 : 0);
destination.writeParcelable(mRttPipeFromInCall, 0);
destination.writeParcelable(mRttPipeToInCall, 0);
+ destination.writeList(mParticipants);
+ destination.writeInt(mIsAdhocConference ? 1 : 0);
}
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 3a0494e..440f044 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -154,6 +154,9 @@
private static final String SESSION_CONNECTION_SERVICE_FOCUS_LOST = "CS.cSFL";
private static final String SESSION_CONNECTION_SERVICE_FOCUS_GAINED = "CS.cSFG";
private static final String SESSION_HANDOVER_FAILED = "CS.haF";
+ private static final String SESSION_CREATE_CONF = "CS.crConf";
+ private static final String SESSION_CREATE_CONF_COMPLETE = "CS.crConfC";
+ private static final String SESSION_CREATE_CONF_FAILED = "CS.crConfF";
private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1;
private static final int MSG_CREATE_CONNECTION = 2;
@@ -188,6 +191,9 @@
private static final int MSG_HANDOVER_FAILED = 32;
private static final int MSG_HANDOVER_COMPLETE = 33;
private static final int MSG_DEFLECT = 34;
+ private static final int MSG_CREATE_CONFERENCE = 35;
+ private static final int MSG_CREATE_CONFERENCE_COMPLETE = 36;
+ private static final int MSG_CREATE_CONFERENCE_FAILED = 37;
private static Connection sNullConnection;
@@ -291,6 +297,63 @@
}
@Override
+ public void createConference(
+ PhoneAccountHandle connectionManagerPhoneAccount,
+ String id,
+ ConnectionRequest request,
+ boolean isIncoming,
+ boolean isUnknown,
+ Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_CREATE_CONF);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = connectionManagerPhoneAccount;
+ args.arg2 = id;
+ args.arg3 = request;
+ args.arg4 = Log.createSubsession();
+ args.argi1 = isIncoming ? 1 : 0;
+ args.argi2 = isUnknown ? 1 : 0;
+ mHandler.obtainMessage(MSG_CREATE_CONFERENCE, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void createConferenceComplete(String id, Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_CREATE_CONF_COMPLETE);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = id;
+ args.arg2 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_CREATE_CONFERENCE_COMPLETE, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void createConferenceFailed(
+ PhoneAccountHandle connectionManagerPhoneAccount,
+ String callId,
+ ConnectionRequest request,
+ boolean isIncoming,
+ Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_CREATE_CONF_FAILED);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = request;
+ args.arg3 = Log.createSubsession();
+ args.arg4 = connectionManagerPhoneAccount;
+ args.argi1 = isIncoming ? 1 : 0;
+ mHandler.obtainMessage(MSG_CREATE_CONFERENCE_FAILED, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
public void handoverFailed(String callId, ConnectionRequest request, int reason,
Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_HANDOVER_FAILED);
@@ -802,6 +865,106 @@
}
break;
}
+ case MSG_CREATE_CONFERENCE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg4, SESSION_HANDLER + SESSION_CREATE_CONN);
+ try {
+ final PhoneAccountHandle connectionManagerPhoneAccount =
+ (PhoneAccountHandle) args.arg1;
+ final String id = (String) args.arg2;
+ final ConnectionRequest request = (ConnectionRequest) args.arg3;
+ final boolean isIncoming = args.argi1 == 1;
+ final boolean isUnknown = args.argi2 == 1;
+ if (!mAreAccountsInitialized) {
+ Log.d(this, "Enqueueing pre-initconference request %s", id);
+ mPreInitializationConnectionRequests.add(
+ new android.telecom.Logging.Runnable(
+ SESSION_HANDLER + SESSION_CREATE_CONF + ".pIConfR",
+ null /*lock*/) {
+ @Override
+ public void loggedRun() {
+ createConference(connectionManagerPhoneAccount,
+ id,
+ request,
+ isIncoming,
+ isUnknown);
+ }
+ }.prepare());
+ } else {
+ createConference(connectionManagerPhoneAccount,
+ id,
+ request,
+ isIncoming,
+ isUnknown);
+ }
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
+ case MSG_CREATE_CONFERENCE_COMPLETE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg2,
+ SESSION_HANDLER + SESSION_CREATE_CONN_COMPLETE);
+ try {
+ final String id = (String) args.arg1;
+ if (!mAreAccountsInitialized) {
+ Log.d(this, "Enqueueing pre-init conference request %s", id);
+ mPreInitializationConnectionRequests.add(
+ new android.telecom.Logging.Runnable(
+ SESSION_HANDLER + SESSION_CREATE_CONF_COMPLETE
+ + ".pIConfR",
+ null /*lock*/) {
+ @Override
+ public void loggedRun() {
+ notifyCreateConferenceComplete(id);
+ }
+ }.prepare());
+ } else {
+ notifyCreateConferenceComplete(id);
+ }
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
+ case MSG_CREATE_CONFERENCE_FAILED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg3, SESSION_HANDLER +
+ SESSION_CREATE_CONN_FAILED);
+ try {
+ final String id = (String) args.arg1;
+ final ConnectionRequest request = (ConnectionRequest) args.arg2;
+ final boolean isIncoming = args.argi1 == 1;
+ final PhoneAccountHandle connectionMgrPhoneAccount =
+ (PhoneAccountHandle) args.arg4;
+ if (!mAreAccountsInitialized) {
+ Log.d(this, "Enqueueing pre-init conference request %s", id);
+ mPreInitializationConnectionRequests.add(
+ new android.telecom.Logging.Runnable(
+ SESSION_HANDLER + SESSION_CREATE_CONF_FAILED
+ + ".pIConfR",
+ null /*lock*/) {
+ @Override
+ public void loggedRun() {
+ createConferenceFailed(connectionMgrPhoneAccount, id,
+ request, isIncoming);
+ }
+ }.prepare());
+ } else {
+ Log.i(this, "createConferenceFailed %s", id);
+ createConferenceFailed(connectionMgrPhoneAccount, id, request,
+ isIncoming);
+ }
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
+
case MSG_HANDOVER_FAILED: {
SomeArgs args = (SomeArgs) msg.obj;
Log.continueSession((Session) args.arg3, SESSION_HANDLER +
@@ -1162,6 +1325,12 @@
public void onStateChanged(Conference conference, int oldState, int newState) {
String id = mIdByConference.get(conference);
switch (newState) {
+ case Connection.STATE_RINGING:
+ mAdapter.setRinging(id);
+ break;
+ case Connection.STATE_DIALING:
+ mAdapter.setDialing(id);
+ break;
case Connection.STATE_ACTIVE:
mAdapter.setActive(id);
break;
@@ -1292,6 +1461,13 @@
mAdapter.onConnectionEvent(id, event, extras);
}
}
+
+ @Override
+ public void onRingbackRequested(Conference c, boolean ringback) {
+ String id = mIdByConference.get(c);
+ Log.d(this, "Adapter conference onRingback %b", ringback);
+ mAdapter.setRingbackRequested(id, ringback);
+ }
};
private final Connection.Listener mConnectionListener = new Connection.Listener() {
@@ -1534,6 +1710,70 @@
return super.onUnbind(intent);
}
+
+ /**
+ * This can be used by telecom to either create a new outgoing conference call or attach
+ * to an existing incoming conference call. In either case, telecom will cycle through a
+ * set of services and call createConference until a connection service cancels the process
+ * or completes it successfully.
+ */
+ private void createConference(
+ final PhoneAccountHandle callManagerAccount,
+ final String callId,
+ final ConnectionRequest request,
+ boolean isIncoming,
+ boolean isUnknown) {
+
+ Conference conference = null;
+ conference = isIncoming ? onCreateIncomingConference(callManagerAccount, request)
+ : onCreateOutgoingConference(callManagerAccount, request);
+
+ Log.d(this, "createConference, conference: %s", conference);
+ if (conference == null) {
+ Log.i(this, "createConference, implementation returned null conference.");
+ conference = Conference.createFailedConference(
+ new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONFERENCE"),
+ request.getAccountHandle());
+ }
+ if (conference.getExtras() != null) {
+ conference.getExtras().putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId);
+ }
+ mConferenceById.put(callId, conference);
+ mIdByConference.put(conference, callId);
+ conference.addListener(mConferenceListener);
+ ParcelableConference parcelableConference = new ParcelableConference(
+ request.getAccountHandle(),
+ conference.getState(),
+ conference.getConnectionCapabilities(),
+ conference.getConnectionProperties(),
+ Collections.<String>emptyList(), //connectionIds
+ conference.getVideoProvider() == null ?
+ null : conference.getVideoProvider().getInterface(),
+ conference.getVideoState(),
+ conference.getConnectTimeMillis(),
+ conference.getConnectionStartElapsedRealTime(),
+ conference.getStatusHints(),
+ conference.getExtras(),
+ conference.getAddress(),
+ conference.getAddressPresentation(),
+ conference.getCallerDisplayName(),
+ conference.getCallerDisplayNamePresentation(),
+ conference.getDisconnectCause(),
+ conference.isRingbackRequested());
+ if (conference.getState() != Connection.STATE_DISCONNECTED) {
+ conference.setTelecomCallId(callId);
+ mAdapter.setVideoProvider(callId, conference.getVideoProvider());
+ mAdapter.setVideoState(callId, conference.getVideoState());
+ onConferenceAdded(conference);
+ }
+
+ Log.d(this, "createConference, calling handleCreateConferenceSuccessful %s", callId);
+ mAdapter.handleCreateConferenceComplete(
+ callId,
+ request,
+ parcelableConference);
+ }
+
/**
* This can be used by telecom to either create a new outgoing call or attach to an existing
* incoming call. In either case, telecom will cycle through a set of services and call
@@ -1645,6 +1885,18 @@
}
}
+ private void createConferenceFailed(final PhoneAccountHandle callManagerAccount,
+ final String callId, final ConnectionRequest request,
+ boolean isIncoming) {
+
+ Log.i(this, "createConferenceFailed %s", callId);
+ if (isIncoming) {
+ onCreateIncomingConferenceFailed(callManagerAccount, request);
+ } else {
+ onCreateOutgoingConferenceFailed(callManagerAccount, request);
+ }
+ }
+
private void handoverFailed(final String callId, final ConnectionRequest request,
int reason) {
@@ -1669,6 +1921,24 @@
"notifyCreateConnectionComplete"));
}
+ /**
+ * Called by Telecom when the creation of a new Conference has completed and it is now added
+ * to Telecom.
+ * @param callId The ID of the connection.
+ */
+ private void notifyCreateConferenceComplete(final String callId) {
+ Log.i(this, "notifyCreateConferenceComplete %s", callId);
+ if (callId == null) {
+ // This could happen if the conference fails quickly and is removed from the
+ // ConnectionService before Telecom sends the create conference complete callback.
+ Log.w(this, "notifyCreateConferenceComplete: callId is null.");
+ return;
+ }
+ onCreateConferenceComplete(findConferenceForAction(callId,
+ "notifyCreateConferenceComplete"));
+ }
+
+
private void abort(String callId) {
Log.d(this, "abort %s", callId);
findConnectionForAction(callId, "abort").onAbort();
@@ -1676,12 +1946,20 @@
private void answerVideo(String callId, int videoState) {
Log.d(this, "answerVideo %s", callId);
- findConnectionForAction(callId, "answer").onAnswer(videoState);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "answer").onAnswer(videoState);
+ } else {
+ findConferenceForAction(callId, "answer").onAnswer(videoState);
+ }
}
private void answer(String callId) {
Log.d(this, "answer %s", callId);
- findConnectionForAction(callId, "answer").onAnswer();
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "answer").onAnswer();
+ } else {
+ findConferenceForAction(callId, "answer").onAnswer();
+ }
}
private void deflect(String callId, Uri address) {
@@ -1691,7 +1969,11 @@
private void reject(String callId) {
Log.d(this, "reject %s", callId);
- findConnectionForAction(callId, "reject").onReject();
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "reject").onReject();
+ } else {
+ findConferenceForAction(callId, "reject").onReject();
+ }
}
private void reject(String callId, String rejectWithMessage) {
@@ -2198,6 +2480,21 @@
ConnectionRequest request) {
return null;
}
+ /**
+ * Create a {@code Connection} given an incoming request. This is used to attach to existing
+ * incoming conference call.
+ *
+ * @param connectionManagerPhoneAccount See description at
+ * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
+ * @param request Details about the incoming call.
+ * @return The {@code Connection} object to satisfy this call, or {@code null} to
+ * not handle the call.
+ */
+ public @Nullable Conference onCreateIncomingConference(
+ @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+ @Nullable ConnectionRequest request) {
+ return null;
+ }
/**
* Called after the {@link Connection} returned by
@@ -2212,6 +2509,19 @@
}
/**
+ * Called after the {@link Conference} returned by
+ * {@link #onCreateIncomingConference(PhoneAccountHandle, ConnectionRequest)}
+ * or {@link #onCreateOutgoingConference(PhoneAccountHandle, ConnectionRequest)} has been
+ * added to the {@link ConnectionService} and sent to Telecom.
+ *
+ * @param conference the {@link Conference}.
+ * @hide
+ */
+ public void onCreateConferenceComplete(Conference conference) {
+ }
+
+
+ /**
* Called by Telecom to inform the {@link ConnectionService} that its request to create a new
* incoming {@link Connection} was denied.
* <p>
@@ -2250,6 +2560,47 @@
}
/**
+ * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
+ * incoming {@link Conference} was denied.
+ * <p>
+ * Used when a self-managed {@link ConnectionService} attempts to create a new incoming
+ * {@link Conference}, but Telecom has determined that the call cannot be allowed at this time.
+ * The {@link ConnectionService} is responsible for silently rejecting the new incoming
+ * {@link Conference}.
+ * <p>
+ * See {@link TelecomManager#isIncomingCallPermitted(PhoneAccountHandle)} for more information.
+ *
+ * @param connectionManagerPhoneAccount See description at
+ * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
+ * @param request The incoming connection request.
+ */
+ public void onCreateIncomingConferenceFailed(
+ @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+ @Nullable ConnectionRequest request) {
+ }
+
+ /**
+ * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
+ * outgoing {@link Conference} was denied.
+ * <p>
+ * Used when a self-managed {@link ConnectionService} attempts to create a new outgoing
+ * {@link Conference}, but Telecom has determined that the call cannot be placed at this time.
+ * The {@link ConnectionService} is responisible for informing the user that the
+ * {@link Conference} cannot be made at this time.
+ * <p>
+ * See {@link TelecomManager#isOutgoingCallPermitted(PhoneAccountHandle)} for more information.
+ *
+ * @param connectionManagerPhoneAccount See description at
+ * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
+ * @param request The outgoing connection request.
+ */
+ public void onCreateOutgoingConferenceFailed(
+ @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+ @Nullable ConnectionRequest request) {
+ }
+
+
+ /**
* Trigger recalculate functinality for conference calls. This is used when a Telephony
* Connection is part of a conference controller but is not yet added to Connection
* Service and hence cannot be added to the conference call.
@@ -2289,6 +2640,36 @@
}
/**
+ * Create a {@code Conference} given an outgoing request. This is used to initiate new
+ * outgoing conference call.
+ *
+ * @param connectionManagerPhoneAccount The connection manager account to use for managing
+ * this call.
+ * <p>
+ * If this parameter is not {@code null}, it means that this {@code ConnectionService}
+ * has registered one or more {@code PhoneAccount}s having
+ * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. This parameter will contain
+ * one of these {@code PhoneAccount}s, while the {@code request} will contain another
+ * (usually but not always distinct) {@code PhoneAccount} to be used for actually
+ * making the connection.
+ * <p>
+ * If this parameter is {@code null}, it means that this {@code ConnectionService} is
+ * being asked to make a direct connection. The
+ * {@link ConnectionRequest#getAccountHandle()} of parameter {@code request} will be
+ * a {@code PhoneAccount} registered by this {@code ConnectionService} to use for
+ * making the connection.
+ * @param request Details about the outgoing call.
+ * @return The {@code Conference} object to satisfy this call, or the result of an invocation
+ * of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call.
+ */
+ public @Nullable Conference onCreateOutgoingConference(
+ @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+ @Nullable ConnectionRequest request) {
+ return null;
+ }
+
+
+ /**
* Called by Telecom to request that a {@link ConnectionService} creates an instance of an
* outgoing handover {@link Connection}.
* <p>
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 04e930c..8f27323 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -100,6 +100,19 @@
}
}
+ void handleCreateConferenceComplete(
+ String id,
+ ConnectionRequest request,
+ ParcelableConference conference) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.handleCreateConferenceComplete(id, request, conference,
+ Log.getExternalSession());
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
/**
* Sets a call's state to active (e.g., an ongoing call where two parties can actively
* communicate).
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index 60b2172..79ad51b 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -75,6 +75,7 @@
private static final int MSG_SET_PHONE_ACCOUNT_CHANGED = 34;
private static final int MSG_CONNECTION_SERVICE_FOCUS_RELEASED = 35;
private static final int MSG_SET_CONFERENCE_STATE = 36;
+ private static final int MSG_HANDLE_CREATE_CONFERENCE_COMPLETE = 37;
private final IConnectionServiceAdapter mDelegate;
@@ -103,6 +104,19 @@
}
break;
}
+ case MSG_HANDLE_CREATE_CONFERENCE_COMPLETE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.handleCreateConferenceComplete(
+ (String) args.arg1,
+ (ConnectionRequest) args.arg2,
+ (ParcelableConference) args.arg3,
+ null /*Session.Info*/);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
case MSG_SET_ACTIVE:
mDelegate.setActive((String) msg.obj, null /*Session.Info*/);
break;
@@ -366,6 +380,20 @@
}
@Override
+ public void handleCreateConferenceComplete(
+ String id,
+ ConnectionRequest request,
+ ParcelableConference conference,
+ Session.Info sessionInfo) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = id;
+ args.arg2 = request;
+ args.arg3 = conference;
+ mHandler.obtainMessage(MSG_HANDLE_CREATE_CONFERENCE_COMPLETE, args).sendToTarget();
+ }
+
+
+ @Override
public void setActive(String connectionId, Session.Info sessionInfo) {
mHandler.obtainMessage(MSG_SET_ACTIVE, connectionId).sendToTarget();
}
diff --git a/telecomm/java/android/telecom/ParcelableConference.java b/telecomm/java/android/telecom/ParcelableConference.java
index ede0594..90b69a3 100644
--- a/telecomm/java/android/telecom/ParcelableConference.java
+++ b/telecomm/java/android/telecom/ParcelableConference.java
@@ -47,6 +47,34 @@
private final int mAddressPresentation;
private final String mCallerDisplayName;
private final int mCallerDisplayNamePresentation;
+ private DisconnectCause mDisconnectCause;
+ private boolean mRingbackRequested;
+
+ public ParcelableConference(
+ PhoneAccountHandle phoneAccount,
+ int state,
+ int connectionCapabilities,
+ int connectionProperties,
+ List<String> connectionIds,
+ IVideoProvider videoProvider,
+ int videoState,
+ long connectTimeMillis,
+ long connectElapsedTimeMillis,
+ StatusHints statusHints,
+ Bundle extras,
+ Uri address,
+ int addressPresentation,
+ String callerDisplayName,
+ int callerDisplayNamePresentation,
+ DisconnectCause disconnectCause,
+ boolean ringbackRequested) {
+ this(phoneAccount, state, connectionCapabilities, connectionProperties, connectionIds,
+ videoProvider, videoState, connectTimeMillis, connectElapsedTimeMillis,
+ statusHints, extras, address, addressPresentation, callerDisplayName,
+ callerDisplayNamePresentation);
+ mDisconnectCause = disconnectCause;
+ mRingbackRequested = ringbackRequested;
+ }
public ParcelableConference(
PhoneAccountHandle phoneAccount,
@@ -79,6 +107,8 @@
mAddressPresentation = addressPresentation;
mCallerDisplayName = callerDisplayName;
mCallerDisplayNamePresentation = callerDisplayNamePresentation;
+ mDisconnectCause = null;
+ mRingbackRequested = false;
}
@Override
@@ -100,6 +130,10 @@
.append(mVideoState)
.append(", VideoProvider: ")
.append(mVideoProvider)
+ .append(", isRingbackRequested: ")
+ .append(mRingbackRequested)
+ .append(", disconnectCause: ")
+ .append(mDisconnectCause)
.toString();
}
@@ -151,6 +185,13 @@
return mAddress;
}
+ public final DisconnectCause getDisconnectCause() {
+ return mDisconnectCause;
+ }
+
+ public boolean isRingbackRequested() {
+ return mRingbackRequested;
+ }
public int getHandlePresentation() {
return mAddressPresentation;
}
@@ -177,11 +218,14 @@
int addressPresentation = source.readInt();
String callerDisplayName = source.readString();
int callerDisplayNamePresentation = source.readInt();
+ DisconnectCause disconnectCause = source.readParcelable(classLoader);
+ boolean isRingbackRequested = source.readInt() == 1;
return new ParcelableConference(phoneAccount, state, capabilities, properties,
connectionIds, videoCallProvider, videoState, connectTimeMillis,
connectElapsedTimeMillis, statusHints, extras, address, addressPresentation,
- callerDisplayName, callerDisplayNamePresentation);
+ callerDisplayName, callerDisplayNamePresentation, disconnectCause,
+ isRingbackRequested);
}
@Override
@@ -215,5 +259,7 @@
destination.writeInt(mAddressPresentation);
destination.writeString(mCallerDisplayName);
destination.writeInt(mCallerDisplayNamePresentation);
+ destination.writeParcelable(mDisconnectCause, 0);
+ destination.writeInt(mRingbackRequested ? 1 : 0);
}
}
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index bb858cb..abb210f 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -331,7 +331,17 @@
*/
public static final int CAPABILITY_EMERGENCY_PREFERRED = 0x2000;
- /* NEXT CAPABILITY: 0x4000 */
+ /**
+ * An adhoc conference call is established by providing a list of addresses to
+ * {@code TelecomManager#startConference(List<Uri>, int videoState)} where the
+ * {@link ConnectionService} is responsible for connecting all indicated participants
+ * to a conference simultaneously.
+ * This is in contrast to conferences formed by merging calls together (e.g. using
+ * {@link android.telecom.Call#mergeConference()}).
+ */
+ public static final int CAPABILITY_ADHOC_CONFERENCE_CALLING = 0x4000;
+
+ /* NEXT CAPABILITY: 0x8000 */
/**
* URI scheme for telephone number URIs.
@@ -1054,6 +1064,9 @@
if (hasCapabilities(CAPABILITY_RTT)) {
sb.append("Rtt");
}
+ if (hasCapabilities(CAPABILITY_ADHOC_CONFERENCE_CALLING)) {
+ sb.append("AdhocConf");
+ }
return sb.toString();
}
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 1e73bd6..76640e0 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -101,6 +101,14 @@
}
@Override
+ public void handleCreateConferenceComplete(
+ String id,
+ ConnectionRequest request,
+ ParcelableConference parcel,
+ Session.Info info) {
+ }
+
+ @Override
public void setActive(String callId, Session.Info sessionInfo) {
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "setActive")
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index c3fb510..49b74c6 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1810,6 +1810,45 @@
}
/**
+ * Registers a new incoming conference. A {@link ConnectionService} should invoke this method
+ * when it has an incoming conference. For managed {@link ConnectionService}s, the specified
+ * {@link PhoneAccountHandle} must have been registered with {@link #registerPhoneAccount} and
+ * the user must have enabled the corresponding {@link PhoneAccount}. This can be checked using
+ * {@link #getPhoneAccount}. Self-managed {@link ConnectionService}s must have
+ * {@link android.Manifest.permission#MANAGE_OWN_CALLS} to add a new incoming call.
+ * <p>
+ * The incoming conference you are adding is assumed to have a video state of
+ * {@link VideoProfile#STATE_AUDIO_ONLY}, unless the extra value
+ * {@link #EXTRA_INCOMING_VIDEO_STATE} is specified.
+ * <p>
+ * Once invoked, this method will cause the system to bind to the {@link ConnectionService}
+ * associated with the {@link PhoneAccountHandle} and request additional information about the
+ * call (See {@link ConnectionService#onCreateIncomingConference}) before starting the incoming
+ * call UI.
+ * <p>
+ * For a managed {@link ConnectionService}, a {@link SecurityException} will be thrown if either
+ * the {@link PhoneAccountHandle} does not correspond to a registered {@link PhoneAccount} or
+ * the associated {@link PhoneAccount} is not currently enabled by the user.
+ *
+ * @param phoneAccount A {@link PhoneAccountHandle} registered with
+ * {@link #registerPhoneAccount}.
+ * @param extras A bundle that will be passed through to
+ * {@link ConnectionService#onCreateIncomingConference}.
+ */
+
+ public void addNewIncomingConference(@NonNull PhoneAccountHandle phoneAccount,
+ @NonNull Bundle extras) {
+ try {
+ if (isServiceConnected()) {
+ getTelecomService().addNewIncomingConference(
+ phoneAccount, extras == null ? new Bundle() : extras);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException adding a new incoming conference: " + phoneAccount, e);
+ }
+ }
+
+ /**
* Registers a new unknown call with Telecom. This can only be called by the system Telephony
* service. This is invoked when Telephony detects a new unknown connection that was neither
* a new incoming call, nor an user-initiated outgoing call.
@@ -2014,6 +2053,42 @@
}
}
+
+ /**
+ * Place a new conference call with the provided participants using the system telecom service
+ * This method doesn't support placing of emergency calls.
+ *
+ * An adhoc conference call is established by providing a list of addresses to
+ * {@code TelecomManager#startConference(List<Uri>, int videoState)} where the
+ * {@link ConnectionService} is responsible for connecting all indicated participants
+ * to a conference simultaneously.
+ * This is in contrast to conferences formed by merging calls together (e.g. using
+ * {@link android.telecom.Call#mergeConference()}).
+ *
+ * The following keys are supported in the supplied extras.
+ * <ul>
+ * <li>{@link #EXTRA_PHONE_ACCOUNT_HANDLE}</li>
+ * <li>{@link #EXTRA_START_CALL_WITH_SPEAKERPHONE}</li>
+ * <li>{@link #EXTRA_START_CALL_WITH_VIDEO_STATE}</li>
+ * </ul>
+ *
+ * @param participants List of participants to start conference with
+ * @param extras Bundle of extras to use with the call
+ */
+ @RequiresPermission(android.Manifest.permission.CALL_PHONE)
+ public void startConference(@NonNull List<Uri> participants,
+ @NonNull Bundle extras) {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.startConference(participants, extras,
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#placeCall", e);
+ }
+ }
+ }
+
/**
* Enables and disables specified phone account.
*
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index e35093c..96f2483 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -53,6 +53,20 @@
void createConnectionFailed(in PhoneAccountHandle connectionManagerPhoneAccount, String callId,
in ConnectionRequest request, boolean isIncoming, in Session.Info sessionInfo);
+ void createConference(
+ in PhoneAccountHandle connectionManagerPhoneAccount,
+ String callId,
+ in ConnectionRequest request,
+ boolean isIncoming,
+ boolean isUnknown,
+ in Session.Info sessionInfo);
+
+ void createConferenceComplete(String callId, in Session.Info sessionInfo);
+
+ void createConferenceFailed(in PhoneAccountHandle connectionManagerPhoneAccount, String callId,
+ in ConnectionRequest request, boolean isIncoming, in Session.Info sessionInfo);
+
+
void abort(String callId, in Session.Info sessionInfo);
void answerVideo(String callId, int videoState, in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index 9cf098c..4f63e08 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -44,6 +44,12 @@
in ParcelableConnection connection,
in Session.Info sessionInfo);
+ void handleCreateConferenceComplete(
+ String callId,
+ in ConnectionRequest request,
+ in ParcelableConference connection,
+ in Session.Info sessionInfo);
+
void setActive(String callId, in Session.Info sessionInfo);
void setRinging(String callId, in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index c54da6b..285cf43 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -246,11 +246,22 @@
void addNewIncomingCall(in PhoneAccountHandle phoneAccount, in Bundle extras);
/**
+ * @see TelecomServiceImpl#addNewIncomingConference
+ */
+ void addNewIncomingConference(in PhoneAccountHandle phoneAccount, in Bundle extras);
+
+ /**
* @see TelecomServiceImpl#addNewUnknownCall
*/
void addNewUnknownCall(in PhoneAccountHandle phoneAccount, in Bundle extras);
/**
+ * @see TelecomServiceImpl#startConference
+ */
+ void startConference(in List<Uri> participants, in Bundle extras,
+ String callingPackage);
+
+ /**
* @see TelecomServiceImpl#placeCall
*/
void placeCall(in Uri handle, in Bundle extras, String callingPackage, String callingFeatureId);
diff --git a/telephony/OWNERS b/telephony/OWNERS
index 58a7ea0..628c480 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -1,18 +1,16 @@
set noparent
-tgunn@google.com
-breadley@google.com
-hallliu@google.com
-rgreenwalt@google.com
-mpq@google.com
amitmahajan@google.com
+breadley@google.com
fionaxu@google.com
jackyu@google.com
+hallliu@google.com
+rgreenwalt@google.com
+tgunn@google.com
jminjie@google.com
-satk@google.com
shuoq@google.com
refuhoo@google.com
-paulye@google.com
nazaninb@google.com
sarahchin@google.com
dbright@google.com
+xiaotonj@google.com
diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
index 9b82828..32f9d53 100644
--- a/telephony/common/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/common/com/android/internal/telephony/SmsApplication.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.role.RoleManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -40,7 +41,6 @@
import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
import android.telephony.PackageChangeReceiver;
-import android.util.Log;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -48,8 +48,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@@ -205,7 +203,7 @@
< android.os.Process.FIRST_APPLICATION_UID) {
return contextUserId;
} else {
- return UserHandle.getUserId(callingUid);
+ return UserHandle.getUserHandleForUid(callingUid).getIdentifier();
}
}
@@ -811,10 +809,10 @@
// This should never happen in prod -- unit tests will put the receiver into a
// unusual state where the pending result is null, which produces a NPE when calling
// getSendingUserId. Just pretend like it's the system user for testing.
- userId = UserHandle.USER_SYSTEM;
+ userId = UserHandle.SYSTEM.getIdentifier();
}
Context userContext = mContext;
- if (userId != UserHandle.USER_SYSTEM) {
+ if (userId != UserHandle.SYSTEM.getIdentifier()) {
try {
userContext = mContext.createPackageContextAsUser(mContext.getPackageName(), 0,
UserHandle.of(userId));
diff --git a/telephony/common/com/google/android/mms/ContentType.java b/telephony/common/com/google/android/mms/ContentType.java
index 12e4b7e..4a971dd 100644
--- a/telephony/common/com/google/android/mms/ContentType.java
+++ b/telephony/common/com/google/android/mms/ContentType.java
@@ -17,7 +17,7 @@
package com.google.android.mms;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import java.util.ArrayList;
diff --git a/telephony/common/com/google/android/mms/InvalidHeaderValueException.java b/telephony/common/com/google/android/mms/InvalidHeaderValueException.java
index 2836c30..55087ff 100644
--- a/telephony/common/com/google/android/mms/InvalidHeaderValueException.java
+++ b/telephony/common/com/google/android/mms/InvalidHeaderValueException.java
@@ -17,7 +17,7 @@
package com.google.android.mms;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* Thrown when an invalid header value was set.
diff --git a/telephony/common/com/google/android/mms/MmsException.java b/telephony/common/com/google/android/mms/MmsException.java
index 5be33ed..24bceb3 100644
--- a/telephony/common/com/google/android/mms/MmsException.java
+++ b/telephony/common/com/google/android/mms/MmsException.java
@@ -17,7 +17,7 @@
package com.google.android.mms;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/**
* A generic exception that is thrown by the Mms client.
diff --git a/telephony/common/com/google/android/mms/pdu/AcknowledgeInd.java b/telephony/common/com/google/android/mms/pdu/AcknowledgeInd.java
index ae447d7..8693385 100644
--- a/telephony/common/com/google/android/mms/pdu/AcknowledgeInd.java
+++ b/telephony/common/com/google/android/mms/pdu/AcknowledgeInd.java
@@ -17,7 +17,7 @@
package com.google.android.mms.pdu;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import com.google.android.mms.InvalidHeaderValueException;
diff --git a/telephony/common/com/google/android/mms/pdu/Base64.java b/telephony/common/com/google/android/mms/pdu/Base64.java
index 483fa7f..0d6a46a 100644
--- a/telephony/common/com/google/android/mms/pdu/Base64.java
+++ b/telephony/common/com/google/android/mms/pdu/Base64.java
@@ -17,7 +17,7 @@
package com.google.android.mms.pdu;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
public class Base64 {
/**
diff --git a/telephony/common/com/google/android/mms/pdu/CharacterSets.java b/telephony/common/com/google/android/mms/pdu/CharacterSets.java
index 27da35e..5172b7b 100644
--- a/telephony/common/com/google/android/mms/pdu/CharacterSets.java
+++ b/telephony/common/com/google/android/mms/pdu/CharacterSets.java
@@ -17,7 +17,7 @@
package com.google.android.mms.pdu;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
diff --git a/telephony/common/com/google/android/mms/pdu/DeliveryInd.java b/telephony/common/com/google/android/mms/pdu/DeliveryInd.java
index 7093ac6..8fb6a75 100644
--- a/telephony/common/com/google/android/mms/pdu/DeliveryInd.java
+++ b/telephony/common/com/google/android/mms/pdu/DeliveryInd.java
@@ -17,7 +17,7 @@
package com.google.android.mms.pdu;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import com.google.android.mms.InvalidHeaderValueException;
diff --git a/telephony/common/com/google/android/mms/pdu/EncodedStringValue.java b/telephony/common/com/google/android/mms/pdu/EncodedStringValue.java
index 4166275..8c0380f 100644
--- a/telephony/common/com/google/android/mms/pdu/EncodedStringValue.java
+++ b/telephony/common/com/google/android/mms/pdu/EncodedStringValue.java
@@ -17,10 +17,9 @@
package com.google.android.mms.pdu;
+import android.compat.annotation.UnsupportedAppUsage;
import android.util.Log;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
diff --git a/telephony/common/com/google/android/mms/pdu/GenericPdu.java b/telephony/common/com/google/android/mms/pdu/GenericPdu.java
index ebf16ac..320b13f 100644
--- a/telephony/common/com/google/android/mms/pdu/GenericPdu.java
+++ b/telephony/common/com/google/android/mms/pdu/GenericPdu.java
@@ -17,7 +17,7 @@
package com.google.android.mms.pdu;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import com.google.android.mms.InvalidHeaderValueException;
diff --git a/telephony/common/com/google/android/mms/pdu/MultimediaMessagePdu.java b/telephony/common/com/google/android/mms/pdu/MultimediaMessagePdu.java
index e108f76..42a89c6 100644
--- a/telephony/common/com/google/android/mms/pdu/MultimediaMessagePdu.java
+++ b/telephony/common/com/google/android/mms/pdu/MultimediaMessagePdu.java
@@ -17,7 +17,7 @@
package com.google.android.mms.pdu;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import com.google.android.mms.InvalidHeaderValueException;
diff --git a/telephony/common/com/google/android/mms/pdu/NotificationInd.java b/telephony/common/com/google/android/mms/pdu/NotificationInd.java
index b561bd4..ca4615c 100644
--- a/telephony/common/com/google/android/mms/pdu/NotificationInd.java
+++ b/telephony/common/com/google/android/mms/pdu/NotificationInd.java
@@ -17,7 +17,7 @@
package com.google.android.mms.pdu;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import com.google.android.mms.InvalidHeaderValueException;
diff --git a/telephony/common/com/google/android/mms/pdu/NotifyRespInd.java b/telephony/common/com/google/android/mms/pdu/NotifyRespInd.java
index 3c70f86..ebd81af 100644
--- a/telephony/common/com/google/android/mms/pdu/NotifyRespInd.java
+++ b/telephony/common/com/google/android/mms/pdu/NotifyRespInd.java
@@ -17,7 +17,7 @@
package com.google.android.mms.pdu;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import com.google.android.mms.InvalidHeaderValueException;
diff --git a/telephony/common/com/google/android/mms/pdu/PduBody.java b/telephony/common/com/google/android/mms/pdu/PduBody.java
index 51914e4..f7f285f 100644
--- a/telephony/common/com/google/android/mms/pdu/PduBody.java
+++ b/telephony/common/com/google/android/mms/pdu/PduBody.java
@@ -17,7 +17,7 @@
package com.google.android.mms.pdu;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import java.util.HashMap;
import java.util.Map;
diff --git a/telephony/common/com/google/android/mms/pdu/PduComposer.java b/telephony/common/com/google/android/mms/pdu/PduComposer.java
index e24bf21..b8b212c 100644
--- a/telephony/common/com/google/android/mms/pdu/PduComposer.java
+++ b/telephony/common/com/google/android/mms/pdu/PduComposer.java
@@ -17,12 +17,11 @@
package com.google.android.mms.pdu;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.Context;
import android.text.TextUtils;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
diff --git a/telephony/common/com/google/android/mms/pdu/PduContentTypes.java b/telephony/common/com/google/android/mms/pdu/PduContentTypes.java
index 8551b2f..57141fe 100644
--- a/telephony/common/com/google/android/mms/pdu/PduContentTypes.java
+++ b/telephony/common/com/google/android/mms/pdu/PduContentTypes.java
@@ -17,7 +17,7 @@
package com.google.android.mms.pdu;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
public class PduContentTypes {
/**
diff --git a/telephony/common/com/google/android/mms/pdu/PduHeaders.java b/telephony/common/com/google/android/mms/pdu/PduHeaders.java
index b524464..3e62184 100644
--- a/telephony/common/com/google/android/mms/pdu/PduHeaders.java
+++ b/telephony/common/com/google/android/mms/pdu/PduHeaders.java
@@ -17,7 +17,7 @@
package com.google.android.mms.pdu;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import com.google.android.mms.InvalidHeaderValueException;
diff --git a/telephony/common/com/google/android/mms/pdu/PduParser.java b/telephony/common/com/google/android/mms/pdu/PduParser.java
index f483994..5340245 100755
--- a/telephony/common/com/google/android/mms/pdu/PduParser.java
+++ b/telephony/common/com/google/android/mms/pdu/PduParser.java
@@ -17,10 +17,9 @@
package com.google.android.mms.pdu;
+import android.compat.annotation.UnsupportedAppUsage;
import android.util.Log;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import com.google.android.mms.ContentType;
import com.google.android.mms.InvalidHeaderValueException;
diff --git a/telephony/common/com/google/android/mms/pdu/PduPart.java b/telephony/common/com/google/android/mms/pdu/PduPart.java
index 09b7751..8dd976b 100644
--- a/telephony/common/com/google/android/mms/pdu/PduPart.java
+++ b/telephony/common/com/google/android/mms/pdu/PduPart.java
@@ -17,10 +17,9 @@
package com.google.android.mms.pdu;
+import android.compat.annotation.UnsupportedAppUsage;
import android.net.Uri;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import java.util.HashMap;
import java.util.Map;
diff --git a/telephony/common/com/google/android/mms/pdu/PduPersister.java b/telephony/common/com/google/android/mms/pdu/PduPersister.java
index 8efca0e..fcd5b8f 100755
--- a/telephony/common/com/google/android/mms/pdu/PduPersister.java
+++ b/telephony/common/com/google/android/mms/pdu/PduPersister.java
@@ -17,6 +17,7 @@
package com.google.android.mms.pdu;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -40,8 +41,6 @@
import android.text.TextUtils;
import android.util.Log;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import com.google.android.mms.ContentType;
import com.google.android.mms.InvalidHeaderValueException;
import com.google.android.mms.MmsException;
diff --git a/telephony/common/com/google/android/mms/pdu/QuotedPrintable.java b/telephony/common/com/google/android/mms/pdu/QuotedPrintable.java
index 9d6535c..4e1d7f5 100644
--- a/telephony/common/com/google/android/mms/pdu/QuotedPrintable.java
+++ b/telephony/common/com/google/android/mms/pdu/QuotedPrintable.java
@@ -17,7 +17,7 @@
package com.google.android.mms.pdu;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import java.io.ByteArrayOutputStream;
diff --git a/telephony/common/com/google/android/mms/pdu/ReadOrigInd.java b/telephony/common/com/google/android/mms/pdu/ReadOrigInd.java
index e38c62d..4ba3c71 100644
--- a/telephony/common/com/google/android/mms/pdu/ReadOrigInd.java
+++ b/telephony/common/com/google/android/mms/pdu/ReadOrigInd.java
@@ -17,7 +17,7 @@
package com.google.android.mms.pdu;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import com.google.android.mms.InvalidHeaderValueException;
diff --git a/telephony/common/com/google/android/mms/pdu/ReadRecInd.java b/telephony/common/com/google/android/mms/pdu/ReadRecInd.java
index 9696bc2..37ccfb9 100644
--- a/telephony/common/com/google/android/mms/pdu/ReadRecInd.java
+++ b/telephony/common/com/google/android/mms/pdu/ReadRecInd.java
@@ -17,7 +17,7 @@
package com.google.android.mms.pdu;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import com.google.android.mms.InvalidHeaderValueException;
diff --git a/telephony/common/com/google/android/mms/pdu/RetrieveConf.java b/telephony/common/com/google/android/mms/pdu/RetrieveConf.java
index 03755af..260adfc 100644
--- a/telephony/common/com/google/android/mms/pdu/RetrieveConf.java
+++ b/telephony/common/com/google/android/mms/pdu/RetrieveConf.java
@@ -17,7 +17,7 @@
package com.google.android.mms.pdu;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import com.google.android.mms.InvalidHeaderValueException;
diff --git a/telephony/common/com/google/android/mms/pdu/SendConf.java b/telephony/common/com/google/android/mms/pdu/SendConf.java
index b859827..7799238 100644
--- a/telephony/common/com/google/android/mms/pdu/SendConf.java
+++ b/telephony/common/com/google/android/mms/pdu/SendConf.java
@@ -17,7 +17,7 @@
package com.google.android.mms.pdu;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import com.google.android.mms.InvalidHeaderValueException;
diff --git a/telephony/common/com/google/android/mms/pdu/SendReq.java b/telephony/common/com/google/android/mms/pdu/SendReq.java
index c1b7f93..6e2f2da 100644
--- a/telephony/common/com/google/android/mms/pdu/SendReq.java
+++ b/telephony/common/com/google/android/mms/pdu/SendReq.java
@@ -17,10 +17,9 @@
package com.google.android.mms.pdu;
+import android.compat.annotation.UnsupportedAppUsage;
import android.util.Log;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import com.google.android.mms.InvalidHeaderValueException;
public class SendReq extends MultimediaMessagePdu {
diff --git a/telephony/common/com/google/android/mms/util/AbstractCache.java b/telephony/common/com/google/android/mms/util/AbstractCache.java
index ab5d48a..25862e7 100644
--- a/telephony/common/com/google/android/mms/util/AbstractCache.java
+++ b/telephony/common/com/google/android/mms/util/AbstractCache.java
@@ -17,10 +17,9 @@
package com.google.android.mms.util;
+import android.compat.annotation.UnsupportedAppUsage;
import android.util.Log;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import java.util.HashMap;
public abstract class AbstractCache<K, V> {
diff --git a/telephony/common/com/google/android/mms/util/DownloadDrmHelper.java b/telephony/common/com/google/android/mms/util/DownloadDrmHelper.java
index 118de46..0f9390d 100644
--- a/telephony/common/com/google/android/mms/util/DownloadDrmHelper.java
+++ b/telephony/common/com/google/android/mms/util/DownloadDrmHelper.java
@@ -17,12 +17,11 @@
package com.google.android.mms.util;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.drm.DrmManagerClient;
import android.util.Log;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
public class DownloadDrmHelper {
private static final String TAG = "DownloadDrmHelper";
diff --git a/telephony/common/com/google/android/mms/util/DrmConvertSession.java b/telephony/common/com/google/android/mms/util/DrmConvertSession.java
index 0e8ec91..156c7ad 100644
--- a/telephony/common/com/google/android/mms/util/DrmConvertSession.java
+++ b/telephony/common/com/google/android/mms/util/DrmConvertSession.java
@@ -16,14 +16,13 @@
*/
package com.google.android.mms.util;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.drm.DrmConvertedStatus;
import android.drm.DrmManagerClient;
import android.provider.Downloads;
import android.util.Log;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
diff --git a/telephony/common/com/google/android/mms/util/PduCache.java b/telephony/common/com/google/android/mms/util/PduCache.java
index 94e3894..c380d6b 100644
--- a/telephony/common/com/google/android/mms/util/PduCache.java
+++ b/telephony/common/com/google/android/mms/util/PduCache.java
@@ -17,14 +17,13 @@
package com.google.android.mms.util;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentUris;
import android.content.UriMatcher;
import android.net.Uri;
import android.provider.Telephony.Mms;
import android.util.Log;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
import java.util.HashMap;
import java.util.HashSet;
diff --git a/telephony/common/com/google/android/mms/util/PduCacheEntry.java b/telephony/common/com/google/android/mms/util/PduCacheEntry.java
index 1ecd1bf..a4a25d2 100644
--- a/telephony/common/com/google/android/mms/util/PduCacheEntry.java
+++ b/telephony/common/com/google/android/mms/util/PduCacheEntry.java
@@ -17,7 +17,7 @@
package com.google.android.mms.util;
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import com.google.android.mms.pdu.GenericPdu;
diff --git a/telephony/common/com/google/android/mms/util/SqliteWrapper.java b/telephony/common/com/google/android/mms/util/SqliteWrapper.java
index 2dd1dc1..31fe4d7 100644
--- a/telephony/common/com/google/android/mms/util/SqliteWrapper.java
+++ b/telephony/common/com/google/android/mms/util/SqliteWrapper.java
@@ -18,6 +18,7 @@
package com.google.android.mms.util;
import android.app.ActivityManager;
+import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
@@ -27,8 +28,6 @@
import android.util.Log;
import android.widget.Toast;
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
public final class SqliteWrapper {
private static final String TAG = "SqliteWrapper";
private static final String SQLITE_EXCEPTION_DETAIL_MESSAGE
diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java
index ef11f46..9315586 100644
--- a/telephony/java/android/service/euicc/EuiccService.java
+++ b/telephony/java/android/service/euicc/EuiccService.java
@@ -31,7 +31,9 @@
import android.telephony.TelephonyManager;
import android.telephony.euicc.DownloadableSubscription;
import android.telephony.euicc.EuiccInfo;
+import android.telephony.euicc.EuiccManager;
import android.telephony.euicc.EuiccManager.OtaStatus;
+import android.text.TextUtils;
import android.util.Log;
import java.io.PrintWriter;
@@ -311,6 +313,65 @@
mStubWrapper = new IEuiccServiceWrapper();
}
+ /**
+ * Given a SubjectCode[5.2.6.1] and ReasonCode[5.2.6.2] from GSMA (SGP.22 v2.2), encode it to
+ * the format described in
+ * {@link android.telephony.euicc.EuiccManager#OPERATION_SMDX_SUBJECT_REASON_CODE}
+ *
+ * @param subjectCode SubjectCode[5.2.6.1] from GSMA (SGP.22 v2.2)
+ * @param reasonCode ReasonCode[5.2.6.2] from GSMA (SGP.22 v2.2)
+ * @return encoded error code described in
+ * {@link android.telephony.euicc.EuiccManager#OPERATION_SMDX_SUBJECT_REASON_CODE}
+ * @throws NumberFormatException when the Subject/Reason code contains non digits
+ * @throws IllegalArgumentException when Subject/Reason code is null/empty
+ * @throws UnsupportedOperationException when sections has more than four layers (e.g 5.8.1.2)
+ * or when an number is bigger than 15
+ */
+ public int encodeSmdxSubjectAndReasonCode(@Nullable String subjectCode,
+ @Nullable String reasonCode)
+ throws NumberFormatException, IllegalArgumentException, UnsupportedOperationException {
+ final int maxSupportedSection = 3;
+ final int maxSupportedDigit = 15;
+ final int bitsPerSection = 4;
+
+ if (TextUtils.isEmpty(subjectCode) || TextUtils.isEmpty(reasonCode)) {
+ throw new IllegalArgumentException("SubjectCode/ReasonCode is empty");
+ }
+
+ final String[] subjectCodeToken = subjectCode.split("\\.");
+ final String[] reasonCodeToken = reasonCode.split("\\.");
+
+ if (subjectCodeToken.length > maxSupportedSection
+ || reasonCodeToken.length > maxSupportedSection) {
+ throw new UnsupportedOperationException("Only three nested layer is supported.");
+ }
+
+ int result = EuiccManager.OPERATION_SMDX_SUBJECT_REASON_CODE;
+
+ // Pad the 0s needed for subject code
+ result = result << (maxSupportedSection - subjectCodeToken.length) * bitsPerSection;
+
+ for (String digitString : subjectCodeToken) {
+ int num = Integer.parseInt(digitString);
+ if (num > maxSupportedDigit) {
+ throw new UnsupportedOperationException("SubjectCode exceeds " + maxSupportedDigit);
+ }
+ result = (result << bitsPerSection) + num;
+ }
+
+ // Pad the 0s needed for reason code
+ result = result << (maxSupportedSection - reasonCodeToken.length) * bitsPerSection;
+ for (String digitString : reasonCodeToken) {
+ int num = Integer.parseInt(digitString);
+ if (num > maxSupportedDigit) {
+ throw new UnsupportedOperationException("ReasonCode exceeds " + maxSupportedDigit);
+ }
+ result = (result << bitsPerSection) + num;
+ }
+
+ return result;
+ }
+
@Override
@CallSuper
public void onCreate() {
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index b1d647f..610eef8 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -317,6 +317,159 @@
public static final int BAND_260 = 260;
public static final int BAND_261 = 261;
+ /**
+ * NR Bands
+ *
+ * @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"BAND_"},
+ value = {BAND_1,
+ BAND_2,
+ BAND_3,
+ BAND_5,
+ BAND_7,
+ BAND_8,
+ BAND_12,
+ BAND_14,
+ BAND_18,
+ BAND_20,
+ BAND_25,
+ BAND_28,
+ BAND_29,
+ BAND_30,
+ BAND_34,
+ BAND_38,
+ BAND_39,
+ BAND_40,
+ BAND_41,
+ BAND_48,
+ BAND_50,
+ BAND_51,
+ BAND_65,
+ BAND_66,
+ BAND_70,
+ BAND_71,
+ BAND_74,
+ BAND_75,
+ BAND_76,
+ BAND_77,
+ BAND_78,
+ BAND_79,
+ BAND_80,
+ BAND_81,
+ BAND_82,
+ BAND_83,
+ BAND_84,
+ BAND_86,
+ BAND_90,
+ BAND_257,
+ BAND_258,
+ BAND_260,
+ BAND_261})
+ public @interface NgranBand {}
+
+ /**
+ * Unknown NR frequency.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int FREQUENCY_RANGE_GROUP_UNKNOWN = 0;
+
+ /**
+ * NR frequency group 1 defined in 3GPP TS 38.101-1 table 5.2-1
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int FREQUENCY_RANGE_GROUP_1 = 1;
+
+ /**
+ * NR frequency group 2 defined in 3GPP TS 38.101-2 table 5.2-1
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int FREQUENCY_RANGE_GROUP_2 = 2;
+
+ /**
+ * Radio frequency range group
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"FREQUENCY_RANGE_GROUP_"},
+ value = {
+ FREQUENCY_RANGE_GROUP_UNKNOWN,
+ FREQUENCY_RANGE_GROUP_1,
+ FREQUENCY_RANGE_GROUP_2})
+ public @interface FrequencyRangeGroup {}
+
+ /**
+ * Get frequency range group
+ *
+ * @param band NR band
+ * @return The frequency range group
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static @FrequencyRangeGroup int getFrequencyRangeGroup(@NgranBand int band) {
+ switch (band) {
+ case BAND_1:
+ case BAND_2:
+ case BAND_3:
+ case BAND_5:
+ case BAND_7:
+ case BAND_8:
+ case BAND_12:
+ case BAND_14:
+ case BAND_18:
+ case BAND_20:
+ case BAND_25:
+ case BAND_28:
+ case BAND_29:
+ case BAND_30:
+ case BAND_34:
+ case BAND_38:
+ case BAND_39:
+ case BAND_40:
+ case BAND_41:
+ case BAND_48:
+ case BAND_50:
+ case BAND_51:
+ case BAND_65:
+ case BAND_66:
+ case BAND_70:
+ case BAND_71:
+ case BAND_74:
+ case BAND_75:
+ case BAND_76:
+ case BAND_77:
+ case BAND_78:
+ case BAND_79:
+ case BAND_80:
+ case BAND_81:
+ case BAND_82:
+ case BAND_83:
+ case BAND_84:
+ case BAND_86:
+ case BAND_90:
+ return FREQUENCY_RANGE_GROUP_1;
+ case BAND_257:
+ case BAND_258:
+ case BAND_260:
+ case BAND_261:
+ return FREQUENCY_RANGE_GROUP_2;
+ default:
+ return FREQUENCY_RANGE_GROUP_UNKNOWN;
+ }
+ };
+
/** @hide */
private NgranBands() {}
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7abad72..b30f586 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1080,6 +1080,14 @@
public static final String KEY_IGNORE_RTT_MODE_SETTING_BOOL =
"ignore_rtt_mode_setting_bool";
+
+ /**
+ * Determines whether adhoc conference calls are supported by a carrier. When {@code true},
+ * adhoc conference calling is supported, {@code false otherwise}.
+ */
+ public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL =
+ "support_adhoc_conference_calls_bool";
+
/**
* Determines whether conference calls are supported by a carrier. When {@code true},
* conference calling is supported, {@code false otherwise}.
@@ -1446,6 +1454,50 @@
"apn_settings_default_apn_types_string_array";
/**
+ * Configs used for APN setup.
+ */
+ public static final class Apn {
+ /** Prefix of all Apn.KEY_* constants. */
+ public static final String KEY_PREFIX = "apn.";
+
+ /** IPv4 internet protocol */
+ public static final String PROTOCOL_IPV4 = "IP";
+ /** IPv6 internet protocol */
+ public static final String PROTOCOL_IPV6 = "IPV6";
+ /** IPv4 or IPv6 internet protocol */
+ public static final String PROTOCOL_IPV4V6 = "IPV4V6";
+
+ /**
+ * Default value of APN protocol field if not specified by user when adding/modifying
+ * an APN.
+ *
+ * Available options are: {@link #PROTOCOL_IPV4}, {@link #PROTOCOL_IPV6},
+ * {@link #PROTOCOL_IPV4V6}
+ */
+ public static final String KEY_SETTINGS_DEFAULT_PROTOCOL_STRING =
+ KEY_PREFIX + "settings_default_protocol_string";
+
+ /**
+ * Default value of APN roaming protocol field if not specified by user when
+ * adding/modifying an APN.
+ *
+ * Available options are: {@link #PROTOCOL_IPV4}, {@link #PROTOCOL_IPV6},
+ * {@link #PROTOCOL_IPV4V6}
+ */
+ public static final String KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING =
+ KEY_PREFIX + "settings_default_roaming_protocol_string";
+
+ private Apn() {}
+
+ private static PersistableBundle getDefaults() {
+ PersistableBundle defaults = new PersistableBundle();
+ defaults.putString(KEY_SETTINGS_DEFAULT_PROTOCOL_STRING, "");
+ defaults.putString(KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING, "");
+ return defaults;
+ }
+ }
+
+ /**
* Boolean indicating if intent for emergency call state changes should be broadcast
* @hide
*/
@@ -2967,7 +3019,6 @@
/**
* Controls hysteresis time in milli seconds for which OpportunisticNetworkService
* will wait before switching data from opportunistic network to primary network.
- * @hide
*/
public static final String KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG =
"opportunistic_network_data_switch_exit_hysteresis_time_long";
@@ -2975,14 +3026,12 @@
/**
* Controls whether to do ping test before switching data to opportunistic network.
* This carrier config is used to disable this feature.
- * @hide
*/
public static final String KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL =
"ping_test_before_data_switch_bool";
/**
* Controls time in milliseconds until DcTracker reevaluates 5G connection state.
- * @hide
*/
public static final String KEY_5G_WATCHDOG_TIME_MS_LONG =
"5g_watchdog_time_long";
@@ -2991,7 +3040,6 @@
* if primary is out of service. This control only affects system or 1st party app
* initiated data switch, but will not override data switch initiated by privileged carrier apps
* This carrier config is used to disable this feature.
- * @hide
*/
public static final String KEY_SWITCH_DATA_TO_PRIMARY_IF_PRIMARY_IS_OOS_BOOL =
"switch_data_to_primary_if_primary_is_oos_bool";
@@ -3003,7 +3051,6 @@
* #KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT within
* #KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG of switching to opportunistic network,
* it will be determined as ping pong situation by system app or 1st party app.
- * @hide
*/
public static final String KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG =
"opportunistic_network_ping_pong_time_long";
@@ -3017,7 +3064,6 @@
* #KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG.
* If ping pong situation continuous #KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG
* will be added to previously determined hysteresis time.
- * @hide
*/
public static final String KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG =
"opportunistic_network_backoff_time_long";
@@ -3029,7 +3075,6 @@
* continuous ping pong situation or not as described in
* #KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG and
* #KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG.
- * @hide
*/
public static final String KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG =
"opportunistic_network_max_backoff_time_long";
@@ -3076,7 +3121,6 @@
* validation result, this value defines customized value of how long we wait for validation
* success before we fail and revoke the switch.
* Time out is in milliseconds.
- * @hide
*/
public static final String KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG =
"data_switch_validation_timeout_long";
@@ -3420,6 +3464,14 @@
"prevent_clir_activation_and_deactivation_code_bool";
/**
+ * Flag specifying whether to show forwarded number on call-in-progress screen.
+ * When true, forwarded number is shown.
+ * When false, forwarded number is not shown.
+ */
+ public static final String KEY_SHOW_FORWARDED_NUMBER_BOOL =
+ "show_forwarded_number_bool";
+
+ /**
* Configs used for epdg tunnel bring up.
*
* @see <a href="https://tools.ietf.org/html/rfc7296">RFC 7296, Internet Key Exchange
@@ -3903,6 +3955,8 @@
sDefaults.putStringArray(KEY_READ_ONLY_APN_TYPES_STRING_ARRAY, new String[] {"dun"});
sDefaults.putStringArray(KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY, null);
sDefaults.putStringArray(KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY, null);
+ sDefaults.putAll(Apn.getDefaults());
+
sDefaults.putBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL, false);
sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
sDefaults.putStringArray(KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS, new String[]{
@@ -3948,6 +4002,7 @@
sDefaults.putBoolean(KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL, false);
sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, false);
sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0);
+ sDefaults.putBoolean(KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true);
@@ -4273,6 +4328,7 @@
// Default wifi configurations.
sDefaults.putAll(Wifi.getDefaults());
sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false);
+ sDefaults.putBoolean(KEY_SHOW_FORWARDED_NUMBER_BOOL, false);
sDefaults.putAll(Iwlan.getDefaults());
}
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 49f425a..dc73cbf 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -23,6 +23,8 @@
import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
+import java.util.Collections;
+import java.util.List;
import java.util.Objects;
/**
@@ -46,6 +48,9 @@
// 6-bit Base Station Identity Code
private final int mBsic;
+ // a list of additional PLMN-IDs reported for this cell
+ private final List<String> mAdditionalPlmns;
+
/**
* @hide
*/
@@ -56,6 +61,7 @@
mCid = CellInfo.UNAVAILABLE;
mArfcn = CellInfo.UNAVAILABLE;
mBsic = CellInfo.UNAVAILABLE;
+ mAdditionalPlmns = Collections.emptyList();
}
/**
@@ -68,35 +74,48 @@
* @param mncStr 2 or 3-digit Mobile Network Code in string format
* @param alphal long alpha Operator Name String or Enhanced Operator Name String
* @param alphas short alpha Operator Name String or Enhanced Operator Name String
+ * @param additionalPlmns a list of additional PLMN IDs broadcast by the cell
*
* @hide
*/
public CellIdentityGsm(int lac, int cid, int arfcn, int bsic, String mccStr,
- String mncStr, String alphal, String alphas) {
+ String mncStr, String alphal, String alphas,
+ List<String> additionalPlmns) {
super(TAG, CellInfo.TYPE_GSM, mccStr, mncStr, alphal, alphas);
mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
mArfcn = inRangeOrUnavailable(arfcn, 0, MAX_ARFCN);
mBsic = inRangeOrUnavailable(bsic, 0, MAX_BSIC);
+ mAdditionalPlmns = additionalPlmns;
}
/** @hide */
public CellIdentityGsm(android.hardware.radio.V1_0.CellIdentityGsm cid) {
this(cid.lac, cid.cid, cid.arfcn,
cid.bsic == (byte) 0xFF ? CellInfo.UNAVAILABLE : cid.bsic,
- cid.mcc, cid.mnc, "", "");
+ cid.mcc, cid.mnc, "", "", Collections.emptyList());
}
/** @hide */
public CellIdentityGsm(android.hardware.radio.V1_2.CellIdentityGsm cid) {
this(cid.base.lac, cid.base.cid, cid.base.arfcn,
cid.base.bsic == (byte) 0xFF ? CellInfo.UNAVAILABLE : cid.base.bsic, cid.base.mcc,
- cid.base.mnc, cid.operatorNames.alphaLong, cid.operatorNames.alphaShort);
+ cid.base.mnc, cid.operatorNames.alphaLong, cid.operatorNames.alphaShort,
+ Collections.emptyList());
+ }
+
+ /** @hide */
+ public CellIdentityGsm(android.hardware.radio.V1_5.CellIdentityGsm cid) {
+ this(cid.base.base.lac, cid.base.base.cid, cid.base.base.arfcn,
+ cid.base.base.bsic == (byte) 0xFF ? CellInfo.UNAVAILABLE
+ : cid.base.base.bsic, cid.base.base.mcc,
+ cid.base.base.mnc, cid.base.operatorNames.alphaLong,
+ cid.base.operatorNames.alphaShort, cid.additionalPlmns);
}
private CellIdentityGsm(CellIdentityGsm cid) {
this(cid.mLac, cid.mCid, cid.mArfcn, cid.mBsic, cid.mMccStr,
- cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort);
+ cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort, cid.mAdditionalPlmns);
}
CellIdentityGsm copy() {
@@ -107,7 +126,7 @@
@Override
public @NonNull CellIdentityGsm sanitizeLocationInfo() {
return new CellIdentityGsm(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
- CellInfo.UNAVAILABLE, mMccStr, mMncStr, mAlphaLong, mAlphaShort);
+ CellInfo.UNAVAILABLE, mMccStr, mMncStr, mAlphaLong, mAlphaShort, mAdditionalPlmns);
}
/**
@@ -193,6 +212,14 @@
}
/**
+ * @return a list of additional PLMN IDs supported by this cell.
+ */
+ @NonNull
+ public List<String> getAdditionalPlmns() {
+ return mAdditionalPlmns;
+ }
+
+ /**
* @deprecated Primary Scrambling Code is not applicable to GSM.
* @return {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} - undefined for GSM
*/
@@ -215,7 +242,7 @@
@Override
public int hashCode() {
- return Objects.hash(mLac, mCid, super.hashCode());
+ return Objects.hash(mLac, mCid, mAdditionalPlmns.hashCode(), super.hashCode());
}
@Override
@@ -235,6 +262,7 @@
&& mBsic == o.mBsic
&& TextUtils.equals(mMccStr, o.mMccStr)
&& TextUtils.equals(mMncStr, o.mMncStr)
+ && mAdditionalPlmns.equals(o.mAdditionalPlmns)
&& super.equals(other);
}
@@ -249,6 +277,7 @@
.append(" mMnc=").append(mMncStr)
.append(" mAlphaLong=").append(mAlphaLong)
.append(" mAlphaShort=").append(mAlphaShort)
+ .append(" mAdditionalPlmns=").append(mAdditionalPlmns)
.append("}").toString();
}
@@ -261,6 +290,7 @@
dest.writeInt(mCid);
dest.writeInt(mArfcn);
dest.writeInt(mBsic);
+ dest.writeList(mAdditionalPlmns);
}
/** Construct from Parcel, type has already been processed */
@@ -270,6 +300,7 @@
mCid = in.readInt();
mArfcn = in.readInt();
mBsic = in.readInt();
+ mAdditionalPlmns = in.readArrayList(null);
if (DBG) log(toString());
}
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index bc46550..cf8fe6a 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -24,6 +24,8 @@
import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
+import java.util.Collections;
+import java.util.List;
import java.util.Objects;
/**
@@ -50,6 +52,11 @@
// cell bandwidth, in kHz
private final int mBandwidth;
+ // a list of additional PLMN-IDs reported for this cell
+ private final List<String> mAdditionalPlmns;
+
+ private ClosedSubscriberGroupInfo mCsgInfo;
+
/**
* @hide
*/
@@ -61,6 +68,8 @@
mTac = CellInfo.UNAVAILABLE;
mEarfcn = CellInfo.UNAVAILABLE;
mBandwidth = CellInfo.UNAVAILABLE;
+ mAdditionalPlmns = Collections.emptyList();
+ mCsgInfo = null;
}
/**
@@ -76,7 +85,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac) {
this(ci, pci, tac, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, String.valueOf(mcc),
- String.valueOf(mnc), null, null);
+ String.valueOf(mnc), null, null, Collections.emptyList(), null);
}
/**
@@ -90,34 +99,49 @@
* @param mncStr 2 or 3-digit Mobile Network Code in string format
* @param alphal long alpha Operator Name String or Enhanced Operator Name String
* @param alphas short alpha Operator Name String or Enhanced Operator Name String
+ * @param additionalPlmns a list of additional PLMN IDs broadcast by the cell
+ * @param csgInfo info about the closed subscriber group broadcast by the cell
*
* @hide
*/
public CellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth, String mccStr,
- String mncStr, String alphal, String alphas) {
+ String mncStr, String alphal, String alphas, List<String> additionalPlmns,
+ ClosedSubscriberGroupInfo csgInfo) {
super(TAG, CellInfo.TYPE_LTE, mccStr, mncStr, alphal, alphas);
mCi = inRangeOrUnavailable(ci, 0, MAX_CI);
mPci = inRangeOrUnavailable(pci, 0, MAX_PCI);
mTac = inRangeOrUnavailable(tac, 0, MAX_TAC);
mEarfcn = inRangeOrUnavailable(earfcn, 0, MAX_EARFCN);
mBandwidth = inRangeOrUnavailable(bandwidth, 0, MAX_BANDWIDTH);
+ mAdditionalPlmns = additionalPlmns;
+ mCsgInfo = csgInfo;
}
/** @hide */
public CellIdentityLte(android.hardware.radio.V1_0.CellIdentityLte cid) {
- this(cid.ci, cid.pci, cid.tac, cid.earfcn, CellInfo.UNAVAILABLE, cid.mcc, cid.mnc, "", "");
+ this(cid.ci, cid.pci, cid.tac, cid.earfcn,
+ CellInfo.UNAVAILABLE, cid.mcc, cid.mnc, "", "", Collections.emptyList(), null);
}
/** @hide */
public CellIdentityLte(android.hardware.radio.V1_2.CellIdentityLte cid) {
this(cid.base.ci, cid.base.pci, cid.base.tac, cid.base.earfcn, cid.bandwidth,
cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong,
- cid.operatorNames.alphaShort);
+ cid.operatorNames.alphaShort, Collections.emptyList(), null);
+ }
+
+ /** @hide */
+ public CellIdentityLte(android.hardware.radio.V1_5.CellIdentityLte cid) {
+ this(cid.base.base.ci, cid.base.base.pci, cid.base.base.tac, cid.base.base.earfcn,
+ cid.base.bandwidth, cid.base.base.mcc, cid.base.base.mnc,
+ cid.base.operatorNames.alphaLong, cid.base.operatorNames.alphaShort,
+ cid.additionalPlmns, cid.optionalCsgInfo.csgInfo() != null
+ ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo()) : null);
}
private CellIdentityLte(CellIdentityLte cid) {
this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mBandwidth, cid.mMccStr,
- cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort);
+ cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort, cid.mAdditionalPlmns, cid.mCsgInfo);
}
/** @hide */
@@ -125,7 +149,7 @@
public @NonNull CellIdentityLte sanitizeLocationInfo() {
return new CellIdentityLte(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
- mMccStr, mMncStr, mAlphaLong, mAlphaShort);
+ mMccStr, mMncStr, mAlphaLong, mAlphaShort, mAdditionalPlmns, null);
}
CellIdentityLte copy() {
@@ -185,6 +209,19 @@
}
/**
+ * Get bands of the cell
+ *
+ * Reference: 3GPP TS 36.101 section 5.5
+ *
+ * @return List of band number or empty list if not available.
+ */
+ @NonNull
+ public List<Integer> getBands() {
+ // Todo: Add actual support
+ return Collections.emptyList();
+ }
+
+ /**
* @return Cell bandwidth in kHz,
* {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
*/
@@ -223,6 +260,22 @@
}
/**
+ * @return a list of additional PLMN IDs supported by this cell.
+ */
+ @NonNull
+ public List<String> getAdditionalPlmns() {
+ return mAdditionalPlmns;
+ }
+
+ /**
+ * @return closed subscriber group information about the cell if available, otherwise null.
+ */
+ @Nullable
+ public ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo() {
+ return mCsgInfo;
+ }
+
+ /**
* A hack to allow tunneling of LTE information via GsmCellLocation
* so that older Network Location Providers can return some information
* on LTE only networks, see bug 9228974.
@@ -247,7 +300,8 @@
@Override
public int hashCode() {
- return Objects.hash(mCi, mPci, mTac, super.hashCode());
+ return Objects.hash(mCi, mPci, mTac,
+ mAdditionalPlmns.hashCode(), mCsgInfo, super.hashCode());
}
@Override
@@ -268,6 +322,8 @@
&& mBandwidth == o.mBandwidth
&& TextUtils.equals(mMccStr, o.mMccStr)
&& TextUtils.equals(mMncStr, o.mMncStr)
+ && mAdditionalPlmns.equals(o.mAdditionalPlmns)
+ && Objects.equals(mCsgInfo, o.mCsgInfo)
&& super.equals(other);
}
@@ -283,6 +339,8 @@
.append(" mMnc=").append(mMncStr)
.append(" mAlphaLong=").append(mAlphaLong)
.append(" mAlphaShort=").append(mAlphaShort)
+ .append(" mAdditionalPlmns=").append(mAdditionalPlmns)
+ .append(" mCsgInfo=").append(mCsgInfo)
.append("}").toString();
}
@@ -296,6 +354,8 @@
dest.writeInt(mTac);
dest.writeInt(mEarfcn);
dest.writeInt(mBandwidth);
+ dest.writeList(mAdditionalPlmns);
+ dest.writeParcelable(mCsgInfo, flags);
}
/** Construct from Parcel, type has already been processed */
@@ -306,7 +366,8 @@
mTac = in.readInt();
mEarfcn = in.readInt();
mBandwidth = in.readInt();
-
+ mAdditionalPlmns = in.readArrayList(null);
+ mCsgInfo = in.readParcelable(null);
if (DBG) log(toString());
}
diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java
index f08a580..d4f181f 100644
--- a/telephony/java/android/telephony/CellIdentityNr.java
+++ b/telephony/java/android/telephony/CellIdentityNr.java
@@ -20,8 +20,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
+import android.telephony.AccessNetworkConstants.NgranBands.NgranBand;
import android.telephony.gsm.GsmCellLocation;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Objects;
/**
@@ -39,40 +43,58 @@
private final int mPci;
private final int mTac;
private final long mNci;
+ private final List<Integer> mBands;
+
+ // a list of additional PLMN-IDs reported for this cell
+ private final List<String> mAdditionalPlmns;
/**
*
* @param pci Physical Cell Id in range [0, 1007].
* @param tac 16-bit Tracking Area Code.
* @param nrArfcn NR Absolute Radio Frequency Channel Number, in range [0, 3279165].
+ * @param bands Bands used by the cell. Band number defined in 3GPP TS 38.101-1 and TS 38.101-2.
* @param mccStr 3-digit Mobile Country Code in string format.
* @param mncStr 2 or 3-digit Mobile Network Code in string format.
* @param nci The 36-bit NR Cell Identity in range [0, 68719476735].
* @param alphal long alpha Operator Name String or Enhanced Operator Name String.
* @param alphas short alpha Operator Name String or Enhanced Operator Name String.
+ * @param additionalPlmns a list of additional PLMN IDs broadcast by the cell
*
* @hide
*/
- public CellIdentityNr(int pci, int tac, int nrArfcn, String mccStr, String mncStr,
- long nci, String alphal, String alphas) {
+ public CellIdentityNr(int pci, int tac, int nrArfcn, @NgranBand List<Integer> bands,
+ String mccStr, String mncStr, long nci, String alphal, String alphas,
+ List<String> additionalPlmns) {
super(TAG, CellInfo.TYPE_NR, mccStr, mncStr, alphal, alphas);
mPci = inRangeOrUnavailable(pci, 0, MAX_PCI);
mTac = inRangeOrUnavailable(tac, 0, MAX_TAC);
mNrArfcn = inRangeOrUnavailable(nrArfcn, 0, MAX_NRARFCN);
+ mBands = new ArrayList<>(bands);
mNci = inRangeOrUnavailable(nci, 0, MAX_NCI);
+ mAdditionalPlmns = new ArrayList<>(additionalPlmns);
}
/** @hide */
public CellIdentityNr(android.hardware.radio.V1_4.CellIdentityNr cid) {
- this(cid.pci, cid.tac, cid.nrarfcn, cid.mcc, cid.mnc, cid.nci, cid.operatorNames.alphaLong,
- cid.operatorNames.alphaShort);
+ this(cid.pci, cid.tac, cid.nrarfcn, Collections.emptyList(), cid.mcc, cid.mnc, cid.nci,
+ cid.operatorNames.alphaLong, cid.operatorNames.alphaShort,
+ Collections.emptyList());
+ }
+
+ /** @hide */
+ public CellIdentityNr(android.hardware.radio.V1_5.CellIdentityNr cid) {
+ this(cid.base.pci, cid.base.tac, cid.base.nrarfcn, cid.bands, cid.base.mcc, cid.base.mnc,
+ cid.base.nci, cid.base.operatorNames.alphaLong,
+ cid.base.operatorNames.alphaShort, cid.additionalPlmns);
}
/** @hide */
@Override
public @NonNull CellIdentityNr sanitizeLocationInfo() {
- return new CellIdentityNr(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
- mMccStr, mMncStr, CellInfo.UNAVAILABLE, mAlphaLong, mAlphaShort);
+ return new CellIdentityNr(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, mNrArfcn,
+ mBands, mMccStr, mMncStr, CellInfo.UNAVAILABLE, mAlphaLong, mAlphaShort,
+ mAdditionalPlmns);
}
/**
@@ -87,7 +109,8 @@
@Override
public int hashCode() {
- return Objects.hash(super.hashCode(), mPci, mTac, mNrArfcn, mNci);
+ return Objects.hash(super.hashCode(), mPci, mTac,
+ mNrArfcn, mBands.hashCode(), mNci, mAdditionalPlmns.hashCode());
}
@Override
@@ -98,7 +121,8 @@
CellIdentityNr o = (CellIdentityNr) other;
return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn
- && mNci == o.mNci;
+ && mBands.equals(o.mBands) && mNci == o.mNci
+ && mAdditionalPlmns.equals(o.mAdditionalPlmns);
}
/**
@@ -125,6 +149,20 @@
}
/**
+ * Get bands of the cell
+ *
+ * Reference: TS 38.101-1 table 5.2-1
+ * Reference: TS 38.101-2 table 5.2-1
+ *
+ * @return List of band number or empty list if not available.
+ */
+ @NgranBand
+ @NonNull
+ public List<Integer> getBands() {
+ return Collections.unmodifiableList(mBands);
+ }
+
+ /**
* Get the physical cell id.
* @return Integer value in range [0, 1007] or {@link CellInfo#UNAVAILABLE} if unknown.
*/
@@ -158,17 +196,33 @@
return mMncStr;
}
+ /** @hide */
+ @Override
+ public int getChannelNumber() {
+ return mNrArfcn;
+ }
+
+ /**
+ * @return a list of additional PLMN IDs supported by this cell.
+ */
+ @NonNull
+ public List<String> getAdditionalPlmns() {
+ return Collections.unmodifiableList(mAdditionalPlmns);
+ }
+
@Override
public String toString() {
return new StringBuilder(TAG + ":{")
.append(" mPci = ").append(mPci)
.append(" mTac = ").append(mTac)
.append(" mNrArfcn = ").append(mNrArfcn)
+ .append(" mBands = ").append(mBands)
.append(" mMcc = ").append(mMccStr)
.append(" mMnc = ").append(mMncStr)
.append(" mNci = ").append(mNci)
.append(" mAlphaLong = ").append(mAlphaLong)
.append(" mAlphaShort = ").append(mAlphaShort)
+ .append(" mAdditionalPlmns = ").append(mAdditionalPlmns)
.append(" }")
.toString();
}
@@ -179,7 +233,9 @@
dest.writeInt(mPci);
dest.writeInt(mTac);
dest.writeInt(mNrArfcn);
+ dest.writeList(mBands);
dest.writeLong(mNci);
+ dest.writeList(mAdditionalPlmns);
}
/** Construct from Parcel, type has already been processed */
@@ -188,7 +244,9 @@
mPci = in.readInt();
mTac = in.readInt();
mNrArfcn = in.readInt();
+ mBands = in.readArrayList(null);
mNci = in.readLong();
+ mAdditionalPlmns = in.readArrayList(null);
}
/** Implement the Parcelable interface */
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 4bb3a95..2ff351c 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -21,6 +21,8 @@
import android.os.Parcel;
import android.telephony.gsm.GsmCellLocation;
+import java.util.Collections;
+import java.util.List;
import java.util.Objects;
/**
@@ -46,6 +48,11 @@
// 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
private final int mUarfcn;
+ // a list of additional PLMN-IDs reported for this cell
+ private final List<String> mAdditionalPlmns;
+
+ private ClosedSubscriberGroupInfo mCsgInfo;
+
/**
* @hide
*/
@@ -55,6 +62,8 @@
mCid = CellInfo.UNAVAILABLE;
mCpid = CellInfo.UNAVAILABLE;
mUarfcn = CellInfo.UNAVAILABLE;
+ mAdditionalPlmns = Collections.emptyList();
+ mCsgInfo = null;
}
/**
@@ -68,39 +77,57 @@
* @param uarfcn 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
* @param alphal long alpha Operator Name String or Enhanced Operator Name String
* @param alphas short alpha Operator Name String or Enhanced Operator Name String
+ * @param additionalPlmns a list of additional PLMN IDs broadcast by the cell
+ * @param csgInfo info about the closed subscriber group broadcast by the cell
*
* @hide
*/
public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid, int uarfcn,
- String alphal, String alphas) {
+ String alphal, String alphas, @NonNull List<String> additionalPlmns,
+ ClosedSubscriberGroupInfo csgInfo) {
super(TAG, CellInfo.TYPE_TDSCDMA, mcc, mnc, alphal, alphas);
mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
mCpid = inRangeOrUnavailable(cpid, 0, MAX_CPID);
mUarfcn = inRangeOrUnavailable(uarfcn, 0, MAX_UARFCN);
+ mAdditionalPlmns = additionalPlmns;
+ mCsgInfo = csgInfo;
}
private CellIdentityTdscdma(CellIdentityTdscdma cid) {
this(cid.mMccStr, cid.mMncStr, cid.mLac, cid.mCid,
- cid.mCpid, cid.mUarfcn, cid.mAlphaLong, cid.mAlphaShort);
+ cid.mCpid, cid.mUarfcn, cid.mAlphaLong,
+ cid.mAlphaShort, cid.mAdditionalPlmns, cid.mCsgInfo);
}
/** @hide */
public CellIdentityTdscdma(android.hardware.radio.V1_0.CellIdentityTdscdma cid) {
- this(cid.mcc, cid.mnc, cid.lac, cid.cid, cid.cpid, CellInfo.UNAVAILABLE, "", "");
+ this(cid.mcc, cid.mnc, cid.lac, cid.cid, cid.cpid, CellInfo.UNAVAILABLE, "", "",
+ Collections.emptyList(), null);
}
/** @hide */
public CellIdentityTdscdma(android.hardware.radio.V1_2.CellIdentityTdscdma cid) {
this(cid.base.mcc, cid.base.mnc, cid.base.lac, cid.base.cid, cid.base.cpid,
- cid.uarfcn, cid.operatorNames.alphaLong, cid.operatorNames.alphaShort);
+ cid.uarfcn, cid.operatorNames.alphaLong, cid.operatorNames.alphaShort,
+ Collections.emptyList(), null);
+ }
+
+ /** @hide */
+ public CellIdentityTdscdma(android.hardware.radio.V1_5.CellIdentityTdscdma cid) {
+ this(cid.base.base.mcc, cid.base.base.mnc, cid.base.base.lac, cid.base.base.cid,
+ cid.base.base.cpid, cid.base.uarfcn, cid.base.operatorNames.alphaLong,
+ cid.base.operatorNames.alphaShort,
+ cid.additionalPlmns, cid.optionalCsgInfo.csgInfo() != null
+ ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo()) : null);
}
/** @hide */
@Override
public @NonNull CellIdentityTdscdma sanitizeLocationInfo() {
return new CellIdentityTdscdma(mMccStr, mMncStr, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
- CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, mAlphaLong, mAlphaShort);
+ CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, mAlphaLong, mAlphaShort,
+ mAdditionalPlmns, null);
}
CellIdentityTdscdma copy() {
@@ -171,6 +198,22 @@
return mUarfcn;
}
+ /**
+ * @return a list of additional PLMN IDs supported by this cell.
+ */
+ @NonNull
+ public List<String> getAdditionalPlmns() {
+ return mAdditionalPlmns;
+ }
+
+ /**
+ * @return closed subscriber group information about the cell if available, otherwise null.
+ */
+ @Nullable
+ public ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo() {
+ return mCsgInfo;
+ }
+
/** @hide */
@NonNull
@Override
@@ -198,12 +241,15 @@
&& mCid == o.mCid
&& mCpid == o.mCpid
&& mUarfcn == o.mUarfcn
+ && mAdditionalPlmns.equals(o.mAdditionalPlmns)
+ && Objects.equals(mCsgInfo, o.mCsgInfo)
&& super.equals(other);
}
@Override
public int hashCode() {
- return Objects.hash(mLac, mCid, mCpid, mUarfcn, super.hashCode());
+ return Objects.hash(mLac, mCid, mCpid, mUarfcn,
+ mAdditionalPlmns.hashCode(), mCsgInfo, super.hashCode());
}
@Override
@@ -217,6 +263,8 @@
.append(" mCid=").append(mCid)
.append(" mCpid=").append(mCpid)
.append(" mUarfcn=").append(mUarfcn)
+ .append(" mAdditionalPlmns=").append(mAdditionalPlmns)
+ .append(" mCsgInfo=").append(mCsgInfo)
.append("}").toString();
}
@@ -235,6 +283,8 @@
dest.writeInt(mCid);
dest.writeInt(mCpid);
dest.writeInt(mUarfcn);
+ dest.writeList(mAdditionalPlmns);
+ dest.writeParcelable(mCsgInfo, flags);
}
/** Construct from Parcel, type has already been processed */
@@ -244,6 +294,8 @@
mCid = in.readInt();
mCpid = in.readInt();
mUarfcn = in.readInt();
+ mAdditionalPlmns = in.readArrayList(null);
+ mCsgInfo = in.readParcelable(null);
if (DBG) log(toString());
}
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 4ecd134..9be42a1 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -23,6 +23,8 @@
import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
+import java.util.Collections;
+import java.util.List;
import java.util.Objects;
/**
@@ -47,6 +49,12 @@
@UnsupportedAppUsage
private final int mUarfcn;
+ // a list of additional PLMN-IDs reported for this cell
+ private final List<String> mAdditionalPlmns;
+
+ @Nullable
+ private final ClosedSubscriberGroupInfo mCsgInfo;
+
/**
* @hide
*/
@@ -56,6 +64,8 @@
mCid = CellInfo.UNAVAILABLE;
mPsc = CellInfo.UNAVAILABLE;
mUarfcn = CellInfo.UNAVAILABLE;
+ mAdditionalPlmns = Collections.emptyList();
+ mCsgInfo = null;
}
/**
@@ -68,33 +78,49 @@
* @param mncStr 2 or 3-digit Mobile Network Code in string format
* @param alphal long alpha Operator Name String or Enhanced Operator Name String
* @param alphas short alpha Operator Name String or Enhanced Operator Name String
+ * @param additionalPlmns a list of additional PLMN IDs broadcast by the cell
+ * @param csgInfo info about the closed subscriber group broadcast by the cell
*
* @hide
*/
public CellIdentityWcdma (int lac, int cid, int psc, int uarfcn,
- String mccStr, String mncStr, String alphal, String alphas) {
+ String mccStr, String mncStr, String alphal, String alphas,
+ @NonNull List<String> additionalPlmns,
+ @Nullable ClosedSubscriberGroupInfo csgInfo) {
super(TAG, CellInfo.TYPE_WCDMA, mccStr, mncStr, alphal, alphas);
mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
mPsc = inRangeOrUnavailable(psc, 0, MAX_PSC);
mUarfcn = inRangeOrUnavailable(uarfcn, 0, MAX_UARFCN);
+ mAdditionalPlmns = additionalPlmns;
+ mCsgInfo = csgInfo;
}
/** @hide */
public CellIdentityWcdma(android.hardware.radio.V1_0.CellIdentityWcdma cid) {
- this(cid.lac, cid.cid, cid.psc, cid.uarfcn, cid.mcc, cid.mnc, "", "");
+ this(cid.lac, cid.cid, cid.psc, cid.uarfcn, cid.mcc, cid.mnc, "", "",
+ Collections.emptyList(), null);
}
/** @hide */
public CellIdentityWcdma(android.hardware.radio.V1_2.CellIdentityWcdma cid) {
this(cid.base.lac, cid.base.cid, cid.base.psc, cid.base.uarfcn,
cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong,
- cid.operatorNames.alphaShort);
+ cid.operatorNames.alphaShort, Collections.emptyList(), null);
+ }
+
+ /** @hide */
+ public CellIdentityWcdma(android.hardware.radio.V1_5.CellIdentityWcdma cid) {
+ this(cid.base.base.lac, cid.base.base.cid, cid.base.base.psc, cid.base.base.uarfcn,
+ cid.base.base.mcc, cid.base.base.mnc, cid.base.operatorNames.alphaLong,
+ cid.base.operatorNames.alphaShort, cid.additionalPlmns,
+ cid.optionalCsgInfo.csgInfo() != null
+ ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo()) : null);
}
private CellIdentityWcdma(CellIdentityWcdma cid) {
this(cid.mLac, cid.mCid, cid.mPsc, cid.mUarfcn, cid.mMccStr,
- cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort);
+ cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort, cid.mAdditionalPlmns, cid.mCsgInfo);
}
/** @hide */
@@ -102,7 +128,7 @@
public @NonNull CellIdentityWcdma sanitizeLocationInfo() {
return new CellIdentityWcdma(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, mMccStr, mMncStr,
- mAlphaLong, mAlphaShort);
+ mAlphaLong, mAlphaShort, mAdditionalPlmns, null);
}
CellIdentityWcdma copy() {
@@ -180,7 +206,7 @@
@Override
public int hashCode() {
- return Objects.hash(mLac, mCid, mPsc, super.hashCode());
+ return Objects.hash(mLac, mCid, mPsc, mAdditionalPlmns.hashCode(), super.hashCode());
}
/**
@@ -197,6 +223,22 @@
return mUarfcn;
}
+ /**
+ * @return a list of additional PLMN IDs supported by this cell.
+ */
+ @NonNull
+ public List<String> getAdditionalPlmns() {
+ return mAdditionalPlmns;
+ }
+
+ /**
+ * @return closed subscriber group information about the cell if available, otherwise null.
+ */
+ @Nullable
+ public ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo() {
+ return mCsgInfo;
+ }
+
/** @hide */
@NonNull
@Override
@@ -228,6 +270,8 @@
&& mUarfcn == o.mUarfcn
&& TextUtils.equals(mMccStr, o.mMccStr)
&& TextUtils.equals(mMncStr, o.mMncStr)
+ && mAdditionalPlmns.equals(o.mAdditionalPlmns)
+ && Objects.equals(mCsgInfo, o.mCsgInfo)
&& super.equals(other);
}
@@ -242,6 +286,8 @@
.append(" mMnc=").append(mMncStr)
.append(" mAlphaLong=").append(mAlphaLong)
.append(" mAlphaShort=").append(mAlphaShort)
+ .append(" mAdditionalPlmns=").append(mAdditionalPlmns)
+ .append(" mCsgInfo=").append(mCsgInfo)
.append("}").toString();
}
@@ -254,6 +300,8 @@
dest.writeInt(mCid);
dest.writeInt(mPsc);
dest.writeInt(mUarfcn);
+ dest.writeList(mAdditionalPlmns);
+ dest.writeParcelable(mCsgInfo, flags);
}
/** Construct from Parcel, type has already been processed */
@@ -263,6 +311,8 @@
mCid = in.readInt();
mPsc = in.readInt();
mUarfcn = in.readInt();
+ mAdditionalPlmns = in.readArrayList(null);
+ mCsgInfo = in.readParcelable(null);
if (DBG) log(toString());
}
diff --git a/telephony/java/android/telephony/ClosedSubscriberGroupInfo.aidl b/telephony/java/android/telephony/ClosedSubscriberGroupInfo.aidl
new file mode 100644
index 0000000..cbe7638
--- /dev/null
+++ b/telephony/java/android/telephony/ClosedSubscriberGroupInfo.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.telephony;
+
+parcelable ClosedSubscriberGroupInfo;
diff --git a/telephony/java/android/telephony/ClosedSubscriberGroupInfo.java b/telephony/java/android/telephony/ClosedSubscriberGroupInfo.java
new file mode 100644
index 0000000..e7dfe634
--- /dev/null
+++ b/telephony/java/android/telephony/ClosedSubscriberGroupInfo.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Information to represent a closed subscriber group.
+ */
+public final class ClosedSubscriberGroupInfo implements Parcelable {
+ private static final String TAG = "ClosedSubscriberGroupInfo";
+
+ private final boolean mCsgIndicator;
+
+ private final String mHomeNodebName;
+
+ private final int mCsgIdentity;
+
+ /** @hide */
+ public ClosedSubscriberGroupInfo(boolean csgIndicator, @Nullable String homeNodebName,
+ int csgIdentity) {
+ mCsgIndicator = csgIndicator;
+ mHomeNodebName = (homeNodebName == null) ? "" : homeNodebName;
+ mCsgIdentity = csgIdentity;
+ }
+
+ /** @hide */
+ public ClosedSubscriberGroupInfo(
+ @NonNull android.hardware.radio.V1_5.ClosedSubscriberGroupInfo csgInfo) {
+ this(csgInfo.csgIndication, csgInfo.homeNodebName, csgInfo.csgIdentity);
+ }
+
+ /**
+ * Indicates whether the cell is restricted to only CSG members.
+ *
+ * A cell not broadcasting the CSG Indication but reporting CSG information is considered a
+ * Hybrid Cell.
+ * Refer to the "csg-Indication" field in 3GPP TS 36.331 section 6.2.2
+ * SystemInformationBlockType1.
+ * Also refer to "CSG Indicator" in 3GPP TS 25.331 section 10.2.48.8.1 and TS 25.304.
+ *
+ * @return true if the cell is restricted to group members only.
+ */
+ public boolean getCsgIndicator() {
+ return mCsgIndicator;
+ }
+
+ /**
+ * Returns human-readable name of the closed subscriber group operating this cell (Node-B).
+ *
+ * Refer to "hnb-Name" in TS 36.331 section 6.2.2 SystemInformationBlockType9.
+ * Also refer to "HNB Name" in 3GPP TS25.331 section 10.2.48.8.23 and TS 23.003 section 4.8.
+ *
+ * @return the home Node-B name if available.
+ */
+ public @NonNull String getHomeNodebName() {
+ return mHomeNodebName;
+ }
+
+ /**
+ * The identity of the closed subscriber group that the cell belongs to.
+ *
+ * Refer to "CSG-Identity" in TS 36.336 section 6.3.4.
+ * Also refer to "CSG Identity" in 3GPP TS 25.331 section 10.3.2.8 and TS 23.003 section 4.7.
+ *
+ * @return the unique 27-bit CSG Identity.
+ */
+ @IntRange(from = 0, to = 0x7FFFFFF)
+ public int getCsgIdentity() {
+ return mCsgIdentity;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mCsgIndicator, mHomeNodebName, mCsgIdentity);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof ClosedSubscriberGroupInfo)) {
+ return false;
+ }
+
+ ClosedSubscriberGroupInfo o = (ClosedSubscriberGroupInfo) other;
+ return mCsgIndicator == o.mCsgIndicator && mHomeNodebName == o.mHomeNodebName
+ && mCsgIdentity == o.mCsgIdentity;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder(TAG + ":{")
+ .append(" mCsgIndicator = ").append(mCsgIndicator)
+ .append(" mHomeNodebName = ").append(mHomeNodebName)
+ .append(" mCsgIdentity = ").append(mCsgIdentity)
+ .toString();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int type) {
+ dest.writeBoolean(mCsgIndicator);
+ dest.writeString(mHomeNodebName);
+ dest.writeInt(mCsgIdentity);
+ }
+
+ /** Construct from Parcel, type has already been processed */
+ private ClosedSubscriberGroupInfo(Parcel in) {
+ this(in.readBoolean(), in.readString(), in.readInt());
+ }
+
+ /**
+ * Implement the Parcelable interface
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ public static final @android.annotation.NonNull Creator<ClosedSubscriberGroupInfo> CREATOR =
+ new Creator<ClosedSubscriberGroupInfo>() {
+ @Override
+ public ClosedSubscriberGroupInfo createFromParcel(Parcel in) {
+ return createFromParcelBody(in);
+ }
+
+ @Override
+ public ClosedSubscriberGroupInfo[] newArray(int size) {
+ return new ClosedSubscriberGroupInfo[size];
+ }
+ };
+
+ /** @hide */
+ protected static ClosedSubscriberGroupInfo createFromParcelBody(Parcel in) {
+ return new ClosedSubscriberGroupInfo(in);
+ }
+}
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index c706d28..9b4292f 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -54,6 +54,43 @@
"com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
/**
+ * An intent action indicating that IMS registration for WiFi calling has resulted in an error.
+ * Contains error information that should be displayed to the user.
+ * <p>
+ * This intent will contain the following extra key/value pairs:
+ * {@link #EXTRA_WFC_REGISTRATION_FAILURE_TITLE}
+ * and {@link #EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE}, which contain carrier specific
+ * error information that should be displayed to the user.
+ * <p>
+ * Usage: This intent is sent as an ordered broadcast. If the settings application is going
+ * to show the error information specified to the user, it should respond to
+ * {@link android.content.BroadcastReceiver#setResultCode(int)} with
+ * {@link android.app.Activity#RESULT_CANCELED}, which will signal to the framework that the
+ * event was handled. If the framework does not receive a response to the ordered broadcast,
+ * it will then show a notification to the user indicating that there was a registration
+ * failure.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_WFC_IMS_REGISTRATION_ERROR =
+ "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
+
+ /**
+ * An extra key corresponding to a String value which contains the carrier specific title to be
+ * displayed as part of the message shown to the user when there is an error registering for
+ * WiFi calling.
+ */
+ public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE =
+ "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
+
+ /**
+ * An extra key corresponding to a String value which contains the carrier specific message to
+ * be displayed as part of the message shown to the user when there is an error registering for
+ * WiFi calling.
+ */
+ public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE =
+ "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
+
+ /**
* Use {@link Context#getSystemService(String)} to get an instance of this class.
* @hide
*/
diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java
index aebe780..1ba21f2 100644
--- a/telephony/java/android/telephony/ModemActivityInfo.java
+++ b/telephony/java/android/telephony/ModemActivityInfo.java
@@ -17,7 +17,6 @@
package android.telephony;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -60,7 +59,7 @@
private int mRxTimeMs;
public ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
- @Nullable int[] txTimeMs, int rxTimeMs) {
+ @NonNull int[] txTimeMs, int rxTimeMs) {
mTimestamp = timestamp;
mSleepTimeMs = sleepTimeMs;
mIdleTimeMs = idleTimeMs;
@@ -69,13 +68,10 @@
}
/** helper API to populate tx power range for each bucket **/
- private void populateTransmitPowerRange(@Nullable int[] transmitPowerMs) {
+ private void populateTransmitPowerRange(@NonNull int[] transmitPowerMs) {
int i = 0;
- if (transmitPowerMs != null) {
- for ( ; i < Math.min(transmitPowerMs.length, TX_POWER_LEVELS); i++) {
- mTransmitPowerInfo.add(i,
- new TransmitPower(TX_POWER_RANGES[i], transmitPowerMs[i]));
- }
+ for ( ; i < Math.min(transmitPowerMs.length, TX_POWER_LEVELS); i++) {
+ mTransmitPowerInfo.add(i, new TransmitPower(TX_POWER_RANGES[i], transmitPowerMs[i]));
}
// Make sure that mTransmitPowerInfo is fully initialized.
for ( ; i < TX_POWER_LEVELS; i++) {
@@ -98,7 +94,7 @@
return 0;
}
- public static final @NonNull Parcelable.Creator<ModemActivityInfo> CREATOR =
+ public static final @android.annotation.NonNull Parcelable.Creator<ModemActivityInfo> CREATOR =
new Parcelable.Creator<ModemActivityInfo>() {
public ModemActivityInfo createFromParcel(Parcel in) {
long timestamp = in.readLong();
@@ -153,7 +149,7 @@
}
/** @hide */
- public void setTransmitTimeMillis(@Nullable int[] txTimeMs) {
+ public void setTransmitTimeMillis(int[] txTimeMs) {
populateTransmitPowerRange(txTimeMs);
}
diff --git a/telephony/java/android/telephony/ModemInfo.java b/telephony/java/android/telephony/ModemInfo.java
deleted file mode 100644
index c0833af..0000000
--- a/telephony/java/android/telephony/ModemInfo.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Objects;
-
-/**
- * Information of a single logical modem indicating
- * its id, supported rats and whether it supports voice or data, etc.
- * @hide
- */
-public class ModemInfo implements Parcelable {
- public final int modemId;
- public final int rat; /* bitset */
- public final boolean isVoiceSupported;
- public final boolean isDataSupported;
-
- // TODO b/121394331: Clean up this class after V1_1.PhoneCapability cleanup.
- public ModemInfo(int modemId) {
- this(modemId, 0, true, true);
- }
-
- public ModemInfo(int modemId, int rat, boolean isVoiceSupported, boolean isDataSupported) {
- this.modemId = modemId;
- this.rat = rat;
- this.isVoiceSupported = isVoiceSupported;
- this.isDataSupported = isDataSupported;
- }
-
- public ModemInfo(Parcel in) {
- modemId = in.readInt();
- rat = in.readInt();
- isVoiceSupported = in.readBoolean();
- isDataSupported = in.readBoolean();
- }
-
- @Override
- public String toString() {
- return "modemId=" + modemId + " rat=" + rat + " isVoiceSupported:" + isVoiceSupported
- + " isDataSupported:" + isDataSupported;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(modemId, rat, isVoiceSupported, isDataSupported);
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == null || !(o instanceof ModemInfo) || hashCode() != o.hashCode()) {
- return false;
- }
-
- if (this == o) {
- return true;
- }
-
- ModemInfo s = (ModemInfo) o;
-
- return (modemId == s.modemId
- && rat == s.rat
- && isVoiceSupported == s.isVoiceSupported
- && isDataSupported == s.isDataSupported);
- }
-
- /**
- * {@link Parcelable#describeContents}
- */
- public @ContentsFlags int describeContents() {
- return 0;
- }
-
- /**
- * {@link Parcelable#writeToParcel}
- */
- public void writeToParcel(Parcel dest, @WriteFlags int flags) {
- dest.writeInt(modemId);
- dest.writeInt(rat);
- dest.writeBoolean(isVoiceSupported);
- dest.writeBoolean(isDataSupported);
- }
-
- public static final @android.annotation.NonNull Parcelable.Creator<ModemInfo> CREATOR = new Parcelable.Creator() {
- public ModemInfo createFromParcel(Parcel in) {
- return new ModemInfo(in);
- }
-
- public ModemInfo[] newArray(int size) {
- return new ModemInfo[size];
- }
- };
-}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 2f95a50..5cd7cf8 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -1658,7 +1658,7 @@
}
/**
- * Copy a raw SMS PDU to the ICC.
+ * Copies a raw SMS PDU to the ICC.
* ICC (Integrated Circuit Card) is the card of the device.
* For example, this can be the SIM or USIM for GSM.
*
@@ -1672,21 +1672,26 @@
* operation is performed on the correct subscription.
* </p>
*
- * @param smsc the SMSC for this message, or NULL for the default SMSC
- * @param pdu the raw PDU to store
- * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD,
- * STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT)
- * @return true for success
+ * @param smsc the SMSC for this messag or null for the default SMSC.
+ * @param pdu the raw PDU to store.
+ * @param status message status. One of these status:
+ * <code>STATUS_ON_ICC_READ</code>
+ * <code>STATUS_ON_ICC_UNREAD</code>
+ * <code>STATUS_ON_ICC_SENT</code>
+ * <code>STATUS_ON_ICC_UNSENT</code>
+ * @return true for success. Otherwise false.
*
- * @throws IllegalArgumentException if pdu is NULL
- * {@hide}
+ * @throws IllegalArgumentException if pdu is null.
+ * @hide
*/
- @UnsupportedAppUsage
- public boolean copyMessageToIcc(byte[] smsc, byte[] pdu,int status) {
+ @SystemApi
+ @RequiresPermission(Manifest.permission.ACCESS_MESSAGES_ON_ICC)
+ public boolean copyMessageToIcc(
+ @Nullable byte[] smsc, @NonNull byte[] pdu, @StatusOnIcc int status) {
boolean success = false;
- if (null == pdu) {
- throw new IllegalArgumentException("pdu is NULL");
+ if (pdu == null) {
+ throw new IllegalArgumentException("pdu is null");
}
try {
ISms iSms = getISmsService();
@@ -1703,7 +1708,7 @@
}
/**
- * Delete the specified message from the ICC.
+ * Deletes the specified message from the ICC.
* ICC (Integrated Circuit Card) is the card of the device.
* For example, this can be the SIM or USIM for GSM.
*
@@ -1787,7 +1792,7 @@
}
/**
- * Retrieves all messages currently stored on ICC.
+ * Retrieves all messages currently stored on the ICC.
* ICC (Integrated Circuit Card) is the card of the device.
* For example, this can be the SIM or USIM for GSM.
*
@@ -1953,8 +1958,7 @@
}
/**
- * Create a list of <code>SmsMessage</code>s from a list of RawSmsData
- * records returned by <code>getAllMessagesFromIcc()</code>
+ * Creates a list of <code>SmsMessage</code>s from a list of SmsRawData records.
*
* <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
* applications or the Telephony framework and will never trigger an SMS disambiguation
@@ -1966,8 +1970,7 @@
* operation is performed on the correct subscription.
* </p>
*
- * @param records SMS EF records, returned by
- * <code>getAllMessagesFromIcc</code>
+ * @param records SMS EF records.
* @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
*/
private ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
@@ -1978,7 +1981,7 @@
SmsRawData data = records.get(i);
// List contains all records, including "free" records (null)
if (data != null) {
- SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes(),
+ SmsMessage sms = SmsMessage.createFromEfRecord(i + 1, data.getBytes(),
getSubscriptionId());
if (sms != null) {
messages.add(sms);
@@ -2122,6 +2125,17 @@
return ret;
}
+ /** @hide */
+ @IntDef(prefix = { "STATUS_ON_ICC_" }, value = {
+ STATUS_ON_ICC_FREE,
+ STATUS_ON_ICC_READ,
+ STATUS_ON_ICC_UNREAD,
+ STATUS_ON_ICC_SENT,
+ STATUS_ON_ICC_UNSENT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StatusOnIcc {}
+
// see SmsMessage.getStatusOnIcc
/** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index eefbd44..7a30f14 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -278,41 +278,24 @@
}
/**
- * Create an SmsMessage from an SMS EF record.
+ * Creates an SmsMessage from an SMS EF record.
*
- * @param index Index of SMS record. This should be index in ArrayList
- * returned by SmsManager.getAllMessagesFromSim + 1.
+ * @param index Index of SMS EF record.
* @param data Record data.
* @return An SmsMessage representing the record.
*
* @hide
*/
public static SmsMessage createFromEfRecord(int index, byte[] data) {
- SmsMessageBase wrappedMessage;
-
- if (isCdmaVoice()) {
- wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord(
- index, data);
- } else {
- wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord(
- index, data);
- }
-
- if (wrappedMessage != null) {
- return new SmsMessage(wrappedMessage);
- } else {
- Rlog.e(LOG_TAG, "createFromEfRecord(): wrappedMessage is null");
- return null;
- }
+ return createFromEfRecord(index, data, SmsManager.getDefaultSmsSubscriptionId());
}
/**
- * Create an SmsMessage from an SMS EF record.
+ * Creates an SmsMessage from an SMS EF record.
*
- * @param index Index of SMS record. This should be index in ArrayList
- * returned by SmsManager.getAllMessagesFromSim + 1.
+ * @param index Index of SMS EF record.
* @param data Record data.
- * @param subId Subscription Id of the SMS
+ * @param subId Subscription Id associated with the record.
* @return An SmsMessage representing the record.
*
* @hide
@@ -602,13 +585,15 @@
*/
/**
- * Get an SMS-SUBMIT PDU for a destination address and a message.
+ * Gets an SMS-SUBMIT PDU for a destination address and a message.
* This method will not attempt to use any GSM national language 7 bit encodings.
*
- * @param scAddress Service Centre address. Null means use default.
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param message string representation of the message payload.
+ * @param statusReportRequested indicates whether a report is requested for this message.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
*/
public static SubmitPdu getSubmitPdu(String scAddress,
String destinationAddress, String message, boolean statusReportRequested) {
@@ -621,17 +606,16 @@
}
/**
- * Get an SMS-SUBMIT PDU for a destination address and a message.
+ * Gets an SMS-SUBMIT PDU for a destination address and a message.
* This method will not attempt to use any GSM national language 7 bit encodings.
*
- * @param scAddress Service Centre address. Null means use default.
+ * @param scAddress Service Centre address. Null means use default.
* @param destinationAddress the address of the destination for the message.
- * @param message String representation of the message payload.
- * @param statusReportRequested Indicates whether a report is requested for this message.
- * @param subId Subscription of the message
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param message string representation of the message payload.
+ * @param statusReportRequested indicates whether a report is requested for this message.
+ * @param subId subscription of the message.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
* @hide
*/
public static SubmitPdu getSubmitPdu(String scAddress,
@@ -649,17 +633,16 @@
}
/**
- * Get an SMS-SUBMIT PDU for a data message to a destination address & port.
+ * Gets an SMS-SUBMIT PDU for a data message to a destination address & port.
* This method will not attempt to use any GSM national language 7 bit encodings.
*
- * @param scAddress Service Centre address. null == use default
- * @param destinationAddress the address of the destination for the message
- * @param destinationPort the port to deliver the message to at the
- * destination
- * @param data the data for the message
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param destinationPort the port to deliver the message to at the destination.
+ * @param data the data for the message.
+ * @param statusReportRequested indicates whether a report is requested for this message.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
*/
public static SubmitPdu getSubmitPdu(String scAddress,
String destinationAddress, short destinationPort, byte[] data,
@@ -677,6 +660,55 @@
return new SubmitPdu(spb);
}
+ // TODO: SubmitPdu class is used for SMS-DELIVER also now. Refactor for SubmitPdu and new
+ // DeliverPdu accordingly.
+
+ /**
+ * Gets an SMS PDU to store in the ICC.
+ *
+ * @param subId subscription of the message.
+ * @param status message status. One of these status:
+ * <code>SmsManager.STATUS_ON_ICC_READ</code>
+ * <code>SmsManager.STATUS_ON_ICC_UNREAD</code>
+ * <code>SmsManager.STATUS_ON_ICC_SENT</code>
+ * <code>SmsManager.STATUS_ON_ICC_UNSENT</code>
+ * @param scAddress Service Centre address. Null means use default.
+ * @param address destination or originating address.
+ * @param message string representation of the message payload.
+ * @param date the time stamp the message was received.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ public static SubmitPdu getSmsPdu(int subId, @SmsManager.StatusOnIcc int status,
+ @Nullable String scAddress, @NonNull String address, @NonNull String message,
+ long date) {
+ SubmitPduBase spb;
+ if (isCdmaVoice(subId)) { // 3GPP2 format
+ if (status == SmsManager.STATUS_ON_ICC_READ
+ || status == SmsManager.STATUS_ON_ICC_UNREAD) { // Deliver PDU
+ spb = com.android.internal.telephony.cdma.SmsMessage.getDeliverPdu(address,
+ message, date);
+ } else { // Submit PDU
+ spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+ address, message, false /* statusReportRequested */, null /* smsHeader */);
+ }
+ } else { // 3GPP format
+ if (status == SmsManager.STATUS_ON_ICC_READ
+ || status == SmsManager.STATUS_ON_ICC_UNREAD) { // Deliver PDU
+ spb = com.android.internal.telephony.gsm.SmsMessage.getDeliverPdu(scAddress,
+ address, message, date);
+ } else { // Submit PDU
+ spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+ address, message, false /* statusReportRequested */, null /* header */);
+ }
+ }
+
+ return spb != null ? new SubmitPdu(spb) : null;
+ }
+
/**
* Get an SMS-SUBMIT PDU's encoded message.
* This is used by Bluetooth MAP profile to handle long non UTF-8 SMS messages.
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 8de5b85..247ffd7 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -16,8 +16,8 @@
package android.telephony;
-import static android.net.NetworkPolicyManager.OVERRIDE_CONGESTED;
-import static android.net.NetworkPolicyManager.OVERRIDE_UNMETERED;
+import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_CONGESTED;
+import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED;
import android.Manifest;
import android.annotation.CallbackExecutor;
@@ -45,6 +45,7 @@
import android.database.ContentObserver;
import android.net.INetworkPolicyManager;
import android.net.NetworkCapabilities;
+import android.net.NetworkPolicyManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -53,6 +54,7 @@
import android.os.ParcelUuid;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.provider.Telephony.SimInfo;
import android.telephony.euicc.EuiccManager;
import android.telephony.ims.ImsMmTelManager;
@@ -676,6 +678,13 @@
public static final String WFC_IMS_ROAMING_ENABLED = SimInfo.WFC_IMS_ROAMING_ENABLED;
/**
+ * Determines if the user has enabled IMS RCS User Capability Exchange (UCE) for this
+ * subscription.
+ * @hide
+ */
+ public static final String IMS_RCS_UCE_ENABLED = SimInfo.IMS_RCS_UCE_ENABLED;
+
+ /**
* TelephonyProvider column name for whether a subscription is opportunistic, that is,
* whether the network it connects to is limited in functionality or coverage.
* For example, CBRS.
@@ -957,6 +966,11 @@
mContext = context;
}
+ private NetworkPolicyManager getNetworkPolicyManager() {
+ return (NetworkPolicyManager) mContext
+ .getSystemService(Context.NETWORK_POLICY_SERVICE);
+ }
+
/**
* @deprecated developers should always obtain references directly from
* {@link Context#getSystemService(Class)}.
@@ -967,13 +981,10 @@
.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
}
- private final INetworkPolicyManager getNetworkPolicy() {
+ private INetworkPolicyManager getINetworkPolicyManager() {
if (mNetworkPolicy == null) {
mNetworkPolicy = INetworkPolicyManager.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getNetworkPolicyServiceRegisterer()
- .get());
+ ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
}
return mNetworkPolicy;
}
@@ -2587,14 +2598,10 @@
* outlined above.
*/
public @NonNull List<SubscriptionPlan> getSubscriptionPlans(int subId) {
- try {
- SubscriptionPlan[] subscriptionPlans =
- getNetworkPolicy().getSubscriptionPlans(subId, mContext.getOpPackageName());
- return subscriptionPlans == null
- ? Collections.emptyList() : Arrays.asList(subscriptionPlans);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ SubscriptionPlan[] subscriptionPlans =
+ getNetworkPolicyManager().getSubscriptionPlans(subId, mContext.getOpPackageName());
+ return subscriptionPlans == null
+ ? Collections.emptyList() : Arrays.asList(subscriptionPlans);
}
/**
@@ -2620,18 +2627,14 @@
* defined in {@link SubscriptionPlan}.
*/
public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) {
- try {
- getNetworkPolicy().setSubscriptionPlans(subId,
- plans.toArray(new SubscriptionPlan[plans.size()]), mContext.getOpPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ getNetworkPolicyManager().setSubscriptionPlans(subId,
+ plans.toArray(new SubscriptionPlan[plans.size()]), mContext.getOpPackageName());
}
/** @hide */
private String getSubscriptionPlansOwner(int subId) {
try {
- return getNetworkPolicy().getSubscriptionPlansOwner(subId);
+ return getINetworkPolicyManager().getSubscriptionPlansOwner(subId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2662,13 +2665,10 @@
*/
public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
@DurationMillisLong long timeoutMillis) {
- try {
- final int overrideValue = overrideUnmetered ? OVERRIDE_UNMETERED : 0;
- getNetworkPolicy().setSubscriptionOverride(subId, OVERRIDE_UNMETERED, overrideValue,
- timeoutMillis, mContext.getOpPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+
+ final int overrideValue = overrideUnmetered ? SUBSCRIPTION_OVERRIDE_UNMETERED : 0;
+ getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_UNMETERED,
+ overrideValue, timeoutMillis, mContext.getOpPackageName());
}
/**
@@ -2697,13 +2697,9 @@
*/
public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
@DurationMillisLong long timeoutMillis) {
- try {
- final int overrideValue = overrideCongested ? OVERRIDE_CONGESTED : 0;
- getNetworkPolicy().setSubscriptionOverride(subId, OVERRIDE_CONGESTED, overrideValue,
- timeoutMillis, mContext.getOpPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ final int overrideValue = overrideCongested ? SUBSCRIPTION_OVERRIDE_CONGESTED : 0;
+ getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_CONGESTED,
+ overrideValue, timeoutMillis, mContext.getOpPackageName());
}
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 075b56b..1d89665 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -50,7 +50,6 @@
import android.net.NetworkStats;
import android.net.Uri;
import android.os.AsyncTask;
-import android.os.BatteryStats;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -100,7 +99,6 @@
import com.android.internal.telephony.IPhoneSubInfo;
import com.android.internal.telephony.ISetOpportunisticDataCallback;
import com.android.internal.telephony.ITelephony;
-import com.android.internal.telephony.ITelephonyRegistry;
import com.android.internal.telephony.IUpdateAvailableNetworksCallback;
import com.android.internal.telephony.OperatorInfo;
import com.android.internal.telephony.PhoneConstants;
@@ -161,8 +159,8 @@
* into the ResultReceiver Bundle.
* @hide
*/
- public static final String MODEM_ACTIVITY_RESULT_KEY =
- BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY;
+ @SystemApi
+ public static final String MODEM_ACTIVITY_RESULT_KEY = "controller_activity";
/**
* The process name of the Phone app as well as many other apps that use this process name, such
@@ -1245,6 +1243,80 @@
public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID";
/**
+ * Broadcast Action: The Service Provider string(s) have been updated. Activities or
+ * services that use these strings should update their display.
+ *
+ * <p>The intent will have the following extra values:
+ * <dl>
+ * <dt>{@link #EXTRA_SHOW_PLMN}</dt>
+ * <dd>Boolean that indicates whether the PLMN should be shown.</dd>
+ * <dt>{@link #EXTRA_PLMN}</dt>
+ * <dd>The operator name of the registered network, as a string.</dd>
+ * <dt>{@link #EXTRA_SHOW_SPN}</dt>
+ * <dd>Boolean that indicates whether the SPN should be shown.</dd>
+ * <dt>{@link #EXTRA_SPN}</dt>
+ * <dd>The service provider name, as a string.</dd>
+ * <dt>{@link #EXTRA_DATA_SPN}</dt>
+ * <dd>The service provider name for data service, as a string.</dd>
+ * </dl>
+ *
+ * Note that {@link #EXTRA_SHOW_PLMN} may indicate that {@link #EXTRA_PLMN} should be displayed,
+ * even though the value for {@link #EXTRA_PLMN} is null. This can happen, for example, if the
+ * phone has not registered to a network yet. In this case the receiver may substitute an
+ * appropriate placeholder string (eg, "No service").
+ *
+ * It is recommended to display {@link #EXTRA_PLMN} before / above {@link #EXTRA_SPN} if
+ * both are displayed.
+ *
+ * <p>Note: this is a protected intent that can only be sent by the system.
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SERVICE_PROVIDERS_UPDATED =
+ "android.telephony.action.SERVICE_PROVIDERS_UPDATED";
+
+ /**
+ * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate
+ * whether the PLMN should be shown.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_SHOW_PLMN = "android.telephony.extra.SHOW_PLMN";
+
+ /**
+ * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate
+ * the operator name of the registered network.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_PLMN = "android.telephony.extra.PLMN";
+
+ /**
+ * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate
+ * whether the PLMN should be shown.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_SHOW_SPN = "android.telephony.extra.SHOW_SPN";
+
+ /**
+ * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate
+ * the service provider name.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_SPN = "android.telephony.extra.SPN";
+
+ /**
+ * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate
+ * the service provider name for data service.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_DATA_SPN = "android.telephony.extra.DATA_SPN";
+
+ /**
* Broadcast intent action indicating that when data stall recovery is attempted by Telephony,
* intended for report every data stall recovery step attempted.
*
@@ -2708,7 +2780,7 @@
@UnsupportedAppUsage
public boolean isNetworkRoaming(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
- return getTelephonyProperty(subId, TelephonyProperties.operator_is_roaming(), false);
+ return getTelephonyProperty(phoneId, TelephonyProperties.operator_is_roaming(), false);
}
/**
@@ -5551,14 +5623,6 @@
.getTelephonyServiceManager().getTelephonyServiceRegisterer().get());
}
- private ITelephonyRegistry getTelephonyRegistry() {
- return ITelephonyRegistry.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getTelephonyRegistryServiceRegisterer()
- .get());
- }
-
private IOns getIOns() {
return IOns.Stub.asInterface(
TelephonyFrameworkInitializer
@@ -5612,29 +5676,27 @@
*/
public void listen(PhoneStateListener listener, int events) {
if (mContext == null) return;
- try {
- boolean notifyNow = (getITelephony() != null);
- ITelephonyRegistry registry = getTelephonyRegistry();
- if (registry != null) {
- // subId from PhoneStateListener is deprecated Q on forward, use the subId from
- // TelephonyManager instance. keep using subId from PhoneStateListener for pre-Q.
- int subId = mSubId;
- if (Compatibility.isChangeEnabled(LISTEN_CODE_CHANGE)) {
- // since mSubId in PhoneStateListener is deprecated from Q on forward, this is
- // the only place to set mSubId and its for "informational" only.
- // TODO: remove this once we completely get rid of mSubId in PhoneStateListener
- listener.mSubId = (events == PhoneStateListener.LISTEN_NONE)
- ? SubscriptionManager.INVALID_SUBSCRIPTION_ID : subId;
- } else if (listener.mSubId != null) {
- subId = listener.mSubId;
- }
- registry.listenForSubscriber(subId, getOpPackageName(), getFeatureId(),
- listener.callback, events, notifyNow);
- } else {
- Rlog.w(TAG, "telephony registry not ready.");
+ boolean notifyNow = (getITelephony() != null);
+ TelephonyRegistryManager telephonyRegistry =
+ (TelephonyRegistryManager)
+ mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
+ if (telephonyRegistry != null) {
+ // subId from PhoneStateListener is deprecated Q on forward, use the subId from
+ // TelephonyManager instance. keep using subId from PhoneStateListener for pre-Q.
+ int subId = mSubId;
+ if (Compatibility.isChangeEnabled(LISTEN_CODE_CHANGE)) {
+ // since mSubId in PhoneStateListener is deprecated from Q on forward, this is
+ // the only place to set mSubId and its for "informational" only.
+ // TODO: remove this once we completely get rid of mSubId in PhoneStateListener
+ listener.mSubId = (events == PhoneStateListener.LISTEN_NONE)
+ ? SubscriptionManager.INVALID_SUBSCRIPTION_ID : subId;
+ } else if (listener.mSubId != null) {
+ subId = listener.mSubId;
}
- } catch (RemoteException ex) {
- // system process dead
+ telephonyRegistry.listenForSubscriber(subId, getOpPackageName(), getFeatureId(),
+ listener, events, notifyNow);
+ } else {
+ Rlog.w(TAG, "telephony registry not ready.");
}
}
@@ -7939,6 +8001,36 @@
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
+ * @param operatorNumeric the PLMN ID of the network to select.
+ * @param ran the initial suggested radio access network type.
+ * If registration fails, the RAN is not available after, the RAN is not within the
+ * network types specified by {@link #setPreferredNetworkTypeBitmask}, or the value is
+ * {@link AccessNetworkConstants.AccessNetworkType#UNKNOWN}, modem will select
+ * the next best RAN for network registration.
+ * @param persistSelection whether the selection will persist until reboot.
+ * If true, only allows attaching to the selected PLMN until reboot; otherwise,
+ * attach to the chosen PLMN and resume normal network selection next time.
+ * @return {@code true} on success; {@code false} on any failure.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @SystemApi
+ public boolean setNetworkSelectionModeManual(@NonNull String operatorNumeric,
+ @AccessNetworkConstants.RadioAccessNetworkType int ran, boolean persistSelection) {
+ return setNetworkSelectionModeManual(new OperatorInfo("" /* operatorAlphaLong */,
+ "" /* operatorAlphaShort */, operatorNumeric, ran), persistSelection);
+ }
+
+ /**
+ * Ask the radio to connect to the input network and change selection mode to manual.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+ * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *
* @param operatorInfo included the PLMN id, long name, short name of the operator to attach to.
* @param persistSelection whether the selection will persist until reboot. If true, only allows
* attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
@@ -7992,6 +8084,30 @@
}
/**
+ * Get the PLMN chosen for Manual Network Selection if active.
+ * Return empty string if in automatic selection.
+ *
+ * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+ * (see {@link #hasCarrierPrivileges})
+ *
+ * @return manually selected network info on success or empty string on failure
+ */
+ @SuppressAutoDoc // No support carrier privileges (b/72967236).
+ @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
+ public @NonNull String getManualNetworkSelectionPlmn() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null && isManualNetworkSelectionAllowed()) {
+ return telephony.getManualNetworkSelectionPlmn(getSubId());
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getManualNetworkSelectionPlmn RemoteException", ex);
+ }
+ return "";
+ }
+
+ /**
* Query Telephony to see if there has recently been an emergency SMS sent to the network by the
* user and we are still within the time interval after the emergency SMS was sent that we are
* considered in Emergency SMS mode.
@@ -10206,7 +10322,8 @@
* Requests the modem activity info. The recipient will place the result
* in `result`.
* @param result The object on which the recipient will send the resulting
- * {@link android.telephony.ModemActivityInfo} object.
+ * {@link android.telephony.ModemActivityInfo} object with key of
+ * {@link #MODEM_ACTIVITY_RESULT_KEY}.
* @hide
*/
@SystemApi
@@ -11043,15 +11160,18 @@
/**
* Checks if manual network selection is allowed.
*
+ * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+ * (see {@link #hasCarrierPrivileges})
+ *
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
* given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}.
*
* @return {@code true} if manual network selection is allowed, otherwise return {@code false}.
- *
- * @hide
*/
- @SystemApi
- @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SuppressAutoDoc // No support carrier privileges (b/72967236).
+ @RequiresPermission(anyOf = {android.Manifest.permission.READ_PRECISE_PHONE_STATE,
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE})
public boolean isManualNetworkSelectionAllowed() {
try {
ITelephony telephony = getITelephony();
@@ -12810,4 +12930,22 @@
}
return 0;
}
+
+ /**
+ * Called when userActivity is signalled in the power manager.
+ * This should only be called from system Uid.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void notifyUserActivity() {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ service.userActivity();
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index d5a48df..7488a1a 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -38,6 +38,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
/**
* EuiccManager is the application interface to eUICCs, or eSIMs/embedded SIMs.
@@ -246,13 +249,69 @@
* Key for an extra set on {@link PendingIntent} result callbacks providing a detailed result
* code.
*
- * <p>This code is an implementation detail of the embedded subscription manager and is only
- * intended for logging or debugging purposes.
+ * <p>The value of this key is an integer and contains two portions. The first byte is
+ * OperationCode and the reaming three bytes is the ErrorCode.
+ *
+ * OperationCode is the first byte of the result code and is a categorization which defines what
+ * type of operation took place when an error occurred. e.g {@link #OPERATION_DOWNLOAD} means
+ * the error is related to download.Since the OperationCode only uses at most one byte, the
+ * maximum allowed quantity is 255(0xFF).
+ *
+ * ErrorCode is the remaining three bytes of the result code, and it denotes what happened.
+ * e.g a combination of {@link #OPERATION_DOWNLOAD} and {@link #ERROR_TIME_OUT} will suggest the
+ * download operation has timed out. The only exception here is
+ * {@link #OPERATION_SMDX_SUBJECT_REASON_CODE}, where instead of ErrorCode, SubjectCode[5.2.6.1
+ * from GSMA (SGP.22 v2.2) and ReasonCode[5.2.6.2] from GSMA (SGP.22 v2.2) are encoded. @see
+ * {@link #EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE} and
+ * {@link #EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE}
+ *
+ * In the case where ErrorCode contains a value of 0, it means it's an unknown error. E.g Intent
+ * only contains {@link #OPERATION_DOWNLOAD} and ErrorCode is 0 implies this is an unknown
+ * Download error.
+ *
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_OPERATION_CODE}
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_ERROR_CODE}
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE}
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE}
*/
public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE =
"android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DETAILED_CODE";
/**
+ * Key for an extra set on {@link PendingIntent} result callbacks providing a
+ * OperationCode of {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE},
+ * value will be an int.
+ */
+ public static final String EXTRA_EMBEDDED_SUBSCRIPTION_OPERATION_CODE =
+ "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_OPERATION_CODE";
+
+ /**
+ * Key for an extra set on {@link PendingIntent} result callbacks providing a
+ * ErrorCode of {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE},
+ * value will be an int.
+ */
+ public static final String EXTRA_EMBEDDED_SUBSCRIPTION_ERROR_CODE =
+ "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_ERROR_CODE";
+
+ /**
+ * Key for an extra set on {@link PendingIntent} result callbacks providing a
+ * SubjectCode[5.2.6.1] from GSMA (SGP.22 v2.2) decoded from
+ * {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE}.
+ * The value of this extra will be a String.
+ */
+ public static final String EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE =
+ "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE";
+
+ /**
+ * Key for an extra set on {@link PendingIntent} result callbacks providing a
+ * ReasonCode[5.2.6.2] from GSMA (SGP.22 v2.2) decoded from
+ * {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE}.
+ * The value of this extra will be a String.
+ */
+ public static final String EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE =
+ "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE";
+
+ /**
* Key for an extra set on {@code #getDownloadableSubscriptionMetadata} PendingIntent result
* callbacks providing the downloadable subscription metadata.
*/
@@ -491,6 +550,259 @@
@SystemApi
public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5;
+ /**
+ * List of OperationCode corresponding to {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE}'s
+ * value, an integer. @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"OPERATION_"}, value = {
+ OPERATION_SYSTEM,
+ OPERATION_SIM_SLOT,
+ OPERATION_EUICC_CARD,
+ OPERATION_SWITCH,
+ OPERATION_DOWNLOAD,
+ OPERATION_METADATA,
+ OPERATION_EUICC_GSMA,
+ OPERATION_APDU,
+ OPERATION_SMDX,
+ OPERATION_HTTP,
+ OPERATION_SMDX_SUBJECT_REASON_CODE,
+ })
+ public @interface OperationCode {
+ }
+
+ /**
+ * Internal system error.
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int OPERATION_SYSTEM = 1;
+
+ /**
+ * SIM slot error. Failed to switch slot, failed to access the physical slot etc.
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int OPERATION_SIM_SLOT = 2;
+
+ /**
+ * eUICC card error.
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int OPERATION_EUICC_CARD = 3;
+
+ /**
+ * Generic switching profile error
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int OPERATION_SWITCH = 4;
+
+ /**
+ * Download profile error.
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int OPERATION_DOWNLOAD = 5;
+
+ /**
+ * Subscription's metadata error
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int OPERATION_METADATA = 6;
+
+ /**
+ * eUICC returned an error defined in GSMA (SGP.22 v2.2) while running one of the ES10x
+ * functions.
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int OPERATION_EUICC_GSMA = 7;
+
+ /**
+ * The exception of failing to execute an APDU command. It can be caused by an error
+ * happening on opening the basic or logical channel, or the response of the APDU command is
+ * not success (0x9000).
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int OPERATION_APDU = 8;
+
+ /**
+ * SMDX(SMDP/SMDS) error
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int OPERATION_SMDX = 9;
+
+ /**
+ * SubjectCode[5.2.6.1] and ReasonCode[5.2.6.2] error from GSMA (SGP.22 v2.2)
+ * When {@link #OPERATION_SMDX_SUBJECT_REASON_CODE} is used as the
+ * {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE}, the remaining three bytes of the integer
+ * result from {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} will be used to stored the
+ * SubjectCode and ReasonCode from the GSMA spec and NOT ErrorCode.
+ *
+ * The encoding will follow the format of:
+ * 1. The first byte of the result will be 255(0xFF).
+ * 2. Remaining three bytes(24 bits) will be split into six sections, 4 bits in each section.
+ * 3. A SubjectCode/ReasonCode will take 12 bits each.
+ * 4. The maximum number can be represented per section is 15, as that is the maximum number
+ * allowed to be stored into 4 bits
+ * 5. Maximum supported nested category from GSMA is three layers. E.g 8.11.1.2 is not
+ * supported.
+ *
+ * E.g given SubjectCode(8.11.1) and ReasonCode(5.1)
+ *
+ * Base10: 0 10 8 11 1 0 5 1
+ * Base2: 0000 1010 1000 1011 0001 0000 0101 0001
+ * Base16: 0 A 8 B 1 0 5 1
+ *
+ * Thus the integer stored in {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} is
+ * 0xA8B1051(176885841)
+ *
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int OPERATION_SMDX_SUBJECT_REASON_CODE = 10;
+
+ /**
+ * HTTP error
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int OPERATION_HTTP = 11;
+
+ /**
+ * List of ErrorCode corresponding to {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE}
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"ERROR_"}, value = {
+ ERROR_CARRIER_LOCKED,
+ ERROR_INVALID_ACTIVATION_CODE,
+ ERROR_INVALID_CONFIRMATION_CODE,
+ ERROR_INCOMPATIBLE_CARRIER,
+ ERROR_EUICC_INSUFFICIENT_MEMORY,
+ ERROR_TIME_OUT,
+ ERROR_EUICC_MISSING,
+ ERROR_UNSUPPORTED_VERSION,
+ ERROR_SIM_MISSING,
+ ERROR_INSTALL_PROFILE,
+ ERROR_DISALLOWED_BY_PPR,
+ ERROR_ADDRESS_MISSING,
+ ERROR_CERTIFICATE_ERROR,
+ ERROR_NO_PROFILES_AVAILABLE,
+ ERROR_CONNECTION_ERROR,
+ ERROR_INVALID_RESPONSE,
+ ERROR_OPERATION_BUSY,
+ })
+ public @interface ErrorCode{}
+
+ /**
+ * Operation such as downloading/switching to another profile failed due to device being
+ * carrier locked.
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int ERROR_CARRIER_LOCKED = 10000;
+
+ /**
+ * The activation code(SGP.22 v2.2 section[4.1]) is invalid.
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int ERROR_INVALID_ACTIVATION_CODE = 10001;
+
+ /**
+ * The confirmation code(SGP.22 v2.2 section[4.7]) is invalid.
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int ERROR_INVALID_CONFIRMATION_CODE = 10002;
+
+ /**
+ * The profile's carrier is incompatible with the LPA.
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int ERROR_INCOMPATIBLE_CARRIER = 10003;
+
+ /**
+ * There is no more space available on the eUICC for new profiles.
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int ERROR_EUICC_INSUFFICIENT_MEMORY = 10004;
+
+ /**
+ * Timed out while waiting for an operation to complete. i.e restart, disable,
+ * switch reset etc.
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int ERROR_TIME_OUT = 10005;
+
+ /**
+ * eUICC is missing or defective on the device.
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int ERROR_EUICC_MISSING = 10006;
+
+ /**
+ * The eUICC card(hardware) version is incompatible with the software
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int ERROR_UNSUPPORTED_VERSION = 10007;
+
+ /**
+ * No SIM card is available in the device.
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int ERROR_SIM_MISSING = 10008;
+
+ /**
+ * Failure to load the profile onto the eUICC card. e.g
+ * 1. iccid of the profile already exists on the eUICC.
+ * 2. GSMA(.22 v2.2) Profile Install Result - installFailedDueToDataMismatch
+ * 3. operation was interrupted
+ * 4. SIMalliance error in PEStatus(SGP.22 v2.2 section 2.5.6.1)
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int ERROR_INSTALL_PROFILE = 10009;
+
+ /**
+ * Failed to load profile onto eUICC due to Profile Poicly Rules.
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int ERROR_DISALLOWED_BY_PPR = 10010;
+
+
+ /**
+ * Address is missing e.g SMDS/SMDP address is missing.
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int ERROR_ADDRESS_MISSING = 10011;
+
+ /**
+ * Certificate needed for authentication is not valid or missing. E.g SMDP/SMDS authentication
+ * failed.
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int ERROR_CERTIFICATE_ERROR = 10012;
+
+
+ /**
+ * No profiles available.
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int ERROR_NO_PROFILES_AVAILABLE = 10013;
+
+ /**
+ * Failure to create a connection.
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int ERROR_CONNECTION_ERROR = 10014;
+
+ /**
+ * Response format is invalid. e.g SMDP/SMDS response contains invalid json, header or/and ASN1.
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int ERROR_INVALID_RESPONSE = 10015;
+
+ /**
+ * The operation is currently busy, try again later.
+ * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ */
+ public static final int ERROR_OPERATION_BUSY = 10016;
+
private final Context mContext;
private int mCardId;
@@ -939,6 +1251,138 @@
}
/**
+ * Sets the supported countries for eUICC.
+ *
+ * <p>Requires that the calling app has the
+ * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+ *
+ * <p>The supported country list will be replaced by {@code supportedCountries}. For how we
+ * determine whether a country is supported please check {@link #isSupportedCountry}.
+ *
+ * @param supportedCountries is a list of strings contains country ISO codes in uppercase.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+ public void setSupportedCountries(@NonNull List<String> supportedCountries) {
+ if (!isEnabled()) {
+ return;
+ }
+ try {
+ getIEuiccController().setSupportedCountries(
+ true /* isSupported */,
+ supportedCountries.stream()
+ .map(String::toUpperCase).collect(Collectors.toList()));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets the unsupported countries for eUICC.
+ *
+ * <p>Requires that the calling app has the
+ * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+ *
+ * <p>The unsupported country list will be replaced by {@code unsupportedCountries}. For how we
+ * determine whether a country is supported please check {@link #isSupportedCountry}.
+ *
+ * @param unsupportedCountries is a list of strings contains country ISO codes in uppercase.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+ public void setUnsupportedCountries(@NonNull List<String> unsupportedCountries) {
+ if (!isEnabled()) {
+ return;
+ }
+ try {
+ getIEuiccController().setSupportedCountries(
+ false /* isSupported */,
+ unsupportedCountries.stream()
+ .map(String::toUpperCase).collect(Collectors.toList()));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the supported countries for eUICC.
+ *
+ * <p>Requires that the calling app has the
+ * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+ *
+ * @return list of strings contains country ISO codes in uppercase.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+ @NonNull
+ public List<String> getSupportedCountries() {
+ if (!isEnabled()) {
+ return Collections.emptyList();
+ }
+ try {
+ return getIEuiccController().getSupportedCountries(true /* isSupported */);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the unsupported countries for eUICC.
+ *
+ * <p>Requires that the calling app has the
+ * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+ *
+ * @return list of strings contains country ISO codes in uppercase.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+ @NonNull
+ public List<String> getUnsupportedCountries() {
+ if (!isEnabled()) {
+ return Collections.emptyList();
+ }
+ try {
+ return getIEuiccController().getSupportedCountries(false /* isSupported */);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns whether the given country supports eUICC.
+ *
+ * <p>Supported country list has a higher prority than unsupported country list. If the
+ * supported country list is not empty, {@code countryIso} will be considered as supported when
+ * it exists in the supported country list. Otherwise {@code countryIso} is not supported. If
+ * the supported country list is empty, {@code countryIso} will be considered as supported if it
+ * does not exist in the unsupported country list. Otherwise {@code countryIso} is not
+ * supported. If both supported and unsupported country lists are empty, then all countries are
+ * consider be supported. For how to set supported and unsupported country list, please check
+ * {@link #setSupportedCountries} and {@link #setUnsupportedCountries}.
+ *
+ * @param countryIso should be the ISO-3166 country code is provided in uppercase 2 character
+ * format.
+ * @return whether the given country supports eUICC or not.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+ public boolean isSupportedCountry(@NonNull String countryIso) {
+ if (!isEnabled()) {
+ return false;
+ }
+ try {
+ return getIEuiccController().isSupportedCountry(countryIso.toUpperCase());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Refreshes the cardId if its uninitialized, and returns whether we should continue the
* operation.
* <p>
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 8c9765b..9c1be48 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -328,6 +328,14 @@
@Deprecated
public static final String EXTRA_CALL_RAT_TYPE_ALT = "callRadioTech";
+ /**
+ * String extra property containing forwarded numbers associated with the current connection
+ * for an IMS call. The value is string array, and it can include multiple numbers, and
+ * the array values are expected E164 (e.g. +1 (650) 253-0000) format.
+ */
+ public static final String EXTRA_FORWARDED_NUMBER =
+ "android.telephony.ims.extra.FORWARDED_NUMBER";
+
/** @hide */
public int mServiceType;
/** @hide */
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 2e3f59a..7216776 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -21,6 +21,8 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
@@ -28,6 +30,7 @@
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.ims.aidl.IImsRcsController;
import android.telephony.ims.aidl.IRcsUceControllerCallback;
+import android.telephony.ims.feature.RcsFeature;
import android.util.Log;
import java.lang.annotation.Retention;
@@ -41,6 +44,8 @@
* @see ImsRcsManager#getUceAdapter() for information on creating an instance of this class.
* @hide
*/
+@SystemApi
+@TestApi
public class RcsUceAdapter {
private static final String TAG = "RcsUceAdapter";
@@ -169,6 +174,7 @@
* Provides a one-time callback for the response to a UCE request. After this callback is called
* by the framework, the reference to this callback will be discarded on the service side.
* @see #requestCapabilities(Executor, List, CapabilitiesCallback)
+ * @hide
*/
public static class CapabilitiesCallback {
@@ -196,6 +202,7 @@
/**
* Not to be instantiated directly, use
* {@link ImsRcsManager#getUceAdapter()} to instantiate this manager class.
+ * @hide
*/
RcsUceAdapter(int subId) {
mSubId = subId;
@@ -219,6 +226,7 @@
* {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
* available. This can happen if the ImsService has crashed, for example, or if the subscription
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+ * @hide
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void requestCapabilities(@CallbackExecutor Executor executor,
@@ -281,6 +289,7 @@
* {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
* available. This can happen if the ImsService has crashed, for example, or if the subscription
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+ * @hide
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public @PublishState int getUcePublishState() throws ImsException {
@@ -305,7 +314,7 @@
* for the associated subscription.
*
* @return true if the user’s setting for UCE is enabled, false otherwise. If false,
- * {@link ImsRcsManager#isCapable(int)} will return false for
+ * {@link ImsRcsManager#isCapable(int, int)} will return false for
* {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} and
* {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE}
* @see #setUceSettingEnabled(boolean)
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
index 3ec4f34..f13371c 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -17,6 +17,8 @@
package android.telephony.ims.stub;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Bundle;
@@ -206,6 +208,13 @@
return ImsUtImplBase.this.updateCallBarringForServiceClass(
cbType, action, barrList, serviceClass);
}
+
+ @Override
+ public int updateCallBarringWithPassword(int cbType, int action, String[] barrList,
+ int serviceClass, String password) throws RemoteException {
+ return ImsUtImplBase.this.updateCallBarringWithPassword(
+ cbType, action, barrList, serviceClass, password);
+ }
};
/**
@@ -328,6 +337,14 @@
}
/**
+ * Updates the configuration of the call barring for specified service class with password.
+ */
+ public int updateCallBarringWithPassword(int cbType, int action, @Nullable String[] barrList,
+ int serviceClass, @NonNull String password) {
+ return -1;
+ }
+
+ /**
* Updates the configuration of the call forward.
*/
public int updateCallForward(int action, int condition, String number, int serviceClass,
diff --git a/telephony/java/com/android/ims/ImsUtInterface.java b/telephony/java/com/android/ims/ImsUtInterface.java
index 15f8371..4a5380e 100644
--- a/telephony/java/com/android/ims/ImsUtInterface.java
+++ b/telephony/java/com/android/ims/ImsUtInterface.java
@@ -166,6 +166,12 @@
String[] barrList, int serviceClass);
/**
+ * Modifies the configuration of the call barring for specified service class with password.
+ */
+ public void updateCallBarring(int cbType, int action, Message result,
+ String[] barrList, int serviceClass, String password);
+
+ /**
* Modifies the configuration of the call forward.
*/
public void updateCallForward(int action, int condition, String number,
diff --git a/telephony/java/com/android/ims/internal/IImsUt.aidl b/telephony/java/com/android/ims/internal/IImsUt.aidl
index 4f97cc5..302be65 100644
--- a/telephony/java/com/android/ims/internal/IImsUt.aidl
+++ b/telephony/java/com/android/ims/internal/IImsUt.aidl
@@ -122,4 +122,10 @@
*/
int updateCallBarringForServiceClass(int cbType, int action, in String[] barrList,
int serviceClass);
+
+ /**
+ * Updates the configuration of the call barring for specified service class with password.
+ */
+ int updateCallBarringWithPassword(int cbType, int action, in String[] barrList,
+ int serviceClass, String password);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 9b45539..6aa5a52 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2186,4 +2186,24 @@
int setIccLockEnabled(int subId, boolean enabled, String password);
int changeIccLockPassword(int subId, String oldPassword, String newPassword);
+
+ /**
+ * Request for receiving user activity notification
+ */
+ oneway void requestUserActivityNotification();
+
+ /**
+ * Called when userActivity is signalled in the power manager.
+ * This is safe to call from any thread, with any window manager locks held or not.
+ */
+ oneway void userActivity();
+
+ /**
+ * Get the user manual network selection.
+ * Return empty string if in automatic selection.
+ *
+ * @param subId the id of the subscription
+ * @return operatorinfo on success
+ */
+ String getManualNetworkSelectionPlmn(int subId);
}
diff --git a/telephony/java/com/android/internal/telephony/OperatorInfo.java b/telephony/java/com/android/internal/telephony/OperatorInfo.java
index 64d7863..2ca4598 100644
--- a/telephony/java/com/android/internal/telephony/OperatorInfo.java
+++ b/telephony/java/com/android/internal/telephony/OperatorInfo.java
@@ -20,6 +20,7 @@
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
/**
* @hide
@@ -43,6 +44,7 @@
@UnsupportedAppUsage
private State mState = State.UNKNOWN;
+ private int mRan = AccessNetworkType.UNKNOWN;
@UnsupportedAppUsage
@@ -69,6 +71,10 @@
return mState;
}
+ public int getRan() {
+ return mRan;
+ }
+
@UnsupportedAppUsage
OperatorInfo(String operatorAlphaLong,
String operatorAlphaShort,
@@ -82,6 +88,14 @@
mState = state;
}
+ OperatorInfo(String operatorAlphaLong,
+ String operatorAlphaShort,
+ String operatorNumeric,
+ State state,
+ int ran) {
+ this (operatorAlphaLong, operatorAlphaShort, operatorNumeric, state);
+ mRan = ran;
+ }
@UnsupportedAppUsage
public OperatorInfo(String operatorAlphaLong,
@@ -92,6 +106,14 @@
operatorNumeric, rilStateToState(stateString));
}
+ public OperatorInfo(String operatorAlphaLong,
+ String operatorAlphaShort,
+ String operatorNumeric,
+ int ran) {
+ this (operatorAlphaLong, operatorAlphaShort, operatorNumeric);
+ mRan = ran;
+ }
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public OperatorInfo(String operatorAlphaLong,
String operatorAlphaShort,
@@ -124,7 +146,8 @@
return "OperatorInfo " + mOperatorAlphaLong
+ "/" + mOperatorAlphaShort
+ "/" + mOperatorNumeric
- + "/" + mState;
+ + "/" + mState
+ + "/" + mRan;
}
/**
@@ -150,6 +173,7 @@
dest.writeString(mOperatorAlphaShort);
dest.writeString(mOperatorNumeric);
dest.writeSerializable(mState);
+ dest.writeInt(mRan);
}
/**
@@ -158,20 +182,21 @@
*/
@UnsupportedAppUsage
public static final Creator<OperatorInfo> CREATOR =
- new Creator<OperatorInfo>() {
- @Override
- public OperatorInfo createFromParcel(Parcel in) {
- OperatorInfo opInfo = new OperatorInfo(
- in.readString(), /*operatorAlphaLong*/
- in.readString(), /*operatorAlphaShort*/
- in.readString(), /*operatorNumeric*/
- (State) in.readSerializable()); /*state*/
- return opInfo;
- }
+ new Creator<OperatorInfo>() {
+ @Override
+ public OperatorInfo createFromParcel(Parcel in) {
+ OperatorInfo opInfo = new OperatorInfo(
+ in.readString(), /*operatorAlphaLong*/
+ in.readString(), /*operatorAlphaShort*/
+ in.readString(), /*operatorNumeric*/
+ (State) in.readSerializable(), /*state*/
+ in.readInt()); /*ran*/
+ return opInfo;
+ }
- @Override
- public OperatorInfo[] newArray(int size) {
- return new OperatorInfo[size];
- }
- };
+ @Override
+ public OperatorInfo[] newArray(int size) {
+ return new OperatorInfo[size];
+ }
+ };
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 0db86d6..9ac8cb1 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -480,6 +480,7 @@
int RIL_REQUEST_STOP_KEEPALIVE = 145;
int RIL_REQUEST_ENABLE_MODEM = 146;
int RIL_REQUEST_GET_MODEM_STATUS = 147;
+ int RIL_REQUEST_CDMA_SEND_SMS_EXPECT_MORE = 148;
/* The following requests are not defined in RIL.h */
int RIL_REQUEST_HAL_NON_RIL_BASE = 200;
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index a15f73c..48f7850 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -180,8 +180,6 @@
public static final String ACTION_SIM_STATE_CHANGED
= Intent.ACTION_SIM_STATE_CHANGED;
- public static final String EXTRA_REBROADCAST_ON_UNLOCK= "rebroadcastOnUnlock";
-
/**
* <p>Broadcast Action: It indicates the Emergency callback mode blocks datacall/sms
* <p class="note">.
@@ -214,37 +212,6 @@
public static final String SECRET_CODE_ACTION = "android.provider.Telephony.SECRET_CODE";
/**
- * Broadcast Action: The Service Provider string(s) have been updated. Activities or
- * services that use these strings should update their display.
- * The intent will have the following extra values:</p>
- *
- * <dl>
- * <dt>showPlmn</dt><dd>Boolean that indicates whether the PLMN should be shown.</dd>
- * <dt>plmn</dt><dd>The operator name of the registered network, as a string.</dd>
- * <dt>showSpn</dt><dd>Boolean that indicates whether the SPN should be shown.</dd>
- * <dt>spn</dt><dd>The service provider name, as a string.</dd>
- * </dl>
- *
- * Note that <em>showPlmn</em> may indicate that <em>plmn</em> should be displayed, even
- * though the value for <em>plmn</em> is null. This can happen, for example, if the phone
- * has not registered to a network yet. In this case the receiver may substitute an
- * appropriate placeholder string (eg, "No service").
- *
- * It is recommended to display <em>plmn</em> before / above <em>spn</em> if
- * both are displayed.
- *
- * <p>Note: this is a protected intent that can only be sent by the system.
- */
- public static final String SPN_STRINGS_UPDATED_ACTION =
- "android.provider.Telephony.SPN_STRINGS_UPDATED";
-
- public static final String EXTRA_SHOW_PLMN = "showPlmn";
- public static final String EXTRA_PLMN = "plmn";
- public static final String EXTRA_SHOW_SPN = "showSpn";
- public static final String EXTRA_SPN = "spn";
- public static final String EXTRA_DATA_SPN = "spnData";
-
- /**
* <p>Broadcast Action: It indicates one column of a subinfo record has been changed
* <p class="note">This is a protected intent that can only be sent
* by the system.
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 832502c..d0c8024 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -148,10 +148,9 @@
}
/**
- * Create an SmsMessage from an SMS EF record.
+ * Creates an SmsMessage from an SMS EF record.
*
- * @param index Index of SMS record. This should be index in ArrayList
- * returned by RuimSmsInterfaceManager.getAllMessagesFromIcc + 1.
+ * @param index Index of SMS EF record.
* @param data Record data.
* @return An SmsMessage representing the record.
*
@@ -202,26 +201,16 @@
}
/**
- * TODO(cleanup): why do getSubmitPdu methods take an scAddr input
- * and do nothing with it? GSM allows us to specify a SC (eg,
- * when responding to an SMS that explicitly requests the response
- * is sent to a specific SC), or pass null to use the default
- * value. Is there no similar notion in CDMA? Or do we just not
- * have it hooked up?
- */
-
- /**
- * Get an SMS-SUBMIT PDU for a destination address and a message
+ * Gets an SMS-SUBMIT PDU for a destination address and a message.
*
- * @param scAddr Service Centre address. Null means use default.
- * @param destAddr Address of the recipient.
- * @param message String representation of the message payload.
- * @param statusReportRequested Indicates whether a report is requested for this message.
- * @param smsHeader Array containing the data for the User Data Header, preceded
- * by the Element Identifiers.
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param scAddr Service Centre address. No use for this message.
+ * @param destAddr the address of the destination for the message.
+ * @param message string representation of the message payload.
+ * @param statusReportRequested indicates whether a report is requested for this message.
+ * @param smsHeader array containing the data for the User Data Header, preceded by the Element
+ * Identifiers.
+ * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns
+ * null on encode error.
* @hide
*/
@UnsupportedAppUsage
@@ -231,18 +220,17 @@
}
/**
- * Get an SMS-SUBMIT PDU for a destination address and a message
+ * Gets an SMS-SUBMIT PDU for a destination address and a message.
*
- * @param scAddr Service Centre address. Null means use default.
- * @param destAddr Address of the recipient.
- * @param message String representation of the message payload.
- * @param statusReportRequested Indicates whether a report is requested for this message.
- * @param smsHeader Array containing the data for the User Data Header, preceded
- * by the Element Identifiers.
- * @param priority Priority level of the message
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param scAddr Service Centre address. No use for this message.
+ * @param destAddr the address of the destination for the message.
+ * @param message string representation of the message payload.
+ * @param statusReportRequested indicates whether a report is requested for this message.
+ * @param smsHeader array containing the data for the User Data Header, preceded by the Element
+ * Identifiers.
+ * @param priority priority level of the message.
+ * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns
+ * null on encode error.
* @hide
*/
@UnsupportedAppUsage
@@ -265,16 +253,15 @@
}
/**
- * Get an SMS-SUBMIT PDU for a data message to a destination address and port.
+ * Gets an SMS-SUBMIT PDU for a data message to a destination address & port.
*
- * @param scAddr Service Centre address. null == use default
- * @param destAddr the address of the destination for the message
- * @param destPort the port to deliver the message to at the
- * destination
- * @param data the data for the message
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param scAddr Service Centre address. No use for this message.
+ * @param destAddr the address of the destination for the message.
+ * @param destPort the port to deliver the message to at the destination.
+ * @param data the data for the message.
+ * @param statusReportRequested indicates whether a report is requested for this message.
+ * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns
+ * null on encode error.
*/
@UnsupportedAppUsage
public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, int destPort,
@@ -305,14 +292,13 @@
}
/**
- * Get an SMS-SUBMIT PDU for a data message to a destination address & port
+ * Gets an SMS-SUBMIT PDU for a data message to a destination address & port.
*
- * @param destAddr the address of the destination for the message
- * @param userData the data for the message
- * @param statusReportRequested Indicates whether a report is requested for this message.
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param destAddr the address of the destination for the message.
+ * @param userData the data for the message.
+ * @param statusReportRequested indicates whether a report is requested for this message.
+ * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns
+ * null on encode error.
*/
@UnsupportedAppUsage
public static SubmitPdu getSubmitPdu(String destAddr, UserData userData,
@@ -321,15 +307,14 @@
}
/**
- * Get an SMS-SUBMIT PDU for a data message to a destination address & port
+ * Gets an SMS-SUBMIT PDU for a data message to a destination address & port.
*
- * @param destAddr the address of the destination for the message
- * @param userData the data for the message
- * @param statusReportRequested Indicates whether a report is requested for this message.
- * @param priority Priority level of the message
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param destAddr the address of the destination for the message.
+ * @param userData the data for the message.
+ * @param statusReportRequested indicates whether a report is requested for this message.
+ * @param priority Priority level of the message.
+ * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns
+ * null on encode error.
*/
@UnsupportedAppUsage
public static SubmitPdu getSubmitPdu(String destAddr, UserData userData,
@@ -1059,6 +1044,72 @@
}
/**
+ * Gets an SMS-DELIVER PDU for a originating address and a message.
+ *
+ * @param origAddr the address of the originating for the message.
+ * @param message string representation of the message payload.
+ * @param date the time stamp the message was received.
+ * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns
+ * null on encode error.
+ * @hide
+ */
+ public static SubmitPdu getDeliverPdu(String origAddr, String message, long date) {
+ if (origAddr == null || message == null) {
+ return null;
+ }
+
+ CdmaSmsAddress addr = CdmaSmsAddress.parse(origAddr);
+ if (addr == null) return null;
+
+ BearerData bearerData = new BearerData();
+ bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER;
+
+ bearerData.messageId = 0;
+
+ bearerData.deliveryAckReq = false;
+ bearerData.userAckReq = false;
+ bearerData.readAckReq = false;
+ bearerData.reportReq = false;
+
+ bearerData.userData = new UserData();
+ bearerData.userData.payloadStr = message;
+
+ bearerData.msgCenterTimeStamp = BearerData.TimeStamp.fromMillis(date);
+
+ byte[] encodedBearerData = BearerData.encode(bearerData);
+ if (encodedBearerData == null) return null;
+
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(100);
+ DataOutputStream dos = new DataOutputStream(baos);
+ dos.writeInt(SmsEnvelope.TELESERVICE_WMT);
+ dos.writeInt(0); // servicePresent
+ dos.writeInt(0); // serviceCategory
+ dos.write(addr.digitMode);
+ dos.write(addr.numberMode);
+ dos.write(addr.ton); // number_type
+ dos.write(addr.numberPlan);
+ dos.write(addr.numberOfDigits);
+ dos.write(addr.origBytes, 0, addr.origBytes.length); // digits
+ // Subaddress is not supported.
+ dos.write(0); // subaddressType
+ dos.write(0); // subaddr_odd
+ dos.write(0); // subaddr_nbr_of_digits
+ dos.write(encodedBearerData.length);
+ dos.write(encodedBearerData, 0, encodedBearerData.length);
+ dos.close();
+
+ SubmitPdu pdu = new SubmitPdu();
+ pdu.encodedMessage = baos.toByteArray();
+ pdu.encodedScAddress = null;
+ return pdu;
+ } catch (IOException ex) {
+ Rlog.e(LOG_TAG, "creating Deliver PDU failed: " + ex);
+ }
+ return null;
+ }
+
+ /**
* Creates byte array (pseudo pdu) from SMS object.
* Note: Do not call this method more than once per object!
* @hide
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index cbf0f5c..6ad6dd1 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -32,6 +32,7 @@
import com.android.internal.util.BitwiseInputStream;
import com.android.internal.util.BitwiseOutputStream;
+import java.io.ByteArrayOutputStream;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
@@ -284,6 +285,33 @@
return ts;
}
+ public static TimeStamp fromMillis(long timeInMillis) {
+ TimeStamp ts = new TimeStamp();
+ LocalDateTime localDateTime =
+ Instant.ofEpochMilli(timeInMillis).atZone(ts.mZoneId).toLocalDateTime();
+ int year = localDateTime.getYear();
+ if (year < 1996 || year > 2095) return null;
+ ts.year = year;
+ ts.month = localDateTime.getMonthValue();
+ ts.monthDay = localDateTime.getDayOfMonth();
+ ts.hour = localDateTime.getHour();
+ ts.minute = localDateTime.getMinute();
+ ts.second = localDateTime.getSecond();
+ return ts;
+ }
+
+ public byte[] toByteArray() {
+ int year = this.year % 100; // 00 - 99
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream(6);
+ outStream.write((((year / 10) & 0x0F) << 4) | ((year % 10) & 0x0F));
+ outStream.write((((month / 10) << 4) & 0xF0) | ((month % 10) & 0x0F));
+ outStream.write((((monthDay / 10) << 4) & 0xF0) | ((monthDay % 10) & 0x0F));
+ outStream.write((((hour / 10) << 4) & 0xF0) | ((hour % 10) & 0x0F));
+ outStream.write((((minute / 10) << 4) & 0xF0) | ((minute % 10) & 0x0F));
+ outStream.write((((second / 10) << 4) & 0xF0) | ((second % 10) & 0x0F));
+ return outStream.toByteArray();
+ }
+
public long toMillis() {
LocalDateTime localDateTime =
LocalDateTime.of(year, month + 1, monthDay, hour, minute, second);
@@ -957,6 +985,12 @@
}
}
+ private static void encodeMsgCenterTimeStamp(BearerData bData, BitwiseOutputStream outStream)
+ throws BitwiseOutputStream.AccessException {
+ outStream.write(8, 6);
+ outStream.writeByteArray(8 * 6, bData.msgCenterTimeStamp.toByteArray());
+ };
+
/**
* Create serialized representation for BearerData object.
* (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
@@ -1021,6 +1055,10 @@
outStream.write(8, SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS);
encodeScpResults(bData, outStream);
}
+ if (bData.msgCenterTimeStamp != null) {
+ outStream.write(8, SUBPARAM_MESSAGE_CENTER_TIME_STAMP);
+ encodeMsgCenterTimeStamp(bData, outStream);
+ }
return outStream.toByteArray();
} catch (BitwiseOutputStream.AccessException ex) {
Rlog.e(LOG_TAG, "BearerData encode failed: " + ex);
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index 7422863..35e8a12 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -21,6 +21,7 @@
import android.os.Bundle;
import android.telephony.euicc.DownloadableSubscription;
import android.telephony.euicc.EuiccInfo;
+import java.util.List;
/** @hide */
interface IEuiccController {
@@ -47,4 +48,7 @@
oneway void eraseSubscriptionsWithOptions(
int cardId, int options, in PendingIntent callbackIntent);
oneway void retainSubscriptionsForFactoryReset(int cardId, in PendingIntent callbackIntent);
+ void setSupportedCountries(boolean isSupported, in List<String> countriesList);
+ List<String> getSupportedCountries(boolean isSupported);
+ boolean isSupportedCountry(String countryIso);
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 417aafd..c91ea69 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -42,8 +42,11 @@
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
+import java.time.Instant;
import java.time.LocalDateTime;
+import java.time.ZoneId;
import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
/**
* A Short Message Service message.
@@ -167,10 +170,9 @@
}
/**
- * Create an SmsMessage from an SMS EF record.
+ * Creates an SmsMessage from an SMS EF record.
*
- * @param index Index of SMS record. This should be index in ArrayList
- * returned by SmsManager.getAllMessagesFromSim + 1.
+ * @param index Index of SMS EF record.
* @param data Record data.
* @return An SmsMessage representing the record.
*
@@ -259,12 +261,15 @@
}
/**
- * Get an SMS-SUBMIT PDU for a destination address and a message
+ * Gets an SMS-SUBMIT PDU for a destination address and a message.
*
- * @param scAddress Service Centre address. Null means use default.
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param message string representation of the message payload.
+ * @param statusReportRequested indicates whether a report is reuested for this message.
+ * @param header a byte array containing the data for the User Data Header.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
* @hide
*/
@UnsupportedAppUsage
@@ -277,17 +282,19 @@
/**
- * Get an SMS-SUBMIT PDU for a destination address and a message using the
- * specified encoding.
+ * Gets an SMS-SUBMIT PDU for a destination address and a message using the specified encoding.
*
- * @param scAddress Service Centre address. Null means use default.
- * @param encoding Encoding defined by constants in
- * com.android.internal.telephony.SmsConstants.ENCODING_*
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param message string representation of the message payload.
+ * @param statusReportRequested indicates whether a report is reuested for this message.
+ * @param header a byte array containing the data for the User Data Header.
+ * @param encoding encoding defined by constants in
+ * com.android.internal.telephony.SmsConstants.ENCODING_*
* @param languageTable
* @param languageShiftTable
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
* @hide
*/
@UnsupportedAppUsage
@@ -300,18 +307,20 @@
}
/**
- * Get an SMS-SUBMIT PDU for a destination address and a message using the
- * specified encoding.
+ * Gets an SMS-SUBMIT PDU for a destination address and a message using the specified encoding.
*
- * @param scAddress Service Centre address. Null means use default.
- * @param encoding Encoding defined by constants in
- * com.android.internal.telephony.SmsConstants.ENCODING_*
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param message string representation of the message payload.
+ * @param statusReportRequested indicates whether a report is reuested for this message.
+ * @param header a byte array containing the data for the User Data Header.
+ * @param encoding encoding defined by constants in
+ * com.android.internal.telephony.SmsConstants.ENCODING_*
* @param languageTable
* @param languageShiftTable
* @param validityPeriod Validity Period of the message in Minutes.
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
* @hide
*/
@UnsupportedAppUsage
@@ -483,12 +492,14 @@
}
/**
- * Get an SMS-SUBMIT PDU for a destination address and a message
+ * Gets an SMS-SUBMIT PDU for a destination address and a message.
*
- * @param scAddress Service Centre address. Null means use default.
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param message string representation of the message payload.
+ * @param statusReportRequested indicates whether a report is reuested for this message.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
*/
@UnsupportedAppUsage
public static SubmitPdu getSubmitPdu(String scAddress,
@@ -499,15 +510,15 @@
}
/**
- * Get an SMS-SUBMIT PDU for a destination address and a message
+ * Gets an SMS-SUBMIT PDU for a destination address and a message.
*
- * @param scAddress Service Centre address. Null means use default.
- * @param destinationAddress the address of the destination for the message
- * @param statusReportRequested staus report of the message Requested
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param message string representation of the message payload.
+ * @param statusReportRequested indicates whether a report is reuested for this message.
* @param validityPeriod Validity Period of the message in Minutes.
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
*/
@UnsupportedAppUsage
public static SubmitPdu getSubmitPdu(String scAddress,
@@ -518,16 +529,15 @@
}
/**
- * Get an SMS-SUBMIT PDU for a data message to a destination address & port
+ * Gets an SMS-SUBMIT PDU for a data message to a destination address & port.
*
- * @param scAddress Service Centre address. null == use default
- * @param destinationAddress the address of the destination for the message
- * @param destinationPort the port to deliver the message to at the
- * destination
- * @param data the data for the message
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param destinationPort the port to deliver the message to at the destination.
+ * @param data the data for the message.
+ * @param statusReportRequested indicates whether a report is reuested for this message.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
*/
public static SubmitPdu getSubmitPdu(String scAddress,
String destinationAddress, int destinationPort, byte[] data,
@@ -551,8 +561,7 @@
SubmitPdu ret = new SubmitPdu();
ByteArrayOutputStream bo = getSubmitPduHead(
- scAddress, destinationAddress, (byte) 0x41, // MTI = SMS-SUBMIT,
- // TP-UDHI = true
+ scAddress, destinationAddress, (byte) 0x41, /* TP-MTI=SMS-SUBMIT, TP-UDHI=true */
statusReportRequested, ret);
// Skip encoding pdu if error occurs when create pdu head and the error will be handled
// properly later on encodedMessage sanity check.
@@ -579,16 +588,18 @@
}
/**
- * Create the beginning of a SUBMIT PDU. This is the part of the
- * SUBMIT PDU that is common to the two versions of {@link #getSubmitPdu},
- * one of which takes a byte array and the other of which takes a
+ * Creates the beginning of a SUBMIT PDU.
+ *
+ * This is the part of the SUBMIT PDU that is common to the two versions of
+ * {@link #getSubmitPdu}, one of which takes a byte array and the other of which takes a
* <code>String</code>.
*
- * @param scAddress Service Centre address. null == use default
- * @param destinationAddress the address of the destination for the message
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
* @param mtiByte
- * @param ret <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message. Returns null on encode error.
+ * @param statusReportRequested indicates whether a report is reuested for this message.
+ * @param ret <code>SubmitPdu</code>.
+ * @return a byte array of the beginning of a SUBMIT PDU. Null for invalid destinationAddress.
*/
@UnsupportedAppUsage
private static ByteArrayOutputStream getSubmitPduHead(
@@ -636,6 +647,161 @@
return bo;
}
+ /**
+ * Gets an SMS-DELIVER PDU for an originating address and a message.
+ *
+ * @param scAddress Service Centre address. Null means use default.
+ * @param originatingAddress the address of the originating for the message.
+ * @param message string representation of the message payload.
+ * @param date the time stamp the message was received.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
+ * @hide
+ */
+ public static SubmitPdu getDeliverPdu(
+ String scAddress, String originatingAddress, String message, long date) {
+ if (originatingAddress == null || message == null) {
+ return null;
+ }
+
+ // Find the best encoding to use
+ TextEncodingDetails ted = calculateLength(message, false);
+ int encoding = ted.codeUnitSize;
+ int languageTable = ted.languageTable;
+ int languageShiftTable = ted.languageShiftTable;
+ byte[] header = null;
+
+ if (encoding == ENCODING_7BIT && (languageTable != 0 || languageShiftTable != 0)) {
+ SmsHeader smsHeader = new SmsHeader();
+ smsHeader.languageTable = languageTable;
+ smsHeader.languageShiftTable = languageShiftTable;
+ header = SmsHeader.toByteArray(smsHeader);
+ }
+
+ SubmitPdu ret = new SubmitPdu();
+
+ ByteArrayOutputStream bo = new ByteArrayOutputStream(MAX_USER_DATA_BYTES + 40);
+
+ // SMSC address with length octet, or 0
+ if (scAddress == null) {
+ ret.encodedScAddress = null;
+ } else {
+ ret.encodedScAddress =
+ PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength(scAddress);
+ }
+
+ // TP-Message-Type-Indicator
+ bo.write(0); // SMS-DELIVER
+
+ byte[] oaBytes;
+
+ oaBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD(originatingAddress);
+
+ // Return null for invalid originating address
+ if (oaBytes == null) return null;
+
+ // Originating address length in BCD digits, ignoring TON byte and pad
+ // TODO Should be better.
+ bo.write((oaBytes.length - 1) * 2 - ((oaBytes[oaBytes.length - 1] & 0xf0) == 0xf0 ? 1 : 0));
+
+ // Originating Address
+ bo.write(oaBytes, 0, oaBytes.length);
+
+ // TP-Protocol-Identifier
+ bo.write(0);
+
+ // User Data (and length)
+ byte[] userData;
+ try {
+ if (encoding == ENCODING_7BIT) {
+ userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header,
+ languageTable, languageShiftTable);
+ } else { // Assume UCS-2
+ try {
+ userData = encodeUCS2(message, header);
+ } catch (UnsupportedEncodingException uex) {
+ Rlog.e(LOG_TAG, "Implausible UnsupportedEncodingException ", uex);
+ return null;
+ }
+ }
+ } catch (EncodeException ex) {
+ if (ex.getError() == EncodeException.ERROR_EXCEED_SIZE) {
+ Rlog.e(LOG_TAG, "Exceed size limitation EncodeException", ex);
+ return null;
+ } else {
+ // Encoding to the 7-bit alphabet failed. Let's see if we can send it as a UCS-2
+ // encoded message
+ try {
+ userData = encodeUCS2(message, header);
+ encoding = ENCODING_16BIT;
+ } catch (EncodeException ex1) {
+ Rlog.e(LOG_TAG, "Exceed size limitation EncodeException", ex1);
+ return null;
+ } catch (UnsupportedEncodingException uex) {
+ Rlog.e(LOG_TAG, "Implausible UnsupportedEncodingException ", uex);
+ return null;
+ }
+ }
+ }
+
+ if (encoding == ENCODING_7BIT) {
+ if ((0xff & userData[0]) > MAX_USER_DATA_SEPTETS) {
+ // Message too long
+ Rlog.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " septets)");
+ return null;
+ }
+ // TP-Data-Coding-Scheme
+ // Default encoding, uncompressed
+ bo.write(0x00);
+ } else { // Assume UCS-2
+ if ((0xff & userData[0]) > MAX_USER_DATA_BYTES) {
+ // Message too long
+ Rlog.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " bytes)");
+ return null;
+ }
+ // TP-Data-Coding-Scheme
+ // UCS-2 encoding, uncompressed
+ bo.write(0x08);
+ }
+
+ // TP-Service-Centre-Time-Stamp
+ byte[] scts = new byte[7];
+
+ ZonedDateTime zoneDateTime = Instant.ofEpochMilli(date).atZone(ZoneId.systemDefault());
+ LocalDateTime localDateTime = zoneDateTime.toLocalDateTime();
+
+ // It indicates the difference, expressed in quarters of an hour, between the local time and
+ // GMT.
+ int timezoneOffset = zoneDateTime.getOffset().getTotalSeconds() / 60 / 15;
+ boolean negativeOffset = timezoneOffset < 0;
+ if (negativeOffset) {
+ timezoneOffset = -timezoneOffset;
+ }
+ int year = localDateTime.getYear();
+ int month = localDateTime.getMonthValue();
+ int day = localDateTime.getDayOfMonth();
+ int hour = localDateTime.getHour();
+ int minute = localDateTime.getMinute();
+ int second = localDateTime.getSecond();
+
+ year = year > 2000 ? year - 2000 : year - 1900;
+ scts[0] = (byte) ((((year % 10) & 0x0F) << 4) | ((year / 10) & 0x0F));
+ scts[1] = (byte) ((((month % 10) & 0x0F) << 4) | ((month / 10) & 0x0F));
+ scts[2] = (byte) ((((day % 10) & 0x0F) << 4) | ((day / 10) & 0x0F));
+ scts[3] = (byte) ((((hour % 10) & 0x0F) << 4) | ((hour / 10) & 0x0F));
+ scts[4] = (byte) ((((minute % 10) & 0x0F) << 4) | ((minute / 10) & 0x0F));
+ scts[5] = (byte) ((((second % 10) & 0x0F) << 4) | ((second / 10) & 0x0F));
+ scts[6] = (byte) ((((timezoneOffset % 10) & 0x0F) << 4) | ((timezoneOffset / 10) & 0x0F));
+ if (negativeOffset) {
+ scts[0] |= 0x08; // Negative offset
+ }
+ bo.write(scts, 0, scts.length);
+
+ bo.write(userData, 0, userData.length);
+ ret.encodedMessage = bo.toByteArray();
+ return ret;
+ }
+
private static class PduParser {
@UnsupportedAppUsage
byte mPdu[];
diff --git a/test-mock/api/test-current.txt b/test-mock/api/test-current.txt
index cc260ac..32ca250 100644
--- a/test-mock/api/test-current.txt
+++ b/test-mock/api/test-current.txt
@@ -2,7 +2,6 @@
package android.test.mock {
public class MockContext extends android.content.Context {
- method public android.view.Display getDisplay();
method public int getDisplayId();
}
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 9d913b9..36074ed 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -17,6 +17,7 @@
package android.test.mock;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
@@ -811,6 +812,11 @@
}
@Override
+ public @NonNull Context createWindowContext(int type) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public boolean isRestricted() {
throw new UnsupportedOperationException();
}
@@ -821,7 +827,6 @@
throw new UnsupportedOperationException();
}
- /** @hide */
@Override
public Display getDisplay() {
throw new UnsupportedOperationException();
diff --git a/tests/ApkVerityTest/block_device_writer/Android.bp b/tests/ApkVerityTest/block_device_writer/Android.bp
index e7a63e4..78850c5 100644
--- a/tests/ApkVerityTest/block_device_writer/Android.bp
+++ b/tests/ApkVerityTest/block_device_writer/Android.bp
@@ -30,7 +30,23 @@
"-g",
],
shared_libs: ["libbase", "libutils"],
+ // For some reasons, cuttlefish (x86) uses x86_64 test suites for testing. Unfortunately, when
+ // the uploader does not pick up the executable from correct output location. The following
+ // workaround allows the test to:
+ // * upload the 32-bit exectuable for both 32 and 64 bits devices to use
+ // * refer to the same executable name in Java
+ // * no need to force the Java test to be archiecture specific.
+ //
+ // See b/145573317 for details.
+ multilib: {
+ lib32: {
+ suffix: "",
+ },
+ lib64: {
+ suffix: "64", // not really used
+ },
+ },
- test_suites: ["general-tests"],
+ test_suites: ["general-tests", "pts"],
gtest: false,
}
diff --git a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
index 592aa3a..153ca79 100644
--- a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
+++ b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
@@ -58,4 +58,31 @@
ambientModeChangedCount[0], 2);
}
+ @Test
+ public void testDeliversZoomChanged() {
+ int[] zoomChangedCount = {0};
+ WallpaperService service = new WallpaperService() {
+ @Override
+ public Engine onCreateEngine() {
+ return new Engine() {
+ @Override
+ public void onZoomChanged(float zoom) {
+ super.onZoomChanged(zoom);
+ zoomChangedCount[0]++;
+ }
+ };
+ }
+ };
+ WallpaperService.Engine engine = service.onCreateEngine();
+ engine.setCreated(true);
+
+ engine.setZoom(.5f);
+ assertEquals("engine scale was not updated", .5f, engine.getZoom(), .001f);
+ assertEquals("onZoomChanged should have been called", 1, zoomChangedCount[0]);
+
+ engine.setZoom(0);
+ assertEquals("engine scale was not updated", 0, engine.getZoom(), .001f);
+ assertEquals("onAmbientModeChanged should have been called", 2, zoomChangedCount[0]);
+ }
+
}
diff --git a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
index 65a3d8a..c900eae 100644
--- a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
+++ b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
@@ -30,6 +30,7 @@
import android.test.suitebuilder.annotation.SmallTest;
import java.util.Arrays;
+import java.util.Locale;
import java.util.Random;
import java.util.UUID;
@@ -38,7 +39,8 @@
@SmallTest
public void testKeyphraseParcelUnparcel_noUsers() throws Exception {
- Keyphrase keyphrase = new Keyphrase(1, 0, "en-US", "hello", null);
+ Keyphrase keyphrase = new Keyphrase(1, 0,
+ Locale.forLanguageTag("en-US"), "hello", null);
// Write to a parcel
Parcel parcel = Parcel.obtain();
@@ -57,7 +59,8 @@
@SmallTest
public void testKeyphraseParcelUnparcel_zeroUsers() throws Exception {
- Keyphrase keyphrase = new Keyphrase(1, 0, "en-US", "hello", new int[0]);
+ Keyphrase keyphrase = new Keyphrase(1, 0,
+ Locale.forLanguageTag("en-US"), "hello", new int[0]);
// Write to a parcel
Parcel parcel = Parcel.obtain();
@@ -76,7 +79,8 @@
@SmallTest
public void testKeyphraseParcelUnparcel_pos() throws Exception {
- Keyphrase keyphrase = new Keyphrase(1, 0, "en-US", "hello", new int[] {1, 2, 3, 4, 5});
+ Keyphrase keyphrase = new Keyphrase(1, 0,
+ Locale.forLanguageTag("en-US"), "hello", new int[] {1, 2, 3, 4, 5});
// Write to a parcel
Parcel parcel = Parcel.obtain();
@@ -96,8 +100,10 @@
@SmallTest
public void testKeyphraseSoundModelParcelUnparcel_noData() throws Exception {
Keyphrase[] keyphrases = new Keyphrase[2];
- keyphrases[0] = new Keyphrase(1, 0, "en-US", "hello", new int[] {0});
- keyphrases[1] = new Keyphrase(2, 0, "fr-FR", "there", new int[] {1, 2});
+ keyphrases[0] = new Keyphrase(1, 0, Locale.forLanguageTag("en-US"),
+ "hello", new int[] {0});
+ keyphrases[1] = new Keyphrase(2, 0, Locale.forLanguageTag("fr-FR"),
+ "there", new int[] {1, 2});
KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), UUID.randomUUID(),
null, keyphrases);
@@ -119,8 +125,10 @@
@SmallTest
public void testKeyphraseSoundModelParcelUnparcel_zeroData() throws Exception {
Keyphrase[] keyphrases = new Keyphrase[2];
- keyphrases[0] = new Keyphrase(1, 0, "en-US", "hello", new int[] {0});
- keyphrases[1] = new Keyphrase(2, 0, "fr-FR", "there", new int[] {1, 2});
+ keyphrases[0] = new Keyphrase(1, 0, Locale.forLanguageTag("en-US"),
+ "hello", new int[] {0});
+ keyphrases[1] = new Keyphrase(2, 0, Locale.forLanguageTag("fr-FR"),
+ "there", new int[] {1, 2});
KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), UUID.randomUUID(),
new byte[0], keyphrases);
@@ -186,8 +194,10 @@
@LargeTest
public void testKeyphraseSoundModelParcelUnparcel_largeData() throws Exception {
Keyphrase[] keyphrases = new Keyphrase[2];
- keyphrases[0] = new Keyphrase(1, 0, "en-US", "hello", new int[] {0});
- keyphrases[1] = new Keyphrase(2, 0, "fr-FR", "there", new int[] {1, 2});
+ keyphrases[0] = new Keyphrase(1, 0, Locale.forLanguageTag("en-US"),
+ "hello", new int[] {0});
+ keyphrases[1] = new Keyphrase(2, 0, Locale.forLanguageTag("fr-FR"),
+ "there", new int[] {1, 2});
byte[] data = new byte[200 * 1024];
mRandom.nextBytes(data);
KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), UUID.randomUUID(),
diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java
index 6687f83..6169671 100644
--- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java
+++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java
@@ -46,15 +46,15 @@
mView.setZOrderOnTop(true);
mView.getHolder().addCallback(this);
+
+ addEmbeddedView();
}
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
+ void addEmbeddedView() {
mVr = new SurfaceControlViewHost(this, this.getDisplay(),
- mView.getInputToken());
+ mView.getHostToken());
- final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- t.reparent(mVr.getSurfacePackage().getSurfaceControl(), mView.getSurfaceControl()).apply();
+ mView.setChildSurfacePackage(mVr.getSurfacePackage());
Button v = new Button(this);
v.setBackgroundColor(Color.BLUE);
@@ -70,6 +70,10 @@
}
@Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ }
+
+ @Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Canvas canvas = holder.lockCanvas();
canvas.drawColor(Color.GREEN);
diff --git a/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java b/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java
index 54c944f9..b357ad0 100644
--- a/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java
+++ b/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java
@@ -16,9 +16,6 @@
package com.android.test.voiceenrollment;
-import java.util.Random;
-import java.util.UUID;
-
import android.app.Activity;
import android.hardware.soundtrigger.SoundTrigger;
import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
@@ -29,6 +26,13 @@
import android.view.View;
import android.widget.Toast;
+import java.util.Locale;
+import java.util.Random;
+import java.util.UUID;
+
+/**
+ * TODO: must be transitioned to a service.
+ */
public class TestEnrollmentActivity extends Activity {
private static final String TAG = "TestEnrollmentActivity";
private static final boolean DBG = false;
@@ -56,7 +60,8 @@
* Performs a fresh enrollment.
*/
public void onEnrollButtonClicked(View v) {
- Keyphrase kp = new Keyphrase(KEYPHRASE_ID, RECOGNITION_MODES, BCP47_LOCALE, TEXT,
+ Keyphrase kp = new Keyphrase(KEYPHRASE_ID, RECOGNITION_MODES,
+ Locale.forLanguageTag(BCP47_LOCALE), TEXT,
new int[] { UserManager.get(this).getUserHandle() /* current user */});
UUID modelUuid = UUID.randomUUID();
// Generate a fake model to push.
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
index f3c89d8a..01e212d 100644
--- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
@@ -22,6 +22,7 @@
import android.app.Activity;
import android.graphics.Insets;
import android.os.Bundle;
+import android.util.Log;
import android.util.Property;
import android.view.View;
import android.view.WindowInsets;
@@ -67,8 +68,8 @@
}
};
- float showY;
- float hideY;
+ float startY;
+ float endY;
InsetsAnimation imeAnim;
@Override
@@ -84,16 +85,6 @@
v.getWindowInsetsController().hide(Type.ime());
}
});
- mRoot.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
- if (imeAnim == null) {
- return;
- }
- if (mRoot.getRootWindowInsets().isVisible(Type.ime())) {
- showY = mButton.getTop();
- } else {
- hideY = mButton.getTop();
- }
- });
mRoot.setWindowInsetsAnimationCallback(new WindowInsetsAnimationCallback() {
@Override
@@ -106,22 +97,19 @@
if ((animation.getTypeMask() & Type.ime()) != 0) {
imeAnim = animation;
}
- if (mRoot.getRootWindowInsets().isVisible(Type.ime())) {
- showY = mButton.getTop();
- } else {
- hideY = mButton.getTop();
- }
+ startY = mButton.getTop();
}
@Override
public WindowInsets onProgress(WindowInsets insets) {
- mButton.setY(hideY + (showY - hideY) * imeAnim.getInterpolatedFraction());
+ mButton.setY(startY + (endY - startY) * imeAnim.getInterpolatedFraction());
return insets;
}
@Override
public AnimationBounds onStart(InsetsAnimation animation,
AnimationBounds bounds) {
+ endY = mButton.getTop();
return bounds;
}
diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/tests/net/common/java/android/net/LinkAddressTest.java
index b2e573b..096f7bd 100644
--- a/tests/net/common/java/android/net/LinkAddressTest.java
+++ b/tests/net/common/java/android/net/LinkAddressTest.java
@@ -38,6 +38,8 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.os.SystemClock;
+
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -316,11 +318,83 @@
l = new LinkAddress(V6_ADDRESS, 64, 123, 456);
assertParcelingIsLossless(l);
+ l = new LinkAddress(V6_ADDRESS, 64, 123, 456,
+ 1L, 3600000L);
+ assertParcelingIsLossless(l);
l = new LinkAddress(V4 + "/28", IFA_F_PERMANENT, RT_SCOPE_LINK);
- assertParcelSane(l, 4);
+ assertParcelSane(l, 6);
}
+ @Test
+ public void testDeprecationTime() {
+ try {
+ new LinkAddress(V6_ADDRESS, 64, 0, 456,
+ LinkAddress.LIFETIME_UNKNOWN,
+ SystemClock.elapsedRealtime() + 200000);
+ fail("Only one time provided should cause exception");
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ new LinkAddress(V6_ADDRESS, 64, 0, 456,
+ SystemClock.elapsedRealtime() - 100000,
+ SystemClock.elapsedRealtime() - 200000);
+ fail("deprecation time later than expiration time should cause exception");
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ new LinkAddress(V6_ADDRESS, 64, 0, 456,
+ -2, SystemClock.elapsedRealtime());
+ fail("negative deprecation time should cause exception");
+ } catch (IllegalArgumentException expected) { }
+ }
+
+ @Test
+ public void testExpirationTime() {
+ try {
+ new LinkAddress(V6_ADDRESS, 64, 0, 456,
+ SystemClock.elapsedRealtime() + 200000,
+ LinkAddress.LIFETIME_UNKNOWN);
+ fail("Only one time provided should cause exception");
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ new LinkAddress(V6_ADDRESS, 64, 0, 456,
+ SystemClock.elapsedRealtime() - 10000, -2);
+ fail("negative expiration time should cause exception");
+ } catch (IllegalArgumentException expected) { }
+ }
+
+ @Test
+ public void testGetFlags() {
+ LinkAddress l = new LinkAddress(V6_ADDRESS, 64, 123, RT_SCOPE_HOST);
+ assertEquals(123, l.getFlags());
+
+ // Test if deprecated bit was added/remove automatically based on the provided deprecation
+ // time
+ l = new LinkAddress(V6_ADDRESS, 64, 0, RT_SCOPE_HOST,
+ SystemClock.elapsedRealtime() - 100000, LinkAddress.LIFETIME_PERMANENT);
+ // Check if the flag is added automatically.
+ assertTrue((l.getFlags() & IFA_F_DEPRECATED) != 0);
+
+ l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, RT_SCOPE_HOST,
+ SystemClock.elapsedRealtime() + 100000, LinkAddress.LIFETIME_PERMANENT);
+ // Check if the flag is removed automatically.
+ assertTrue((l.getFlags() & IFA_F_DEPRECATED) == 0);
+
+ l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, RT_SCOPE_HOST,
+ LinkAddress.LIFETIME_PERMANENT, LinkAddress.LIFETIME_PERMANENT);
+ // Check if the permanent flag is added.
+ assertTrue((l.getFlags() & IFA_F_PERMANENT) != 0);
+
+ l = new LinkAddress(V6_ADDRESS, 64, IFA_F_PERMANENT, RT_SCOPE_HOST,
+ SystemClock.elapsedRealtime() - 100000,
+ SystemClock.elapsedRealtime() + 100000);
+ // Check if the permanent flag is removed
+ assertTrue((l.getFlags() & IFA_F_PERMANENT) == 0);
+ }
+
+
private void assertGlobalPreferred(LinkAddress l, String msg) {
assertTrue(msg, l.isGlobalPreferred());
}
@@ -389,5 +463,12 @@
(IFA_F_TEMPORARY|IFA_F_TENTATIVE|IFA_F_OPTIMISTIC),
RT_SCOPE_UNIVERSE);
assertGlobalPreferred(l, "v6,global,tempaddr+optimistic");
+
+ l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED,
+ RT_SCOPE_UNIVERSE, SystemClock.elapsedRealtime() + 100000,
+ SystemClock.elapsedRealtime() + 200000);
+ // Although the deprecated bit is set, but the deprecation time is in the future, test
+ // if the flag is removed automatically.
+ assertGlobalPreferred(l, "v6,global,tempaddr+deprecated in the future");
}
}
diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
new file mode 100644
index 0000000..065add4
--- /dev/null
+++ b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport;
+
+import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.PersistableBundle;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ConnectivityDiagnosticsManagerTest {
+ private static final int NET_ID = 1;
+ private static final int DETECTION_METHOD = 2;
+ private static final long TIMESTAMP = 10L;
+ private static final String INTERFACE_NAME = "interface";
+ private static final String BUNDLE_KEY = "key";
+ private static final String BUNDLE_VALUE = "value";
+
+ private ConnectivityReport createSampleConnectivityReport() {
+ final LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setInterfaceName(INTERFACE_NAME);
+
+ final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+ networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+
+ final PersistableBundle bundle = new PersistableBundle();
+ bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+
+ return new ConnectivityReport(
+ new Network(NET_ID), TIMESTAMP, linkProperties, networkCapabilities, bundle);
+ }
+
+ private ConnectivityReport createDefaultConnectivityReport() {
+ return new ConnectivityReport(
+ new Network(0),
+ 0L,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY);
+ }
+
+ @Test
+ public void testPersistableBundleEquals() {
+ assertFalse(
+ ConnectivityDiagnosticsManager.persistableBundleEquals(
+ null, PersistableBundle.EMPTY));
+ assertFalse(
+ ConnectivityDiagnosticsManager.persistableBundleEquals(
+ PersistableBundle.EMPTY, null));
+ assertTrue(
+ ConnectivityDiagnosticsManager.persistableBundleEquals(
+ PersistableBundle.EMPTY, PersistableBundle.EMPTY));
+
+ final PersistableBundle a = new PersistableBundle();
+ a.putString(BUNDLE_KEY, BUNDLE_VALUE);
+
+ final PersistableBundle b = new PersistableBundle();
+ b.putString(BUNDLE_KEY, BUNDLE_VALUE);
+
+ final PersistableBundle c = new PersistableBundle();
+ c.putString(BUNDLE_KEY, null);
+
+ assertFalse(
+ ConnectivityDiagnosticsManager.persistableBundleEquals(PersistableBundle.EMPTY, a));
+ assertFalse(
+ ConnectivityDiagnosticsManager.persistableBundleEquals(a, PersistableBundle.EMPTY));
+
+ assertTrue(ConnectivityDiagnosticsManager.persistableBundleEquals(a, b));
+ assertTrue(ConnectivityDiagnosticsManager.persistableBundleEquals(b, a));
+
+ assertFalse(ConnectivityDiagnosticsManager.persistableBundleEquals(a, c));
+ assertFalse(ConnectivityDiagnosticsManager.persistableBundleEquals(c, a));
+ }
+
+ @Test
+ public void testConnectivityReportEquals() {
+ assertEquals(createSampleConnectivityReport(), createSampleConnectivityReport());
+ assertEquals(createDefaultConnectivityReport(), createDefaultConnectivityReport());
+
+ final LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setInterfaceName(INTERFACE_NAME);
+
+ final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+ networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+
+ final PersistableBundle bundle = new PersistableBundle();
+ bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+
+ assertNotEquals(
+ createDefaultConnectivityReport(),
+ new ConnectivityReport(
+ new Network(NET_ID),
+ 0L,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY));
+ assertNotEquals(
+ createDefaultConnectivityReport(),
+ new ConnectivityReport(
+ new Network(0),
+ TIMESTAMP,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY));
+ assertNotEquals(
+ createDefaultConnectivityReport(),
+ new ConnectivityReport(
+ new Network(0),
+ 0L,
+ linkProperties,
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY));
+ assertNotEquals(
+ createDefaultConnectivityReport(),
+ new ConnectivityReport(
+ new Network(0),
+ TIMESTAMP,
+ new LinkProperties(),
+ networkCapabilities,
+ PersistableBundle.EMPTY));
+ assertNotEquals(
+ createDefaultConnectivityReport(),
+ new ConnectivityReport(
+ new Network(0),
+ TIMESTAMP,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ bundle));
+ }
+
+ @Test
+ public void testConnectivityReportParcelUnparcel() {
+ assertParcelSane(createSampleConnectivityReport(), 5);
+ }
+
+ private DataStallReport createSampleDataStallReport() {
+ final PersistableBundle bundle = new PersistableBundle();
+ bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+ return new DataStallReport(new Network(NET_ID), TIMESTAMP, DETECTION_METHOD, bundle);
+ }
+
+ private DataStallReport createDefaultDataStallReport() {
+ return new DataStallReport(new Network(0), 0L, 0, PersistableBundle.EMPTY);
+ }
+
+ @Test
+ public void testDataStallReportEquals() {
+ assertEquals(createSampleDataStallReport(), createSampleDataStallReport());
+ assertEquals(createDefaultDataStallReport(), createDefaultDataStallReport());
+
+ final PersistableBundle bundle = new PersistableBundle();
+ bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+
+ assertNotEquals(
+ createDefaultDataStallReport(),
+ new DataStallReport(new Network(NET_ID), 0L, 0, PersistableBundle.EMPTY));
+ assertNotEquals(
+ createDefaultDataStallReport(),
+ new DataStallReport(new Network(0), TIMESTAMP, 0, PersistableBundle.EMPTY));
+ assertNotEquals(
+ createDefaultDataStallReport(),
+ new DataStallReport(new Network(0), 0L, DETECTION_METHOD, PersistableBundle.EMPTY));
+ assertNotEquals(
+ createDefaultDataStallReport(), new DataStallReport(new Network(0), 0L, 0, bundle));
+ }
+
+ @Test
+ public void testDataStallReportParcelUnparcel() {
+ assertParcelSane(createSampleDataStallReport(), 4);
+ }
+}
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index 8eac3ea..fe0224a 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -234,7 +234,9 @@
try {
mLooper = setAsMain ? Looper.getMainLooper() : createLooper();
mTestableLooper = new TestableLooper(mLooper, false);
- mTestableLooper.getLooper().getThread().setName(test.getClass().getName());
+ if (!setAsMain) {
+ mTestableLooper.getLooper().getThread().setName(test.getClass().getName());
+ }
} catch (Exception e) {
throw new RuntimeException(e);
}
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 8fc8af9..dae04c6 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -91,6 +91,8 @@
srcs: [
":framework-wifi-updatable-sources",
],
+ // java_api_finder must accompany `srcs`
+ plugins: ["java_api_finder"],
installable: false,
visibility: [
"//frameworks/opt/net/wifi/service",
@@ -128,7 +130,6 @@
"com.android.wifi",
"test_com.android.wifi",
],
- plugins: ["java_api_finder"],
}
droidstubs {
diff --git a/wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl b/wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl
index 51d74f0..d14ec57 100644
--- a/wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl
+++ b/wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl
@@ -17,6 +17,7 @@
package android.net.wifi;
import android.net.wifi.INetworkRequestUserSelectionCallback;
+import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
/**
@@ -30,7 +31,7 @@
void onAbort();
- void onMatch(in List<android.net.wifi.ScanResult> scanResults);
+ void onMatch(in List<ScanResult> scanResults);
void onUserSelectionConnectSuccess(in WifiConfiguration wificonfiguration);
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 0b5969a..558de7c 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -36,6 +36,7 @@
import android.net.wifi.ITrafficStateCallback;
import android.net.wifi.ITxPacketCountListener;
import android.net.wifi.IWifiConnectedNetworkScorer;
+import android.net.wifi.ScanResult;
import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
@@ -60,9 +61,9 @@
ParceledListSlice getPrivilegedConfiguredNetworks(String packageName, String featureId);
- Map getAllMatchingFqdnsForScanResults(in List<android.net.wifi.ScanResult> scanResult);
+ Map getAllMatchingFqdnsForScanResults(in List<ScanResult> scanResult);
- Map getMatchingOsuProviders(in List<android.net.wifi.ScanResult> scanResult);
+ Map getMatchingOsuProviders(in List<ScanResult> scanResult);
Map getMatchingPasspointConfigsForOsuProviders(in List<OsuProvider> osuProviders);
@@ -88,6 +89,8 @@
boolean disableNetwork(int netId, String packageName);
+ void allowAutojoinGlobal(boolean choice);
+
void allowAutojoin(int netId, boolean choice);
void allowAutojoinPasspoint(String fqdn, boolean enableAutoJoin);
@@ -96,7 +99,7 @@
boolean startScan(String packageName, String featureId);
- List<android.net.wifi.ScanResult> getScanResults(String callingPackage, String callingFeatureId);
+ List<ScanResult> getScanResults(String callingPackage, String callingFeatureId);
boolean disconnect(String packageName);
@@ -179,8 +182,6 @@
int getVerboseLoggingLevel();
- void enableWifiConnectivityManager(boolean enabled);
-
void disableEphemeralNetwork(String SSID, String packageName);
void factoryReset(String packageName);
@@ -255,9 +256,14 @@
int calculateSignalLevel(int rssi);
- List<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(in List<android.net.wifi.ScanResult> scanResults);
+ List<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(in List<ScanResult> scanResults);
boolean setWifiConnectedNetworkScorer(in IBinder binder, in IWifiConnectedNetworkScorer scorer);
void clearWifiConnectedNetworkScorer();
+
+ /**
+ * Return the Map of {@link WifiNetworkSuggestion} and the list of <ScanResult>
+ */
+ Map getMatchingScanResults(in List<WifiNetworkSuggestion> networkSuggestions, in List<ScanResult> scanResults, String callingPackage, String callingFeatureId);
}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index e2befeb..114e0fa 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -1662,7 +1662,9 @@
*/
@NonNull
public NetworkSelectionStatus build() {
- return mNetworkSelectionStatus;
+ NetworkSelectionStatus status = new NetworkSelectionStatus();
+ status.copy(mNetworkSelectionStatus);
+ return status;
}
}
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 5edcc2d..04016b6 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -856,11 +856,11 @@
* like /etc/ssl/certs. If configured, these certificates are added to the
* list of trusted CAs. ca_cert may also be included in that case, but it is
* not required.
- * @param path The path for CA certificate files, or null/empty string to clear.
+ * @param path The path for CA certificate files, or empty string to clear.
* @hide
*/
@SystemApi
- public void setCaPath(@Nullable String path) {
+ public void setCaPath(@NonNull String path) {
setFieldValue(CA_PATH_KEY, path);
}
@@ -881,11 +881,11 @@
* <p> See the {@link android.security.KeyChain} for details on installing or choosing
* a certificate
* </p>
- * @param alias identifies the certificate, or null/empty string to clear.
+ * @param alias identifies the certificate, or empty string to clear.
* @hide
*/
@SystemApi
- public void setClientCertificateAlias(@Nullable String alias) {
+ public void setClientCertificateAlias(@NonNull String alias) {
setFieldValue(CLIENT_CERT_KEY, alias, CLIENT_CERT_PREFIX);
setFieldValue(PRIVATE_KEY_ID_KEY, alias, USER_PRIVATE_KEY);
// Also, set engine parameters
@@ -1360,11 +1360,11 @@
* If this field is not specified, WAPI-CERT uses ASU ID from WAI packet
* as the certificate suite name automatically.
*
- * @param wapiCertSuite The name for WAPI certificate suite, or null/empty string to clear.
+ * @param wapiCertSuite The name for WAPI certificate suite, or empty string to clear.
* @hide
*/
@SystemApi
- public void setWapiCertSuite(@Nullable String wapiCertSuite) {
+ public void setWapiCertSuite(@NonNull String wapiCertSuite) {
setFieldValue(WAPI_CERT_SUITE_KEY, wapiCertSuite);
}
@@ -1373,7 +1373,7 @@
* @return the certificate suite name
* @hide
*/
- @Nullable
+ @NonNull
@SystemApi
public String getWapiCertSuite() {
return getFieldValue(WAPI_CERT_SUITE_KEY);
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index f2a875b..419bcb1 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -435,7 +435,7 @@
*/
@NonNull
public WifiInfo build() {
- return mWifiInfo;
+ return new WifiInfo(mWifiInfo);
}
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 208ce9c..7f99fe4 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -22,6 +22,7 @@
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -1792,18 +1793,6 @@
}
/**
- * Same as {@link #registerNetworkRequestMatchCallback(Executor, NetworkRequestMatchCallback)},
- * except that the callback will be executed on the application's main thread.
- * @param callback Callback for network match events to register.
- * @hide
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
- public void registerNetworkRequestMatchCallback(@NonNull NetworkRequestMatchCallback callback) {
- registerNetworkRequestMatchCallback(mContext.getMainExecutor(), callback);
- }
-
- /**
* Registers a callback for NetworkRequest matches. See {@link NetworkRequestMatchCallback}.
* Caller can unregister a previously registered callback using
* {@link #unregisterNetworkRequestMatchCallback(NetworkRequestMatchCallback)}
@@ -2353,7 +2342,7 @@
return (getSupportedFeatures() & feature) == feature;
}
- /**
+ /**
* @return true if this adapter supports Passpoint
* @hide
*/
@@ -2685,6 +2674,34 @@
}
/**
+ * Return the filtered ScanResults which may be authenticated by the suggested network
+ * configurations.
+ * @param networkSuggestions The list of {@link WifiNetworkSuggestion}
+ * @param scanResults The scan results to be filtered, this is optional, if it is null or
+ * empty, wifi system would use the recent scan results in the system.
+ * @return The map of {@link WifiNetworkSuggestion} and the list of {@link ScanResult} which
+ * may be authenticated by the corresponding network configuration.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE})
+ @NonNull
+ public Map<WifiNetworkSuggestion, List<ScanResult>> getMatchingScanResults(
+ @NonNull List<WifiNetworkSuggestion> networkSuggestions,
+ @Nullable List<ScanResult> scanResults) {
+ if (networkSuggestions == null) {
+ throw new IllegalArgumentException("networkSuggestions must not be null.");
+ }
+ try {
+ return mService.getMatchingScanResults(
+ networkSuggestions, scanResults,
+ mContext.getOpPackageName(), mContext.getFeatureId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Check if scanning is always available.
*
* If this return {@code true}, apps can issue {@link #startScan} and fetch scan results
@@ -2877,6 +2894,7 @@
* [0, {@link #getMaxSignalLevel()}], where 0 is the lowest (worst signal) RSSI
* rating and {@link #getMaxSignalLevel()} is the highest (best signal) RSSI rating.
*/
+ @IntRange(from = 0)
public int calculateSignalLevel(int rssi) {
try {
return mService.calculateSignalLevel(rssi);
@@ -2889,6 +2907,7 @@
* Get the system default maximum signal level.
* This is the maximum RSSI level returned by {@link #calculateSignalLevel(int)}.
*/
+ @IntRange(from = 0)
public int getMaxSignalLevel() {
return calculateSignalLevel(Integer.MAX_VALUE);
}
@@ -3688,18 +3707,6 @@
}
/**
- * Same as {@link #registerSoftApCallback(Executor, SoftApCallback)},
- * except that the callback will be executed on the application's main thread.
- * @param callback Callback for soft AP events
- * @hide
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
- public void registerSoftApCallback(@NonNull SoftApCallback callback) {
- registerSoftApCallback(mContext.getMainExecutor(), callback);
- }
-
- /**
* Registers a callback for Soft AP. See {@link SoftApCallback}. Caller will receive the current
* soft AP state and number of connected devices immediately after a successful call to this API
* via callback. Note that receiving an immediate WIFI_AP_STATE_FAILED value for soft AP state
@@ -4271,6 +4278,23 @@
}
/**
+ * Allows the OEM to enable/disable auto-join globally.
+ *
+ * @param choice true to allow autojoin, false to disallow autojoin
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void allowAutojoinGlobal(boolean choice) {
+ try {
+ mService.allowAutojoinGlobal(choice);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /**
* Sets the user choice for allowing auto-join to a network.
* The updated choice will be made available through the updated config supplied by the
* CONFIGURED_NETWORKS_CHANGED broadcast.
@@ -4897,18 +4921,6 @@
}
/**
- * Enable/disable WifiConnectivityManager
- * @hide
- */
- public void enableWifiConnectivityManager(boolean enabled) {
- try {
- mService.enableWifiConnectivityManager(enabled);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Returns a byte stream representing the data that needs to be backed up to save the
* current Wifi state.
* This Wifi state can be restored by calling {@link #restoreBackupData(byte[])}.
@@ -5116,18 +5128,6 @@
}
/**
- * Same as {@link #registerTrafficStateCallback(Executor, TrafficStateCallback)},
- * except that the callback will be executed on the application's main thread.
- * @param callback Callback for traffic state events
- * @hide
- */
- @SystemApi
- @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
- public void registerTrafficStateCallback(@NonNull TrafficStateCallback callback) {
- registerTrafficStateCallback(mContext.getMainExecutor(), callback);
- }
-
- /**
* Registers a callback for monitoring traffic state. See {@link TrafficStateCallback}. These
* callbacks will be invoked periodically by platform to inform clients about the current
* traffic state. Caller can unregister a previously registered callback using
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index 8fa9c3d..9562f95 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -66,12 +66,20 @@
/** @hide */
public String passphrase = "";
- /** Get the required band for the group owner. */
+ /**
+ * Get the required band for the group owner.
+ * The result will be one of the following:
+ * {@link #GROUP_OWNER_BAND_AUTO},
+ * {@link #GROUP_OWNER_BAND_2GHZ},
+ * {@link #GROUP_OWNER_BAND_5GHZ}
+ */
+ @GroupOperatingBandType
public int getGroupOwnerBand() {
return groupOwnerBand;
}
/** @hide */
+ @GroupOperatingBandType
public int groupOwnerBand = GROUP_OWNER_BAND_AUTO;
/** @hide */
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index 56fa6e2..080c6c7 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -178,6 +178,11 @@
}
@Override
+ public void allowAutojoinGlobal(boolean choice) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void allowAutojoin(int netId, boolean choice) {
throw new UnsupportedOperationException();
}
@@ -404,7 +409,8 @@
throw new UnsupportedOperationException();
}
- @Override
+ /** @deprecated use {@link #allowAutojoinGlobal(boolean)} instead */
+ @Deprecated
public void enableWifiConnectivityManager(boolean enabled) {
throw new UnsupportedOperationException();
}
@@ -617,4 +623,12 @@
public void clearWifiConnectedNetworkScorer() {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public Map<WifiNetworkSuggestion, List<ScanResult>> getMatchingScanResults(
+ List<WifiNetworkSuggestion> networkSuggestions,
+ List<ScanResult> scanResults,
+ String callingPackage, String callingFeatureId) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 0ef75aa..8023160 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -24,6 +24,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
import android.net.MacAddress;
@@ -386,4 +387,43 @@
.get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
assertTrue(config.requirePMF);
}
+
+ /**
+ * Test that the NetworkSelectionStatus Builder returns the same values that was set, and that
+ * calling build multiple times returns different instances.
+ */
+ @Test
+ public void testNetworkSelectionStatusBuilder() throws Exception {
+ NetworkSelectionStatus.Builder builder = new NetworkSelectionStatus.Builder()
+ .setNetworkSelectionDisableReason(
+ NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION)
+ .setNetworkSelectionStatus(
+ NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED);
+
+ NetworkSelectionStatus status1 = builder.build();
+
+ assertEquals(NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION,
+ status1.getNetworkSelectionDisableReason());
+ assertEquals(NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED,
+ status1.getNetworkSelectionStatus());
+
+ NetworkSelectionStatus status2 = builder
+ .setNetworkSelectionDisableReason(NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD)
+ .build();
+
+ // different instances
+ assertNotSame(status1, status2);
+
+ // assert that status1 didn't change
+ assertEquals(NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION,
+ status1.getNetworkSelectionDisableReason());
+ assertEquals(NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED,
+ status1.getNetworkSelectionStatus());
+
+ // assert that status2 changed
+ assertEquals(NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD,
+ status2.getNetworkSelectionDisableReason());
+ assertEquals(NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED,
+ status2.getNetworkSelectionStatus());
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiInfoTest.java b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
index af85ce0..04759ac 100644
--- a/wifi/tests/src/android/net/wifi/WifiInfoTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
@@ -26,6 +27,8 @@
import org.junit.Test;
+import java.nio.charset.StandardCharsets;
+
/**
* Unit tests for {@link android.net.wifi.WifiInfo}.
*/
@@ -41,6 +44,11 @@
private static final int TEST_WIFI_STANDARD = ScanResult.WIFI_STANDARD_11AC;
private static final int TEST_MAX_SUPPORTED_TX_LINK_SPEED_MBPS = 866;
private static final int TEST_MAX_SUPPORTED_RX_LINK_SPEED_MBPS = 1200;
+ private static final String TEST_SSID = "Test123";
+ private static final String TEST_BSSID = "12:12:12:12:12:12";
+ private static final int TEST_RSSI = -60;
+ private static final int TEST_NETWORK_ID = 5;
+ private static final int TEST_NETWORK_ID2 = 6;
/**
* Verify parcel write/read with WifiInfo.
@@ -101,4 +109,43 @@
assertEquals(null, wifiInfo.getBSSID());
assertEquals(-1, wifiInfo.getNetworkId());
}
+
+ /**
+ * Test that the WifiInfo Builder returns the same values that was set, and that
+ * calling build multiple times returns different instances.
+ */
+ @Test
+ public void testWifiInfoBuilder() throws Exception {
+ WifiInfo.Builder builder = new WifiInfo.Builder()
+ .setSsid(TEST_SSID.getBytes(StandardCharsets.UTF_8))
+ .setBssid(TEST_BSSID)
+ .setRssi(TEST_RSSI)
+ .setNetworkId(TEST_NETWORK_ID);
+
+ WifiInfo info1 = builder.build();
+
+ assertEquals("\"" + TEST_SSID + "\"", info1.getSSID());
+ assertEquals(TEST_BSSID, info1.getBSSID());
+ assertEquals(TEST_RSSI, info1.getRssi());
+ assertEquals(TEST_NETWORK_ID, info1.getNetworkId());
+
+ WifiInfo info2 = builder
+ .setNetworkId(TEST_NETWORK_ID2)
+ .build();
+
+ // different instances
+ assertNotSame(info1, info2);
+
+ // assert that info1 didn't change
+ assertEquals("\"" + TEST_SSID + "\"", info1.getSSID());
+ assertEquals(TEST_BSSID, info1.getBSSID());
+ assertEquals(TEST_RSSI, info1.getRssi());
+ assertEquals(TEST_NETWORK_ID, info1.getNetworkId());
+
+ // assert that info2 changed
+ assertEquals("\"" + TEST_SSID + "\"", info2.getSSID());
+ assertEquals(TEST_BSSID, info2.getBSSID());
+ assertEquals(TEST_RSSI, info2.getRssi());
+ assertEquals(TEST_NETWORK_ID2, info2.getNetworkId());
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index f369203..3846473 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -881,17 +881,6 @@
}
/**
- * Verify main looper is used when handler is not provided.
- */
- @Test
- public void registerSoftApCallbackUsesMainExecutorOnNoExecutorProvided() {
- when(mContext.getMainExecutor()).thenReturn(
- new HandlerExecutor(new Handler(mLooper.getLooper())));
- mWifiManager.registerSoftApCallback(mSoftApCallback);
- verify(mContext).getMainExecutor();
- }
-
- /**
* Verify the call to registerSoftApCallback goes to WifiServiceImpl.
*/
@Test
@@ -1389,11 +1378,10 @@
@Test
public void registerTrafficStateCallbackUsesMainLooperOnNullArgumentForHandler()
throws Exception {
- when(mContext.getMainExecutor()).thenReturn(
- new HandlerExecutor(new Handler(mLooper.getLooper())));
ArgumentCaptor<ITrafficStateCallback.Stub> callbackCaptor =
ArgumentCaptor.forClass(ITrafficStateCallback.Stub.class);
- mWifiManager.registerTrafficStateCallback(mTrafficStateCallback);
+ mWifiManager.registerTrafficStateCallback(
+ new HandlerExecutor(new Handler(mLooper.getLooper())), mTrafficStateCallback);
verify(mWifiService).registerTrafficStateCallback(
any(IBinder.class), callbackCaptor.capture(), anyInt());
@@ -1474,11 +1462,11 @@
@Test
public void registerNetworkRequestMatchCallbackCallGoesToWifiServiceImpl()
throws Exception {
- when(mContext.getMainExecutor()).thenReturn(
- new HandlerExecutor(new Handler(mLooper.getLooper())));
ArgumentCaptor<INetworkRequestMatchCallback.Stub> callbackCaptor =
ArgumentCaptor.forClass(INetworkRequestMatchCallback.Stub.class);
- mWifiManager.registerNetworkRequestMatchCallback(mNetworkRequestMatchCallback);
+ mWifiManager.registerNetworkRequestMatchCallback(
+ new HandlerExecutor(new Handler(mLooper.getLooper())),
+ mNetworkRequestMatchCallback);
verify(mWifiService).registerNetworkRequestMatchCallback(
any(IBinder.class), callbackCaptor.capture(), anyInt());
@@ -1530,11 +1518,11 @@
@Test
public void networkRequestUserSelectionCallbackCallGoesToWifiServiceImpl()
throws Exception {
- when(mContext.getMainExecutor()).thenReturn(
- new HandlerExecutor(new Handler(mLooper.getLooper())));
ArgumentCaptor<INetworkRequestMatchCallback.Stub> callbackCaptor =
ArgumentCaptor.forClass(INetworkRequestMatchCallback.Stub.class);
- mWifiManager.registerNetworkRequestMatchCallback(mNetworkRequestMatchCallback);
+ mWifiManager.registerNetworkRequestMatchCallback(
+ new HandlerExecutor(new Handler(mLooper.getLooper())),
+ mNetworkRequestMatchCallback);
verify(mWifiService).registerNetworkRequestMatchCallback(
any(IBinder.class), callbackCaptor.capture(), anyInt());