diff options
419 files changed, 14860 insertions, 3785 deletions
diff --git a/Android.bp b/Android.bp index 58f6119c2243..9411eeca834c 100644 --- a/Android.bp +++ b/Android.bp @@ -297,6 +297,8 @@ filegroup { ], } +// 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 @@ java_defaults { "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 @@ java_library { "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 @@ java_defaults { "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 { java_library { name: "framework-annotation-proc", - defaults: ["framework-aidl-export-defaults"], srcs: [":framework-all-sources"], libs: [ "app-compat-annotations", @@ -713,7 +715,10 @@ filegroup { 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 273b867e8f98..39a2d38fa642 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 fd201866e702..8bf13eec6249 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 @@ public final class AppSearch { 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 e3f6b3de433a..15c336820df7 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.android.internal.infra.AndroidFuture; 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 @@ public class AppSearchManager { *<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 @@ public class AppSearchManager { }, 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 20c8af985c21..eef41ed7104d 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 @@ interface IAppSearchManager { 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 000000000000..6aa91a3fe9e4 --- /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 d763103f1217..f48ebde288f3 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 final class SearchResults { 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 @@ public final class SearchResults { * @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 5df7108fec09..c276ae1fe45e 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 @@ package android.app.appsearch; 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 @@ public final class SearchSpec { /** @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,51 +81,164 @@ public final class SearchSpec { @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; } @@ -117,11 +249,12 @@ public final class SearchSpec { */ @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 d2d9cf9fdf17..6293ee7059e5 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 @@ public class AppSearchManagerService extends SystemService { // 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 @@ public class AppSearchManagerService extends SystemService { throw new RuntimeException(e); } SearchResultProto searchResults = - mFakeIcing.query(queryExpression); + mFakeIcing.query(searchSpecProto.getQuery()); callback.complete(searchResults.toByteArray()); } } diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java index 47af7c0a5ed9..756bc5ee14c7 100644 --- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java +++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java @@ -37,11 +37,99 @@ import java.util.function.Consumer; /** * This class provides access to the blob store managed by the system. * - * Apps can publish data blobs which might be useful for other apps on the device to be - * managed by the system and apps that would like to access these data blobs can do so - * by addressing them via their cryptographically secure hashes. + * <p> Apps can publish and access a data blob using a {@link BlobHandle} object which can + * be created with {@link BlobHandle#createWithSha256(byte[], CharSequence, long, String)}. + * This {@link BlobHandle} object encapsulates the following pieces of information used for + * identifying the blobs: + * <ul> + * <li> {@link BlobHandle#getSha256Digest()} + * <li> {@link BlobHandle#getLabel()} + * <li> {@link BlobHandle#getExpiryTimeMillis()} + * <li> {@link BlobHandle#getTag()} + * </ul> + * For two {@link BlobHandle} objects to be considered identical, all these pieces of information + * must be equal. * - * TODO: More documentation. + * <p> For contributing a new data blob, an app needs to create a session using + * {@link BlobStoreManager#createSession(BlobHandle)} and then open this session for writing using + * {@link BlobStoreManager#openSession(long)}. + * + * <p> The following code snippet shows how to create and open a session for writing: + * <pre class="prettyprint"> + * final long sessionId = blobStoreManager.createSession(blobHandle); + * try (BlobStoreManager.Session session = blobStoreManager.openSession(sessionId)) { + * try (ParcelFileDescriptor pfd = new ParcelFileDescriptor.AutoCloseOutputStream( + * session.openWrite(offsetBytes, lengthBytes))) { + * writeData(pfd); + * } + * } + * </pre> + * + * <p> If all the data could not be written in a single attempt, apps can close this session + * and re-open it again using the session id obtained via + * {@link BlobStoreManager#createSession(BlobHandle)}. Note that the session data is persisted + * and can be re-opened for completing the data contribution, even across device reboots. + * + * <p> After the data is written to the session, it can be committed using + * {@link Session#commit(Executor, Consumer)}. Until the session is committed, data written + * to the session will not be shared with any app. + * + * <p class="note"> Once a session is committed using {@link Session#commit(Executor, Consumer)}, + * any data written as part of this session is sealed and cannot be modified anymore. + * + * <p> Before committing the session, apps can indicate which apps are allowed to access the + * contributed data using one or more of the following access modes: + * <ul> + * <li> {@link Session#allowPackageAccess(String, byte[])} which will allow whitelisting + * specific packages to access the blobs. + * <li> {@link Session#allowSameSignatureAccess()} which will allow only apps which are signed + * with the same certificate as the app which contributed the blob to access it. + * <li> {@link Session#allowPublicAccess()} which will allow any app on the device to access + * the blob. + * </ul> + * + * <p> The following code snippet shows how to specify the access mode and commit the session: + * <pre class="prettyprint"> + * try (BlobStoreManager.Session session = blobStoreManager.openSession(sessionId)) { + * try (ParcelFileDescriptor pfd = new ParcelFileDescriptor.AutoCloseOutputStream( + * session.openWrite(offsetBytes, lengthBytes))) { + * writeData(pfd); + * } + * session.allowSameSignatureAccess(); + * session.allowPackageAccess(packageName, certificate); + * session.commit(executor, callback); + * } + * </pre> + * + * <p> Apps that satisfy at least one of the access mode constraints specified by the publisher + * of the data blob will be able to access it. + * + * <p> A data blob published without specifying any of + * these access modes will be considered private and only the app that contributed the data + * blob will be allowed to access it. This is still useful for overall device system health as + * the System can try to keep one copy of data blob on disk when multiple apps contribute the + * same data. + * + * <p class="note"> It is strongly recommended that apps use one of + * {@link Session#allowPackageAccess(String, byte[])} or {@link Session#allowSameSignatureAccess()} + * when they know, ahead of time, the set of apps they would like to share the blobs with. + * {@link Session#allowPublicAccess()} is meant for publicly available data committed from + * libraries and SDKs. + * + * <p> Once a data blob is committed with {@link Session#commit(Executor, Consumer)}, it + * can be accessed using {@link BlobStoreManager#openBlob(BlobHandle)}, assuming the caller + * satisfies constraints of any of the access modes associated with that data blob. An app may + * acquire a lease on a blob with {@link BlobStoreManager#acquireLease(BlobHandle, int)} and + * release the lease with {@link BlobStoreManager#releaseLease(BlobHandle)}. A blob will not be + * deleted from the system while there is at least one app leasing it. + * + * <p> The following code snippet shows how to access the data blob: + * <pre class="prettyprint"> + * try (ParcelFileDescriptor pfd = new ParcelFileDescriptor.AutoCloseInputStream( + * blobStoreManager.openBlob(blobHandle)) { + * useData(pfd); + * } + * </pre> */ @SystemService(Context.BLOB_STORE_SERVICE) public class BlobStoreManager { @@ -323,6 +411,28 @@ public class BlobStoreManager { } /** + * 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 4ae919bfedab..4035b96938d9 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 @@ import android.os.ParcelFileDescriptor; /** {@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 29092b327fc7..612fd89ebbe0 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 @@ package com.android.server.blob; 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 @@ public class BlobStoreSession extends IBlobStoreSession.Stub { } @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 7ba21575df4a..b4acf1283d3e 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/permission/apex_manifest.json b/apex/permission/apex_manifest.json index 2a8c4f737dff..7960598affa3 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 51b911a94edc..1dbad456760c 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 @@ package com.android.permission.persistence; 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 @@ public class RuntimePermissionsPersistenceImpl implements RuntimePermissionsPers 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 @@ public class RuntimePermissionsPersistenceImpl implements RuntimePermissionsPers @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 5061742f4c58..06fad77c495c 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 @@ package com.android.role.persistence; 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 @@ public class RolesPersistenceImpl implements RolesPersistence { 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 @@ public class RolesPersistenceImpl implements RolesPersistence { @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 048f5c4f177b..deeb29e155c0 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 0c0ad860f3d1..e2972e700880 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/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp index 0b46645ad06f..f66f0340edab 100644 --- a/apex/statsd/framework/Android.bp +++ b/apex/statsd/framework/Android.bp @@ -24,7 +24,7 @@ java_library { name: "framework-statsd", installable: true, // TODO(b/146209659): Use system_current instead. - sdk_version: "core_current", + sdk_version: "core_platform", srcs: [ ":framework-statsd-sources", ], @@ -35,7 +35,9 @@ java_library { libs: [ "framework-annotations-lib", // TODO(b/146230220): Use framework-system-stubs instead. - "android_system_stubs_current", + //"android_system_stubs_current", + //"framework_module_lib_stubs_current", + "framework-all", ], hostdex: true, // for hiddenapi check visibility: [ @@ -52,12 +54,14 @@ java_library { droidstubs { name: "framework-statsd-stubs-docs", defaults: [ - "framework-module-stubs-defaults-publicapi" + "framework-module-stubs-defaults-systemapi" ], srcs: [ + ":framework-annotations", ":framework-statsd-sources", ], libs: [ + // TODO(b/148218250): Change to android_system_stubs_current "framework-all", ], sdk_version: "core_platform", @@ -70,6 +74,7 @@ java_library { ":framework-statsd-stubs-docs", ], libs: [ + // TODO(b/148218250): Change to android_system_stubs_current "framework-all", ], sdk_version: "core_platform", diff --git a/core/java/android/app/StatsManager.java b/apex/statsd/framework/java/android/app/StatsManager.java index 0ea05d8f683c..ad1ac95d667c 100644 --- a/core/java/android/app/StatsManager.java +++ b/apex/statsd/framework/java/android/app/StatsManager.java @@ -30,7 +30,7 @@ import android.os.IPullAtomResultReceiver; import android.os.IStatsManagerService; import android.os.IStatsd; import android.os.RemoteException; -import android.os.ServiceManager; +import android.os.StatsFrameworkInitializer; import android.util.AndroidException; import android.util.Slog; import android.util.StatsEvent; @@ -702,7 +702,10 @@ public final class StatsManager { return mStatsManagerService; } mStatsManagerService = IStatsManagerService.Stub.asInterface( - ServiceManager.getService(Context.STATS_MANAGER_SERVICE)); + StatsFrameworkInitializer + .getStatsServiceManager() + .getStatsManagerServiceRegisterer() + .get()); return mStatsManagerService; } diff --git a/apex/statsd/framework/java/android/os/StatsFrameworkInitializer.java b/apex/statsd/framework/java/android/os/StatsFrameworkInitializer.java new file mode 100644 index 000000000000..3d955336b45c --- /dev/null +++ b/apex/statsd/framework/java/android/os/StatsFrameworkInitializer.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.os; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.app.StatsManager; +import android.app.SystemServiceRegistry; +import android.content.Context; + +/** + * Class for performing registration for all stats services + * + * TODO(b/148225705) Change to @SystemApi(client=MODULE_LIBRARIES) when the build system is ready. + * @hide + */ +@SystemApi +public class StatsFrameworkInitializer { + private StatsFrameworkInitializer() { + } + + private static volatile StatsServiceManager sStatsServiceManager; + + /** + * Sets an instance of {@link StatsServiceManager} that allows + * the statsd mainline module to register/obtain stats binder services. This is called + * by the platform during the system initialization. + * + * @param statsServiceManager instance of {@link StatsServiceManager} that allows + * the statsd mainline module to register/obtain statsd binder services. + */ + public static void setStatsServiceManager( + @NonNull StatsServiceManager statsServiceManager) { + if (sStatsServiceManager != null) { + throw new IllegalStateException("setStatsServiceManager called twice!"); + } + + if (statsServiceManager == null) { + throw new NullPointerException("statsServiceManager is null"); + } + + sStatsServiceManager = statsServiceManager; + } + + /** @hide */ + public static StatsServiceManager getStatsServiceManager() { + return sStatsServiceManager; + } + + /** + * Called by {@link SystemServiceRegistry}'s static initializer and registers all statsd + * services to {@link Context}, so that {@link Context#getSystemService} can return them. + * + * @throws IllegalStateException if this is called from anywhere besides + * {@link SystemServiceRegistry} + */ + public static void registerServiceWrappers() { + SystemServiceRegistry.registerContextAwareService( + Context.STATS_MANAGER, + StatsManager.class, + context -> new StatsManager(context) + ); + } +} diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java index bcbb5a1407f6..4c8790f47bb6 100644 --- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java +++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java @@ -85,6 +85,7 @@ import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.StatsFrameworkInitializer; import android.os.StatFs; import android.os.StatsLogEventWrapper; import android.os.SynchronousResultReceiver; @@ -750,7 +751,10 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { * sStatsd with a null check. */ private static IStatsd fetchStatsdService() { - return IStatsd.Stub.asInterface(ServiceManager.getService("stats")); + return IStatsd.Stub.asInterface(StatsFrameworkInitializer + .getStatsServiceManager() + .getStatsdServiceRegisterer() + .get()); } /** diff --git a/api/current.txt b/api/current.txt index eb8ef8fa0190..056d8cab10f6 100644 --- a/api/current.txt +++ b/api/current.txt @@ -333,6 +333,9 @@ package android { 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 @@ package android { 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 @@ package android.app.admin { 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 @@ package android.app.admin { 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 @@ package android.app.admin { 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 @@ package android.app.admin { 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 @@ package android.app.blob { 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; } @@ -9965,6 +9975,7 @@ package android.content { 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); @@ -9989,6 +10000,7 @@ package android.content { 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(); @@ -10101,6 +10113,7 @@ package android.content { 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 @@ -11770,6 +11783,7 @@ package android.content.pm { 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(); @@ -12077,6 +12091,7 @@ package android.content.pm { 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"; @@ -13515,227 +13530,227 @@ package android.database.sqlite { 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; - } - - 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 - } - - 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 - } - - 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); - } - - 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 - } - - 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 - } - - 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; - } - - 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 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; + } + + @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 + } + + @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 + } + + @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); + } + + @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 + } + + @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 + } + + @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; + } + + @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 - } - - 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"; - } - - public static class DrmStore.DrmObjectType { + 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 + } + + @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"; + } + + @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(); } } @@ -17249,6 +17264,8 @@ package android.hardware.camera2 { 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 { @@ -23380,6 +23397,9 @@ package android.location { 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(); @@ -23390,6 +23410,9 @@ package android.location { 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; @@ -23414,6 +23437,10 @@ package android.location { 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(); @@ -23425,6 +23452,10 @@ package android.location { 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 @@ -23828,6 +23859,8 @@ package android.media { 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(); @@ -24185,8 +24218,10 @@ package android.media { 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(); } @@ -24194,9 +24229,11 @@ package android.media { 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 { @@ -24448,6 +24485,8 @@ package android.media { 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 @@ -27616,6 +27655,8 @@ package android.media.audiofx { 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; @@ -29547,8 +29588,6 @@ package android.net { 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 { @@ -29558,21 +29597,29 @@ package android.net { 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 { @@ -31070,7 +31117,7 @@ package android.net.wifi { 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); @@ -31083,7 +31130,7 @@ package android.net.wifi { 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(); @@ -42725,6 +42772,7 @@ package android.service.autofill { public static final class FillResponse.Builder { ctor public FillResponse.Builder(); method @NonNull public android.service.autofill.FillResponse.Builder addDataset(@Nullable android.service.autofill.Dataset); + method @NonNull public android.service.autofill.FillResponse.Builder addInlineAction(@NonNull android.service.autofill.InlinePresentation); method @NonNull public android.service.autofill.FillResponse build(); method @NonNull public android.service.autofill.FillResponse.Builder disableAutofill(long); method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews); @@ -42754,10 +42802,11 @@ package android.service.autofill { } public final class InlinePresentation implements android.os.Parcelable { - ctor public InlinePresentation(@NonNull android.app.slice.Slice, @NonNull android.view.inline.InlinePresentationSpec); + ctor public InlinePresentation(@NonNull android.app.slice.Slice, @NonNull android.view.inline.InlinePresentationSpec, boolean); method public int describeContents(); method @NonNull public android.view.inline.InlinePresentationSpec getInlinePresentationSpec(); method @NonNull public android.app.slice.Slice getSlice(); + method public boolean isPinned(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.service.autofill.InlinePresentation> CREATOR; } @@ -43524,7 +43573,7 @@ package android.service.voice { 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 { @@ -46126,6 +46175,7 @@ package android.telephony { 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"; @@ -46172,6 +46222,15 @@ package android.telephony { 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."; @@ -46271,6 +46330,7 @@ package android.telephony { 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(); @@ -46288,7 +46348,7 @@ package android.telephony { public final class CellIdentityNr extends android.telephony.CellIdentity { method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns(); - method public int getBand(); + method @NonNull public java.util.List<java.lang.Integer> getBands(); method @Nullable public String getMccString(); method @Nullable public String getMncString(); method public long getNci(); @@ -47209,6 +47269,7 @@ package android.telephony { 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(); @@ -47264,6 +47325,7 @@ package android.telephony { 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(); @@ -47672,10 +47734,42 @@ package android.telephony.euicc { 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 } } @@ -52548,6 +52642,7 @@ package android.view { 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); @@ -52591,6 +52686,7 @@ package android.view { 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); @@ -52598,6 +52694,20 @@ package android.view { 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(); @@ -52642,7 +52752,9 @@ package android.view { 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); @@ -52782,7 +52894,7 @@ package android.view { 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); @@ -52792,7 +52904,7 @@ package android.view { 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); @@ -52945,7 +53057,7 @@ package android.view { 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(); @@ -52973,7 +53085,7 @@ package android.view { 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); @@ -53111,7 +53223,7 @@ package android.view { 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); @@ -53253,7 +53365,7 @@ package android.view { 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); @@ -53290,7 +53402,7 @@ package android.view { 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); @@ -53468,18 +53580,18 @@ package android.view { 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 @@ -53603,8 +53715,8 @@ package android.view { 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 { @@ -54241,6 +54353,7 @@ package android.view { 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); @@ -54279,6 +54392,7 @@ package android.view { 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); @@ -54373,6 +54487,10 @@ package android.view { 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); } @@ -54417,23 +54535,23 @@ package android.view { 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(); @@ -54449,17 +54567,24 @@ package android.view { 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(); @@ -54468,7 +54593,6 @@ package android.view { method public static int systemBars(); method public static int systemGestures(); method public static int tappableElement(); - method public static int windowDecor(); } public interface WindowInsetsAnimationCallback { @@ -54496,7 +54620,7 @@ package android.view { 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); } @@ -54517,19 +54641,27 @@ package android.view { } 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); } @@ -54555,9 +54687,15 @@ package android.view { 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 @@ -54578,13 +54716,13 @@ package android.view { 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 @@ -54598,8 +54736,8 @@ package android.view { 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 @@ -54625,7 +54763,7 @@ package android.view { 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 @@ -54682,7 +54820,7 @@ package android.view { 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; @@ -54692,6 +54830,12 @@ package android.view { 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 { @@ -55789,7 +55933,12 @@ package android.view.inputmethod { 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 @@ -55869,10 +56018,13 @@ package android.view.inputmethod { method @Nullable public String[] getAutofillHints(); method @NonNull public android.view.inline.InlinePresentationSpec getPresentationSpec(); method @NonNull public String getSource(); + method @NonNull public String getType(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.InlineSuggestionInfo> CREATOR; field public static final String SOURCE_AUTOFILL = "android:autofill"; field public static final String SOURCE_PLATFORM = "android:platform"; + field public static final String TYPE_ACTION = "android:autofill:action"; + field public static final String TYPE_SUGGESTION = "android:autofill:suggestion"; } public final class InlineSuggestionsRequest implements android.os.Parcelable { diff --git a/api/system-current.txt b/api/system-current.txt index 2aac908b3937..9b2e19cfc620 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -55,13 +55,16 @@ package android { 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 @@ package android { 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 @@ package android { 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 @@ package android { 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"; } @@ -249,6 +254,7 @@ package android { 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 @@ -393,6 +399,7 @@ package android.app { 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"; @@ -1718,7 +1725,7 @@ package android.bluetooth.le { 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); } } @@ -2062,6 +2069,21 @@ package android.content.pm { 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[]); @@ -2152,11 +2174,16 @@ package android.content.pm { 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 { @@ -2241,6 +2268,7 @@ package android.content.pm { 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 @@ -2927,6 +2955,48 @@ package android.hardware.hdmi { } +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 { @@ -3639,9 +3709,34 @@ package android.hardware.radio { 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; @@ -3680,6 +3775,16 @@ package android.hardware.soundtrigger { 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 { @@ -4189,9 +4294,11 @@ package android.media { 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); @@ -4205,6 +4312,7 @@ package android.media { 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); @@ -4331,6 +4439,7 @@ package android.media.audiopolicy { 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 { @@ -4351,9 +4460,11 @@ package android.media.audiopolicy { 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 @@ -5017,6 +5128,7 @@ package android.media.tv.tuner.filter { } 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(); @@ -5884,6 +5996,16 @@ package android.media.tv.tuner.frontend { } +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 { @@ -5970,44 +6092,44 @@ package android.net { 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 startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler); + 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 } - public abstract static class ConnectivityManager.OnStartTetheringCallback { - ctor public ConnectivityManager.OnStartTetheringCallback(); - method public void onTetheringFailed(); - method public void onTetheringStarted(); + @Deprecated public abstract static class ConnectivityManager.OnStartTetheringCallback { + ctor @Deprecated public ConnectivityManager.OnStartTetheringCallback(); + method @Deprecated public void onTetheringFailed(); + method @Deprecated 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 { @@ -6079,13 +6201,18 @@ package android.net { 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 { @@ -6213,7 +6340,7 @@ package android.net { 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; @@ -6264,12 +6391,12 @@ package android.net { } 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"; @@ -6284,9 +6411,10 @@ package android.net { 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 { @@ -6395,17 +6523,82 @@ package android.net { 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(); + public final class TelephonyNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { 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 @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering(); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) 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.StartTetheringCallback { + ctor public TetheringManager.StartTetheringCallback(); + method public void onTetheringFailed(int); + method public void onTetheringStarted(); + } + + 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 static class TetheringManager.TetheringRequest { + } + + public static class TetheringManager.TetheringRequest.Builder { + ctor public TetheringManager.TetheringRequest.Builder(int); + method @NonNull public android.net.TetheringManager.TetheringRequest build(); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress); } public class TrafficStats { @@ -7365,12 +7558,12 @@ package android.net.wifi { 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 @@ -7408,6 +7601,7 @@ package android.net.wifi { 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); @@ -7438,11 +7632,8 @@ package android.net.wifi { 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[]); @@ -7453,6 +7644,7 @@ package android.net.wifi { method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener); method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int); method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean); + method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMeteredOverridePasspoint(@NonNull String, int); method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public boolean setSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration); method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration); method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public boolean setWifiConnectedNetworkScorer(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.WifiConnectedNetworkScorer); @@ -7818,6 +8010,7 @@ package android.net.wifi.hotspot2 { } public final class PasspointConfiguration implements android.os.Parcelable { + method public int getMeteredOverride(); method public boolean isAutoJoinEnabled(); method public boolean isMacRandomizationEnabled(); } @@ -8178,22 +8371,22 @@ package android.os { 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 @@ -8603,6 +8796,26 @@ package android.os { field public static final int TUPLE_VALUE_TYPE = 7; // 0x7 } + public class StatsFrameworkInitializer { + method public static void registerServiceWrappers(); + method public static void setStatsServiceManager(@NonNull android.os.StatsServiceManager); + } + + public class StatsServiceManager { + method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsCompanionServiceRegisterer(); + method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsManagerServiceRegisterer(); + method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsdServiceRegisterer(); + } + + public static class StatsServiceManager.ServiceNotFoundException extends java.lang.Exception { + ctor public StatsServiceManager.ServiceNotFoundException(@NonNull String); + } + + public static final class StatsServiceManager.ServiceRegisterer { + method @Nullable public android.os.IBinder get(); + method @Nullable public android.os.IBinder getOrThrow() throws android.os.StatsServiceManager.ServiceNotFoundException; + } + public class SystemConfigManager { method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps(); method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps(); @@ -8637,16 +8850,13 @@ package android.os { 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(); } @@ -8867,12 +9077,12 @@ package android.os.connectivity { 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(); @@ -9571,6 +9781,7 @@ package android.provider { 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"; @@ -9983,6 +10194,16 @@ package android.service.dataloader { 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; } } @@ -10051,6 +10272,7 @@ package android.service.euicc { 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); @@ -10411,6 +10633,14 @@ package android.service.trust { } +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 { @@ -11399,7 +11629,7 @@ package android.telephony { } 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(); @@ -11971,7 +12201,6 @@ package android.telephony { 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(); @@ -12139,6 +12368,7 @@ package android.telephony { 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); @@ -12155,6 +12385,9 @@ package android.telephony { 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); @@ -12163,6 +12396,7 @@ package android.telephony { 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); @@ -12468,6 +12702,11 @@ package android.telephony.euicc { 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"; @@ -12633,6 +12872,7 @@ package android.telephony.ims { 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"; @@ -13161,6 +13401,29 @@ package android.telephony.ims { 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 { @@ -13422,6 +13685,7 @@ package android.telephony.ims.stub { 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 cf1a34a5934b..2f1889cea4eb 100644 --- a/api/system-lint-baseline.txt +++ b/api/system-lint-baseline.txt @@ -8,6 +8,15 @@ ActionValue: android.location.Location#EXTRA_NO_GPS_LOCATION: 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 dc6b5154e62d..259d401c2ada 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -10,6 +10,7 @@ package android { 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 @@ package android { 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.bluetooth { 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); } } @@ -758,7 +759,6 @@ package android.content { 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(); @@ -779,7 +779,6 @@ package android.content { } public class ContextWrapper extends android.content.Context { - method public android.view.Display getDisplay(); method public int getDisplayId(); } @@ -1132,6 +1131,49 @@ package android.hardware.display { } +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 { @@ -1145,6 +1187,9 @@ package android.location { 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); @@ -1156,6 +1201,9 @@ package android.location { 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); } @@ -1170,6 +1218,10 @@ package android.location { 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); @@ -1189,6 +1241,10 @@ package android.location { 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); @@ -1399,6 +1455,7 @@ package android.media.audiopolicy { 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 { @@ -1419,9 +1476,11 @@ package android.media.audiopolicy { 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 @@ -1562,9 +1621,12 @@ package android.net { 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(); @@ -1664,6 +1726,80 @@ package android.net { method public void teardownTestNetwork(@NonNull android.net.Network); } + public class TetheringManager { + method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback); + method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener); + method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); + method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); + method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering(); + method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int); + method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.ACCESS_NETWORK_STATE}) 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.StartTetheringCallback { + ctor public TetheringManager.StartTetheringCallback(); + method public void onTetheringFailed(int); + method public void onTetheringStarted(); + } + + 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 static class TetheringManager.TetheringRequest { + } + + public static class TetheringManager.TetheringRequest.Builder { + ctor public TetheringManager.TetheringRequest.Builder(int); + method @NonNull public android.net.TetheringManager.TetheringRequest build(); + method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); + method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean); + method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress); + } + public class TrafficStats { method public static long getLoopbackRxBytes(); method public static long getLoopbackRxPackets(); @@ -3383,6 +3519,7 @@ package android.telephony { 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); @@ -3399,6 +3536,8 @@ package android.telephony { 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); @@ -3522,6 +3661,7 @@ package android.telephony.ims { 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"; @@ -3999,6 +4139,29 @@ package android.telephony.ims { 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 { @@ -4260,6 +4423,7 @@ package android.telephony.ims.stub { 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); @@ -4547,22 +4711,6 @@ package android.view { 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(); @@ -4607,7 +4755,7 @@ package android.view { 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 603f7a259462..a9c18363d8b0 100644 --- a/api/test-lint-baseline.txt +++ b/api/test-lint-baseline.txt @@ -7,6 +7,16 @@ AcronymName: android.app.NotificationChannel#setImportanceLockedByOEM(boolean): 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#setElapsedRealtimeUncertaintyNanos 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#setBasebandCn0DbHz(double): 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 d05ac189c834..d9b3a6c05c2a 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -1724,6 +1724,7 @@ message WatchdogRollbackOccurred { 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/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index b82a67556fc0..48f0087f6b30 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -111,6 +111,8 @@ import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.StatsFrameworkInitializer; +import android.os.StatsServiceManager; import android.os.StrictMode; import android.os.SystemClock; import android.os.SystemProperties; @@ -7326,6 +7328,15 @@ public final class ActivityThread extends ClientTransactionHandler { } } + 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 @@ -7514,6 +7525,7 @@ public final class ActivityThread extends ClientTransactionHandler { */ public static void initializeMainlineModules() { TelephonyFrameworkInitializer.setTelephonyServiceManager(new TelephonyServiceManager()); + StatsFrameworkInitializer.setStatsServiceManager(new StatsServiceManager()); } private void purgePendingResources() { diff --git a/core/java/android/app/AppGlobals.java b/core/java/android/app/AppGlobals.java index 81e1565ee86c..f66bf0d89c37 100644 --- a/core/java/android/app/AppGlobals.java +++ b/core/java/android/app/AppGlobals.java @@ -75,4 +75,20 @@ public class AppGlobals { 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 bc7e1e591021..46f86690a753 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1159,6 +1159,7 @@ public class AppOpsManager { @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/ContextImpl.java b/core/java/android/app/ContextImpl.java index cd84310356b1..b7555ee1c04e 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -19,7 +19,6 @@ package android.app; 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 @@ class ContextImpl extends Context { @UnsupportedAppUsage private @Nullable ClassLoader mClassLoader; - private final @Nullable IBinder mActivityToken; + private final @Nullable IBinder mToken; private final @NonNull UserHandle mUser; @@ -219,7 +218,7 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { private final Object mSync = new Object(); + private boolean mIsSystemOrSystemUiContext; + private boolean mIsUiContext; + @GuardedBy("mSync") private File mDatabasesDir; @GuardedBy("mSync") @@ -1883,6 +1885,9 @@ class ContextImpl extends Context { @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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { // 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { } 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 @@ class ContextImpl extends Context { } 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { return (mFlags & Context.CONTEXT_IGNORE_SECURITY) != 0; } - @UnsupportedAppUsage - @TestApi @Override public Display getDisplay() { if (mDisplay == null) { @@ -2408,7 +2437,8 @@ class ContextImpl extends Context { @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 @@ class ContextImpl extends Context { context.setResources(packageInfo.getResources()); context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(), context.mResourcesManager.getDisplayMetrics()); + context.mIsSystemOrSystemUiContext = true; return context; } @@ -2535,6 +2566,7 @@ class ContextImpl extends Context { context.setResources(createResources(null, packageInfo, null, displayId, null, packageInfo.getCompatibilityInfo())); context.updateDisplay(displayId); + context.mIsSystemOrSystemUiContext = true; return context; } @@ -2584,6 +2616,7 @@ class ContextImpl extends Context { 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 @@ class ContextImpl extends Context { } mMainThread = mainThread; - mActivityToken = activityToken; + mToken = activityToken; mFlags = flags; if (user == null) { @@ -2649,6 +2682,7 @@ class ContextImpl extends Context { opPackageName = container.mOpPackageName; setResources(container.mResources); mDisplay = container.mDisplay; + mIsSystemOrSystemUiContext = container.mIsSystemOrSystemUiContext; } else { mBasePackageName = packageInfo.mPackageName; ApplicationInfo ainfo = packageInfo.getApplicationInfo(); @@ -2710,7 +2744,7 @@ class ContextImpl extends Context { @Override @UnsupportedAppUsage public IBinder getActivityToken() { - return mActivityToken; + return mToken; } private void checkMode(int mode) { diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index c1e535643ddf..dcd179f8694d 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -89,6 +89,7 @@ import android.hardware.hdmi.IHdmiControlService; 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.session.MediaSessionManager; 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; @@ -148,6 +150,7 @@ import android.os.RecoverySystem; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; +import android.os.StatsFrameworkInitializer; import android.os.SystemConfigManager; import android.os.SystemUpdateManager; import android.os.SystemVibrator; @@ -376,6 +379,18 @@ public final class SystemServiceRegistry { 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, @@ -587,13 +602,6 @@ public final class SystemServiceRegistry { return SensorPrivacyManager.getInstance(ctx); }}); - registerService(Context.STATS_MANAGER, StatsManager.class, - new CachedServiceFetcher<StatsManager>() { - @Override - public StatsManager createService(ContextImpl ctx) { - return new StatsManager(ctx.getOuterContext()); - }}); - registerService(Context.STATUS_BAR_SERVICE, StatusBarManager.class, new CachedServiceFetcher<StatusBarManager>() { @Override @@ -1262,6 +1270,13 @@ public final class SystemServiceRegistry { 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 @@ -1306,6 +1321,7 @@ public final class SystemServiceRegistry { TelephonyFrameworkInitializer.registerServiceWrappers(); AppSearchManagerFrameworkInitializer.initialize(); WifiFrameworkInitializer.registerServiceWrappers(); + StatsFrameworkInitializer.registerServiceWrappers(); } finally { // If any of the above code throws, we're in a pretty bad shape and the process // will likely crash, but we'll reset it just in case there's an exception handler... diff --git a/core/java/android/app/WindowContext.java b/core/java/android/app/WindowContext.java new file mode 100644 index 000000000000..22cc14bd5ed6 --- /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 1bf6c99233e5..fa9dd274488f 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2391,6 +2391,28 @@ public class DevicePolicyManager { "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 @@ public class DevicePolicyManager { = "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 @@ public class DevicePolicyManager { * 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 @@ public class DevicePolicyManager { * @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 @@ public class DevicePolicyManager { * 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 @@ public class DevicePolicyManager { } /** - * 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 @@ public class DevicePolicyManager { * 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 @@ public class DevicePolicyManager { * {@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 @@ public class DevicePolicyManager { } /** - * 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 @@ public class DevicePolicyManager { } 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 e7667c0a1b4a..3d6bf9db5535 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -233,8 +233,8 @@ interface IDevicePolicyManager { 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 @@ interface IDevicePolicyManager { 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/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index 3107c6302775..d4b5b1aab532 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -269,7 +269,7 @@ public final class CompanionDeviceManager { @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 @@ public final class CompanionDeviceManager { 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 2e1ff0be8577..b323f58aa827 100644 --- a/core/java/android/companion/ICompanionDeviceManager.aidl +++ b/core/java/android/companion/ICompanionDeviceManager.aidl @@ -40,5 +40,6 @@ interface ICompanionDeviceManager { 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/Context.java b/core/java/android/content/Context.java index 2943e398dd87..ebc5e0e4aa31 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3480,6 +3480,7 @@ public abstract class Context { //@hide: TIME_DETECTOR_SERVICE, //@hide: TIME_ZONE_DETECTOR_SERVICE, PERMISSION_SERVICE, + LIGHTS_SERVICE, }) @Retention(RetentionPolicy.SOURCE) public @interface ServiceName {} @@ -3984,6 +3985,16 @@ public abstract class Context { /** * 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 abstract class Context { 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 class Context { 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 class Context { 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 6fe11873d327..b2b7988de896 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -977,6 +977,12 @@ public class ContextWrapper extends Context { } @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 @@ public class ContextWrapper extends Context { 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 e86bb250c033..fc20263fe00a 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 @@ interface IPackageInstallerSession { 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 b3d8eb51e324..93126b8002ad 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -230,7 +230,7 @@ interface IPackageManager { * @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 ac5fd1e41075..111ad32d1e41 100644 --- a/core/java/android/content/pm/InstallationFile.java +++ b/core/java/android/content/pm/InstallationFile.java @@ -19,6 +19,7 @@ package android.content.pm; 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 @@ import java.lang.annotation.RetentionPolicy; * * @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 f264adbbb592..b1b9454aeddd 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -365,6 +365,41 @@ public class PackageInstaller { @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 @@ public class PackageInstaller { } } + /** + * @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 @@ public class PackageInstaller { * 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 @@ public class PackageInstaller { * {@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 @@ public class PackageInstaller { /** * 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 class PackageInstaller { public boolean isCommitted; /** {@hide} */ + public long createdMillis; + + /** {@hide} */ public long updatedMillis; /** {@hide} */ @@ -2078,6 +2147,7 @@ public class PackageInstaller { mStagedSessionErrorMessage = source.readString(); isCommitted = source.readBoolean(); rollbackDataPolicy = source.readInt(); + createdMillis = source.readLong(); } /** @@ -2520,6 +2590,13 @@ public class PackageInstaller { } /** + * 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 @@ public class PackageInstaller { 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 fe5e67234fc4..9f8d32b96b6d 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -369,7 +369,7 @@ public abstract class PackageManager { * 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 @@ public abstract class PackageManager { /** * 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 abstract class PackageManager { 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 @@ public abstract class PackageManager { * 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 @@ public abstract class PackageManager { * 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 @@ public abstract class PackageManager { * 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 @@ public abstract class PackageManager { * 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 @@ public abstract class PackageManager { * 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 @@ public abstract class PackageManager { * 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 @@ public abstract class PackageManager { * 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 @@ public abstract class PackageManager { * 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 @@ -4544,7 +4570,7 @@ public abstract class PackageManager { /** * 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. @@ -4555,7 +4581,7 @@ public abstract class PackageManager { * 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); @@ -4564,7 +4590,7 @@ public abstract class PackageManager { * 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. @@ -4577,7 +4603,7 @@ public abstract class PackageManager { * 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 000000000000..c77a267958f5 --- /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 990c8359e698..fbe5a48ad61e 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.content.pm.parsing.ComponentParseUtils.ParsedService; 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 @@ public interface AndroidPackage extends Parcelable { @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 9b069acd1b06..3018230fac27 100644 --- a/core/java/android/content/pm/parsing/ApkParseUtils.java +++ b/core/java/android/content/pm/parsing/ApkParseUtils.java @@ -2426,6 +2426,21 @@ public class ApkParseUtils { 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 3846202aa04d..9a0a6d54da50 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.Parcel; 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 class ComponentParseUtils { }; } + 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 @@ public class ComponentParseUtils { 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 8677fced18fa..9baf3258a230 100644 --- a/core/java/android/content/pm/parsing/PackageImpl.java +++ b/core/java/android/content/pm/parsing/PackageImpl.java @@ -215,6 +215,9 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android @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 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android return usesStaticLibraries; } + @Nullable + @Override + public ArrayMap<String, ComponentParseUtils.ParsedProcess> getProcesses() { + return processes; + } + @Override public boolean isBaseHardwareAccelerated() { return baseHardwareAccelerated; @@ -948,6 +957,12 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android } @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 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android 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 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android 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 e0ba99bb4937..72df18998470 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.PackageParser; 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.ParsedActivity; 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 @@ public class PackageInfoUtils { 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 411c74991594..9ddcc0995fd4 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.ParsedPermissionGroup; 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 @@ public interface ParsingPackage extends AndroidPackage { 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 08b5b776c94e..29802cb33c38 100644 --- a/core/java/android/hardware/CameraStatus.java +++ b/core/java/android/hardware/CameraStatus.java @@ -30,6 +30,7 @@ import android.os.Parcelable; public class CameraStatus implements Parcelable { public String cameraId; public int status; + public String[] unavailablePhysicalCameras; @Override public int describeContents() { @@ -40,11 +41,13 @@ public class CameraStatus implements Parcelable { 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 9b58578e3811..55025f0411f9 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -718,6 +718,52 @@ public final class CameraManager { 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 @@ public final class CameraManager { 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 @@ public final class CameraManager { 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 final class CameraManager { 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 @@ public final class CameraManager { } 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 @@ public final class CameraManager { 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 @@ public final class CameraManager { 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 @@ public final class CameraManager { 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 @@ public final class CameraManager { 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 @@ public final class CameraManager { 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 @@ public final class CameraManager { } @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 89dac2aef68f..4dd3a30e046c 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -865,14 +865,20 @@ public abstract class CameraMetadata<TKey> { * <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 000000000000..6ea24b74a4a3 --- /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 000000000000..946e06d44cf3 --- /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 000000000000..c5cb8037d4db --- /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 000000000000..d598336ae40c --- /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 000000000000..e55aa702f15c --- /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 000000000000..1bc051b977a8 --- /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 000000000000..a36da4c7d85d --- /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 a11f2e9e8373..6d56d2d45922 100644 --- a/core/java/android/hardware/location/ContextHubInfo.java +++ b/core/java/android/hardware/location/ContextHubInfo.java @@ -21,6 +21,7 @@ import android.annotation.SystemApi; 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 @@ public class ContextHubInfo implements Parcelable { 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 d43a619a7c1f..a30fd6b51e76 100644 --- a/core/java/android/hardware/soundtrigger/ConversionUtil.java +++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java @@ -130,7 +130,7 @@ class ConversionUtil { 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 eb5d0cb539a1..ef76c620f3f3 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.pm.ResolveInfo; 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.XmlPullParser; 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 @@ public class KeyphraseEnrollmentInfo { "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 @@ public class KeyphraseEnrollmentInfo { "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 @@ public class KeyphraseEnrollmentInfo { * 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 000000000000..7a5e932bb7a0 --- /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 ed8c296e572f..15462deea158 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 @@ import java.util.Locale; * * @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 1932f46975c5..d505ae59dfaf 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.Retention; 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 class SoundTrigger { 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 @@ public class SoundTrigger { 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 @@ public class SoundTrigger { } } - /***************************************************************************** + /** * 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 class SoundTrigger { 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 class SoundTrigger { 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 @@ public class SoundTrigger { @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 @@ public class SoundTrigger { 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 @@ public class SoundTrigger { } @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 @@ public class SoundTrigger { } } + /** @hide */ @Override public int describeContents() { return 0; @@ -462,49 +504,57 @@ public class SoundTrigger { @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 @@ public class SoundTrigger { 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 @@ public class SoundTrigger { 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 @@ public class SoundTrigger { @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 @@ public class SoundTrigger { } /** - * 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/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 81a0d629380a..92047dcad09e 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -20,8 +20,8 @@ import static android.view.Display.DEFAULT_DISPLAY; 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 @@ public class InputMethodService extends AbstractInputMethodService { 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 000000000000..82ba0ca113c5 --- /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 6afdb5ef1b16..a6f9b96269e1 100644 --- a/core/java/android/net/ConnectivityDiagnosticsManager.java +++ b/core/java/android/net/ConnectivityDiagnosticsManager.java @@ -18,10 +18,18 @@ package android.net; 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 @@ import java.util.concurrent.Executor; * </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 @@ public class ConnectivityDiagnosticsManager { @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 @@ public class ConnectivityDiagnosticsManager { 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 8ba3131a83f1..ce9693d88a97 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -33,6 +33,9 @@ import android.content.Context; import android.content.Intent; import android.net.IpSecManager.UdpEncapsulationSocket; import android.net.SocketKeepalive.Callback; +import android.net.TetheringManager.StartTetheringCallback; +import android.net.TetheringManager.TetheringEventCallback; +import android.net.TetheringManager.TetheringRequest; import android.os.Binder; import android.os.Build; import android.os.Build.VERSION_CODES; @@ -58,6 +61,7 @@ import android.util.ArrayMap; 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 +79,7 @@ import java.util.ArrayList; 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 +489,35 @@ public class ConnectivityManager { * 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 +2374,12 @@ public class ConnectivityManager { * * @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 +2389,12 @@ public class ConnectivityManager { * * @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 +2410,12 @@ public class ConnectivityManager { * @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 +2424,11 @@ public class ConnectivityManager { * 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(); } @@ -2440,10 +2454,12 @@ public class ConnectivityManager { * * @param iface the interface name to tether. * @return error a {@code TETHER_ERROR} value indicating success or failure type + * @deprecated Use {@link TetheringManager#startTethering} instead * * {@hide} */ @UnsupportedAppUsage + @Deprecated public int tether(String iface) { return getTetheringManager().tether(iface); } @@ -2467,6 +2483,7 @@ public class ConnectivityManager { * {@hide} */ @UnsupportedAppUsage + @Deprecated public int untether(String iface) { return getTetheringManager().untether(iface); } @@ -2487,6 +2504,7 @@ public class ConnectivityManager { * * @return a boolean - {@code true} indicating Tethering is supported. * + * @deprecated Use {@link TetheringEventCallback#onTetheringSupported(boolean)} instead. * {@hide} */ @SystemApi @@ -2498,9 +2516,12 @@ public class ConnectivityManager { /** * Callback for use with {@link #startTethering} to find out whether tethering succeeded. + * + * @deprecated Use {@link TetheringManager.StartTetheringCallback} instead. * @hide */ @SystemApi + @Deprecated public static abstract class OnStartTetheringCallback { /** * Called when tethering has been successfully started. @@ -2517,9 +2538,12 @@ public class ConnectivityManager { * Convenient overload for * {@link #startTethering(int, boolean, OnStartTetheringCallback, Handler)} which passes a null * handler to run on the current thread's {@link Looper}. + * + * @deprecated Use {@link TetheringManager#startTethering} instead. * @hide */ @SystemApi + @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int type, boolean showProvisioningUi, final OnStartTetheringCallback callback) { @@ -2543,26 +2567,44 @@ public class ConnectivityManager { * @param callback an {@link OnStartTetheringCallback} which will be called to notify the caller * of the result of trying to tether. * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. + * + * @deprecated Use {@link TetheringManager#startTethering} instead. * @hide */ @SystemApi + @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int type, boolean showProvisioningUi, final OnStartTetheringCallback callback, Handler handler) { Preconditions.checkNotNull(callback, "OnStartTetheringCallback cannot be null."); - ResultReceiver wrappedCallback = new ResultReceiver(handler) { + final Executor executor = new Executor() { @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - if (resultCode == TETHER_ERROR_NO_ERROR) { - callback.onTetheringStarted(); + public void execute(Runnable command) { + if (handler == null) { + command.run(); } else { - callback.onTetheringFailed(); + handler.post(command); } } }; - getTetheringManager().startTethering(type, wrappedCallback, showProvisioningUi); + final StartTetheringCallback tetheringCallback = new StartTetheringCallback() { + @Override + public void onTetheringStarted() { + callback.onTetheringStarted(); + } + + @Override + public void onTetheringFailed(final int resultCode) { + callback.onTetheringFailed(); + } + }; + + final TetheringRequest request = new TetheringRequest.Builder(type) + .setSilentProvisioning(!showProvisioningUi).build(); + + getTetheringManager().startTethering(request, executor, tetheringCallback); } /** @@ -2573,9 +2615,12 @@ public class ConnectivityManager { * {@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 +2630,11 @@ public class ConnectivityManager { * Callback for use with {@link registerTetheringEventCallback} to find out tethering * upstream status. * - *@hide + * @deprecated Use {@link TetheringManager#OnTetheringEventCallback} instead. + * @hide */ @SystemApi + @Deprecated public abstract static class OnTetheringEventCallback { /** @@ -2600,6 +2647,10 @@ public class ConnectivityManager { 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 +2659,30 @@ public class ConnectivityManager { * * @param executor the executor on which callback will be invoked. * @param callback the callback to be called when tethering has change events. + * + * @deprecated Use {@link 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 +2690,21 @@ public class ConnectivityManager { * {@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 +2716,12 @@ public class ConnectivityManager { * @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 +2734,12 @@ public class ConnectivityManager { * @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 +2752,13 @@ public class ConnectivityManager { * @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(); } @@ -2697,45 +2777,114 @@ public class ConnectivityManager { * * @param enable a boolean - {@code true} to enable tethering * @return error a {@code TETHER_ERROR} value indicating success or failure type + * @deprecated Use {@link TetheringManager#startTethering} instead * * {@hide} */ @UnsupportedAppUsage + @Deprecated public int setUsbTethering(boolean enable) { 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 +2894,12 @@ public class ConnectivityManager { * @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 +2917,12 @@ public class ConnectivityManager { /** * 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 +2952,11 @@ public class ConnectivityManager { * @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 bf8b38fc7f84..8d9f0d068a57 100644 --- a/core/java/android/net/LinkAddress.java +++ b/core/java/android/net/LinkAddress.java @@ -19,6 +19,7 @@ package android.net; 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.compat.annotation.UnsupportedAppUsage; 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.Inet6Address; 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 @@ import java.net.UnknownHostException; * </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 @@ public class LinkAddress implements Parcelable { 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 @@ public class LinkAddress implements Parcelable { 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 @@ public class LinkAddress implements Parcelable { /** * 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 @@ public class LinkAddress implements Parcelable { 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 @@ public class LinkAddress implements Parcelable { @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 @@ public class LinkAddress implements Parcelable { // 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 @@ public class LinkAddress implements Parcelable { 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 @@ public class LinkAddress implements Parcelable { */ @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 @@ public class LinkAddress implements Parcelable { * 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 @@ public class LinkAddress implements Parcelable { } /** - * 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 @@ public class LinkAddress implements Parcelable { * 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 @@ public class LinkAddress implements Parcelable { dest.writeInt(prefixLength); dest.writeInt(this.flags); dest.writeInt(scope); + dest.writeLong(deprecationTime); + dest.writeLong(expirationTime); } /** @@ -392,7 +541,10 @@ public class LinkAddress implements Parcelable { 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 7cc78f7389c2..7cc569a42b0b 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -304,7 +304,10 @@ public abstract class NetworkAgent { 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 47c08a450fc4..4469d7de28fb 100644 --- a/core/java/android/net/NetworkKey.java +++ b/core/java/android/net/NetworkKey.java @@ -73,13 +73,14 @@ public class NetworkKey implements Parcelable { /** * 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/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java index c233ec0e52cf..a190c473f0a0 100644 --- a/core/java/android/net/NetworkScoreManager.java +++ b/core/java/android/net/NetworkScoreManager.java @@ -35,6 +35,7 @@ import android.util.Log; 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 @@ public class NetworkScoreManager { * * @hide */ - @SystemApi @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES) public boolean requestScores(@NonNull NetworkKey[] networks) throws SecurityException { try { @@ -396,6 +396,28 @@ public class NetworkScoreManager { } /** + * 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 @@ public class NetworkScoreManager { /** * 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 class NetworkScoreManager { public void updateScores(@NonNull List<ScoredNetwork> networks) { Binder.clearCallingIdentity(); mExecutor.execute(() -> { - mCallback.updateScores(networks); + mCallback.onScoresUpdated(networks); }); } @@ -500,7 +521,7 @@ public class NetworkScoreManager { 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 83dbc637fb65..6ae59716cfd8 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 com.android.internal.util.Preconditions; 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 0545666ca743..f2e16b46422f 100644 --- a/core/java/android/os/BatteryStatsManager.java +++ b/core/java/android/os/BatteryStatsManager.java @@ -42,7 +42,7 @@ import java.lang.annotation.RetentionPolicy; */ @SystemApi @SystemService(Context.BATTERY_STATS_SERVICE) -public class BatteryStatsManager { +public final class BatteryStatsManager { /** * Wifi states. * @@ -166,7 +166,7 @@ public class BatteryStatsManager { * @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 @@ public class BatteryStatsManager { * 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 @@ public class BatteryStatsManager { * 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 @@ public class BatteryStatsManager { * @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 @@ public class BatteryStatsManager { * @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 @@ public class BatteryStatsManager { * @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 @@ public class BatteryStatsManager { * @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 @@ public class BatteryStatsManager { * @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 @@ public class BatteryStatsManager { * @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 @@ public class BatteryStatsManager { * @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 @@ public class BatteryStatsManager { * 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 @@ public class BatteryStatsManager { * @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 @@ public class BatteryStatsManager { * @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/StatsServiceManager.java b/core/java/android/os/StatsServiceManager.java new file mode 100644 index 000000000000..d032e98da00c --- /dev/null +++ b/core/java/android/os/StatsServiceManager.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.os; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; + +/** + * Provides a way to register and obtain the system service binder objects managed by the stats + * service. + * + * <p> Only the statsd mainline module will be able to access an instance of this class. + * + * TODO(b/148225705) Change to @SystemApi(client=MODULE_LIBRARIES) when the build system is ready. + * @hide + */ +@SystemApi +public class StatsServiceManager { + /** + * @hide + */ + public StatsServiceManager() {} + + /** + * A class that exposes the methods to register and obtain each system service. + */ + public static final class ServiceRegisterer { + private final String mServiceName; + + /** + * @hide + */ + public ServiceRegisterer(String serviceName) { + mServiceName = serviceName; + } + + /** + * Get the system server binding object for StatsManagerService. + * + * <p> This blocks until the service instance is ready. + * or a timeout happens, in which case it returns null. + */ + @Nullable + public IBinder get() { + return ServiceManager.getService(mServiceName); + } + + /** + * Get the system server binding object for a service. + * + * <p>This blocks until the service instance is ready, + * or a timeout happens, in which case it throws {@link ServiceNotFoundException}. + */ + @Nullable + public IBinder getOrThrow() throws ServiceNotFoundException { + try { + return ServiceManager.getServiceOrThrow(mServiceName); + } catch (ServiceManager.ServiceNotFoundException e) { + throw new ServiceNotFoundException(mServiceName); + } + } + + /** + * Get the system server binding object for a service. If the specified service is + * not available, it returns null. + */ + @Nullable + private IBinder tryGet() { + return ServiceManager.checkService(mServiceName); + } + } + + /** + * See {@link ServiceRegisterer#getOrThrow()} + */ + public static class ServiceNotFoundException extends ServiceManager.ServiceNotFoundException { + /** + * Constructor + * + * @param name the name of the binder service that cannot be found. + */ + public ServiceNotFoundException(@NonNull String name) { + super(name); + } + } + + /** + * Returns {@link ServiceRegisterer} for the "statscompanion" service. + */ + @NonNull + public ServiceRegisterer getStatsCompanionServiceRegisterer() { + return new ServiceRegisterer("statscompanion"); + } + + /** + * Returns {@link ServiceRegisterer} for the "statsmanager" service. + */ + @NonNull + public ServiceRegisterer getStatsManagerServiceRegisterer() { + return new ServiceRegisterer("statsmanager"); + } + + /** + * Returns {@link ServiceRegisterer} for the "statsd" service. + */ + @NonNull + public ServiceRegisterer getStatsdServiceRegisterer() { + return new ServiceRegisterer("stats"); + } +} diff --git a/core/java/android/os/TelephonyServiceManager.java b/core/java/android/os/TelephonyServiceManager.java index 4f5f3d69b6f3..c93eba6523f0 100644 --- a/core/java/android/os/TelephonyServiceManager.java +++ b/core/java/android/os/TelephonyServiceManager.java @@ -119,14 +119,6 @@ public class TelephonyServiceManager { } /** - * 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 @@ public class TelephonyServiceManager { } /** - * 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 @@ public class TelephonyServiceManager { return new ServiceRegisterer("econtroller"); } + /** + * Returns {@link ServiceRegisterer} for the eUICC card controller service. + */ @NonNull public ServiceRegisterer getEuiccCardControllerServiceRegisterer() { return new ServiceRegisterer("euicc_card_controller"); @@ -212,14 +199,6 @@ public class TelephonyServiceManager { } /** - * 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 diff --git a/core/java/android/os/connectivity/WifiBatteryStats.java b/core/java/android/os/connectivity/WifiBatteryStats.java index 895d837a359e..3c30f6343405 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 @@ import java.util.Objects; */ @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 @@ public final class WifiBatteryStats implements Parcelable { 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 @@ public final class WifiBatteryStats implements Parcelable { && 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 @@ public final class WifiBatteryStats implements Parcelable { 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 @@ public final class WifiBatteryStats implements Parcelable { } /** - * 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 @@ public final class WifiBatteryStats implements Parcelable { } /** - * 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 @@ public final class WifiBatteryStats implements Parcelable { } /** - * 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 @@ public final class WifiBatteryStats implements Parcelable { * * @return Number of app scans. */ - public long getNumAppScanRequest() { - return mNumAppScanRequest; + public long getAppScanRequestCount() { + return mAppScanRequestCount; } /** @@ -288,113 +318,4 @@ public final class WifiBatteryStats implements Parcelable { 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 84ceca0c3622..091d78e88c98 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -796,6 +796,14 @@ public final class DeviceConfig { } /** + * 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 c7c3140f7365..00b2feba8bcd 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -63,6 +63,7 @@ import android.os.IBinder; 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 final class Settings { 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 @@ public final class Settings { /** @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 @@ public final class Settings { } } + /** + * 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 453728137d9a..f25cdf1b8c98 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -37,7 +37,6 @@ import android.database.sqlite.SqliteWrapper; 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 @@ public final class Telephony { } /** - * 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 final class Telephony { 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 final class Telephony { 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 final class Telephony { 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 5330cffee3db..f67af85d00e3 100644 --- a/core/java/android/security/ConfirmationPrompt.java +++ b/core/java/android/security/ConfirmationPrompt.java @@ -212,20 +212,16 @@ public class ConfirmationPrompt { 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/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java index dc0f5623e5e3..9333dbd1e1d5 100644 --- a/core/java/android/service/autofill/FillResponse.java +++ b/core/java/android/service/autofill/FillResponse.java @@ -87,6 +87,7 @@ public final class FillResponse implements Parcelable { private final @Nullable UserData mUserData; private final @Nullable int[] mCancelIds; private final boolean mSupportsInlineSuggestions; + private final @Nullable ParceledListSlice<InlinePresentation> mInlineActions; private FillResponse(@NonNull Builder builder) { mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null; @@ -105,6 +106,8 @@ public final class FillResponse implements Parcelable { mUserData = builder.mUserData; mCancelIds = builder.mCancelIds; mSupportsInlineSuggestions = builder.mSupportsInlineSuggestions; + mInlineActions = (builder.mInlineActions != null) ? new ParceledListSlice<>( + builder.mInlineActions) : null; } /** @hide */ @@ -202,6 +205,11 @@ public final class FillResponse implements Parcelable { return mSupportsInlineSuggestions; } + /** @hide */ + public @Nullable List<InlinePresentation> getInlineActions() { + return (mInlineActions != null) ? mInlineActions.getList() : null; + } + /** * Builder for {@link FillResponse} objects. You must to provide at least * one dataset or set an authentication intent with a presentation view. @@ -223,6 +231,7 @@ public final class FillResponse implements Parcelable { private UserData mUserData; private int[] mCancelIds; private boolean mSupportsInlineSuggestions; + private ArrayList<InlinePresentation> mInlineActions; /** * Triggers a custom UI before before autofilling the screen with any data set in this @@ -578,6 +587,25 @@ public final class FillResponse implements Parcelable { } /** + * Adds a new {@link InlinePresentation} to this response representing an action UI. + * + * <p> For example, the UI can be associated with an intent which can open an activity for + * the user to manage the Autofill provider settings. + * + * @return This builder. + */ + @NonNull + public Builder addInlineAction(@NonNull InlinePresentation inlineAction) { + throwIfDestroyed(); + throwIfAuthenticationCalled(); + if (mInlineActions == null) { + mInlineActions = new ArrayList<>(); + } + mInlineActions.add(inlineAction); + return this; + } + + /** * Builds a new {@link FillResponse} instance. * * @throws IllegalStateException if any of the following conditions occur: @@ -688,6 +716,10 @@ public final class FillResponse implements Parcelable { if (mCancelIds != null) { builder.append(", mCancelIds=").append(mCancelIds.length); } + builder.append(", mSupportInlinePresentations=").append(mSupportsInlineSuggestions); + if (mInlineActions != null) { + builder.append(", mInlineActions=" + mInlineActions.getList()); + } return builder.append("]").toString(); } @@ -717,6 +749,7 @@ public final class FillResponse implements Parcelable { parcel.writeInt(mFlags); parcel.writeIntArray(mCancelIds); parcel.writeInt(mRequestId); + parcel.writeParcelable(mInlineActions, flags); } public static final @android.annotation.NonNull Parcelable.Creator<FillResponse> CREATOR = @@ -771,6 +804,15 @@ public final class FillResponse implements Parcelable { final int[] cancelIds = parcel.createIntArray(); builder.setPresentationCancelIds(cancelIds); + final ParceledListSlice<InlinePresentation> inlineActionsSlice = parcel.readParcelable( + null); + final List<InlinePresentation> inlineActions = + (inlineActionsSlice != null) ? inlineActionsSlice.getList() : null; + final int inlineActionsCount = (inlineActions != null) ? inlineActions.size() : 0; + for (int i = 0; i < inlineActionsCount; i++) { + builder.addInlineAction(inlineActions.get(i)); + } + final FillResponse response = builder.build(); response.setRequestId(parcel.readInt()); diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java index 1568fb3af4c0..fb8406e99fa0 100644 --- a/core/java/android/service/autofill/InlinePresentation.java +++ b/core/java/android/service/autofill/InlinePresentation.java @@ -34,10 +34,22 @@ import com.android.internal.util.DataClass; genEqualsHashCode = true) public final class InlinePresentation implements Parcelable { + + /** + * Represents the UI content and the action for the inline suggestion. + */ private final @NonNull Slice mSlice; + /** + * Specifies the UI specification for the inline suggestion. + */ private final @NonNull InlinePresentationSpec mInlinePresentationSpec; + /** + * Indicates whether the UI should be pinned, hence non-scrollable, in the host. + */ + private final boolean mPinned; + // Code below generated by codegen v1.0.14. @@ -53,30 +65,56 @@ public final class InlinePresentation implements Parcelable { //@formatter:off + /** + * Creates a new InlinePresentation. + * + * @param slice + * Represents the UI content and the action for the inline suggestion. + * @param inlinePresentationSpec + * Specifies the UI specification for the inline suggestion. + * @param pinned + * Indicates whether the UI should be pinned, hence non-scrollable, in the host. + */ @DataClass.Generated.Member public InlinePresentation( @NonNull Slice slice, - @NonNull InlinePresentationSpec inlinePresentationSpec) { + @NonNull InlinePresentationSpec inlinePresentationSpec, + boolean pinned) { this.mSlice = slice; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mSlice); this.mInlinePresentationSpec = inlinePresentationSpec; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mInlinePresentationSpec); + this.mPinned = pinned; // onConstructed(); // You can define this method to get a callback } + /** + * Represents the UI content and the action for the inline suggestion. + */ @DataClass.Generated.Member public @NonNull Slice getSlice() { return mSlice; } + /** + * Specifies the UI specification for the inline suggestion. + */ @DataClass.Generated.Member public @NonNull InlinePresentationSpec getInlinePresentationSpec() { return mInlinePresentationSpec; } + /** + * Indicates whether the UI should be pinned, hence non-scrollable, in the host. + */ + @DataClass.Generated.Member + public boolean isPinned() { + return mPinned; + } + @Override @DataClass.Generated.Member public String toString() { @@ -85,7 +123,8 @@ public final class InlinePresentation implements Parcelable { return "InlinePresentation { " + "slice = " + mSlice + ", " + - "inlinePresentationSpec = " + mInlinePresentationSpec + + "inlinePresentationSpec = " + mInlinePresentationSpec + ", " + + "pinned = " + mPinned + " }"; } @@ -103,7 +142,8 @@ public final class InlinePresentation implements Parcelable { //noinspection PointlessBooleanExpression return true && java.util.Objects.equals(mSlice, that.mSlice) - && java.util.Objects.equals(mInlinePresentationSpec, that.mInlinePresentationSpec); + && java.util.Objects.equals(mInlinePresentationSpec, that.mInlinePresentationSpec) + && mPinned == that.mPinned; } @Override @@ -115,6 +155,7 @@ public final class InlinePresentation implements Parcelable { int _hash = 1; _hash = 31 * _hash + java.util.Objects.hashCode(mSlice); _hash = 31 * _hash + java.util.Objects.hashCode(mInlinePresentationSpec); + _hash = 31 * _hash + Boolean.hashCode(mPinned); return _hash; } @@ -124,6 +165,9 @@ public final class InlinePresentation implements Parcelable { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } + byte flg = 0; + if (mPinned) flg |= 0x4; + dest.writeByte(flg); dest.writeTypedObject(mSlice, flags); dest.writeTypedObject(mInlinePresentationSpec, flags); } @@ -139,6 +183,8 @@ public final class InlinePresentation implements Parcelable { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } + byte flg = in.readByte(); + boolean pinned = (flg & 0x4) != 0; Slice slice = (Slice) in.readTypedObject(Slice.CREATOR); InlinePresentationSpec inlinePresentationSpec = (InlinePresentationSpec) in.readTypedObject(InlinePresentationSpec.CREATOR); @@ -148,6 +194,7 @@ public final class InlinePresentation implements Parcelable { this.mInlinePresentationSpec = inlinePresentationSpec; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mInlinePresentationSpec); + this.mPinned = pinned; // onConstructed(); // You can define this method to get a callback } @@ -167,10 +214,10 @@ public final class InlinePresentation implements Parcelable { }; @DataClass.Generated( - time = 1578081082387L, + time = 1579726472535L, codegenVersion = "1.0.14", sourceFile = "frameworks/base/core/java/android/service/autofill/InlinePresentation.java", - inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mInlinePresentationSpec\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)") + inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final boolean mPinned\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java index 75f252e23f79..c21577842199 100644 --- a/core/java/android/service/dataloader/DataLoaderService.java +++ b/core/java/android/service/dataloader/DataLoaderService.java @@ -58,6 +58,7 @@ public abstract class DataLoaderService extends Service { * Managed DataLoader interface. Each instance corresponds to a single installation session. * @hide */ + @SystemApi public interface DataLoader { /** * A virtual constructor. @@ -78,8 +79,8 @@ public abstract class DataLoaderService extends Service { * @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 @@ public abstract class DataLoaderService extends Service { * @return An instance of a DataLoader. * @hide */ + @SystemApi public @Nullable DataLoader onCreateDataLoader() { return null; } @@ -188,6 +190,7 @@ public abstract class DataLoaderService extends Service { * * @hide */ + @SystemApi public static final class FileSystemConnector { /** * Create a wrapper for a native instance. @@ -211,8 +214,8 @@ public abstract class DataLoaderService extends Service { * @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/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index 0f339988ba3e..1966f17aaf35 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; 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 @@ public class AlwaysOnHotwordDetector { /** * 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 @@ public class AlwaysOnHotwordDetector { */ 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 @@ public class AlwaysOnHotwordDetector { * 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 @@ public class AlwaysOnHotwordDetector { mText = text; mLocale = locale; mKeyphraseEnrollmentInfo = keyphraseEnrollmentInfo; - mKeyphraseMetadata = mKeyphraseEnrollmentInfo.getKeyphraseMetadata(text, locale); mExternalCallback = callback; mHandler = new MyHandler(); mInternalCallback = new SoundTriggerListener(mHandler); @@ -484,8 +460,7 @@ public class AlwaysOnHotwordDetector { } // 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 class AlwaysOnHotwordDetector { 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 class AlwaysOnHotwordDetector { 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 class AlwaysOnHotwordDetector { 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 @@ public class AlwaysOnHotwordDetector { 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 @@ public class AlwaysOnHotwordDetector { // 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 @@ public class AlwaysOnHotwordDetector { @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 @@ public class AlwaysOnHotwordDetector { } 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 36e057f4a97d..fc99836b82fd 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 @@ public class VoiceInteractionService extends Service { } /** + * 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 52b72949ee04..36f2c6267622 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -1137,7 +1137,7 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall 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/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 91691436a87a..dd78c78654c3 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -869,7 +869,7 @@ public abstract class WallpaperService extends Service { // 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; diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index 57375919e6cd..6787c46c046e 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -36,6 +36,7 @@ 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; @@ -198,6 +199,25 @@ public class TelephonyRegistryManager { } /** + * 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. @@ -258,6 +278,32 @@ public class TelephonyRegistryManager { } /** + * 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. @@ -394,6 +440,36 @@ public class TelephonyRegistryManager { } /** + * 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. diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index eb4af1c2a979..06fccaf8ea81 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -44,6 +44,9 @@ public class FeatureFlagUtils { 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 @@ public class FeatureFlagUtils { 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 9f0f24617f5d..f5025f7a9e99 100644 --- a/core/java/android/util/SparseSetArray.java +++ b/core/java/android/util/SparseSetArray.java @@ -37,10 +37,10 @@ public class SparseSetArray<T> { 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 904c510a5b01..0304328f734a 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -423,10 +423,14 @@ public final class Display { /** * 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 993bdc4d6543..d9c502e14e68 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -32,6 +32,7 @@ import android.graphics.Region; 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 @@ interface IWindowManager // 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 @@ interface IWindowManager * 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 271566acb74e..8d58ee83cd67 100644 --- a/core/java/android/view/ImeFocusController.java +++ b/core/java/android/view/ImeFocusController.java @@ -45,12 +45,25 @@ public final class ImeFocusController { 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 @@ public final class ImeFocusController { } 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 @@ public final class ImeFocusController { 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 @@ public final class ImeFocusController { // 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 @@ public final class ImeFocusController { @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 f5afd106a4a7..405eccd56e3c 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -40,6 +40,7 @@ import android.view.WindowInsets.Type.InsetsType; 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 class InsetsAnimationControlImpl implements WindowInsetsAnimationControll 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 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll 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 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll 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 411e910e1af1..c6e383539a82 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -28,6 +28,7 @@ import android.animation.ObjectAnimator; 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 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation controller.setInsetsAndAlpha( value, 1f /* alpha */, (((DefaultAnimationControlListener) ((InsetsAnimationControlImpl) controller).getListener()) - .getRawProgress())); + .getRawFraction())); } } @@ -204,9 +205,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation 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 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @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 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } 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 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation // 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 7707ad163b85..a6b7c33de3d9 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 @@ public class Surface implements Parcelable { 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 @@ public class Surface implements Parcelable { } /** + * 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 bcc9e41b8ab0..f7b87cce7338 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -211,6 +211,9 @@ public final class SurfaceControl implements Parcelable { 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 @@ public final class SurfaceControl implements Parcelable { } /** + * 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 4f8aecd08f6d..71cf051a4e08 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -20,15 +20,21 @@ import android.annotation.NonNull; 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 @@ public class SurfaceControlViewHost { 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 @@ public class SurfaceControlViewHost { 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 class SurfaceControlViewHost { } } - 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 class SurfaceControlViewHost { }); } - 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 0de1a4f038ff..1981bdd93eeb 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_OVERLA 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.AttributeSet; 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 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall // The token of embedded windowless view hierarchy. private IBinder mEmbeddedViewHierarchy; + SurfaceControlViewHost.SurfacePackage mSurfacePackage; public SurfaceView(Context context) { this(context, null); @@ -877,6 +880,11 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } else { mTmpTransaction.hide(mSurfaceControl); } + + if (mSurfacePackage != null) { + reparentSurfacePackage(mTmpTransaction, mSurfacePackage); + } + updateBackgroundVisibility(mTmpTransaction); if (mUseAlpha) { mTmpTransaction.setAlpha(mSurfaceControl, alpha); @@ -1471,11 +1479,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } /** - * @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 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } /** + * 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 377a7644f21b..c5f4faf2f462 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__LO 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.FloatProperty; 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.ParagraphTextSegmentIterator; 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.FrameLayout; 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 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#setOnContentApplyWindowInsetsListener} 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @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#setOnContentApplyWindowInsetsListener} 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * </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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * For use by PhoneWindow to make its own system window fitting optional. + * @see #OPTIONAL_FITS_SYSTEM_WINDOWS * @hide */ @UnsupportedAppUsage @@ -11458,6 +11533,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * window. */ final static class AttachInfo { + interface Callbacks { void playSoundEffect(int effectId); boolean performHapticFeedback(int effectId, boolean always); @@ -28854,6 +28970,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, 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 e6470a7d1e27..4f03ca152850 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -55,6 +55,7 @@ import android.util.Pools; 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 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } + /** + * @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 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } @Override + @Deprecated public void dispatchWindowSystemUiVisiblityChanged(int visible) { super.dispatchWindowSystemUiVisiblityChanged(visible); @@ -1883,6 +1898,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } @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 09ebd005e7ed..f5cfbec924ac 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -122,6 +122,7 @@ import android.view.SurfaceControl.Transaction; 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 @@ public final class ViewRootImpl implements ViewParent, 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 @@ public final class ViewRootImpl implements ViewParent, // 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 @@ public final class ViewRootImpl implements ViewParent, 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 @@ public final class ViewRootImpl implements ViewParent, && 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 a1894f30d6f6..0ef4e338f81c 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -33,6 +33,7 @@ import android.content.pm.ActivityInfo; 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.os.RemoteException; 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 @@ public abstract class Window { 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 @@ public abstract class Window { } /** - * A shortcut for {@link WindowManager.LayoutParams#setFitWindowInsetsTypes(int)} - * @hide pending unhide - */ - public void setFitWindowInsetsTypes(@InsetsType int types) { - final WindowManager.LayoutParams attrs = getAttributes(); - attrs.setFitWindowInsetsTypes(types); - dispatchWindowAttributesChanged(attrs); - } - - /** - * A shortcut for {@link WindowManager.LayoutParams#setFitWindowInsetsSides(int)} - * @hide pending unhide - */ - 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 + * 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 @InsetsSide int getFitWindowInsetsSides() { - return getAttributes().getFitWindowInsetsSides(); + public void setOnContentApplyWindowInsetsListener( + @Nullable OnContentApplyWindowInsetsListener contentOnApplyWindowInsetsListener) { } /** - * A shortcut for {@link WindowManager.LayoutParams#getFitIgnoreVisibility()} - * @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 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 0a2a45b44523..a6c311e1daa5 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -301,11 +301,14 @@ public final class WindowInsets { * </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 @@ public final class WindowInsets { } /** - * 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 @@ public final class WindowInsets { * </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 @@ public final class WindowInsets { * </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 @@ public final class WindowInsets { * </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 @@ public final class WindowInsets { * </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 @@ public final class WindowInsets { * </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 @@ public final class WindowInsets { * 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 @@ public final class WindowInsets { * 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 @@ public final class WindowInsets { * 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 @@ public final class WindowInsets { * 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 @@ public final class WindowInsets { * 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 @@ public final class WindowInsets { * 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 @@ public final class WindowInsets { * 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 @@ public final class WindowInsets { * 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 @@ public final class WindowInsets { * * <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 @@ public final class WindowInsets { * * @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 @@ public final class WindowInsets { * * @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 @@ public final class WindowInsets { * * @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 @@ public final class WindowInsets { * * @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 @@ public final class WindowInsets { } /** - * 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 @@ public final class WindowInsets { * 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 @@ public final class WindowInsets { * * @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 @@ public final class WindowInsets { } /** - * @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 @@ public final class WindowInsets { } /** - * @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 @@ public final class WindowInsets { /** * 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 53d493985b32..1e04d02fcb80 100644 --- a/core/java/android/view/WindowInsetsAnimationCallback.java +++ b/core/java/android/view/WindowInsetsAnimationCallback.java @@ -88,7 +88,7 @@ public interface WindowInsetsAnimationCallback { * <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 @@ public interface WindowInsetsAnimationCallback { 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 @@ public interface WindowInsetsAnimationCallback { /** * 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 @@ public interface WindowInsetsAnimationCallback { /** * 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 @@ public interface WindowInsetsAnimationCallback { 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 f292ca4facbf..02323cfb4f00 100644 --- a/core/java/android/view/WindowInsetsController.java +++ b/core/java/android/view/WindowInsetsController.java @@ -20,8 +20,11 @@ import static android.view.WindowInsets.Type.ime; 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 @@ public interface WindowInsetsController { * 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 @@ public interface WindowInsetsController { * * <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 @@ public interface WindowInsetsController { * 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 @@ public interface WindowInsetsController { * * @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 @@ public interface WindowInsetsController { * * @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 @@ public interface WindowInsetsController { * 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 @@ public interface WindowInsetsController { * 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 @@ public interface WindowInsetsController { * 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 cd9dee4f7329..55c298e2a92b 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -421,7 +421,9 @@ public interface WindowManager extends ViewManager { * </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 interface WindowManager extends ViewManager { 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 @@ -452,13 +497,48 @@ public interface WindowManager extends ViewManager { * Message for taking fullscreen screenshot * @hide */ - final int TAKE_SCREENSHOT_FULLSCREEN = 1; + int TAKE_SCREENSHOT_FULLSCREEN = 1; /** * Message for taking screenshot of selected region. * @hide */ - final int TAKE_SCREENSHOT_SELECTED_REGION = 2; + int TAKE_SCREENSHOT_SELECTED_REGION = 2; + + /** + * Message for handling a screenshot flow with an image provided by the caller. + * @hide + */ + int TAKE_SCREENSHOT_PROVIDED_IMAGE = 3; + + /** + * Parcel key for the screen shot bitmap sent with messages of type + * {@link #TAKE_SCREENSHOT_PROVIDED_IMAGE}, type {@link android.graphics.Bitmap} + * @hide + */ + String PARCEL_KEY_SCREENSHOT_BITMAP = "screenshot_screen_bitmap"; + + /** + * Parcel key for the screen bounds of the image sent with messages of type + * [@link {@link #TAKE_SCREENSHOT_PROVIDED_IMAGE}], type {@link Rect} in screen coordinates. + * @hide + */ + String PARCEL_KEY_SCREENSHOT_BOUNDS = "screenshot_screen_bounds"; + + /** + * Parcel key for the task id of the task that the screen shot was taken of, sent with messages + * of type [@link {@link #TAKE_SCREENSHOT_PROVIDED_IMAGE}], type int. + * @hide + */ + String PARCEL_KEY_SCREENSHOT_TASK_ID = "screenshot_task_id"; + + /** + * Parcel key for the visible insets of the image sent with messages of type + * [@link {@link #TAKE_SCREENSHOT_PROVIDED_IMAGE}], type {@link android.graphics.Insets} in + * screen coordinates. + * @hide + */ + String PARCEL_KEY_SCREENSHOT_INSETS = "screenshot_insets"; /** * @hide @@ -1243,11 +1323,9 @@ public interface WindowManager extends ViewManager { * 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 +1355,21 @@ public interface WindowManager extends ViewManager { * {@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 +1401,18 @@ public interface WindowManager extends ViewManager { * 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 +1598,11 @@ public interface WindowManager extends ViewManager { * * <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 +1625,11 @@ public interface WindowManager extends ViewManager { * <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 +1659,11 @@ public interface WindowManager extends ViewManager { * 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 +1983,6 @@ public interface WindowManager extends ViewManager { 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 +2092,7 @@ public interface WindowManager extends ViewManager { @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 +2192,11 @@ public interface WindowManager extends ViewManager { * 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 +2491,11 @@ public interface WindowManager extends ViewManager { * * @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 +2821,7 @@ public interface WindowManager extends ViewManager { 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 +2841,18 @@ public interface WindowManager extends ViewManager { 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 +2861,9 @@ public interface WindowManager extends ViewManager { * * @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 +2871,32 @@ public interface WindowManager extends ViewManager { * 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 +3062,9 @@ public interface WindowManager extends ViewManager { 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 +3123,9 @@ public interface WindowManager extends ViewManager { 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 +3372,18 @@ public interface WindowManager extends ViewManager { 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 +3545,17 @@ public interface WindowManager extends ViewManager { 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 +3596,9 @@ public interface WindowManager extends ViewManager { 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 cdeeaa438acb..4365d1f5194a 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 @@ public final class WindowManagerImpl implements WindowManager { 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 final class WindowManagerImpl implements WindowManager { 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 @@ public final class WindowManagerImpl implements WindowManager { } 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 000000000000..8caf5b7fc725 --- /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 a26243c7cad5..c80a1ae55228 100644 --- a/core/java/android/view/inputmethod/EditorInfo.java +++ b/core/java/android/view/inputmethod/EditorInfo.java @@ -18,6 +18,8 @@ package android.view.inputmethod; 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.os.UserHandle; 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 @@ public class EditorInfo implements InputType, Parcelable { @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 @@ public class EditorInfo implements InputType, Parcelable { 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 @@ public class EditorInfo implements InputType, Parcelable { 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 @@ public class EditorInfo implements InputType, Parcelable { 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/InlineSuggestionInfo.java b/core/java/android/view/inputmethod/InlineSuggestionInfo.java index 195b63a8fc8e..703b64f8224f 100644 --- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java +++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java @@ -18,6 +18,7 @@ package android.view.inputmethod; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.TestApi; import android.os.Parcelable; import android.view.inline.InlinePresentationSpec; @@ -45,6 +46,17 @@ public final class InlineSuggestionInfo implements Parcelable { */ public static final @Source String SOURCE_PLATFORM = "android:platform"; + /** + * UI type: the UI contains an Autofill suggestion that will autofill the fields when tapped. + */ + public static final @Type String TYPE_SUGGESTION = "android:autofill:suggestion"; + + /** + * UI type: the UI contains an widget that will launch an intent when tapped. + */ + @SuppressLint({"IntentName"}) + public static final @Type String TYPE_ACTION = "android:autofill:action"; + /** The presentation spec to which the inflated suggestion view abides. */ private final @NonNull InlinePresentationSpec mPresentationSpec; @@ -54,6 +66,9 @@ public final class InlineSuggestionInfo implements Parcelable { /** Hints for the type of data being suggested. */ private final @Nullable String[] mAutofillHints; + /** The type of the UI. */ + private final @NonNull @Type String mType; + /** * Creates a new {@link InlineSuggestionInfo}, for testing purpose. * @@ -65,7 +80,8 @@ public final class InlineSuggestionInfo implements Parcelable { @NonNull InlinePresentationSpec presentationSpec, @NonNull @Source String source, @Nullable String[] autofillHints) { - return new InlineSuggestionInfo(presentationSpec, source, autofillHints); + // TODO(b/147394280): Add CTS test for the type field. + return new InlineSuggestionInfo(presentationSpec, source, autofillHints, TYPE_SUGGESTION); } @@ -92,6 +108,15 @@ public final class InlineSuggestionInfo implements Parcelable { @DataClass.Generated.Member public @interface Source {} + /** @hide */ + @android.annotation.StringDef(prefix = "TYPE_", value = { + TYPE_SUGGESTION, + TYPE_ACTION + }) + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) + @DataClass.Generated.Member + public @interface Type {} + /** * Creates a new InlineSuggestionInfo. * @@ -101,13 +126,16 @@ public final class InlineSuggestionInfo implements Parcelable { * The source from which the suggestion is provided. * @param autofillHints * Hints for the type of data being suggested. + * @param type + * The type of the UI. * @hide */ @DataClass.Generated.Member public InlineSuggestionInfo( @NonNull InlinePresentationSpec presentationSpec, @NonNull @Source String source, - @Nullable String[] autofillHints) { + @Nullable String[] autofillHints, + @NonNull @Type String type) { this.mPresentationSpec = presentationSpec; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mPresentationSpec); @@ -124,6 +152,18 @@ public final class InlineSuggestionInfo implements Parcelable { com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mSource); this.mAutofillHints = autofillHints; + this.mType = type; + + if (!(java.util.Objects.equals(mType, TYPE_SUGGESTION)) + && !(java.util.Objects.equals(mType, TYPE_ACTION))) { + throw new java.lang.IllegalArgumentException( + "type was " + mType + " but must be one of: " + + "TYPE_SUGGESTION(" + TYPE_SUGGESTION + "), " + + "TYPE_ACTION(" + TYPE_ACTION + ")"); + } + + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mType); // onConstructed(); // You can define this method to get a callback } @@ -152,6 +192,14 @@ public final class InlineSuggestionInfo implements Parcelable { return mAutofillHints; } + /** + * The type of the UI. + */ + @DataClass.Generated.Member + public @NonNull @Type String getType() { + return mType; + } + @Override @DataClass.Generated.Member public String toString() { @@ -161,7 +209,8 @@ public final class InlineSuggestionInfo implements Parcelable { return "InlineSuggestionInfo { " + "presentationSpec = " + mPresentationSpec + ", " + "source = " + mSource + ", " + - "autofillHints = " + java.util.Arrays.toString(mAutofillHints) + + "autofillHints = " + java.util.Arrays.toString(mAutofillHints) + ", " + + "type = " + mType + " }"; } @@ -180,7 +229,8 @@ public final class InlineSuggestionInfo implements Parcelable { return true && java.util.Objects.equals(mPresentationSpec, that.mPresentationSpec) && java.util.Objects.equals(mSource, that.mSource) - && java.util.Arrays.equals(mAutofillHints, that.mAutofillHints); + && java.util.Arrays.equals(mAutofillHints, that.mAutofillHints) + && java.util.Objects.equals(mType, that.mType); } @Override @@ -193,6 +243,7 @@ public final class InlineSuggestionInfo implements Parcelable { _hash = 31 * _hash + java.util.Objects.hashCode(mPresentationSpec); _hash = 31 * _hash + java.util.Objects.hashCode(mSource); _hash = 31 * _hash + java.util.Arrays.hashCode(mAutofillHints); + _hash = 31 * _hash + java.util.Objects.hashCode(mType); return _hash; } @@ -208,6 +259,7 @@ public final class InlineSuggestionInfo implements Parcelable { dest.writeTypedObject(mPresentationSpec, flags); dest.writeString(mSource); if (mAutofillHints != null) dest.writeStringArray(mAutofillHints); + dest.writeString(mType); } @Override @@ -225,6 +277,7 @@ public final class InlineSuggestionInfo implements Parcelable { InlinePresentationSpec presentationSpec = (InlinePresentationSpec) in.readTypedObject(InlinePresentationSpec.CREATOR); String source = in.readString(); String[] autofillHints = (flg & 0x4) == 0 ? null : in.createStringArray(); + String type = in.readString(); this.mPresentationSpec = presentationSpec; com.android.internal.util.AnnotationValidations.validate( @@ -242,6 +295,18 @@ public final class InlineSuggestionInfo implements Parcelable { com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mSource); this.mAutofillHints = autofillHints; + this.mType = type; + + if (!(java.util.Objects.equals(mType, TYPE_SUGGESTION)) + && !(java.util.Objects.equals(mType, TYPE_ACTION))) { + throw new java.lang.IllegalArgumentException( + "type was " + mType + " but must be one of: " + + "TYPE_SUGGESTION(" + TYPE_SUGGESTION + "), " + + "TYPE_ACTION(" + TYPE_ACTION + ")"); + } + + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mType); // onConstructed(); // You can define this method to get a callback } @@ -261,10 +326,10 @@ public final class InlineSuggestionInfo implements Parcelable { }; @DataClass.Generated( - time = 1578972121865L, + time = 1579806757327L, codegenVersion = "1.0.14", sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionInfo.java", - inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mPresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.view.inline.InlinePresentationSpec,java.lang.String,java.lang.String[])\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)") + inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_SUGGESTION\npublic static final @android.annotation.SuppressLint({\"IntentName\"}) @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_ACTION\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mPresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String mType\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.view.inline.InlinePresentationSpec,java.lang.String,java.lang.String[])\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index e5545405728d..4337ed5109db 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -158,7 +158,11 @@ public interface InputConnection { * 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 @@ public interface InputConnection { * * <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 @@ public interface InputConnection { * * <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 b10631b7ef2f..7fd41508dfc3 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -391,6 +391,9 @@ public class Editor { // 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 @@ public class Editor { 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 @@ public class Editor { 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 @@ public class Editor { 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 @@ public class Editor { } }; - 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 @@ public class Editor { 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 @@ public class Editor { // 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 @@ public class Editor { // 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 @@ public class Editor { } @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 733a7753568a..970d70cf1fb4 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -52,7 +52,6 @@ import android.view.View; 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 @@ public class ProgressBar extends View { 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 @@ public class ProgressBar extends View { } 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 32d3fef88e81..8ce6ee576b00 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -8668,6 +8668,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 969bda9da5a0..42d7892eeffb 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -456,7 +456,7 @@ public class Toast { 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 fa1e498d55b6..1a8e7a713e7a 100644 --- a/core/java/android/widget/WidgetFlags.java +++ b/core/java/android/widget/WidgetFlags.java @@ -35,6 +35,66 @@ public final class WidgetFlags { */ 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 08022e983892..b2aa0431251d 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.annotations.VisibleForTesting; 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 @@ import java.util.Objects; */ 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 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { 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 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { * 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 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { return mCurrentPage; } - UserHandle getCurrentUserHandle() { + @VisibleForTesting + public UserHandle getCurrentUserHandle() { return getActiveListAdapter().mResolverListController.getUserHandle(); } @@ -135,7 +153,8 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { * <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 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { abstract Object getCurrentRootAdapter(); - abstract ViewGroup getCurrentAdapterView(); + abstract ViewGroup getActiveAdapterView(); + + abstract @Nullable ViewGroup getInactiveAdapterView(); protected class ProfileDescriptor { final ViewGroup rootView; @@ -160,4 +181,15 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { 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 9532faecb4df..8bbc343fa4ca 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -508,7 +508,6 @@ public class ChooserActivity extends ResolverActivity implements 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 @@ public class ChooserActivity extends ResolverActivity implements 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 @@ public class ChooserActivity extends ResolverActivity implements } @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 @@ public class ChooserActivity extends ResolverActivity implements 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 @@ public class ChooserActivity extends ResolverActivity implements @Override public void onListRebuilt(ResolverListAdapter listAdapter) { + setupScrollListener(); + ChooserListAdapter chooserListAdapter = (ChooserListAdapter) listAdapter; if (chooserListAdapter.mDisplayList == null || chooserListAdapter.mDisplayList.isEmpty()) { @@ -2368,6 +2346,34 @@ public class ChooserActivity extends ResolverActivity implements } } + 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 @@ public class ChooserActivity extends ResolverActivity implements * 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 @@ public class ChooserActivity extends ResolverActivity implements 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 a8a676d03971..6ff844a2eaae 100644 --- a/core/java/com/android/internal/app/ChooserListAdapter.java +++ b/core/java/com/android/internal/app/ChooserListAdapter.java @@ -246,11 +246,6 @@ public class ChooserListAdapter extends ResolverListAdapter { } @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 7d856e1b945d..1c52d0e8f9f1 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 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd } @Override - ChooserActivity.ChooserGridAdapter getAdapterForIndex(int pageIndex) { + @VisibleForTesting + public ChooserActivity.ChooserGridAdapter getAdapterForIndex(int pageIndex) { return mItems[pageIndex].chooserGridAdapter; } @@ -121,10 +123,19 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd } @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 be2d1d60e9a2..f3b6d292623d 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.IVoiceInteractionSessionShowCallback; 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 @@ interface IVoiceInteractionManagerService { */ 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 @@ interface IVoiceInteractionManagerService { 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 b2417b2e79cc..68d6e03f654c 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.provider.Settings; 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.view.WindowInsets; 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.content.PackageMonitor; 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 @@ public class ResolverActivity extends Activity implements /** * 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 @@ public class ResolverActivity extends Activity implements 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 @@ public class ResolverActivity extends Activity implements /* payloadIntents */ mIntents, initialIntents, rList, - filterLastUsed, + (filterLastUsed && UserHandle.myUserId() + == getWorkProfileUserHandle().getIdentifier()), mUseLayoutForBrowsables, /* userHandle */ getWorkProfileUserHandle()); return new ResolverMultiProfilePagerAdapter( @@ -495,12 +508,12 @@ public class ResolverActivity extends Activity implements 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 class ResolverActivity extends Activity implements 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 @@ public class ResolverActivity extends Activity implements @Override // ResolverListCommunicator public void onPostListReady(ResolverListAdapter listAdapter) { - setHeader(); + if (mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier() + == UserHandle.myUserId()) { + setHeader(); + } resetButtonBar(); onListRebuilt(listAdapter); } @@ -913,6 +929,9 @@ public class ResolverActivity extends Activity implements finish(); } } + + final ItemClickListener listener = new ItemClickListener(); + setupAdapterListView((ListView) mMultiProfilePagerAdapter.getActiveAdapterView(), listener); } protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) { @@ -1094,6 +1113,7 @@ public class ResolverActivity extends Activity implements 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 @@ public class ResolverActivity extends Activity implements + "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 @@ public class ResolverActivity extends Activity implements } } - 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 @@ public class ResolverActivity extends Activity implements } // 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 @@ public class ResolverActivity extends Activity implements @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 @@ public class ResolverActivity extends Activity implements .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 ef7a347cf7be..d227ec5c1b38 100644 --- a/core/java/com/android/internal/app/ResolverListAdapter.java +++ b/core/java/com/android/internal/app/ResolverListAdapter.java @@ -193,7 +193,8 @@ public class ResolverListAdapter extends BaseAdapter { mBaseResolveList); } else { currentResolveList = mUnfilteredResolveList = - mResolverListController.getResolversForIntent(shouldGetResolvedFilter(), + mResolverListController.getResolversForIntent( + /* shouldGetResolvedFilter= */ true, mResolverListCommunicator.shouldGetActivityMetadata(), mIntents); if (currentResolveList == null) { @@ -363,10 +364,6 @@ public class ResolverListAdapter extends BaseAdapter { } } - 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 abd3eb2453df..0bfe9eb04d28 100644 --- a/core/java/com/android/internal/app/ResolverListController.java +++ b/core/java/com/android/internal/app/ResolverListController.java @@ -133,7 +133,8 @@ public class ResolverListController { 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 d72c52bfe6b6..567ed74670bf 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 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA } @Override - ResolverListAdapter getAdapterForIndex(int pageIndex) { + @VisibleForTesting + public ResolverListAdapter getAdapterForIndex(int pageIndex) { return mItems[pageIndex].resolverListAdapter; } @@ -106,10 +108,19 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA } @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 index b017bb44d751..8ec94f159725 100644 --- a/core/java/com/android/internal/app/WrapHeightViewPager.java +++ b/core/java/com/android/internal/app/ResolverViewPager.java @@ -18,39 +18,36 @@ package com.android.internal.app; 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 @@ public class WrapHeightViewPager extends ViewPager { 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 b3548b8c82f8..580c1f00d788 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -12644,7 +12644,6 @@ public class BatteryStatsImpl extends BatteryStats { /*@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 @@ public class BatteryStatsImpl extends BatteryStats { 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 5e3c5dfe6bdc..7a9b6590d56b 100644 --- a/core/java/com/android/internal/os/ProcessCpuTracker.java +++ b/core/java/com/android/internal/os/ProcessCpuTracker.java @@ -753,6 +753,8 @@ public class ProcessCpuTracker { 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 a6637a2fb601..8412b846b2a6 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.ColorDrawable; 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 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind 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 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind initResizingPaints(); mLegacyNavigationBarBackgroundPaint.setColor(Color.BLACK); + mUseNewInsetsApi = context.getApplicationInfo().targetSdkVersion >= VERSION_CODES.R; } void setBackgroundFallback(@Nullable Drawable fallbackDrawable) { @@ -1174,20 +1181,24 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind // 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 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind 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 4abd39797ba0..95558c31e671 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.FLAG_TRANSLUCENT_STATUS; 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.Configuration; 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.transition.TransitionSet; 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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { /** @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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { /** 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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { 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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { 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 class PhoneWindow extends Window implements MenuBuilder.Callback { 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/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java index 8cad5a0d1d09..7cff90bbf437 100644 --- a/core/java/com/android/internal/util/ScreenshotHelper.java +++ b/core/java/com/android/internal/util/ScreenshotHelper.java @@ -6,7 +6,11 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.graphics.Bitmap; +import android.graphics.Insets; +import android.graphics.Rect; import android.net.Uri; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -14,6 +18,7 @@ import android.os.Messenger; import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; +import android.view.WindowManager; import java.util.function.Consumer; @@ -38,9 +43,9 @@ public class ScreenshotHelper { * is recommended for general use. * * @param screenshotType The type of screenshot, for example either - * {@link android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN} + * {@link android.view.WindowManager#TAKE_SCREENSHOT_FULLSCREEN} * or - * {@link android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION} + * {@link android.view.WindowManager#TAKE_SCREENSHOT_SELECTED_REGION} * @param hasStatus {@code true} if the status bar is currently showing. {@code false} * if * not. @@ -65,9 +70,9 @@ public class ScreenshotHelper { * is recommended for general use. * * @param screenshotType The type of screenshot, for example either - * {@link android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN} + * {@link android.view.WindowManager#TAKE_SCREENSHOT_FULLSCREEN} * or - * {@link android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION} + * {@link android.view.WindowManager#TAKE_SCREENSHOT_SELECTED_REGION} * @param hasStatus {@code true} if the status bar is currently showing. {@code false} * if * not. @@ -84,6 +89,40 @@ public class ScreenshotHelper { public void takeScreenshot(final int screenshotType, final boolean hasStatus, final boolean hasNav, long timeoutMs, @NonNull Handler handler, @Nullable Consumer<Uri> completionConsumer) { + takeScreenshot(screenshotType, hasStatus, hasNav, timeoutMs, handler, null, + completionConsumer + ); + } + + /** + * Request that provided image be handled as if it was a screenshot. + * + * @param screenshot The bitmap to treat as the screen shot. + * @param boundsInScreen The bounds in screen coordinates that the bitmap orginated from. + * @param insets The insets that the image was shown with, inside the screenbounds. + * @param taskId The taskId of the task that the screen shot was taken of. + * @param handler A handler used in case the screenshot times out + * @param completionConsumer Consumes `false` if a screenshot was not taken, and `true` if the + * screenshot was taken. + */ + public void provideScreenshot(@NonNull Bitmap screenshot, @NonNull Rect boundsInScreen, + @NonNull Insets insets, int taskId, @NonNull Handler handler, + @Nullable Consumer<Uri> completionConsumer) { + Bundle imageBundle = new Bundle(); + imageBundle.putParcelable(WindowManager.PARCEL_KEY_SCREENSHOT_BITMAP, screenshot); + imageBundle.putParcelable(WindowManager.PARCEL_KEY_SCREENSHOT_BOUNDS, boundsInScreen); + imageBundle.putParcelable(WindowManager.PARCEL_KEY_SCREENSHOT_INSETS, insets); + imageBundle.putInt(WindowManager.PARCEL_KEY_SCREENSHOT_TASK_ID, taskId); + + takeScreenshot( + WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE, + false, false, // ignored when image bundle is set + SCREENSHOT_TIMEOUT_MS, handler, imageBundle, completionConsumer); + } + + private void takeScreenshot(final int screenshotType, final boolean hasStatus, + final boolean hasNav, long timeoutMs, @NonNull Handler handler, + @Nullable Bundle providedImage, @Nullable Consumer<Uri> completionConsumer) { synchronized (mScreenshotLock) { if (mScreenshotConnection != null) { return; @@ -139,6 +178,10 @@ public class ScreenshotHelper { msg.arg1 = hasStatus ? 1 : 0; msg.arg2 = hasNav ? 1 : 0; + if (screenshotType == WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) { + msg.setData(providedImage); + } + try { messenger.send(msg); } catch (RemoteException e) { diff --git a/core/jni/android_media_AudioEffectDescriptor.cpp b/core/jni/android_media_AudioEffectDescriptor.cpp index 5175a05c4c3b..37d8114052b8 100644 --- a/core/jni/android_media_AudioEffectDescriptor.cpp +++ b/core/jni/android_media_AudioEffectDescriptor.cpp @@ -39,17 +39,21 @@ jint convertAudioEffectDescriptorFromNative(JNIEnv* env, jobject* jDescriptor, 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 e266fe623947..0156e23e94b6 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -307,6 +307,43 @@ static int _check_AudioSystem_Command(const char* caller, status_t status) 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 @@ static jint convertAudioMixToNative(JNIEnv *env, 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 @@ exit: 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 @@ static jint android_media_AudioSystem_removeUidDeviceAffinities(JNIEnv *env, job 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 @@ static const JNINativeMethod gMethods[] = { {"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_view_Surface.cpp b/core/jni/android_view_Surface.cpp index 01b5920755fa..b01083bba643 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -413,6 +413,12 @@ static jint nativeSetAutoRefreshEnabled(JNIEnv* env, jclass clazz, jlong nativeO 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 @@ static const JNINativeMethod gSurfaceMethods[] = { (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 e0f9571472da..2b9d45431582 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -584,6 +584,14 @@ static void nativeSetShadowRadius(JNIEnv* env, jclass clazz, jlong transactionOb 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 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (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/hardware/location/context_hub_info.proto b/core/proto/android/hardware/location/context_hub_info.proto new file mode 100644 index 000000000000..de5cd55fafaf --- /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 d08cbed5909d..8adcc9ed905d 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/activitymanagerservice.proto"; 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 @@ message IncidentProto { (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/location/context_hub.proto b/core/proto/android/server/location/context_hub.proto new file mode 100644 index 000000000000..c87f79193ee3 --- /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 3c657530061b..4595bab82465 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 f7ac1642f6b7..27faea4b00de 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 0c45e45e7980..4e8a41f81c48 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 c5d891254227..757cd539152d 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 68b991755e73..6d8d3480dc8c 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 5b3d929d23a5..b54673834ea9 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 940e9f1ef88b..c9c47b92f782 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 0895edc1da70..cfed805d5d88 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 1dcd389d9d8f..731e2ec95f9e 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 7fd444a1a416..dadb92415839 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> @@ -4211,7 +4178,7 @@ where: IDs are unique per device, Modality as defined in BiometricAuthenticator.java, and Strength as defined in Authenticators.java --> <string-array name="config_biometric_sensors" translatable="false" > - <item>0:2:15</item> <!-- ID0:Fingerprint:Strong --> + <!-- <item>0:2:15</item> ID0:Fingerprint:Strong --> </string-array> <!-- Messages that should not be shown to the user during face auth enrollment. This should be @@ -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 5d8d31debb54..4f61730ad2e2 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 bed418dc4c2d..be2b678565d3 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> @@ -5319,4 +5325,8 @@ <!-- 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 bcce1f05f0dd..751eca036ba6 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1482,6 +1482,22 @@ please see styles_device_defaults.xml. <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 703281964442..18ca003dd8c6 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" /> @@ -3822,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" /> @@ -3833,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 ad38f3d57c23..5e6dd8294776 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -893,4 +893,11 @@ please see themes_device_defaults.xml. <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 000000000000..3103708f985d --- /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 f2852fa49b5e..bcf0b8ce4439 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.platform.test.annotations.Presubmit; 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 @@ public class InsetsAnimationControlImplTest { 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 838190387c35..f720c987a525 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.platform.test.annotations.Presubmit; 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 @@ public class InsetsControllerTest { 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 @@ public class InsetsControllerTest { 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 @@ public class InsetsControllerTest { 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 9e4b1c55304f..20be8c22ccfc 100644 --- a/core/tests/coretests/src/android/view/InsetsStateTest.java +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -94,7 +94,7 @@ public class InsetsStateTest { 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 cf5d079ba688..df6ed8c3fe0d 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -109,7 +109,7 @@ public class ViewRootImplTest { 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 @@ public class ViewRootImplTest { 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 @@ public class ViewRootImplTest { 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 @@ public class ViewRootImplTest { 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 @@ public class ViewRootImplTest { 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 @@ public class ViewRootImplTest { 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 68099fe19d13..12c057f5a91a 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 @@ package android.view.inputmethod; 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 @@ public class EditorInfoTest { } } } + + @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 c086421501b5..411868d8befe 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.ChooserActivity.TARGET_TYPE_SHORTCUTS_FRO 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.Paint; 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.app.chooser.DisplayResolveInfo; 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 @@ public class ChooserActivityTest { assertThat(activity.getIsSelected(), is(true)); } + @Ignore // b/148158199 @Test public void noResultsFromPackageManager() { when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), @@ -346,6 +354,9 @@ public class ChooserActivityTest { @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 @@ public class ChooserActivityTest { // 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 @@ public class ChooserActivityTest { @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 @@ public class ChooserActivityTest { // 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 @@ public class ChooserActivityTest { @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 @@ public class ChooserActivityTest { // 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 @@ public class ChooserActivityTest { .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 @@ public class ChooserActivityTest { 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 @@ public class ChooserActivityTest { 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 2a1044361d42..eee62bb791bf 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.content.res.Resources; 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 @@ public class ChooserWrapperActivity extends ChooserActivity { 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 @@ public class ChooserWrapperActivity extends ChooserActivity { @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 @@ public class ChooserWrapperActivity extends ChooserActivity { 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 class ChooserWrapperActivity extends ChooserActivity { 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 class ChooserWrapperActivity extends ChooserActivity { public MetricsLogger metricsLogger; public int alternateProfileSetting; public Resources resources; + public UserHandle workProfileUserHandle; public void reset() { onSafelyStartCallback = null; @@ -172,9 +203,11 @@ public class ChooserWrapperActivity extends ChooserActivity { 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 000000000000..a4766318a21e --- /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 923ce3e3935d..42f7736d37b0 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 @@ package com.android.internal.app; 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 static org.mockito.Mockito.when; 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.ActivityInfoPresentationGett 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 @@ public class ResolverActivityTest { @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 @@ public class ResolverActivityTest { // 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 @@ public class ResolverActivityTest { @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 @@ public class ResolverActivityTest { 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 @@ public class ResolverActivityTest { @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 @@ public class ResolverActivityTest { 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 @@ public class ResolverActivityTest { 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 @@ public class ResolverActivityTest { 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 59634f6d261c..d7db5f8e46eb 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 @@ class ResolverDataProvider { 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 c5d2cfaa9512..36c8724e522e 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 @@ public class ResolverWrapperActivity extends ResolverActivity { 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 @@ public class ResolverWrapperActivity extends ResolverActivity { @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 @@ public class ResolverWrapperActivity extends ResolverActivity { 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 class ResolverWrapperActivity extends ResolverActivity { 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/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java index 1472b9034249..cd6b3af5fa6d 100644 --- a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java +++ b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java @@ -31,6 +31,9 @@ import static org.mockito.Mockito.mock; import android.content.Context; import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Insets; +import android.graphics.Rect; import android.os.Handler; import android.os.Looper; @@ -85,6 +88,13 @@ public final class ScreenshotHelperTest { } @Test + public void testProvidedImageScreenshot() { + mScreenshotHelper.provideScreenshot( + Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888), new Rect(), + Insets.of(0, 0, 0, 0), 1, mHandler, null); + } + + @Test public void testScreenshotTimesOut() { long timeoutMs = 10; diff --git a/data/etc/com.android.documentsui.xml b/data/etc/com.android.documentsui.xml index 36b282c87c40..4d9860387b8d 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 877ef2687349..0541db121ae6 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/drm/java/android/drm/DrmConvertedStatus.java b/drm/java/android/drm/DrmConvertedStatus.java index f6e570a76af0..0f7ceb4ba685 100644 --- a/drm/java/android/drm/DrmConvertedStatus.java +++ b/drm/java/android/drm/DrmConvertedStatus.java @@ -25,7 +25,9 @@ package android.drm; * 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 c61819dacd99..f37c8accc84d 100644 --- a/drm/java/android/drm/DrmErrorEvent.java +++ b/drm/java/android/drm/DrmErrorEvent.java @@ -22,7 +22,9 @@ import java.util.HashMap; * 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 1a19f5c62b94..e2fe87b55578 100644 --- a/drm/java/android/drm/DrmEvent.java +++ b/drm/java/android/drm/DrmEvent.java @@ -21,7 +21,9 @@ import java.util.HashMap; /** * 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 8c43252e95b2..3240893a1f6c 100644 --- a/drm/java/android/drm/DrmInfo.java +++ b/drm/java/android/drm/DrmInfo.java @@ -30,7 +30,9 @@ import java.util.Iterator; * 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 2826dcee4f67..853f566cbe05 100644 --- a/drm/java/android/drm/DrmInfoEvent.java +++ b/drm/java/android/drm/DrmInfoEvent.java @@ -22,7 +22,9 @@ import java.util.HashMap; * 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 621da413bf97..135bbc07e391 100644 --- a/drm/java/android/drm/DrmInfoRequest.java +++ b/drm/java/android/drm/DrmInfoRequest.java @@ -24,7 +24,9 @@ import java.util.Iterator; * 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 9a3a7df66185..0fa1a708d52f 100644 --- a/drm/java/android/drm/DrmInfoStatus.java +++ b/drm/java/android/drm/DrmInfoStatus.java @@ -25,7 +25,9 @@ package android.drm; * 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 041300c4b1b0..ba3ebddd4b86 100644 --- a/drm/java/android/drm/DrmManagerClient.java +++ b/drm/java/android/drm/DrmManagerClient.java @@ -47,7 +47,9 @@ import java.util.concurrent.atomic.AtomicBoolean; * 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 9c238348846e..73e7f23b19c6 100644 --- a/drm/java/android/drm/DrmOutputStream.java +++ b/drm/java/android/drm/DrmOutputStream.java @@ -40,7 +40,9 @@ import java.net.UnknownServiceException; * 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 8747f777def0..0a8df090e64a 100644 --- a/drm/java/android/drm/DrmRights.java +++ b/drm/java/android/drm/DrmRights.java @@ -37,7 +37,9 @@ import java.util.Arrays; * 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 3a77ea19a19b..98d4449eaf47 100644 --- a/drm/java/android/drm/DrmStore.java +++ b/drm/java/android/drm/DrmStore.java @@ -19,7 +19,9 @@ package android.drm; /** * 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 3694ff4304c4..f7e4fbdf50ff 100644 --- a/drm/java/android/drm/DrmSupportInfo.java +++ b/drm/java/android/drm/DrmSupportInfo.java @@ -26,7 +26,9 @@ import java.util.Iterator; * 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 60ee6d94949f..66a60cf90f11 100644 --- a/drm/java/android/drm/DrmUtils.java +++ b/drm/java/android/drm/DrmUtils.java @@ -33,7 +33,9 @@ import java.util.Iterator; * 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 06e03e73be91..35b728841a0c 100644 --- a/drm/java/android/drm/ProcessedData.java +++ b/drm/java/android/drm/ProcessedData.java @@ -23,7 +23,9 @@ package android.drm; * * 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/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in index 4a252afc1df3..49817925d9b4 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 c0df2faf120a..dc467c41baed 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -130,6 +130,12 @@ struct SaveBehind final : Op { } }; +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 @@ struct SetMatrix final : Op { 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 @@ void DisplayListData::saveBehind(const SkRect* subset) { 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 @@ bool RecordingCanvas::onDoSaveBehind(const SkRect* subset) { 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 322eff24dd34..7eb1ce3eb18a 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -82,8 +82,10 @@ private: 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 @@ public: 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/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp index a1b2b18195bc..aa8849b642b1 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 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { 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 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { // 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 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { // 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 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { 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 112792611fc3..68f111752a4c 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 @@ void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) { 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 @@ void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) { .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 706325f00bd2..241d3708def5 100644 --- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp @@ -121,7 +121,7 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) { 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 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) { 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 52305b8244a6..ebc622bae302 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -100,6 +100,7 @@ public: 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 @@ public: 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 8a7878b46e89..ed4bf1b2d53e 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 @@ public final class GnssClock implements Parcelable { 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 @@ public final class GnssClock implements Parcelable { private int mHardwareClockDiscontinuityCount; private long mElapsedRealtimeNanos; private double mElapsedRealtimeUncertaintyNanos; + private int mReferenceConstellationTypeForIsb; + private double mReferenceCarrierFrequencyHzForIsb; + private String mReferenceCodeTypeForIsb; /** * @hide @@ -81,6 +88,9 @@ public final class GnssClock implements Parcelable { mHardwareClockDiscontinuityCount = clock.mHardwareClockDiscontinuityCount; mElapsedRealtimeNanos = clock.mElapsedRealtimeNanos; mElapsedRealtimeUncertaintyNanos = clock.mElapsedRealtimeUncertaintyNanos; + mReferenceConstellationTypeForIsb = clock.mReferenceConstellationTypeForIsb; + mReferenceCarrierFrequencyHzForIsb = clock.mReferenceCarrierFrequencyHzForIsb; + mReferenceCodeTypeForIsb = clock.mReferenceCodeTypeForIsb; } /** @@ -196,7 +206,6 @@ public final class GnssClock implements Parcelable { @TestApi public void resetTimeUncertaintyNanos() { resetFlag(HAS_TIME_UNCERTAINTY); - mTimeUncertaintyNanos = Double.NaN; } /** @@ -286,7 +295,6 @@ public final class GnssClock implements Parcelable { @TestApi public void resetBiasNanos() { resetFlag(HAS_BIAS); - mBiasNanos = Double.NaN; } /** @@ -327,7 +335,6 @@ public final class GnssClock implements Parcelable { @TestApi public void resetBiasUncertaintyNanos() { resetFlag(HAS_BIAS_UNCERTAINTY); - mBiasUncertaintyNanos = Double.NaN; } /** @@ -371,7 +378,6 @@ public final class GnssClock implements Parcelable { @TestApi public void resetDriftNanosPerSecond() { resetFlag(HAS_DRIFT); - mDriftNanosPerSecond = Double.NaN; } /** @@ -411,7 +417,6 @@ public final class GnssClock implements Parcelable { @TestApi public void resetDriftUncertaintyNanosPerSecond() { resetFlag(HAS_DRIFT_UNCERTAINTY); - mDriftUncertaintyNanosPerSecond = Double.NaN; } /** @@ -495,7 +500,128 @@ public final class GnssClock implements Parcelable { @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 @@ public final class GnssClock implements Parcelable { 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 @@ public final class GnssClock implements Parcelable { parcel.writeInt(mHardwareClockDiscontinuityCount); parcel.writeLong(mElapsedRealtimeNanos); parcel.writeDouble(mElapsedRealtimeUncertaintyNanos); + parcel.writeInt(mReferenceConstellationTypeForIsb); + parcel.writeDouble(mReferenceCarrierFrequencyHzForIsb); + parcel.writeString(mReferenceCodeTypeForIsb); } @Override @@ -580,7 +712,9 @@ public final class GnssClock implements Parcelable { 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 @@ public final class GnssClock implements Parcelable { "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 @@ public final class GnssClock implements Parcelable { 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 3eeb3a2051e9..83a8995bee13 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 @@ public final class GnssMeasurement implements Parcelable { 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 @@ public final class GnssMeasurement implements Parcelable { mSnrInDb = measurement.mSnrInDb; mAutomaticGainControlLevelInDb = measurement.mAutomaticGainControlLevelInDb; mCodeType = measurement.mCodeType; + mReceiverInterSignalBiasNanos = measurement.mReceiverInterSignalBiasNanos; + mReceiverInterSignalBiasUncertaintyNanos = + measurement.mReceiverInterSignalBiasUncertaintyNanos; + mSatelliteInterSignalBiasNanos = measurement.mSatelliteInterSignalBiasNanos; + mSatelliteInterSignalBiasUncertaintyNanos = + measurement.mSatelliteInterSignalBiasUncertaintyNanos; } /** @@ -838,7 +849,6 @@ public final class GnssMeasurement implements Parcelable { @TestApi public void resetBasebandCn0DbHz() { resetFlag(HAS_BASEBAND_CN0); - mBasebandCn0DbHz = Double.NaN; } /** @@ -1169,7 +1179,6 @@ public final class GnssMeasurement implements Parcelable { @Deprecated public void resetCarrierPhase() { resetFlag(HAS_CARRIER_PHASE); - mCarrierPhase = Double.NaN; } /** @@ -1224,7 +1233,6 @@ public final class GnssMeasurement implements Parcelable { @Deprecated public void resetCarrierPhaseUncertainty() { resetFlag(HAS_CARRIER_PHASE_UNCERTAINTY); - mCarrierPhaseUncertainty = Double.NaN; } /** @@ -1295,7 +1303,6 @@ public final class GnssMeasurement implements Parcelable { @TestApi public void resetSnrInDb() { resetFlag(HAS_SNR); - mSnrInDb = Double.NaN; } /** @@ -1343,7 +1350,6 @@ public final class GnssMeasurement implements Parcelable { @TestApi public void resetAutomaticGainControlLevel() { resetFlag(HAS_AUTOMATIC_GAIN_CONTROL); - mAutomaticGainControlLevelInDb = Double.NaN; } /** @@ -1428,7 +1434,200 @@ public final class GnssMeasurement implements Parcelable { 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(); @@ -1455,6 +1654,10 @@ public final class GnssMeasurement implements Parcelable { 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; } @@ -1489,6 +1692,10 @@ public final class GnssMeasurement implements Parcelable { parcel.writeDouble(mAutomaticGainControlLevelInDb); parcel.writeString(mCodeType); parcel.writeDouble(mBasebandCn0DbHz); + parcel.writeDouble(mReceiverInterSignalBiasNanos); + parcel.writeDouble(mReceiverInterSignalBiasUncertaintyNanos); + parcel.writeDouble(mSatelliteInterSignalBiasNanos); + parcel.writeDouble(mSatelliteInterSignalBiasUncertaintyNanos); } @Override @@ -1517,8 +1724,9 @@ public final class GnssMeasurement implements Parcelable { 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, @@ -1539,37 +1747,57 @@ public final class GnssMeasurement implements Parcelable { "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(); } @@ -1596,6 +1824,10 @@ public final class GnssMeasurement implements Parcelable { resetAutomaticGainControlLevel(); resetCodeType(); resetBasebandCn0DbHz(); + resetReceiverInterSignalBiasNanos(); + resetReceiverInterSignalBiasUncertaintyNanos(); + resetSatelliteInterSignalBiasNanos(); + resetSatelliteInterSignalBiasUncertaintyNanos(); } private void setFlag(int flag) { diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java index 89a3bc070578..f17fa399dace 100644 --- a/location/java/android/location/GnssStatus.java +++ b/location/java/android/location/GnssStatus.java @@ -189,6 +189,7 @@ public final class GnssStatus { * <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 b2314c585e6e..572fbc373730 100644 --- a/location/java/com/android/internal/location/ProviderRequest.java +++ b/location/java/com/android/internal/location/ProviderRequest.java @@ -118,6 +118,7 @@ public final class ProviderRequest implements Parcelable { for (LocationRequest request : locationRequests) { request.writeToParcel(parcel, flags); } + parcel.writeParcelable(workSource, flags); } @Override diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java index cb132f5b1101..6e63d1704fcb 100644 --- a/media/java/android/media/AudioDeviceInfo.java +++ b/media/java/android/media/AudioDeviceInfo.java @@ -422,6 +422,40 @@ public final class AudioDeviceInfo { 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 8ad5c04a1f85..861b76d2b153 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -4567,6 +4567,70 @@ public class AudioManager { } /** + * @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 453704eea398..65f2f1789491 100644 --- a/media/java/android/media/AudioPlaybackCaptureConfiguration.java +++ b/media/java/android/media/AudioPlaybackCaptureConfiguration.java @@ -102,6 +102,12 @@ public final class AudioPlaybackCaptureConfiguration { 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 @@ public final class AudioPlaybackCaptureConfiguration { 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 @@ public final class AudioPlaybackCaptureConfiguration { 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 @@ public final class AudioPlaybackCaptureConfiguration { } /** + * 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 @@ public final class AudioPlaybackCaptureConfiguration { } /** + * 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 48d27faa0855..02cb8aafea0c 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -1169,6 +1169,13 @@ public class AudioSystem /** 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 1f6159104d58..81275f6891ab 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -256,6 +256,38 @@ public class AudioTrack extends PlayerBase */ 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 diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 7f1c69218f45..1f97be5c3f4d 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -266,6 +266,10 @@ interface IAudioService { 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/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java index cad5aa6aaa3c..c25a5333017b 100644 --- a/media/java/android/media/audiofx/AudioEffect.java +++ b/media/java/android/media/audiofx/AudioEffect.java @@ -356,10 +356,14 @@ public class AudioEffect { 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 8c204d222cd4..bca3fa7834b4 100644 --- a/media/java/android/media/audiopolicy/AudioMixingRule.java +++ b/media/java/android/media/audiopolicy/AudioMixingRule.java @@ -73,6 +73,12 @@ public class AudioMixingRule { * 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 class AudioMixingRule { 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 @@ public class AudioMixingRule { 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 @@ public class AudioMixingRule { 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 @@ public class AudioMixingRule { 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 @@ public class AudioMixingRule { 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 @@ public class AudioMixingRule { * 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 @@ public class AudioMixingRule { * 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 @@ public class AudioMixingRule { * {@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 @@ public class AudioMixingRule { } } 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 @@ public class AudioMixingRule { 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 @@ public class AudioMixingRule { .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 27f02fe528f3..32a4a4f70192 100644 --- a/media/java/android/media/audiopolicy/AudioPolicy.java +++ b/media/java/android/media/audiopolicy/AudioPolicy.java @@ -51,6 +51,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * @hide @@ -404,7 +405,7 @@ public class AudioPolicy { /** * @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 @@ public class AudioPolicy { } } + /** + * @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 c4ba0c1fc835..b048158c3979 100644 --- a/media/java/android/media/audiopolicy/AudioPolicyConfig.java +++ b/media/java/android/media/audiopolicy/AudioPolicyConfig.java @@ -197,6 +197,14 @@ public class AudioPolicyConfig implements Parcelable { 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 9953626f0a7a..870c1b4a3558 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -146,6 +146,8 @@ public final class MediaSession { * 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 @@ public final class MediaSession { * 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/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java index 9e671b1177cd..5babb1698c85 100644 --- a/media/java/android/media/tv/TvContentRating.java +++ b/media/java/android/media/tv/TvContentRating.java @@ -17,7 +17,6 @@ package android.media.tv; import android.annotation.NonNull; -import android.annotation.SystemApi; import android.text.TextUtils; import com.android.internal.util.Preconditions; @@ -725,16 +724,16 @@ import java.util.Objects; * <td>NZ_TV_G</td> * <td>Programmes which exclude material likely to be unsuitable for children. Programmes * may not necessarily be designed for child viewers but should not contain material likely - * to alarm or distress them.</td> + * to alarm or distress them</td> * </tr> * <tr> * <td>NZ_TV_PGR</td> * <td>Programmes containing material more suited for mature audiences but not necessarily - * unsuitable for child viewers when subject to the guidance of a parent or an adult.</td> + * unsuitable for child viewers when subject to the guidance of a parent or an adult</td> * </tr> * <tr> * <td>NZ_TV_AO</td> - * <td>Programmes containing adult themes and directed primarily at mature audiences.</td> + * <td>Programmes containing adult themes and directed primarily at mature audiences</td> * </tr> * <tr> * <td valign="top" rowspan="6">SG_TV</td> diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java index 5e0b1eab4393..b40d43a8e616 100644 --- a/media/java/android/media/tv/TvTrackInfo.java +++ b/media/java/android/media/tv/TvTrackInfo.java @@ -353,8 +353,7 @@ public final class TvTrackInfo implements Parcelable { 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 @@ public final class TvTrackInfo implements Parcelable { @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/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java index 97a2b22e5030..b6bd86befd87 100644 --- a/media/java/android/media/tv/tuner/filter/MediaEvent.java +++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java @@ -124,15 +124,16 @@ public class MediaEvent extends FilterEvent{ } /** - * Reserves the ID which is used by HAL to provide additional information for AV data. + * Gets the audio handle. * - * <p>The corresponding data is used and released by {@link android.media.AudioTrack}. - * <p>The data is also released when the {@link Filter} instance is closed. + * <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 reserved AV data ID. - * @hide + * @return the audio handle. + * @see android.media.AudioTrack#ENCAPSULATION_MODE_HANDLE */ - public long reserveAvDataId() { + public long getAudioHandle() { // TODO: implement return mDataId; } diff --git a/media/java/android/media/voice/KeyphraseModelManager.java b/media/java/android/media/voice/KeyphraseModelManager.java new file mode 100644 index 000000000000..3fa38e0a5854 --- /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/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java index f979fddd7bda..c529952843a1 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 @@ public class CameraBinderTest extends AndroidTestCase { 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 203adfc749d2..97b861b390ad 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -240,6 +240,7 @@ LIBANDROID { 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 b34b31ac8439..392c9f6404ba 100644 --- a/native/android/surface_control.cpp +++ b/native/android/surface_control.cpp @@ -545,3 +545,18 @@ void ASurfaceTransaction_setColor(ASurfaceTransaction* aSurfaceTransaction, 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/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp index 1c45ea6aaecc..79bcc15e1f0f 100644 --- a/native/graphics/jni/imagedecoder.cpp +++ b/native/graphics/jni/imagedecoder.cpp @@ -208,13 +208,6 @@ const char* AImageDecoderHeaderInfo_getMimeType(const AImageDecoderHeaderInfo* i return getMimeType(toDecoder(info)->mCodec->getEncodedFormat()); } -bool AImageDecoderHeaderInfo_isAnimated(const AImageDecoderHeaderInfo* info) { - if (!info) { - return false; - } - return toDecoder(info)->mCodec->codec()->getFrameCount() > 1; -} - int32_t AImageDecoderHeaderInfo_getDataSpace(const AImageDecoderHeaderInfo* info) { if (!info) { return ANDROID_IMAGE_DECODER_BAD_PARAMETER; @@ -247,8 +240,7 @@ static AndroidBitmapFormat getFormat(SkColorType colorType) { } } -AndroidBitmapFormat AImageDecoderHeaderInfo_getAndroidBitmapFormat( - const AImageDecoderHeaderInfo* info) { +int32_t AImageDecoderHeaderInfo_getAndroidBitmapFormat(const AImageDecoderHeaderInfo* info) { if (!info) { return ANDROID_BITMAP_FORMAT_NONE; } @@ -281,7 +273,7 @@ int AImageDecoder_setUnpremultipliedRequired(AImageDecoder* decoder, bool requir ? 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; } @@ -291,7 +283,7 @@ int AImageDecoder_setTargetSize(AImageDecoder* decoder, int width, int height) { } int AImageDecoder_computeSampledSize(const AImageDecoder* decoder, int sampleSize, - int* width, int* height) { + int32_t* width, int32_t* height) { if (!decoder || !width || !height || sampleSize < 1) { return ANDROID_IMAGE_DECODER_BAD_PARAMETER; } diff --git a/native/graphics/jni/libjnigraphics.map.txt b/native/graphics/jni/libjnigraphics.map.txt index 1b396b893f4e..01c14770bebd 100644 --- a/native/graphics/jni/libjnigraphics.map.txt +++ b/native/graphics/jni/libjnigraphics.map.txt @@ -17,7 +17,6 @@ LIBJNIGRAPHICS { 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; diff --git a/opengl/java/com/google/android/gles_jni/EGLImpl.java b/opengl/java/com/google/android/gles_jni/EGLImpl.java index f94f69f0fd3f..b4ea0a6132a5 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 2a8d07f03148..3c808a6ada48 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 3fe1ea331a07..cb65045533f8 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 c7b22f823ba4..d93f62f8809d 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 c7e14d677b04..3f55ac8ccace 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 @@ public class SystemUIPrimaryWindowController implements 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 78764dd19741..3a5201517af2 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 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks 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 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks 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 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks 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 b2f8aad77dd4..07dbd667acf4 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 @@ class CarTrustAgentUnlockDialogHelper extends BroadcastReceiver{ | 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 8c756ecbaefc..7dd3be4b2c11 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_NEGATIVE; 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 @@ public class UserGridRecyclerView extends RecyclerView { 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/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java index 2d7d59cc0022..4ebb1029ff04 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 @@ public class WifiEntryPreference extends Preference implements WifiEntry.WifiEnt 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 c913999ecb7c..486386f3d284 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.Looper; 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 @@ public class SettingsProvider extends ContentProvider { private final Object mLock = new Object(); @GuardedBy("mLock") + private RemoteCallback mConfigMonitorCallback; + + @GuardedBy("mLock") private SettingsRegistry mSettingsRegistry; @GuardedBy("mLock") @@ -450,8 +454,17 @@ public class SettingsProvider extends ContentProvider { 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 @@ public class SettingsProvider extends ContentProvider { 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 @@ public class SettingsProvider extends ContentProvider { 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 @@ public class SettingsProvider extends ContentProvider { } /** - * 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/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl index 1cabee1ae679..2d288ff40b2c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl @@ -16,12 +16,15 @@ package com.android.systemui.shared.recents; +import android.graphics.Bitmap; +import android.graphics.Insets; import android.graphics.Rect; import android.os.Bundle; import android.view.MotionEvent; /** * Temporary callbacks into SystemUI. + * Next id = 22 */ interface ISystemUiProxy { @@ -114,4 +117,10 @@ interface ISystemUiProxy { * Sets the shelf height and visibility. */ void setShelfHeight(boolean visible, int shelfHeight) = 20; + + /** + * Handle the provided image as if it was a screenshot. + */ + void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen, + in Insets visibleInsets, int taskId) = 21; } 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 70a464dd254c..871cae3b4f8d 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 @@ public final class UniversalSmartspaceUtils { 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 2f8ef2dc8828..b2423b9bf252 100644 --- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java +++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java @@ -172,7 +172,7 @@ public class AdminSecondaryLockScreenController { 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 4a5bc2a8e28e..0106609b9271 100644 --- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java +++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java @@ -39,7 +39,6 @@ import android.util.Log; 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 @@ public class CarrierTextController { 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 4e7956db4a2b..571c4ae0e386 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -290,7 +290,7 @@ public class KeyguardDisplayManager { 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/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index f51cd83bb2d1..58a6c17e8239 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.Log; 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,7 +1082,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab 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. @@ -1122,7 +1121,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } 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 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab 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 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab 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 db8b5831faf1..e66b9f21bd8c 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -503,7 +503,7 @@ public class ScreenDecorations extends SystemUI implements Tunable { 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 659629b59b45..5532a0427e79 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 @@ public class AssistDisclosure { | 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 eb615a0392fa..f201a6fb1137 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 @@ public class DefaultUiController implements AssistManager.UiController { 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 89446adb0fd7..b8d32aec30e3 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 @@ public class AuthContainerView extends LinearLayout 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 ccfd3a57811c..82c8a469a057 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 @@ public class AuthCredentialPasswordView extends AuthCredentialView } // 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 e7055847f034..6e23777babd9 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 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask PackageManager pm = c.getPackageManager(); ApplicationInfo appInfo; Drawable badgedIcon; + Drawable appIcon; try { appInfo = pm.getApplicationInfo( packageName, @@ -167,7 +168,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask 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 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask // 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 37351985b3bd..83f6d45465b3 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, | 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 c911bf28effd..dd1856a93d2e 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 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks, 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 750cc607abe3..b7258117c48c 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 @@ public class PipDismissViewController { 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/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 569f660d1797..79a33c926993 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -38,6 +38,8 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.graphics.Bitmap; +import android.graphics.Insets; import android.graphics.Rect; import android.graphics.Region; import android.hardware.input.InputManager; @@ -55,6 +57,7 @@ import android.view.MotionEvent; import android.view.accessibility.AccessibilityManager; import com.android.internal.policy.ScreenDecorationsUtils; +import com.android.internal.util.ScreenshotHelper; import com.android.systemui.Dumpable; import com.android.systemui.model.SysUiState; import com.android.systemui.pip.PipUI; @@ -115,6 +118,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private final DeviceProvisionedController mDeviceProvisionedController; private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>(); private final Intent mQuickStepIntent; + private final ScreenshotHelper mScreenshotHelper; private Region mActiveNavBarRegion; @@ -365,6 +369,13 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } } + @Override + public void handleImageAsScreenshot(Bitmap screenImage, Rect locationInScreen, + Insets visibleInsets, int taskId) { + mScreenshotHelper.provideScreenshot(screenImage, locationInScreen, visibleInsets, + taskId, mHandler, null); + } + private boolean verifyCaller(String reason) { final int callerId = Binder.getCallingUserHandle().getIdentifier(); if (callerId != mCurrentBoundedUserId) { @@ -518,6 +529,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis // Listen for status bar state changes statusBarWinController.registerCallback(mStatusBarWindowCallback); + mScreenshotHelper = new ScreenshotHelper(context); } public void notifyBackAction(boolean completed, int downX, int downY, boolean isButton, diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java index 1d649eee4d57..fe84d81836e8 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 @@ public class ScreenPinningRequest implements View.OnClickListener, 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/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index 50e9a51478ed..880b8f8776e8 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -29,6 +29,7 @@ import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.Notification; @@ -38,6 +39,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.Insets; import android.graphics.Outline; import android.graphics.PixelFormat; import android.graphics.PointF; @@ -240,7 +242,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset 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(); @@ -300,8 +302,11 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset int width = crop.width(); int height = crop.height(); - // Take the screenshot - mScreenBitmap = SurfaceControl.screenshot(crop, width, height, rot); + takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, null); + } + + private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect) { + mScreenBitmap = screenshot; if (mScreenBitmap == null) { mNotificationsController.notifyScreenshotError( R.string.screenshot_failed_to_capture_text); @@ -317,7 +322,8 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this); // Start the post-screenshot animation - startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels); + startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels, + screenRect); } void takeScreenshot(Consumer<Uri> finisher) { @@ -327,9 +333,16 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)); } + void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds, + Insets visibleInsets, int taskId, Consumer<Uri> finisher) { + // TODO use taskId and visibleInsets + takeScreenshot(screenshot, finisher, screenshotScreenBounds); + } + /** * Displays a screenshot selector */ + @SuppressLint("ClickableViewAccessibility") void takeScreenshotPartial(final Consumer<Uri> finisher) { mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams); mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() { @@ -402,7 +415,8 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset /** * Starts the animation after taking the screenshot */ - private void startAnimation(final Consumer<Uri> finisher, int w, int h) { + private void startAnimation(final Consumer<Uri> finisher, int w, int h, + @Nullable Rect screenRect) { // If power save is on, show a toast so there is some visual indication that a screenshot // has been taken. PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); @@ -422,7 +436,8 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset mScreenshotAnimation.removeAllListeners(); } - ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation(); + ValueAnimator screenshotDropInAnim = screenRect != null ? createRectAnimation(screenRect) + : createScreenshotDropInAnimation(); ValueAnimator screenshotFadeOutAnim = createScreenshotToCornerAnimation(w, h); mScreenshotAnimation = new AnimatorSet(); mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim); @@ -460,6 +475,46 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset }); } + private ValueAnimator createRectAnimation(Rect rect) { + mScreenshotView.setAdjustViewBounds(true); + mScreenshotView.setMaxHeight(rect.height()); + mScreenshotView.setMaxWidth(rect.width()); + + final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION) + / SCREENSHOT_DROP_IN_DURATION); + final float flashDurationPct = 2f * flashPeakDurationPct; + final Interpolator scaleInterpolator = x -> { + // We start scaling when the flash is at it's peak + if (x < flashPeakDurationPct) { + return 0; + } + return (x - flashDurationPct) / (1f - flashDurationPct); + }; + + ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); + anim.setDuration(SCREENSHOT_DROP_IN_DURATION); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + mBackgroundView.setAlpha(0f); + mBackgroundView.setVisibility(View.VISIBLE); + mScreenshotView.setAlpha(0f); + mScreenshotView.setElevation(0f); + mScreenshotView.setTranslationX(0f); + mScreenshotView.setTranslationY(0f); + mScreenshotView.setScaleX(1f); + mScreenshotView.setScaleY(1f); + mScreenshotView.setVisibility(View.VISIBLE); + } + }); + anim.addUpdateListener(animation -> { + float t = (Float) animation.getAnimatedValue(); + mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA); + mScreenshotView.setAlpha(t); + }); + return anim; + } + private ValueAnimator createScreenshotDropInAnimation() { final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION) / SCREENSHOT_DROP_IN_DURATION); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java index 16447fbdd4aa..f3614ffbdb1b 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java @@ -30,6 +30,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Color; +import android.graphics.Insets; import android.graphics.PixelFormat; import android.graphics.PointF; import android.graphics.Rect; @@ -156,7 +157,7 @@ public class GlobalScreenshotLegacy { 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(); @@ -205,8 +206,13 @@ public class GlobalScreenshotLegacy { int width = crop.width(); int height = crop.height(); - // Take the screenshot - mScreenBitmap = SurfaceControl.screenshot(crop, width, height, rot); + takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, + statusBarVisible, navBarVisible, null); + } + + private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, boolean statusBarVisible, + boolean navBarVisible, Rect screenboundsOfBitmap) { + mScreenBitmap = screenshot; if (mScreenBitmap == null) { mNotificationsController.notifyScreenshotError( R.string.screenshot_failed_to_capture_text); @@ -220,7 +226,7 @@ public class GlobalScreenshotLegacy { // Start the post-screenshot animation startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels, - statusBarVisible, navBarVisible); + statusBarVisible, navBarVisible, screenboundsOfBitmap); } void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible, boolean navBarVisible) { @@ -229,6 +235,12 @@ public class GlobalScreenshotLegacy { new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)); } + void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds, + Insets visibleInsets, int taskId, Consumer<Uri> finisher) { + // TODO use taskId and visibleInsets + takeScreenshot(screenshot, finisher, false, false, screenshotScreenBounds); + } + /** * Displays a screenshot selector */ @@ -302,7 +314,7 @@ public class GlobalScreenshotLegacy { * Starts the animation after taking the screenshot */ private void startAnimation(final Consumer<Uri> finisher, int w, int h, - boolean statusBarVisible, boolean navBarVisible) { + boolean statusBarVisible, boolean navBarVisible, @Nullable Rect screenBoundsOfBitmap) { // If power save is on, show a toast so there is some visual indication that a screenshot // has been taken. PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); @@ -323,7 +335,8 @@ public class GlobalScreenshotLegacy { } mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams); - ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation(); + ValueAnimator screenshotDropInAnim = screenBoundsOfBitmap != null + ? createRectAnimation(screenBoundsOfBitmap) : createScreenshotDropInAnimation(); ValueAnimator screenshotFadeOutAnim = createScreenshotDropOutAnimation(w, h, statusBarVisible, navBarVisible); mScreenshotAnimation = new AnimatorSet(); @@ -430,6 +443,53 @@ public class GlobalScreenshotLegacy { return anim; } + /** + * If a bitmap was supplied to be used as the screenshot, animated from where that bitmap was + * on screen, rather than using the whole screen. + */ + private ValueAnimator createRectAnimation(Rect rect) { + mScreenshotView.setAdjustViewBounds(true); + mScreenshotView.setMaxHeight(rect.height()); + mScreenshotView.setMaxWidth(rect.width()); + + final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION) + / SCREENSHOT_DROP_IN_DURATION); + final float flashDurationPct = 2f * flashPeakDurationPct; + final Interpolator scaleInterpolator = x -> { + // We start scaling when the flash is at it's peak + if (x < flashPeakDurationPct) { + return 0; + } + return (x - flashDurationPct) / (1f - flashDurationPct); + }; + + ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); + anim.setDuration(SCREENSHOT_DROP_IN_DURATION); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + mBackgroundView.setAlpha(0f); + mBackgroundView.setVisibility(View.VISIBLE); + mScreenshotView.setAlpha(0f); + mScreenshotView.setElevation(0f); + mScreenshotView.setTranslationX(0f); + mScreenshotView.setTranslationY(0f); + mScreenshotView.setScaleX(SCREENSHOT_SCALE + mBgPaddingScale); + mScreenshotView.setScaleY(SCREENSHOT_SCALE + mBgPaddingScale); + mScreenshotView.setVisibility(View.VISIBLE); + } + }); + anim.addUpdateListener(animation -> { + float t = (Float) animation.getAnimatedValue(); + float scaleT = (SCREENSHOT_SCALE + mBgPaddingScale) + - scaleInterpolator.getInterpolation(t) + * (SCREENSHOT_SCALE - SCREENSHOT_DROP_IN_MIN_SCALE); + mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA); + mScreenshotView.setAlpha(t); + }); + return anim; + } + private ValueAnimator createScreenshotDropOutAnimation(int w, int h, boolean statusBarVisible, boolean navBarVisible) { ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java index 9570b5a3b57c..4ac59df07eb9 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java @@ -22,6 +22,9 @@ import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREEN import android.app.Service; import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.Insets; +import android.graphics.Rect; import android.net.Uri; import android.os.Handler; import android.os.IBinder; @@ -85,6 +88,22 @@ public class TakeScreenshotService extends Service { finisher, msg.arg1 > 0, msg.arg2 > 0); } break; + case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE: + Bitmap screenshot = msg.getData().getParcelable( + WindowManager.PARCEL_KEY_SCREENSHOT_BITMAP); + Rect screenBounds = msg.getData().getParcelable( + WindowManager.PARCEL_KEY_SCREENSHOT_BOUNDS); + Insets insets = msg.getData().getParcelable( + WindowManager.PARCEL_KEY_SCREENSHOT_INSETS); + int taskId = msg.getData().getInt(WindowManager.PARCEL_KEY_SCREENSHOT_TASK_ID); + if (useCornerFlow) { + mScreenshot.handleImageAsScreenshot( + screenshot, screenBounds, insets, taskId, finisher); + } else { + mScreenshotLegacy.handleImageAsScreenshot( + screenshot, screenBounds, insets, taskId, finisher); + } + break; default: Log.d(TAG, "Invalid screenshot option: " + msg.what); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java index ac05c53c38dd..6839921e90a7 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 class FeatureFlags { } 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/notification/collection/coordinator/ForegroundCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java index da119c1502c6..854444fc8bb7 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.NotificationEntry; 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 @@ import javax.inject.Singleton; * 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 @@ public class ForegroundCoordinator implements Coordinator { private final ForegroundServiceController mForegroundServiceController; private final AppOpsController mAppOpsController; - private final Handler mMainHandler; + private final DelayableExecutor mMainExecutor; private NotifPipeline mNotifPipeline; @@ -62,10 +65,10 @@ public class ForegroundCoordinator implements Coordinator { 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 @@ public class ForegroundCoordinator implements Coordinator { // 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 class ForegroundCoordinator implements Coordinator { 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 @@ public class ForegroundCoordinator implements Coordinator { 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 @@ public class ForegroundCoordinator implements Coordinator { 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 @@ public class ForegroundCoordinator implements Coordinator { 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 @@ public class ForegroundCoordinator implements Coordinator { @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 @@ public class ForegroundCoordinator implements Coordinator { 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 @@ public class ForegroundCoordinator implements Coordinator { && 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/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt index 315ea0a49bb7..4f27c0f04c3f 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.MATCH_PARENT 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 @@ class ChannelEditorDialogController @Inject constructor( 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/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java index ef581db0b053..6bd122d97dea 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 @@ public class EdgeBackGestureHandler implements DisplayListener, 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 783e7adf2a8b..16b5a2389ec6 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 @@ public class FloatingRotationButton implements RotationButton { 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 fe4879b0b071..3af80387778b 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 @@ public class NotificationShadeWindowController implements Callback, Dumpable, 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 6979554303b3..7650a3ab3a4e 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 @@ public class NotificationShadeWindowView extends FrameLayout { @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 7558022df96d..41d896856daa 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.telephony.TelephonyManager; 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 @@ public class PhoneStatusBarPolicy 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,7 +613,7 @@ public class PhoneStatusBarPolicy 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(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) { break; 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 fb30bdec68b5..e448d0ac8649 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 @@ public class StatusBarWindowController { 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 28b6c389d4df..06105f532eb6 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 @@ public class SystemUIDialog extends AlertDialog { 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 @@ public class SystemUIDialog extends AlertDialog { 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 e675a7f373b6..5dc91047770d 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.text.TextUtils; 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 @@ public class EmergencyCryptkeeperText extends TextView { 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 a76d41c12709..3a1feaa2de14 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -41,7 +41,6 @@ import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.telephony.TelephonyIntents; import com.android.settingslib.Utils; import com.android.settingslib.graph.SignalDrawable; import com.android.settingslib.net.SignalStrengthUtil; @@ -430,12 +429,12 @@ public class MobileSignalController extends SignalController< 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 899279d46161..6dd113377ce7 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 @@ public class NetworkControllerImpl extends BroadcastReceiver 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,7 +524,7 @@ public class NetworkControllerImpl extends BroadcastReceiver 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(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) { break; 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 718522c9908a..6baf36c81d30 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 @@ public class WifiSignalController extends 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/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java index 6cc8dd908760..eb1af7c82324 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.NotificationEntry; 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 @@ public class ForegroundCoordinatorTest extends SysuiTestCase { 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 @@ public class ForegroundCoordinatorTest extends SysuiTestCase { 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/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java index cd89d3c32697..4406248ec9ea 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.test.suitebuilder.annotation.SmallTest; 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 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { 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/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp index 264ce440f59f..b0ef15399435 100644 --- a/packages/Tethering/common/TetheringLib/Android.bp +++ b/packages/Tethering/common/TetheringLib/Android.bp @@ -36,6 +36,7 @@ java_library { sdk_version: "system_current", srcs: [ "src/android/net/TetheringManager.java", + "src/android/net/TetheringConstants.java", ":framework-tethering-annotations", ], static_libs: [ @@ -63,10 +64,13 @@ filegroup { 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/TetheringRequestParcel.aidl", "src/android/net/TetherStatesParcel.aidl", ], path: "src" diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl index d30c39986984..5febe73288bf 100644 --- a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl +++ b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl @@ -17,6 +17,7 @@ package android.net; import android.net.IIntResultListener; import android.net.ITetheringEventCallback; +import android.net.TetheringRequestParcel; import android.os.ResultReceiver; /** @hide */ @@ -27,8 +28,8 @@ oneway interface ITetheringConnector { void setUsbTethering(boolean enable, String callerPkg, IIntResultListener receiver); - void startTethering(int type, in ResultReceiver receiver, boolean showProvisioningUi, - String callerPkg); + void startTethering(in TetheringRequestParcel request, String callerPkg, + IIntResultListener receiver); void stopTethering(int type, String callerPkg, IIntResultListener receiver); diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl index 28361954e11e..28a810dbfac3 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 @@ package android.net; import android.net.Network; import android.net.TetheringConfigurationParcel; +import android.net.TetheringCallbackStartedParcel; import android.net.TetherStatesParcel; /** @@ -26,8 +27,8 @@ import android.net.TetherStatesParcel; */ 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 000000000000..14ee2d3e5d38 --- /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 000000000000..00cf98e0fc2d --- /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 11e57186c666..58bc4e7633ce 100644 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java @@ -15,9 +15,14 @@ */ package android.net; +import android.Manifest; 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 +30,11 @@ import android.os.ResultReceiver; 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 +44,8 @@ import java.util.concurrent.Executor; * * @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 +55,7 @@ public class TetheringManager { 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 +83,7 @@ public class TetheringManager { * 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 +129,6 @@ public class TetheringManager { */ 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 +142,14 @@ public class TetheringManager { 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 +213,9 @@ public class TetheringManager { 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 +258,8 @@ public class TetheringManager { * * @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 +281,8 @@ public class TetheringManager { * * @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 +307,8 @@ public class TetheringManager { * #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) { @@ -338,27 +327,171 @@ public class TetheringManager { } /** + * Use with {@link #startTethering} to specify additional parameters when starting tethering. + */ + public static class TetheringRequest { + /** A configuration set for TetheringRequest. */ + private final TetheringRequestParcel mRequestParcel; + + private TetheringRequest(final TetheringRequestParcel request) { + mRequestParcel = request; + } + + /** Builder used to create TetheringRequest. */ + public static class Builder { + private final TetheringRequestParcel mBuilderParcel; + + /** Default constructor of Builder. */ + public Builder(final int type) { + mBuilderParcel = new TetheringRequestParcel(); + mBuilderParcel.tetheringType = type; + mBuilderParcel.localIPv4Address = null; + mBuilderParcel.exemptFromEntitlementCheck = false; + mBuilderParcel.showProvisioningUi = true; + } + + /** + * Configure tethering with static IPv4 assignment (with DHCP disabled). + * + * @param localIPv4Address The preferred local IPv4 address to use. + */ + @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) + @NonNull + public Builder useStaticIpv4Addresses(@NonNull final LinkAddress localIPv4Address) { + mBuilderParcel.localIPv4Address = localIPv4Address; + return this; + } + + /** Start tethering without entitlement checks. */ + @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) + @NonNull + public Builder setExemptFromEntitlementCheck(boolean exempt) { + mBuilderParcel.exemptFromEntitlementCheck = exempt; + return this; + } + + /** Start tethering without showing the provisioning UI. */ + @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) + @NonNull + public Builder setSilentProvisioning(boolean silent) { + mBuilderParcel.showProvisioningUi = silent; + return this; + } + + /** Build {@link TetheringRequest] with the currently set configuration. */ + @NonNull + public TetheringRequest build() { + return new TetheringRequest(mBuilderParcel); + } + } + + /** + * Get a TetheringRequestParcel from the configuration + * @hide + */ + public TetheringRequestParcel getParcel() { + return mRequestParcel; + } + + /** String of TetheringRequest detail. */ + public String toString() { + return "TetheringRequest [ type= " + mRequestParcel.tetheringType + + ", localIPv4Address= " + mRequestParcel.localIPv4Address + + ", exemptFromEntitlementCheck= " + + mRequestParcel.exemptFromEntitlementCheck + ", showProvisioningUi= " + + mRequestParcel.showProvisioningUi + " ]"; + } + } + + /** + * Callback for use with {@link #startTethering} to find out whether tethering succeeded. + */ + public abstract static class StartTetheringCallback { + /** + * Called when tethering has been successfully started. + */ + public void onTetheringStarted() {} + + /** + * Called when starting tethering failed. + * + * @param resultCode One of the {@code TETHER_ERROR_*} constants. + */ + public void onTetheringFailed(final int resultCode) {} + } + + /** * Starts tethering and runs tether provisioning for the given type if needed. If provisioning * fails, stopTethering will be called automatically. * - */ - // TODO: improve the usage of ResultReceiver, b/145096122 - public void startTethering(final int type, @NonNull final ResultReceiver receiver, - final boolean showProvisioningUi) { + * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will + * fail if a tethering entitlement check is required. + * + * @param request a {@link TetheringRequest} which can specify the preferred configuration. + * @param executor {@link Executor} to specify the thread upon which the callback of + * TetheringRequest will be invoked. + * @param callback A callback that will be called to indicate the success status of the + * tethering start request. + */ + @RequiresPermission(anyOf = { + android.Manifest.permission.TETHER_PRIVILEGED, + android.Manifest.permission.WRITE_SETTINGS + }) + public void startTethering(@NonNull final TetheringRequest request, + @NonNull final Executor executor, @NonNull final StartTetheringCallback callback) { final String callerPkg = mContext.getOpPackageName(); Log.i(TAG, "startTethering caller:" + callerPkg); + final IIntResultListener listener = new IIntResultListener.Stub() { + @Override + public void onResult(final int resultCode) { + executor.execute(() -> { + if (resultCode == TETHER_ERROR_NO_ERROR) { + callback.onTetheringStarted(); + } else { + callback.onTetheringFailed(resultCode); + } + }); + } + }; try { - mConnector.startTethering(type, receiver, showProvisioningUi, callerPkg); + mConnector.startTethering(request.getParcel(), callerPkg, listener); } catch (RemoteException e) { throw new IllegalStateException(e); } } /** + * Starts tethering and runs tether provisioning for the given type if needed. If provisioning + * fails, stopTethering will be called automatically. + * + * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will + * fail if a tethering entitlement check is required. + * + * @param type The tethering type, on of the {@code TetheringManager#TETHERING_*} constants. + * @param executor {@link Executor} to specify the thread upon which the callback of + * TetheringRequest will be invoked. + */ + @RequiresPermission(anyOf = { + android.Manifest.permission.TETHER_PRIVILEGED, + android.Manifest.permission.WRITE_SETTINGS + }) + public void startTethering(int type, @NonNull final Executor executor, + @NonNull final StartTetheringCallback callback) { + startTethering(new TetheringRequest.Builder(type).build(), executor, callback); + } + + /** * Stops tethering for the given type. Also cancels any provisioning rechecks for that type if * applicable. + * + * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will + * fail if a tethering entitlement check is required. */ + @RequiresPermission(anyOf = { + android.Manifest.permission.TETHER_PRIVILEGED, + android.Manifest.permission.WRITE_SETTINGS + }) public void stopTethering(final int type) { final String callerPkg = mContext.getOpPackageName(); Log.i(TAG, "stopTethering caller:" + callerPkg); @@ -375,11 +508,69 @@ public class TetheringManager { } /** + * 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. + * + * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will + * fail if a tethering entitlement check is required. + * + * @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(anyOf = { + android.Manifest.permission.TETHER_PRIVILEGED, + android.Manifest.permission.WRITE_SETTINGS + }) + 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,25 +587,148 @@ public class TetheringManager { } /** + * 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. * * @param executor the executor on which callback will be invoked. * @param callback the callback to be called when tethering has change events. */ + @RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE) 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 +736,45 @@ public class TetheringManager { }); } + 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 +785,26 @@ public class TetheringManager { }); } + private void sendRegexpsChanged(TetheringConfigurationParcel parcel) { + callback.onTetherableInterfaceRegexpsChanged(new TetheringInterfaceRegexps( + parcel.tetherableBluetoothRegexs, + parcel.tetherableUsbRegexs, + parcel.tetherableWifiRegexs)); + } + @Override - public void onConfigurationChanged(TetheringConfigurationParcel config) { } + public void onConfigurationChanged(TetheringConfigurationParcel config) { + executor.execute(() -> sendRegexpsChanged(config)); + } @Override - public void onTetherStatesChanged(TetherStatesParcel states) { } + public void onTetherStatesChanged(TetherStatesParcel states) { + executor.execute(() -> { + sendErrorCallbacks(states); + maybeSendTetherableIfacesChangedCallback(states); + maybeSendTetheredIfacesChangedCallback(states); + }); + } }; try { mConnector.registerTetheringEventCallback(remoteCallback, callerPkg); @@ -458,7 +821,11 @@ public class TetheringManager { * * @param callback previously registered callback. */ - public void unregisterTetheringEventCallback(@NonNull final OnTetheringEventCallback callback) { + @RequiresPermission(anyOf = { + Manifest.permission.TETHER_PRIVILEGED, + Manifest.permission.ACCESS_NETWORK_STATE + }) + public void unregisterTetheringEventCallback(@NonNull final TetheringEventCallback callback) { final String callerPkg = mContext.getOpPackageName(); Log.i(TAG, "unregisterTetheringEventCallback caller:" + callerPkg); @@ -482,6 +849,7 @@ public class TetheringManager { * @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 +871,7 @@ public class TetheringManager { * * @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 +885,7 @@ public class TetheringManager { * * @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 +899,7 @@ public class TetheringManager { * * @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 +911,7 @@ public class TetheringManager { * 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 +924,7 @@ public class TetheringManager { * 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 +943,7 @@ public class TetheringManager { * * @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 +956,7 @@ public class TetheringManager { * 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 +970,7 @@ public class TetheringManager { * due to device configuration. * * @return a boolean - {@code true} indicating Tethering is supported. + * @hide */ public boolean isTetheringSupported() { final String callerPkg = mContext.getOpPackageName(); @@ -613,7 +989,14 @@ public class TetheringManager { /** * Stop all active tethering. + * + * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will + * fail if a tethering entitlement check is required. */ + @RequiresPermission(anyOf = { + android.Manifest.permission.TETHER_PRIVILEGED, + android.Manifest.permission.WRITE_SETTINGS + }) public void stopAllTethering() { final String callerPkg = mContext.getOpPackageName(); Log.i(TAG, "stopAllTethering caller:" + callerPkg); diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl new file mode 100644 index 000000000000..bf19d85f6a83 --- /dev/null +++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl @@ -0,0 +1,30 @@ +/* + * 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.LinkAddress; + +/** + * Configuration details for requesting tethering. + * @hide + */ +parcelable TetheringRequestParcel { + int tetheringType; + LinkAddress localIPv4Address; + boolean exemptFromEntitlementCheck; + boolean showProvisioningUi; +} 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 1cabc8d0b5b7..e81d6ac7a588 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 @@ public class EntitlementManager { 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 @@ public class EntitlementManager { } 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 8d1e0c96e300..c47f2d6d4db2 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -66,6 +66,7 @@ import android.content.IntentFilter; import android.content.res.Resources; import android.hardware.usb.UsbManager; import android.net.ConnectivityManager; +import android.net.IIntResultListener; import android.net.INetd; import android.net.ITetheringEventCallback; import android.net.IpPrefix; @@ -74,7 +75,9 @@ import android.net.LinkProperties; import android.net.Network; import android.net.NetworkInfo; import android.net.TetherStatesParcel; +import android.net.TetheringCallbackStartedParcel; import android.net.TetheringConfigurationParcel; +import android.net.TetheringRequestParcel; import android.net.ip.IpServer; import android.net.shared.NetdUtils; import android.net.util.BaseNetdUnsolicitedEventListener; @@ -423,9 +426,10 @@ public class Tethering { } } - void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) { - mEntitlementMgr.startProvisioningIfNeeded(type, showProvisioningUi); - enableTetheringInternal(type, true /* enabled */, receiver); + void startTethering(final TetheringRequestParcel request, final IIntResultListener listener) { + mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType, + request.showProvisioningUi); + enableTetheringInternal(request.tetheringType, true /* enabled */, listener); } void stopTethering(int type) { @@ -437,29 +441,32 @@ public class Tethering { * Enables or disables tethering for the given type. If provisioning is required, it will * schedule provisioning rechecks for the specified interface. */ - private void enableTetheringInternal(int type, boolean enable, ResultReceiver receiver) { + private void enableTetheringInternal(int type, boolean enable, + final IIntResultListener listener) { int result; switch (type) { case TETHERING_WIFI: result = setWifiTethering(enable); - sendTetherResult(receiver, result); + sendTetherResult(listener, result); break; case TETHERING_USB: result = setUsbTethering(enable); - sendTetherResult(receiver, result); + sendTetherResult(listener, result); break; case TETHERING_BLUETOOTH: - setBluetoothTethering(enable, receiver); + setBluetoothTethering(enable, listener); break; default: Log.w(TAG, "Invalid tether type."); - sendTetherResult(receiver, TETHER_ERROR_UNKNOWN_IFACE); + sendTetherResult(listener, TETHER_ERROR_UNKNOWN_IFACE); } } - private void sendTetherResult(ResultReceiver receiver, int result) { - if (receiver != null) { - receiver.send(result, null); + private void sendTetherResult(final IIntResultListener listener, int result) { + if (listener != null) { + try { + listener.onResult(result); + } catch (RemoteException e) { } } } @@ -485,12 +492,12 @@ public class Tethering { return TETHER_ERROR_MASTER_ERROR; } - private void setBluetoothTethering(final boolean enable, final ResultReceiver receiver) { + private void setBluetoothTethering(final boolean enable, final IIntResultListener listener) { final BluetoothAdapter adapter = mDeps.getBluetoothAdapter(); if (adapter == null || !adapter.isEnabled()) { Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: " + (adapter == null)); - sendTetherResult(receiver, TETHER_ERROR_SERVICE_UNAVAIL); + sendTetherResult(listener, TETHER_ERROR_SERVICE_UNAVAIL); return; } @@ -519,7 +526,7 @@ public class Tethering { final int result = (((BluetoothPan) proxy).isTetheringOn() == enable) ? TETHER_ERROR_NO_ERROR : TETHER_ERROR_MASTER_ERROR; - sendTetherResult(receiver, result); + sendTetherResult(listener, result); adapter.closeProfileProxy(BluetoothProfile.PAN, proxy); } }, BluetoothProfile.PAN); @@ -951,6 +958,7 @@ public class Tethering { mWrapper.showTetheredNotification( R.drawable.stat_sys_tether_general, false); mWrapper.untetherAll(); + // TODO(b/148139325): send tetheringSupported on restriction change } } } @@ -1844,9 +1852,13 @@ public class Tethering { 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 +1893,7 @@ public class Tethering { 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/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java index cb7d3920e693..7dc5c5f2db8a 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java @@ -33,6 +33,7 @@ import android.net.ITetheringConnector; import android.net.ITetheringEventCallback; import android.net.NetworkCapabilities; import android.net.NetworkRequest; +import android.net.TetheringRequestParcel; import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServingParamsParcel; import android.net.ip.IpServer; @@ -143,11 +144,11 @@ public class TetheringService extends Service { } @Override - public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi, - String callerPkg) { - if (checkAndNotifyCommonError(callerPkg, receiver)) return; + public void startTethering(TetheringRequestParcel request, String callerPkg, + IIntResultListener listener) { + if (checkAndNotifyCommonError(callerPkg, listener)) return; - mTethering.startTethering(type, receiver, showProvisioningUi); + mTethering.startTethering(request, listener); } @Override 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 4f0746199786..3a1d4a61a39e 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 @@ package com.android.server.connectivity.tethering; 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 final class EntitlementManagerTest { } 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 final class EntitlementManagerTest { } public void reset() { - fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKONWN; + fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKNOWN; uiProvisionCount = 0; silentProvisionCount = 0; } @@ -274,7 +274,7 @@ public final class EntitlementManagerTest { 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 @@ public final class EntitlementManagerTest { 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 d6afa4744d26..6fc0c659f397 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,7 +86,9 @@ import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.RouteInfo; import android.net.TetherStatesParcel; +import android.net.TetheringCallbackStartedParcel; import android.net.TetheringConfigurationParcel; +import android.net.TetheringRequestParcel; import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.IDhcpServer; @@ -467,6 +469,16 @@ public class TetheringTest { return new Tethering(mTetheringDependencies); } + private TetheringRequestParcel createTetheringRquestParcel(final int type) { + final TetheringRequestParcel request = new TetheringRequestParcel(); + request.tetheringType = type; + request.localIPv4Address = null; + request.exemptFromEntitlementCheck = false; + request.showProvisioningUi = false; + + return request; + } + @After public void tearDown() { mServiceContext.unregisterReceiver(mBroadcastReceiver); @@ -572,7 +584,7 @@ public class TetheringTest { .thenReturn(upstreamState); // Emulate pressing the USB tethering button in Settings UI. - mTethering.startTethering(TETHERING_USB, null, false); + mTethering.startTethering(createTetheringRquestParcel(TETHERING_USB), null); mLooper.dispatchAll(); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); @@ -818,7 +830,7 @@ public class TetheringTest { when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); // Emulate pressing the WiFi tethering button. - mTethering.startTethering(TETHERING_WIFI, null, false); + mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null); mLooper.dispatchAll(); verify(mWifiManager, times(1)).startTetheredHotspot(null); verifyNoMoreInteractions(mWifiManager); @@ -845,7 +857,7 @@ public class TetheringTest { when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); // Emulate pressing the WiFi tethering button. - mTethering.startTethering(TETHERING_WIFI, null, false); + mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null); mLooper.dispatchAll(); verify(mWifiManager, times(1)).startTetheredHotspot(null); verifyNoMoreInteractions(mWifiManager); @@ -922,7 +934,7 @@ public class TetheringTest { doThrow(new RemoteException()).when(mNetd).ipfwdEnableForwarding(TETHERING_NAME); // Emulate pressing the WiFi tethering button. - mTethering.startTethering(TETHERING_WIFI, null, false); + mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null); mLooper.dispatchAll(); verify(mWifiManager, times(1)).startTetheredHotspot(null); verifyNoMoreInteractions(mWifiManager); @@ -1113,11 +1125,10 @@ public class TetheringTest { } @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 @@ -1188,7 +1199,7 @@ public class TetheringTest { tetherState = callback.pollTetherStatesChanged(); assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME}); - mTethering.startTethering(TETHERING_WIFI, null, false); + mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null); sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); mLooper.dispatchAll(); tetherState = callback.pollTetherStatesChanged(); diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto index ad802ff879f2..54b420191deb 100644 --- a/proto/src/system_messages.proto +++ b/proto/src/system_messages.proto @@ -312,5 +312,9 @@ message SystemMessage { // 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 5e6f97e4f282..c7be80cd538b 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 static com.android.server.autofill.Helper.sDebug; 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; @@ -123,7 +122,8 @@ public final class InlineSuggestionFactory { // TODO(b/146453195): fill in the autofill hint properly. final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo( inlinePresentation.getInlinePresentationSpec(), - InlineSuggestionInfo.SOURCE_PLATFORM, new String[]{""}); + InlineSuggestionInfo.SOURCE_PLATFORM, new String[]{""}, + InlineSuggestionInfo.TYPE_SUGGESTION); final View.OnClickListener onClickListener = v -> { try { client.autofill(sessionId, dataset.getFieldIds(), dataset.getFieldValues()); @@ -132,7 +132,7 @@ public final class InlineSuggestionFactory { } }; final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo, - createInlineContentProvider(inlinePresentation.getSlice(), inlineSuggestionUi, + createInlineContentProvider(inlinePresentation, inlineSuggestionUi, onClickListener)); return inlineSuggestion; } @@ -146,25 +146,28 @@ public final class InlineSuggestionFactory { // TODO(b/146453195): fill in the autofill hint properly. final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo( inlinePresentation.getInlinePresentationSpec(), - InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""}); + InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""}, + InlineSuggestionInfo.TYPE_SUGGESTION); final View.OnClickListener onClickListener = v -> { 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 2460732d17dc..2adefeabfcc8 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.annotation.Nullable; 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 android.widget.TextView; 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 @@ public class InlineSuggestionUi { 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 @@ public class InlineSuggestionUi { */ @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 @@ public class InlineSuggestionUi { 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 5e10916c4491..0bcf45d4a526 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.RemoteException; 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 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { * 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 e976811a3094..434a97ee0bc5 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 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } @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/core/Android.bp b/services/core/Android.bp index 1691a96b0dc4..a603fa975b74 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -113,6 +113,7 @@ java_library_static { "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 368416b0d4a1..994c3147d70c 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.AndroidPackage; 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 @@ public abstract class PackageManagerInternal { * 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 @@ public abstract class PackageManagerInternal { * 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 @@ public abstract class PackageManagerInternal { 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 a33fcd557369..8074900d2776 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.app.IBatteryStats; 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 @@ public final class BatteryService extends SystemService { } private final class Led { - private final Light mBatteryLight; + private final LogicalLight mBatteryLight; private final int mBatteryLowARGB; private final int mBatteryMediumARGB; @@ -1100,7 +1100,7 @@ public final class BatteryService extends SystemService { 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 e9db9c819ab7..de0b6fcbd4ae 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -18,6 +18,8 @@ package com.android.server; 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.BroadcastReceiver; 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.IBatchedLocationCallback; 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.IndentingPrintWriter; 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.FileDescriptor; 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; 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 @@ public class LocationManagerService extends ILocationManager.Stub { } @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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { } // 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 e8e3b39d5112..131a22b07c7b 100644 --- a/services/core/java/com/android/server/RescueParty.java +++ b/services/core/java/com/android/server/RescueParty.java @@ -18,6 +18,7 @@ package com.android.server; 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.ApplicationInfo; 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.FailureReasons; 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 @@ public class RescueParty { @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 @@ public class RescueParty { 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 class RescueParty { */ 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 @@ public class RescueParty { 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 @@ public class RescueParty { /** * 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 @@ public class RescueParty { + 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 @@ public class RescueParty { } } - 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 @@ public class RescueParty { } } - 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 @@ public class RescueParty { } } + 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 class RescueParty { */ 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 @@ public class RescueParty { } } + @VisibleForTesting + static void reset() { + synchronized (RescuePartyObserver.class) { + sRescuePartyObserver = null; + } + } + @Override public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage, @FailureReasons int failureReason) { @@ -314,7 +416,8 @@ public class RescueParty { || 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 @@ public class RescueParty { return false; } incrementRescueLevel(Process.ROOT_UID); - executeRescueLevel(mContext); + executeRescueLevel(mContext, /*failedPackage=*/ null); return true; } @@ -363,6 +466,32 @@ public class RescueParty { 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 7f51aa9068fc..8564cb456ba6 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.Context; 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.Looper; 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.TimeUnit; 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 @@ public class ServiceWatcher implements ServiceConnection { 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; - - HashSet<Signature> set = new HashSet<>(); - Collections.addAll(set, signatures); - signatureSets.add(set); - } catch (NameNotFoundException e) { - Log.w(TAG, packageName + " not found"); + public static final ServiceInfo NONE = new ServiceInfo(Integer.MIN_VALUE, null, + UserHandle.USER_NULL); + + 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; } - } - return signatureSets; - } - /** Checks if signatures match. */ - public static boolean isSignatureMatch(Signature[] signatures, - List<HashSet<Signature>> sigSets) { - if (signatures == null) return false; + component = resolveInfo.serviceInfo.getComponentName(); + userId = isMultiuser ? UserHandle.USER_SYSTEM : currentUserId; + } - // build hashset of input to test against - HashSet<Signature> inputSet = new HashSet<>(); - Collections.addAll(inputSet, signatures); + 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; + } - // 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); + } + + @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 + "]"; } - return false; } 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; + + @Nullable private final BinderRunner mOnBind; + @Nullable private final Runnable mOnUnbind; - // read/write from handler thread - private IBinder mBestService; + // 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; - - 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); + mHandler = FgThread.getHandler(); + mIntent = new Intent(Objects.requireNonNull(action)); + + 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())); - } + 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; + } - @Override - public void onPackageAdded(String packageName, int uid) { - bindBestPackage(Objects.equals(packageName, getCurrentPackageName())); - } + new PackageMonitor() { + @Override + public void onPackageUpdateFinished(String packageName, int uid) { + ServiceWatcher.this.onPackageChanged(packageName); + } - @Override - public void onPackageRemoved(String packageName, int uid) { - bindBestPackage(Objects.equals(packageName, getCurrentPackageName())); - } + @Override + public void onPackageAdded(String packageName, int uid) { + ServiceWatcher.this.onPackageChanged(packageName); + } - @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); - } + @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); - // listen for user change on handler 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(); - } - - private boolean isServiceMissing() { - return mContext.getPackageManager().queryIntentServicesAsUser(new Intent(mAction), - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, - UserHandle.USER_SYSTEM).isEmpty(); + /** + * Returns information on the currently selected service. + */ + public ServiceInfo getBoundService() { + return mServiceInfo; } - 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(); + + ServiceInfo bestServiceInfo = ServiceInfo.NONE; + for (ResolveInfo resolveInfo : resolveInfos) { + ServiceInfo serviceInfo = new ServiceInfo(resolveInfo, mCurrentUserId); + if (serviceInfo.compareTo(bestServiceInfo) > 0) { + bestServiceInfo = serviceInfo; + } } - ComponentName bestComponent = null; - int bestVersion = Integer.MIN_VALUE; - boolean bestIsMultiuser = false; + if (forceRebind || !bestServiceInfo.equals(mServiceInfo)) { + rebind(bestServiceInfo); + } + } - for (ResolveInfo rInfo : rInfos) { - ComponentName component = rInfo.serviceInfo.getComponentName(); - String packageName = component.getPackageName(); + private void rebind(ServiceInfo newServiceInfo) { + Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); - // 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; + if (!mServiceInfo.equals(ServiceInfo.NONE)) { + if (D) { + Log.i(TAG, "[" + mIntent.getAction() + "] unbinding from " + mServiceInfo); } - // 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); - } + mContext.unbindService(this); + mServiceInfo = ServiceInfo.NONE; + } - if (version > bestVersion) { - bestComponent = component; - bestVersion = version; - bestIsMultiuser = isMultiuser; - } + mServiceInfo = newServiceInfo; + if (mServiceInfo.equals(ServiceInfo.NONE)) { + return; } + Preconditions.checkState(mServiceInfo.component != null); + 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))); + Log.i(TAG, getLogPrefix() + " binding to " + mServiceInfo); } - if (bestComponent == null) { - Slog.w(mTag, "Odd, no component found for service " + mAction); - unbind(); - return; + 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); } - 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); + 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.i(TAG, getLogPrefix() + " disconnected from " + component); + } - 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)); + mBinder = null; + if (mOnUnbind != null) { + mOnUnbind.run(); + } } - private void unbind() { - Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); + private void onUserSwitched(@UserIdInt int userId) { + mCurrentUserId = userId; + onBestServiceChanged(false); + } - if (mBestComponent != null) { - if (D) Log.d(mTag, "unbinding " + mBestComponent); - mContext.unbindService(this); + private void onUserUnlocked(@UserIdInt int userId) { + if (userId == mCurrentUserId) { + onBestServiceChanged(false); } + } - mBestComponent = null; - mBestVersion = Integer.MIN_VALUE; - mBestUserId = UserHandle.USER_NULL; + 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 class ServiceWatcher implements ServiceConnection { */ 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 class ServiceWatcher implements ServiceConnection { 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 @@ public class ServiceWatcher implements ServiceConnection { } } - @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 @@ public class ServiceWatcher implements ServiceConnection { } } } + + private String getLogPrefix() { + return "[" + mIntent.getAction() + "]"; + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 00c0b3e88517..5596b2fcb762 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.PackageParser; 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 @@ public class ActivityManagerService extends IActivityManager.Stub } /** + * 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub @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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index fa55701cd882..a03f0bb4e399 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 @@ final class CoreSettingsObserver extends ContentObserver { 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/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 4a8984524bb7..38cb501111ca 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { } 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { 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 @@ public final class ProcessList { .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 @@ public final class ProcessList { } }; } - diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 156466cee970..0e1e0f9f64f1 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.app.IApplicationThread; 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 @@ class ProcessRecord implements WindowProcessListener { 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 @@ class ProcessRecord implements WindowProcessListener { 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 46972d90ce3b..8f6bd212da19 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_NORMAL; 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.PlayerBase; 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 @@ public class AudioService extends IAudioService.Stub boolean requireValidProjection = false; boolean requireCaptureAudioOrMediaOutputPerm = false; + boolean requireVoiceComunicationOutputPerm = false; boolean requireModifyRouting = false; if (hasFocusAccess || isVolumeController) { @@ -6783,8 +6786,15 @@ public class AudioService extends IAudioService.Stub 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 @@ public class AudioService extends IAudioService.Stub 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 @@ public class AudioService extends IAudioService.Stub 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 @@ public class AudioService extends IAudioService.Stub } } + /** 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 @@ public class AudioService extends IAudioService.Stub } } + /** 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 @@ public class AudioService extends IAudioService.Stub 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 @@ public class AudioService extends IAudioService.Stub 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/connectivity/DefaultNetworkMetrics.java b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java index 299cb66faf0c..4fb6607a6b08 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 @@ public class DefaultNetworkMetrics { } /** - * 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 1337a93476bc..2c06d8230f13 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 @@ final public class IpConnectivityMetrics extends SystemService { 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 0d3105139b88..f2892cc81951 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 @@ public class NetdEventListenerService extends INetdEventListener.Stub { } /** - * 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/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 4796edf85279..e09cf6178981 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -20,6 +20,7 @@ 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; @@ -30,6 +31,7 @@ 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; @@ -40,12 +42,15 @@ import javax.xml.datatype.DatatypeConfigurationException; 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() { } @@ -83,6 +88,18 @@ public class DisplayDeviceConfig { 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. @@ -104,7 +121,8 @@ public class DisplayDeviceConfig { } private void loadBrightnessMap(DisplayConfiguration config) { - final List<Point> points = config.getScreenBrightnessMap().getPoint(); + final NitsMap map = config.getScreenBrightnessMap(); + final List<Point> points = map.getPoint(); final int size = points.size(); float[] nits = new float[size]; @@ -130,7 +148,9 @@ public class DisplayDeviceConfig { } ++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 1f17f9f1ca43..7e8fe3ae8428 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -41,8 +41,8 @@ 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; @@ -164,7 +164,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { 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; diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java b/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java index 5c18f5880146..5161a77e4ede 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 @@ public final class IncrementalManagerShellCommand extends ShellCommand { 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/lights/LightsManager.java b/services/core/java/com/android/server/lights/LightsManager.java index be20a445432b..521913a0c439 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 abstract class LightsManager { 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 8e6e1d6502de..5683e6901a31 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 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 final class LightImpl extends Light { + 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); @@ -78,8 +269,8 @@ public class LightsService extends SystemService { 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 @@ -138,7 +329,7 @@ public class LightsService extends SystemService { 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); } } } @@ -185,8 +376,10 @@ public class LightsService extends SystemService { 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; @@ -194,10 +387,31 @@ public class LightsService extends SystemService { 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); } @@ -208,7 +422,15 @@ public class LightsService extends SystemService { 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; @@ -223,16 +445,59 @@ public class LightsService extends SystemService { } public LightsService(Context context) { + this(context, + ILights.Stub.asInterface( + ServiceManager.getService("android.hardware.light.ILights/default")), + Looper.myLooper()); + } + + @VisibleForTesting + LightsService(Context context, ILights service, Looper looper) { super(context); + mH = new Handler(looper); + mVintfLights = service; + mManagerService = new LightsManagerBinderService(); + populateAvailableLights(context); + } - for (int i = 0; i < LightsManager.LIGHT_ID_COUNT; i++) { - mLights[i] = new LightImpl(context, i); + 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 @@ -249,22 +514,25 @@ public class LightsService extends SystemService { 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 index 998c7c66b504..33dfbb4eea48 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 @@ package com.android.server.lights; 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; /** @@ -55,10 +70,33 @@ public abstract class Light { */ 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 c7c6d5658d1d..0e795b9a5ef8 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 80ab7903cef7..000000000000 --- 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 45d9bae23e26..bb96e985cf53 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.Binder; 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 @@ public class ContextHubClientBroker extends IContextHubClient.Stub } } + /** + * 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 46db8dc5dd77..0f70bb8bc840 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.IContextHubClientCallback; 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 @@ import java.util.function.Consumer; 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 @@ import java.util.function.Consumer; 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 @@ import java.util.function.Consumer; 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 787a8007920d..e79eddf247cb 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.hardware.location.NanoAppState; 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 @@ public class ContextHubService extends IContextHubService.Stub { 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 @@ public class ContextHubService extends IContextHubService.Stub { // 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 e6f0ed9d14b0..536f95a40431 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 @@ import java.util.List; /** * 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 boolean bind() { - return mServiceWatcher.start(); + 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); } - 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 @@ public class GeocoderProxy { 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 ce93661a8810..f006fb177382 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.Intent; 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 android.util.Log; import com.android.server.FgThread; import com.android.server.ServiceWatcher; +import java.util.Objects; + /** * @hide */ @@ -41,64 +42,41 @@ public final class GeofenceProxy { 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 @@ public final class GeofenceProxy { 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 000000000000..9d9852ba5da3 --- /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 8a149afa6238..805b018a8f45 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 @@ package com.android.server.location; 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.ILocationProviderManager; 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 @@ import java.util.List; 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 @@ public class LocationProviderProxy extends AbstractLocationProvider { } } + // add the binder package + ComponentName service = mServiceWatcher.getBoundService().component; + if (service != null) { + allPackages.add(service.getPackageName()); + } + setPackageNames(allPackages); } @@ -100,63 +118,39 @@ public class LocationProviderProxy extends AbstractLocationProvider { @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, Handler handler, String action, - int overlaySwitchResId, int defaultServicePackageNameResId, - int initialPackageNamesResId) { - super(context, new HandlerExecutor(handler), Collections.emptySet()); + private LocationProviderProxy(Context context, String action, int enableOverlayResId, + int nonOverlayPackageResId) { + super(context, FgThread.getExecutor()); - 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 45c833498ac7..b191338970e9 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 @@ public class LocationRequestStatistics { /** * 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 @@ public class LocationRequestStatistics { } @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 @@ public class LocationRequestStatistics { 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 4a6fcdf73b90..a6ad57a7ae3a 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 @@ public class MediaSessionService extends SystemService implements Monitor { 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 @@ public class MediaSessionService extends SystemService implements Monitor { 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 @@ public class MediaSessionService extends SystemService implements Monitor { } } + 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 @@ public class MediaSessionService extends SystemService implements Monitor { 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 @@ public class MediaSessionService extends SystemService implements Monitor { 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 @@ public class MediaSessionService extends SystemService implements Monitor { 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 6f43952a3f58..7cc67324032a 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.IoThread; 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 @@ public class NotificationManagerService extends SystemService { 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 @@ public class NotificationManagerService extends SystemService { } @VisibleForTesting - void setLights(Light light) { + void setLights(LogicalLight light) { mNotificationLight = light; mAttentionLight = light; mNotificationPulseEnabled = true; @@ -7875,7 +7875,7 @@ public class NotificationManagerService extends SystemService { 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); } } diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 78e17192ed58..9dff7754c4f3 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 @@ public class AppsFilter { * 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 @@ public class AppsFilter { } 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 ed955a260a52..ef2873358cd4 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 @@ package com.android.server.pm; 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.IntentSender; 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { info.isStagedSessionReady = mStagedSessionReady; info.isStagedSessionFailed = mStagedSessionFailed; info.setStagedSessionErrorCode(mStagedSessionErrorCode, mStagedSessionErrorMessage); + info.createdMillis = createdMillis; info.updatedMillis = updatedMillis; } return info; @@ -2325,11 +2335,23 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } @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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { assertCallerIsOwnerOrRootLocked(); assertPreparedAndNotSealedLocked("removeFile"); - mFiles.add(FileInfo.removed(getRemoveMarkerName(name))); + mFiles.add(FileInfo.removed(location, getRemoveMarkerName(name))); } } @@ -2891,9 +2913,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 3a333136abe6..ca4ae0277aaf 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.PackageUserState; 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 @@ public class PackageManagerService extends IPackageManager.Stub // 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 @@ public class PackageManagerService extends IPackageManager.Stub } /* - * 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 @@ public class PackageManagerService extends IPackageManager.Stub } @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 @@ public class PackageManagerService extends IPackageManager.Stub 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 83840067ecc2..e7f6b8982b04 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 @@ class PackageManagerShellCommand extends ShellCommand { // 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 @@ class PackageManagerShellCommand extends ShellCommand { 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 @@ class PackageManagerShellCommand extends ShellCommand { 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 a814cb8942e2..281c44a61153 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 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService { } @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 0a42ccf1c5ba..b9bb9e0ad0d8 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 @@ package com.android.server.pm; 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 @@ public final class SharedUserSetting extends SettingBase { 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 @@ public final class SharedUserSetting extends SettingBase { 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 @@ public final class SharedUserSetting extends SettingBase { } setPrivateFlags(aggregatedPrivateFlags); } + // recalculate processes. + updateProcesses(); return true; } @@ -104,6 +129,9 @@ public final class SharedUserSetting extends SettingBase { 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 @@ public final class SharedUserSetting extends SettingBase { } } + /** + * 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 @@ public final class SharedUserSetting extends SettingBase { 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 e08c004866ea..157f8256ce50 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 @@ final class GlobalKeyManager { 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 ec9049ee05ee..c3e7f62e4f31 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index ca368694ca92..3f3a13368ffc 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.SystemService; 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 @@ public final class PowerManagerService extends SystemService private WirelessChargerDetector mWirelessChargerDetector; private SettingsObserver mSettingsObserver; private DreamManagerInternal mDreamManager; - private Light mAttentionLight; + private LogicalLight mAttentionLight; private InattentiveSleepWarningController mInattentiveSleepWarningOverlayController; @@ -3347,7 +3347,7 @@ public final class PowerManagerService extends SystemService } private void setAttentionLightInternal(boolean on, int color) { - Light light; + LogicalLight light; synchronized (mLock) { if (!mSystemReady) { return; @@ -3356,7 +3356,7 @@ public final class PowerManagerService extends SystemService } // 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 f8c7447fc55d..000000000000 --- 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 36e97755ab8a..2115f7ccfe98 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -3433,6 +3433,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub 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 4f1d40e39314..f8df883a3e1c 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.LAYOUT_IN_DISPLAY_CUTOUT_M 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { // 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { } } - 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 @@ public class DisplayPolicy { 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 bef1442c73c5..ef6f84703723 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 @@ public class ImmersiveModeConfirmation { | 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/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java index 38a7000803bd..828775a4b934 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 @@ class TaskSnapshotPersister { 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 @@ class TaskSnapshotPersister { 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 81a4c682e809..8d4ad28972e9 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_TOAST; 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 @@ public class WindowManagerService extends IWindowManager.Stub 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); - 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); + if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type, + rootType, attrs.token, attrs.packageName)) { 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub @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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 c0891d739788..212a3a638634 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 @@ private: 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 @@ bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const c 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 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon outConfig->setDisplayViewports(mLocked.viewports); + outConfig->defaultPointerDisplayId = mLocked.pointerDisplayId; + outConfig->disabledDevices = mLocked.disabledInputDevices; } // release lock } @@ -564,8 +555,6 @@ sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32 updateInactivityTimeoutLocked(); } - updatePointerDisplayLocked(); - return controller; } @@ -580,23 +569,6 @@ int32_t NativeInputManager::getPointerDisplayId() { 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 00436bb8ca70..08a759227526 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 @@ void GnssMeasurementCallback::translateSingleGnssMeasurement 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 @@ void GnssMeasurementCallback::translateGnssClock( 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 @@ void GnssMeasurementCallback::translateGnssClock( 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/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd index c26629d28476..5c7f30576741 100644 --- a/services/core/xsd/display-device-config/display-device-config.xsd +++ b/services/core/xsd/display-device-config/display-device-config.xsd @@ -35,6 +35,7 @@ <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> diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt index e2b1b9bf1e2c..5a9c9457b423 100644 --- a/services/core/xsd/display-device-config/schema/current.txt +++ b/services/core/xsd/display-device-config/schema/current.txt @@ -9,7 +9,9 @@ package com.android.server.display.config { 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 { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 8641059aebd5..43ee97dfa17b 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 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { 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 7830c60806ae..b6953f6e3e36 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 @@ package com.android.server.devicepolicy; 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.DevicePolicyCache; 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.notification.SystemNotificationChannels; 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { synchronized (getLockObject()) { maybeMigrateToProfileOnOrganizationOwnedDeviceLocked(); } + checkPackageSuspensionOnBoot(); break; case SystemService.PHASE_BOOT_COMPLETED: ensureDeviceOwnerUserStarted(); // TODO Consider better place to do this. @@ -3774,6 +3810,34 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } AccessibilityManager accessibilityManager = getAccessibilityManagerForUser(userId); enabledServices = accessibilityManager.getEnabledAccessibilityServiceList( - AccessibilityServiceInfo.FEEDBACK_ALL_MASK); + FEEDBACK_ALL_MASK); } finally { mInjector.binderRestoreCallingIdentity(id); } @@ -10671,30 +10737,35 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { .setAdmin(admin) .setBoolean(isDeviceAB()) .write(); - enforceDeviceOwner(admin); + enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(admin); mInjector.binderWithCleanCallingIdentity(() -> { UpdateInstaller updateInstaller; if (isDeviceAB()) { @@ -14981,23 +15077,24 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - @Override - public boolean isUnattendedManagedKiosk() { - if (!mHasFeature) { - return false; - } - enforceManageUsers(); - long id = mInjector.binderClearCallingIdentity(); + private boolean isUnattendedManagedKioskUnchecked() { try { return isManagedKioskInternal() && getPowerManagerInternal().wasDeviceIdleFor(UNATTENDED_MANAGED_KIOSK_MS); } catch (RemoteException e) { throw new IllegalStateException(e); - } finally { - mInjector.binderRestoreCallingIdentity(id); } } + @Override + public boolean isUnattendedManagedKiosk() { + if (!mHasFeature) { + return false; + } + enforceManageUsers(); + return mInjector.binderWithCleanCallingIdentity(() -> isUnattendedManagedKioskUnchecked()); + } + /** * Returns whether the device is currently being used as a publicly-accessible dedicated device. * Assumes that feature checks and permission checks have already been performed, and that the @@ -15166,4 +15263,121 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } 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 000000000000..180acc85e5f6 --- /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 ec56e1ebc8e0..62ff3a1c2126 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.Message; 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 class KeyValueBackupTaskTest { 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 @@ public class KeyValueBackupTaskTest { @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 @@ public class KeyValueBackupTaskTest { @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 b3b5af0796ab..0e24b0314b2d 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 c3602f8db9bc..2080fdf2e40d 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.assertEquals; 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.dx.mockito.inline.extended.ExtendedMockito; 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 @@ public class RescuePartyTest { 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 @@ public class RescuePartyTest { .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 @@ public class RescuePartyTest { 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 class RescuePartyTest { 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 class RescuePartyTest { 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 @@ public class RescuePartyTest { } @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 @@ public class RescuePartyTest { 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 @@ public class RescuePartyTest { 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 @@ public class RescuePartyTest { 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 @@ public class RescuePartyTest { 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/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 8f1d0f7648f5..def5b617becd 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { 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/lights/LightsServiceTest.java b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java new file mode 100644 index 000000000000..b0def605db79 --- /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 355cadaa1de8..a1baf0e9ce05 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.Range; 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 @@ public class NetworkPolicyManagerServiceTest { 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 15327b6e5463..a8674a8f8be4 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 @@ public class PackageInstallerSessionTest { 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/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java index 1e55b1521956..587cfbf062fb 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 androidx.test.runner.AndroidJUnit4; 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 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { @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/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 9260fbf97b86..93e09dfb3f57 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.internal.util.FastXmlSerializer; 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { }); 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/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index 0527561880f2..5ba676d1c544 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_M 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 class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { 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 class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { 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 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { 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 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { 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 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { 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 class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { 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 class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { 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 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { 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 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { 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 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { 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 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { 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 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { 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 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { 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 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { 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 e699b526e848..c370d6c7c516 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_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.view.WindowManager.LayoutParams.FLAG_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 @@ public class DisplayPolicyTests extends WindowTestsBase { 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 @@ public class DisplayPolicyTests extends WindowTestsBase { 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 b8cd37860284..5119e5824f7f 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 @@ public class UsageStatsService extends SystemService implements /** * 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/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java index c58b6da64baa..af81ab6339f3 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 @@ public class DatabaseHelper extends SQLiteOpenHelper { 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 @@ public class DatabaseHelper extends SQLiteOpenHelper { 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 @@ public class DatabaseHelper extends SQLiteOpenHelper { * 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,90 +230,117 @@ public class DatabaseHelper extends SQLiteOpenHelper { 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); + return getValidKeyphraseSoundModelForUser(selectQuery, userHandle); + } + } - 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; - } + /** + * 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); + } + } - 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; - } + private KeyphraseSoundModel getValidKeyphraseSoundModelForUser(String selectQuery, + int userHandle) { + SQLiteDatabase db = getReadableDatabase(); + Cursor c = db.rawQuery(selectQuery, null); - 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; + 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; + } - 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); - } + 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; + } - Keyphrase[] keyphrases = new Keyphrase[1]; - keyphrases[0] = new Keyphrase( - keyphraseId, recognitionModes, modelLocale, text, users); - UUID vendorUuid = null; - if (vendorUuidString != null) { - vendorUuid = UUID.fromString(vendorUuidString); + boolean isAvailableForCurrentUser = false; + for (int user : users) { + if (userHandle == user) { + isAvailableForCurrentUser = true; + break; } - KeyphraseSoundModel model = new KeyphraseSoundModel( - UUID.fromString(modelUuid), vendorUuid, data, keyphrases, version); + } + if (!isAvailableForCurrentUser) { if (DBG) { - Slog.d(TAG, "Found SoundModel for the given keyphrase/locale/user: " - + model); + Slog.w(TAG, "Ignoring SoundModel since user handles don't match"); } - return model; - } while (c.moveToNext()); - } - Slog.w(TAG, "No SoundModel available for the given keyphrase"); - } finally { - c.close(); - db.close(); + 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()); } - return null; + Slog.w(TAG, "No SoundModel available for the given keyphrase"); + } finally { + c.close(); + db.close(); } + + return null; } private static String getCommaSeparatedString(int[] users) { @@ -431,8 +462,11 @@ public class DatabaseHelper extends SQLiteOpenHelper { } } + /** + * 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 506c67e12528..d5eec332cda0 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.pm.UserInfo; 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 com.android.server.wm.ActivityTaskManagerInternal; 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 @@ public class VoiceInteractionManagerService extends SystemService { } //----------------- 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 @@ public class VoiceInteractionManagerService extends SystemService { } } + @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/CallerInfoAsyncQuery.java b/telecomm/java/android/telecom/CallerInfoAsyncQuery.java index 4a50e98e527e..4a81a8eea5cf 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.PhoneNumberUtils; import android.telephony.SubscriptionManager; import android.text.TextUtils; -import dalvik.annotation.compat.UnsupportedAppUsage; - import java.util.ArrayList; import java.util.List; diff --git a/telephony/OWNERS b/telephony/OWNERS index 58a7ea08da3f..628c48070314 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 9b8282806c3c..32f9d53e59f8 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.Manifest.permission; 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.os.UserHandle; 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.annotations.VisibleForTesting; 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 @@ public final class SmsApplication { < android.os.Process.FIRST_APPLICATION_UID) { return contextUserId; } else { - return UserHandle.getUserId(callingUid); + return UserHandle.getUserHandleForUid(callingUid).getIdentifier(); } } @@ -811,10 +809,10 @@ public final class SmsApplication { // 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 12e4b7e26e1e..4a971dd34c8f 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 2836c3075b3b..55087ff0fb1d 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 5be33ed1fac9..24bceb37f590 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 ae447d7a7417..8693385bb032 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 483fa7f9842e..0d6a46a59fcc 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 27da35e2d928..5172b7b67f88 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 7093ac63338c..8fb6a7545abf 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 41662750842f..8c0380f77cdd 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 ebf16ac7e632..320b13ffed2b 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 e108f7600baf..42a89c69e873 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 b561bd4ab3a7..ca4615c2e9fe 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 3c70f86a0890..ebd81afc0173 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 51914e4110b0..f7f285f653b9 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 e24bf21a11b5..b8b212c493aa 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 8551b2f9b693..57141fedf1e0 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 b5244645fda1..3e6218480dc5 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 f48399410723..5340245ae869 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 09b775118dc3..8dd976b2569f 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 8efca0ea3909..fcd5b8ff57a8 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.telephony.TelephonyManager; 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 9d6535c72e90..4e1d7f5775ec 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 e38c62dde622..4ba3c71580e0 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 9696bc259d00..37ccfb9c9b9b 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 03755af4189c..260adfc093f2 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 b85982791ada..779923801bfa 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 c1b7f934c0f7..6e2f2da01791 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 ab5d48a4ce3d..25862e73581e 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 118de465a518..0f9390daa725 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 0e8ec91f4ef6..156c7ad8baac 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 94e38946f632..c380d6b3e30f 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 1ecd1bf93e7f..a4a25d2471ff 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 d030246ed32a..4871434ebc31 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.net.Uri; 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 ef11f469d9a0..93155865c166 100644 --- a/telephony/java/android/service/euicc/EuiccService.java +++ b/telephony/java/android/service/euicc/EuiccService.java @@ -31,7 +31,9 @@ import android.os.RemoteException; 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 @@ public abstract class EuiccService extends Service { 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/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 822e55b29f76..b30f5868cc63 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1454,6 +1454,50 @@ public class CarrierConfigManager { "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 */ @@ -3420,6 +3464,14 @@ public class CarrierConfigManager { "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 @@ public class CarrierConfigManager { 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[]{ @@ -4274,6 +4328,7 @@ public class CarrierConfigManager { // 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/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java index 560c895d7b27..cf8fe6a3c345 100644 --- a/telephony/java/android/telephony/CellIdentityLte.java +++ b/telephony/java/android/telephony/CellIdentityLte.java @@ -209,6 +209,19 @@ public final class CellIdentityLte extends CellIdentity { } /** + * 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. */ diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java index 40927a13919a..d4f181fc735a 100644 --- a/telephony/java/android/telephony/CellIdentityNr.java +++ b/telephony/java/android/telephony/CellIdentityNr.java @@ -23,6 +23,7 @@ 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; @@ -42,7 +43,7 @@ public final class CellIdentityNr extends CellIdentity { private final int mPci; private final int mTac; private final long mNci; - private final int mBand; + private final List<Integer> mBands; // a list of additional PLMN-IDs reported for this cell private final List<String> mAdditionalPlmns; @@ -52,7 +53,7 @@ public final class CellIdentityNr extends CellIdentity { * @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 band Band number defined in 3GPP TS 38.101-1 and TS 38.101-2. + * @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]. @@ -62,28 +63,28 @@ public final class CellIdentityNr extends CellIdentity { * * @hide */ - public CellIdentityNr(int pci, int tac, int nrArfcn, @NgranBand int band, String mccStr, - String mncStr, long nci, String alphal, String alphas, List<String> additionalPlmns) { + 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); - mBand = inRangeOrUnavailable(band, AccessNetworkConstants.NgranBands.BAND_1, - AccessNetworkConstants.NgranBands.BAND_261); + mBands = new ArrayList<>(bands); mNci = inRangeOrUnavailable(nci, 0, MAX_NCI); - mAdditionalPlmns = additionalPlmns; + mAdditionalPlmns = new ArrayList<>(additionalPlmns); } /** @hide */ public CellIdentityNr(android.hardware.radio.V1_4.CellIdentityNr cid) { - this(cid.pci, cid.tac, cid.nrarfcn, 0, cid.mcc, cid.mnc, cid.nci, + 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.band, cid.base.mcc, cid.base.mnc, + 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); } @@ -92,7 +93,7 @@ public final class CellIdentityNr extends CellIdentity { @Override public @NonNull CellIdentityNr sanitizeLocationInfo() { return new CellIdentityNr(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, mNrArfcn, - mBand, mMccStr, mMncStr, CellInfo.UNAVAILABLE, mAlphaLong, mAlphaShort, + mBands, mMccStr, mMncStr, CellInfo.UNAVAILABLE, mAlphaLong, mAlphaShort, mAdditionalPlmns); } @@ -109,7 +110,7 @@ public final class CellIdentityNr extends CellIdentity { @Override public int hashCode() { return Objects.hash(super.hashCode(), mPci, mTac, - mNrArfcn, mBand, mNci, mAdditionalPlmns.hashCode()); + mNrArfcn, mBands.hashCode(), mNci, mAdditionalPlmns.hashCode()); } @Override @@ -120,7 +121,7 @@ public final class CellIdentityNr extends CellIdentity { CellIdentityNr o = (CellIdentityNr) other; return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn - && mBand == o.mBand && mNci == o.mNci + && mBands.equals(o.mBands) && mNci == o.mNci && mAdditionalPlmns.equals(o.mAdditionalPlmns); } @@ -148,16 +149,17 @@ public final class CellIdentityNr extends CellIdentity { } /** - * Get band of the cell + * Get bands of the cell * * Reference: TS 38.101-1 table 5.2-1 * Reference: TS 38.101-2 table 5.2-1 * - * @return band number or {@link CellInfo@UNAVAILABLE} if not available. + * @return List of band number or empty list if not available. */ @NgranBand - public int getBand() { - return mBand; + @NonNull + public List<Integer> getBands() { + return Collections.unmodifiableList(mBands); } /** @@ -205,7 +207,7 @@ public final class CellIdentityNr extends CellIdentity { */ @NonNull public List<String> getAdditionalPlmns() { - return mAdditionalPlmns; + return Collections.unmodifiableList(mAdditionalPlmns); } @Override @@ -214,7 +216,7 @@ public final class CellIdentityNr extends CellIdentity { .append(" mPci = ").append(mPci) .append(" mTac = ").append(mTac) .append(" mNrArfcn = ").append(mNrArfcn) - .append(" mBand = ").append(mBand) + .append(" mBands = ").append(mBands) .append(" mMcc = ").append(mMccStr) .append(" mMnc = ").append(mMncStr) .append(" mNci = ").append(mNci) @@ -231,7 +233,7 @@ public final class CellIdentityNr extends CellIdentity { dest.writeInt(mPci); dest.writeInt(mTac); dest.writeInt(mNrArfcn); - dest.writeInt(mBand); + dest.writeList(mBands); dest.writeLong(mNci); dest.writeList(mAdditionalPlmns); } @@ -242,7 +244,7 @@ public final class CellIdentityNr extends CellIdentity { mPci = in.readInt(); mTac = in.readInt(); mNrArfcn = in.readInt(); - mBand = in.readInt(); + mBands = in.readArrayList(null); mNci = in.readLong(); mAdditionalPlmns = in.readArrayList(null); } diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java index aebe78031ba2..1ba21f29df11 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 @@ public final class ModemActivityInfo implements Parcelable { 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 @@ public final class ModemActivityInfo implements Parcelable { } /** 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 @@ public final class ModemActivityInfo implements Parcelable { 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 @@ public final class ModemActivityInfo implements Parcelable { } /** @hide */ - public void setTransmitTimeMillis(@Nullable int[] txTimeMs) { + public void setTransmitTimeMillis(int[] txTimeMs) { populateTransmitPowerRange(txTimeMs); } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 63a85fa2845c..247ffd7313b9 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -54,6 +54,7 @@ import android.os.Looper; 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; @@ -677,6 +678,13 @@ public class SubscriptionManager { 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. @@ -976,10 +984,7 @@ public class SubscriptionManager { private INetworkPolicyManager getINetworkPolicyManager() { if (mNetworkPolicy == null) { mNetworkPolicy = INetworkPolicyManager.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getNetworkPolicyServiceRegisterer() - .get()); + ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); } return mNetworkPolicy; } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index a1f3cefb59b7..1d89665c1670 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -99,7 +99,6 @@ import com.android.internal.telephony.IOns; 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; @@ -2781,7 +2780,7 @@ public class TelephonyManager { @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); } /** @@ -5624,14 +5623,6 @@ public class TelephonyManager { .getTelephonyServiceManager().getTelephonyServiceRegisterer().get()); } - private ITelephonyRegistry getTelephonyRegistry() { - return ITelephonyRegistry.Stub.asInterface( - TelephonyFrameworkInitializer - .getTelephonyServiceManager() - .getTelephonyRegistryServiceRegisterer() - .get()); - } - private IOns getIOns() { return IOns.Stub.asInterface( TelephonyFrameworkInitializer @@ -5685,29 +5676,27 @@ public class TelephonyManager { */ 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."); - } - } catch (RemoteException ex) { - // system process dead + 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; + } + telephonyRegistry.listenForSubscriber(subId, getOpPackageName(), getFeatureId(), + listener, events, notifyNow); + } else { + Rlog.w(TAG, "telephony registry not ready."); } } @@ -8095,6 +8084,30 @@ public class TelephonyManager { } /** + * 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. @@ -11147,15 +11160,18 @@ public class TelephonyManager { /** * 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(); diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java index d5a48df149f1..7488a1aec0e5 100644 --- a/telephony/java/android/telephony/euicc/EuiccManager.java +++ b/telephony/java/android/telephony/euicc/EuiccManager.java @@ -38,6 +38,9 @@ import com.android.internal.telephony.euicc.IEuiccController; 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 @@ public class EuiccManager { * 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 @@ public class EuiccManager { @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 @@ public class EuiccManager { } /** + * 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 8c9765b4bf70..9c1be48e247a 100644 --- a/telephony/java/android/telephony/ims/ImsCallProfile.java +++ b/telephony/java/android/telephony/ims/ImsCallProfile.java @@ -328,6 +328,14 @@ public final class ImsCallProfile implements Parcelable { @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 2e3f59a13670..72167761c88d 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -21,6 +21,8 @@ import android.annotation.CallbackExecutor; 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.os.RemoteException; 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 @@ import java.util.concurrent.Executor; * @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 @@ public class RcsUceAdapter { * 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 @@ public class RcsUceAdapter { /** * Not to be instantiated directly, use * {@link ImsRcsManager#getUceAdapter()} to instantiate this manager class. + * @hide */ RcsUceAdapter(int subId) { mSubId = subId; @@ -219,6 +226,7 @@ public class RcsUceAdapter { * {@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 @@ public class RcsUceAdapter { * {@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 @@ public class RcsUceAdapter { * 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 3ec4f3468497..f13371c1d0fa 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 @@ public class ImsUtImplBase { 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 @@ public class ImsUtImplBase { } /** + * 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 15f837189843..4a5380e4551b 100644 --- a/telephony/java/com/android/ims/ImsUtInterface.java +++ b/telephony/java/com/android/ims/ImsUtInterface.java @@ -166,6 +166,12 @@ public interface ImsUtInterface { 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 4f97cc5cfb22..302be65070f7 100644 --- a/telephony/java/com/android/ims/internal/IImsUt.aidl +++ b/telephony/java/com/android/ims/internal/IImsUt.aidl @@ -122,4 +122,10 @@ interface IImsUt { */ 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 0bc6640a8331..6aa5a52d55d5 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -2197,4 +2197,13 @@ interface ITelephony { * 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/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index 0db86d6054b3..9ac8cb136c6b 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -480,6 +480,7 @@ public interface RILConstants { 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 b697d5835b09..48f785091764 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -212,37 +212,6 @@ public class TelephonyIntents { 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/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl index 7422863d862c..35e8a12e898a 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.content.Intent; 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 @@ interface IEuiccController { 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/test-mock/api/test-current.txt b/test-mock/api/test-current.txt index cc260ac14147..32ca250b6c74 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 9d913b9861e5..36074edd187e 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 @@ public class MockContext extends Context { } @Override + public @NonNull Context createWindowContext(int type) { + throw new UnsupportedOperationException(); + } + + @Override public boolean isRestricted() { throw new UnsupportedOperationException(); } @@ -821,7 +827,6 @@ public class MockContext extends Context { 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 e7a63e414172..78850c534596 100644 --- a/tests/ApkVerityTest/block_device_writer/Android.bp +++ b/tests/ApkVerityTest/block_device_writer/Android.bp @@ -30,7 +30,23 @@ cc_test { "-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/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java index 65a3d8a337db..c900eaedbdae 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.LargeTest; 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 @@ public class SoundTriggerTest extends InstrumentationTestCase { @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 @@ public class SoundTriggerTest extends InstrumentationTestCase { @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 @@ public class SoundTriggerTest extends InstrumentationTestCase { @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 @@ public class SoundTriggerTest extends InstrumentationTestCase { @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 @@ public class SoundTriggerTest extends InstrumentationTestCase { @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 @@ public class SoundTriggerTest extends InstrumentationTestCase { @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 6687f83ad0db..61696718c76e 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 @@ public class SurfaceControlViewHostTest extends Activity implements SurfaceHolde 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 @@ public class SurfaceControlViewHostTest extends Activity implements SurfaceHolde } @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 54c944f9588e..b357ad076c11 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.util.Log; 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 @@ public class TestEnrollmentActivity extends Activity { * 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 f3c89d8addf6..01e212d01574 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.animation.ValueAnimator; 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 @@ public class WindowInsetsActivity extends Activity { } }; - float showY; - float hideY; + float startY; + float endY; InsetsAnimation imeAnim; @Override @@ -84,16 +85,6 @@ public class WindowInsetsActivity extends Activity { 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 @@ public class WindowInsetsActivity extends Activity { 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 b2e573b6c74b..096f7bdca088 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.assertNotEquals; 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 @@ public class LinkAddressTest { 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 @@ public class LinkAddressTest { (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 000000000000..065add4fc253 --- /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/wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl b/wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl index 51d74f0fcfa9..d14ec57ea07a 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 @@ oneway interface INetworkRequestMatchCallback 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 d13a625963b6..31b3a5064932 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.ISuggestionConnectionStatusListener; 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 @@ interface IWifiManager 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,15 +89,19 @@ interface IWifiManager boolean disableNetwork(int netId, String packageName); + void allowAutojoinGlobal(boolean choice); + void allowAutojoin(int netId, boolean choice); void allowAutojoinPasspoint(String fqdn, boolean enableAutoJoin); void setMacRandomizationSettingPasspointEnabled(String fqdn, boolean enable); + void setMeteredOverridePasspoint(String fqdn, int meteredOverride); + 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 +184,6 @@ interface IWifiManager int getVerboseLoggingLevel(); - void enableWifiConnectivityManager(boolean enabled); - void disableEphemeralNetwork(String SSID, String packageName); void factoryReset(String packageName); @@ -255,7 +258,7 @@ interface IWifiManager 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); diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index 5edcc2df3804..04016b606b96 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -856,11 +856,11 @@ public class WifiEnterpriseConfig implements Parcelable { * 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 @@ public class WifiEnterpriseConfig implements Parcelable { * <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 @@ public class WifiEnterpriseConfig implements Parcelable { * 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 @@ public class WifiEnterpriseConfig implements Parcelable { * @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/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index f6e3ff0ff02c..fb3e794d92d1 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -22,6 +22,7 @@ import static android.Manifest.permission.READ_WIFI_CREDENTIAL; 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 @@ public class WifiManager { } /** - * 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 @@ public class WifiManager { return (getSupportedFeatures() & feature) == feature; } - /** + /** * @return true if this adapter supports Passpoint * @hide */ @@ -2905,6 +2894,7 @@ public class WifiManager { * [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); @@ -2917,6 +2907,7 @@ public class WifiManager { * 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); } @@ -3716,18 +3707,6 @@ public class WifiManager { } /** - * 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 @@ -4299,6 +4278,23 @@ public class WifiManager { } /** + * 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. @@ -4353,6 +4349,25 @@ public class WifiManager { } /** + * Sets the user's choice of metered override for a Passpoint profile. + * + * @param fqdn the FQDN (fully qualified domain name) of the passpoint profile. + * @param meteredOverride One of three values: {@link WifiConfiguration#METERED_OVERRIDE_NONE}, + * {@link WifiConfiguration#METERED_OVERRIDE_METERED}, + * {@link WifiConfiguration#METERED_OVERRIDE_NOT_METERED} + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + public void setMeteredOverridePasspoint(@NonNull String fqdn, int meteredOverride) { + try { + mService.setMeteredOverridePasspoint(fqdn, meteredOverride); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Disable an ephemeral network. * * @param ssid in the format of WifiConfiguration's SSID. @@ -4925,18 +4940,6 @@ public class WifiManager { } /** - * 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[])}. @@ -5144,18 +5147,6 @@ public class WifiManager { } /** - * 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/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java index 7c335fc323f5..3a0d080594c8 100644 --- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java +++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java @@ -16,6 +16,9 @@ package android.net.wifi.hotspot2; +import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_NONE; +import static android.net.wifi.WifiConfiguration.MeteredOverride; + import android.annotation.Nullable; import android.annotation.SystemApi; import android.net.wifi.hotspot2.pps.Credential; @@ -438,6 +441,18 @@ public final class PasspointConfiguration implements Parcelable { private boolean mIsMacRandomizationEnabled = true; /** + * Indicates if the end user has expressed an explicit opinion about the + * meteredness of this network, such as through the Settings app. + * This value is one of {@link #METERED_OVERRIDE_NONE}, {@link #METERED_OVERRIDE_METERED}, + * or {@link #METERED_OVERRIDE_NOT_METERED}. + * <p> + * This should always override any values from {@link WifiInfo#getMeteredHint()}. + * + * By default this field is set to {@link #METERED_OVERRIDE_NONE}. + */ + private int mMeteredOverride = METERED_OVERRIDE_NONE; + + /** * Configures the auto-association status of this Passpoint configuration. A value of true * indicates that the configuration will be considered for auto-connection, a value of false * indicates that only manual connection will work - the framework will not auto-associate to @@ -463,6 +478,16 @@ public final class PasspointConfiguration implements Parcelable { } /** + * Sets the metered override setting for this Passpoint configuration. + * + * @param meteredOverride One of the values in {@link MeteredOverride} + * @hide + */ + public void setMeteredOverride(@MeteredOverride int meteredOverride) { + mMeteredOverride = meteredOverride; + } + + /** * Indicates whether the Passpoint configuration may be auto-connected to by the framework. A * value of true indicates that auto-connection can happen, a value of false indicates that it * cannot. However, even when auto-connection is not possible manual connection by the user is @@ -478,6 +503,18 @@ public final class PasspointConfiguration implements Parcelable { } /** + * Indicates whether the user chose this configuration to be treated as metered or not. + * + * @return One of the values in {@link MeteredOverride} + * @hide + */ + @SystemApi + @MeteredOverride + public int getMeteredOverride() { + return mMeteredOverride; + } + + /** * Indicates whether a randomized MAC address or device MAC address will be used for * connections to this Passpoint network. If true, a randomized MAC address will be used. * Otherwise, the device MAC address will be used. @@ -534,6 +571,7 @@ public final class PasspointConfiguration implements Parcelable { mCarrierId = source.mCarrierId; mIsAutoJoinEnabled = source.mIsAutoJoinEnabled; mIsMacRandomizationEnabled = source.mIsMacRandomizationEnabled; + mMeteredOverride = source.mMeteredOverride; } @Override @@ -565,6 +603,7 @@ public final class PasspointConfiguration implements Parcelable { dest.writeInt(mCarrierId); dest.writeBoolean(mIsAutoJoinEnabled); dest.writeBoolean(mIsMacRandomizationEnabled); + dest.writeInt(mMeteredOverride); } @Override @@ -597,6 +636,7 @@ public final class PasspointConfiguration implements Parcelable { && mCarrierId == that.mCarrierId && mIsAutoJoinEnabled == that.mIsAutoJoinEnabled && mIsMacRandomizationEnabled == that.mIsMacRandomizationEnabled + && mMeteredOverride == that.mMeteredOverride && (mServiceFriendlyNames == null ? that.mServiceFriendlyNames == null : mServiceFriendlyNames.equals(that.mServiceFriendlyNames)); } @@ -607,7 +647,8 @@ public final class PasspointConfiguration implements Parcelable { mUpdateIdentifier, mCredentialPriority, mSubscriptionCreationTimeInMillis, mSubscriptionExpirationTimeInMillis, mUsageLimitUsageTimePeriodInMinutes, mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes, - mServiceFriendlyNames, mCarrierId, mIsAutoJoinEnabled, mIsMacRandomizationEnabled); + mServiceFriendlyNames, mCarrierId, mIsAutoJoinEnabled, mIsMacRandomizationEnabled, + mMeteredOverride); } @Override @@ -663,6 +704,7 @@ public final class PasspointConfiguration implements Parcelable { builder.append("CarrierId:" + mCarrierId); builder.append("IsAutoJoinEnabled:" + mIsAutoJoinEnabled); builder.append("mIsMacRandomizationEnabled:" + mIsMacRandomizationEnabled); + builder.append("mMeteredOverride:" + mMeteredOverride); return builder.toString(); } @@ -770,6 +812,7 @@ public final class PasspointConfiguration implements Parcelable { config.mCarrierId = in.readInt(); config.mIsAutoJoinEnabled = in.readBoolean(); config.mIsMacRandomizationEnabled = in.readBoolean(); + config.mMeteredOverride = in.readInt(); return config; } diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java index 8fa9c3d6f1a6..9562f95ac162 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java @@ -66,12 +66,20 @@ public class WifiP2pConfig implements Parcelable { /** @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 19d09d1f2a9c..b46755349ad7 100644 --- a/wifi/java/com/android/server/wifi/BaseWifiService.java +++ b/wifi/java/com/android/server/wifi/BaseWifiService.java @@ -178,6 +178,11 @@ public class BaseWifiService extends IWifiManager.Stub { } @Override + public void allowAutojoinGlobal(boolean choice) { + throw new UnsupportedOperationException(); + } + + @Override public void allowAutojoin(int netId, boolean choice) { throw new UnsupportedOperationException(); } @@ -193,6 +198,11 @@ public class BaseWifiService extends IWifiManager.Stub { } @Override + public void setMeteredOverridePasspoint(String fqdn, int meteredOverride) { + throw new UnsupportedOperationException(); + } + + @Override public boolean startScan(String packageName, String featureId) { throw new UnsupportedOperationException(); } @@ -404,7 +414,8 @@ public class BaseWifiService extends IWifiManager.Stub { throw new UnsupportedOperationException(); } - @Override + /** @deprecated use {@link #allowAutojoinGlobal(boolean)} instead */ + @Deprecated public void enableWifiConnectivityManager(boolean enabled) { throw new UnsupportedOperationException(); } diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index f369203e05ab..0c2876e340c1 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -16,6 +16,7 @@ package android.net.wifi; +import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_METERED; import static android.net.wifi.WifiManager.ActionListener; import static android.net.wifi.WifiManager.BUSY; import static android.net.wifi.WifiManager.ERROR; @@ -881,17 +882,6 @@ public class WifiManagerTest { } /** - * 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 +1379,10 @@ public class WifiManagerTest { @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 +1463,11 @@ public class WifiManagerTest { @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 +1519,11 @@ public class WifiManagerTest { @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()); @@ -1808,6 +1797,16 @@ public class WifiManagerTest { verify(mWifiService).setMacRandomizationSettingPasspointEnabled(fqdn, true); } + /** + * Test behavior of + * {@link WifiManager#setMacRandomizationSettingPasspointEnabled(String, boolean)} + */ + @Test + public void testSetMeteredOverridePasspoint() throws Exception { + final String fqdn = "FullyQualifiedDomainName"; + mWifiManager.setMeteredOverridePasspoint(fqdn, METERED_OVERRIDE_METERED); + verify(mWifiService).setMeteredOverridePasspoint(fqdn, METERED_OVERRIDE_METERED); + } /** * Test behavior of {@link WifiManager#disconnect()} diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java index 603e78b90ff2..654154d77b0d 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java @@ -16,6 +16,8 @@ package android.net.wifi.hotspot2; +import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_NONE; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -173,6 +175,7 @@ public class PasspointConfigurationTest { assertFalse(config.validateForR2()); assertTrue(config.isAutoJoinEnabled()); assertTrue(config.isMacRandomizationEnabled()); + assertTrue(config.getMeteredOverride() == METERED_OVERRIDE_NONE); } /** |