diff options
887 files changed, 35991 insertions, 7692 deletions
diff --git a/Android.bp b/Android.bp index 1865a3d873ca..3ecb5a9340d2 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 @@ -416,6 +412,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 +541,6 @@ java_library { java_library { name: "framework-annotation-proc", - defaults: ["framework-aidl-export-defaults"], srcs: [":framework-all-sources"], libs: [ "app-compat-annotations", @@ -623,6 +624,7 @@ gensrcs { "&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)", srcs: [ + ":ipconnectivity-proto-src", "core/proto/**/*.proto", "libs/incident/**/*.proto", ], @@ -712,7 +714,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 @@ -737,6 +742,7 @@ java_library { java_library_host { name: "platformprotos", srcs: [ + ":ipconnectivity-proto-src", "cmds/am/proto/instrumentation_data.proto", "cmds/statsd/src/**/*.proto", "core/proto/**/*.proto", @@ -765,6 +771,7 @@ java_library { ], sdk_version: "9", srcs: [ + ":ipconnectivity-proto-src", "core/proto/**/*.proto", "libs/incident/proto/android/os/**/*.proto", ], @@ -779,6 +786,7 @@ java_library { }, srcs: [ + ":ipconnectivity-proto-src", "core/proto/**/*.proto", "libs/incident/proto/android/os/**/*.proto", ], @@ -809,6 +817,7 @@ cc_defaults { ], srcs: [ + ":ipconnectivity-proto-src", "core/proto/**/*.proto", ], } @@ -1309,6 +1318,7 @@ java_library { libs: [ "framework-minus-apex", "unsupportedappusage", + "ike-stubs", ], static_libs: [ "libphonenumber-platform", 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/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java b/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java index 02a79a11032f..d07ef4bc3bf5 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java @@ -25,6 +25,7 @@ import android.util.SparseArray; import com.google.android.icing.proto.DocumentProto; import com.google.android.icing.proto.PropertyProto; import com.google.android.icing.proto.SearchResultProto; +import com.google.android.icing.proto.StatusProto; import java.util.Locale; import java.util.Map; @@ -97,10 +98,12 @@ public class FakeIcing { public SearchResultProto query(@NonNull String term) { String normTerm = normalizeString(term); Set<Integer> docIds = mIndex.get(normTerm); + SearchResultProto.Builder results = SearchResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.OK)); if (docIds == null || docIds.isEmpty()) { - return SearchResultProto.getDefaultInstance(); + return results.build(); } - SearchResultProto.Builder results = SearchResultProto.newBuilder(); + for (int docId : docIds) { DocumentProto document = mDocStore.get(docId); if (document != null) { diff --git a/apex/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/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java index ef1351e6d597..b96161aba758 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java @@ -48,6 +48,13 @@ public class JobParameters implements Parcelable { public static final int REASON_DEVICE_IDLE = JobProtoEnums.STOP_REASON_DEVICE_IDLE; // 4. /** @hide */ public static final int REASON_DEVICE_THERMAL = JobProtoEnums.STOP_REASON_DEVICE_THERMAL; // 5. + /** + * The job is in the {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} + * bucket. + * + * @hide + */ + public static final int REASON_RESTRAINED = JobProtoEnums.STOP_REASON_RESTRAINED; // 6. /** * All the stop reason codes. This should be regarded as an immutable array at runtime. @@ -65,6 +72,7 @@ public class JobParameters implements Parcelable { REASON_TIMEOUT, REASON_DEVICE_IDLE, REASON_DEVICE_THERMAL, + REASON_RESTRAINED, }; /** @@ -80,6 +88,7 @@ public class JobParameters implements Parcelable { case REASON_TIMEOUT: return "timeout"; case REASON_DEVICE_IDLE: return "device_idle"; case REASON_DEVICE_THERMAL: return "thermal"; + case REASON_RESTRAINED: return "restrained"; default: return "unknown:" + reason; } } diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java index 6109b713de24..d2d942a4a7e5 100644 --- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java @@ -103,6 +103,8 @@ public interface AppStandbyInternal { /** * Changes an app's standby bucket to the provided value. The caller can only set the standby * bucket for a different app than itself. + * If attempting to automatically place an app in the RESTRICTED bucket, use + * {@link #restrictApp(String, int, int)} instead. */ void setAppStandbyBucket(@NonNull String packageName, int bucket, int userId, int callingUid, int callingPid); @@ -113,6 +115,17 @@ public interface AppStandbyInternal { void setAppStandbyBuckets(@NonNull List<AppStandbyInfo> appBuckets, int userId, int callingUid, int callingPid); + /** + * Put the specified app in the + * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} + * bucket. If it has been used by the user recently, the restriction will delayed until an + * appropriate time. + * + * @param restrictReason The restrictReason for restricting the app. Should be one of the + * UsageStatsManager.REASON_SUB_RESTRICT_* reasons. + */ + void restrictApp(@NonNull String packageName, int userId, int restrictReason); + void addActiveDeviceAdmin(String adminPkg, int userId); void setActiveAdminApps(Set<String> adminPkgs, int userId); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index 102e8485aac5..ed5626a1acbc 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -101,6 +101,7 @@ import com.android.server.job.controllers.DeviceIdleJobsController; import com.android.server.job.controllers.IdleController; import com.android.server.job.controllers.JobStatus; import com.android.server.job.controllers.QuotaController; +import com.android.server.job.controllers.RestrictingController; import com.android.server.job.controllers.StateController; import com.android.server.job.controllers.StorageController; import com.android.server.job.controllers.TimeController; @@ -241,6 +242,11 @@ public class JobSchedulerService extends com.android.server.SystemService /** List of controllers that will notify this service of updates to jobs. */ final List<StateController> mControllers; + /** + * List of controllers that will apply to all jobs in the RESTRICTED bucket. This is a subset of + * {@link #mControllers}. + */ + private final List<RestrictingController> mRestrictiveControllers; /** Need direct access to this for testing. */ private final BatteryController mBatteryController; /** Need direct access to this for testing. */ @@ -277,6 +283,7 @@ public class JobSchedulerService extends com.android.server.SystemService DeviceIdleInternal mLocalDeviceIdleController; AppStateTracker mAppStateTracker; final UsageStatsManagerInternal mUsageStats; + private final AppStandbyInternal mAppStandbyInternal; /** * Set to true once we are allowed to run third party apps. @@ -312,6 +319,9 @@ public class JobSchedulerService extends com.android.server.SystemService public static final int FREQUENT_INDEX = 2; public static final int RARE_INDEX = 3; public static final int NEVER_INDEX = 4; + // Putting RESTRICTED_INDEX after NEVER_INDEX to make it easier for proto dumping + // (ScheduledJobStateChanged and JobStatusDumpProto). + public static final int RESTRICTED_INDEX = 5; // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked -- @@ -1062,7 +1072,8 @@ public class JobSchedulerService extends com.android.server.SystemService packageName == null ? job.getService().getPackageName() : packageName; if (!mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG)) { Slog.e(TAG, userId + "-" + pkg + " has called schedule() too many times"); - // TODO(b/145551233): attempt to restrict app + mAppStandbyInternal.restrictApp( + pkg, userId, UsageStatsManager.REASON_SUB_RESTRICT_BUGGY); if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION && mPlatformCompat.isChangeEnabledByPackageName( CRASH_ON_EXCEEDED_LIMIT, pkg, userId)) { @@ -1365,6 +1376,40 @@ public class JobSchedulerService extends com.android.server.SystemService } } + @Override + public void onRestrictedBucketChanged(List<JobStatus> jobs) { + final int len = jobs.size(); + if (len == 0) { + Slog.wtf(TAG, "onRestrictedBucketChanged called with no jobs"); + return; + } + synchronized (mLock) { + for (int i = 0; i < len; ++i) { + JobStatus js = jobs.get(i); + for (int j = mRestrictiveControllers.size() - 1; j >= 0; --j) { + // Effective standby bucket can change after this in some situations so use + // the real bucket so that the job is tracked by the controllers. + if (js.getStandbyBucket() == RESTRICTED_INDEX) { + js.addDynamicConstraint(JobStatus.CONSTRAINT_BATTERY_NOT_LOW); + js.addDynamicConstraint(JobStatus.CONSTRAINT_CHARGING); + js.addDynamicConstraint(JobStatus.CONSTRAINT_CONNECTIVITY); + js.addDynamicConstraint(JobStatus.CONSTRAINT_IDLE); + + mRestrictiveControllers.get(j).startTrackingRestrictedJobLocked(js); + } else { + js.removeDynamicConstraint(JobStatus.CONSTRAINT_BATTERY_NOT_LOW); + js.removeDynamicConstraint(JobStatus.CONSTRAINT_CHARGING); + js.removeDynamicConstraint(JobStatus.CONSTRAINT_CONNECTIVITY); + js.removeDynamicConstraint(JobStatus.CONSTRAINT_IDLE); + + mRestrictiveControllers.get(j).stopTrackingRestrictedJobLocked(js); + } + } + } + } + mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); + } + void reportActiveLocked() { // active is true if pending queue contains jobs OR some job is running. boolean active = mPendingJobs.size() > 0; @@ -1430,8 +1475,8 @@ public class JobSchedulerService extends com.android.server.SystemService mConstants.API_QUOTA_SCHEDULE_COUNT, mConstants.API_QUOTA_SCHEDULE_WINDOW_MS); - AppStandbyInternal appStandby = LocalServices.getService(AppStandbyInternal.class); - appStandby.addListener(mStandbyTracker); + mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class); + mAppStandbyInternal.addListener(mStandbyTracker); // The job store needs to call back publishLocalService(JobSchedulerInternal.class, new LocalService()); @@ -1441,9 +1486,11 @@ public class JobSchedulerService extends com.android.server.SystemService // Create the controllers. mControllers = new ArrayList<StateController>(); - mControllers.add(new ConnectivityController(this)); + final ConnectivityController connectivityController = new ConnectivityController(this); + mControllers.add(connectivityController); mControllers.add(new TimeController(this)); - mControllers.add(new IdleController(this)); + final IdleController idleController = new IdleController(this); + mControllers.add(idleController); mBatteryController = new BatteryController(this); mControllers.add(mBatteryController); mStorageController = new StorageController(this); @@ -1455,6 +1502,11 @@ public class JobSchedulerService extends com.android.server.SystemService mQuotaController = new QuotaController(this); mControllers.add(mQuotaController); + mRestrictiveControllers = new ArrayList<>(); + mRestrictiveControllers.add(mBatteryController); + mRestrictiveControllers.add(connectivityController); + mRestrictiveControllers.add(idleController); + // Create restrictions mJobRestrictions = new ArrayList<>(); mJobRestrictions.add(new ThermalStatusRestriction(this)); @@ -2125,11 +2177,13 @@ public class JobSchedulerService extends com.android.server.SystemService } } catch (RemoteException e) { } - if (mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT > 1 + // Restricted jobs must always be batched + if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX + || (mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT > 1 && job.getEffectiveStandbyBucket() != ACTIVE_INDEX && (job.getFirstForceBatchedTimeElapsed() == 0 || sElapsedRealtimeClock.millis() - job.getFirstForceBatchedTimeElapsed() - < mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS)) { + < mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS))) { // Force batching non-ACTIVE jobs. Don't include them in the other counts. forceBatchedCount++; if (job.getFirstForceBatchedTimeElapsed() == 0) { @@ -2536,11 +2590,19 @@ public class JobSchedulerService extends com.android.server.SystemService public static int standbyBucketToBucketIndex(int bucket) { // Normalize AppStandby constants to indices into our bookkeeping - if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) return NEVER_INDEX; - else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) return RARE_INDEX; - else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) return FREQUENT_INDEX; - else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) return WORKING_INDEX; - else return ACTIVE_INDEX; + if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) { + return NEVER_INDEX; + } else if (bucket > UsageStatsManager.STANDBY_BUCKET_RARE) { + return RESTRICTED_INDEX; + } else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) { + return RARE_INDEX; + } else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) { + return FREQUENT_INDEX; + } else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) { + return WORKING_INDEX; + } else { + return ACTIVE_INDEX; + } } // Static to support external callers diff --git a/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java b/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java index 87bfc27a715f..cb3c43714111 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java +++ b/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java @@ -16,8 +16,12 @@ package com.android.server.job; +import android.annotation.NonNull; + import com.android.server.job.controllers.JobStatus; +import java.util.List; + /** * Interface through which a {@link com.android.server.job.controllers.StateController} informs * the {@link com.android.server.job.JobSchedulerService} that there are some tasks potentially @@ -39,4 +43,10 @@ public interface StateChangedListener { public void onRunJobNow(JobStatus jobStatus); public void onDeviceIdleStateChanged(boolean deviceIdle); + + /** + * Called when these jobs are added or removed from the + * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket. + */ + void onRestrictedBucketChanged(@NonNull List<JobStatus> jobs); } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java index 46658ad33b85..461ef21af7ee 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java @@ -43,7 +43,7 @@ import java.util.function.Predicate; * be charging when it's been plugged in for more than two minutes, and the system has broadcast * ACTION_BATTERY_OK. */ -public final class BatteryController extends StateController { +public final class BatteryController extends RestrictingController { private static final String TAG = "JobScheduler.Battery"; private static final boolean DEBUG = JobSchedulerService.DEBUG || Log.isLoggable(TAG, Log.DEBUG); @@ -73,12 +73,24 @@ public final class BatteryController extends StateController { } @Override + public void startTrackingRestrictedJobLocked(JobStatus jobStatus) { + maybeStartTrackingJobLocked(jobStatus, null); + } + + @Override public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) { if (taskStatus.clearTrackingController(JobStatus.TRACKING_BATTERY)) { mTrackedTasks.remove(taskStatus); } } + @Override + public void stopTrackingRestrictedJobLocked(JobStatus jobStatus) { + if (!jobStatus.hasPowerConstraint()) { + maybeStopTrackingJobLocked(jobStatus, null, false); + } + } + private void maybeReportNewChargingStateLocked() { final boolean stablePower = mChargeTracker.isOnStablePower(); final boolean batteryNotLow = mChargeTracker.isBatteryNotLow(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index 8eeea1ba17e2..a0e83daf877d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java @@ -20,6 +20,8 @@ import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; +import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; + import android.app.job.JobInfo; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; @@ -63,7 +65,7 @@ import java.util.function.Predicate; * * Test: atest com.android.server.job.controllers.ConnectivityControllerTest */ -public final class ConnectivityController extends StateController implements +public final class ConnectivityController extends RestrictingController implements ConnectivityManager.OnNetworkActiveListener { private static final String TAG = "JobScheduler.Connectivity"; private static final boolean DEBUG = JobSchedulerService.DEBUG @@ -138,8 +140,22 @@ public final class ConnectivityController extends StateController implements } } + @Override + public void startTrackingRestrictedJobLocked(JobStatus jobStatus) { + // Don't need to start tracking the job. If the job needed network, it would already be + // tracked. + updateConstraintsSatisfied(jobStatus); + } + + @Override + public void stopTrackingRestrictedJobLocked(JobStatus jobStatus) { + // Shouldn't stop tracking the job here. If the job was tracked, it still needs network, + // even after being unrestricted. + updateConstraintsSatisfied(jobStatus); + } + /** - * Returns true if the job's requested network is available. This DOES NOT necesarilly mean + * Returns true if the job's requested network is available. This DOES NOT necessarily mean * that the UID has been granted access to the network. */ public boolean isNetworkAvailable(JobStatus job) { @@ -353,14 +369,24 @@ public final class ConnectivityController extends StateController implements private static boolean isStrictSatisfied(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants) { - return jobStatus.getJob().getRequiredNetwork().networkCapabilities - .satisfiedByNetworkCapabilities(capabilities); + final NetworkCapabilities required; + // A restricted job that's out of quota MUST use an unmetered network. + if (jobStatus.getEffectiveStandbyBucket() == RESTRICTED_INDEX + && !jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) { + required = new NetworkCapabilities( + jobStatus.getJob().getRequiredNetwork().networkCapabilities) + .addCapability(NET_CAPABILITY_NOT_METERED); + } else { + required = jobStatus.getJob().getRequiredNetwork().networkCapabilities; + } + + return required.satisfiedByNetworkCapabilities(capabilities); } private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants) { - // Only consider doing this for prefetching jobs - if (!jobStatus.getJob().isPrefetch()) { + // Only consider doing this for unrestricted prefetching jobs + if (!jobStatus.getJob().isPrefetch() || jobStatus.getStandbyBucket() == RESTRICTED_INDEX) { return false; } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java index d355715920b6..c0b3204192d6 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java @@ -32,7 +32,15 @@ import com.android.server.job.controllers.idle.IdlenessTracker; import java.util.function.Predicate; -public final class IdleController extends StateController implements IdlenessListener { +/** + * Simple controller that tracks whether the device is idle or not. Idleness depends on the device + * type and is not related to device-idle (Doze mode) despite the similar naming. + * + * @see CarIdlenessTracker + * @see DeviceIdlenessTracker + * @see IdlenessTracker + */ +public final class IdleController extends RestrictingController implements IdlenessListener { private static final String TAG = "JobScheduler.IdleController"; // Policy: we decide that we're "idle" if the device has been unused / // screen off or dreaming or wireless charging dock idle for at least this long @@ -57,6 +65,11 @@ public final class IdleController extends StateController implements IdlenessLis } @Override + public void startTrackingRestrictedJobLocked(JobStatus jobStatus) { + maybeStartTrackingJobLocked(jobStatus, null); + } + + @Override public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) { if (taskStatus.clearTrackingController(JobStatus.TRACKING_IDLE)) { @@ -64,6 +77,13 @@ public final class IdleController extends StateController implements IdlenessLis } } + @Override + public void stopTrackingRestrictedJobLocked(JobStatus jobStatus) { + if (!jobStatus.hasIdleConstraint()) { + maybeStopTrackingJobLocked(jobStatus, null, false); + } + } + /** * State-change notifications from the idleness tracker */ diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index a8d8bd9b2621..dbdce70dda03 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -69,13 +69,14 @@ public final class JobStatus { public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE; public static final long NO_EARLIEST_RUNTIME = 0L; - static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0 - static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE; // 1 << 2 - static final int CONSTRAINT_BATTERY_NOT_LOW = JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1 + public static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0 + public static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE; // 1 << 2 + public static final int CONSTRAINT_BATTERY_NOT_LOW = + JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1 static final int CONSTRAINT_STORAGE_NOT_LOW = JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW; // 1 << 3 static final int CONSTRAINT_TIMING_DELAY = 1<<31; static final int CONSTRAINT_DEADLINE = 1<<30; - static final int CONSTRAINT_CONNECTIVITY = 1<<28; + public static final int CONSTRAINT_CONNECTIVITY = 1 << 28; static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26; static final int CONSTRAINT_DEVICE_NOT_DOZING = 1 << 25; // Implicit constraint static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24; // Implicit constraint @@ -117,7 +118,7 @@ public final class JobStatus { /** The minimum possible update delay is 1/2 second. */ public static final long MIN_TRIGGER_UPDATE_DELAY = 500; - /** If not specified, trigger maxumum delay is 2 minutes. */ + /** If not specified, trigger maximum delay is 2 minutes. */ public static final long DEFAULT_TRIGGER_MAX_DELAY = 2*60*1000; /** The minimum possible update delay is 1 second. */ @@ -188,6 +189,11 @@ public final class JobStatus { private final int mRequiredConstraintsOfInterest; int satisfiedConstraints = 0; private int mSatisfiedConstraintsOfInterest = 0; + /** + * Set of constraints that must be satisfied for the job if/because it's in the RESTRICTED + * bucket. + */ + private int mDynamicConstraints = 0; // Set to true if doze constraint was satisfied due to app being whitelisted. public boolean dozeWhitelisted; @@ -328,6 +334,9 @@ public final class JobStatus { /** The job is within its quota based on its standby bucket. */ private boolean mReadyWithinQuota; + /** The job's dynamic requirements have been satisfied. */ + private boolean mReadyDynamicSatisfied; + /** Provide a handle to the service that this job will be run on. */ public int getServiceToken() { return callingUid; @@ -410,6 +419,7 @@ public final class JobStatus { this.requiredConstraints = requiredConstraints; mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST; mReadyNotDozing = (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0; + mReadyDynamicSatisfied = true; mLastSuccessfulRunTime = lastSuccessfulRunTime; mLastFailedRunTime = lastFailedRunTime; @@ -830,41 +840,54 @@ public final class JobStatus { /** Does this job have any sort of networking constraint? */ public boolean hasConnectivityConstraint() { + // No need to check mDynamicConstraints since connectivity will only be in that list if + // it's already in the requiredConstraints list. return (requiredConstraints&CONSTRAINT_CONNECTIVITY) != 0; } public boolean hasChargingConstraint() { - return (requiredConstraints&CONSTRAINT_CHARGING) != 0; + return hasConstraint(CONSTRAINT_CHARGING); } public boolean hasBatteryNotLowConstraint() { - return (requiredConstraints&CONSTRAINT_BATTERY_NOT_LOW) != 0; + return hasConstraint(CONSTRAINT_BATTERY_NOT_LOW); } - public boolean hasPowerConstraint() { - return (requiredConstraints&(CONSTRAINT_CHARGING|CONSTRAINT_BATTERY_NOT_LOW)) != 0; + /** Returns true if the job requires charging OR battery not low. */ + boolean hasPowerConstraint() { + return hasConstraint(CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW); } public boolean hasStorageNotLowConstraint() { - return (requiredConstraints&CONSTRAINT_STORAGE_NOT_LOW) != 0; + return hasConstraint(CONSTRAINT_STORAGE_NOT_LOW); } public boolean hasTimingDelayConstraint() { - return (requiredConstraints&CONSTRAINT_TIMING_DELAY) != 0; + return hasConstraint(CONSTRAINT_TIMING_DELAY); } public boolean hasDeadlineConstraint() { - return (requiredConstraints&CONSTRAINT_DEADLINE) != 0; + return hasConstraint(CONSTRAINT_DEADLINE); } public boolean hasIdleConstraint() { - return (requiredConstraints&CONSTRAINT_IDLE) != 0; + return hasConstraint(CONSTRAINT_IDLE); } public boolean hasContentTriggerConstraint() { + // No need to check mDynamicConstraints since content trigger will only be in that list if + // it's already in the requiredConstraints list. return (requiredConstraints&CONSTRAINT_CONTENT_TRIGGER) != 0; } + /** + * Checks both {@link #requiredConstraints} and {@link #mDynamicConstraints} to see if this job + * requires the specified constraint. + */ + private boolean hasConstraint(int constraint) { + return (requiredConstraints & constraint) != 0 || (mDynamicConstraints & constraint) != 0; + } + public long getTriggerContentUpdateDelay() { long time = job.getTriggerContentUpdateDelay(); if (time < 0) { @@ -1033,6 +1056,8 @@ public final class JobStatus { } satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0); mSatisfiedConstraintsOfInterest = satisfiedConstraints & CONSTRAINTS_OF_INTEREST; + mReadyDynamicSatisfied = + mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints); if (STATS_LOG_ENABLED && (STATSD_CONSTRAINTS_TO_LOG & constraint) != 0) { StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED, sourceUid, null, getBatteryName(), getProtoConstraint(constraint), @@ -1058,6 +1083,43 @@ public final class JobStatus { trackingControllers |= which; } + /** + * Indicates that this job cannot run without the specified constraint. This is evaluated + * separately from the job's explicitly requested constraints and MUST be satisfied before + * the job can run if the app doesn't have quota. + * + */ + public void addDynamicConstraint(int constraint) { + if (constraint == CONSTRAINT_WITHIN_QUOTA) { + Slog.wtf(TAG, "Tried to set quota as a dynamic constraint"); + return; + } + + // Connectivity and content trigger are special since they're only valid to add if the + // job has requested network or specific content URIs. Adding these constraints to jobs + // that don't need them doesn't make sense. + if ((constraint == CONSTRAINT_CONNECTIVITY && !hasConnectivityConstraint()) + || (constraint == CONSTRAINT_CONTENT_TRIGGER && !hasContentTriggerConstraint())) { + return; + } + + mDynamicConstraints |= constraint; + mReadyDynamicSatisfied = + mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints); + } + + /** + * Removes a dynamic constraint from a job, meaning that the requirement is not required for + * the job to run (if the job itself hasn't requested the constraint. This is separate from + * the job's explicitly requested constraints and does not remove those requested constraints. + * + */ + public void removeDynamicConstraint(int constraint) { + mDynamicConstraints &= ~constraint; + mReadyDynamicSatisfied = + mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints); + } + public long getLastSuccessfulRunTime() { return mLastSuccessfulRunTime; } @@ -1099,6 +1161,8 @@ public final class JobStatus { break; default: satisfied |= constraint; + mReadyDynamicSatisfied = + mDynamicConstraints == (satisfied & mDynamicConstraints); break; } @@ -1117,24 +1181,29 @@ public final class JobStatus { case CONSTRAINT_WITHIN_QUOTA: mReadyWithinQuota = oldValue; break; + default: + mReadyDynamicSatisfied = + mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints); + break; } return toReturn; } private boolean isReady(int satisfiedConstraints) { - // Quota constraints trumps all other constraints. - if (!mReadyWithinQuota) { + // Quota and dynamic constraints trump all other constraints. + if (!mReadyWithinQuota && !mReadyDynamicSatisfied) { return false; } - // Deadline constraint trumps other constraints besides quota (except for periodic jobs - // where deadline is an implementation detail. A periodic job should only run if its - // constraints are satisfied). + // Deadline constraint trumps other constraints besides quota and dynamic (except for + // periodic jobs where deadline is an implementation detail. A periodic job should only + // run if its constraints are satisfied). // DeviceNotDozing implicit constraint must be satisfied // NotRestrictedInBackground implicit constraint must be satisfied return mReadyNotDozing && mReadyNotRestrictedInBg && (mReadyDeadlineSatisfied || isConstraintsSatisfied(satisfiedConstraints)); } + /** All constraints besides implicit and deadline. */ static final int CONSTRAINTS_OF_INTEREST = CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW | CONSTRAINT_STORAGE_NOT_LOW | CONSTRAINT_TIMING_DELAY | CONSTRAINT_CONNECTIVITY | CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER; @@ -1441,6 +1510,8 @@ public final class JobStatus { case 2: return "FREQUENT"; case 3: return "RARE"; case 4: return "NEVER"; + case 5: + return "RESTRICTED"; default: return "Unknown: " + standbyBucket; } @@ -1560,6 +1631,10 @@ public final class JobStatus { pw.print(prefix); pw.print("Required constraints:"); dumpConstraints(pw, requiredConstraints); pw.println(); + pw.print(prefix); + pw.print("Dynamic constraints:"); + dumpConstraints(pw, mDynamicConstraints); + pw.println(); if (full) { pw.print(prefix); pw.print("Satisfied constraints:"); dumpConstraints(pw, satisfiedConstraints); @@ -1599,6 +1674,9 @@ public final class JobStatus { pw.print(prefix); pw.print(" readyDeadlineSatisfied: "); pw.println(mReadyDeadlineSatisfied); } + pw.print(prefix); + pw.print(" readyDynamicSatisfied: "); + pw.println(mReadyDynamicSatisfied); if (changedAuthorities != null) { pw.print(prefix); pw.println("Changed authorities:"); @@ -1760,6 +1838,7 @@ public final class JobStatus { } dumpConstraints(proto, JobStatusDumpProto.REQUIRED_CONSTRAINTS, requiredConstraints); + dumpConstraints(proto, JobStatusDumpProto.DYNAMIC_CONSTRAINTS, mDynamicConstraints); if (full) { dumpConstraints(proto, JobStatusDumpProto.SATISFIED_CONSTRAINTS, satisfiedConstraints); dumpConstraints(proto, JobStatusDumpProto.UNSATISFIED_CONSTRAINTS, @@ -1807,6 +1886,8 @@ public final class JobStatus { mReadyNotRestrictedInBg); // mReadyDeadlineSatisfied isn't an implicit constraint...and can be determined from other // field values. + proto.write(JobStatusDumpProto.ImplicitConstraints.IS_DYNAMIC_SATISFIED, + mReadyDynamicSatisfied); proto.end(icToken); if (changedAuthorities != null) { diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index 2e735a4e45f5..8eefac8abdac 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java @@ -24,6 +24,7 @@ import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX; import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX; import static com.android.server.job.JobSchedulerService.NEVER_INDEX; import static com.android.server.job.JobSchedulerService.RARE_INDEX; +import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.WORKING_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; @@ -424,7 +425,9 @@ public final class QuotaController extends StateController { QcConstants.DEFAULT_WINDOW_SIZE_ACTIVE_MS, QcConstants.DEFAULT_WINDOW_SIZE_WORKING_MS, QcConstants.DEFAULT_WINDOW_SIZE_FREQUENT_MS, - QcConstants.DEFAULT_WINDOW_SIZE_RARE_MS + QcConstants.DEFAULT_WINDOW_SIZE_RARE_MS, + 0, // NEVER + QcConstants.DEFAULT_WINDOW_SIZE_RESTRICTED_MS }; /** The maximum period any bucket can have. */ @@ -441,7 +444,9 @@ public final class QuotaController extends StateController { QcConstants.DEFAULT_MAX_JOB_COUNT_ACTIVE, QcConstants.DEFAULT_MAX_JOB_COUNT_WORKING, QcConstants.DEFAULT_MAX_JOB_COUNT_FREQUENT, - QcConstants.DEFAULT_MAX_JOB_COUNT_RARE + QcConstants.DEFAULT_MAX_JOB_COUNT_RARE, + 0, // NEVER + QcConstants.DEFAULT_MAX_JOB_COUNT_RESTRICTED }; /** @@ -455,7 +460,9 @@ public final class QuotaController extends StateController { QcConstants.DEFAULT_MAX_SESSION_COUNT_ACTIVE, QcConstants.DEFAULT_MAX_SESSION_COUNT_WORKING, QcConstants.DEFAULT_MAX_SESSION_COUNT_FREQUENT, - QcConstants.DEFAULT_MAX_SESSION_COUNT_RARE + QcConstants.DEFAULT_MAX_SESSION_COUNT_RARE, + 0, // NEVER + QcConstants.DEFAULT_MAX_SESSION_COUNT_RESTRICTED, }; /** @@ -648,7 +655,11 @@ public final class QuotaController extends StateController { // Quota constraint is not enforced while charging. if (mChargeTracker.isCharging()) { - return true; + // Restricted jobs require additional constraints when charging, so don't immediately + // mark quota as free when charging. + if (standbyBucket != RESTRICTED_INDEX) { + return true; + } } ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket); @@ -1105,14 +1116,37 @@ public final class QuotaController extends StateController { } } + private class TimerChargingUpdateFunctor implements Consumer<Timer> { + private long mNowElapsed; + private boolean mIsCharging; + + private void setStatus(long nowElapsed, boolean isCharging) { + mNowElapsed = nowElapsed; + mIsCharging = isCharging; + } + + @Override + public void accept(Timer timer) { + if (JobSchedulerService.standbyBucketForPackage(timer.mPkg.packageName, + timer.mPkg.userId, mNowElapsed) != RESTRICTED_INDEX) { + // Restricted jobs need additional constraints even when charging, so don't + // immediately say that quota is free. + timer.onStateChangedLocked(mNowElapsed, mIsCharging); + } + } + } + + private final TimerChargingUpdateFunctor + mTimerChargingUpdateFunctor = new TimerChargingUpdateFunctor(); + private void handleNewChargingStateLocked() { - final long nowElapsed = sElapsedRealtimeClock.millis(); - final boolean isCharging = mChargeTracker.isCharging(); + mTimerChargingUpdateFunctor.setStatus(sElapsedRealtimeClock.millis(), + mChargeTracker.isCharging()); if (DEBUG) { - Slog.d(TAG, "handleNewChargingStateLocked: " + isCharging); + Slog.d(TAG, "handleNewChargingStateLocked: " + mChargeTracker.isCharging()); } // Deal with Timers first. - mPkgTimers.forEach((t) -> t.onStateChangedLocked(nowElapsed, isCharging)); + mPkgTimers.forEach(mTimerChargingUpdateFunctor); // Now update jobs. maybeUpdateAllConstraintsLocked(); } @@ -1555,7 +1589,10 @@ public final class QuotaController extends StateController { } private boolean shouldTrackLocked() { - return !mChargeTracker.isCharging() && !mForegroundUids.get(mUid); + final int standbyBucket = JobSchedulerService.standbyBucketForPackage(mPkg.packageName, + mPkg.userId, sElapsedRealtimeClock.millis()); + return (standbyBucket == RESTRICTED_INDEX || !mChargeTracker.isCharging()) + && !mForegroundUids.get(mUid); } void onStateChangedLocked(long nowElapsed, boolean isQuotaFree) { @@ -1670,6 +1707,7 @@ public final class QuotaController extends StateController { Slog.i(TAG, "Moving pkg " + string(userId, packageName) + " to bucketIndex " + bucketIndex); } + List<JobStatus> restrictedChanges = new ArrayList<>(); synchronized (mLock) { ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, packageName); if (jobs == null || jobs.size() == 0) { @@ -1677,6 +1715,13 @@ public final class QuotaController extends StateController { } for (int i = jobs.size() - 1; i >= 0; i--) { JobStatus js = jobs.valueAt(i); + // Effective standby bucket can change after this in some situations so + // use the real bucket so that the job is tracked by the controllers. + if ((bucketIndex == RESTRICTED_INDEX + || js.getStandbyBucket() == RESTRICTED_INDEX) + && bucketIndex != js.getStandbyBucket()) { + restrictedChanges.add(js); + } js.setStandbyBucket(bucketIndex); } Timer timer = mPkgTimers.get(userId, packageName); @@ -1687,6 +1732,9 @@ public final class QuotaController extends StateController { mStateChangedListener.onControllerStateChanged(); } } + if (restrictedChanges.size() > 0) { + mStateChangedListener.onRestrictedBucketChanged(restrictedChanges); + } }); } } @@ -1863,11 +1911,13 @@ public final class QuotaController extends StateController { private static final String KEY_WINDOW_SIZE_WORKING_MS = "window_size_working_ms"; private static final String KEY_WINDOW_SIZE_FREQUENT_MS = "window_size_frequent_ms"; private static final String KEY_WINDOW_SIZE_RARE_MS = "window_size_rare_ms"; + private static final String KEY_WINDOW_SIZE_RESTRICTED_MS = "window_size_restricted_ms"; private static final String KEY_MAX_EXECUTION_TIME_MS = "max_execution_time_ms"; private static final String KEY_MAX_JOB_COUNT_ACTIVE = "max_job_count_active"; private static final String KEY_MAX_JOB_COUNT_WORKING = "max_job_count_working"; private static final String KEY_MAX_JOB_COUNT_FREQUENT = "max_job_count_frequent"; private static final String KEY_MAX_JOB_COUNT_RARE = "max_job_count_rare"; + private static final String KEY_MAX_JOB_COUNT_RESTRICTED = "max_job_count_restricted"; private static final String KEY_RATE_LIMITING_WINDOW_MS = "rate_limiting_window_ms"; private static final String KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = "max_job_count_per_rate_limiting_window"; @@ -1875,6 +1925,8 @@ public final class QuotaController extends StateController { private static final String KEY_MAX_SESSION_COUNT_WORKING = "max_session_count_working"; private static final String KEY_MAX_SESSION_COUNT_FREQUENT = "max_session_count_frequent"; private static final String KEY_MAX_SESSION_COUNT_RARE = "max_session_count_rare"; + private static final String KEY_MAX_SESSION_COUNT_RESTRICTED = + "max_session_count_restricted"; private static final String KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = "max_session_count_per_rate_limiting_window"; private static final String KEY_TIMING_SESSION_COALESCING_DURATION_MS = @@ -1892,6 +1944,8 @@ public final class QuotaController extends StateController { 8 * 60 * 60 * 1000L; // 8 hours private static final long DEFAULT_WINDOW_SIZE_RARE_MS = 24 * 60 * 60 * 1000L; // 24 hours + private static final long DEFAULT_WINDOW_SIZE_RESTRICTED_MS = + 24 * 60 * 60 * 1000L; // 24 hours private static final long DEFAULT_MAX_EXECUTION_TIME_MS = 4 * HOUR_IN_MILLIS; private static final long DEFAULT_RATE_LIMITING_WINDOW_MS = @@ -1905,6 +1959,7 @@ public final class QuotaController extends StateController { (int) (25.0 * DEFAULT_WINDOW_SIZE_FREQUENT_MS / HOUR_IN_MILLIS); private static final int DEFAULT_MAX_JOB_COUNT_RARE = // 48/window = 2/hr = 16/session (int) (2.0 * DEFAULT_WINDOW_SIZE_RARE_MS / HOUR_IN_MILLIS); + private static final int DEFAULT_MAX_JOB_COUNT_RESTRICTED = 10; private static final int DEFAULT_MAX_SESSION_COUNT_ACTIVE = 75; // 450/hr private static final int DEFAULT_MAX_SESSION_COUNT_WORKING = @@ -1913,6 +1968,7 @@ public final class QuotaController extends StateController { 8; // 1/hr private static final int DEFAULT_MAX_SESSION_COUNT_RARE = 3; // .125/hr + private static final int DEFAULT_MAX_SESSION_COUNT_RESTRICTED = 1; // 1/day private static final int DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = 20; private static final long DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS = 5000; // 5 seconds @@ -1954,6 +2010,13 @@ public final class QuotaController extends StateController { public long WINDOW_SIZE_RARE_MS = DEFAULT_WINDOW_SIZE_RARE_MS; /** + * The quota window size of the particular standby bucket. Apps in this standby bucket are + * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past + * WINDOW_SIZE_MS. + */ + public long WINDOW_SIZE_RESTRICTED_MS = DEFAULT_WINDOW_SIZE_RESTRICTED_MS; + + /** * The maximum amount of time an app can have its jobs running within a 24 hour window. */ public long MAX_EXECUTION_TIME_MS = DEFAULT_MAX_EXECUTION_TIME_MS; @@ -1982,6 +2045,12 @@ public final class QuotaController extends StateController { */ public int MAX_JOB_COUNT_RARE = DEFAULT_MAX_JOB_COUNT_RARE; + /** + * The maximum number of jobs an app can run within this particular standby bucket's + * window size. + */ + public int MAX_JOB_COUNT_RESTRICTED = DEFAULT_MAX_JOB_COUNT_RESTRICTED; + /** The period of time used to rate limit recently run jobs. */ public long RATE_LIMITING_WINDOW_MS = DEFAULT_RATE_LIMITING_WINDOW_MS; @@ -2016,6 +2085,12 @@ public final class QuotaController extends StateController { public int MAX_SESSION_COUNT_RARE = DEFAULT_MAX_SESSION_COUNT_RARE; /** + * The maximum number of {@link TimingSession}s an app can run within this particular + * standby bucket's window size. + */ + public int MAX_SESSION_COUNT_RESTRICTED = DEFAULT_MAX_SESSION_COUNT_RESTRICTED; + + /** * The maximum number of {@link TimingSession}s that can run within the past * {@link #ALLOWED_TIME_PER_PERIOD_MS}. */ @@ -2087,6 +2162,8 @@ public final class QuotaController extends StateController { KEY_WINDOW_SIZE_FREQUENT_MS, DEFAULT_WINDOW_SIZE_FREQUENT_MS); WINDOW_SIZE_RARE_MS = mParser.getDurationMillis( KEY_WINDOW_SIZE_RARE_MS, DEFAULT_WINDOW_SIZE_RARE_MS); + WINDOW_SIZE_RESTRICTED_MS = mParser.getDurationMillis( + KEY_WINDOW_SIZE_RESTRICTED_MS, DEFAULT_WINDOW_SIZE_RESTRICTED_MS); MAX_EXECUTION_TIME_MS = mParser.getDurationMillis( KEY_MAX_EXECUTION_TIME_MS, DEFAULT_MAX_EXECUTION_TIME_MS); MAX_JOB_COUNT_ACTIVE = mParser.getInt( @@ -2097,6 +2174,8 @@ public final class QuotaController extends StateController { KEY_MAX_JOB_COUNT_FREQUENT, DEFAULT_MAX_JOB_COUNT_FREQUENT); MAX_JOB_COUNT_RARE = mParser.getInt( KEY_MAX_JOB_COUNT_RARE, DEFAULT_MAX_JOB_COUNT_RARE); + MAX_JOB_COUNT_RESTRICTED = mParser.getInt( + KEY_MAX_JOB_COUNT_RESTRICTED, DEFAULT_MAX_JOB_COUNT_RESTRICTED); RATE_LIMITING_WINDOW_MS = mParser.getLong( KEY_RATE_LIMITING_WINDOW_MS, DEFAULT_RATE_LIMITING_WINDOW_MS); MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = mParser.getInt( @@ -2110,6 +2189,8 @@ public final class QuotaController extends StateController { KEY_MAX_SESSION_COUNT_FREQUENT, DEFAULT_MAX_SESSION_COUNT_FREQUENT); MAX_SESSION_COUNT_RARE = mParser.getInt( KEY_MAX_SESSION_COUNT_RARE, DEFAULT_MAX_SESSION_COUNT_RARE); + MAX_SESSION_COUNT_RESTRICTED = mParser.getInt( + KEY_MAX_SESSION_COUNT_RESTRICTED, DEFAULT_MAX_SESSION_COUNT_RESTRICTED); MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = mParser.getInt( KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW, DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW); @@ -2173,6 +2254,13 @@ public final class QuotaController extends StateController { mBucketPeriodsMs[RARE_INDEX] = newRarePeriodMs; changed = true; } + // Fit in the range [allowed time (10 mins), 1 week]. + long newRestrictedPeriodMs = Math.max(mAllowedTimePerPeriodMs, + Math.min(7 * 24 * 60 * MINUTE_IN_MILLIS, WINDOW_SIZE_RESTRICTED_MS)); + if (mBucketPeriodsMs[RESTRICTED_INDEX] != newRestrictedPeriodMs) { + mBucketPeriodsMs[RESTRICTED_INDEX] = newRestrictedPeriodMs; + changed = true; + } long newRateLimitingWindowMs = Math.min(MAX_PERIOD_MS, Math.max(MIN_RATE_LIMITING_WINDOW_MS, RATE_LIMITING_WINDOW_MS)); if (mRateLimitingWindowMs != newRateLimitingWindowMs) { @@ -2206,6 +2294,12 @@ public final class QuotaController extends StateController { mMaxBucketJobCounts[RARE_INDEX] = newRareMaxJobCount; changed = true; } + int newRestrictedMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT, + MAX_JOB_COUNT_RESTRICTED); + if (mMaxBucketJobCounts[RESTRICTED_INDEX] != newRestrictedMaxJobCount) { + mMaxBucketJobCounts[RESTRICTED_INDEX] = newRestrictedMaxJobCount; + changed = true; + } int newMaxSessionCountPerRateLimitPeriod = Math.max( MIN_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW, MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW); @@ -2237,6 +2331,11 @@ public final class QuotaController extends StateController { mMaxBucketSessionCounts[RARE_INDEX] = newRareMaxSessionCount; changed = true; } + int newRestrictedMaxSessionCount = Math.max(0, MAX_SESSION_COUNT_RESTRICTED); + if (mMaxBucketSessionCounts[RESTRICTED_INDEX] != newRestrictedMaxSessionCount) { + mMaxBucketSessionCounts[RESTRICTED_INDEX] = newRestrictedMaxSessionCount; + changed = true; + } long newSessionCoalescingDurationMs = Math.min(15 * MINUTE_IN_MILLIS, Math.max(0, TIMING_SESSION_COALESCING_DURATION_MS)); if (mTimingSessionCoalescingDurationMs != newSessionCoalescingDurationMs) { @@ -2266,11 +2365,13 @@ public final class QuotaController extends StateController { pw.printPair(KEY_WINDOW_SIZE_WORKING_MS, WINDOW_SIZE_WORKING_MS).println(); pw.printPair(KEY_WINDOW_SIZE_FREQUENT_MS, WINDOW_SIZE_FREQUENT_MS).println(); pw.printPair(KEY_WINDOW_SIZE_RARE_MS, WINDOW_SIZE_RARE_MS).println(); + pw.printPair(KEY_WINDOW_SIZE_RESTRICTED_MS, WINDOW_SIZE_RESTRICTED_MS).println(); pw.printPair(KEY_MAX_EXECUTION_TIME_MS, MAX_EXECUTION_TIME_MS).println(); pw.printPair(KEY_MAX_JOB_COUNT_ACTIVE, MAX_JOB_COUNT_ACTIVE).println(); pw.printPair(KEY_MAX_JOB_COUNT_WORKING, MAX_JOB_COUNT_WORKING).println(); pw.printPair(KEY_MAX_JOB_COUNT_FREQUENT, MAX_JOB_COUNT_FREQUENT).println(); pw.printPair(KEY_MAX_JOB_COUNT_RARE, MAX_JOB_COUNT_RARE).println(); + pw.printPair(KEY_MAX_JOB_COUNT_RESTRICTED, MAX_JOB_COUNT_RESTRICTED).println(); pw.printPair(KEY_RATE_LIMITING_WINDOW_MS, RATE_LIMITING_WINDOW_MS).println(); pw.printPair(KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW, MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW).println(); @@ -2278,6 +2379,7 @@ public final class QuotaController extends StateController { pw.printPair(KEY_MAX_SESSION_COUNT_WORKING, MAX_SESSION_COUNT_WORKING).println(); pw.printPair(KEY_MAX_SESSION_COUNT_FREQUENT, MAX_SESSION_COUNT_FREQUENT).println(); pw.printPair(KEY_MAX_SESSION_COUNT_RARE, MAX_SESSION_COUNT_RARE).println(); + pw.printPair(KEY_MAX_SESSION_COUNT_RESTRICTED, MAX_SESSION_COUNT_RESTRICTED).println(); pw.printPair(KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW, MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW).println(); pw.printPair(KEY_TIMING_SESSION_COALESCING_DURATION_MS, @@ -2297,6 +2399,8 @@ public final class QuotaController extends StateController { proto.write(ConstantsProto.QuotaController.FREQUENT_WINDOW_SIZE_MS, WINDOW_SIZE_FREQUENT_MS); proto.write(ConstantsProto.QuotaController.RARE_WINDOW_SIZE_MS, WINDOW_SIZE_RARE_MS); + proto.write(ConstantsProto.QuotaController.RESTRICTED_WINDOW_SIZE_MS, + WINDOW_SIZE_RESTRICTED_MS); proto.write(ConstantsProto.QuotaController.MAX_EXECUTION_TIME_MS, MAX_EXECUTION_TIME_MS); proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_ACTIVE, MAX_JOB_COUNT_ACTIVE); @@ -2305,6 +2409,8 @@ public final class QuotaController extends StateController { proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_FREQUENT, MAX_JOB_COUNT_FREQUENT); proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_RARE, MAX_JOB_COUNT_RARE); + proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_RESTRICTED, + MAX_JOB_COUNT_RESTRICTED); proto.write(ConstantsProto.QuotaController.RATE_LIMITING_WINDOW_MS, RATE_LIMITING_WINDOW_MS); proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW, @@ -2317,6 +2423,8 @@ public final class QuotaController extends StateController { MAX_SESSION_COUNT_FREQUENT); proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_RARE, MAX_SESSION_COUNT_RARE); + proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_RESTRICTED, + MAX_SESSION_COUNT_RESTRICTED); proto.write(ConstantsProto.QuotaController.MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW, MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW); proto.write(ConstantsProto.QuotaController.TIMING_SESSION_COALESCING_DURATION_MS, diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/RestrictingController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/RestrictingController.java new file mode 100644 index 000000000000..5c637bb92ccc --- /dev/null +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/RestrictingController.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.job.controllers; + +import com.android.server.job.JobSchedulerService; + +/** + * Controller that can also handle jobs in the + * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket. + */ +public abstract class RestrictingController extends StateController { + RestrictingController(JobSchedulerService service) { + super(service); + } + + /** + * Start tracking a job that has been added to the + * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket. + */ + public abstract void startTrackingRestrictedJobLocked(JobStatus jobStatus); + + /** + * Stop tracking a job that has been removed from the + * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket. + */ + public abstract void stopTrackingRestrictedJobLocked(JobStatus jobStatus); +} diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java index b9df30aa4d95..9d6e012da89b 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java @@ -25,8 +25,11 @@ import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACT import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; +import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; +import static com.android.server.usage.AppStandbyController.isUserUsage; + import android.app.usage.AppStandbyInfo; import android.app.usage.UsageStatsManager; import android.os.SystemClock; @@ -81,6 +84,8 @@ public class AppIdleHistory { private static final String ATTR_SCREEN_IDLE = "screenIdleTime"; // Elapsed timebase time when app was last used private static final String ATTR_ELAPSED_IDLE = "elapsedIdleTime"; + // Elapsed timebase time when app was last used by the user + private static final String ATTR_LAST_USED_BY_USER_ELAPSED = "lastUsedByUserElapsedTime"; // Elapsed timebase time when the app bucket was last predicted externally private static final String ATTR_LAST_PREDICTED_TIME = "lastPredictedTime"; // The standby bucket for the app @@ -93,6 +98,12 @@ public class AppIdleHistory { private static final String ATTR_BUCKET_ACTIVE_TIMEOUT_TIME = "activeTimeoutTime"; // The time when the forced working_set state can be overridden. private static final String ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME = "workingSetTimeoutTime"; + // Elapsed timebase time when the app was last marked for restriction. + private static final String ATTR_LAST_RESTRICTION_ATTEMPT_ELAPSED = + "lastRestrictionAttemptElapsedTime"; + // Reason why the app was last marked for restriction. + private static final String ATTR_LAST_RESTRICTION_ATTEMPT_REASON = + "lastRestrictionAttemptReason"; // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot) private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration @@ -107,8 +118,10 @@ public class AppIdleHistory { private boolean mScreenOn; static class AppUsageHistory { - // Last used time using elapsed timebase + // Last used time (including system usage), using elapsed timebase long lastUsedElapsedTime; + // Last time the user used the app, using elapsed timebase + long lastUsedByUserElapsedTime; // Last used time using screen_on timebase long lastUsedScreenTime; // Last predicted time using elapsed timebase @@ -136,6 +149,10 @@ public class AppIdleHistory { // under any active state timeout, so that it becomes applicable after the active state // timeout expires. long bucketWorkingSetTimeoutTime; + // The last time an agent attempted to put the app into the RESTRICTED bucket. + long lastRestrictAttemptElapsedTime; + // The last reason the app was marked to be put into the RESTRICTED bucket. + int lastRestrictReason; } AppIdleHistory(File storageDir, long elapsedRealtime) { @@ -229,25 +246,37 @@ public class AppIdleHistory { */ public AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName, int newBucket, int usageReason, long elapsedRealtime, long timeout) { - // Set the timeout if applicable - if (timeout > elapsedRealtime) { - // Convert to elapsed timebase - final long timeoutTime = mElapsedDuration + (timeout - mElapsedSnapshot); - if (newBucket == STANDBY_BUCKET_ACTIVE) { - appUsageHistory.bucketActiveTimeoutTime = Math.max(timeoutTime, - appUsageHistory.bucketActiveTimeoutTime); - } else if (newBucket == STANDBY_BUCKET_WORKING_SET) { - appUsageHistory.bucketWorkingSetTimeoutTime = Math.max(timeoutTime, - appUsageHistory.bucketWorkingSetTimeoutTime); - } else { - throw new IllegalArgumentException("Cannot set a timeout on bucket=" + - newBucket); + int bucketingReason = REASON_MAIN_USAGE | usageReason; + final boolean isUserUsage = isUserUsage(bucketingReason); + + if (appUsageHistory.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage) { + // Only user usage should bring an app out of the RESTRICTED bucket. + newBucket = STANDBY_BUCKET_RESTRICTED; + bucketingReason = appUsageHistory.bucketingReason; + } else { + // Set the timeout if applicable + if (timeout > elapsedRealtime) { + // Convert to elapsed timebase + final long timeoutTime = mElapsedDuration + (timeout - mElapsedSnapshot); + if (newBucket == STANDBY_BUCKET_ACTIVE) { + appUsageHistory.bucketActiveTimeoutTime = Math.max(timeoutTime, + appUsageHistory.bucketActiveTimeoutTime); + } else if (newBucket == STANDBY_BUCKET_WORKING_SET) { + appUsageHistory.bucketWorkingSetTimeoutTime = Math.max(timeoutTime, + appUsageHistory.bucketWorkingSetTimeoutTime); + } else { + throw new IllegalArgumentException("Cannot set a timeout on bucket=" + + newBucket); + } } } if (elapsedRealtime != 0) { appUsageHistory.lastUsedElapsedTime = mElapsedDuration + (elapsedRealtime - mElapsedSnapshot); + if (isUserUsage) { + appUsageHistory.lastUsedByUserElapsedTime = appUsageHistory.lastUsedElapsedTime; + } appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime); } @@ -259,7 +288,7 @@ public class AppIdleHistory { + ", reason=0x0" + Integer.toHexString(appUsageHistory.bucketingReason)); } } - appUsageHistory.bucketingReason = REASON_MAIN_USAGE | usageReason; + appUsageHistory.bucketingReason = bucketingReason; return appUsageHistory; } @@ -386,6 +415,24 @@ public class AppIdleHistory { } /** + * Notes an attempt to put the app in the {@link UsageStatsManager#STANDBY_BUCKET_RESTRICTED} + * bucket. + * + * @param packageName The package name of the app that is being restricted + * @param userId The ID of the user in which the app is being restricted + * @param elapsedRealtime The time the attempt was made, in the (unadjusted) elapsed realtime + * timebase + * @param reason The reason for the restriction attempt + */ + void noteRestrictionAttempt(String packageName, int userId, long elapsedRealtime, int reason) { + ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); + AppUsageHistory appUsageHistory = + getPackageHistory(userHistory, packageName, elapsedRealtime, true); + appUsageHistory.lastRestrictAttemptElapsedTime = getElapsedTime(elapsedRealtime); + appUsageHistory.lastRestrictReason = reason; + } + + /** * Returns the time since the last job was run for this app. This can be larger than the * current elapsedRealtime, in case it happened before boot or a really large value if no jobs * were ever run. @@ -547,6 +594,9 @@ public class AppIdleHistory { AppUsageHistory appUsageHistory = new AppUsageHistory(); appUsageHistory.lastUsedElapsedTime = Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE)); + appUsageHistory.lastUsedByUserElapsedTime = getLongValue(parser, + ATTR_LAST_USED_BY_USER_ELAPSED, + appUsageHistory.lastUsedElapsedTime); appUsageHistory.lastUsedScreenTime = Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE)); appUsageHistory.lastPredictedTime = getLongValue(parser, @@ -570,6 +620,19 @@ public class AppIdleHistory { appUsageHistory.bucketingReason = Integer.parseInt(bucketingReason, 16); } catch (NumberFormatException nfe) { + Slog.wtf(TAG, "Unable to read bucketing reason", nfe); + } + } + appUsageHistory.lastRestrictAttemptElapsedTime = + getLongValue(parser, ATTR_LAST_RESTRICTION_ATTEMPT_ELAPSED, 0); + String lastRestrictReason = parser.getAttributeValue( + null, ATTR_LAST_RESTRICTION_ATTEMPT_REASON); + if (lastRestrictReason != null) { + try { + appUsageHistory.lastRestrictReason = + Integer.parseInt(lastRestrictReason, 16); + } catch (NumberFormatException nfe) { + Slog.wtf(TAG, "Unable to read last restrict reason", nfe); } } appUsageHistory.lastInformedBucket = -1; @@ -618,6 +681,8 @@ public class AppIdleHistory { xml.attribute(null, ATTR_NAME, packageName); xml.attribute(null, ATTR_ELAPSED_IDLE, Long.toString(history.lastUsedElapsedTime)); + xml.attribute(null, ATTR_LAST_USED_BY_USER_ELAPSED, + Long.toString(history.lastUsedByUserElapsedTime)); xml.attribute(null, ATTR_SCREEN_IDLE, Long.toString(history.lastUsedScreenTime)); xml.attribute(null, ATTR_LAST_PREDICTED_TIME, @@ -638,6 +703,12 @@ public class AppIdleHistory { xml.attribute(null, ATTR_LAST_RUN_JOB_TIME, Long.toString(history .lastJobRunTime)); } + if (history.lastRestrictAttemptElapsedTime > 0) { + xml.attribute(null, ATTR_LAST_RESTRICTION_ATTEMPT_ELAPSED, + Long.toString(history.lastRestrictAttemptElapsedTime)); + } + xml.attribute(null, ATTR_LAST_RESTRICTION_ATTEMPT_REASON, + Integer.toHexString(history.lastRestrictReason)); xml.endTag(null, TAG_PACKAGE); } @@ -672,6 +743,9 @@ public class AppIdleHistory { + UsageStatsManager.reasonToString(appUsageHistory.bucketingReason)); idpw.print(" used="); TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw); + idpw.print(" usedByUser="); + TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedByUserElapsedTime, + idpw); idpw.print(" usedScr="); TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw); idpw.print(" lastPred="); @@ -684,6 +758,13 @@ public class AppIdleHistory { idpw); idpw.print(" lastJob="); TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastJobRunTime, idpw); + if (appUsageHistory.lastRestrictAttemptElapsedTime > 0) { + idpw.print(" lastRestrictAttempt="); + TimeUtils.formatDuration( + totalElapsedTime - appUsageHistory.lastRestrictAttemptElapsedTime, idpw); + idpw.print(" lastRestrictReason=" + + UsageStatsManager.reasonToString(appUsageHistory.lastRestrictReason)); + } idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n")); idpw.println(); } diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index eb0b54b1d9fc..b1b8fba78ab9 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -23,6 +23,7 @@ import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK; import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED; import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT; import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE; +import static android.app.usage.UsageStatsManager.REASON_SUB_MASK; import static android.app.usage.UsageStatsManager.REASON_SUB_PREDICTED_RESTORED; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE; @@ -44,6 +45,7 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; +import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY; @@ -73,6 +75,7 @@ import android.net.Network; import android.net.NetworkRequest; import android.net.NetworkScoreManager; import android.os.BatteryStats; +import android.os.Build; import android.os.Environment; import android.os.Handler; import android.os.IDeviceIdleController; @@ -93,7 +96,9 @@ import android.util.SparseArray; import android.util.SparseIntArray; import android.util.TimeUtils; import android.view.Display; +import android.widget.Toast; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; @@ -124,7 +129,7 @@ import java.util.concurrent.CountDownLatch; public class AppStandbyController implements AppStandbyInternal { private static final String TAG = "AppStandbyController"; - static final boolean DEBUG = false; + static final boolean DEBUG = true; static final boolean COMPRESS_TIME = false; private static final long ONE_MINUTE = 60 * 1000; @@ -615,6 +620,16 @@ public class AppStandbyController implements AppStandbyInternal { Slog.d(TAG, " Keeping at WORKING_SET due to min timeout"); } } + + if (app.lastRestrictAttemptElapsedTime > app.lastUsedByUserElapsedTime + && elapsedTimeAdjusted - app.lastUsedByUserElapsedTime + >= mInjector.getRestrictedBucketDelayMs()) { + newBucket = STANDBY_BUCKET_RESTRICTED; + reason = app.lastRestrictReason; + if (DEBUG) { + Slog.d(TAG, "Bringing down to RESTRICTED due to timeout"); + } + } if (DEBUG) { Slog.d(TAG, " Old bucket=" + oldBucket + ", newBucket=" + newBucket); @@ -733,15 +748,16 @@ public class AppStandbyController implements AppStandbyInternal { elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis); nextCheckTime = mStrongUsageTimeoutMillis; } - mHandler.sendMessageDelayed(mHandler.obtainMessage - (MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, pkg), - nextCheckTime); - final boolean userStartedInteracting = - appHistory.currentBucket == STANDBY_BUCKET_ACTIVE && - prevBucket != appHistory.currentBucket && - (prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE; - maybeInformListeners(pkg, userId, elapsedRealtime, - appHistory.currentBucket, reason, userStartedInteracting); + if (appHistory.currentBucket != prevBucket) { + mHandler.sendMessageDelayed( + mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, pkg), + nextCheckTime); + final boolean userStartedInteracting = + appHistory.currentBucket == STANDBY_BUCKET_ACTIVE + && (prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE; + maybeInformListeners(pkg, userId, elapsedRealtime, + appHistory.currentBucket, reason, userStartedInteracting); + } if (previouslyIdle) { notifyBatteryStats(pkg, userId, false); @@ -923,6 +939,15 @@ public class AppStandbyController implements AppStandbyInternal { } } + static boolean isUserUsage(int reason) { + if ((reason & REASON_MAIN_MASK) == REASON_MAIN_USAGE) { + final int subReason = reason & REASON_SUB_MASK; + return subReason == REASON_SUB_USAGE_USER_INTERACTION + || subReason == REASON_SUB_USAGE_MOVE_TO_FOREGROUND; + } + return false; + } + @Override public int[] getIdleUidsForUser(int userId) { if (!mAppIdleEnabled) { @@ -1017,6 +1042,20 @@ public class AppStandbyController implements AppStandbyInternal { } @Override + public void restrictApp(@NonNull String packageName, int userId, int restrictReason) { + // If the package is not installed, don't allow the bucket to be set. + if (!mInjector.isPackageInstalled(packageName, 0, userId)) { + Slog.e(TAG, "Tried to restrict uninstalled app: " + packageName); + return; + } + + final int reason = REASON_MAIN_FORCED_BY_SYSTEM | (REASON_SUB_MASK & restrictReason); + final long nowElapsed = mInjector.elapsedRealtime(); + setAppStandbyBucket(packageName, userId, STANDBY_BUCKET_RESTRICTED, reason, + nowElapsed, false); + } + + @Override public void setAppStandbyBucket(@NonNull String packageName, int bucket, int userId, int callingUid, int callingPid) { setAppStandbyBuckets( @@ -1080,6 +1119,7 @@ public class AppStandbyController implements AppStandbyInternal { synchronized (mAppIdleLock) { // If the package is not installed, don't allow the bucket to be set. if (!mInjector.isPackageInstalled(packageName, 0, userId)) { + Slog.e(TAG, "Tried to set bucket of uninstalled app: " + packageName); return; } AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName, @@ -1089,8 +1129,9 @@ public class AppStandbyController implements AppStandbyInternal { // Don't allow changing bucket if higher than ACTIVE if (app.currentBucket < STANDBY_BUCKET_ACTIVE) return; - // Don't allow prediction to change from/to NEVER + // Don't allow prediction to change from/to NEVER or from RESTRICTED. if ((app.currentBucket == STANDBY_BUCKET_NEVER + || app.currentBucket == STANDBY_BUCKET_RESTRICTED || newBucket == STANDBY_BUCKET_NEVER) && predicted) { return; @@ -1103,6 +1144,50 @@ public class AppStandbyController implements AppStandbyInternal { return; } + final boolean isForcedByUser = + (reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER; + + // If the current bucket is RESTRICTED, only user force or usage should bring it out. + if (app.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage(reason) + && !isForcedByUser) { + return; + } + + if (newBucket == STANDBY_BUCKET_RESTRICTED) { + mAppIdleHistory + .noteRestrictionAttempt(packageName, userId, elapsedRealtime, reason); + + if (isForcedByUser) { + // Only user force can bypass the delay restriction. If the user forced the + // app into the RESTRICTED bucket, then a toast confirming the action + // shouldn't be surprising. + if (Build.IS_DEBUGGABLE) { + Toast.makeText(mContext, + // Since AppStandbyController sits low in the lock hierarchy, + // make sure not to call out with the lock held. + mHandler.getLooper(), + mContext.getResources().getString( + R.string.as_app_forced_to_restricted_bucket, packageName), + Toast.LENGTH_SHORT) + .show(); + } else { + Slog.i(TAG, packageName + " restricted by user"); + } + } else { + final long timeUntilRestrictPossibleMs = app.lastUsedByUserElapsedTime + + mInjector.getRestrictedBucketDelayMs() - elapsedRealtime; + if (timeUntilRestrictPossibleMs > 0) { + Slog.w(TAG, "Tried to restrict recently used app: " + packageName + + " due to " + reason); + mHandler.sendMessageDelayed( + mHandler.obtainMessage( + MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, packageName), + timeUntilRestrictPossibleMs); + return; + } + } + } + // If the bucket is required to stay in a higher state for a specified duration, don't // override unless the duration has passed if (predicted) { @@ -1435,6 +1520,12 @@ public class AppStandbyController implements AppStandbyInternal { private DisplayManager mDisplayManager; private PowerManager mPowerManager; int mBootPhase; + /** + * The minimum amount of time required since the last user interaction before an app can be + * placed in the RESTRICTED bucket. + */ + // TODO: make configurable via DeviceConfig + private long mRestrictedBucketDelayMs = ONE_DAY; Injector(Context context, Looper looper) { mContext = context; @@ -1459,6 +1550,12 @@ public class AppStandbyController implements AppStandbyInternal { mDisplayManager = (DisplayManager) mContext.getSystemService( Context.DISPLAY_SERVICE); mPowerManager = mContext.getSystemService(PowerManager.class); + + final ActivityManager activityManager = + (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); + if (activityManager.isLowRamDevice() || ActivityManager.isSmallBatteryDevice()) { + mRestrictedBucketDelayMs = 12 * ONE_HOUR; + } } mBootPhase = phase; } @@ -1498,6 +1595,10 @@ public class AppStandbyController implements AppStandbyInternal { return Environment.getDataSystemDirectory(); } + long getRestrictedBucketDelayMs() { + return mRestrictedBucketDelayMs; + } + void noteEvent(int event, String packageName, int uid) throws RemoteException { mBatteryStats.noteEvent(event, packageName, uid); } diff --git a/apex/permission/apex_manifest.json b/apex/permission/apex_manifest.json index 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/aidl/android/os/IStatsCompanionService.aidl b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl index 99b9d398e30c..bdd1da7bf3d3 100644 --- a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl +++ b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl @@ -16,9 +16,6 @@ package android.os; -import android.os.IPullAtomCallback; -import android.os.StatsLogEventWrapper; - /** * Binder interface to communicate with the Java-based statistics service helper. * {@hide} @@ -62,9 +59,6 @@ interface IStatsCompanionService { /** Cancel any alarm for the purpose of subscriber triggering. */ oneway void cancelAlarmForSubscriberTriggering(); - /** Pull the specified data. Results will be sent to statsd when complete. */ - StatsLogEventWrapper[] pullData(int pullCode); - /** Tells StatsCompaionService to grab the uid map snapshot and send it to statsd. */ oneway void triggerUidSnapshot(); } 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/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java index 018037ce596e..bcbb5a1407f6 100644 --- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java +++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java @@ -714,87 +714,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } - private void pullDebugElapsedClock(int tagId, - long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) { - final long elapsedMillis = SystemClock.elapsedRealtime(); - final long clockDiffMillis = mDebugElapsedClockPreviousValue == 0 - ? 0 : elapsedMillis - mDebugElapsedClockPreviousValue; - - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e.writeLong(mDebugElapsedClockPullCount); - e.writeLong(elapsedMillis); - // Log it twice to be able to test multi-value aggregation from ValueMetric. - e.writeLong(elapsedMillis); - e.writeLong(clockDiffMillis); - e.writeInt(1 /* always set */); - pulledData.add(e); - - if (mDebugElapsedClockPullCount % 2 == 1) { - StatsLogEventWrapper e2 = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - e2.writeLong(mDebugElapsedClockPullCount); - e2.writeLong(elapsedMillis); - // Log it twice to be able to test multi-value aggregation from ValueMetric. - e2.writeLong(elapsedMillis); - e2.writeLong(clockDiffMillis); - e2.writeInt(2 /* set on odd pulls */); - pulledData.add(e2); - } - - mDebugElapsedClockPullCount++; - mDebugElapsedClockPreviousValue = elapsedMillis; - } - - private void pullDebugFailingElapsedClock(int tagId, - long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) { - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); - final long elapsedMillis = SystemClock.elapsedRealtime(); - // Fails every 5 buckets. - if (mDebugFailingElapsedClockPullCount++ % 5 == 0) { - mDebugFailingElapsedClockPreviousValue = elapsedMillis; - throw new RuntimeException("Failing debug elapsed clock"); - } - - e.writeLong(mDebugFailingElapsedClockPullCount); - e.writeLong(elapsedMillis); - // Log it twice to be able to test multi-value aggregation from ValueMetric. - e.writeLong(elapsedMillis); - e.writeLong(mDebugFailingElapsedClockPreviousValue == 0 - ? 0 : elapsedMillis - mDebugFailingElapsedClockPreviousValue); - mDebugFailingElapsedClockPreviousValue = elapsedMillis; - pulledData.add(e); - } - - /** - * Pulls various data. - */ - @Override // Binder call - public StatsLogEventWrapper[] pullData(int tagId) { - StatsCompanion.enforceStatsCompanionPermission(mContext); - if (DEBUG) { - Slog.d(TAG, "Pulling " + tagId); - } - List<StatsLogEventWrapper> ret = new ArrayList<>(); - long elapsedNanos = SystemClock.elapsedRealtimeNanos(); - long wallClockNanos = SystemClock.currentTimeMicro() * 1000L; - switch (tagId) { - - case StatsLog.DEBUG_ELAPSED_CLOCK: { - pullDebugElapsedClock(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - - case StatsLog.DEBUG_FAILING_ELAPSED_CLOCK: { - pullDebugFailingElapsedClock(tagId, elapsedNanos, wallClockNanos, ret); - break; - } - - default: - Slog.w(TAG, "No such tagId data as " + tagId); - return null; - } - return ret.toArray(new StatsLogEventWrapper[ret.size()]); - } - @Override // Binder call public void statsdReady() { StatsCompanion.enforceStatsCompanionPermission(mContext); diff --git a/api/current.txt b/api/current.txt index e0183a0681ea..16dee4b58da6 100644 --- a/api/current.txt +++ b/api/current.txt @@ -39,6 +39,7 @@ package android { field public static final String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE"; field public static final String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"; field public static final String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE"; + field public static final String BIND_QUICK_ACCESS_WALLET_SERVICE = "android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE"; field public static final String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE"; field public static final String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS"; field public static final String BIND_SCREENING_SERVICE = "android.permission.BIND_SCREENING_SERVICE"; @@ -6767,6 +6768,13 @@ package android.app.admin { method public final android.os.IBinder onBind(android.content.Intent); } + public class DevicePolicyKeyguardService extends android.app.Service { + ctor public DevicePolicyKeyguardService(); + method @Nullable public void dismiss(); + method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent); + method @Nullable public android.view.SurfaceControl onSurfaceReady(@Nullable android.os.IBinder); + } + public class DevicePolicyManager { method public void addCrossProfileIntentFilter(@NonNull android.content.ComponentName, android.content.IntentFilter, int); method public boolean addCrossProfileWidgetProvider(@NonNull android.content.ComponentName, String); @@ -6845,13 +6853,14 @@ 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); method public java.util.List<android.os.UserHandle> getSecondaryUsers(@NonNull android.content.ComponentName); method public CharSequence getShortSupportMessage(@NonNull android.content.ComponentName); method public CharSequence getStartUserSessionMessage(@NonNull android.content.ComponentName); - method public boolean getStorageEncryption(@Nullable android.content.ComponentName); + method @Deprecated public boolean getStorageEncryption(@Nullable android.content.ComponentName); method public int getStorageEncryptionStatus(); method @Nullable public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy(); method @Nullable public android.os.PersistableBundle getTransferOwnershipBundle(); @@ -6971,6 +6980,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>); @@ -6979,12 +6989,13 @@ package android.app.admin { method public boolean setResetPasswordToken(android.content.ComponentName, byte[]); method public void setRestrictionsProvider(@NonNull android.content.ComponentName, @Nullable android.content.ComponentName); method public void setScreenCaptureDisabled(@NonNull android.content.ComponentName, boolean); + method public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean); method public void setSecureSetting(@NonNull android.content.ComponentName, String, String); method public void setSecurityLoggingEnabled(@NonNull android.content.ComponentName, boolean); method public void setShortSupportMessage(@NonNull android.content.ComponentName, @Nullable CharSequence); method public void setStartUserSessionMessage(@NonNull android.content.ComponentName, @Nullable CharSequence); method public boolean setStatusBarDisabled(@NonNull android.content.ComponentName, boolean); - method public int setStorageEncryption(@NonNull android.content.ComponentName, boolean); + method @Deprecated public int setStorageEncryption(@NonNull android.content.ComponentName, boolean); method public void setSystemSetting(@NonNull android.content.ComponentName, @NonNull String, String); method public void setSystemUpdatePolicy(@NonNull android.content.ComponentName, android.app.admin.SystemUpdatePolicy); method public boolean setTime(@NonNull android.content.ComponentName, long); @@ -7004,6 +7015,8 @@ package android.app.admin { field public static final String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_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"; @@ -7127,6 +7140,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 @@ -8020,6 +8035,7 @@ package android.app.usage { field public static final int STANDBY_BUCKET_ACTIVE = 10; // 0xa field public static final int STANDBY_BUCKET_FREQUENT = 30; // 0x1e field public static final int STANDBY_BUCKET_RARE = 40; // 0x28 + field public static final int STANDBY_BUCKET_RESTRICTED = 45; // 0x2d field public static final int STANDBY_BUCKET_WORKING_SET = 20; // 0x14 } @@ -9954,6 +9970,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); @@ -9978,6 +9995,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(); @@ -10090,6 +10108,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 @@ -11480,6 +11499,7 @@ package android.content.pm { method @NonNull public android.graphics.drawable.Drawable getProfileSwitchingIconDrawable(@NonNull android.os.UserHandle); method @NonNull public CharSequence getProfileSwitchingLabel(@NonNull android.os.UserHandle); method @NonNull public java.util.List<android.os.UserHandle> getTargetUserProfiles(); + method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_PROFILES, "android.permission.INTERACT_ACROSS_USERS"}) public void startActivity(@NonNull android.content.Intent, @NonNull android.os.UserHandle); method public void startMainActivity(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle); field public static final String ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED = "android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED"; } @@ -11758,6 +11778,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(); @@ -11874,7 +11895,7 @@ package android.content.pm { method @NonNull public abstract CharSequence getApplicationLabel(@NonNull android.content.pm.ApplicationInfo); method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull android.content.pm.ApplicationInfo); method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; - method @NonNull public CharSequence getBackgroundPermissionButtonLabel(); + method @NonNull public CharSequence getBackgroundPermissionOptionLabel(); method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int); method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName); method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon(); @@ -11925,6 +11946,7 @@ package android.content.pm { method public boolean hasSigningCertificate(int, @NonNull byte[], int); method public abstract boolean hasSystemFeature(@NonNull String); method public abstract boolean hasSystemFeature(@NonNull String, int); + method public boolean isDefaultApplicationIcon(@NonNull android.graphics.drawable.Drawable); method public boolean isDeviceUpgrading(); method public abstract boolean isInstantApp(); method public abstract boolean isInstantApp(@NonNull String); @@ -11987,6 +12009,7 @@ package android.content.pm { field public static final String FEATURE_COMPANION_DEVICE_SETUP = "android.software.companion_device_setup"; field public static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice"; field public static final String FEATURE_CONSUMER_IR = "android.hardware.consumerir"; + field public static final String FEATURE_CONTEXTHUB = "android.hardware.context_hub"; field public static final String FEATURE_DEVICE_ADMIN = "android.software.device_admin"; field public static final String FEATURE_EMBEDDED = "android.hardware.type.embedded"; field public static final String FEATURE_ETHERNET = "android.hardware.ethernet"; @@ -12063,6 +12086,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"; @@ -13501,227 +13525,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(); } } @@ -17039,6 +17063,8 @@ package android.hardware.camera2 { method public abstract int setRepeatingRequest(@NonNull android.hardware.camera2.CaptureRequest, @Nullable android.hardware.camera2.CameraCaptureSession.CaptureCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException; method public int setSingleRepeatingRequest(@NonNull android.hardware.camera2.CaptureRequest, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraCaptureSession.CaptureCallback) throws android.hardware.camera2.CameraAccessException; method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException; + method public boolean supportsOfflineProcessing(@NonNull android.view.Surface); + method @Nullable public android.hardware.camera2.CameraOfflineSession switchToOffline(@NonNull java.util.Collection<android.view.Surface>, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraOfflineSession.CameraOfflineSessionCallback) throws android.hardware.camera2.CameraAccessException; method public void updateOutputConfiguration(android.hardware.camera2.params.OutputConfiguration) throws android.hardware.camera2.CameraAccessException; } @@ -17463,6 +17489,20 @@ package android.hardware.camera2 { field public static final int TONEMAP_PRESET_CURVE_SRGB = 0; // 0x0 } + public abstract class CameraOfflineSession extends android.hardware.camera2.CameraCaptureSession { + ctor public CameraOfflineSession(); + } + + public abstract static class CameraOfflineSession.CameraOfflineSessionCallback { + ctor public CameraOfflineSession.CameraOfflineSessionCallback(); + method public abstract void onClosed(@NonNull android.hardware.camera2.CameraOfflineSession); + method public abstract void onError(@NonNull android.hardware.camera2.CameraOfflineSession, int); + method public abstract void onIdle(@NonNull android.hardware.camera2.CameraOfflineSession); + method public abstract void onReady(@NonNull android.hardware.camera2.CameraOfflineSession); + method public abstract void onSwitchFailed(@NonNull android.hardware.camera2.CameraOfflineSession); + field public static final int STATUS_INTERNAL_ERROR = 0; // 0x0 + } + public class CaptureFailure { method public long getFrameNumber(); method @Nullable public String getPhysicalCameraId(); @@ -23371,12 +23411,12 @@ package android.location { method public int getAccumulatedDeltaRangeState(); method public double getAccumulatedDeltaRangeUncertaintyMeters(); method public double getAutomaticGainControlLevelDb(); - method @FloatRange(from=0, to=50) public double getBasebandCn0DbHz(); + method @FloatRange(from=0, to=63) public double getBasebandCn0DbHz(); method @Deprecated public long getCarrierCycles(); method public float getCarrierFrequencyHz(); method @Deprecated public double getCarrierPhase(); method @Deprecated public double getCarrierPhaseUncertainty(); - method public double getCn0DbHz(); + method @FloatRange(from=0, to=63) public double getCn0DbHz(); method @NonNull public String getCodeType(); method public int getConstellationType(); method public int getMultipathIndicator(); @@ -23458,6 +23498,8 @@ package android.location { field public static final int STATUS_PARITY_PASSED = 1; // 0x1 field public static final int STATUS_PARITY_REBUILT = 2; // 0x2 field public static final int STATUS_UNKNOWN = 0; // 0x0 + field public static final int TYPE_BDS_CNAV1 = 1283; // 0x503 + field public static final int TYPE_BDS_CNAV2 = 1284; // 0x504 field public static final int TYPE_BDS_D1 = 1281; // 0x501 field public static final int TYPE_BDS_D2 = 1282; // 0x502 field public static final int TYPE_GAL_F = 1538; // 0x602 @@ -23467,6 +23509,9 @@ package android.location { field public static final int TYPE_GPS_L1CA = 257; // 0x101 field public static final int TYPE_GPS_L2CNAV = 258; // 0x102 field public static final int TYPE_GPS_L5CNAV = 259; // 0x103 + field public static final int TYPE_IRN_L5CA = 1793; // 0x701 + field public static final int TYPE_QZS_L1CA = 1025; // 0x401 + field public static final int TYPE_SBS = 513; // 0x201 field public static final int TYPE_UNKNOWN = 0; // 0x0 } @@ -23793,6 +23838,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(); @@ -24116,11 +24163,44 @@ package android.media { method public void onAudioFocusChange(int); } + public final class AudioMetadata { + method @NonNull public static android.media.AudioMetadata.Map createMap(); + } + + public static class AudioMetadata.Format { + field @NonNull public static final android.media.AudioMetadata.Key<java.lang.Boolean> KEY_ATMOS_PRESENT; + field @NonNull public static final android.media.AudioMetadata.Key<java.lang.Integer> KEY_AUDIO_ENCODING; + field @NonNull public static final android.media.AudioMetadata.Key<java.lang.Integer> KEY_BIT_RATE; + field @NonNull public static final android.media.AudioMetadata.Key<java.lang.Integer> KEY_BIT_WIDTH; + field @NonNull public static final android.media.AudioMetadata.Key<java.lang.Integer> KEY_CHANNEL_MASK; + field @NonNull public static final android.media.AudioMetadata.Key<java.lang.String> KEY_MIME; + field @NonNull public static final android.media.AudioMetadata.Key<java.lang.Integer> KEY_SAMPLE_RATE; + } + + public static interface AudioMetadata.Key<T> { + method @NonNull public String getName(); + method @NonNull public Class<T> getValueClass(); + } + + public static interface AudioMetadata.Map extends android.media.AudioMetadata.ReadMap { + method @Nullable public <T> T remove(@NonNull android.media.AudioMetadata.Key<T>); + method @Nullable public <T> T set(@NonNull android.media.AudioMetadata.Key<T>, @NonNull T); + } + + public static interface AudioMetadata.ReadMap { + method public <T> boolean containsKey(@NonNull android.media.AudioMetadata.Key<T>); + method @NonNull public android.media.AudioMetadata.Map dup(); + method @Nullable public <T> T get(@NonNull android.media.AudioMetadata.Key<T>); + method public int size(); + } + 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(); } @@ -24128,9 +24208,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 { @@ -24301,6 +24383,7 @@ package android.media { ctor @Deprecated public AudioTrack(int, int, int, int, int, int) throws java.lang.IllegalArgumentException; ctor @Deprecated public AudioTrack(int, int, int, int, int, int, int) throws java.lang.IllegalArgumentException; ctor public AudioTrack(android.media.AudioAttributes, android.media.AudioFormat, int, int, int) throws java.lang.IllegalArgumentException; + method public void addOnCodecFormatChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioTrack.OnCodecFormatChangedListener); method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler); method @Deprecated public void addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler); method public int attachAuxEffect(int); @@ -24308,12 +24391,14 @@ package android.media { method protected void finalize(); method public void flush(); method @NonNull public android.media.AudioAttributes getAudioAttributes(); + method public float getAudioDescriptionMixLeveldB(); method public int getAudioFormat(); method public int getAudioSessionId(); method @IntRange(from=0) public int getBufferCapacityInFrames(); method @IntRange(from=0) public int getBufferSizeInFrames(); method public int getChannelConfiguration(); method public int getChannelCount(); + method public int getDualMonoMode(); method @NonNull public android.media.AudioFormat getFormat(); method public static float getMaxVolume(); method public android.os.PersistableBundle getMetrics(); @@ -24344,10 +24429,13 @@ package android.media { method public void registerStreamEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioTrack.StreamEventCallback); method public void release(); method public int reloadStaticData(); + method public void removeOnCodecFormatChangedListener(@NonNull android.media.AudioTrack.OnCodecFormatChangedListener); method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener); method @Deprecated public void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener); + method public boolean setAudioDescriptionMixLeveldB(@FloatRange(to=48.0f, toInclusive=true) float); method public int setAuxEffectSendLevel(@FloatRange(from=0.0) float); method public int setBufferSizeInFrames(@IntRange(from=0) int); + method public boolean setDualMonoMode(int); method public int setLoopPoints(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0xffffffff) int); method public int setNotificationMarkerPosition(int); method public void setOffloadDelayPadding(@IntRange(from=0) int, @IntRange(from=0) int); @@ -24372,6 +24460,12 @@ package android.media { method public int write(@NonNull float[], int, int, int); method public int write(@NonNull java.nio.ByteBuffer, int, int); method public int write(@NonNull java.nio.ByteBuffer, int, int, long); + field public static final int DUAL_MONO_MODE_LL = 2; // 0x2 + field public static final int DUAL_MONO_MODE_LR = 1; // 0x1 + field public static final int DUAL_MONO_MODE_OFF = 0; // 0x0 + field public static final int DUAL_MONO_MODE_RR = 3; // 0x3 + field public static final int ENCAPSULATION_METADATA_TYPE_DVB_AD_DESCRIPTOR = 2; // 0x2 + field public static final int ENCAPSULATION_METADATA_TYPE_FRAMEWORK_TUNER = 1; // 0x1 field public static final int ENCAPSULATION_MODE_ELEMENTARY_STREAM = 1; // 0x1 field public static final int ENCAPSULATION_MODE_HANDLE = 2; // 0x2 field public static final int ENCAPSULATION_MODE_NONE = 0; // 0x0 @@ -24417,6 +24511,10 @@ package android.media { field public static final String USAGE = "android.media.audiotrack.usage"; } + public static interface AudioTrack.OnCodecFormatChangedListener { + method public void onCodecFormatChanged(@NonNull android.media.AudioTrack, @Nullable android.media.AudioMetadata.ReadMap); + } + public static interface AudioTrack.OnPlaybackPositionUpdateListener { method public void onMarkerReached(android.media.AudioTrack); method public void onPeriodicNotification(android.media.AudioTrack); @@ -24917,7 +25015,9 @@ package android.media { method @Deprecated @NonNull public java.nio.ByteBuffer[] getOutputBuffers(); method @NonNull public android.media.MediaFormat getOutputFormat(); method @NonNull public android.media.MediaFormat getOutputFormat(int); + method @NonNull public android.media.MediaCodec.OutputFrame getOutputFrame(int); method @Nullable public android.media.Image getOutputImage(int); + method @NonNull public android.media.MediaCodec.QueueRequest getQueueRequest(int); method public void queueInputBuffer(int, int, int, long, int) throws android.media.MediaCodec.CryptoException; method public void queueSecureInputBuffer(int, int, @NonNull android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException; method public void release(); @@ -24941,6 +25041,7 @@ package android.media { field public static final int BUFFER_FLAG_PARTIAL_FRAME = 8; // 0x8 field @Deprecated public static final int BUFFER_FLAG_SYNC_FRAME = 1; // 0x1 field public static final int CONFIGURE_FLAG_ENCODE = 1; // 0x1 + field public static final int CONFIGURE_FLAG_USE_BLOCK_MODEL = 2; // 0x2 field public static final int CRYPTO_MODE_AES_CBC = 2; // 0x2 field public static final int CRYPTO_MODE_AES_CTR = 1; // 0x1 field public static final int CRYPTO_MODE_UNENCRYPTED = 0; // 0x0 @@ -25017,6 +25118,24 @@ package android.media { method public void set(int, int); } + public static final class MediaCodec.GraphicBlock { + method protected void finalize(); + method public static boolean isCodecCopyFreeCompatible(@NonNull String[]); + method public boolean isMappable(); + method @NonNull public android.media.Image map(); + method @NonNull public static android.media.MediaCodec.GraphicBlock obtain(int, int, int, long, @NonNull String[]); + method public void recycle(); + } + + public static final class MediaCodec.LinearBlock { + method protected void finalize(); + method public static boolean isCodecCopyFreeCompatible(@NonNull String[]); + method public boolean isMappable(); + method @NonNull public java.nio.ByteBuffer map(); + method @Nullable public static android.media.MediaCodec.LinearBlock obtain(int, @NonNull String[]); + method public void recycle(); + } + public static final class MediaCodec.MetricsConstants { field public static final String CODEC = "android.media.mediacodec.codec"; field public static final String ENCODER = "android.media.mediacodec.encoder"; @@ -25034,6 +25153,27 @@ package android.media { method public void onFrameRendered(@NonNull android.media.MediaCodec, long, long); } + public static final class MediaCodec.OutputFrame { + method public void getChangedKeys(@NonNull java.util.Set<java.lang.String>); + method public int getFlags(); + method @NonNull public android.media.MediaFormat getFormat(); + method @Nullable public android.media.MediaCodec.GraphicBlock getGraphicBlock(); + method @Nullable public android.media.MediaCodec.LinearBlock getLinearBlock(); + method public long getPresentationTimeUs(); + } + + public final class MediaCodec.QueueRequest { + method public void queue(); + method @NonNull public android.media.MediaCodec.QueueRequest setByteBufferParameter(@NonNull String, @NonNull java.nio.ByteBuffer); + method @NonNull public android.media.MediaCodec.QueueRequest setEncryptedLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, @NonNull android.media.MediaCodec.CryptoInfo, long, int); + method @NonNull public android.media.MediaCodec.QueueRequest setFloatParameter(@NonNull String, float); + method @NonNull public android.media.MediaCodec.QueueRequest setGraphicBlock(@NonNull android.media.MediaCodec.GraphicBlock, long, int); + method @NonNull public android.media.MediaCodec.QueueRequest setIntegerParameter(@NonNull String, int); + method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, long, int); + method @NonNull public android.media.MediaCodec.QueueRequest setLongParameter(@NonNull String, long); + method @NonNull public android.media.MediaCodec.QueueRequest setStringParameter(@NonNull String, @NonNull String); + } + public final class MediaCodecInfo { method @NonNull public String getCanonicalName(); method public android.media.MediaCodecInfo.CodecCapabilities getCapabilitiesForType(String); @@ -25827,6 +25967,7 @@ package android.media { field public static final String KEY_GRID_COLUMNS = "grid-cols"; field public static final String KEY_GRID_ROWS = "grid-rows"; field public static final String KEY_HAPTIC_CHANNEL_COUNT = "haptic-channel-count"; + field public static final String KEY_HARDWARE_AV_SYNC_ID = "hw-av-sync-id"; field public static final String KEY_HDR10_PLUS_INFO = "hdr10-plus-info"; field public static final String KEY_HDR_STATIC_INFO = "hdr-static-info"; field public static final String KEY_HEIGHT = "height"; @@ -29424,8 +29565,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 { @@ -29435,21 +29574,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 { @@ -29866,6 +30013,7 @@ package android.net { } @Deprecated public class NetworkInfo implements android.os.Parcelable { + ctor @Deprecated public NetworkInfo(int, int, @Nullable String, @Nullable String); method @Deprecated public int describeContents(); method @Deprecated @NonNull public android.net.NetworkInfo.DetailedState getDetailedState(); method @Deprecated public String getExtraInfo(); @@ -29880,6 +30028,7 @@ package android.net { method @Deprecated public boolean isConnectedOrConnecting(); method @Deprecated public boolean isFailover(); method @Deprecated public boolean isRoaming(); + method @Deprecated public void setDetailedState(@NonNull android.net.NetworkInfo.DetailedState, @Nullable String, @Nullable String); method @Deprecated public void writeToParcel(android.os.Parcel, int); field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkInfo> CREATOR; } @@ -30945,7 +31094,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); @@ -30958,7 +31107,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(); @@ -31794,7 +31943,7 @@ package android.net.wifi.rtt { method public boolean isLciSubelementValid(); method public boolean isZaxisSubelementValid(); method @Nullable public android.location.Address toCivicLocationAddress(); - method @Nullable public android.util.SparseArray toCivicLocationSparseArray(); + method @Nullable public android.util.SparseArray<java.lang.String> toCivicLocationSparseArray(); method @NonNull public android.location.Location toLocation(); method public void writeToParcel(android.os.Parcel, int); field public static final int ALTITUDE_FLOORS = 2; // 0x2 @@ -40032,6 +40181,7 @@ package android.provider { field public static final String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS"; field public static final String ACTION_PRIVACY_SETTINGS = "android.settings.PRIVACY_SETTINGS"; field public static final String ACTION_PROCESS_WIFI_EASY_CONNECT_URI = "android.settings.PROCESS_WIFI_EASY_CONNECT_URI"; + field public static final String ACTION_QUICK_ACCESS_WALLET_SETTINGS = "android.settings.QUICK_ACCESS_WALLET_SETTINGS"; field public static final String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS"; field public static final String ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"; field public static final String ACTION_REQUEST_SET_AUTOFILL_SERVICE = "android.settings.REQUEST_SET_AUTOFILL_SERVICE"; @@ -42218,7 +42368,6 @@ package android.security.identity { } public abstract class WritableIdentityCredential { - ctor public WritableIdentityCredential(); method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getCredentialKeyCertificateChain(@NonNull byte[]); method @NonNull public abstract byte[] personalize(@NonNull android.security.identity.PersonalizationData); } @@ -43208,6 +43357,94 @@ package android.service.notification { } +package android.service.quickaccesswallet { + + public final class GetWalletCardsCallback { + method public void onFailure(@NonNull android.service.quickaccesswallet.GetWalletCardsError); + method public void onSuccess(@NonNull android.service.quickaccesswallet.GetWalletCardsResponse); + } + + public final class GetWalletCardsError implements android.os.Parcelable { + ctor public GetWalletCardsError(@Nullable android.graphics.drawable.Icon, @Nullable CharSequence); + method public int describeContents(); + method @Nullable public android.graphics.drawable.Icon getIcon(); + method @Nullable public CharSequence getMessage(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.quickaccesswallet.GetWalletCardsError> CREATOR; + } + + public final class GetWalletCardsRequest implements android.os.Parcelable { + ctor public GetWalletCardsRequest(int, int, int, int); + method public int describeContents(); + method public int getCardHeightPx(); + method public int getCardWidthPx(); + method public int getIconSizePx(); + method public int getMaxCards(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.quickaccesswallet.GetWalletCardsRequest> CREATOR; + } + + public final class GetWalletCardsResponse implements android.os.Parcelable { + ctor public GetWalletCardsResponse(@NonNull java.util.List<android.service.quickaccesswallet.WalletCard>, int); + method public int describeContents(); + method public int getSelectedIndex(); + method @NonNull public java.util.List<android.service.quickaccesswallet.WalletCard> getWalletCards(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.quickaccesswallet.GetWalletCardsResponse> CREATOR; + } + + public abstract class QuickAccessWalletService extends android.app.Service { + ctor public QuickAccessWalletService(); + method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent); + method public abstract void onWalletCardSelected(@NonNull android.service.quickaccesswallet.SelectWalletCardRequest); + method public abstract void onWalletCardsRequested(@NonNull android.service.quickaccesswallet.GetWalletCardsRequest, @NonNull android.service.quickaccesswallet.GetWalletCardsCallback); + method public abstract void onWalletDismissed(); + method public final void sendWalletServiceEvent(@NonNull android.service.quickaccesswallet.WalletServiceEvent); + field public static final String ACTION_DISMISS_WALLET = "android.service.quickaccesswallet.action.DISMISS_WALLET"; + field public static final String ACTION_VIEW_WALLET = "android.service.quickaccesswallet.action.VIEW_WALLET"; + field public static final String ACTION_VIEW_WALLET_SETTINGS = "android.service.quickaccesswallet.action.VIEW_WALLET_SETTINGS"; + field public static final String SERVICE_INTERFACE = "android.service.quickaccesswallet.QuickAccessWalletService"; + field public static final String SERVICE_META_DATA = "android.quickaccesswallet"; + } + + public final class SelectWalletCardRequest implements android.os.Parcelable { + ctor public SelectWalletCardRequest(@NonNull String); + method public int describeContents(); + method @NonNull public String getCardId(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.quickaccesswallet.SelectWalletCardRequest> CREATOR; + } + + public final class WalletCard implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public android.graphics.drawable.Icon getCardIcon(); + method @NonNull public String getCardId(); + method @NonNull public android.graphics.drawable.Icon getCardImage(); + method @Nullable public CharSequence getCardLabel(); + method @NonNull public CharSequence getContentDescription(); + method @NonNull public android.app.PendingIntent getPendingIntent(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.quickaccesswallet.WalletCard> CREATOR; + } + + public static final class WalletCard.Builder { + ctor public WalletCard.Builder(@NonNull String, @NonNull android.graphics.drawable.Icon, @NonNull CharSequence, @NonNull android.app.PendingIntent); + method @NonNull public android.service.quickaccesswallet.WalletCard build(); + method @NonNull public android.service.quickaccesswallet.WalletCard.Builder setCardIcon(@Nullable android.graphics.drawable.Icon); + method @NonNull public android.service.quickaccesswallet.WalletCard.Builder setCardLabel(@Nullable CharSequence); + } + + public final class WalletServiceEvent implements android.os.Parcelable { + ctor public WalletServiceEvent(int); + method public int describeContents(); + method public int getEventType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.quickaccesswallet.WalletServiceEvent> CREATOR; + field public static final int TYPE_NFC_PAYMENT_STARTED = 1; // 0x1 + } + +} + package android.service.quicksettings { public final class Tile implements android.os.Parcelable { @@ -43311,7 +43548,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 { @@ -43530,6 +43767,7 @@ package android.service.wallpaper { method public void onSurfaceRedrawNeeded(android.view.SurfaceHolder); method public void onTouchEvent(android.view.MotionEvent); method public void onVisibilityChanged(boolean); + method public void onZoomChanged(@FloatRange(from=0.0f, to=1.0f) float); method public void setOffsetNotificationsEnabled(boolean); method public void setTouchEventsEnabled(boolean); } @@ -44673,6 +44911,7 @@ package android.telecom { field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2 field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80 field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10 + field public static final int PROPERTY_IS_ADHOC_CONFERENCE = 8192; // 0x2000 field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40 field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 2048; // 0x800 field public static final int PROPERTY_RTT = 1024; // 0x400 @@ -44750,6 +44989,7 @@ package android.telecom { public abstract class Conference extends android.telecom.Conferenceable { ctor public Conference(android.telecom.PhoneAccountHandle); method public final boolean addConnection(android.telecom.Connection); + method @NonNull public static android.telecom.Conference createFailedConference(@NonNull android.telecom.DisconnectCause, @NonNull android.telecom.PhoneAccountHandle); method public final void destroy(); method public final android.telecom.CallAudioState getCallAudioState(); method public final java.util.List<android.telecom.Connection> getConferenceableConnections(); @@ -44764,6 +45004,8 @@ package android.telecom { method public final android.telecom.StatusHints getStatusHints(); method public android.telecom.Connection.VideoProvider getVideoProvider(); method public int getVideoState(); + method public final boolean isRingbackRequested(); + method public void onAnswer(int); method public void onCallAudioStateChanged(android.telecom.CallAudioState); method public void onConnectionAdded(android.telecom.Connection); method public void onDisconnect(); @@ -44772,6 +45014,7 @@ package android.telecom { method public void onMerge(android.telecom.Connection); method public void onMerge(); method public void onPlayDtmfTone(char); + method public void onReject(); method public void onSeparate(android.telecom.Connection); method public void onStopDtmfTone(); method public void onSwap(); @@ -44791,6 +45034,8 @@ package android.telecom { method public final void setDisconnected(android.telecom.DisconnectCause); method public final void setExtras(@Nullable android.os.Bundle); method public final void setOnHold(); + method public final void setRingbackRequested(boolean); + method public final void setRinging(); method public final void setStatusHints(android.telecom.StatusHints); method public final void setVideoProvider(android.telecom.Connection, android.telecom.Connection.VideoProvider); method public final void setVideoState(android.telecom.Connection, int); @@ -44948,6 +45193,7 @@ package android.telecom { field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200 field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20 field public static final int PROPERTY_HIGH_DEF_AUDIO = 4; // 0x4 + field public static final int PROPERTY_IS_ADHOC_CONFERENCE = 4096; // 0x1000 field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10 field public static final int PROPERTY_IS_RTT = 256; // 0x100 field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 1024; // 0x400 @@ -45020,8 +45266,10 @@ package android.telecom { method public android.telecom.PhoneAccountHandle getAccountHandle(); method public android.net.Uri getAddress(); method public android.os.Bundle getExtras(); + method @Nullable public java.util.List<android.net.Uri> getParticipants(); method public android.telecom.Connection.RttTextStream getRttTextStream(); method public int getVideoState(); + method public boolean isAdhocConferenceCall(); method public boolean isRequestingRtt(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telecom.ConnectionRequest> CREATOR; @@ -45041,9 +45289,13 @@ package android.telecom { method public void onConference(android.telecom.Connection, android.telecom.Connection); method public void onConnectionServiceFocusGained(); method public void onConnectionServiceFocusLost(); + method @Nullable public android.telecom.Conference onCreateIncomingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest); + method public void onCreateIncomingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest); method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); method public android.telecom.Connection onCreateIncomingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); + method @Nullable public android.telecom.Conference onCreateOutgoingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest); + method public void onCreateOutgoingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest); method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); method public void onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); method public android.telecom.Connection onCreateOutgoingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); @@ -45158,6 +45410,7 @@ package android.telecom { method public boolean supportsUriScheme(String); method public android.telecom.PhoneAccount.Builder toBuilder(); method public void writeToParcel(android.os.Parcel, int); + field public static final int CAPABILITY_ADHOC_CONFERENCE_CALLING = 16384; // 0x4000 field public static final int CAPABILITY_CALL_PROVIDER = 2; // 0x2 field public static final int CAPABILITY_CALL_SUBJECT = 64; // 0x40 field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1 @@ -45353,6 +45606,7 @@ package android.telecom { method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall(int); method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle); + method public void addNewIncomingConference(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.os.Bundle); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void cancelMissedCallsNotification(); method public android.content.Intent createManageBlockedNumbersIntent(); method @Deprecated @RequiresPermission(android.Manifest.permission.ANSWER_PHONE_CALLS) public boolean endCall(); @@ -45380,6 +45634,7 @@ package android.telecom { method public void registerPhoneAccount(android.telecom.PhoneAccount); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void showInCallScreen(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void silenceRinger(); + method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void startConference(@NonNull java.util.List<android.net.Uri>, @NonNull android.os.Bundle); method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle); field public static final String ACTION_CHANGE_DEFAULT_DIALER = "android.telecom.action.CHANGE_DEFAULT_DIALER"; field public static final String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS"; @@ -45627,6 +45882,12 @@ package android.telephony { field public static final int BAND_7 = 7; // 0x7 field public static final int BAND_8 = 8; // 0x8 field public static final int BAND_9 = 9; // 0x9 + field public static final int BAND_A = 101; // 0x65 + field public static final int BAND_B = 102; // 0x66 + field public static final int BAND_C = 103; // 0x67 + field public static final int BAND_D = 104; // 0x68 + field public static final int BAND_E = 105; // 0x69 + field public static final int BAND_F = 106; // 0x6a } public final class AvailableNetworkInfo implements android.os.Parcelable { @@ -45694,6 +45955,7 @@ package android.telephony { field public static final String KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrp_thresholds_int_array"; field public static final String KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrq_thresholds_int_array"; field public static final String KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY = "5g_nr_sssinr_thresholds_int_array"; + field public static final String KEY_5G_WATCHDOG_TIME_MS_LONG = "5g_watchdog_time_long"; field public static final String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool"; field public static final String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool"; field public static final String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call"; @@ -45777,6 +46039,7 @@ package android.telephony { field public static final String KEY_DATA_LIMIT_NOTIFICATION_BOOL = "data_limit_notification_bool"; field public static final String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long"; field public static final String KEY_DATA_RAPID_NOTIFICATION_BOOL = "data_rapid_notification_bool"; + field public static final String KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG = "data_switch_validation_timeout_long"; field public static final String KEY_DATA_WARNING_NOTIFICATION_BOOL = "data_warning_notification_bool"; field public static final String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long"; field public static final String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string"; @@ -45859,6 +46122,8 @@ package android.telephony { field public static final String KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL = "only_auto_select_in_home_network"; field public static final String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array"; field public static final String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool"; + field public static final String KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG = "opportunistic_network_backoff_time_long"; + field public static final String KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG = "opportunistic_network_data_switch_exit_hysteresis_time_long"; field public static final String KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_HYSTERESIS_TIME_LONG = "opportunistic_network_data_switch_hysteresis_time_long"; field public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_OR_EXIT_HYSTERESIS_TIME_LONG = "opportunistic_network_entry_or_exit_hysteresis_time_long"; field public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_BANDWIDTH_INT = "opportunistic_network_entry_threshold_bandwidth_int"; @@ -45866,6 +46131,9 @@ package android.telephony { field public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT = "opportunistic_network_entry_threshold_rssnr_int"; field public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT = "opportunistic_network_exit_threshold_rsrp_int"; field public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT = "opportunistic_network_exit_threshold_rssnr_int"; + field public static final String KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG = "opportunistic_network_max_backoff_time_long"; + field public static final String KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG = "opportunistic_network_ping_pong_time_long"; + field public static final String KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL = "ping_test_before_data_switch_bool"; field public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool"; field public static final String KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL = "prevent_clir_activation_and_deactivation_code_bool"; field public static final String KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY = "radio_restart_failure_causes_int_array"; @@ -45882,6 +46150,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"; @@ -45892,6 +46161,7 @@ package android.telephony { field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool"; field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool"; field public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool"; + field public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL = "support_adhoc_conference_calls_bool"; field public static final String KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL = "support_clir_network_default_bool"; field public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool"; field public static final String KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL = "support_emergency_sms_over_ims_bool"; @@ -45901,6 +46171,7 @@ package android.telephony { field public static final String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool"; field public static final String KEY_SUPPORT_TDSCDMA_BOOL = "support_tdscdma_bool"; field public static final String KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY = "support_tdscdma_roaming_networks_string_array"; + field public static final String KEY_SWITCH_DATA_TO_PRIMARY_IF_PRIMARY_IS_OOS_BOOL = "switch_data_to_primary_if_primary_is_oos_bool"; field public static final String KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL = "treat_downgraded_video_calls_as_video_calls_bool"; field public static final String KEY_TTY_SUPPORTED_BOOL = "tty_supported_bool"; field public static final String KEY_UNLOGGABLE_NUMBERS_STRING_ARRAY = "unloggable_numbers_string_array"; @@ -45926,6 +46197,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."; @@ -45936,6 +46216,59 @@ package android.telephony { field public static final String KEY_WIFI_OFF_DEFERRING_TIME_INT = "ims.wifi_off_deferring_time_int"; } + public static final class CarrierConfigManager.Iwlan { + field public static final int AUTHENTICATION_METHOD_CERT = 1; // 0x1 + field public static final int AUTHENTICATION_METHOD_EAP_ONLY = 0; // 0x0 + field public static final int DH_GROUP_1024_BIT_MODP = 2; // 0x2 + field public static final int DH_GROUP_2048_BIT_MODP = 14; // 0xe + field public static final int DH_GROUP_NONE = 0; // 0x0 + field public static final int ENCRYPTION_ALGORITHM_3DES = 3; // 0x3 + field public static final int ENCRYPTION_ALGORITHM_AES_CBC = 12; // 0xc + field public static final int ENCRYPTION_ALGORITHM_AES_GCM_12 = 19; // 0x13 + field public static final int ENCRYPTION_ALGORITHM_AES_GCM_16 = 20; // 0x14 + field public static final int ENCRYPTION_ALGORITHM_AES_GCM_8 = 18; // 0x12 + field public static final int EPDG_ADDRESS_PCO = 2; // 0x2 + field public static final int EPDG_ADDRESS_PLMN = 1; // 0x1 + field public static final int EPDG_ADDRESS_STATIC = 0; // 0x0 + field public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 = 5; // 0x5 + field public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 = 2; // 0x2 + field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 = 12; // 0xc + field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 = 13; // 0xd + field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = 14; // 0xe + field public static final int INTEGRITY_ALGORITHM_NONE = 0; // 0x0 + field public static final String KEY_CHILD_SA_REKEY_HARD_TIMER_SEC_INT = "iwlan.child_sa_rekey_hard_timer_sec_int"; + field public static final String KEY_CHILD_SA_REKEY_SOFT_TIMER_SEC_INT = "iwlan.child_sa_rekey_soft_timer_sec_int"; + field public static final String KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY = "iwlan.child_session_aes_cbc_key_size_int_array"; + field public static final String KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = "iwlan.child_encryption_aes_ctr_key_size_int_array"; + field public static final String KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY = "iwlan.diffie_hellman_groups_int_array"; + field public static final String KEY_DPD_TIMER_SEC_INT = "iwlan.dpd_timer_sec_int"; + field public static final String KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY = "iwlan.epdg_address_priority_int_array"; + field public static final String KEY_EPDG_AUTHENTICATION_METHOD_INT = "iwlan.epdg_authentication_method_int"; + field public static final String KEY_EPDG_STATIC_ADDRESS_ROAMING_STRING = "iwlan.epdg_static_address_roaming_string"; + field public static final String KEY_EPDG_STATIC_ADDRESS_STRING = "iwlan.epdg_static_address_string"; + field public static final String KEY_IKE_FRAGMENTATION_ENABLED_BOOL = "iwlan.ike_fragmentation_enabled_bool"; + field public static final String KEY_IKE_REKEY_HARD_TIMER_SEC_INT = "iwlan.ike_rekey_hard_timer_in_sec"; + field public static final String KEY_IKE_REKEY_SOFT_TIMER_SEC_INT = "iwlan.ike_rekey_soft_timer_sec_int"; + field public static final String KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY = "iwlan.ike_session_encryption_aes_cbc_key_size_int_array"; + field public static final String KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = "iwlan.ike_session_aes_ctr_key_size_int_array"; + field public static final int KEY_LEN_AES_128 = 128; // 0x80 + field public static final int KEY_LEN_AES_192 = 192; // 0xc0 + field public static final int KEY_LEN_AES_256 = 256; // 0x100 + field public static final int KEY_LEN_UNUSED = 0; // 0x0 + field public static final String KEY_MAX_RETRIES_INT = "iwlan.max_retries_int"; + field public static final String KEY_MCC_MNCS_STRING_ARRAY = "iwlan.mcc_mncs_string_array"; + field public static final String KEY_NATT_ENABLED_BOOL = "iwlan.natt_enabled_bool"; + field public static final String KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT = "iwlan.natt_keep_alive_timer_sec_int"; + field public static final String KEY_PREFIX = "iwlan."; + field public static final String KEY_RETRANSMIT_TIMER_SEC_INT = "iwlan.retransmit_timer_sec_int"; + field public static final String KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = "iwlan.supported_child_session_encryption_algorithms_int_array"; + field public static final String KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = "iwlan.supported_ike_session_encryption_algorithms_int_array"; + field public static final String KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY = "iwlan.supported_integrity_algorithms_int_array"; + field public static final String KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY = "iwlan.supported_prf_algorithms_int_array"; + field public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC = 4; // 0x4 + field public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2; // 0x2 + } + public abstract class CellIdentity implements android.os.Parcelable { method public int describeContents(); method @Nullable public CharSequence getOperatorAlphaLong(); @@ -45955,6 +46288,7 @@ package android.telephony { } public final class CellIdentityGsm extends android.telephony.CellIdentity { + method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns(); method public int getArfcn(); method public int getBsic(); method public int getCid(); @@ -45970,8 +46304,11 @@ 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(); method public int getEarfcn(); method @Deprecated public int getMcc(); method @Nullable public String getMccString(); @@ -45985,6 +46322,8 @@ package android.telephony { } public final class CellIdentityNr extends android.telephony.CellIdentity { + method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns(); + method @NonNull public java.util.List<java.lang.Integer> getBands(); method @Nullable public String getMccString(); method @Nullable public String getMncString(); method public long getNci(); @@ -45996,7 +46335,9 @@ package android.telephony { } public final class CellIdentityTdscdma extends android.telephony.CellIdentity { + method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns(); method public int getCid(); + method @Nullable public android.telephony.ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo(); method public int getCpid(); method public int getLac(); method @Nullable public String getMccString(); @@ -46008,7 +46349,9 @@ package android.telephony { } public final class CellIdentityWcdma extends android.telephony.CellIdentity { + method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns(); method public int getCid(); + method @Nullable public android.telephony.ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo(); method public int getLac(); method @Deprecated public int getMcc(); method @Nullable public String getMccString(); @@ -46178,6 +46521,15 @@ package android.telephony { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthWcdma> CREATOR; } + public final class ClosedSubscriberGroupInfo implements android.os.Parcelable { + method public int describeContents(); + method @IntRange(from=0, to=134217727) public int getCsgIdentity(); + method public boolean getCsgIndicator(); + method @NonNull public String getHomeNodebName(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ClosedSubscriberGroupInfo> CREATOR; + } + public class IccOpenLogicalChannelResponse implements android.os.Parcelable { method public int describeContents(); method public int getChannel(); @@ -46326,8 +46678,32 @@ package android.telephony { public final class PhoneCapability implements android.os.Parcelable { method public int describeContents(); + method @NonNull public java.util.List<java.lang.Integer> getBands(int); + method @NonNull public java.util.List<java.util.List<java.lang.Long>> getConcurrentFeaturesSupport(); + method @NonNull public java.util.List<java.lang.String> getLogicalModemUuids(); + method public int getMaxActiveDedicatedBearers(); + method public int getMaxActiveInternetData(); + method public int getMaxActivePsVoice(); + method public long getPsDataConnectionLingerTimeMillis(); + method @NonNull public java.util.List<android.telephony.SimSlotCapability> getSimSlotCapabilities(); + method public long getSupportedRats(); + method public int getUeCategory(boolean, int); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhoneCapability> CREATOR; + field public static final long MODEM_FEATURE_3GPP2_REG = 1L; // 0x1L + field public static final long MODEM_FEATURE_3GPP_REG = 2L; // 0x2L + field public static final long MODEM_FEATURE_CDMA2000_EHRPD_REG = 4L; // 0x4L + field public static final long MODEM_FEATURE_CSIM = 8192L; // 0x2000L + field public static final long MODEM_FEATURE_CS_VOICE_SESSION = 512L; // 0x200L + field public static final long MODEM_FEATURE_DEDICATED_BEARER = 2048L; // 0x800L + field public static final long MODEM_FEATURE_EUTRAN_REG = 32L; // 0x20L + field public static final long MODEM_FEATURE_EUTRA_NR_DUAL_CONNECTIVITY_REG = 128L; // 0x80L + field public static final long MODEM_FEATURE_GERAN_REG = 8L; // 0x8L + field public static final long MODEM_FEATURE_INTERACTIVE_DATA_SESSION = 1024L; // 0x400L + field public static final long MODEM_FEATURE_NETWORK_SCAN = 4096L; // 0x1000L + field public static final long MODEM_FEATURE_NGRAN_REG = 64L; // 0x40L + field public static final long MODEM_FEATURE_PS_VOICE_REG = 256L; // 0x100L + field public static final long MODEM_FEATURE_UTRAN_REG = 16L; // 0x10L } public class PhoneNumberFormattingTextWatcher implements android.text.TextWatcher { @@ -46430,7 +46806,7 @@ package android.telephony { field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000 field public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4 field public static final int LISTEN_NONE = 0; // 0x0 - field @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000 + field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000 field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000 field public static final int LISTEN_SERVICE_STATE = 1; // 0x1 field @Deprecated public static final int LISTEN_SIGNAL_STRENGTH = 2; // 0x2 @@ -46514,6 +46890,18 @@ package android.telephony { field public static final int INVALID = 2147483647; // 0x7fffffff } + public final class SimSlotCapability implements android.os.Parcelable { + method public int describeContents(); + method public int getPhysicalSlotIndex(); + method public int getSlotType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SimSlotCapability> CREATOR; + field public static final int SLOT_TYPE_EUICC = 3; // 0x3 + field public static final int SLOT_TYPE_IUICC = 2; // 0x2 + field public static final int SLOT_TYPE_SOFT_SIM = 4; // 0x4 + field public static final int SLOT_TYPE_UICC = 1; // 0x1 + } + public final class SmsManager { method public String createAppSpecificSmsToken(android.app.PendingIntent); method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent); @@ -46856,6 +47244,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(); @@ -46866,8 +47255,10 @@ package android.telephony { method public String getNetworkCountryIso(); method public String getNetworkOperator(); method public String getNetworkOperatorName(); + method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getNetworkSelectionMode(); method public String getNetworkSpecifier(); method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getNetworkType(); + method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.PhoneCapability getPhoneCapability(); method @Deprecated public int getPhoneCount(); method public int getPhoneType(); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription(); @@ -46909,6 +47300,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(); @@ -47000,6 +47392,9 @@ package android.telephony { field public static final int MULTISIM_ALLOWED = 0; // 0x0 field public static final int MULTISIM_NOT_SUPPORTED_BY_CARRIER = 2; // 0x2 field public static final int MULTISIM_NOT_SUPPORTED_BY_HARDWARE = 1; // 0x1 + field public static final int NETWORK_SELECTION_MODE_AUTO = 1; // 0x1 + field public static final int NETWORK_SELECTION_MODE_MANUAL = 2; // 0x2 + field public static final int NETWORK_SELECTION_MODE_UNKNOWN = 0; // 0x0 field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7 field public static final int NETWORK_TYPE_CDMA = 4; // 0x4 field public static final int NETWORK_TYPE_EDGE = 2; // 0x2 @@ -47314,10 +47709,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_GSMA_INSTALL_ERROR = 10009; // 0x2719 + 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_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 } } @@ -52240,6 +52667,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(); @@ -52284,7 +52725,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); @@ -52424,7 +52867,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); @@ -52434,7 +52877,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); @@ -52587,7 +53030,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(); @@ -52615,7 +53058,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); @@ -52753,7 +53196,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); @@ -52895,7 +53338,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); @@ -52932,7 +53375,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); @@ -53110,18 +53553,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 @@ -53245,8 +53688,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 { @@ -53883,6 +54326,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); @@ -53921,6 +54365,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); @@ -54015,6 +54460,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); } @@ -54059,23 +54508,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(); @@ -54091,17 +54540,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(); @@ -54110,7 +54566,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 { @@ -54138,7 +54593,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); } @@ -54159,19 +54614,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); } @@ -54197,9 +54660,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 @@ -54220,13 +54689,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 @@ -54240,8 +54709,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 @@ -54267,7 +54736,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 @@ -54324,7 +54793,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; @@ -54334,6 +54803,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 { @@ -55431,7 +55906,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 diff --git a/api/module-app-current.txt b/api/module-app-current.txt index db774ef8ea2e..dadbd79e0e8d 100644 --- a/api/module-app-current.txt +++ b/api/module-app-current.txt @@ -9,6 +9,10 @@ package android.app { package android.provider { + public final class DocumentsContract { + method @NonNull public static android.net.Uri buildDocumentUriAsUser(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); + } + public static final class Settings.Global extends android.provider.Settings.NameValueTable { field public static final String COMMON_CRITERIA_MODE = "common_criteria_mode"; } diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt index 1cb1c20c738c..6f4a27ecfb0b 100644 --- a/api/module-lib-current.txt +++ b/api/module-lib-current.txt @@ -118,10 +118,32 @@ package android.timezone { } public final class TimeZoneFinder { + method @Nullable public String getIanaVersion(); method @NonNull public static android.timezone.TimeZoneFinder getInstance(); method @Nullable public android.timezone.CountryTimeZones lookupCountryTimeZones(@NonNull String); } + public final class TzDataSetVersion { + method public static int currentFormatMajorVersion(); + method public static int currentFormatMinorVersion(); + method public int getFormatMajorVersion(); + method public int getFormatMinorVersion(); + method public int getRevision(); + method @NonNull public String getRulesVersion(); + method public static boolean isCompatibleWithThisDevice(android.timezone.TzDataSetVersion); + method @NonNull public static android.timezone.TzDataSetVersion read() throws java.io.IOException, android.timezone.TzDataSetVersion.TzDataSetException; + } + + public static class TzDataSetVersion.TzDataSetException extends java.lang.Exception { + ctor public TzDataSetVersion.TzDataSetException(String); + ctor public TzDataSetVersion.TzDataSetException(String, Throwable); + } + + public final class ZoneInfoDb { + method @NonNull public static android.timezone.ZoneInfoDb getInstance(); + method @NonNull public String getVersion(); + } + } package android.util { diff --git a/api/system-current.txt b/api/system-current.txt index 66b779d51669..8a30cae29280 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"; } @@ -388,6 +393,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"; @@ -845,6 +851,7 @@ package android.app.admin { method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioningConfigApplied(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isManagedKiosk(); + method public boolean isSecondaryLockscreenEnabled(int); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isUnattendedManagedKiosk(); method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long); method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean); @@ -1173,6 +1180,7 @@ package android.app.contentsuggestions { method public void classifyContentSelections(@NonNull android.app.contentsuggestions.ClassificationsRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.contentsuggestions.ContentSuggestionsManager.ClassificationsCallback); method public boolean isEnabled(); method public void notifyInteraction(@NonNull String, @NonNull android.os.Bundle); + method public void provideContextImage(@NonNull android.graphics.Bitmap, @NonNull android.os.Bundle); method public void provideContextImage(int, @NonNull android.os.Bundle); method public void suggestContentSelections(@NonNull android.app.contentsuggestions.SelectionsRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.contentsuggestions.ContentSuggestionsManager.SelectionsCallback); } @@ -1604,12 +1612,13 @@ package android.bluetooth { field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED"; } - public final class BluetoothPan implements android.bluetooth.BluetoothProfile { - method protected void finalize(); + public final class BluetoothPan implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile { + method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public void close(); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH) protected void finalize(); method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); - method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice); - method public boolean isTetheringOn(); - method public void setBluetoothTethering(boolean); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isTetheringOn(); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public void setBluetoothTethering(boolean); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED"; field public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE"; @@ -1627,10 +1636,14 @@ package android.bluetooth { } public interface BluetoothProfile { + field public static final int A2DP_SINK = 11; // 0xb + field public static final int AVRCP_CONTROLLER = 12; // 0xc field public static final int CONNECTION_POLICY_ALLOWED = 100; // 0x64 field public static final int CONNECTION_POLICY_FORBIDDEN = 0; // 0x0 field public static final int CONNECTION_POLICY_UNKNOWN = -1; // 0xffffffff + field public static final int HEADSET_CLIENT = 16; // 0x10 field public static final int PAN = 5; // 0x5 + field public static final int PBAP_CLIENT = 17; // 0x11 field @Deprecated public static final int PRIORITY_OFF = 0; // 0x0 field @Deprecated public static final int PRIORITY_ON = 100; // 0x64 } @@ -1706,13 +1719,20 @@ 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); } } package android.content { + public class ApexContext { + method @NonNull public static android.content.ApexContext getApexContext(@NonNull String); + method @NonNull public java.io.File getCredentialProtectedDataDirForUser(@NonNull android.os.UserHandle); + method @NonNull public java.io.File getDeviceProtectedDataDir(); + method @NonNull public java.io.File getDeviceProtectedDataDirForUser(@NonNull android.os.UserHandle); + } + public abstract class BroadcastReceiver { method @NonNull public final android.os.UserHandle getSendingUser(); } @@ -2133,11 +2153,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 { @@ -2286,6 +2311,7 @@ package android.content.pm { field public static final int MATCH_ANY_USER = 4194304; // 0x400000 field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000 field public static final int MATCH_INSTANT = 8388608; // 0x800000 + field public static final int MODULE_APEX_NAME = 1; // 0x1 field public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 1; // 0x1 field public static final int RESTRICTION_HIDE_NOTIFICATIONS = 2; // 0x2 field public static final int RESTRICTION_NONE = 0; // 0x0 @@ -2434,6 +2460,15 @@ package android.content.rollback { } +package android.debug { + + public class AdbManager { + method @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING) public boolean isAdbWifiQrSupported(); + method @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING) public boolean isAdbWifiSupported(); + } + +} + package android.hardware { public final class Sensor { @@ -2898,6 +2933,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 { @@ -3610,9 +3687,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; @@ -3651,6 +3753,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 { @@ -3663,6 +3775,7 @@ package android.hardware.usb { method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public long getCurrentFunctions(); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_USB) public java.util.List<android.hardware.usb.UsbPort> getPorts(); method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void grantPermission(android.hardware.usb.UsbDevice, String); + method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void resetUsbGadget(); method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long); field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED"; field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE"; @@ -4097,17 +4210,24 @@ package android.media { method public int getAllFlags(); method public android.os.Bundle getBundle(); method public int getCapturePreset(); + method public int getSystemUsage(); + method public static boolean isSystemUsage(int); field public static final int FLAG_BEACON = 8; // 0x8 field public static final int FLAG_BYPASS_INTERRUPTION_POLICY = 64; // 0x40 field public static final int FLAG_BYPASS_MUTE = 128; // 0x80 field public static final int FLAG_HW_HOTWORD = 32; // 0x20 - field @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public static final int USAGE_CALL_ASSISTANT = 17; // 0x11 + field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int USAGE_ANNOUNCEMENT = 1003; // 0x3eb + field @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public static final int USAGE_CALL_ASSISTANT = 17; // 0x11 + field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int USAGE_EMERGENCY = 1000; // 0x3e8 + field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int USAGE_SAFETY = 1001; // 0x3e9 + field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int USAGE_VEHICLE_STATUS = 1002; // 0x3ea } public static class AudioAttributes.Builder { method public android.media.AudioAttributes.Builder addBundle(@NonNull android.os.Bundle); method public android.media.AudioAttributes.Builder setCapturePreset(int); method public android.media.AudioAttributes.Builder setInternalCapturePreset(int); + method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioAttributes.Builder setSystemUsage(int); } public final class AudioDeviceAddress implements android.os.Parcelable { @@ -4152,12 +4272,15 @@ 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); + method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages(); method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); method public boolean isAudioServerRunning(); method public boolean isHdmiSystemAudioSupported(); @@ -4167,9 +4290,11 @@ 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); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setVolumeIndexForAttributes(@NonNull android.media.AudioAttributes, int, int); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy); @@ -4292,6 +4417,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 { @@ -4312,9 +4438,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 @@ -4584,7 +4712,8 @@ package android.media.tv { } public final class TvInputManager { - method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, android.media.tv.TvInputInfo, android.media.tv.TvInputManager.HardwareCallback); + method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, @NonNull android.media.tv.TvInputInfo, @NonNull android.media.tv.TvInputManager.HardwareCallback); + method @Nullable @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, @NonNull android.media.tv.TvInputInfo, @NonNull android.media.tv.TvInputManager.HardwareCallback, @Nullable String, int); method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void addBlockedRating(@NonNull android.media.tv.TvContentRating); method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean captureFrame(String, android.view.Surface, android.media.tv.TvStreamConfig); method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(String); @@ -4685,6 +4814,7 @@ package android.media.tv.tuner { method public long getSectionFilterLength(); method public int getTsFilterCount(); method public int getVideoFilterCount(); + method public boolean isTimeFilterSupported(); } public class Descrambler implements java.lang.AutoCloseable { @@ -4727,10 +4857,18 @@ package android.media.tv.tuner { method public void onEvent(int); } - public final class Tuner implements java.lang.AutoCloseable { - ctor public Tuner(@NonNull android.content.Context); + public class Tuner implements java.lang.AutoCloseable { + ctor @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public Tuner(@NonNull android.content.Context, @NonNull String, int, @Nullable android.media.tv.tuner.Tuner.OnResourceLostListener); method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void clearOnTuneEventListener(); - method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.Tuner.Descrambler openDescrambler(); + method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void close(); + method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int connectCiCam(int); + method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int disconnectCiCam(); + method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int getAvSyncHwId(@NonNull android.media.tv.tuner.filter.Filter); + method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public long getAvSyncTime(int); + method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities(@NonNull android.content.Context); + method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo(); + method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]); + method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.Descrambler openDescrambler(); method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.dvr.DvrPlayback openDvrPlayback(long, @Nullable java.util.concurrent.Executor, @Nullable android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener); method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.dvr.DvrRecorder openDvrRecorder(long, @Nullable java.util.concurrent.Executor, @Nullable android.media.tv.tuner.dvr.OnRecordStatusChangedListener); method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.filter.Filter openFilter(int, int, long, @Nullable java.util.concurrent.Executor, @Nullable android.media.tv.tuner.filter.FilterCallback); @@ -4738,16 +4876,22 @@ package android.media.tv.tuner { method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.Lnb openLnbByName(@Nullable String, @Nullable java.util.concurrent.Executor, @NonNull android.media.tv.tuner.LnbCallback); method @Nullable public android.media.tv.tuner.filter.TimeFilter openTimeFilter(); method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int scan(@NonNull android.media.tv.tuner.frontend.FrontendSettings, int, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.ScanCallback); + method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setLna(boolean); method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void setOnTuneEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.OnTuneEventListener); + method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner); method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int stopScan(); method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int stopTune(); method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings); + method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void updateResourcePriority(int, int); } - public class Tuner.Descrambler { + public static interface Tuner.OnResourceLostListener { + method public void onResourceLost(@NonNull android.media.tv.tuner.Tuner); } public final class TunerConstants { + field public static final int INVALID_STREAM_ID = -1; // 0xffffffff + field public static final int INVALID_TS_PID = -1; // 0xffffffff field public static final int RESULT_INVALID_ARGUMENT = 4; // 0x4 field public static final int RESULT_INVALID_STATE = 3; // 0x3 field public static final int RESULT_NOT_INITIALIZED = 2; // 0x2 @@ -4758,18 +4902,6 @@ package android.media.tv.tuner { field public static final int SCAN_TYPE_AUTO = 1; // 0x1 field public static final int SCAN_TYPE_BLIND = 2; // 0x2 field public static final int SCAN_TYPE_UNDEFINED = 0; // 0x0 - field public static final int SC_HEVC_INDEX_AUD = 2; // 0x2 - field public static final int SC_HEVC_INDEX_SLICE_BLA_N_LP = 16; // 0x10 - field public static final int SC_HEVC_INDEX_SLICE_BLA_W_RADL = 8; // 0x8 - field public static final int SC_HEVC_INDEX_SLICE_CE_BLA_W_LP = 4; // 0x4 - field public static final int SC_HEVC_INDEX_SLICE_IDR_N_LP = 64; // 0x40 - field public static final int SC_HEVC_INDEX_SLICE_IDR_W_RADL = 32; // 0x20 - field public static final int SC_HEVC_INDEX_SLICE_TRAIL_CRA = 128; // 0x80 - field public static final int SC_HEVC_INDEX_SPS = 1; // 0x1 - field public static final int SC_INDEX_B_FRAME = 4; // 0x4 - field public static final int SC_INDEX_I_FRAME = 1; // 0x1 - field public static final int SC_INDEX_P_FRAME = 2; // 0x2 - field public static final int SC_INDEX_SEQUENCE = 8; // 0x8 } } @@ -4974,9 +5106,11 @@ 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(); + method @Nullable public android.media.MediaCodec.LinearBlock getLinearBlock(); method public int getMpuSequenceNumber(); method public long getOffset(); method public long getPts(); @@ -5025,6 +5159,21 @@ package android.media.tv.tuner.filter { method public int getScIndexMask(); method public int getScIndexType(); method public int getTsIndexMask(); + field public static final int INDEX_TYPE_NONE = 0; // 0x0 + field public static final int INDEX_TYPE_SC = 1; // 0x1 + field public static final int INDEX_TYPE_SC_HEVC = 2; // 0x2 + field public static final int SC_HEVC_INDEX_AUD = 2; // 0x2 + field public static final int SC_HEVC_INDEX_SLICE_BLA_N_LP = 16; // 0x10 + field public static final int SC_HEVC_INDEX_SLICE_BLA_W_RADL = 8; // 0x8 + field public static final int SC_HEVC_INDEX_SLICE_CE_BLA_W_LP = 4; // 0x4 + field public static final int SC_HEVC_INDEX_SLICE_IDR_N_LP = 64; // 0x40 + field public static final int SC_HEVC_INDEX_SLICE_IDR_W_RADL = 32; // 0x20 + field public static final int SC_HEVC_INDEX_SLICE_TRAIL_CRA = 128; // 0x80 + field public static final int SC_HEVC_INDEX_SPS = 1; // 0x1 + field public static final int SC_INDEX_B_FRAME = 4; // 0x4 + field public static final int SC_INDEX_I_FRAME = 1; // 0x1 + field public static final int SC_INDEX_P_FRAME = 2; // 0x2 + field public static final int SC_INDEX_SEQUENCE = 8; // 0x8 field public static final int TS_INDEX_ADAPTATION_EXTENSION_FLAG = 4096; // 0x1000 field public static final int TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED = 8; // 0x8 field public static final int TS_INDEX_CHANGE_TO_NOT_SCRAMBLED = 4; // 0x4 @@ -5054,7 +5203,16 @@ package android.media.tv.tuner.filter { method public int getVersion(); } - public class SectionSettings extends android.media.tv.tuner.filter.Settings { + public abstract class SectionSettings extends android.media.tv.tuner.filter.Settings { + method public boolean isCrcEnabled(); + method public boolean isRaw(); + method public boolean isRepeat(); + } + + public abstract static class SectionSettings.Builder<T extends android.media.tv.tuner.filter.SectionSettings.Builder<T>> extends android.media.tv.tuner.filter.Settings.Builder<android.media.tv.tuner.filter.SectionSettings.Builder<T>> { + method @NonNull public T setCrcEnabled(boolean); + method @NonNull public T setRaw(boolean); + method @NonNull public T setRepeat(boolean); } public class SectionSettingsWithSectionBits extends android.media.tv.tuner.filter.SectionSettings { @@ -5064,7 +5222,7 @@ package android.media.tv.tuner.filter { method @NonNull public byte[] getMode(); } - public static class SectionSettingsWithSectionBits.Builder extends android.media.tv.tuner.filter.Settings.Builder<android.media.tv.tuner.filter.SectionSettingsWithSectionBits.Builder> { + public static class SectionSettingsWithSectionBits.Builder extends android.media.tv.tuner.filter.SectionSettings.Builder<android.media.tv.tuner.filter.SectionSettingsWithSectionBits.Builder> { method @NonNull public android.media.tv.tuner.filter.SectionSettingsWithSectionBits build(); method @NonNull public android.media.tv.tuner.filter.SectionSettingsWithSectionBits.Builder setFilter(@NonNull byte[]); method @NonNull public android.media.tv.tuner.filter.SectionSettingsWithSectionBits.Builder setMask(@NonNull byte[]); @@ -5077,7 +5235,7 @@ package android.media.tv.tuner.filter { method public int getVersion(); } - public static class SectionSettingsWithTableInfo.Builder extends android.media.tv.tuner.filter.Settings.Builder<android.media.tv.tuner.filter.SectionSettingsWithTableInfo.Builder> { + public static class SectionSettingsWithTableInfo.Builder extends android.media.tv.tuner.filter.SectionSettings.Builder<android.media.tv.tuner.filter.SectionSettingsWithTableInfo.Builder> { method @NonNull public android.media.tv.tuner.filter.SectionSettingsWithTableInfo build(); method @NonNull public android.media.tv.tuner.filter.SectionSettingsWithTableInfo.Builder setTableId(int); method @NonNull public android.media.tv.tuner.filter.SectionSettingsWithTableInfo.Builder setVersion(int); @@ -5115,7 +5273,7 @@ package android.media.tv.tuner.filter { public static class TlvFilterConfiguration.Builder extends android.media.tv.tuner.filter.FilterConfiguration.Builder<android.media.tv.tuner.filter.TlvFilterConfiguration.Builder> { method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration build(); - method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration.Builder setIsCompressedIpPacket(boolean); + method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration.Builder setCompressedIpPacket(boolean); method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration.Builder setPacketType(int); method @NonNull public android.media.tv.tuner.filter.TlvFilterConfiguration.Builder setPassthrough(boolean); } @@ -5143,6 +5301,11 @@ package android.media.tv.tuner.filter { package android.media.tv.tuner.frontend { + public class AnalogFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities { + method public int getSifStandardCapability(); + method public int getSignalTypeCapability(); + } + public class AnalogFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings { method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder builder(@NonNull android.content.Context); method public int getSifStandard(); @@ -5180,8 +5343,74 @@ package android.media.tv.tuner.frontend { public static class AnalogFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder> { method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings build(); - method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setASignalType(int); method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSifStandard(int); + method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSignalType(int); + } + + public class Atsc3FrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities { + method public int getBandwidthCapability(); + method public int getDemodOutputFormatCapability(); + method public int getFecCapability(); + method public int getModulationCapability(); + method public int getPlpCodeRateCapability(); + method public int getTimeInterleaveModeCapability(); + } + + public class Atsc3FrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings { + method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder builder(@NonNull android.content.Context); + method public int getBandwidth(); + method public int getDemodOutputFormat(); + method @NonNull public android.media.tv.tuner.frontend.Atsc3PlpSettings[] getPlpSettings(); + method public int getType(); + field public static final int BANDWIDTH_AUTO = 1; // 0x1 + field public static final int BANDWIDTH_BANDWIDTH_6MHZ = 2; // 0x2 + field public static final int BANDWIDTH_BANDWIDTH_7MHZ = 4; // 0x4 + field public static final int BANDWIDTH_BANDWIDTH_8MHZ = 8; // 0x8 + field public static final int BANDWIDTH_UNDEFINED = 0; // 0x0 + field public static final int CODERATE_10_15 = 512; // 0x200 + field public static final int CODERATE_11_15 = 1024; // 0x400 + field public static final int CODERATE_12_15 = 2048; // 0x800 + field public static final int CODERATE_13_15 = 4096; // 0x1000 + field public static final int CODERATE_2_15 = 2; // 0x2 + field public static final int CODERATE_3_15 = 4; // 0x4 + field public static final int CODERATE_4_15 = 8; // 0x8 + field public static final int CODERATE_5_15 = 16; // 0x10 + field public static final int CODERATE_6_15 = 32; // 0x20 + field public static final int CODERATE_7_15 = 64; // 0x40 + field public static final int CODERATE_8_15 = 128; // 0x80 + field public static final int CODERATE_9_15 = 256; // 0x100 + field public static final int CODERATE_AUTO = 1; // 0x1 + field public static final int CODERATE_UNDEFINED = 0; // 0x0 + field public static final int DEMOD_OUTPUT_FORMAT_ATSC3_LINKLAYER_PACKET = 1; // 0x1 + field public static final int DEMOD_OUTPUT_FORMAT_BASEBAND_PACKET = 2; // 0x2 + field public static final int DEMOD_OUTPUT_FORMAT_UNDEFINED = 0; // 0x0 + field public static final int FEC_AUTO = 1; // 0x1 + field public static final int FEC_BCH_LDPC_16K = 2; // 0x2 + field public static final int FEC_BCH_LDPC_64K = 4; // 0x4 + field public static final int FEC_CRC_LDPC_16K = 8; // 0x8 + field public static final int FEC_CRC_LDPC_64K = 16; // 0x10 + field public static final int FEC_LDPC_16K = 32; // 0x20 + field public static final int FEC_LDPC_64K = 64; // 0x40 + field public static final int FEC_UNDEFINED = 0; // 0x0 + field public static final int MODULATION_AUTO = 1; // 0x1 + field public static final int MODULATION_MOD_1024QAM = 32; // 0x20 + field public static final int MODULATION_MOD_16QAM = 4; // 0x4 + field public static final int MODULATION_MOD_256QAM = 16; // 0x10 + field public static final int MODULATION_MOD_4096QAM = 64; // 0x40 + field public static final int MODULATION_MOD_64QAM = 8; // 0x8 + field public static final int MODULATION_MOD_QPSK = 2; // 0x2 + field public static final int MODULATION_UNDEFINED = 0; // 0x0 + field public static final int TIME_INTERLEAVE_MODE_AUTO = 1; // 0x1 + field public static final int TIME_INTERLEAVE_MODE_CTI = 2; // 0x2 + field public static final int TIME_INTERLEAVE_MODE_HTI = 4; // 0x4 + field public static final int TIME_INTERLEAVE_MODE_UNDEFINED = 0; // 0x0 + } + + public static class Atsc3FrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder> { + method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings build(); + method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setBandwidth(int); + method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setDemodOutputFormat(byte); + method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setPlpSettings(@NonNull android.media.tv.tuner.frontend.Atsc3PlpSettings[]); } public class Atsc3PlpInfo { @@ -5189,9 +5418,326 @@ package android.media.tv.tuner.frontend { method public int getPlpId(); } + public class Atsc3PlpSettings { + method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.frontend.Atsc3PlpSettings.Builder builder(@NonNull android.content.Context); + method public int getCodeRate(); + method public int getFec(); + method public int getInterleaveMode(); + method public int getModulation(); + method public int getPlpId(); + } + + public static class Atsc3PlpSettings.Builder { + method @NonNull public android.media.tv.tuner.frontend.Atsc3PlpSettings build(); + method @NonNull public android.media.tv.tuner.frontend.Atsc3PlpSettings.Builder setCodeRate(int); + method @NonNull public android.media.tv.tuner.frontend.Atsc3PlpSettings.Builder setFec(int); + method @NonNull public android.media.tv.tuner.frontend.Atsc3PlpSettings.Builder setInterleaveMode(int); + method @NonNull public android.media.tv.tuner.frontend.Atsc3PlpSettings.Builder setModulation(int); + method @NonNull public android.media.tv.tuner.frontend.Atsc3PlpSettings.Builder setPlpId(int); + } + + public class AtscFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities { + method public int getModulationCapability(); + } + + public class AtscFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings { + method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.frontend.AtscFrontendSettings.Builder builder(@NonNull android.content.Context); + method public int getModulation(); + method public int getType(); + field public static final int MODULATION_AUTO = 1; // 0x1 + field public static final int MODULATION_MOD_16VSB = 8; // 0x8 + field public static final int MODULATION_MOD_8VSB = 4; // 0x4 + field public static final int MODULATION_UNDEFINED = 0; // 0x0 + } + + public static class AtscFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.AtscFrontendSettings.Builder> { + method @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings build(); + method @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings.Builder setModulation(int); + } + + public class DvbcFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities { + method public int getAnnexCapability(); + method public int getFecCapability(); + method public int getModulationCapability(); + } + + public class DvbcFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings { + method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder builder(@NonNull android.content.Context); + method public byte getAnnex(); + method public long getFec(); + method public int getModulation(); + method public int getOuterFec(); + method public int getSpectralInversion(); + method public int getSymbolRate(); + method public int getType(); + field public static final int ANNEX_A = 1; // 0x1 + field public static final int ANNEX_B = 2; // 0x2 + field public static final int ANNEX_C = 4; // 0x4 + field public static final int ANNEX_UNDEFINED = 0; // 0x0 + field public static final int MODULATION_AUTO = 1; // 0x1 + field public static final int MODULATION_MOD_128QAM = 16; // 0x10 + field public static final int MODULATION_MOD_16QAM = 2; // 0x2 + field public static final int MODULATION_MOD_256QAM = 32; // 0x20 + field public static final int MODULATION_MOD_32QAM = 4; // 0x4 + field public static final int MODULATION_MOD_64QAM = 8; // 0x8 + field public static final int MODULATION_UNDEFINED = 0; // 0x0 + field public static final int OUTER_FEC_OUTER_FEC_NONE = 1; // 0x1 + field public static final int OUTER_FEC_OUTER_FEC_RS = 2; // 0x2 + field public static final int OUTER_FEC_UNDEFINED = 0; // 0x0 + field public static final int SPECTRAL_INVERSION_INVERTED = 2; // 0x2 + field public static final int SPECTRAL_INVERSION_NORMAL = 1; // 0x1 + field public static final int SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0 + } + + public static class DvbcFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder> { + method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings build(); + method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setAnnex(byte); + method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setFec(long); + method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setModulation(int); + method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setOuterFec(int); + method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setSpectralInversion(int); + method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setSymbolRate(int); + } + + public class DvbsCodeRate { + method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.frontend.DvbsCodeRate.Builder builder(@NonNull android.content.Context); + method public int getBitsPer1000Symbol(); + method public long getInnerFec(); + method public boolean isLinear(); + method public boolean isShortFrameEnabled(); + } + + public static class DvbsCodeRate.Builder { + method @NonNull public android.media.tv.tuner.frontend.DvbsCodeRate build(); + method @NonNull public android.media.tv.tuner.frontend.DvbsCodeRate.Builder setBitsPer1000Symbol(int); + method @NonNull public android.media.tv.tuner.frontend.DvbsCodeRate.Builder setInnerFec(long); + method @NonNull public android.media.tv.tuner.frontend.DvbsCodeRate.Builder setLinear(boolean); + method @NonNull public android.media.tv.tuner.frontend.DvbsCodeRate.Builder setShortFrameEnabled(boolean); + } + + public class DvbsFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities { + method public long getInnerFecCapability(); + method public int getModulationCapability(); + method public int getStandardCapability(); + } + + public class DvbsFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings { + method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder builder(@NonNull android.content.Context); + method @Nullable public android.media.tv.tuner.frontend.DvbsCodeRate getCodeRate(); + method public int getInputStreamId(); + method public int getModulation(); + method public int getPilot(); + method public int getRolloff(); + method public int getStandard(); + method public int getSymbolRate(); + method public int getType(); + method public int getVcmMode(); + field public static final int MODULATION_AUTO = 1; // 0x1 + field public static final int MODULATION_MOD_128APSK = 2048; // 0x800 + field public static final int MODULATION_MOD_16APSK = 256; // 0x100 + field public static final int MODULATION_MOD_16PSK = 16; // 0x10 + field public static final int MODULATION_MOD_16QAM = 8; // 0x8 + field public static final int MODULATION_MOD_256APSK = 4096; // 0x1000 + field public static final int MODULATION_MOD_32APSK = 512; // 0x200 + field public static final int MODULATION_MOD_32PSK = 32; // 0x20 + field public static final int MODULATION_MOD_64APSK = 1024; // 0x400 + field public static final int MODULATION_MOD_8APSK = 128; // 0x80 + field public static final int MODULATION_MOD_8PSK = 4; // 0x4 + field public static final int MODULATION_MOD_ACM = 64; // 0x40 + field public static final int MODULATION_MOD_QPSK = 2; // 0x2 + field public static final int MODULATION_MOD_RESERVED = 8192; // 0x2000 + field public static final int MODULATION_UNDEFINED = 0; // 0x0 + field public static final int PILOT_AUTO = 3; // 0x3 + field public static final int PILOT_OFF = 2; // 0x2 + field public static final int PILOT_ON = 1; // 0x1 + field public static final int PILOT_UNDEFINED = 0; // 0x0 + field public static final int ROLLOFF_0_10 = 5; // 0x5 + field public static final int ROLLOFF_0_15 = 4; // 0x4 + field public static final int ROLLOFF_0_20 = 3; // 0x3 + field public static final int ROLLOFF_0_25 = 2; // 0x2 + field public static final int ROLLOFF_0_35 = 1; // 0x1 + field public static final int ROLLOFF_0_5 = 6; // 0x6 + field public static final int ROLLOFF_UNDEFINED = 0; // 0x0 + field public static final int STANDARD_AUTO = 1; // 0x1 + field public static final int STANDARD_S = 2; // 0x2 + field public static final int STANDARD_S2 = 4; // 0x4 + field public static final int STANDARD_S2X = 8; // 0x8 + field public static final int VCM_MODE_AUTO = 1; // 0x1 + field public static final int VCM_MODE_MANUAL = 2; // 0x2 + field public static final int VCM_MODE_UNDEFINED = 0; // 0x0 + } + + public static class DvbsFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder> { + method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings build(); + method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setCodeRate(@Nullable android.media.tv.tuner.frontend.DvbsCodeRate); + method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setInputStreamId(int); + method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setModulation(int); + method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setPilot(int); + method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setRolloff(int); + method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setStandard(int); + method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setSymbolRate(int); + method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setVcmMode(int); + } + + public class DvbtFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities { + method public int getBandwidthCapability(); + method public int getCodeRateCapability(); + method public int getConstellationCapability(); + method public int getGuardIntervalCapability(); + method public int getHierarchyCapability(); + method public int getTransmissionModeCapability(); + method public boolean isMisoSupported(); + method public boolean isT2Supported(); + } + + public class DvbtFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings { + method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder builder(@NonNull android.content.Context); + method public int getBandwidth(); + method public int getConstellation(); + method public int getGuardInterval(); + method public int getHierarchy(); + method public int getHpCodeRate(); + method public int getLpCodeRate(); + method public int getPlpGroupId(); + method public int getPlpId(); + method public int getPlpMode(); + method public int getStandard(); + method public int getTransmissionMode(); + method public int getType(); + method public boolean isHighPriority(); + method public boolean isMiso(); + field public static final int BANDWIDTH_10MHZ = 64; // 0x40 + field public static final int BANDWIDTH_1_7MHZ = 32; // 0x20 + field public static final int BANDWIDTH_5MHZ = 16; // 0x10 + field public static final int BANDWIDTH_6MHZ = 8; // 0x8 + field public static final int BANDWIDTH_7MHZ = 4; // 0x4 + field public static final int BANDWIDTH_8MHZ = 2; // 0x2 + field public static final int BANDWIDTH_AUTO = 1; // 0x1 + field public static final int BANDWIDTH_UNDEFINED = 0; // 0x0 + field public static final int CODERATE_1_2 = 2; // 0x2 + field public static final int CODERATE_2_3 = 4; // 0x4 + field public static final int CODERATE_3_4 = 8; // 0x8 + field public static final int CODERATE_3_5 = 64; // 0x40 + field public static final int CODERATE_4_5 = 128; // 0x80 + field public static final int CODERATE_5_6 = 16; // 0x10 + field public static final int CODERATE_6_7 = 256; // 0x100 + field public static final int CODERATE_7_8 = 32; // 0x20 + field public static final int CODERATE_8_9 = 512; // 0x200 + field public static final int CODERATE_AUTO = 1; // 0x1 + field public static final int CODERATE_UNDEFINED = 0; // 0x0 + field public static final int CONSTELLATION_AUTO = 1; // 0x1 + field public static final int CONSTELLATION_CONSTELLATION_16QAM = 4; // 0x4 + field public static final int CONSTELLATION_CONSTELLATION_256QAM = 16; // 0x10 + field public static final int CONSTELLATION_CONSTELLATION_64QAM = 8; // 0x8 + field public static final int CONSTELLATION_CONSTELLATION_QPSK = 2; // 0x2 + field public static final int CONSTELLATION_UNDEFINED = 0; // 0x0 + field public static final int GUARD_INTERVAL_AUTO = 1; // 0x1 + field public static final int GUARD_INTERVAL_INTERVAL_19_128 = 64; // 0x40 + field public static final int GUARD_INTERVAL_INTERVAL_19_256 = 128; // 0x80 + field public static final int GUARD_INTERVAL_INTERVAL_1_128 = 32; // 0x20 + field public static final int GUARD_INTERVAL_INTERVAL_1_16 = 4; // 0x4 + field public static final int GUARD_INTERVAL_INTERVAL_1_32 = 2; // 0x2 + field public static final int GUARD_INTERVAL_INTERVAL_1_4 = 16; // 0x10 + field public static final int GUARD_INTERVAL_INTERVAL_1_8 = 8; // 0x8 + field public static final int GUARD_INTERVAL_UNDEFINED = 0; // 0x0 + field public static final int HIERARCHY_1_INDEPTH = 64; // 0x40 + field public static final int HIERARCHY_1_NATIVE = 4; // 0x4 + field public static final int HIERARCHY_2_INDEPTH = 128; // 0x80 + field public static final int HIERARCHY_2_NATIVE = 8; // 0x8 + field public static final int HIERARCHY_4_INDEPTH = 256; // 0x100 + field public static final int HIERARCHY_4_NATIVE = 16; // 0x10 + field public static final int HIERARCHY_AUTO = 1; // 0x1 + field public static final int HIERARCHY_NON_INDEPTH = 32; // 0x20 + field public static final int HIERARCHY_NON_NATIVE = 2; // 0x2 + field public static final int HIERARCHY_UNDEFINED = 0; // 0x0 + field public static final int PLP_MODE_AUTO = 1; // 0x1 + field public static final int PLP_MODE_MANUAL = 2; // 0x2 + field public static final int PLP_MODE_UNDEFINED = 0; // 0x0 + field public static final int STANDARD_AUTO = 1; // 0x1 + field public static final int STANDARD_T = 2; // 0x2 + field public static final int STANDARD_T2 = 4; // 0x4 + field public static final int TRANSMISSION_MODE_16K = 32; // 0x20 + field public static final int TRANSMISSION_MODE_1K = 16; // 0x10 + field public static final int TRANSMISSION_MODE_2K = 2; // 0x2 + field public static final int TRANSMISSION_MODE_32K = 64; // 0x40 + field public static final int TRANSMISSION_MODE_4K = 8; // 0x8 + field public static final int TRANSMISSION_MODE_8K = 4; // 0x4 + field public static final int TRANSMISSION_MODE_AUTO = 1; // 0x1 + field public static final int TRANSMISSION_MODE_UNDEFINED = 0; // 0x0 + } + + public static class DvbtFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder> { + method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings build(); + method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setBandwidth(int); + method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setConstellation(int); + method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setGuardInterval(int); + method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setHierarchy(int); + method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setHighPriority(boolean); + method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setHpCodeRate(int); + method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setLpCodeRate(int); + method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setMiso(boolean); + method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setPlpGroupId(int); + method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setPlpId(int); + method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setPlpMode(int); + method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setStandard(int); + method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setTransmissionMode(int); + } + + public abstract class FrontendCapabilities { + ctor public FrontendCapabilities(); + } + + public class FrontendInfo { + method public int getAcquireRange(); + method public int getExclusiveGroupId(); + method @NonNull public android.util.Range<java.lang.Integer> getFrequencyRange(); + method @NonNull public android.media.tv.tuner.frontend.FrontendCapabilities getFrontendCapability(); + method public int getId(); + method @NonNull public int[] getStatusCapabilities(); + method @NonNull public android.util.Range<java.lang.Integer> getSymbolRateRange(); + method public int getType(); + } + public abstract class FrontendSettings { method public int getFrequency(); method public abstract int getType(); + field public static final long FEC_11_15 = 4194304L; // 0x400000L + field public static final long FEC_11_20 = 8388608L; // 0x800000L + field public static final long FEC_11_45 = 16777216L; // 0x1000000L + field public static final long FEC_13_18 = 33554432L; // 0x2000000L + field public static final long FEC_13_45 = 67108864L; // 0x4000000L + field public static final long FEC_14_45 = 134217728L; // 0x8000000L + field public static final long FEC_1_2 = 2L; // 0x2L + field public static final long FEC_1_3 = 4L; // 0x4L + field public static final long FEC_1_4 = 8L; // 0x8L + field public static final long FEC_1_5 = 16L; // 0x10L + field public static final long FEC_23_36 = 268435456L; // 0x10000000L + field public static final long FEC_25_36 = 536870912L; // 0x20000000L + field public static final long FEC_26_45 = 1073741824L; // 0x40000000L + field public static final long FEC_28_45 = -2147483648L; // 0xffffffff80000000L + field public static final long FEC_29_45 = 1L; // 0x1L + field public static final long FEC_2_3 = 32L; // 0x20L + field public static final long FEC_2_5 = 64L; // 0x40L + field public static final long FEC_2_9 = 128L; // 0x80L + field public static final long FEC_31_45 = 2L; // 0x2L + field public static final long FEC_32_45 = 4L; // 0x4L + field public static final long FEC_3_4 = 256L; // 0x100L + field public static final long FEC_3_5 = 512L; // 0x200L + field public static final long FEC_4_15 = 2048L; // 0x800L + field public static final long FEC_4_5 = 1024L; // 0x400L + field public static final long FEC_5_6 = 4096L; // 0x1000L + field public static final long FEC_5_9 = 8192L; // 0x2000L + field public static final long FEC_6_7 = 16384L; // 0x4000L + field public static final long FEC_77_90 = 8L; // 0x8L + field public static final long FEC_7_15 = 131072L; // 0x20000L + field public static final long FEC_7_8 = 32768L; // 0x8000L + field public static final long FEC_7_9 = 65536L; // 0x10000L + field public static final long FEC_8_15 = 524288L; // 0x80000L + field public static final long FEC_8_9 = 262144L; // 0x40000L + field public static final long FEC_9_10 = 1048576L; // 0x100000L + field public static final long FEC_9_20 = 2097152L; // 0x200000L + field public static final long FEC_AUTO = 1L; // 0x1L + field public static final long FEC_UNDEFINED = 0L; // 0x0L field public static final int TYPE_ANALOG = 1; // 0x1 field public static final int TYPE_ATSC = 2; // 0x2 field public static final int TYPE_ATSC3 = 3; // 0x3 @@ -5208,6 +5754,200 @@ package android.media.tv.tuner.frontend { method @IntRange(from=1) @NonNull public T setFrequency(int); } + public class FrontendStatus { + method public int getAgc(); + method @NonNull public android.media.tv.tuner.frontend.FrontendStatus.Atsc3PlpInfo[] getAtsc3PlpInfo(); + method public int getBer(); + method public long getFec(); + method public int getFreqOffset(); + method public int getHierarchy(); + method @NonNull public boolean[] getLayerErrors(); + method public int getLberCn(); + method public int getLnbVoltage(); + method public int getMer(); + method public int getModulation(); + method public int getPer(); + method public int getPerBer(); + method public int getPlpId(); + method public int getSignalQuality(); + method public int getSignalStrength(); + method public int getSnr(); + method public int getSpectralInversion(); + method public int getSymbolRate(); + method public int getVberCn(); + method public int getXerCn(); + method public boolean isDemodLocked(); + method public boolean isEwbs(); + method public boolean isLnaOn(); + method public boolean isRfLock(); + field public static final int FRONTEND_STATUS_TYPE_AGC = 14; // 0xe + field public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO = 24; // 0x18 + field public static final int FRONTEND_STATUS_TYPE_BER = 2; // 0x2 + field public static final int FRONTEND_STATUS_TYPE_DEMOD_LOCK = 0; // 0x0 + field public static final int FRONTEND_STATUS_TYPE_EWBS = 13; // 0xd + field public static final int FRONTEND_STATUS_TYPE_FEC = 8; // 0x8 + field public static final int FRONTEND_STATUS_TYPE_FREQ_OFFSET = 21; // 0x15 + field public static final int FRONTEND_STATUS_TYPE_HIERARCHY = 22; // 0x16 + field public static final int FRONTEND_STATUS_TYPE_LAYER_ERROR = 16; // 0x10 + field public static final int FRONTEND_STATUS_TYPE_LBER_CN = 18; // 0x12 + field public static final int FRONTEND_STATUS_TYPE_LNA = 15; // 0xf + field public static final int FRONTEND_STATUS_TYPE_LNB_VOLTAGE = 11; // 0xb + field public static final int FRONTEND_STATUS_TYPE_MER = 20; // 0x14 + field public static final int FRONTEND_STATUS_TYPE_MODULATION = 9; // 0x9 + field public static final int FRONTEND_STATUS_TYPE_PER = 3; // 0x3 + field public static final int FRONTEND_STATUS_TYPE_PLP_ID = 12; // 0xc + field public static final int FRONTEND_STATUS_TYPE_PRE_BER = 4; // 0x4 + field public static final int FRONTEND_STATUS_TYPE_RF_LOCK = 23; // 0x17 + field public static final int FRONTEND_STATUS_TYPE_SIGNAL_QUALITY = 5; // 0x5 + field public static final int FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH = 6; // 0x6 + field public static final int FRONTEND_STATUS_TYPE_SNR = 1; // 0x1 + field public static final int FRONTEND_STATUS_TYPE_SPECTRAL = 10; // 0xa + field public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE = 7; // 0x7 + field public static final int FRONTEND_STATUS_TYPE_VBER_CN = 17; // 0x11 + field public static final int FRONTEND_STATUS_TYPE_XER_CN = 19; // 0x13 + } + + public static class FrontendStatus.Atsc3PlpInfo { + method public int getPlpId(); + method public int getUec(); + method public boolean isLock(); + } + + public class Isdbs3FrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities { + method public int getCodeRateCapability(); + method public int getModulationCapability(); + } + + public class Isdbs3FrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings { + method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder builder(@NonNull android.content.Context); + method public int getCodeRate(); + method public int getModulation(); + method public int getRolloff(); + method public int getStreamId(); + method public int getStreamIdType(); + method public int getSymbolRate(); + method public int getType(); + field public static final int CODERATE_1_2 = 8; // 0x8 + field public static final int CODERATE_1_3 = 2; // 0x2 + field public static final int CODERATE_2_3 = 32; // 0x20 + field public static final int CODERATE_2_5 = 4; // 0x4 + field public static final int CODERATE_3_4 = 64; // 0x40 + field public static final int CODERATE_3_5 = 16; // 0x10 + field public static final int CODERATE_4_5 = 256; // 0x100 + field public static final int CODERATE_5_6 = 512; // 0x200 + field public static final int CODERATE_7_8 = 1024; // 0x400 + field public static final int CODERATE_7_9 = 128; // 0x80 + field public static final int CODERATE_9_10 = 2048; // 0x800 + field public static final int CODERATE_AUTO = 1; // 0x1 + field public static final int CODERATE_UNDEFINED = 0; // 0x0 + field public static final int MODULATION_AUTO = 1; // 0x1 + field public static final int MODULATION_MOD_16APSK = 16; // 0x10 + field public static final int MODULATION_MOD_32APSK = 32; // 0x20 + field public static final int MODULATION_MOD_8PSK = 8; // 0x8 + field public static final int MODULATION_MOD_BPSK = 2; // 0x2 + field public static final int MODULATION_MOD_QPSK = 4; // 0x4 + field public static final int MODULATION_UNDEFINED = 0; // 0x0 + field public static final int ROLLOFF_0_03 = 1; // 0x1 + field public static final int ROLLOFF_UNDEFINED = 0; // 0x0 + } + + public static class Isdbs3FrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder> { + method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings build(); + method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setCodeRate(int); + method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setModulation(int); + method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setRolloff(int); + method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setStreamId(int); + method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setStreamIdType(int); + method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setSymbolRate(int); + } + + public class IsdbsFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities { + method public int getCodeRateCapability(); + method public int getModulationCapability(); + } + + public class IsdbsFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings { + method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder builder(@NonNull android.content.Context); + method public int getCodeRate(); + method public int getModulation(); + method public int getRolloff(); + method public int getStreamId(); + method public int getStreamIdType(); + method public int getSymbolRate(); + method public int getType(); + field public static final int CODERATE_1_2 = 2; // 0x2 + field public static final int CODERATE_2_3 = 4; // 0x4 + field public static final int CODERATE_3_4 = 8; // 0x8 + field public static final int CODERATE_5_6 = 16; // 0x10 + field public static final int CODERATE_7_8 = 32; // 0x20 + field public static final int CODERATE_AUTO = 1; // 0x1 + field public static final int CODERATE_UNDEFINED = 0; // 0x0 + field public static final int MODULATION_AUTO = 1; // 0x1 + field public static final int MODULATION_MOD_BPSK = 2; // 0x2 + field public static final int MODULATION_MOD_QPSK = 4; // 0x4 + field public static final int MODULATION_MOD_TC8PSK = 8; // 0x8 + field public static final int MODULATION_UNDEFINED = 0; // 0x0 + field public static final int ROLLOFF_0_35 = 1; // 0x1 + field public static final int ROLLOFF_UNDEFINED = 0; // 0x0 + field public static final int STREAM_ID_TYPE_ID = 0; // 0x0 + field public static final int STREAM_ID_TYPE_RELATIVE_NUMBER = 1; // 0x1 + } + + public static class IsdbsFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder> { + method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings build(); + method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setCodeRate(int); + method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setModulation(int); + method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setRolloff(int); + method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setStreamId(int); + method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setStreamIdType(int); + method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setSymbolRate(int); + } + + public class IsdbtFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities { + method public int getBandwidthCapability(); + method public int getCodeRateCapability(); + method public int getGuardIntervalCapability(); + method public int getModeCapability(); + method public int getModulationCapability(); + } + + public class IsdbtFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings { + method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder builder(@NonNull android.content.Context); + method public int getBandwidth(); + method public int getCodeRate(); + method public int getGuardInterval(); + method public int getMode(); + method public int getModulation(); + method public int getServiceAreaId(); + method public int getType(); + field public static final int BANDWIDTH_6MHZ = 8; // 0x8 + field public static final int BANDWIDTH_7MHZ = 4; // 0x4 + field public static final int BANDWIDTH_8MHZ = 2; // 0x2 + field public static final int BANDWIDTH_AUTO = 1; // 0x1 + field public static final int BANDWIDTH_UNDEFINED = 0; // 0x0 + field public static final int MODE_1 = 2; // 0x2 + field public static final int MODE_2 = 4; // 0x4 + field public static final int MODE_3 = 8; // 0x8 + field public static final int MODE_AUTO = 1; // 0x1 + field public static final int MODE_UNDEFINED = 0; // 0x0 + field public static final int MODULATION_AUTO = 1; // 0x1 + field public static final int MODULATION_MOD_16QAM = 8; // 0x8 + field public static final int MODULATION_MOD_64QAM = 16; // 0x10 + field public static final int MODULATION_MOD_DQPSK = 2; // 0x2 + field public static final int MODULATION_MOD_QPSK = 4; // 0x4 + field public static final int MODULATION_UNDEFINED = 0; // 0x0 + } + + public static class IsdbtFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder> { + method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings build(); + method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setBandwidth(int); + method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setCodeRate(int); + method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setGuardInterval(int); + method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setMode(int); + method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setModulation(int); + method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setServiceAreaId(int); + } + public interface OnTuneEventListener { method public void onTuneEvent(int); field public static final int SIGNAL_LOCKED = 0; // 0x0 @@ -5234,6 +5974,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 { @@ -5290,31 +6040,57 @@ package android.net { field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2 } + public final class CaptivePortalData implements android.os.Parcelable { + method public int describeContents(); + method public long getByteLimit(); + method public long getExpiryTimeMillis(); + method public long getRefreshTimeMillis(); + method @Nullable public android.net.Uri getUserPortalUrl(); + method @Nullable public android.net.Uri getVenueInfoUrl(); + method public boolean isCaptive(); + method public boolean isSessionExtendable(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR; + } + + public static class CaptivePortalData.Builder { + ctor public CaptivePortalData.Builder(); + ctor public CaptivePortalData.Builder(@Nullable android.net.CaptivePortalData); + method @NonNull public android.net.CaptivePortalData build(); + method @NonNull public android.net.CaptivePortalData.Builder setBytesRemaining(long); + method @NonNull public android.net.CaptivePortalData.Builder setCaptive(boolean); + method @NonNull public android.net.CaptivePortalData.Builder setExpiryTime(long); + method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long); + method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean); + method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri); + method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri); + } + public class ConnectivityManager { method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull android.os.ParcelFileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback); method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback); method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCaptivePortalServerUrl(); - method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported(); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported(); method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public int registerNetworkProvider(@NonNull android.net.NetworkProvider); - method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback); method @Deprecated public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int, int, @NonNull android.os.Handler); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi(); method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle); method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback); method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler); - method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int); method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider); - method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback); field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC"; field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT"; field public static final int TETHERING_BLUETOOTH = 2; // 0x2 field public static final int TETHERING_USB = 1; // 0x1 field public static final int TETHERING_WIFI = 0; // 0x0 - field public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; // 0xd - field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 - field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb + field @Deprecated public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; // 0xd + field @Deprecated public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 + field @Deprecated public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb field public static final int TYPE_NONE = -1; // 0xffffffff field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd } @@ -5325,13 +6101,13 @@ package android.net { method public void onTetheringStarted(); } - public static interface ConnectivityManager.OnTetheringEntitlementResultListener { - method public void onTetheringEntitlementResult(int); + @Deprecated public static interface ConnectivityManager.OnTetheringEntitlementResultListener { + method @Deprecated public void onTetheringEntitlementResult(int); } - public abstract static class ConnectivityManager.OnTetheringEventCallback { - ctor public ConnectivityManager.OnTetheringEventCallback(); - method public void onUpstreamChanged(@Nullable android.net.Network); + @Deprecated public abstract static class ConnectivityManager.OnTetheringEventCallback { + ctor @Deprecated public ConnectivityManager.OnTetheringEventCallback(); + method @Deprecated public void onUpstreamChanged(@Nullable android.net.Network); } public class InvalidPacketException extends java.lang.Exception { @@ -5421,6 +6197,8 @@ package android.net { method @NonNull public java.util.List<java.lang.String> getAllInterfaceNames(); method @NonNull public java.util.List<android.net.LinkAddress> getAllLinkAddresses(); method @NonNull public java.util.List<android.net.RouteInfo> getAllRoutes(); + method @Nullable public android.net.Uri getCaptivePortalApiUrl(); + method @Nullable public android.net.CaptivePortalData getCaptivePortalData(); method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers(); method @Nullable public String getTcpBufferSizes(); method @NonNull public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers(); @@ -5434,9 +6212,12 @@ package android.net { method public boolean isIpv6Provisioned(); method public boolean isProvisioned(); method public boolean isReachable(@NonNull java.net.InetAddress); + method @NonNull public android.net.LinkProperties makeSensitiveFieldsParcelingCopy(); method public boolean removeDnsServer(@NonNull java.net.InetAddress); method public boolean removeLinkAddress(@NonNull android.net.LinkAddress); method public boolean removeRoute(@NonNull android.net.RouteInfo); + method public void setCaptivePortalApiUrl(@Nullable android.net.Uri); + method public void setCaptivePortalData(@Nullable android.net.CaptivePortalData); method public void setPcscfServers(@NonNull java.util.Collection<java.net.InetAddress>); method public void setPrivateDnsServerName(@Nullable String); method public void setTcpBufferSizes(@Nullable String); @@ -5521,6 +6302,7 @@ package android.net { public final class NetworkCapabilities implements android.os.Parcelable { method public boolean deduceRestrictedCapability(); + method @Nullable public String getSSID(); method @NonNull public int[] getTransportTypes(); method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities); method @NonNull public android.net.NetworkCapabilities setSSID(@Nullable String); @@ -5531,7 +6313,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; @@ -5540,6 +6322,22 @@ package android.net { field public final android.net.WifiKey wifiKey; } + public class NetworkPolicyManager { + method @NonNull public android.telephony.SubscriptionPlan[] getSubscriptionPlans(int, @NonNull String); + method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void registerSubscriptionCallback(@NonNull android.net.NetworkPolicyManager.SubscriptionCallback); + method public void setSubscriptionOverride(int, int, int, long, @NonNull String); + method public void setSubscriptionPlans(int, @NonNull android.telephony.SubscriptionPlan[], @NonNull String); + method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void unregisterSubscriptionCallback(@NonNull android.net.NetworkPolicyManager.SubscriptionCallback); + field public static final int SUBSCRIPTION_OVERRIDE_CONGESTED = 2; // 0x2 + field public static final int SUBSCRIPTION_OVERRIDE_UNMETERED = 1; // 0x1 + } + + public static class NetworkPolicyManager.SubscriptionCallback { + ctor public NetworkPolicyManager.SubscriptionCallback(); + method public void onSubscriptionOverride(int, int, int); + method public void onSubscriptionPlansChanged(int, @NonNull android.telephony.SubscriptionPlan[]); + } + public class NetworkProvider { ctor public NetworkProvider(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String); method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void declareNetworkRequestUnfulfillable(@NonNull android.net.NetworkRequest); @@ -5566,12 +6364,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"; @@ -5586,9 +6384,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 { @@ -5630,6 +6429,8 @@ package android.net { public final class RouteInfo implements android.os.Parcelable { ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int); + ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int, int); + method public int getMtu(); method public int getType(); field public static final int RTN_THROW = 9; // 0x9 field public static final int RTN_UNICAST = 1; // 0x1 @@ -5695,17 +6496,63 @@ 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 public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback); + method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener); + method public void stopAllTethering(); + method public void stopTethering(int); + method public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback); + field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED"; + field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY"; + field public static final String EXTRA_ACTIVE_TETHER = "tetherArray"; + field public static final String EXTRA_AVAILABLE_TETHER = "availableArray"; + field public static final String EXTRA_ERRORED_TETHER = "erroredArray"; + field public static final int TETHERING_BLUETOOTH = 2; // 0x2 + field public static final int TETHERING_INVALID = -1; // 0xffffffff + field public static final int TETHERING_USB = 1; // 0x1 + field public static final int TETHERING_WIFI = 0; // 0x0 + field public static final int TETHERING_WIFI_P2P = 3; // 0x3 + field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc + field public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; // 0x9 + field public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; // 0x8 + field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd + field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa + field public static final int TETHER_ERROR_MASTER_ERROR = 5; // 0x5 + field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf + field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe + field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 + field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb + field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2 + field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6 + field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4 + field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1 + field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3 + field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7 + } + + public static interface TetheringManager.OnTetheringEntitlementResultListener { + method public void onTetheringEntitlementResult(int); + } + + public abstract static class TetheringManager.TetheringEventCallback { + ctor public TetheringManager.TetheringEventCallback(); + method public void onError(@NonNull String, int); + method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps); + method public void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>); + method public void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>); + method public void onTetheringSupported(boolean); + method public void onUpstreamChanged(@Nullable android.net.Network); + } + + @Deprecated public static class TetheringManager.TetheringInterfaceRegexps { + ctor @Deprecated public TetheringManager.TetheringInterfaceRegexps(@NonNull String[], @NonNull String[], @NonNull String[]); + method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs(); + method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs(); + method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs(); } public class TrafficStats { @@ -6665,12 +7512,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 @@ -6708,6 +7555,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); @@ -6723,6 +7571,7 @@ package android.net.wifi { method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String[] getFactoryMacAddresses(); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,java.util.List<android.net.wifi.ScanResult>> getMatchingOsuProviders(@Nullable java.util.List<android.net.wifi.ScanResult>); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,android.net.wifi.hotspot2.PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(@NonNull java.util.Set<android.net.wifi.hotspot2.OsuProvider>); + method @NonNull @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE}) public java.util.Map<android.net.wifi.WifiNetworkSuggestion,java.util.List<android.net.wifi.ScanResult>> getMatchingScanResults(@NonNull java.util.List<android.net.wifi.WifiNetworkSuggestion>, @Nullable java.util.List<android.net.wifi.ScanResult>); method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks(); method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.SoftApConfiguration getSoftApConfiguration(); method public int getVerboseLoggingLevel(); @@ -7591,7 +8440,7 @@ package android.os { method @Nullable public android.os.NativeHandle getHandle(); method @NonNull public String getName(); method public long getSize(); - method @NonNull public android.os.NativeHandle releaseHandle(); + method @Nullable public android.os.NativeHandle releaseHandle(); } public class HidlSupport { @@ -7742,8 +8591,11 @@ package android.os { method @Nullable @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public android.os.IncidentManager.IncidentReport getIncidentReport(android.net.Uri); method @NonNull @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public java.util.List<android.net.Uri> getIncidentReportList(String); method @RequiresPermission(android.Manifest.permission.APPROVE_INCIDENT_REPORTS) public java.util.List<android.os.IncidentManager.PendingReport> getPendingReports(); + method public void registerSection(int, @NonNull String, @NonNull android.os.IncidentManager.DumpCallback); + method public void registerSection(int, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.IncidentManager.DumpCallback); method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void reportIncident(android.os.IncidentReportArgs); method @RequiresPermission("android.permission.REQUEST_INCIDENT_REPORT_APPROVAL") public void requestAuthorization(int, String, int, android.os.IncidentManager.AuthListener); + method public void unregisterSection(int); field public static final int FLAG_CONFIRMATION_DIALOG = 1; // 0x1 field public static final int PRIVACY_POLICY_AUTO = 200; // 0xc8 field public static final int PRIVACY_POLICY_EXPLICIT = 100; // 0x64 @@ -7756,6 +8608,11 @@ package android.os { method public void onReportDenied(); } + public static class IncidentManager.DumpCallback { + ctor public IncidentManager.DumpCallback(); + method public void onDumpSection(int, @NonNull java.io.OutputStream); + } + public static class IncidentManager.IncidentReport implements java.io.Closeable android.os.Parcelable { ctor public IncidentManager.IncidentReport(android.os.Parcel); method public void close(); @@ -7811,10 +8668,14 @@ package android.os { method @RequiresPermission(allOf={android.Manifest.permission.READ_DREAM_STATE, android.Manifest.permission.WRITE_DREAM_STATE}) public void dream(long); method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean forceSuspend(); method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public int getPowerSaveModeTrigger(); + method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplayAvailable(); + method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplaySuppressed(); + method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplaySuppressedForToken(@NonNull String); method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSaveEnabled(boolean); method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSavePolicy(@NonNull android.os.BatterySaverPolicyConfig); method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public boolean setDynamicPowerSaveHint(boolean, int); method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setPowerSaveModeEnabled(boolean); + method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void suppressAmbientDisplay(@NonNull String, boolean); method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.USER_ACTIVITY}) public void userActivity(long, int, int); field public static final int POWER_SAVE_MODE_TRIGGER_DYNAMIC = 1; // 0x1 field public static final int POWER_SAVE_MODE_TRIGGER_PERCENTAGE = 0; // 0x0 @@ -7935,7 +8796,6 @@ package android.os { method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyRcsMessageServiceRegisterer(); method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyRegistryServiceRegisterer(); method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getTelephonyServiceRegisterer(); - method @NonNull public android.os.TelephonyServiceManager.ServiceRegisterer getWindowServiceRegisterer(); } public static class TelephonyServiceManager.ServiceNotFoundException extends java.lang.Exception { @@ -9339,6 +10199,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); @@ -9437,6 +10298,7 @@ package android.service.notification { field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR; field public static final String KEY_CONTEXTUAL_ACTIONS = "key_contextual_actions"; field public static final String KEY_IMPORTANCE = "key_importance"; + field public static final String KEY_NOT_CONVERSATION = "key_not_conversation"; field public static final String KEY_PEOPLE = "key_people"; field public static final String KEY_RANKING_SCORE = "key_ranking_score"; field public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria"; @@ -9698,6 +10560,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 { @@ -10043,6 +10913,13 @@ package android.telephony { field public static final int TRANSPORT_TYPE_INVALID = -1; // 0xffffffff } + public static final class AccessNetworkConstants.NgranBands { + method public static int getFrequencyRangeGroup(int); + field public static final int FREQUENCY_RANGE_GROUP_1 = 1; // 0x1 + field public static final int FREQUENCY_RANGE_GROUP_2 = 2; // 0x2 + field public static final int FREQUENCY_RANGE_GROUP_UNKNOWN = 0; // 0x0 + } + public final class BarringInfo implements android.os.Parcelable { ctor public BarringInfo(); method @NonNull public android.telephony.BarringInfo createLocationInfoSanitizedCopy(); @@ -10154,6 +11031,19 @@ package android.telephony { method @NonNull public java.util.List<android.telephony.CbGeoUtils.LatLng> getVertices(); } + public final class CdmaEriInformation implements android.os.Parcelable { + method public int describeContents(); + method public int getEriIconIndex(); + method public int getEriIconMode(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CdmaEriInformation> CREATOR; + field public static final int ERI_FLASH = 2; // 0x2 + field public static final int ERI_ICON_MODE_FLASH = 1; // 0x1 + field public static final int ERI_ICON_MODE_NORMAL = 0; // 0x0 + field public static final int ERI_OFF = 1; // 0x1 + field public static final int ERI_ON = 0; // 0x0 + } + public class CellBroadcastIntents { method public static void sendOrderedBroadcastForBackgroundReceivers(@NonNull android.content.Context, @Nullable android.os.UserHandle, @NonNull android.content.Intent, @Nullable String, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle); } @@ -10772,7 +11662,7 @@ package android.telephony { method public void onSrvccStateChanged(int); method public void onVoiceActivationStateChanged(int); field @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) public static final int LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH = 512; // 0x200 - field public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000 + field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000 field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000 field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000 field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800 @@ -11067,6 +11957,7 @@ package android.telephony { } public final class SmsManager { + method @RequiresPermission(android.Manifest.permission.ACCESS_MESSAGES_ON_ICC) public boolean copyMessageToIcc(@Nullable byte[], @NonNull byte[], int); method @RequiresPermission(android.Manifest.permission.ACCESS_MESSAGES_ON_ICC) public boolean deleteMessageFromIcc(int); method public boolean disableCellBroadcastRange(int, int, int); method public boolean enableCellBroadcastRange(int, int, int); @@ -11084,6 +11975,7 @@ package android.telephony { public class SmsMessage { method @Nullable public static android.telephony.SmsMessage createFromNativeSmsSubmitPdu(@NonNull byte[], boolean); + method @Nullable public static android.telephony.SmsMessage.SubmitPdu getSmsPdu(int, int, @Nullable String, @NonNull String, @NonNull String, long); method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static byte[] getSubmitPduEncodedMessage(boolean, @NonNull String, @NonNull String, int, int, int, int, int, int); } @@ -11179,6 +12071,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCarrierPrivilegeStatus(int); method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCarrierPrivilegedPackagesForAllActiveSubscriptions(); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules(); + method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CdmaEriInformation getCdmaEriInformation(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin(); @@ -11235,7 +12128,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(); @@ -11248,6 +12140,7 @@ package android.telephony { method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting); method public boolean needsOtaServiceProvisioning(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled(); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyUserActivity(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean); method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback); @@ -11271,6 +12164,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setIccLockEnabled(boolean, @NonNull String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setNetworkSelectionModeManual(@NonNull String, int, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPolicyDataEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long); @@ -11279,6 +12173,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadioPower(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerState(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerStateForSlot(int, int); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Boolean>); method @Deprecated public void setVisualVoicemailEnabled(android.telecom.PhoneAccountHandle, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoiceActivationState(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void shutdownAllRadios(); @@ -11304,6 +12199,7 @@ package android.telephony { field public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED"; field public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED = "android.intent.action.EMERGENCY_CALL_STATE_CHANGED"; field public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE = "com.android.omadm.service.CONFIGURATION_UPDATE"; + field public static final String ACTION_SERVICE_PROVIDERS_UPDATED = "android.telephony.action.SERVICE_PROVIDERS_UPDATED"; field public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS = "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS"; field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED"; field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED"; @@ -11326,19 +12222,35 @@ package android.telephony { field public static final String EXTRA_APN_PROTOCOL_INT = "apnProtoInt"; field @Deprecated public static final String EXTRA_APN_TYPE = "apnType"; field public static final String EXTRA_APN_TYPE_INT = "apnTypeInt"; + field public static final String EXTRA_DATA_SPN = "android.telephony.extra.DATA_SPN"; field public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = "defaultNetworkAvailable"; + field public static final String EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE = "android.telephony.extra.DEFAULT_SUBSCRIPTION_SELECT_TYPE"; + field public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL = 4; // 0x4 + field public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA = 1; // 0x1 + field public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE = 0; // 0x0 + field public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_SMS = 3; // 0x3 + field public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_VOICE = 2; // 0x2 field public static final String EXTRA_ERROR_CODE = "errorCode"; field public static final String EXTRA_PCO_ID = "pcoId"; field public static final String EXTRA_PCO_VALUE = "pcoValue"; field public static final String EXTRA_PHONE_IN_ECM_STATE = "android.telephony.extra.PHONE_IN_ECM_STATE"; field public static final String EXTRA_PHONE_IN_EMERGENCY_CALL = "android.telephony.extra.PHONE_IN_EMERGENCY_CALL"; + field public static final String EXTRA_PLMN = "android.telephony.extra.PLMN"; field public static final String EXTRA_REDIRECTION_URL = "redirectionUrl"; + field public static final String EXTRA_SHOW_PLMN = "android.telephony.extra.SHOW_PLMN"; + field public static final String EXTRA_SHOW_SPN = "android.telephony.extra.SHOW_SPN"; + field public static final String EXTRA_SIM_COMBINATION_NAMES = "android.telephony.extra.SIM_COMBINATION_NAMES"; + field public static final String EXTRA_SIM_COMBINATION_WARNING_TYPE = "android.telephony.extra.SIM_COMBINATION_WARNING_TYPE"; + field public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA = 1; // 0x1 + field public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE = 0; // 0x0 field public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE"; + field public static final String EXTRA_SPN = "android.telephony.extra.SPN"; field public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL"; field public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING"; field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff field public static final int KEY_TYPE_EPDG = 1; // 0x1 field public static final int KEY_TYPE_WLAN = 2; // 0x2 + field public static final String MODEM_ACTIVITY_RESULT_KEY = "controller_activity"; field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L @@ -11383,10 +12295,32 @@ 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 notifyActiveDataSubIdChanged(int); method public void notifyBarringInfoChanged(int, int, @NonNull android.telephony.BarringInfo); + method public void notifyCallForwardingChanged(int, boolean); + method public void notifyCallQualityChanged(int, int, @NonNull android.telephony.CallQuality, int); + method public void notifyCallStateChanged(int, int, int, @Nullable String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyCallStateChangedForAllSubscriptions(int, @Nullable String); method public void notifyCarrierNetworkChange(boolean); + method public void notifyCellInfoChanged(int, @NonNull java.util.List<android.telephony.CellInfo>); + method public void notifyCellLocation(int, @NonNull android.telephony.CellIdentity); + method public void notifyDataActivationStateChanged(int, int, int); + method public void notifyDataActivityChanged(int, int); + method public void notifyDataConnectionForSubscriber(int, int, int, @Nullable android.telephony.PreciseDataConnectionState); + method public void notifyDisconnectCause(int, int, int, int); + method public void notifyEmergencyNumberList(int, int); + method public void notifyImsDisconnectCause(int, @NonNull android.telephony.ims.ImsReasonInfo); + method public void notifyMessageWaitingChanged(int, int, boolean); + method public void notifyPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability); + method public void notifyPreciseCallState(int, int, int, int, int); + method public void notifyPreciseDataConnectionFailed(int, int, int, @Nullable String, int); + method public void notifyRadioPowerStateChanged(int, int, int); method public void notifyRegistrationFailed(int, int, @NonNull android.telephony.CellIdentity, @NonNull String, int, int, int); + method public void notifyServiceStateChanged(int, int, @NonNull android.telephony.ServiceState); + method public void notifySignalStrengthChanged(int, int, @NonNull android.telephony.SignalStrength); + method public void notifySrvccStateChanged(int, int); + method public void notifyUserMobileDataStateChanged(int, int, boolean); + method public void notifyVoiceActivationStateChanged(int, int, int); method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener); method public void removeOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener); } @@ -11490,7 +12424,9 @@ package android.telephony.data { method public int getId(); method @NonNull public String getInterfaceName(); method public int getLinkStatus(); - method public int getMtu(); + method @Deprecated public int getMtu(); + method public int getMtuV4(); + method public int getMtuV6(); method @NonNull public java.util.List<java.net.InetAddress> getPcscfAddresses(); method public int getProtocolType(); method public int getSuggestedRetryTime(); @@ -11512,7 +12448,9 @@ package android.telephony.data { method @NonNull public android.telephony.data.DataCallResponse.Builder setId(int); method @NonNull public android.telephony.data.DataCallResponse.Builder setInterfaceName(@NonNull String); method @NonNull public android.telephony.data.DataCallResponse.Builder setLinkStatus(int); - method @NonNull public android.telephony.data.DataCallResponse.Builder setMtu(int); + method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setMtu(int); + method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV4(int); + method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV6(int); method @NonNull public android.telephony.data.DataCallResponse.Builder setPcscfAddresses(@NonNull java.util.List<java.net.InetAddress>); method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int); method @NonNull public android.telephony.data.DataCallResponse.Builder setSuggestedRetryTime(int); @@ -11523,7 +12461,9 @@ package android.telephony.data { method @NonNull public String getApn(); method public int getAuthType(); method public int getBearerBitmask(); - method public int getMtu(); + method @Deprecated public int getMtu(); + method public int getMtuV4(); + method public int getMtuV6(); method @Nullable public String getPassword(); method public int getProfileId(); method public int getProtocolType(); @@ -11548,7 +12488,9 @@ package android.telephony.data { method @NonNull public android.telephony.data.DataProfile.Builder setApn(@NonNull String); method @NonNull public android.telephony.data.DataProfile.Builder setAuthType(int); method @NonNull public android.telephony.data.DataProfile.Builder setBearerBitmask(int); - method @NonNull public android.telephony.data.DataProfile.Builder setMtu(int); + method @Deprecated @NonNull public android.telephony.data.DataProfile.Builder setMtu(int); + method @NonNull public android.telephony.data.DataProfile.Builder setMtuV4(int); + method @NonNull public android.telephony.data.DataProfile.Builder setMtuV6(int); method @NonNull public android.telephony.data.DataProfile.Builder setPassword(@NonNull String); method @NonNull public android.telephony.data.DataProfile.Builder setPersistent(boolean); method @NonNull public android.telephony.data.DataProfile.Builder setPreferred(boolean); @@ -11847,6 +12789,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"; @@ -11958,6 +12901,9 @@ package android.telephony.ims { method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int); method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int); field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION"; + field public static final String ACTION_WFC_IMS_REGISTRATION_ERROR = "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR"; + field public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE"; + field public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE"; } public class ImsMmTelManager implements android.telephony.ims.RegistrationManager { @@ -12240,7 +13186,27 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback); + field public static final int KEY_1X_EPDG_TIMER_SEC = 64; // 0x40 + field public static final int KEY_1X_THRESHOLD = 59; // 0x3b + field public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50; // 0x32 + field public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0; // 0x0 + field public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53; // 0x35 + field public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49; // 0x31 + field public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48; // 0x30 + field public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1; // 0x1 + field public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47; // 0x2f + field public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52; // 0x34 + field public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51; // 0x33 field public static final int KEY_EAB_PROVISIONING_STATUS = 25; // 0x19 + field public static final int KEY_ENABLE_SILENT_REDIAL = 6; // 0x6 + field public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31; // 0x1f + field public static final int KEY_LTE_EPDG_TIMER_SEC = 62; // 0x3e + field public static final int KEY_LTE_THRESHOLD_1 = 56; // 0x38 + field public static final int KEY_LTE_THRESHOLD_2 = 57; // 0x39 + field public static final int KEY_LTE_THRESHOLD_3 = 58; // 0x3a + field public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3; // 0x3 + field public static final int KEY_MOBILE_DATA_ENABLED = 29; // 0x1d + field public static final int KEY_MULTIENDPOINT_ENABLED = 65; // 0x41 field public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19; // 0x13 field public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18; // 0x12 field public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20; // 0x14 @@ -12250,16 +13216,52 @@ package android.telephony.ims { field public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21; // 0x15 field public static final int KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC = 16; // 0x10 field public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15; // 0xf + field public static final int KEY_REGISTRATION_DOMAIN_NAME = 12; // 0xc + field public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33; // 0x21 + field public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34; // 0x22 + field public static final int KEY_RTP_SPEECH_END_PORT = 36; // 0x24 + field public static final int KEY_RTP_SPEECH_START_PORT = 35; // 0x23 + field public static final int KEY_RTT_ENABLED = 66; // 0x42 + field public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43; // 0x2b + field public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44; // 0x2c + field public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38; // 0x26 + field public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4; // 0x4 + field public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37; // 0x25 + field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42; // 0x2a + field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39; // 0x27 + field public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32; // 0x20 + field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45; // 0x2d + field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40; // 0x28 + field public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46; // 0x2e + field public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41; // 0x29 + field public static final int KEY_SIP_SESSION_TIMER_SEC = 2; // 0x2 + field public static final int KEY_SMS_FORMAT = 13; // 0xd + field public static final int KEY_SMS_OVER_IP_ENABLED = 14; // 0xe + field public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54; // 0x36 field public static final int KEY_T1_TIMER_VALUE_MS = 7; // 0x7 + field public static final int KEY_T2_TIMER_VALUE_MS = 8; // 0x8 + field public static final int KEY_TF_TIMER_VALUE_MS = 9; // 0x9 + field public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5; // 0x5 + field public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24; // 0x18 + field public static final int KEY_VIDEO_QUALITY = 55; // 0x37 + field public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28; // 0x1c field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a field public static final int KEY_VOLTE_PROVISIONING_STATUS = 10; // 0xa + field public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30; // 0x1e field public static final int KEY_VT_PROVISIONING_STATUS = 11; // 0xb + field public static final int KEY_WIFI_EPDG_TIMER_SEC = 63; // 0x3f + field public static final int KEY_WIFI_THRESHOLD_A = 60; // 0x3c + field public static final int KEY_WIFI_THRESHOLD_B = 61; // 0x3d field public static final int PROVISIONING_RESULT_UNKNOWN = -1; // 0xffffffff field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0 field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1 + field public static final int SMS_FORMAT_3GPP = 1; // 0x1 + field public static final int SMS_FORMAT_3GPP2 = 0; // 0x0 field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC"; field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY"; + field public static final int VIDEO_QUALITY_HIGH = 1; // 0x1 + field public static final int VIDEO_QUALITY_LOW = 0; // 0x0 } public static class ProvisioningManager.Callback { @@ -12577,6 +13579,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); @@ -12895,8 +13898,8 @@ package android.webkit { } public interface PacProcessor { + method @Nullable public String findProxyForUrl(@NonNull String); method @NonNull public static android.webkit.PacProcessor getInstance(); - method @Nullable public String makeProxyRequest(@NonNull String); method public boolean setProxyScript(@NonNull String); } @@ -13246,4 +14249,3 @@ package android.webkit { } } - diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt index fde6bb3424f7..23e1ed75998a 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 c110ebe4479c..e2407d65ef5c 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); } } @@ -742,6 +743,10 @@ package android.content { field @Nullable public final android.util.ArraySet<android.content.ComponentName> whitelistedComponents; } + public abstract class ContentProvider implements android.content.ComponentCallbacks2 { + method @NonNull public static android.os.UserHandle getUserHandleFromUri(@NonNull android.net.Uri); + } + public class ContentProviderClient implements java.lang.AutoCloseable { method @RequiresPermission(android.Manifest.permission.REMOVE_TASKS) public void setDetectNotResponding(long); } @@ -754,7 +759,6 @@ 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(); @@ -775,7 +779,6 @@ package android.content { } public class ContextWrapper extends android.content.Context { - method public android.view.Display getDisplay(); method public int getDisplayId(); } @@ -1128,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 { @@ -1395,6 +1441,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 { @@ -1415,9 +1462,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 @@ -1515,6 +1564,32 @@ package android.net { field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2 } + public final class CaptivePortalData implements android.os.Parcelable { + method public int describeContents(); + method public long getByteLimit(); + method public long getExpiryTimeMillis(); + method public long getRefreshTimeMillis(); + method @Nullable public android.net.Uri getUserPortalUrl(); + method @Nullable public android.net.Uri getVenueInfoUrl(); + method public boolean isCaptive(); + method public boolean isSessionExtendable(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR; + } + + public static class CaptivePortalData.Builder { + ctor public CaptivePortalData.Builder(); + ctor public CaptivePortalData.Builder(@Nullable android.net.CaptivePortalData); + method @NonNull public android.net.CaptivePortalData build(); + method @NonNull public android.net.CaptivePortalData.Builder setBytesRemaining(long); + method @NonNull public android.net.CaptivePortalData.Builder setCaptive(boolean); + method @NonNull public android.net.CaptivePortalData.Builder setExpiryTime(long); + method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long); + method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean); + method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri); + method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri); + } + public class ConnectivityManager { method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle); field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC"; @@ -1545,6 +1620,8 @@ package android.net { ctor public LinkProperties(@Nullable android.net.LinkProperties); method public boolean addDnsServer(@NonNull java.net.InetAddress); method public boolean addLinkAddress(@NonNull android.net.LinkAddress); + method @Nullable public android.net.Uri getCaptivePortalApiUrl(); + method @Nullable public android.net.CaptivePortalData getCaptivePortalData(); method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers(); method @Nullable public String getTcpBufferSizes(); method @NonNull public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers(); @@ -1555,9 +1632,12 @@ package android.net { method public boolean isIpv6Provisioned(); method public boolean isProvisioned(); method public boolean isReachable(@NonNull java.net.InetAddress); + method @NonNull public android.net.LinkProperties makeSensitiveFieldsParcelingCopy(); method public boolean removeDnsServer(@NonNull java.net.InetAddress); method public boolean removeLinkAddress(@NonNull android.net.LinkAddress); method public boolean removeRoute(@NonNull android.net.RouteInfo); + method public void setCaptivePortalApiUrl(@Nullable android.net.Uri); + method public void setCaptivePortalData(@Nullable android.net.CaptivePortalData); method public void setPcscfServers(@NonNull java.util.Collection<java.net.InetAddress>); method public void setPrivateDnsServerName(@Nullable String); method public void setTcpBufferSizes(@Nullable String); @@ -1629,6 +1709,61 @@ package android.net { method public void teardownTestNetwork(@NonNull android.net.Network); } + public class TetheringManager { + method public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback); + method @RequiresPermission("android.permission.TETHER_PRIVILEGED") public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener); + method public void stopAllTethering(); + method public void stopTethering(int); + method public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback); + field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED"; + field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY"; + field public static final String EXTRA_ACTIVE_TETHER = "tetherArray"; + field public static final String EXTRA_AVAILABLE_TETHER = "availableArray"; + field public static final String EXTRA_ERRORED_TETHER = "erroredArray"; + field public static final int TETHERING_BLUETOOTH = 2; // 0x2 + field public static final int TETHERING_INVALID = -1; // 0xffffffff + field public static final int TETHERING_USB = 1; // 0x1 + field public static final int TETHERING_WIFI = 0; // 0x0 + field public static final int TETHERING_WIFI_P2P = 3; // 0x3 + field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc + field public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; // 0x9 + field public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; // 0x8 + field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd + field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa + field public static final int TETHER_ERROR_MASTER_ERROR = 5; // 0x5 + field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf + field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe + field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 + field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb + field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2 + field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6 + field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4 + field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1 + field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3 + field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7 + } + + public static interface TetheringManager.OnTetheringEntitlementResultListener { + method public void onTetheringEntitlementResult(int); + } + + public abstract static class TetheringManager.TetheringEventCallback { + ctor public TetheringManager.TetheringEventCallback(); + method public void onError(@NonNull String, int); + method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps); + method public void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>); + method public void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>); + method public void onTetheringSupported(boolean); + method public void onUpstreamChanged(@Nullable android.net.Network); + } + + @Deprecated public static class TetheringManager.TetheringInterfaceRegexps { + ctor @Deprecated public TetheringManager.TetheringInterfaceRegexps(@NonNull String[], @NonNull String[], @NonNull String[]); + method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs(); + method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs(); + method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs(); + } + public class TrafficStats { method public static long getLoopbackRxBytes(); method public static long getLoopbackRxPackets(); @@ -1893,7 +2028,7 @@ package android.os { method @Nullable public android.os.NativeHandle getHandle(); method @NonNull public String getName(); method public long getSize(); - method @NonNull public android.os.NativeHandle releaseHandle(); + method @Nullable public android.os.NativeHandle releaseHandle(); } public abstract class HwBinder implements android.os.IHwBinder { @@ -2037,8 +2172,11 @@ package android.os { method @Nullable @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public android.os.IncidentManager.IncidentReport getIncidentReport(android.net.Uri); method @NonNull @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public java.util.List<android.net.Uri> getIncidentReportList(String); method @RequiresPermission(android.Manifest.permission.APPROVE_INCIDENT_REPORTS) public java.util.List<android.os.IncidentManager.PendingReport> getPendingReports(); + method public void registerSection(int, @NonNull String, @NonNull android.os.IncidentManager.DumpCallback); + method public void registerSection(int, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.IncidentManager.DumpCallback); method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void reportIncident(android.os.IncidentReportArgs); method @RequiresPermission("android.permission.REQUEST_INCIDENT_REPORT_APPROVAL") public void requestAuthorization(int, String, int, android.os.IncidentManager.AuthListener); + method public void unregisterSection(int); field public static final int FLAG_CONFIRMATION_DIALOG = 1; // 0x1 field public static final int PRIVACY_POLICY_AUTO = 200; // 0xc8 field public static final int PRIVACY_POLICY_EXPLICIT = 100; // 0x64 @@ -2051,6 +2189,11 @@ package android.os { method public void onReportDenied(); } + public static class IncidentManager.DumpCallback { + ctor public IncidentManager.DumpCallback(); + method public void onDumpSection(int, @NonNull java.io.OutputStream); + } + public static class IncidentManager.IncidentReport implements java.io.Closeable android.os.Parcelable { ctor public IncidentManager.IncidentReport(android.os.Parcel); method public void close(); @@ -3113,6 +3256,8 @@ package android.telecom { method @NonNull public android.telecom.ConnectionRequest.Builder setAccountHandle(@NonNull android.telecom.PhoneAccountHandle); method @NonNull public android.telecom.ConnectionRequest.Builder setAddress(@NonNull android.net.Uri); method @NonNull public android.telecom.ConnectionRequest.Builder setExtras(@NonNull android.os.Bundle); + method @NonNull public android.telecom.ConnectionRequest.Builder setIsAdhocConferenceCall(boolean); + method @NonNull public android.telecom.ConnectionRequest.Builder setParticipants(@Nullable java.util.List<android.net.Uri>); method @NonNull public android.telecom.ConnectionRequest.Builder setRttPipeFromInCall(@NonNull android.os.ParcelFileDescriptor); method @NonNull public android.telecom.ConnectionRequest.Builder setRttPipeToInCall(@NonNull android.os.ParcelFileDescriptor); method @NonNull public android.telecom.ConnectionRequest.Builder setShouldShowIncomingCallUi(boolean); @@ -3152,6 +3297,13 @@ package android.telephony { field public static final int TRANSPORT_TYPE_INVALID = -1; // 0xffffffff } + public static final class AccessNetworkConstants.NgranBands { + method public static int getFrequencyRangeGroup(int); + field public static final int FREQUENCY_RANGE_GROUP_1 = 1; // 0x1 + field public static final int FREQUENCY_RANGE_GROUP_2 = 2; // 0x2 + field public static final int FREQUENCY_RANGE_GROUP_UNKNOWN = 0; // 0x0 + } + public final class BarringInfo implements android.os.Parcelable { ctor public BarringInfo(); ctor public BarringInfo(@Nullable android.telephony.CellIdentity, @NonNull android.util.SparseArray<android.telephony.BarringInfo.BarringServiceInfo>); @@ -3318,6 +3470,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile(); method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String); method public void setCarrierTestOverride(String, String, String, String, String, String, String, String, String); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Boolean>); method @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public void updateTestOtaEmergencyNumberDbFilePath(@NonNull String); field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1 @@ -3327,6 +3480,39 @@ package android.telephony { field public static final int UNKNOWN_CARRIER_ID_LIST_VERSION = -1; // 0xffffffff } + public class TelephonyRegistryManager { + method public void addOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor); + method public void addOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor); + method public void notifyActiveDataSubIdChanged(int); + method public void notifyBarringInfoChanged(int, int, @NonNull android.telephony.BarringInfo); + method public void notifyCallForwardingChanged(int, boolean); + method public void notifyCallQualityChanged(int, int, @NonNull android.telephony.CallQuality, int); + method public void notifyCallStateChanged(int, int, int, @Nullable String); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyCallStateChangedForAllSubscriptions(int, @Nullable String); + method public void notifyCarrierNetworkChange(boolean); + method public void notifyCellInfoChanged(int, @NonNull java.util.List<android.telephony.CellInfo>); + method public void notifyCellLocation(int, @NonNull android.telephony.CellIdentity); + method public void notifyDataActivationStateChanged(int, int, int); + method public void notifyDataActivityChanged(int, int); + method public void notifyDataConnectionForSubscriber(int, int, int, @Nullable android.telephony.PreciseDataConnectionState); + method public void notifyDisconnectCause(int, int, int, int); + method public void notifyEmergencyNumberList(int, int); + method public void notifyImsDisconnectCause(int, @NonNull android.telephony.ims.ImsReasonInfo); + method public void notifyMessageWaitingChanged(int, int, boolean); + method public void notifyPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability); + method public void notifyPreciseCallState(int, int, int, int, int); + method public void notifyPreciseDataConnectionFailed(int, int, int, @Nullable String, int); + method public void notifyRadioPowerStateChanged(int, int, int); + method public void notifyRegistrationFailed(int, int, @NonNull android.telephony.CellIdentity, @NonNull String, int, int, int); + method public void notifyServiceStateChanged(int, int, @NonNull android.telephony.ServiceState); + method public void notifySignalStrengthChanged(int, int, @NonNull android.telephony.SignalStrength); + method public void notifySrvccStateChanged(int, int); + method public void notifyUserMobileDataStateChanged(int, int, boolean); + method public void notifyVoiceActivationStateChanged(int, int, int); + method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener); + method public void removeOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener); + } + } package android.telephony.emergency { @@ -3436,6 +3622,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"; @@ -3548,6 +3735,9 @@ package android.telephony.ims { method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int); method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int); field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION"; + field public static final String ACTION_WFC_IMS_REGISTRATION_ERROR = "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR"; + field public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE"; + field public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE"; } public class ImsMmTelManager implements android.telephony.ims.RegistrationManager { @@ -3826,7 +4016,27 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback); + field public static final int KEY_1X_EPDG_TIMER_SEC = 64; // 0x40 + field public static final int KEY_1X_THRESHOLD = 59; // 0x3b + field public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50; // 0x32 + field public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0; // 0x0 + field public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53; // 0x35 + field public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49; // 0x31 + field public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48; // 0x30 + field public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1; // 0x1 + field public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47; // 0x2f + field public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52; // 0x34 + field public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51; // 0x33 field public static final int KEY_EAB_PROVISIONING_STATUS = 25; // 0x19 + field public static final int KEY_ENABLE_SILENT_REDIAL = 6; // 0x6 + field public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31; // 0x1f + field public static final int KEY_LTE_EPDG_TIMER_SEC = 62; // 0x3e + field public static final int KEY_LTE_THRESHOLD_1 = 56; // 0x38 + field public static final int KEY_LTE_THRESHOLD_2 = 57; // 0x39 + field public static final int KEY_LTE_THRESHOLD_3 = 58; // 0x3a + field public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3; // 0x3 + field public static final int KEY_MOBILE_DATA_ENABLED = 29; // 0x1d + field public static final int KEY_MULTIENDPOINT_ENABLED = 65; // 0x41 field public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19; // 0x13 field public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18; // 0x12 field public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20; // 0x14 @@ -3836,16 +4046,52 @@ package android.telephony.ims { field public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21; // 0x15 field public static final int KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC = 16; // 0x10 field public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15; // 0xf + field public static final int KEY_REGISTRATION_DOMAIN_NAME = 12; // 0xc + field public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33; // 0x21 + field public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34; // 0x22 + field public static final int KEY_RTP_SPEECH_END_PORT = 36; // 0x24 + field public static final int KEY_RTP_SPEECH_START_PORT = 35; // 0x23 + field public static final int KEY_RTT_ENABLED = 66; // 0x42 + field public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43; // 0x2b + field public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44; // 0x2c + field public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38; // 0x26 + field public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4; // 0x4 + field public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37; // 0x25 + field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42; // 0x2a + field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39; // 0x27 + field public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32; // 0x20 + field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45; // 0x2d + field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40; // 0x28 + field public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46; // 0x2e + field public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41; // 0x29 + field public static final int KEY_SIP_SESSION_TIMER_SEC = 2; // 0x2 + field public static final int KEY_SMS_FORMAT = 13; // 0xd + field public static final int KEY_SMS_OVER_IP_ENABLED = 14; // 0xe + field public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54; // 0x36 field public static final int KEY_T1_TIMER_VALUE_MS = 7; // 0x7 + field public static final int KEY_T2_TIMER_VALUE_MS = 8; // 0x8 + field public static final int KEY_TF_TIMER_VALUE_MS = 9; // 0x9 + field public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5; // 0x5 + field public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24; // 0x18 + field public static final int KEY_VIDEO_QUALITY = 55; // 0x37 + field public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28; // 0x1c field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a field public static final int KEY_VOLTE_PROVISIONING_STATUS = 10; // 0xa + field public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30; // 0x1e field public static final int KEY_VT_PROVISIONING_STATUS = 11; // 0xb + field public static final int KEY_WIFI_EPDG_TIMER_SEC = 63; // 0x3f + field public static final int KEY_WIFI_THRESHOLD_A = 60; // 0x3c + field public static final int KEY_WIFI_THRESHOLD_B = 61; // 0x3d field public static final int PROVISIONING_RESULT_UNKNOWN = -1; // 0xffffffff field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0 field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1 + field public static final int SMS_FORMAT_3GPP = 1; // 0x1 + field public static final int SMS_FORMAT_3GPP2 = 0; // 0x0 field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC"; field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY"; + field public static final int VIDEO_QUALITY_HIGH = 1; // 0x1 + field public static final int VIDEO_QUALITY_LOW = 0; // 0x0 } public static class ProvisioningManager.Callback { @@ -4115,6 +4361,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); @@ -4402,22 +4649,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(); @@ -4462,7 +4693,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..54f7f68d96cb 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: diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp index 62312d1cccaa..6c2b8551bf73 100644 --- a/cmds/incidentd/src/IncidentService.cpp +++ b/cmds/incidentd/src/IncidentService.cpp @@ -350,11 +350,11 @@ Status IncidentService::reportIncidentToDumpstate(unique_fd stream, Status IncidentService::registerSection(const int id, const String16& name16, const sp<IIncidentDumpCallback>& callback) { const char* name = String8(name16).c_str(); - ALOGI("Register section %d: %s", id, name); + const uid_t callingUid = IPCThreadState::self()->getCallingUid(); + ALOGI("Uid %d registers section %d '%s'", callingUid, id, name); if (callback == nullptr) { return Status::fromExceptionCode(Status::EX_NULL_POINTER); } - const uid_t callingUid = IPCThreadState::self()->getCallingUid(); for (int i = 0; i < mRegisteredSections.size(); i++) { if (mRegisteredSections.at(i)->id == id) { if (mRegisteredSections.at(i)->uid != callingUid) { @@ -370,8 +370,9 @@ Status IncidentService::registerSection(const int id, const String16& name16, } Status IncidentService::unregisterSection(const int id) { - ALOGI("Unregister section %d", id); uid_t callingUid = IPCThreadState::self()->getCallingUid(); + ALOGI("Uid %d unregisters section %d", callingUid, id); + for (auto it = mRegisteredSections.begin(); it != mRegisteredSections.end(); it++) { if ((*it)->id == id) { if ((*it)->uid != callingUid) { diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 6eafbd8bb3f1..080b1af35059 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -73,7 +73,6 @@ cc_defaults { "src/external/puller_util.cpp", "src/external/ResourceHealthManagerPuller.cpp", "src/external/StatsCallbackPuller.cpp", - "src/external/StatsCompanionServicePuller.cpp", "src/external/StatsPuller.cpp", "src/external/StatsPullerManager.cpp", "src/external/SubsystemSleepStatePuller.cpp", diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 468a43fe84f9..d05ac189c834 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -810,6 +810,7 @@ message ScheduledJobStateChanged { FREQUENT = 2; RARE = 3; NEVER = 4; + RESTRICTED = 5; } optional Bucket standby_bucket = 5 [default = UNKNOWN]; @@ -3303,6 +3304,9 @@ message UiEventReported { // For example, the package posting a notification, or the destination package of a share. optional int32 uid = 2 [(is_uid) = true]; optional string package_name = 3; + // An identifier used to disambiguate which logs refer to a particular instance of some + // UI element. Useful when there might be multiple instances simultaneously active. + optional int32 instance_id = 4; } /** diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp deleted file mode 100644 index f37d2bedf8c2..000000000000 --- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG false -#include "Log.h" - -#include <android/os/IStatsCompanionService.h> -#include <binder/IPCThreadState.h> -#include <private/android_filesystem_config.h> -#include "../stats_log_util.h" -#include "../statscompanion_util.h" -#include "StatsCompanionServicePuller.h" - -using namespace android; -using namespace android::base; -using namespace android::binder; -using namespace android::os; -using std::make_shared; -using std::shared_ptr; -using std::vector; - -namespace android { -namespace os { -namespace statsd { - -// The reading and parsing are implemented in Java. It is not difficult to port over. But for now -// let StatsCompanionService handle that and send the data back. -StatsCompanionServicePuller::StatsCompanionServicePuller(int tagId) : StatsPuller(tagId) { -} - -void StatsCompanionServicePuller::SetStatsCompanionService( - sp<IStatsCompanionService> statsCompanionService) { - AutoMutex _l(mStatsCompanionServiceLock); - sp<IStatsCompanionService> tmpForLock = mStatsCompanionService; - mStatsCompanionService = statsCompanionService; -} - -bool StatsCompanionServicePuller::PullInternal(vector<shared_ptr<LogEvent> >* data) { - sp<IStatsCompanionService> statsCompanionServiceCopy = mStatsCompanionService; - if (statsCompanionServiceCopy != nullptr) { - vector<StatsLogEventWrapper> returned_value; - Status status = statsCompanionServiceCopy->pullData(mTagId, &returned_value); - if (!status.isOk()) { - ALOGW("StatsCompanionServicePuller::pull failed for %d", mTagId); - StatsdStats::getInstance().noteStatsCompanionPullFailed(mTagId); - if (status.exceptionCode() == Status::Exception::EX_TRANSACTION_FAILED) { - StatsdStats::getInstance().noteStatsCompanionPullBinderTransactionFailed(mTagId); - } - return false; - } - data->clear(); - for (const StatsLogEventWrapper& it : returned_value) { - LogEvent::createLogEvents(it, *data); - } - VLOG("StatsCompanionServicePuller::pull succeeded for %d", mTagId); - return true; - } else { - ALOGW("statsCompanion not found!"); - return false; - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.h b/cmds/statsd/src/external/StatsCompanionServicePuller.h deleted file mode 100644 index 2e133207f01d..000000000000 --- a/cmds/statsd/src/external/StatsCompanionServicePuller.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <utils/String16.h> -#include "StatsPuller.h" - -namespace android { -namespace os { -namespace statsd { - -class StatsCompanionServicePuller : public StatsPuller { -public: - explicit StatsCompanionServicePuller(int tagId); - - void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) override; - -private: - Mutex mStatsCompanionServiceLock; - sp<IStatsCompanionService> mStatsCompanionService = nullptr; - bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override; -}; - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 16a65e293219..8d67b5c169f5 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -37,7 +37,6 @@ #include "PowerStatsPuller.h" #include "ResourceHealthManagerPuller.h" #include "StatsCallbackPuller.h" -#include "StatsCompanionServicePuller.h" #include "SubsystemSleepStatePuller.h" #include "TrainInfoPuller.h" #include "statslog.h" @@ -86,16 +85,6 @@ std::map<PullerKey, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { {{.atomTag = android::util::BATTERY_CYCLE_COUNT}, {.puller = new ResourceHealthManagerPuller(android::util::BATTERY_CYCLE_COUNT)}}, - // DebugElapsedClock. - {{.atomTag = android::util::DEBUG_ELAPSED_CLOCK}, - {.additiveFields = {1, 2, 3, 4}, - .puller = new StatsCompanionServicePuller(android::util::DEBUG_ELAPSED_CLOCK)}}, - - // DebugFailingElapsedClock. - {{.atomTag = android::util::DEBUG_FAILING_ELAPSED_CLOCK}, - {.additiveFields = {1, 2, 3, 4}, - .puller = new StatsCompanionServicePuller(android::util::DEBUG_FAILING_ELAPSED_CLOCK)}}, - // TrainInfo. {{.atomTag = android::util::TRAIN_INFO}, {.puller = new TrainInfoPuller()}}, diff --git a/cmds/svc/src/com/android/commands/svc/UsbCommand.java b/cmds/svc/src/com/android/commands/svc/UsbCommand.java index 3893be49e739..cd751f4e2d20 100644 --- a/cmds/svc/src/com/android/commands/svc/UsbCommand.java +++ b/cmds/svc/src/com/android/commands/svc/UsbCommand.java @@ -42,7 +42,9 @@ public class UsbCommand extends Svc.Command { + " Sets the functions which, if the device was charging, become current on" + "screen unlock. If function is blank, turn off this feature.\n" + " svc usb getFunctions\n" - + " Gets the list of currently enabled functions\n\n" + + " Gets the list of currently enabled functions\n" + + " svc usb resetUsbGadget\n" + + " Reset usb gadget\n\n" + "possible values of [function] are any of 'mtp', 'ptp', 'rndis', 'midi'\n"; } @@ -75,6 +77,13 @@ public class UsbCommand extends Svc.Command { System.err.println("Error communicating with UsbManager: " + e); } return; + } else if ("resetUsbGadget".equals(args[1])) { + try { + usbMgr.resetUsbGadget(); + } catch (RemoteException e) { + System.err.println("Error communicating with UsbManager: " + e); + } + return; } } System.err.println(longHelp()); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index b82a67556fc0..2ca5b1d5c76f 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -7326,6 +7326,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 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/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index fd56834ac361..4e6319db97f4 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -868,7 +868,7 @@ public class ApplicationPackageManager extends PackageManager { } @Override - public CharSequence getBackgroundPermissionButtonLabel() { + public CharSequence getBackgroundPermissionOptionLabel() { try { String permissionController = getPermissionControllerPackageName(); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 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/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index 4cb8d936aa9c..34684c4c4228 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -175,4 +175,11 @@ interface IWallpaperManager { * Called from SystemUI when it shows the AoD UI. */ oneway void setInAmbientMode(boolean inAmbientMode, long animationDuration); + + /** + * Called when the wallpaper needs to zoom out. + * The zoom value goes from 0 to 1 (inclusive) where 1 means fully zoomed out, + * 0 means fully zoomed in + */ + oneway void setWallpaperZoomOut(float zoom, String callingPackage, int displayId); } diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 0c5e67cd4c6a..f0d0e98b841f 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -1180,13 +1180,26 @@ public final class LoadedApk { } try { - java.lang.ClassLoader cl = getClassLoader(); + final java.lang.ClassLoader cl = getClassLoader(); if (!mPackageName.equals("android")) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "initializeJavaContextClassLoader"); initializeJavaContextClassLoader(); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } + + // Rewrite the R 'constants' for all library apks. + SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers( + false, false); + for (int i = 0, n = packageIdentifiers.size(); i < n; i++) { + final int id = packageIdentifiers.keyAt(i); + if (id == 0x01 || id == 0x7f) { + continue; + } + + rewriteRValues(cl, packageIdentifiers.valueAt(i), id); + } + ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this); app = mActivityThread.mInstrumentation.newApplication( cl, appClass, appContext); @@ -1215,19 +1228,6 @@ public final class LoadedApk { } } - // Rewrite the R 'constants' for all library apks. - SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers( - false, false); - final int N = packageIdentifiers.size(); - for (int i = 0; i < N; i++) { - final int id = packageIdentifiers.keyAt(i); - if (id == 0x01 || id == 0x7f) { - continue; - } - - rewriteRValues(getClassLoader(), packageIdentifiers.valueAt(i), id); - } - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); return app; diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 3c4e861d55f8..1af275fedd74 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1004,6 +1004,31 @@ public class Notification implements Parcelable */ public static final String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory"; + + /** + * {@link #extras} key: this is a remote input history which can include media messages + * in addition to text, as supplied to + * {@link Builder#setRemoteInputHistory(RemoteInputHistoryItem[])} or + * {@link Builder#setRemoteInputHistory(CharSequence[])}. + * + * SystemUI can populate this through + * {@link Builder#setRemoteInputHistory(RemoteInputHistoryItem[])} with the most recent inputs + * that have been sent through a {@link RemoteInput} of this Notification. These items can + * represent either media content (specified by a URI and a MIME type) or a text message + * (described by a CharSequence). + * + * To maintain compatibility, this can also be set by apps with + * {@link Builder#setRemoteInputHistory(CharSequence[])}, which will create a + * {@link RemoteInputHistoryItem} for each of the provided text-only messages. + * + * The extra with this key is of type {@link RemoteInputHistoryItem[]} and contains the most + * recent entry at the 0 index, the second most recent at the 1 index, etc. + * + * @see Builder#setRemoteInputHistory(RemoteInputHistoryItem[]) + * @hide + */ + public static final String EXTRA_REMOTE_INPUT_HISTORY_ITEMS = "android.remoteInputHistoryItems"; + /** * {@link #extras} key: boolean as supplied to * {@link Builder#setShowRemoteInputSpinner(boolean)}. @@ -3833,12 +3858,37 @@ public class Notification implements Parcelable if (text == null) { mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, null); } else { - final int N = Math.min(MAX_REPLY_HISTORY, text.length); - CharSequence[] safe = new CharSequence[N]; - for (int i = 0; i < N; i++) { + final int itemCount = Math.min(MAX_REPLY_HISTORY, text.length); + CharSequence[] safe = new CharSequence[itemCount]; + RemoteInputHistoryItem[] items = new RemoteInputHistoryItem[itemCount]; + for (int i = 0; i < itemCount; i++) { safe[i] = safeCharSequence(text[i]); + items[i] = new RemoteInputHistoryItem(text[i]); } mN.extras.putCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY, safe); + + // Also add these messages as structured history items. + mN.extras.putParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS, items); + } + return this; + } + + /** + * Set the remote input history, with support for embedding URIs and mime types for + * images and other media. + * @hide + */ + @NonNull + public Builder setRemoteInputHistory(RemoteInputHistoryItem[] items) { + if (items == null) { + mN.extras.putParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS, null); + } else { + final int itemCount = Math.min(MAX_REPLY_HISTORY, items.length); + RemoteInputHistoryItem[] history = new RemoteInputHistoryItem[itemCount]; + for (int i = 0; i < itemCount; i++) { + history[i] = items[i]; + } + mN.extras.putParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS, history); } return this; } @@ -5246,16 +5296,17 @@ public class Notification implements Parcelable big.setViewVisibility(R.id.actions_container, View.GONE); } - CharSequence[] replyText = mN.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY); - if (validRemoteInput && replyText != null - && replyText.length > 0 && !TextUtils.isEmpty(replyText[0]) + RemoteInputHistoryItem[] replyText = (RemoteInputHistoryItem[]) + mN.extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); + if (validRemoteInput && replyText != null && replyText.length > 0 + && !TextUtils.isEmpty(replyText[0].getText()) && p.maxRemoteInputHistory > 0) { boolean showSpinner = mN.extras.getBoolean(EXTRA_SHOW_REMOTE_INPUT_SPINNER); big.setViewVisibility(R.id.notification_material_reply_container, View.VISIBLE); big.setViewVisibility(R.id.notification_material_reply_text_1_container, View.VISIBLE); big.setTextViewText(R.id.notification_material_reply_text_1, - processTextSpans(replyText[0])); + processTextSpans(replyText[0].getText())); setTextViewColorSecondary(big, R.id.notification_material_reply_text_1, p); big.setViewVisibility(R.id.notification_material_reply_progress, showSpinner ? View.VISIBLE : View.GONE); @@ -5264,19 +5315,19 @@ public class Notification implements Parcelable ColorStateList.valueOf( isColorized(p) ? getPrimaryTextColor(p) : resolveContrastColor(p))); - if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1]) + if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1].getText()) && p.maxRemoteInputHistory > 1) { big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE); big.setTextViewText(R.id.notification_material_reply_text_2, - processTextSpans(replyText[1])); + processTextSpans(replyText[1].getText())); setTextViewColorSecondary(big, R.id.notification_material_reply_text_2, p); - if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2]) + if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2].getText()) && p.maxRemoteInputHistory > 2) { big.setViewVisibility( R.id.notification_material_reply_text_3, View.VISIBLE); big.setTextViewText(R.id.notification_material_reply_text_3, - processTextSpans(replyText[2])); + processTextSpans(replyText[2].getText())); setTextViewColorSecondary(big, R.id.notification_material_reply_text_3, p); } } @@ -7517,7 +7568,7 @@ public class Notification implements Parcelable @Nullable private final Person mSender; /** True if this message was generated from the extra - * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY} + * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY_ITEMS} */ private final boolean mRemoteInputHistory; @@ -7569,7 +7620,7 @@ public class Notification implements Parcelable * Should be <code>null</code> for messages by the current user, in which case * the platform will insert the user set in {@code MessagingStyle(Person)}. * @param remoteInputHistory True if the messages was generated from the extra - * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY}. + * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY_ITEMS}. * <p> * The person provided should contain an Icon, set with * {@link Person.Builder#setIcon(Icon)} and also have a name provided @@ -7676,7 +7727,7 @@ public class Notification implements Parcelable /** * @return True if the message was generated from - * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY}. + * {@link Notification#EXTRA_REMOTE_INPUT_HISTORY_ITEMS}. * @hide */ public boolean isRemoteInputHistory() { @@ -7906,8 +7957,8 @@ public class Notification implements Parcelable if (mBuilder.mActions.size() > 0) { maxRows--; } - CharSequence[] remoteInputHistory = mBuilder.mN.extras.getCharSequenceArray( - EXTRA_REMOTE_INPUT_HISTORY); + RemoteInputHistoryItem[] remoteInputHistory = (RemoteInputHistoryItem[]) + mBuilder.mN.extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); if (remoteInputHistory != null && remoteInputHistory.length > NUMBER_OF_HISTORY_ALLOWED_UNTIL_REDUCTION) { // Let's remove some messages to make room for the remote input history. diff --git a/core/java/android/app/RemoteInputHistoryItem.java b/core/java/android/app/RemoteInputHistoryItem.java new file mode 100644 index 000000000000..091db3f142ae --- /dev/null +++ b/core/java/android/app/RemoteInputHistoryItem.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Stores historical input from a RemoteInput attached to a Notification. + * + * History items represent either a text message (specified by providing a CharSequence, + * or a media message (specified by providing a URI and a MIME type). Media messages must also + * include text to insert when the image cannot be loaded, ex. when URI read permission has not been + * granted correctly. + * + * @hide + */ +public class RemoteInputHistoryItem implements Parcelable { + private CharSequence mText; + private String mMimeType; + private Uri mUri; + + public RemoteInputHistoryItem(String mimeType, Uri uri, CharSequence backupText) { + this.mMimeType = mimeType; + this.mUri = uri; + this.mText = Notification.safeCharSequence(backupText); + } + + public RemoteInputHistoryItem(CharSequence text) { + this.mText = Notification.safeCharSequence(text); + } + + protected RemoteInputHistoryItem(Parcel in) { + mText = in.readCharSequence(); + mMimeType = in.readStringNoHelper(); + mUri = in.readParcelable(Uri.class.getClassLoader()); + } + + public static final Creator<RemoteInputHistoryItem> CREATOR = + new Creator<RemoteInputHistoryItem>() { + @Override + public RemoteInputHistoryItem createFromParcel(Parcel in) { + return new RemoteInputHistoryItem(in); + } + + @Override + public RemoteInputHistoryItem[] newArray(int size) { + return new RemoteInputHistoryItem[size]; + } + }; + + public CharSequence getText() { + return mText; + } + + public String getMimeType() { + return mMimeType; + } + + public Uri getUri() { + return mUri; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeCharSequence(mText); + dest.writeStringNoHelper(mMimeType); + dest.writeParcelable(mUri, flags); + } +} diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index c1e535643ddf..7f698653bef7 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; @@ -376,6 +378,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, @@ -1262,6 +1276,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 diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index a88500920e97..6f1effd228e3 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -1848,6 +1848,24 @@ public class WallpaperManager { } /** + * Set the current zoom out level of the wallpaper + * @param zoom from 0 to 1 (inclusive) where 1 means fully zoomed out, 0 means fully zoomed in + * + * @hide + */ + public void setWallpaperZoomOut(float zoom) { + if (zoom < 0 || zoom > 1f) { + throw new IllegalArgumentException("zoom must be between 0 and one: " + zoom); + } + try { + sGlobals.mService.setWallpaperZoomOut(zoom, mContext.getOpPackageName(), + mContext.getDisplayId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Returns whether wallpapers are supported for the calling user. If this function returns * {@code false}, any attempts to changing the wallpaper will have no effect, * and any attempt to obtain of the wallpaper will return {@code null}. diff --git a/core/java/android/app/WindowContext.java b/core/java/android/app/WindowContext.java new file mode 100644 index 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/DevicePolicyKeyguardService.java b/core/java/android/app/admin/DevicePolicyKeyguardService.java new file mode 100644 index 000000000000..c2a76c5014c0 --- /dev/null +++ b/core/java/android/app/admin/DevicePolicyKeyguardService.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.admin; + +import android.annotation.Nullable; +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; +import android.view.SurfaceControl; + +/** + * Client interface for providing the SystemUI with secondary lockscreen information. + * + * <p>An implementation must be provided by the device admin app when + * {@link DevicePolicyManager#setSecondaryLockscreenEnabled} is set to true and the service must be + * declared in the manifest as handling the action + * {@link DevicePolicyManager#ACTION_BIND_SECONDARY_LOCKSCREEN_SERVICE}, otherwise the keyguard + * will fail to bind to the service and continue to unlock. + * + * @see DevicePolicyManager#setSecondaryLockscreenEnabled + */ +public class DevicePolicyKeyguardService extends Service { + private static final String TAG = "DevicePolicyKeyguardService"; + private IKeyguardCallback mCallback; + + private final IKeyguardClient mClient = new IKeyguardClient.Stub() { + @Override + public void onSurfaceReady(@Nullable IBinder hostInputToken, IKeyguardCallback callback) { + mCallback = callback; + SurfaceControl surfaceControl = + DevicePolicyKeyguardService.this.onSurfaceReady(hostInputToken); + + if (mCallback != null) { + try { + mCallback.onSurfaceControlCreated(surfaceControl); + } catch (RemoteException e) { + Log.e(TAG, "Failed to return created SurfaceControl", e); + } + } + } + }; + + @Override + @Nullable + public final IBinder onBind(@Nullable Intent intent) { + return mClient.asBinder(); + } + + /** + * Called by keyguard once the host surface for the secondary lockscreen is ready to display + * remote content. + * @return the {@link SurfaceControl} for the Surface the secondary lockscreen content is + * attached to. + */ + @Nullable + public SurfaceControl onSurfaceReady(@Nullable IBinder hostInputToken) { + return null; + } + + /** + * Signals to keyguard that the secondary lock screen is ready to be dismissed. + */ + @Nullable + public void dismiss() { + try { + mCallback.onDismiss(); + } catch (RemoteException e) { + Log.e(TAG, "onDismiss failed", e); + } + } +} diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index a35a89948f25..62557dca9e7e 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2384,6 +2384,35 @@ public class DevicePolicyManager { public static final int MAX_PASSWORD_LENGTH = 16; /** + * Service Action: Service implemented by a device owner or profile owner to provide a + * secondary lockscreen. + */ + public static final String ACTION_BIND_SECONDARY_LOCKSCREEN_SERVICE = + "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. @@ -4203,7 +4232,18 @@ public class DevicePolicyManager { * device by first calling {@link #resetPassword} to set the password and then lock the device. * <p> * This method can be called on the {@link DevicePolicyManager} instance returned by - * {@link #getParentProfileInstance(ComponentName)} in order to lock the parent profile. + * {@link #getParentProfileInstance(ComponentName)} in order to lock the parent profile as + * well as the managed profile. + * <p> + * NOTE: In order to lock the parent profile and evict the encryption key of the managed + * profile, {@link #lockNow()} must be called twice: First, {@link #lockNow()} should be called + * on the {@link DevicePolicyManager} instance returned by + * {@link #getParentProfileInstance(ComponentName)}, then {@link #lockNow(int)} should be + * called on the {@link DevicePolicyManager} instance associated with the managed profile, + * with the {@link #FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY} flag. + * Calling the method twice in this order ensures that all users are locked and does not + * stop the device admin on the managed profile from issuing a second call to lock its own + * profile. * * @param flags May be 0 or {@link #FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY}. * @throws SecurityException if the calling application does not own an active administrator @@ -4559,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 @@ -4653,6 +4705,9 @@ public class DevicePolicyManager { | DevicePolicyManager.KEYGUARD_DISABLE_BIOMETRICS; /** + * @deprecated This method does not actually modify the storage encryption of the device. + * It has never affected the encryption status of a device. + * * Called by an application that is administering the device to request that the storage system * be encrypted. Does nothing if the caller is on a secondary user or a managed profile. * <p> @@ -4686,6 +4741,7 @@ public class DevicePolicyManager { * @throws SecurityException if {@code admin} is not an active administrator or does not use * {@link DeviceAdminInfo#USES_ENCRYPTED_STORAGE} */ + @Deprecated public int setStorageEncryption(@NonNull ComponentName admin, boolean encrypt) { throwIfParentInstance("setStorageEncryption"); if (mService != null) { @@ -4699,6 +4755,10 @@ public class DevicePolicyManager { } /** + * @deprecated This method only returns the value set by {@link #setStorageEncryption}. + * It does not actually reflect the storage encryption status. + * Use {@link #getStorageEncryptionStatus} for that. + * * Called by an application that is administering the device to * determine the requested setting for secure storage. * @@ -4707,6 +4767,7 @@ public class DevicePolicyManager { * administrators. * @return true if the admin(s) are requesting encryption, false if not. */ + @Deprecated public boolean getStorageEncryption(@Nullable ComponentName admin) { throwIfParentInstance("getStorageEncryption"); if (mService != null) { @@ -8199,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. @@ -8206,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(); } @@ -8228,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(); } @@ -8393,6 +8472,52 @@ public class DevicePolicyManager { } /** + * Called by device owner or profile owner to set whether a secondary lockscreen needs to be + * shown. + * + * <p>The secondary lockscreen will by displayed after the primary keyguard security screen + * requirements are met. To provide the lockscreen content the DO/PO will need to provide a + * service handling the {@link #ACTION_BIND_SECONDARY_LOCKSCREEN_SERVICE} intent action, + * extending the {@link DevicePolicyKeyguardService} class. + * + * <p>Relevant interactions on the secondary lockscreen should be communicated back to the + * keyguard via {@link IKeyguardCallback}, such as when the screen is ready to be dismissed. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param enabled Whether or not the lockscreen needs to be shown. + * @throws SecurityException if {@code admin} is not a device or profile owner. + * @see #isSecondaryLockscreenEnabled + **/ + public void setSecondaryLockscreenEnabled(@NonNull ComponentName admin, boolean enabled) { + throwIfParentInstance("setSecondaryLockscreenEnabled"); + if (mService != null) { + try { + mService.setSecondaryLockscreenEnabled(admin, enabled); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Returns whether the secondary lock screen needs to be shown. + * @see #setSecondaryLockscreenEnabled + * @hide + */ + @SystemApi + public boolean isSecondaryLockscreenEnabled(int userId) { + throwIfParentInstance("isSecondaryLockscreenEnabled"); + if (mService != null) { + try { + return mService.isSecondaryLockscreenEnabled(userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return false; + } + + /** * Sets which packages may enter lock task mode. * <p> * Any packages that share uid with an allowed package will also be allowed to activate lock @@ -9261,6 +9386,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. @@ -11337,6 +11472,10 @@ public class DevicePolicyManager { * * <p>Previous calls are overridden by each subsequent call to this method. * + * <p>When previously-set cross-profile packages are missing from {@code packageNames}, the + * app-op for {@code INTERACT_ACROSS_PROFILES} will be reset for those packages. This will not + * occur for packages that are whitelisted by the OEM. + * * @param admin the {@link DeviceAdminReceiver} this request is associated with * @param packageNames the new cross-profile package names */ @@ -11588,4 +11727,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 a2c0856717f5..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); @@ -252,6 +252,9 @@ interface IDevicePolicyManager { String[] getAccountTypesWithManagementDisabled(); String[] getAccountTypesWithManagementDisabledAsUser(int userId); + void setSecondaryLockscreenEnabled(in ComponentName who, boolean enabled); + boolean isSecondaryLockscreenEnabled(int userId); + void setLockTaskPackages(in ComponentName who, in String[] packages); String[] getLockTaskPackages(in ComponentName who); boolean isLockTaskPermitted(in String pkg); @@ -467,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/app/admin/IKeyguardCallback.aidl b/core/java/android/app/admin/IKeyguardCallback.aidl new file mode 100644 index 000000000000..81e7d4dee902 --- /dev/null +++ b/core/java/android/app/admin/IKeyguardCallback.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.admin; + +import android.view.SurfaceControl; + +/** + * Internal IPC interface for informing the keyguard of events on the secondary lockscreen. + * @hide + */ +interface IKeyguardCallback { + oneway void onSurfaceControlCreated(in SurfaceControl remoteSurfaceControl); + oneway void onDismiss(); +} diff --git a/core/java/android/app/admin/IKeyguardClient.aidl b/core/java/android/app/admin/IKeyguardClient.aidl new file mode 100644 index 000000000000..4bfd990cf717 --- /dev/null +++ b/core/java/android/app/admin/IKeyguardClient.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.admin; + +import android.app.admin.IKeyguardCallback; + +/** + * Internal IPC interface for a service to provide the SystemUI with secondary lockscreen + * information. + * @hide + */ +interface IKeyguardClient { + oneway void onSurfaceReady(in IBinder hostInputToken, in IKeyguardCallback keyguardCallback); +} diff --git a/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java b/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java index 1e6ab4136187..bea1bd6e70d6 100644 --- a/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java +++ b/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.UserIdInt; +import android.graphics.Bitmap; import android.os.Binder; import android.os.Bundle; import android.os.RemoteException; @@ -77,6 +78,28 @@ public final class ContentSuggestionsManager { } /** + * Hints to the system that a new context image using the provided bitmap should be sent to + * the system content suggestions service. + * + * @param bitmap the new context image + * @param imageContextRequestExtras sent with request to provide implementation specific + * extra information. + */ + public void provideContextImage( + @NonNull Bitmap bitmap, @NonNull Bundle imageContextRequestExtras) { + if (mService == null) { + Log.e(TAG, "provideContextImage called, but no ContentSuggestionsManager configured"); + return; + } + + try { + mService.provideContextBitmap(mUser, bitmap, imageContextRequestExtras); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** * Hints to the system that a new context image for the provided task should be sent to the * system content suggestions service. * diff --git a/core/java/android/app/contentsuggestions/IContentSuggestionsManager.aidl b/core/java/android/app/contentsuggestions/IContentSuggestionsManager.aidl index b18a758ff69e..8e6338babf22 100644 --- a/core/java/android/app/contentsuggestions/IContentSuggestionsManager.aidl +++ b/core/java/android/app/contentsuggestions/IContentSuggestionsManager.aidl @@ -20,6 +20,7 @@ import android.app.contentsuggestions.IClassificationsCallback; import android.app.contentsuggestions.ISelectionsCallback; import android.app.contentsuggestions.ClassificationsRequest; import android.app.contentsuggestions.SelectionsRequest; +import android.graphics.Bitmap; import android.os.Bundle; import android.os.UserHandle; import com.android.internal.os.IResultReceiver; @@ -30,6 +31,10 @@ oneway interface IContentSuggestionsManager { int userId, int taskId, in Bundle imageContextRequestExtras); + void provideContextBitmap( + int userId, + in Bitmap bitmap, + in Bundle imageContextRequestExtras); void suggestContentSelections( int userId, in SelectionsRequest request, diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index a60e591dd0e6..5668944dfd4e 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -142,7 +142,7 @@ public final class UsageStatsManager { /** * The app has not be used for several days and/or is unlikely to be used for several days. - * Apps in this bucket will have the most restrictions, including network restrictions, except + * Apps in this bucket will have more restrictions, including network restrictions, except * during certain short periods (at a minimum, once a day) when they are allowed to execute * jobs, access the network, etc. * @see #getAppStandbyBucket() @@ -150,6 +150,15 @@ public final class UsageStatsManager { public static final int STANDBY_BUCKET_RARE = 40; /** + * The app has not be used for several days, is unlikely to be used for several days, and has + * been misbehaving in some manner. + * Apps in this bucket will have the most restrictions, including network restrictions and + * additional restrictions on jobs. + * @see #getAppStandbyBucket() + */ + public static final int STANDBY_BUCKET_RESTRICTED = 45; + + /** * The app has never been used. * {@hide} */ @@ -278,6 +287,26 @@ public final class UsageStatsManager { * @hide */ public static final int REASON_SUB_PREDICTED_RESTORED = 0x0001; + /** + * The reason for restricting the app is unknown or undefined. + * @hide + */ + public static final int REASON_SUB_RESTRICT_UNDEFINED = 0x0000; + /** + * The app was unnecessarily using system resources (battery, memory, etc) in the background. + * @hide + */ + public static final int REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE = 0x0001; + /** + * The app was deemed to be intentionally abusive. + * @hide + */ + public static final int REASON_SUB_RESTRICT_ABUSE = 0x0002; + /** + * The app was displaying buggy behavior. + * @hide + */ + public static final int REASON_SUB_RESTRICT_BUGGY = 0x0003; /** @hide */ @@ -287,6 +316,7 @@ public final class UsageStatsManager { STANDBY_BUCKET_WORKING_SET, STANDBY_BUCKET_FREQUENT, STANDBY_BUCKET_RARE, + STANDBY_BUCKET_RESTRICTED, STANDBY_BUCKET_NEVER, }) @Retention(RetentionPolicy.SOURCE) @@ -598,7 +628,7 @@ public final class UsageStatsManager { * state of the app based on app usage patterns. Standby buckets determine how much an app will * be restricted from running background tasks such as jobs and alarms. * <p>Restrictions increase progressively from {@link #STANDBY_BUCKET_ACTIVE} to - * {@link #STANDBY_BUCKET_RARE}, with {@link #STANDBY_BUCKET_ACTIVE} being the least + * {@link #STANDBY_BUCKET_RESTRICTED}, with {@link #STANDBY_BUCKET_ACTIVE} being the least * restrictive. The battery level of the device might also affect the restrictions. * <p>Apps in buckets ≤ {@link #STANDBY_BUCKET_ACTIVE} have no standby restrictions imposed. * Apps in buckets > {@link #STANDBY_BUCKET_FREQUENT} may have network access restricted when @@ -642,7 +672,8 @@ public final class UsageStatsManager { /** * {@hide} * Changes an app's standby bucket to the provided value. The caller can only set the standby - * bucket for a different app than itself. + * bucket for a different app than itself. The caller will not be able to change an app's + * standby bucket if that app is in the {@link #STANDBY_BUCKET_RESTRICTED} bucket. * @param packageName the package name of the app to set the bucket for. A SecurityException * will be thrown if the package name is that of the caller. * @param bucket the standby bucket to set it to, which should be one of STANDBY_BUCKET_*. @@ -688,7 +719,8 @@ public final class UsageStatsManager { /** * {@hide} * Changes the app standby bucket for multiple apps at once. The Map is keyed by the package - * name and the value is one of STANDBY_BUCKET_*. + * name and the value is one of STANDBY_BUCKET_*. The caller will not be able to change an + * app's standby bucket if that app is in the {@link #STANDBY_BUCKET_RESTRICTED} bucket. * @param appBuckets a map of package name to bucket value. */ @SystemApi @@ -1027,6 +1059,20 @@ public final class UsageStatsManager { break; case REASON_MAIN_FORCED_BY_SYSTEM: sb.append("s"); + switch (standbyReason & REASON_SUB_MASK) { + case REASON_SUB_RESTRICT_ABUSE: + sb.append("-ra"); + break; + case REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE: + sb.append("-rbru"); + break; + case REASON_SUB_RESTRICT_BUGGY: + sb.append("-rb"); + break; + case REASON_SUB_RESTRICT_UNDEFINED: + sb.append("-r"); + break; + } break; case REASON_MAIN_FORCED_BY_USER: sb.append("f"); diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index cb1f05573d93..e7513544a886 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1222,6 +1222,7 @@ public final class BluetoothAdapter { if (mService != null) { return mService.factoryReset(); } + Log.e(TAG, "factoryReset(): IBluetooth Service is null"); SystemProperties.set("persist.bluetooth.factoryreset", "true"); } catch (RemoteException e) { Log.e(TAG, "", e); @@ -1239,7 +1240,7 @@ public final class BluetoothAdapter { */ @UnsupportedAppUsage @RequiresPermission(Manifest.permission.BLUETOOTH) - public @NonNull ParcelUuid[] getUuids() { + public @Nullable ParcelUuid[] getUuids() { if (getState() != STATE_ON) { return null; } diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java index 024bb06098ab..ec63fd058b16 100644 --- a/core/java/android/bluetooth/BluetoothPan.java +++ b/core/java/android/bluetooth/BluetoothPan.java @@ -30,6 +30,7 @@ import android.content.Context; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.util.CloseGuard; import android.util.Log; import java.lang.annotation.Retention; @@ -50,10 +51,11 @@ import java.util.List; * @hide */ @SystemApi -public final class BluetoothPan implements BluetoothProfile { +public final class BluetoothPan implements BluetoothProfile, AutoCloseable { private static final String TAG = "BluetoothPan"; private static final boolean DBG = true; private static final boolean VDBG = false; + private CloseGuard mCloseGuard; /** * Intent used to broadcast the change in connection state of the Pan @@ -166,10 +168,15 @@ public final class BluetoothPan implements BluetoothProfile { mAdapter = BluetoothAdapter.getDefaultAdapter(); mContext = context; mProfileConnector.connect(context, listener); + mCloseGuard = new CloseGuard(); + mCloseGuard.open("close"); } - @UnsupportedAppUsage - /*package*/ void close() { + /** + * Closes the connection to the service and unregisters callbacks + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public void close() { if (VDBG) log("close()"); mProfileConnector.disconnect(); } @@ -178,8 +185,11 @@ public final class BluetoothPan implements BluetoothProfile { return mProfileConnector.getService(); } - + @RequiresPermission(Manifest.permission.BLUETOOTH) protected void finalize() { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } close(); } @@ -316,6 +326,7 @@ public final class BluetoothPan implements BluetoothProfile { * @hide */ @Override + @RequiresPermission(Manifest.permission.BLUETOOTH) public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); final IBluetoothPan service = getService(); @@ -335,6 +346,7 @@ public final class BluetoothPan implements BluetoothProfile { * {@inheritDoc} */ @Override + @RequiresPermission(Manifest.permission.BLUETOOTH) public int getConnectionState(@Nullable BluetoothDevice device) { if (VDBG) log("getState(" + device + ")"); final IBluetoothPan service = getService(); @@ -355,6 +367,7 @@ public final class BluetoothPan implements BluetoothProfile { * * @param value is whether to enable or disable bluetooth tethering */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void setBluetoothTethering(boolean value) { String pkgName = mContext.getOpPackageName(); if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName); @@ -373,6 +386,7 @@ public final class BluetoothPan implements BluetoothProfile { * * @return true if tethering is on, false if not or some error occurred */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean isTetheringOn() { if (VDBG) log("isTetheringOn()"); final IBluetoothPan service = getService(); diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index 638e6de06be7..7538df8bbe5f 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -146,7 +146,7 @@ public interface BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @SystemApi int A2DP_SINK = 11; /** @@ -154,7 +154,7 @@ public interface BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @SystemApi int AVRCP_CONTROLLER = 12; /** @@ -169,6 +169,7 @@ public interface BluetoothProfile { * * @hide */ + @SystemApi int HEADSET_CLIENT = 16; /** @@ -176,6 +177,7 @@ public interface BluetoothProfile { * * @hide */ + @SystemApi int PBAP_CLIENT = 17; /** 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/ApexContext.java b/core/java/android/content/ApexContext.java new file mode 100644 index 000000000000..fe5cedca4654 --- /dev/null +++ b/core/java/android/content/ApexContext.java @@ -0,0 +1,98 @@ +/* + * 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; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Environment; +import android.os.UserHandle; + +import java.io.File; +import java.util.Objects; + +/** + * Provides information about the environment for a particular APEX. + * + * @hide + */ +@SystemApi +public class ApexContext { + + private static final String APEX_DATA = "apexdata"; + + /** + * Returns an ApexContext instance for the APEX with the provided {@code apexModuleName}. + * + * <p>To preserve the safety and integrity of APEX modules, you must only obtain the ApexContext + * for your specific APEX, and you <em>must never</em> attempt to obtain an ApexContext for + * another APEX. Any coordination between APEXs must be performed through well-defined + * interfaces; attempting to directly read or write raw files belonging to another APEX will + * violate the hermetic storage requirements placed upon each module. + */ + @NonNull + public static ApexContext getApexContext(@NonNull String apexModuleName) { + Objects.requireNonNull(apexModuleName, "apexModuleName cannot be null"); + //TODO(b/141148175): Check that apexModuleName is an actual APEX name + return new ApexContext(apexModuleName); + } + + private final String mApexModuleName; + + private ApexContext(String apexModuleName) { + mApexModuleName = apexModuleName; + } + + /** + * Returns the data directory for the APEX in device-encrypted, non-user-specific storage. + * + * <p>This directory is automatically created by the system for installed APEXes, and its + * contents will be rolled back if the APEX is rolled back. + */ + @NonNull + public File getDeviceProtectedDataDir() { + return Environment.buildPath( + Environment.getDataMiscDirectory(), APEX_DATA, mApexModuleName); + } + + /** + * Returns the data directory for the APEX in device-encrypted, user-specific storage for the + * specified {@code user}. + * + * <p>This directory is automatically created by the system for each user and for each installed + * APEX, and its contents will be rolled back if the APEX is rolled back. + */ + @NonNull + public File getDeviceProtectedDataDirForUser(@NonNull UserHandle user) { + return Environment.buildPath( + Environment.getDataMiscDeDirectory(user.getIdentifier()), APEX_DATA, + mApexModuleName); + } + + /** + * Returns the data directory for the APEX in credential-encrypted, user-specific storage for + * the specified {@code user}. + * + * <p>This directory is automatically created by the system for each user and for each installed + * APEX, and its contents will be rolled back if the APEX is rolled back. + */ + @NonNull + public File getCredentialProtectedDataDirForUser(@NonNull UserHandle user) { + return Environment.buildPath( + Environment.getDataMiscCeDirectory(user.getIdentifier()), APEX_DATA, + mApexModuleName); + } +} diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 85826fd4e669..c271e3c66adc 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -28,6 +28,7 @@ import static android.os.Trace.TRACE_TAG_DATABASE; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.app.AppOpsManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.PackageManager; @@ -2526,6 +2527,16 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } /** + * Returns the user associated with the given URI. + * + * @hide + */ + @TestApi + public @NonNull static UserHandle getUserHandleFromUri(@NonNull Uri uri) { + return UserHandle.of(getUserIdFromUri(uri, Process.myUserHandle().getIdentifier())); + } + + /** * Removes userId part from authority string. Expects format: * userId@some.authority * If there is no userId in the authority, it symply returns the argument diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 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/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index 5aa9c9bebc2c..de153d00b48b 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -34,8 +34,10 @@ import android.provider.Settings; import com.android.internal.R; import com.android.internal.util.UserIcons; +import java.util.Collection; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; /** * Class for handling cross profile operations. Apps can use this class to interact with its @@ -94,6 +96,32 @@ public class CrossProfileApps { } /** + * Starts the specified activity of the caller package in the specified profile. + * + * <p>The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} + * permission and both the caller and target user profiles must be in the same profile group. + * + * @param intent The intent to launch. A component in the caller package must be specified. + * @param targetUser The {@link UserHandle} of the profile; must be one of the users returned by + * {@link #getTargetUserProfiles()} if different to the calling user, otherwise a + * {@link SecurityException} will be thrown. + */ + @RequiresPermission(anyOf = { + android.Manifest.permission.INTERACT_ACROSS_PROFILES, + android.Manifest.permission.INTERACT_ACROSS_USERS}) + public void startActivity(@NonNull Intent intent, @NonNull UserHandle targetUser) { + try { + mService.startActivityAsUserByIntent( + mContext.getIApplicationThread(), + mContext.getPackageName(), + intent, + targetUser.getIdentifier()); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** * Starts the specified activity of the caller package in the specified profile. Unlike * {@link #startMainActivity}, this can start any activity of the caller package, not just * the main activity. @@ -272,19 +300,24 @@ public class CrossProfileApps { * configurable by users in Settings. This configures it for the profile group of the calling * package. * - * <p>Before calling, check {@link #canRequestInteractAcrossProfiles()} and do not call if it is - * {@code false}. If presenting a user interface, do not allow the user to configure the app-op - * in that case. + * <p>Before calling, check {@link #canConfigureInteractAcrossProfiles(String)} and do not call + * if it is {@code false}. If presenting a user interface, do not allow the user to configure + * the app-op in that case. * * <p>The underlying app-op {@link android.app.AppOpsManager#OP_INTERACT_ACROSS_PROFILES} should * never be set directly. This method ensures that the app-op is kept in sync for the app across * each user in the profile group and that those apps are sent a broadcast when their ability to * interact across profiles changes. * - * <p>This method should be used whenever an app's ability to interact across profiles changes, - * as defined by the return value of {@link #canInteractAcrossProfiles()}. This includes user - * consent changes in Settings or during provisioning, plus changes to the admin or OEM consent - * whitelists that make the current app-op value invalid. + * <p>This method should be used directly whenever a user's action results in a change in an + * app's ability to interact across profiles, as defined by the return value of {@link + * #canInteractAcrossProfiles()}. This includes user consent changes in Settings or during + * provisioning. + * + * <p>If other changes could have affected the app's ability to interact across profiles, as + * defined by the return value of {@link #canInteractAcrossProfiles()}, such as changes to the + * admin or OEM consent whitelists, then {@link + * #resetInteractAcrossProfilesAppOpsIfInvalid(List)} should be used. * * @hide */ @@ -299,6 +332,58 @@ public class CrossProfileApps { } } + /** + * Returns whether the given package can have its ability to interact across profiles configured + * by the user. This means that every other condition to interact across profiles has been set. + * + * <p>This differs from {@link #canRequestInteractAcrossProfiles()} since it will not return + * {@code false} simply when the target profile is disabled. + * + * @hide + */ + public boolean canConfigureInteractAcrossProfiles(@NonNull String packageName) { + try { + return mService.canConfigureInteractAcrossProfiles(packageName); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** + * For each of the packages defined in {@code previousCrossProfilePackages} but not included in + * {@code newCrossProfilePackages}, resets the app-op for {@link android.Manifest.permission + * #INTERACT_ACROSS_PROFILES} back to its default value if it can no longer be configured by + * users in Settings, as defined by {@link #canConfigureInteractAcrossProfiles(String)}. + * + * <p>This method should be used whenever an app's ability to interact across profiles could + * have changed as a result of non-user actions, such as changes to admin or OEM consent + * whitelists. + * + * @hide + */ + @RequiresPermission( + allOf={android.Manifest.permission.MANAGE_APP_OPS_MODES, + android.Manifest.permission.INTERACT_ACROSS_USERS}) + public void resetInteractAcrossProfilesAppOps( + @NonNull Collection<String> previousCrossProfilePackages, + @NonNull Set<String> newCrossProfilePackages) { + if (previousCrossProfilePackages.isEmpty()) { + return; + } + final List<String> unsetCrossProfilePackages = + previousCrossProfilePackages.stream() + .filter(packageName -> !newCrossProfilePackages.contains(packageName)) + .collect(Collectors.toList()); + if (unsetCrossProfilePackages.isEmpty()) { + return; + } + try { + mService.resetInteractAcrossProfilesAppOps(unsetCrossProfilePackages); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + private void verifyCanAccessUser(UserHandle userHandle) { if (!getTargetUserProfiles().contains(userHandle)) { throw new SecurityException("Not allowed to access " + userHandle); diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl index 694b1a3c4e73..a69b9881aa01 100644 --- a/core/java/android/content/pm/ICrossProfileApps.aidl +++ b/core/java/android/content/pm/ICrossProfileApps.aidl @@ -29,8 +29,12 @@ import android.os.UserHandle; interface ICrossProfileApps { void startActivityAsUser(in IApplicationThread caller, in String callingPackage, in ComponentName component, int userId, boolean launchMainActivity); + void startActivityAsUserByIntent(in IApplicationThread caller, in String callingPackage, + in Intent intent, int userId); List<UserHandle> getTargetUserProfiles(in String callingPackage); boolean canInteractAcrossProfiles(in String callingPackage); boolean canRequestInteractAcrossProfiles(in String callingPackage); void setInteractAcrossProfilesAppOp(in String packageName, int newMode); + boolean canConfigureInteractAcrossProfiles(in String packageName); + void resetInteractAcrossProfilesAppOps(in List<String> packageNames); }
\ No newline at end of file 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/ModuleInfo.java b/core/java/android/content/pm/ModuleInfo.java index d930c92d66ed..a6db662bcc43 100644 --- a/core/java/android/content/pm/ModuleInfo.java +++ b/core/java/android/content/pm/ModuleInfo.java @@ -37,6 +37,12 @@ public final class ModuleInfo implements Parcelable { /** The package name of this module. */ private String mPackageName; + /** + * The name of the APEX this module is distributed as, or null if it is not distributed via + * APEX. + */ + @Nullable private String mApexModuleName; + /** Whether or not this module is hidden from the user. */ private boolean mHidden; @@ -54,6 +60,7 @@ public final class ModuleInfo implements Parcelable { mName = orig.mName; mPackageName = orig.mPackageName; mHidden = orig.mHidden; + mApexModuleName = orig.mApexModuleName; } /** @hide Sets the public name of this module. */ @@ -89,6 +96,17 @@ public final class ModuleInfo implements Parcelable { return mHidden; } + /** @hide Sets the apex module name. */ + public ModuleInfo setApexModuleName(@Nullable String apexModuleName) { + mApexModuleName = apexModuleName; + return this; + } + + /** @hide Gets the apex module name. */ + public @Nullable String getApexModuleName() { + return mApexModuleName; + } + /** Returns a string representation of this object. */ public String toString() { return "ModuleInfo{" @@ -106,6 +124,7 @@ public final class ModuleInfo implements Parcelable { int hashCode = 0; hashCode = 31 * hashCode + Objects.hashCode(mName); hashCode = 31 * hashCode + Objects.hashCode(mPackageName); + hashCode = 31 * hashCode + Objects.hashCode(mApexModuleName); hashCode = 31 * hashCode + Boolean.hashCode(mHidden); return hashCode; } @@ -118,6 +137,7 @@ public final class ModuleInfo implements Parcelable { final ModuleInfo other = (ModuleInfo) obj; return Objects.equals(mName, other.mName) && Objects.equals(mPackageName, other.mPackageName) + && Objects.equals(mApexModuleName, other.mApexModuleName) && mHidden == other.mHidden; } @@ -126,12 +146,14 @@ public final class ModuleInfo implements Parcelable { dest.writeCharSequence(mName); dest.writeString(mPackageName); dest.writeBoolean(mHidden); + dest.writeString(mApexModuleName); } private ModuleInfo(Parcel source) { mName = source.readCharSequence(); mPackageName = source.readString(); mHidden = source.readBoolean(); + mApexModuleName = source.readString(); } public static final @android.annotation.NonNull Parcelable.Creator<ModuleInfo> CREATOR = 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 925d70cdc855..205617022aa1 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -50,6 +50,7 @@ import android.content.pm.dex.ArtManager; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.Rect; +import android.graphics.drawable.AdaptiveIconDrawable; import android.graphics.drawable.Drawable; import android.net.wifi.WifiManager; import android.os.Build; @@ -229,7 +230,7 @@ public abstract class PackageManager { MATCH_ALL, }) @Retention(RetentionPolicy.SOURCE) - public @interface ModuleInfoFlags {} + public @interface InstalledModulesFlags {} /** @hide */ @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = { @@ -368,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 @@ -581,6 +582,22 @@ public abstract class PackageManager { public static final int ONLY_IF_NO_MATCH_FOUND = 0x00000004; /** @hide */ + @IntDef(flag = true, prefix = { "MODULE_" }, value = { + MODULE_APEX_NAME, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ModuleInfoFlags {} + + /** + * Flag for {@link #getModuleInfo}: allow ModuleInfo to be retrieved using the apex module + * name, rather than the package name. + * + * @hide + */ + @SystemApi + public static final int MODULE_APEX_NAME = 0x00000001; + + /** @hide */ @IntDef(prefix = { "PERMISSION_" }, value = { PERMISSION_GRANTED, PERMISSION_DENIED @@ -1948,6 +1965,13 @@ public abstract class PackageManager { @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_CONSUMER_IR = "android.hardware.consumerir"; + /** + * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device supports a Context Hub. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_CONTEXTHUB = "android.hardware.context_hub"; + /** {@hide} */ @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_CTS = "android.software.cts"; @@ -2179,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 */ @@ -3493,7 +3534,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. */ @@ -3519,7 +3560,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. */ @@ -3540,7 +3581,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 @@ -3787,7 +3828,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. */ @@ -3814,7 +3855,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 @@ -3921,7 +3962,7 @@ public abstract class PackageManager { * there are no installed modules, an empty list is returned. */ @NonNull - public List<ModuleInfo> getInstalledModules(@ModuleInfoFlags int flags) { + public List<ModuleInfo> getInstalledModules(@InstalledModulesFlags int flags) { throw new UnsupportedOperationException( "getInstalledModules not implemented in subclass"); } @@ -3937,7 +3978,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); @@ -3955,7 +3996,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( @@ -3974,7 +4015,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 @@ -4386,14 +4427,17 @@ public abstract class PackageManager { public abstract boolean shouldShowRequestPermissionRationale(@NonNull String permName); /** - * Gets the string that is displayed on the button which corresponds to granting background - * location in settings. The intended use for this is to help apps instruct users how to - * grant a background permission by providing the string that users will see. + * Gets the localized label that corresponds to the option in settings for granting + * background access. + * + * <p>The intended use is for apps to reference this label in its instruction for users to grant + * a background permission. * - * @return the string shown on the button for granting background location + * @return the localized label that corresponds to the settings option for granting + * background access */ @NonNull - public CharSequence getBackgroundPermissionButtonLabel() { + public CharSequence getBackgroundPermissionOptionLabel() { return ""; } @@ -4517,7 +4561,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. @@ -4528,7 +4572,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); @@ -4537,7 +4581,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. @@ -4550,7 +4594,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 @@ -7612,4 +7656,19 @@ public abstract class PackageManager { "sendDeviceCustomizationReadyBroadcast not implemented in subclass"); } + /** + * Returns if the provided drawable represents the default activity icon provided by the system. + * + * PackageManager provides a default icon for any package/activity if the app itself does not + * define one or if the system encountered any error when loading the icon. + * + * @return true if the drawable represents the default activity icon, false otherwise + * @see #getDefaultActivityIcon() + */ + public boolean isDefaultApplicationIcon(@NonNull Drawable drawable) { + int resId = drawable instanceof AdaptiveIconDrawable + ? ((AdaptiveIconDrawable) drawable).getSourceDrawableResId() : Resources.ID_NULL; + return resId == com.android.internal.R.drawable.sym_def_app_icon + || resId == com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon; + } } 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/debug/AdbManager.java b/core/java/android/debug/AdbManager.java index ae3d79490c98..0a76bedcd66e 100644 --- a/core/java/android/debug/AdbManager.java +++ b/core/java/android/debug/AdbManager.java @@ -16,15 +16,17 @@ package android.debug; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; +import android.os.RemoteException; /** - * This class allows the control of ADB-related functions. Currently only ADB over USB is - * supported, and none of the API is public. - * + * This class allows the control of ADB-related functions. * @hide */ +@SystemApi @SystemService(Context.ADB_SERVICE) public class AdbManager { private static final String TAG = "AdbManager"; @@ -39,4 +41,33 @@ public class AdbManager { mContext = context; mService = service; } + + /** + * @return true if the device supports secure ADB over Wi-Fi. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING) + public boolean isAdbWifiSupported() { + try { + return mService.isAdbWifiSupported(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @return true if the device supports secure ADB over Wi-Fi and device pairing by + * QR code. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING) + public boolean isAdbWifiQrSupported() { + try { + return mService.isAdbWifiQrSupported(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/debug/IAdbManager.aidl b/core/java/android/debug/IAdbManager.aidl index 79e0794fd9af..c48fc07791c0 100644 --- a/core/java/android/debug/IAdbManager.aidl +++ b/core/java/android/debug/IAdbManager.aidl @@ -41,4 +41,15 @@ interface IAdbManager { * Clear all public keys installed for secure ADB debugging. */ void clearDebuggingKeys(); + + /** + * Returns true if device supports secure Adb over Wi-Fi. + */ + boolean isAdbWifiSupported(); + + /** + * Returns true if device supports secure Adb over Wi-Fi and device pairing by + * QR code. + */ + boolean isAdbWifiQrSupported(); } diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java index bc7ab476b01b..2e48ce955a90 100644 --- a/core/java/android/hardware/camera2/CameraCaptureSession.java +++ b/core/java/android/hardware/camera2/CameraCaptureSession.java @@ -19,10 +19,13 @@ package android.hardware.camera2; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; +import android.hardware.camera2.CameraOfflineSession; +import android.hardware.camera2.CameraOfflineSession.CameraOfflineSessionCallback; import android.hardware.camera2.params.OutputConfiguration; import android.os.Handler; import android.view.Surface; +import java.util.Collection; import java.util.List; import java.util.concurrent.Executor; @@ -855,6 +858,97 @@ public abstract class CameraCaptureSession implements AutoCloseable { } /** + * Switch the current capture session and a given set of registered camera surfaces + * to offline processing mode. + * + * <p>Offline processing mode and the corresponding {@link CameraOfflineSession} differ from + * a regular online camera capture session in several ways. Successful offline switches will + * close the currently active camera capture session. Camera clients are also allowed + * to call {@link CameraDevice#close} while offline processing of selected capture + * requests is still in progress. Such side effects free device close is only possible + * when the offline session moves to the ready state. Once this happens, closing the camera + * device will not affect the pending offline requests and they must complete as normal.</p> + * + * <p>Offline processing mode switches may need several hundred milliseconds to complete + * as the underlying camera implementation must abort all currently active repeating requests + * as well as all other pending requests not specified by the client. Additionally the switch + * will be blocked until all requests that continue processing within the offline session + * acquire their initial input frame from camera sensor. The call to {@link #switchToOffline} + * itself is not blocking and will only trigger the offline switch sequence. Clients will + * be notified via {@link CameraOfflineSessionCallback#onReady} once the entire sequence is + * complete.</p> + * + * <p>Please note that after a successful call to this method the currently active capture + * session will no longer be valid and clients will begin receiving capture + * callbacks with a corresponding {@link CameraOfflineSession} passed as a session + * argument.</p> + * + * @param offlineSurfaces Client-specified collection of input/output camera registered surfaces + * that need to be switched to offline mode along with their pending + * capture requests. Do note that not all camera registered + * surfaces can be switched to offline mode. Offline processing + * support for individual surfaces can be queried using + * {@link #supportsOfflineProcessing}. Additionally offline mode + * switches are not available for shared surfaces + * {@link OutputConfiguration#enableSurfaceSharing} and surfaces + * as part of a surface group. + * + * @param executor The executor which will be used for invoking the offline callback listener. + * + * @param listener The callback object to notify for offline session events. + * + * @return camera offline session which in case of successful offline switch will move in ready + * state after clients receive {@link CameraOfflineSessionCallback#onReady}. In case the + * offline switch was not successful clients will receive respective + * {@link CameraOfflineSessionCallback#onSwitchFailed} notification. + * + * @throws IllegalArgumentException if an attempt was made to pass a {@link Surface} that was + * not registered with this capture session or a shared + * surface {@link OutputConfiguration#enableSurfaceSharing} or + * surface as part of a surface group. The current capture + * session will remain valid and active in case of this + * exception. + * + * @throws CameraAccessException if the camera device is no longer connected or has + * encountered a fatal error. + * + * @see CameraOfflineSession + * @see CameraOfflineSessionCallback + * @see #supportsOfflineProcessing + */ + @Nullable + public CameraOfflineSession switchToOffline(@NonNull Collection<Surface> offlineSurfaces, + @NonNull Executor executor, @NonNull CameraOfflineSessionCallback listener) + throws CameraAccessException { + throw new UnsupportedOperationException("Subclasses must override this method"); + } + + /** + * <p>Query whether a given Surface is able to support offline mode. </p> + * + * <p>Surfaces that support offline mode can be passed as arguments to {@link #switchToOffline}. + * </p> + * + * @param surface An input/output surface that was used to create this session or the result of + * {@link #getInputSurface}. + * + * @return {@code true} if the surface can support offline mode and can be passed as argument to + * {@link #switchToOffline}. {@code false} otherwise. + * + * @throws IllegalArgumentException if an attempt was made to pass a {@link Surface} that was + * not registered with this capture session. + * @throws UnsupportedOperationException if an attempt was made to call this method using + * unsupported camera capture session like + * {@link CameraConstrainedHighSpeedCaptureSession} or + * {@link CameraOfflineSession}. + * + * @see #switchToOffline + */ + public boolean supportsOfflineProcessing(@NonNull Surface surface) { + throw new UnsupportedOperationException("Subclasses must override this method"); + } + + /** * Close this capture session asynchronously. * * <p>Closing a session frees up the target output Surfaces of the session for reuse with either diff --git a/core/java/android/hardware/camera2/CameraOfflineSession.java b/core/java/android/hardware/camera2/CameraOfflineSession.java new file mode 100644 index 000000000000..312559c6c7df --- /dev/null +++ b/core/java/android/hardware/camera2/CameraOfflineSession.java @@ -0,0 +1,159 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.camera2; + +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraDevice; + +import android.annotation.IntDef; +import android.annotation.NonNull; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * A camera capture session that was switched to offline mode via successful call to + * {@link CameraCaptureSession#switchToOffline}. + * + * <p>Offline capture sessions allow clients to select a set of camera registered surfaces that + * support offline mode. After a successful offline mode switch all non-repeating pending requests + * on those surfaces will continue to be processed by the camera stack even if clients close the + * corresponding camera device.<p> + * + * <p>Offline capture session instances will replace the previously active capture session arguments + * in all capture callbacks after {@link CameraCaptureSession#switchToOffline} completes.</p> + * + * <p>Processing of pending offline capture requests will begin only after the offline session + * moves to ready state which will be indicated by the {@link CameraOfflineSessionCallback#onReady} + * callback.</p> + * + * <p>In contrast to a regular {@link CameraCaptureSession} an offline capture session will + * not accept any further capture requests. Besides {@link CameraOfflineSession#close} all + * remaining methods will throw {@link UnsupportedOperationException} and are not supported.</p> + * + * @see CameraCaptureSession#supportsOfflineProcessing + */ +public abstract class CameraOfflineSession extends CameraCaptureSession { + public static abstract class CameraOfflineSessionCallback { + /** + * This method indicates that the offline switch call + * {@link CameraCaptureSession#switchToOffline} was successful. + * + * <p>This callback will be invoked once the offline session moves to the ready state.</p> + * + * <p>Calls to {@link CameraDevice#close} will not have impact on the processing of offline + * requests once the offline session moves in ready state.</p> + * + * @param session the currently ready offline session + * + */ + public abstract void onReady(@NonNull CameraOfflineSession session); + + /** + * This method indicates that the offline switch call + * {@link CameraCaptureSession#switchToOffline} was not able to complete successfully. + * + * <p>The offline switch can fail either due to internal camera error during the switch + * sequence or because the camera implementation was not able to find any pending capture + * requests that can be migrated to offline mode.</p> + * + * <p>Calling {@link CameraOfflineSession#close} is not necessary and clients will not + * receive any further offline session notifications.</p> + * + * @param session the offline session that failed to switch to ready state + */ + public abstract void onSwitchFailed(@NonNull CameraOfflineSession session); + + /** + * This method indicates that all pending offline requests were processed. + * + * <p>This callback will be invoked once the offline session finishes processing + * all of its pending offline capture requests.</p> + * + * @param session the currently ready offline session + * + */ + public abstract void onIdle(@NonNull CameraOfflineSession session); + + /** + * This status code indicates unexpected and fatal internal camera error. + * + * <p>Pending offline requests will be discarded and the respective registered + * capture callbacks may not get triggered.</p> + * + * @see #onError + */ + public static final int STATUS_INTERNAL_ERROR = 0; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"STATUS_"}, value = {STATUS_INTERNAL_ERROR}) + public @interface StatusCode {}; + + /** + * This method is called when the offline session encounters an unexpected error. + * + * <p>This notification will only be invoked for sessions that reached the ready state. + * Clients will need to call {@link CameraOfflineSession#close} to close and release all + * resources. {@link #onClosed} will not be triggered automatically in error scenarios.</p> + * + * @param session the current offline session + * @param status error status + * + */ + public abstract void onError(@NonNull CameraOfflineSession session, @StatusCode int status); + + /** + * This method is called when the offline session is closed. + * + * <p>An offline session will be closed after a call to + * {@link CameraOfflineSession#close}.</p> + * + * <p>In case of failure to switch to offline mode, only {@link #onSwitchFailed} will be + * called and {@link #onClosed} will not be.</p> + * + * <p>In case there was no previous {@link #onIdle} notification any in-progress + * offline capture requests within the offline session will be discarded + * and further result callbacks will not be triggered.</p> + * + * @param session the session returned by {@link CameraCaptureSession#switchToOffline} + * + */ + public abstract void onClosed(@NonNull CameraOfflineSession session); + } + + /** + * Close this offline capture session. + * + * <p>Abort all pending offline requests and close the connection to the offline camera session + * as quickly as possible.</p> + * + * <p>This method can be called only after clients receive + * {@link CameraOfflineSessionCallback#onReady}.</p> + * + * <p>Immediately after this call, besides the final + * {@link CameraOfflineSessionCallback#onClosed} notification, no further callbacks from the + * offline session will be triggered and all remaining offline capture requests will be + * discarded.</p> + * + * <p>Closing a session is idempotent; closing more than once has no effect.</p> + * + * @throws IllegalStateException if the offline sesion is not ready. + */ + @Override + public abstract void close(); +} diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionCore.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionCore.java index 116f0f1724b5..44408c2d274b 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionCore.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionCore.java @@ -61,4 +61,14 @@ public interface CameraCaptureSessionCore { */ boolean isAborting(); + /** + * Close the capture session without draining the pending requests. + * + * <p>This is usually used when switching to offline session mode. Depending + * on the client input, some of the pending requests will be flushed and some + * will remain for further processing. In either case, the regular drain logic + * needs to be skipped.</p> + * + */ + void closeWithoutDraining(); } diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index a4640c1fa519..b6f4bd3c4c28 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -18,6 +18,8 @@ package android.hardware.camera2.impl; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CameraOfflineSession; +import android.hardware.camera2.CameraOfflineSession.CameraOfflineSessionCallback; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.ICameraDeviceUser; import android.hardware.camera2.params.OutputConfiguration; @@ -29,6 +31,7 @@ import android.util.Log; import android.view.Surface; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.concurrent.Executor; @@ -106,7 +109,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession * Use the same handler as the device's StateCallback for all the internal coming events * * This ensures total ordering between CameraDevice.StateCallback and - * CameraDeviceImpl.CaptureCallback events. + * CaptureCallback events. */ mSequenceDrainer = new TaskDrainer<>(mDeviceExecutor, new SequenceDrainListener(), /*name*/"seq"); @@ -136,23 +139,35 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession @Override public void prepare(Surface surface) throws CameraAccessException { - mDeviceImpl.prepare(surface); + synchronized (mDeviceImpl.mInterfaceLock) { + checkNotClosed(); + mDeviceImpl.prepare(surface); + } } @Override public void prepare(int maxCount, Surface surface) throws CameraAccessException { - mDeviceImpl.prepare(maxCount, surface); + synchronized (mDeviceImpl.mInterfaceLock) { + checkNotClosed(); + mDeviceImpl.prepare(maxCount, surface); + } } @Override public void tearDown(Surface surface) throws CameraAccessException { - mDeviceImpl.tearDown(surface); + synchronized (mDeviceImpl.mInterfaceLock) { + checkNotClosed(); + mDeviceImpl.tearDown(surface); + } } @Override public void finalizeOutputConfigurations( List<OutputConfiguration> outputConfigs) throws CameraAccessException { - mDeviceImpl.finalizeOutputConfigs(outputConfigs); + synchronized (mDeviceImpl.mInterfaceLock) { + checkNotClosed(); + mDeviceImpl.finalizeOutputConfigs(outputConfigs); + } } @Override @@ -446,6 +461,24 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession } @Override + public CameraOfflineSession switchToOffline(Collection<Surface> offlineOutputs, + Executor executor, CameraOfflineSessionCallback listener) throws CameraAccessException { + synchronized (mDeviceImpl.mInterfaceLock) { + checkNotClosed(); + } + return mDeviceImpl.switchToOffline(offlineOutputs, executor, listener); + } + + + @Override + public boolean supportsOfflineProcessing(Surface surface) { + synchronized (mDeviceImpl.mInterfaceLock) { + checkNotClosed(); + } + return mDeviceImpl.supportsOfflineProcessing(surface); + } + + @Override public boolean isReprocessable() { return mInput != null; } @@ -501,6 +534,25 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession } @Override + public void closeWithoutDraining() { + synchronized (mDeviceImpl.mInterfaceLock) { + if (mClosed) { + if (DEBUG) Log.v(TAG, mIdString + "close - reentering"); + return; + } + + if (DEBUG) Log.v(TAG, mIdString + "close - first time"); + + mClosed = true; + mStateCallback.onClosed(this); + } + + if (mInput != null) { + mInput.release(); + } + } + + @Override public void close() { synchronized (mDeviceImpl.mInterfaceLock) { if (mClosed) { @@ -571,13 +623,13 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession } /** - * Forward callbacks from - * CameraDeviceImpl.CaptureCallback to the CameraCaptureSession.CaptureCallback. + * Forward callbacks that usually originate from + * CameraDeviceImpl.CameraDeviceCallbacks to the CameraCaptureSession.CaptureCallback. * * <p>When a capture sequence finishes, update the pending checked sequences set.</p> */ @SuppressWarnings("deprecation") - private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxy( + private android.hardware.camera2.impl.CaptureCallback createCaptureCallbackProxy( Handler handler, CaptureCallback callback) { final Executor executor = (callback != null) ? CameraDeviceImpl.checkAndWrapHandler( handler) : null; @@ -585,9 +637,9 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession return createCaptureCallbackProxyWithExecutor(executor, callback); } - private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxyWithExecutor( + private android.hardware.camera2.impl.CaptureCallback createCaptureCallbackProxyWithExecutor( Executor executor, CaptureCallback callback) { - return new CameraDeviceImpl.CaptureCallback() { + return new android.hardware.camera2.impl.CaptureCallback(executor, callback) { @Override public void onCaptureStarted(CameraDevice camera, CaptureRequest request, long timestamp, long frameNumber) { diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java index eb331373e691..0a4298162027 100644 --- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java @@ -20,6 +20,8 @@ import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession; import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CameraOfflineSession; +import android.hardware.camera2.CameraOfflineSession.CameraOfflineSessionCallback; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.params.OutputConfiguration; import android.hardware.camera2.params.StreamConfigurationMap; @@ -283,6 +285,25 @@ public class CameraConstrainedHighSpeedCaptureSessionImpl } @Override + public CameraOfflineSession switchToOffline(Collection<Surface> offlineOutputs, + Executor executor, CameraOfflineSessionCallback listener) throws CameraAccessException { + throw new UnsupportedOperationException("Constrained high speed session doesn't support" + + " this method"); + } + + @Override + public boolean supportsOfflineProcessing(Surface surface) { + throw new UnsupportedOperationException("Constrained high speed session doesn't support" + + " offline mode"); + } + + @Override + public void closeWithoutDraining() { + throw new UnsupportedOperationException("Constrained high speed session doesn't support" + + " this method"); + } + + @Override public void close() { mSessionImpl.close(); } diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 67e7a4c821e7..a385771484fd 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -23,6 +23,7 @@ import android.hardware.ICameraService; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraOfflineSession; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.CaptureRequest; @@ -52,18 +53,17 @@ import android.view.Surface; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.Set; -import java.util.TreeMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.Executor; - +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; /** * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate @@ -103,6 +103,9 @@ public class CameraDeviceImpl extends CameraDevice private final SparseArray<OutputConfiguration> mConfiguredOutputs = new SparseArray<>(); + // Cache all stream IDs capable of supporting offline mode. + private final HashSet<Integer> mOfflineSupport = new HashSet<>(); + private final String mCameraId; private final CameraCharacteristics mCharacteristics; private final int mTotalPartialCount; @@ -127,6 +130,9 @@ public class CameraDeviceImpl extends CameraDevice private final int mAppTargetSdkVersion; + private ExecutorService mOfflineSwitchService; + private CameraOfflineSessionImpl mOfflineSessionImpl; + // Runnables for all state transitions, except error, which needs the // error code argument @@ -470,10 +476,19 @@ public class CameraDeviceImpl extends CameraDevice } } + int offlineStreamIds[]; if (sessionParams != null) { - mRemoteDevice.endConfigure(operatingMode, sessionParams.getNativeCopy()); + offlineStreamIds = mRemoteDevice.endConfigure(operatingMode, + sessionParams.getNativeCopy()); } else { - mRemoteDevice.endConfigure(operatingMode, null); + offlineStreamIds = mRemoteDevice.endConfigure(operatingMode, null); + } + + mOfflineSupport.clear(); + if ((offlineStreamIds != null) && (offlineStreamIds.length > 0)) { + for (int offlineStreamId : offlineStreamIds) { + mOfflineSupport.add(offlineStreamId); + } } success = true; @@ -866,22 +881,29 @@ public class CameraDeviceImpl extends CameraDevice } } - public void switchToOffline(ICameraDeviceCallbacks cbs, Surface[] offlineOutputs) + public CameraOfflineSession switchToOffline( + @NonNull Collection<Surface> offlineOutputs, @NonNull Executor executor, + @NonNull CameraOfflineSession.CameraOfflineSessionCallback listener) throws CameraAccessException { - if ((offlineOutputs == null) || (offlineOutputs.length == 0)) { - throw new IllegalArgumentException("Invalid offline outputs!"); - } - if (cbs == null) { - throw new IllegalArgumentException("Invalid device callbacks!"); + if (offlineOutputs.isEmpty()) { + throw new IllegalArgumentException("Invalid offline surfaces!"); } - ICameraOfflineSession offlineSession = null; + HashSet<Integer> offlineStreamIds = new HashSet<Integer>(); + SparseArray<OutputConfiguration> offlineConfiguredOutputs = + new SparseArray<OutputConfiguration>(); + synchronized(mInterfaceLock) { - int streamId = -1; + if (mOfflineSessionImpl != null) { + throw new IllegalStateException("Switch to offline mode already in progress"); + } + for (Surface surface : offlineOutputs) { + int streamId = -1; for (int i = 0; i < mConfiguredOutputs.size(); i++) { if (surface == mConfiguredOutputs.valueAt(i).getSurface()) { streamId = mConfiguredOutputs.keyAt(i); + offlineConfiguredOutputs.append(streamId, mConfiguredOutputs.valueAt(i)); break; } } @@ -889,12 +911,67 @@ public class CameraDeviceImpl extends CameraDevice throw new IllegalArgumentException("Offline surface is not part of this" + " session"); } + + if (!mOfflineSupport.contains(streamId)) { + throw new IllegalArgumentException("Surface: " + surface + " does not " + + " support offline mode"); + } + + offlineStreamIds.add(streamId); + } + + mOfflineSessionImpl = new CameraOfflineSessionImpl(mCameraId, + mCharacteristics, executor, listener, offlineConfiguredOutputs, + mConfiguredInput, mFrameNumberTracker, mCaptureCallbackMap, + mRequestLastFrameNumbersList); + + mOfflineSwitchService = Executors.newSingleThreadExecutor(); + mConfiguredOutputs.clear(); + mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(REQUEST_ID_NONE, null); + + mCurrentSession.closeWithoutDraining(); + mCurrentSession = null; + } + + mOfflineSwitchService.execute(new Runnable() { + @Override + public void run() { + // We cannot hold 'mInterfaceLock' during the remote IPC in 'switchToOffline'. + // The call will block until all non-offline requests are completed and/or flushed. + // The results/errors need to be handled by 'CameraDeviceCallbacks' which also sync + // on 'mInterfaceLock'. + try { + ICameraOfflineSession remoteOfflineSession = mRemoteDevice.switchToOffline( + mOfflineSessionImpl.getCallbacks(), + Arrays.stream(offlineStreamIds.toArray( + new Integer[offlineStreamIds.size()])).mapToInt( + Integer::intValue).toArray()); + mOfflineSessionImpl.setRemoteSession(remoteOfflineSession); + } catch (CameraAccessException e) { + mOfflineSessionImpl.notifyFailedSwitch(); + } + } + }); + + return mOfflineSessionImpl; + } + + public boolean supportsOfflineProcessing(Surface surface) { + if (surface == null) throw new IllegalArgumentException("Surface is null"); + + synchronized(mInterfaceLock) { + int streamId = -1; + for (int i = 0; i < mConfiguredOutputs.size(); i++) { + if (surface == mConfiguredOutputs.valueAt(i).getSurface()) { + streamId = mConfiguredOutputs.keyAt(i); + break; + } + } + if (streamId == -1) { + throw new IllegalArgumentException("Surface is not part of this session"); } - offlineSession = mRemoteDevice.switchToOffline(cbs, - offlineOutputs); - // TODO: Initialize CameraOfflineSession wrapper, clear 'mConfiguredOutputs', - // and update request tracking + return mOfflineSupport.contains(streamId); } } @@ -985,7 +1062,7 @@ public class CameraDeviceImpl extends CameraDevice final int[] repeatingRequestTypes) { // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately. - if (lastFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) { + if (lastFrameNumber == CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED) { final CaptureCallbackHolder holder; int index = mCaptureCallbackMap.indexOfKey(requestId); holder = (index >= 0) ? mCaptureCallbackMap.valueAt(index) : null; @@ -1225,6 +1302,11 @@ public class CameraDeviceImpl extends CameraDevice return; } + if (mOfflineSwitchService != null) { + mOfflineSwitchService.shutdownNow(); + mOfflineSwitchService = null; + } + if (mRemoteDevice != null) { mRemoteDevice.disconnect(); mRemoteDevice.unlinkToDeath(this, /*flags*/0); @@ -1286,91 +1368,6 @@ public class CameraDeviceImpl extends CameraDevice } /** - * <p>A callback for tracking the progress of a {@link CaptureRequest} - * submitted to the camera device.</p> - * - * An interface instead of an abstract class because this is internal and - * we want to make sure we always implement all its callbacks until we reach - * the public layer. - */ - public interface CaptureCallback { - - /** - * This constant is used to indicate that no images were captured for - * the request. - * - * @hide - */ - public static final int NO_FRAMES_CAPTURED = -1; - - /** - * This method is called when the camera device has started capturing - * the output image for the request, at the beginning of image exposure. - * - * @see android.media.MediaActionSound - */ - public void onCaptureStarted(CameraDevice camera, - CaptureRequest request, long timestamp, long frameNumber); - - /** - * This method is called when some results from an image capture are - * available. - * - * @hide - */ - public void onCapturePartial(CameraDevice camera, - CaptureRequest request, CaptureResult result); - - /** - * This method is called when an image capture makes partial forward progress; some - * (but not all) results from an image capture are available. - * - */ - public void onCaptureProgressed(CameraDevice camera, - CaptureRequest request, CaptureResult partialResult); - - /** - * This method is called when an image capture has fully completed and all the - * result metadata is available. - */ - public void onCaptureCompleted(CameraDevice camera, - CaptureRequest request, TotalCaptureResult result); - - /** - * This method is called instead of {@link #onCaptureCompleted} when the - * camera device failed to produce a {@link CaptureResult} for the - * request. - */ - public void onCaptureFailed(CameraDevice camera, - CaptureRequest request, CaptureFailure failure); - - /** - * This method is called independently of the others in CaptureCallback, - * when a capture sequence finishes and all {@link CaptureResult} - * or {@link CaptureFailure} for it have been returned via this callback. - */ - public void onCaptureSequenceCompleted(CameraDevice camera, - int sequenceId, long frameNumber); - - /** - * This method is called independently of the others in CaptureCallback, - * when a capture sequence aborts before any {@link CaptureResult} - * or {@link CaptureFailure} for it have been returned via this callback. - */ - public void onCaptureSequenceAborted(CameraDevice camera, - int sequenceId); - - /** - * This method is called independently of the others in CaptureCallback, if an output buffer - * is dropped for a particular capture request. - * - * Loss of metadata is communicated via onCaptureFailed, independently of any buffer loss. - */ - public void onCaptureBufferLost(CameraDevice camera, - CaptureRequest request, Surface target, long frameNumber); - } - - /** * A callback for notifications about the state of a camera device, adding in the callbacks that * were part of the earlier KK API design, but now only used internally. */ @@ -1426,478 +1423,6 @@ public class CameraDeviceImpl extends CameraDevice } } - static class CaptureCallbackHolder { - - private final boolean mRepeating; - private final CaptureCallback mCallback; - private final List<CaptureRequest> mRequestList; - private final Executor mExecutor; - private final int mSessionId; - /** - * <p>Determine if the callback holder is for a constrained high speed request list that - * expects batched capture results. Capture results will be batched if the request list - * is interleaved with preview and video requests. Capture results won't be batched if the - * request list only contains preview requests, or if the request doesn't belong to a - * constrained high speed list. - */ - private final boolean mHasBatchedOutputs; - - CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList, - Executor executor, boolean repeating, int sessionId) { - if (callback == null || executor == null) { - throw new UnsupportedOperationException( - "Must have a valid handler and a valid callback"); - } - mRepeating = repeating; - mExecutor = executor; - mRequestList = new ArrayList<CaptureRequest>(requestList); - mCallback = callback; - mSessionId = sessionId; - - // Check whether this callback holder is for batched outputs. - // The logic here should match createHighSpeedRequestList. - boolean hasBatchedOutputs = true; - for (int i = 0; i < requestList.size(); i++) { - CaptureRequest request = requestList.get(i); - if (!request.isPartOfCRequestList()) { - hasBatchedOutputs = false; - break; - } - if (i == 0) { - Collection<Surface> targets = request.getTargets(); - if (targets.size() != 2) { - hasBatchedOutputs = false; - break; - } - } - } - mHasBatchedOutputs = hasBatchedOutputs; - } - - public boolean isRepeating() { - return mRepeating; - } - - public CaptureCallback getCallback() { - return mCallback; - } - - public CaptureRequest getRequest(int subsequenceId) { - if (subsequenceId >= mRequestList.size()) { - throw new IllegalArgumentException( - String.format( - "Requested subsequenceId %d is larger than request list size %d.", - subsequenceId, mRequestList.size())); - } else { - if (subsequenceId < 0) { - throw new IllegalArgumentException(String.format( - "Requested subsequenceId %d is negative", subsequenceId)); - } else { - return mRequestList.get(subsequenceId); - } - } - } - - public CaptureRequest getRequest() { - return getRequest(0); - } - - public Executor getExecutor() { - return mExecutor; - } - - public int getSessionId() { - return mSessionId; - } - - public int getRequestCount() { - return mRequestList.size(); - } - - public boolean hasBatchedOutputs() { - return mHasBatchedOutputs; - } - } - - /** - * This class holds a capture ID and its expected last regular, zslStill, and reprocess - * frame number. - */ - static class RequestLastFrameNumbersHolder { - // request ID - private final int mRequestId; - // The last regular frame number for this request ID. It's - // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no regular request. - private final long mLastRegularFrameNumber; - // The last reprocess frame number for this request ID. It's - // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no reprocess request. - private final long mLastReprocessFrameNumber; - // The last ZSL still capture frame number for this request ID. It's - // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no zsl request. - private final long mLastZslStillFrameNumber; - - /** - * Create a request-last-frame-numbers holder with a list of requests, request ID, and - * the last frame number returned by camera service. - */ - public RequestLastFrameNumbersHolder(List<CaptureRequest> requestList, SubmitInfo requestInfo) { - long lastRegularFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; - long lastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; - long lastZslStillFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; - long frameNumber = requestInfo.getLastFrameNumber(); - - if (requestInfo.getLastFrameNumber() < requestList.size() - 1) { - throw new IllegalArgumentException( - "lastFrameNumber: " + requestInfo.getLastFrameNumber() + - " should be at least " + (requestList.size() - 1) + " for the number of " + - " requests in the list: " + requestList.size()); - } - - // find the last regular, zslStill, and reprocess frame number - for (int i = requestList.size() - 1; i >= 0; i--) { - CaptureRequest request = requestList.get(i); - int requestType = request.getRequestType(); - if (requestType == CaptureRequest.REQUEST_TYPE_REPROCESS - && lastReprocessFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) { - lastReprocessFrameNumber = frameNumber; - } else if (requestType == CaptureRequest.REQUEST_TYPE_ZSL_STILL - && lastZslStillFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) { - lastZslStillFrameNumber = frameNumber; - } else if (requestType == CaptureRequest.REQUEST_TYPE_REGULAR - && lastRegularFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) { - lastRegularFrameNumber = frameNumber; - } - - if (lastReprocessFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED - && lastZslStillFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED - && lastRegularFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED) { - break; - } - - frameNumber--; - } - - mLastRegularFrameNumber = lastRegularFrameNumber; - mLastReprocessFrameNumber = lastReprocessFrameNumber; - mLastZslStillFrameNumber = lastZslStillFrameNumber; - mRequestId = requestInfo.getRequestId(); - } - - /** - * Create a request-last-frame-numbers holder with a request ID and last regular/ZslStill - * frame number. - */ - RequestLastFrameNumbersHolder(int requestId, long lastFrameNumber, - int[] repeatingRequestTypes) { - long lastRegularFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; - long lastZslStillFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; - - if (repeatingRequestTypes == null) { - throw new IllegalArgumentException( - "repeatingRequest list must not be null"); - } - if (lastFrameNumber < repeatingRequestTypes.length - 1) { - throw new IllegalArgumentException( - "lastFrameNumber: " + lastFrameNumber + " should be at least " - + (repeatingRequestTypes.length - 1) - + " for the number of requests in the list: " - + repeatingRequestTypes.length); - } - - long frameNumber = lastFrameNumber; - for (int i = repeatingRequestTypes.length - 1; i >= 0; i--) { - if (repeatingRequestTypes[i] == CaptureRequest.REQUEST_TYPE_ZSL_STILL - && lastZslStillFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) { - lastZslStillFrameNumber = frameNumber; - } else if (repeatingRequestTypes[i] == CaptureRequest.REQUEST_TYPE_REGULAR - && lastRegularFrameNumber == CaptureCallback.NO_FRAMES_CAPTURED) { - lastRegularFrameNumber = frameNumber; - } - - if (lastZslStillFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED - && lastRegularFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED) { - break; - } - - frameNumber--; - } - - mLastRegularFrameNumber = lastRegularFrameNumber; - mLastZslStillFrameNumber = lastZslStillFrameNumber; - mLastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; - mRequestId = requestId; - } - - /** - * Return the last regular frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if - * it contains no regular request. - */ - public long getLastRegularFrameNumber() { - return mLastRegularFrameNumber; - } - - /** - * Return the last reprocess frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if - * it contains no reprocess request. - */ - public long getLastReprocessFrameNumber() { - return mLastReprocessFrameNumber; - } - - /** - * Return the last ZslStill frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if - * it contains no Zsl request. - */ - public long getLastZslStillFrameNumber() { - return mLastZslStillFrameNumber; - } - - /** - * Return the last frame number overall. - */ - public long getLastFrameNumber() { - return Math.max(mLastZslStillFrameNumber, - Math.max(mLastRegularFrameNumber, mLastReprocessFrameNumber)); - } - - /** - * Return the request ID. - */ - public int getRequestId() { - return mRequestId; - } - } - - /** - * This class tracks the last frame number for submitted requests. - */ - public class FrameNumberTracker { - - /** the completed frame number for each type of capture results */ - private long[] mCompletedFrameNumber = new long[CaptureRequest.REQUEST_TYPE_COUNT]; - - /** the skipped frame numbers that don't belong to each type of capture results */ - private final LinkedList<Long>[] mSkippedOtherFrameNumbers = - new LinkedList[CaptureRequest.REQUEST_TYPE_COUNT]; - - /** the skipped frame numbers that belong to each type of capture results */ - private final LinkedList<Long>[] mSkippedFrameNumbers = - new LinkedList[CaptureRequest.REQUEST_TYPE_COUNT]; - - /** frame number -> request type */ - private final TreeMap<Long, Integer> mFutureErrorMap = new TreeMap<Long, Integer>(); - /** Map frame numbers to list of partial results */ - private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>(); - - public FrameNumberTracker() { - for (int i = 0; i < CaptureRequest.REQUEST_TYPE_COUNT; i++) { - mCompletedFrameNumber[i] = CaptureCallback.NO_FRAMES_CAPTURED; - mSkippedOtherFrameNumbers[i] = new LinkedList<Long>(); - mSkippedFrameNumbers[i] = new LinkedList<Long>(); - } - } - - private void update() { - Iterator iter = mFutureErrorMap.entrySet().iterator(); - while (iter.hasNext()) { - TreeMap.Entry pair = (TreeMap.Entry)iter.next(); - Long errorFrameNumber = (Long)pair.getKey(); - int requestType = (int) pair.getValue(); - Boolean removeError = false; - if (errorFrameNumber == mCompletedFrameNumber[requestType] + 1) { - mCompletedFrameNumber[requestType] = errorFrameNumber; - removeError = true; - } else { - if (!mSkippedFrameNumbers[requestType].isEmpty()) { - if (errorFrameNumber == mSkippedFrameNumbers[requestType].element()) { - mCompletedFrameNumber[requestType] = errorFrameNumber; - mSkippedFrameNumbers[requestType].remove(); - removeError = true; - } - } else { - for (int i = 1; i < CaptureRequest.REQUEST_TYPE_COUNT; i++) { - int otherType = (requestType + i) % CaptureRequest.REQUEST_TYPE_COUNT; - if (!mSkippedOtherFrameNumbers[otherType].isEmpty() && errorFrameNumber - == mSkippedOtherFrameNumbers[otherType].element()) { - mCompletedFrameNumber[requestType] = errorFrameNumber; - mSkippedOtherFrameNumbers[otherType].remove(); - removeError = true; - break; - } - } - } - } - if (removeError) { - iter.remove(); - } - } - } - - /** - * This function is called every time when a result or an error is received. - * @param frameNumber the frame number corresponding to the result or error - * @param isError true if it is an error, false if it is not an error - * @param requestType the type of capture request: Reprocess, ZslStill, or Regular. - */ - public void updateTracker(long frameNumber, boolean isError, int requestType) { - if (isError) { - mFutureErrorMap.put(frameNumber, requestType); - } else { - try { - updateCompletedFrameNumber(frameNumber, requestType); - } catch (IllegalArgumentException e) { - Log.e(TAG, e.getMessage()); - } - } - update(); - } - - /** - * This function is called every time a result has been completed. - * - * <p>It keeps a track of all the partial results already created for a particular - * frame number.</p> - * - * @param frameNumber the frame number corresponding to the result - * @param result the total or partial result - * @param partial {@true} if the result is partial, {@code false} if total - * @param requestType the type of capture request: Reprocess, ZslStill, or Regular. - */ - public void updateTracker(long frameNumber, CaptureResult result, boolean partial, - int requestType) { - if (!partial) { - // Update the total result's frame status as being successful - updateTracker(frameNumber, /*isError*/false, requestType); - // Don't keep a list of total results, we don't need to track them - return; - } - - if (result == null) { - // Do not record blank results; this also means there will be no total result - // so it doesn't matter that the partials were not recorded - return; - } - - // Partial results must be aggregated in-order for that frame number - List<CaptureResult> partials = mPartialResults.get(frameNumber); - if (partials == null) { - partials = new ArrayList<>(); - mPartialResults.put(frameNumber, partials); - } - - partials.add(result); - } - - /** - * Attempt to pop off all of the partial results seen so far for the {@code frameNumber}. - * - * <p>Once popped-off, the partial results are forgotten (unless {@code updateTracker} - * is called again with new partials for that frame number).</p> - * - * @param frameNumber the frame number corresponding to the result - * @return a list of partial results for that frame with at least 1 element, - * or {@code null} if there were no partials recorded for that frame - */ - public List<CaptureResult> popPartialResults(long frameNumber) { - return mPartialResults.remove(frameNumber); - } - - public long getCompletedFrameNumber() { - return mCompletedFrameNumber[CaptureRequest.REQUEST_TYPE_REGULAR]; - } - - public long getCompletedReprocessFrameNumber() { - return mCompletedFrameNumber[CaptureRequest.REQUEST_TYPE_REPROCESS]; - } - - public long getCompletedZslStillFrameNumber() { - return mCompletedFrameNumber[CaptureRequest.REQUEST_TYPE_ZSL_STILL]; - } - - /** - * Update the completed frame number for results of 3 categories - * (Regular/Reprocess/ZslStill). - * - * It validates that all previous frames of the same category have arrived. - * - * If there is a gap since previous frame number of the same category, assume the frames in - * the gap are other categories and store them in the skipped frame number queue to check - * against when frames of those categories arrive. - */ - private void updateCompletedFrameNumber(long frameNumber, - int requestType) throws IllegalArgumentException { - if (frameNumber <= mCompletedFrameNumber[requestType]) { - throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat"); - } - - // Assume there are only 3 different types of capture requests. - int otherType1 = (requestType + 1) % CaptureRequest.REQUEST_TYPE_COUNT; - int otherType2 = (requestType + 2) % CaptureRequest.REQUEST_TYPE_COUNT; - long maxOtherFrameNumberSeen = - Math.max(mCompletedFrameNumber[otherType1], mCompletedFrameNumber[otherType2]); - if (frameNumber < maxOtherFrameNumberSeen) { - // if frame number is smaller than completed frame numbers of other categories, - // it must be: - // - the head of mSkippedFrameNumbers for this category, or - // - in one of other mSkippedOtherFrameNumbers - if (!mSkippedFrameNumbers[requestType].isEmpty()) { - // frame number must be head of current type of mSkippedFrameNumbers if - // mSkippedFrameNumbers isn't empty. - if (frameNumber < mSkippedFrameNumbers[requestType].element()) { - throw new IllegalArgumentException("frame number " + frameNumber - + " is a repeat"); - } else if (frameNumber > mSkippedFrameNumbers[requestType].element()) { - throw new IllegalArgumentException("frame number " + frameNumber - + " comes out of order. Expecting " - + mSkippedFrameNumbers[requestType].element()); - } - // frame number matches the head of the skipped frame number queue. - mSkippedFrameNumbers[requestType].remove(); - } else { - // frame number must be in one of the other mSkippedOtherFrameNumbers. - int index1 = mSkippedOtherFrameNumbers[otherType1].indexOf(frameNumber); - int index2 = mSkippedOtherFrameNumbers[otherType2].indexOf(frameNumber); - boolean inSkippedOther1 = index1 != -1; - boolean inSkippedOther2 = index2 != -1; - if (!(inSkippedOther1 ^ inSkippedOther2)) { - throw new IllegalArgumentException("frame number " + frameNumber - + " is a repeat or invalid"); - } - - // We know the category of frame numbers in skippedOtherFrameNumbers leading up - // to the current frame number. Move them into the correct skippedFrameNumbers. - LinkedList<Long> srcList, dstList; - int index; - if (inSkippedOther1) { - srcList = mSkippedOtherFrameNumbers[otherType1]; - dstList = mSkippedFrameNumbers[otherType2]; - index = index1; - } else { - srcList = mSkippedOtherFrameNumbers[otherType2]; - dstList = mSkippedFrameNumbers[otherType1]; - index = index2; - } - for (int i = 0; i < index; i++) { - dstList.add(srcList.removeFirst()); - } - - // Remove current frame number from skippedOtherFrameNumbers - srcList.remove(); - } - } else { - // there is a gap of unseen frame numbers which should belong to the other - // 2 categories. Put all the skipped frame numbers in the queue. - for (long i = - Math.max(maxOtherFrameNumberSeen, mCompletedFrameNumber[requestType]) + 1; - i < frameNumber; i++) { - mSkippedOtherFrameNumbers[requestType].add(i); - } - } - - mCompletedFrameNumber[requestType] = frameNumber; - } - } - private void checkAndFireSequenceComplete() { long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber(); long completedReprocessFrameNumber = mFrameNumberTracker.getCompletedReprocessFrameNumber(); @@ -1979,69 +1504,211 @@ public class CameraDeviceImpl extends CameraDevice } } - public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub { - - @Override - public IBinder asBinder() { - return this; + public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) { + if (DEBUG) { + Log.d(TAG, String.format( + "Device error received, code %d, frame number %d, request ID %d, subseq ID %d", + errorCode, resultExtras.getFrameNumber(), resultExtras.getRequestId(), + resultExtras.getSubsequenceId())); } - @Override - public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) { - if (DEBUG) { - Log.d(TAG, String.format( - "Device error received, code %d, frame number %d, request ID %d, subseq ID %d", - errorCode, resultExtras.getFrameNumber(), resultExtras.getRequestId(), - resultExtras.getSubsequenceId())); + synchronized(mInterfaceLock) { + if (mRemoteDevice == null) { + return; // Camera already closed } - synchronized(mInterfaceLock) { - if (mRemoteDevice == null) { - return; // Camera already closed - } + // Redirect device callback to the offline session in case we are in the middle + // of an offline switch + if (mOfflineSessionImpl != null) { + mOfflineSessionImpl.getCallbacks().onDeviceError(errorCode, resultExtras); + return; + } - switch (errorCode) { - case ERROR_CAMERA_DISCONNECTED: - final long ident = Binder.clearCallingIdentity(); - try { - CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnDisconnected); - } finally { - Binder.restoreCallingIdentity(ident); + switch (errorCode) { + case CameraDeviceCallbacks.ERROR_CAMERA_DISCONNECTED: + final long ident = Binder.clearCallingIdentity(); + try { + mDeviceExecutor.execute(mCallOnDisconnected); + } finally { + Binder.restoreCallingIdentity(ident); + } + break; + case CameraDeviceCallbacks.ERROR_CAMERA_REQUEST: + case CameraDeviceCallbacks.ERROR_CAMERA_RESULT: + case CameraDeviceCallbacks.ERROR_CAMERA_BUFFER: + onCaptureErrorLocked(errorCode, resultExtras); + break; + case CameraDeviceCallbacks.ERROR_CAMERA_DEVICE: + scheduleNotifyError(StateCallback.ERROR_CAMERA_DEVICE); + break; + case CameraDeviceCallbacks.ERROR_CAMERA_DISABLED: + scheduleNotifyError(StateCallback.ERROR_CAMERA_DISABLED); + break; + default: + Log.e(TAG, "Unknown error from camera device: " + errorCode); + scheduleNotifyError(StateCallback.ERROR_CAMERA_SERVICE); + } + } + } + + private void scheduleNotifyError(int code) { + mInError = true; + final long ident = Binder.clearCallingIdentity(); + try { + mDeviceExecutor.execute(obtainRunnable( + CameraDeviceImpl::notifyError, this, code).recycleOnUse()); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + private void notifyError(int code) { + if (!CameraDeviceImpl.this.isClosed()) { + mDeviceCallback.onError(CameraDeviceImpl.this, code); + } + } + + /** + * Called by onDeviceError for handling single-capture failures. + */ + private void onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras) { + + final int requestId = resultExtras.getRequestId(); + final int subsequenceId = resultExtras.getSubsequenceId(); + final long frameNumber = resultExtras.getFrameNumber(); + final String errorPhysicalCameraId = resultExtras.getErrorPhysicalCameraId(); + final CaptureCallbackHolder holder = mCaptureCallbackMap.get(requestId); + + if (holder == null) { + Log.e(TAG, String.format("Receive capture error on unknown request ID %d", + requestId)); + return; + } + + final CaptureRequest request = holder.getRequest(subsequenceId); + + Runnable failureDispatch = null; + if (errorCode == CameraDeviceCallbacks.ERROR_CAMERA_BUFFER) { + // Because 1 stream id could map to multiple surfaces, we need to specify both + // streamId and surfaceId. + OutputConfiguration config = mConfiguredOutputs.get( + resultExtras.getErrorStreamId()); + if (config == null) { + Log.v(TAG, String.format( + "Stream %d has been removed. Skipping buffer lost callback", + resultExtras.getErrorStreamId())); + return; + } + for (Surface surface : config.getSurfaces()) { + if (!request.containsTarget(surface)) { + continue; + } + if (DEBUG) { + Log.v(TAG, String.format( + "Lost output buffer reported for frame %d, target %s", + frameNumber, surface)); + } + failureDispatch = new Runnable() { + @Override + public void run() { + if (!isClosed()){ + holder.getCallback().onCaptureBufferLost(CameraDeviceImpl.this, request, + surface, frameNumber); } - break; - case ERROR_CAMERA_REQUEST: - case ERROR_CAMERA_RESULT: - case ERROR_CAMERA_BUFFER: - onCaptureErrorLocked(errorCode, resultExtras); - break; - case ERROR_CAMERA_DEVICE: - scheduleNotifyError(StateCallback.ERROR_CAMERA_DEVICE); - break; - case ERROR_CAMERA_DISABLED: - scheduleNotifyError(StateCallback.ERROR_CAMERA_DISABLED); - break; - default: - Log.e(TAG, "Unknown error from camera device: " + errorCode); - scheduleNotifyError(StateCallback.ERROR_CAMERA_SERVICE); + } + }; + // Dispatch the failure callback + final long ident = Binder.clearCallingIdentity(); + try { + holder.getExecutor().execute(failureDispatch); + } finally { + Binder.restoreCallingIdentity(ident); } } - } + } else { + boolean mayHaveBuffers = (errorCode == CameraDeviceCallbacks.ERROR_CAMERA_RESULT); + + // This is only approximate - exact handling needs the camera service and HAL to + // disambiguate between request failures to due abort and due to real errors. For + // now, assume that if the session believes we're mid-abort, then the error is due + // to abort. + int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ? + CaptureFailure.REASON_FLUSHED : + CaptureFailure.REASON_ERROR; + + final CaptureFailure failure = new CaptureFailure( + request, + reason, + /*dropped*/ mayHaveBuffers, + requestId, + frameNumber, + errorPhysicalCameraId); + + failureDispatch = new Runnable() { + @Override + public void run() { + if (!isClosed()){ + holder.getCallback().onCaptureFailed(CameraDeviceImpl.this, request, + failure); + } + } + }; - private void scheduleNotifyError(int code) { - mInError = true; + // Fire onCaptureSequenceCompleted if appropriate + if (DEBUG) { + Log.v(TAG, String.format("got error frame %d", frameNumber)); + } + mFrameNumberTracker.updateTracker(frameNumber, + /*error*/true, request.getRequestType()); + checkAndFireSequenceComplete(); + + // Dispatch the failure callback final long ident = Binder.clearCallingIdentity(); try { - CameraDeviceImpl.this.mDeviceExecutor.execute(obtainRunnable( - CameraDeviceCallbacks::notifyError, this, code).recycleOnUse()); + holder.getExecutor().execute(failureDispatch); } finally { Binder.restoreCallingIdentity(ident); } } - private void notifyError(int code) { - if (!CameraDeviceImpl.this.isClosed()) { - mDeviceCallback.onError(CameraDeviceImpl.this, code); + } + + public void onDeviceIdle() { + if (DEBUG) { + Log.d(TAG, "Camera now idle"); + } + synchronized(mInterfaceLock) { + if (mRemoteDevice == null) return; // Camera already closed + + // Redirect device callback to the offline session in case we are in the middle + // of an offline switch + if (mOfflineSessionImpl != null) { + mOfflineSessionImpl.getCallbacks().onDeviceIdle(); + return; + } + + if (!CameraDeviceImpl.this.mIdle) { + final long ident = Binder.clearCallingIdentity(); + try { + mDeviceExecutor.execute(mCallOnIdle); + } finally { + Binder.restoreCallingIdentity(ident); + } } + mIdle = true; + } + } + + public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub { + + @Override + public IBinder asBinder() { + return this; + } + + @Override + public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) { + CameraDeviceImpl.this.onDeviceError(errorCode, resultExtras); } @Override @@ -2057,6 +1724,14 @@ public class CameraDeviceImpl extends CameraDevice return; // Camera already closed } + // Redirect device callback to the offline session in case we are in the middle + // of an offline switch + if (mOfflineSessionImpl != null) { + mOfflineSessionImpl.getCallbacks().onRepeatingRequestError( + lastFrameNumber, repeatingRequestId); + return; + } + checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber, mRepeatingRequestTypes); // Check if there is already a new repeating request @@ -2069,22 +1744,7 @@ public class CameraDeviceImpl extends CameraDevice @Override public void onDeviceIdle() { - if (DEBUG) { - Log.d(TAG, "Camera now idle"); - } - synchronized(mInterfaceLock) { - if (mRemoteDevice == null) return; // Camera already closed - - if (!CameraDeviceImpl.this.mIdle) { - final long ident = Binder.clearCallingIdentity(); - try { - CameraDeviceImpl.this.mDeviceExecutor.execute(mCallOnIdle); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - CameraDeviceImpl.this.mIdle = true; - } + CameraDeviceImpl.this.onDeviceIdle(); } @Override @@ -2100,6 +1760,15 @@ public class CameraDeviceImpl extends CameraDevice synchronized(mInterfaceLock) { if (mRemoteDevice == null) return; // Camera already closed + + // Redirect device callback to the offline session in case we are in the middle + // of an offline switch + if (mOfflineSessionImpl != null) { + mOfflineSessionImpl.getCallbacks().onCaptureStarted(resultExtras, + timestamp); + return; + } + // Get the callback for this frame ID, if there is one holder = CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId); @@ -2164,6 +1833,15 @@ public class CameraDeviceImpl extends CameraDevice synchronized(mInterfaceLock) { if (mRemoteDevice == null) return; // Camera already closed + + // Redirect device callback to the offline session in case we are in the middle + // of an offline switch + if (mOfflineSessionImpl != null) { + mOfflineSessionImpl.getCallbacks().onResultReceived(result, resultExtras, + physicalResults); + return; + } + // TODO: Handle CameraCharacteristics access from CaptureResult correctly. result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE, getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE)); @@ -2325,6 +2003,13 @@ public class CameraDeviceImpl extends CameraDevice } synchronized(mInterfaceLock) { + // Redirect device callback to the offline session in case we are in the middle + // of an offline switch + if (mOfflineSessionImpl != null) { + mOfflineSessionImpl.getCallbacks().onPrepared(streamId); + return; + } + output = mConfiguredOutputs.get(streamId); sessionCallback = mSessionStateCallback; } @@ -2350,6 +2035,13 @@ public class CameraDeviceImpl extends CameraDevice } synchronized(mInterfaceLock) { + // Redirect device callback to the offline session in case we are in the middle + // of an offline switch + if (mOfflineSessionImpl != null) { + mOfflineSessionImpl.getCallbacks().onRequestQueueEmpty(); + return; + } + sessionCallback = mSessionStateCallback; } @@ -2358,117 +2050,6 @@ public class CameraDeviceImpl extends CameraDevice sessionCallback.onRequestQueueEmpty(); } - /** - * Called by onDeviceError for handling single-capture failures. - */ - private void onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras) { - - final int requestId = resultExtras.getRequestId(); - final int subsequenceId = resultExtras.getSubsequenceId(); - final long frameNumber = resultExtras.getFrameNumber(); - final String errorPhysicalCameraId = resultExtras.getErrorPhysicalCameraId(); - final CaptureCallbackHolder holder = - CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId); - - if (holder == null) { - Log.e(TAG, String.format("Receive capture error on unknown request ID %d", - requestId)); - return; - } - - final CaptureRequest request = holder.getRequest(subsequenceId); - - Runnable failureDispatch = null; - if (errorCode == ERROR_CAMERA_BUFFER) { - // Because 1 stream id could map to multiple surfaces, we need to specify both - // streamId and surfaceId. - OutputConfiguration config = mConfiguredOutputs.get( - resultExtras.getErrorStreamId()); - if (config == null) { - Log.v(TAG, String.format( - "Stream %d has been removed. Skipping buffer lost callback", - resultExtras.getErrorStreamId())); - return; - } - for (Surface surface : config.getSurfaces()) { - if (!request.containsTarget(surface)) { - continue; - } - if (DEBUG) { - Log.v(TAG, String.format( - "Lost output buffer reported for frame %d, target %s", - frameNumber, surface)); - } - failureDispatch = new Runnable() { - @Override - public void run() { - if (!CameraDeviceImpl.this.isClosed()){ - holder.getCallback().onCaptureBufferLost( - CameraDeviceImpl.this, - request, - surface, - frameNumber); - } - } - }; - // Dispatch the failure callback - final long ident = Binder.clearCallingIdentity(); - try { - holder.getExecutor().execute(failureDispatch); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } else { - boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT); - - // This is only approximate - exact handling needs the camera service and HAL to - // disambiguate between request failures to due abort and due to real errors. For - // now, assume that if the session believes we're mid-abort, then the error is due - // to abort. - int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ? - CaptureFailure.REASON_FLUSHED : - CaptureFailure.REASON_ERROR; - - final CaptureFailure failure = new CaptureFailure( - request, - reason, - /*dropped*/ mayHaveBuffers, - requestId, - frameNumber, - errorPhysicalCameraId); - - failureDispatch = new Runnable() { - @Override - public void run() { - if (!CameraDeviceImpl.this.isClosed()){ - holder.getCallback().onCaptureFailed( - CameraDeviceImpl.this, - request, - failure); - } - } - }; - - // Fire onCaptureSequenceCompleted if appropriate - if (DEBUG) { - Log.v(TAG, String.format("got error frame %d", frameNumber)); - } - mFrameNumberTracker.updateTracker(frameNumber, - /*error*/true, request.getRequestType()); - checkAndFireSequenceComplete(); - - // Dispatch the failure callback - final long ident = Binder.clearCallingIdentity(); - try { - holder.getExecutor().execute(failureDispatch); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - } - } // public class CameraDeviceCallbacks /** diff --git a/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java new file mode 100644 index 000000000000..1db377a61465 --- /dev/null +++ b/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java @@ -0,0 +1,840 @@ +/* + * 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.hardware.camera2.impl; + +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CameraOfflineSession; +import android.hardware.camera2.CameraOfflineSession.CameraOfflineSessionCallback; +import android.hardware.camera2.CaptureFailure; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.ICameraDeviceCallbacks; +import android.hardware.camera2.ICameraOfflineSession; +import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.params.InputConfiguration; +import android.hardware.camera2.params.OutputConfiguration; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; +import android.util.Range; +import android.util.SparseArray; +import android.view.Surface; + +import java.util.AbstractMap.SimpleEntry; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.Executor; + +import static com.android.internal.util.Preconditions.*; + +public class CameraOfflineSessionImpl extends CameraOfflineSession + implements IBinder.DeathRecipient { + private static final String TAG = "CameraOfflineSessionImpl"; + private static final int REQUEST_ID_NONE = -1; + private static final long NANO_PER_SECOND = 1000000000; //ns + private final boolean DEBUG = false; + + private ICameraOfflineSession mRemoteSession; + private final AtomicBoolean mClosing = new AtomicBoolean(); + + private SimpleEntry<Integer, InputConfiguration> mOfflineInput = + new SimpleEntry<>(REQUEST_ID_NONE, null); + private SparseArray<OutputConfiguration> mOfflineOutputs = new SparseArray<>(); + + final Object mInterfaceLock = new Object(); // access from this class and Session only! + + private final String mCameraId; + private final CameraCharacteristics mCharacteristics; + private final int mTotalPartialCount; + + private final Executor mOfflineExecutor; + private final CameraOfflineSessionCallback mOfflineCallback; + + private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks(); + + /** + * A list tracking request and its expected last regular/reprocess/zslStill frame + * number. + */ + private List<RequestLastFrameNumbersHolder> mOfflineRequestLastFrameNumbersList = + new ArrayList<>(); + + /** + * An object tracking received frame numbers. + * Updated when receiving callbacks from ICameraDeviceCallbacks. + */ + private FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker(); + + /** map request IDs to callback/request data */ + private SparseArray<CaptureCallbackHolder> mCaptureCallbackMap = + new SparseArray<CaptureCallbackHolder>(); + + public CameraOfflineSessionImpl(String cameraId, CameraCharacteristics characteristics, + Executor offlineExecutor, CameraOfflineSessionCallback offlineCallback, + SparseArray<OutputConfiguration> offlineOutputs, + SimpleEntry<Integer, InputConfiguration> offlineInput, + FrameNumberTracker frameNumberTracker, SparseArray<CaptureCallbackHolder> callbackMap, + List<RequestLastFrameNumbersHolder> frameNumberList) { + if ((cameraId == null) || (characteristics == null)) { + throw new IllegalArgumentException("Null argument given"); + } + + mCameraId = cameraId; + mCharacteristics = characteristics; + + Integer partialCount = + mCharacteristics.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT); + if (partialCount == null) { + // 1 means partial result is not supported. + mTotalPartialCount = 1; + } else { + mTotalPartialCount = partialCount; + } + + mOfflineRequestLastFrameNumbersList.addAll(frameNumberList); + mFrameNumberTracker = frameNumberTracker; + mCaptureCallbackMap = callbackMap; + mOfflineOutputs = offlineOutputs; + mOfflineInput = offlineInput; + mOfflineExecutor = checkNotNull(offlineExecutor, "offline executor must not be null"); + mOfflineCallback = checkNotNull(offlineCallback, "offline callback must not be null"); + + } + + public CameraDeviceCallbacks getCallbacks() { + return mCallbacks; + } + + public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub { + @Override + public IBinder asBinder() { + return this; + } + + @Override + public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) { + synchronized(mInterfaceLock) { + if (mRemoteSession == null) { + return; // Camera already closed + } + + switch (errorCode) { + case CameraDeviceCallbacks.ERROR_CAMERA_REQUEST: + case CameraDeviceCallbacks.ERROR_CAMERA_RESULT: + case CameraDeviceCallbacks.ERROR_CAMERA_BUFFER: + onCaptureErrorLocked(errorCode, resultExtras); + break; + default: + Runnable errorDispatch = new Runnable() { + @Override + public void run() { + if (!isClosed()) { + mOfflineCallback.onError(CameraOfflineSessionImpl.this, + CameraOfflineSessionCallback.STATUS_INTERNAL_ERROR); + } + } + }; + + final long ident = Binder.clearCallingIdentity(); + try { + mOfflineExecutor.execute(errorDispatch); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + } + + @Override + public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) { + Log.e(TAG, "Unexpected repeating request error received. Last frame number is " + + lastFrameNumber); + } + + @Override + public void onDeviceIdle() { + synchronized(mInterfaceLock) { + Runnable idleDispatch = new Runnable() { + @Override + public void run() { + if (!isClosed()) { + mOfflineCallback.onIdle(CameraOfflineSessionImpl.this); + } + } + }; + + final long ident = Binder.clearCallingIdentity(); + try { + mOfflineExecutor.execute(idleDispatch); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + @Override + public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) { + int requestId = resultExtras.getRequestId(); + final long frameNumber = resultExtras.getFrameNumber(); + + final CaptureCallbackHolder holder; + + synchronized(mInterfaceLock) { + if (mRemoteSession == null) return; // Camera already closed + + // Get the callback for this frame ID, if there is one + holder = CameraOfflineSessionImpl.this.mCaptureCallbackMap.get(requestId); + + if (holder == null) { + return; + } + + final Executor executor = holder.getCallback().getExecutor(); + if (isClosed() || (executor == null)) return; + + // Dispatch capture start notice + final long ident = Binder.clearCallingIdentity(); + try { + executor.execute( + new Runnable() { + @Override + public void run() { + final CameraCaptureSession.CaptureCallback callback = + holder.getCallback().getSessionCallback(); + if (!CameraOfflineSessionImpl.this.isClosed() && + (callback != null)) { + final int subsequenceId = resultExtras.getSubsequenceId(); + final CaptureRequest request = holder.getRequest(subsequenceId); + + if (holder.hasBatchedOutputs()) { + // Send derived onCaptureStarted for requests within the + // batch + final Range<Integer> fpsRange = + request.get( + CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE); + for (int i = 0; i < holder.getRequestCount(); i++) { + final CaptureRequest cbRequest = holder.getRequest(i); + final long cbTimestamp = + timestamp - (subsequenceId - i) * + NANO_PER_SECOND/fpsRange.getUpper(); + final long cbFrameNumber = + frameNumber - (subsequenceId - i); + callback.onCaptureStarted(CameraOfflineSessionImpl.this, + cbRequest, cbTimestamp, cbFrameNumber); + } + } else { + callback.onCaptureStarted(CameraOfflineSessionImpl.this, + holder.getRequest( + resultExtras.getSubsequenceId()), + timestamp, frameNumber); + } + } + } + }); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + @Override + public void onResultReceived(CameraMetadataNative result, + CaptureResultExtras resultExtras, PhysicalCaptureResultInfo physicalResults[]) + throws RemoteException { + + int requestId = resultExtras.getRequestId(); + long frameNumber = resultExtras.getFrameNumber(); + + synchronized(mInterfaceLock) { + if (mRemoteSession == null) return; // Camera already closed + + // TODO: Handle CameraCharacteristics access from CaptureResult correctly. + result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE, + mCharacteristics.get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE)); + + final CaptureCallbackHolder holder = + CameraOfflineSessionImpl.this.mCaptureCallbackMap.get(requestId); + final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId()); + + boolean isPartialResult = + (resultExtras.getPartialResultCount() < mTotalPartialCount); + int requestType = request.getRequestType(); + + // Check if we have a callback for this + if (holder == null) { + mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult, + requestType); + + return; + } + + if (isClosed()) { + mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult, + requestType); + return; + } + + + Runnable resultDispatch = null; + + CaptureResult finalResult; + // Make a copy of the native metadata before it gets moved to a CaptureResult + // object. + final CameraMetadataNative resultCopy; + if (holder.hasBatchedOutputs()) { + resultCopy = new CameraMetadataNative(result); + } else { + resultCopy = null; + } + + final Executor executor = holder.getCallback().getExecutor(); + // Either send a partial result or the final capture completed result + if (isPartialResult) { + final CaptureResult resultAsCapture = + new CaptureResult(result, request, resultExtras); + // Partial result + resultDispatch = new Runnable() { + @Override + public void run() { + final CameraCaptureSession.CaptureCallback callback = + holder.getCallback().getSessionCallback(); + if (!CameraOfflineSessionImpl.this.isClosed() && (callback != null)) { + if (holder.hasBatchedOutputs()) { + // Send derived onCaptureProgressed for requests within + // the batch. + for (int i = 0; i < holder.getRequestCount(); i++) { + CameraMetadataNative resultLocal = + new CameraMetadataNative(resultCopy); + final CaptureResult resultInBatch = new CaptureResult( + resultLocal, holder.getRequest(i), resultExtras); + + final CaptureRequest cbRequest = holder.getRequest(i); + callback.onCaptureProgressed(CameraOfflineSessionImpl.this, + cbRequest, resultInBatch); + } + } else { + callback.onCaptureProgressed(CameraOfflineSessionImpl.this, + request, resultAsCapture); + } + } + } + }; + finalResult = resultAsCapture; + } else { + List<CaptureResult> partialResults = + mFrameNumberTracker.popPartialResults(frameNumber); + + final long sensorTimestamp = + result.get(CaptureResult.SENSOR_TIMESTAMP); + final Range<Integer> fpsRange = + request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE); + final int subsequenceId = resultExtras.getSubsequenceId(); + final TotalCaptureResult resultAsCapture = new TotalCaptureResult(result, + request, resultExtras, partialResults, holder.getSessionId(), + physicalResults); + // Final capture result + resultDispatch = new Runnable() { + @Override + public void run() { + final CameraCaptureSession.CaptureCallback callback = + holder.getCallback().getSessionCallback(); + if (!CameraOfflineSessionImpl.this.isClosed() && (callback != null)) { + if (holder.hasBatchedOutputs()) { + // Send derived onCaptureCompleted for requests within + // the batch. + for (int i = 0; i < holder.getRequestCount(); i++) { + resultCopy.set(CaptureResult.SENSOR_TIMESTAMP, + sensorTimestamp - (subsequenceId - i) * + NANO_PER_SECOND/fpsRange.getUpper()); + CameraMetadataNative resultLocal = + new CameraMetadataNative(resultCopy); + // No logical multi-camera support for batched output mode. + TotalCaptureResult resultInBatch = new TotalCaptureResult( + resultLocal, holder.getRequest(i), resultExtras, + partialResults, holder.getSessionId(), + new PhysicalCaptureResultInfo[0]); + + final CaptureRequest cbRequest = holder.getRequest(i); + callback.onCaptureCompleted(CameraOfflineSessionImpl.this, + cbRequest, resultInBatch); + } + } else { + callback.onCaptureCompleted(CameraOfflineSessionImpl.this, + request, resultAsCapture); + } + } + } + }; + finalResult = resultAsCapture; + } + + if (executor != null) { + final long ident = Binder.clearCallingIdentity(); + try { + executor.execute(resultDispatch); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + // Collect the partials for a total result; or mark the frame as totally completed + mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult, + requestType); + + // Fire onCaptureSequenceCompleted + if (!isPartialResult) { + checkAndFireSequenceComplete(); + } + } + } + + @Override + public void onPrepared(int streamId) { + Log.e(TAG, "Unexpected stream " + streamId + " is prepared"); + } + + @Override + public void onRequestQueueEmpty() { + // No-op during offline mode + Log.v(TAG, "onRequestQueueEmpty"); + } + + /** + * Called by onDeviceError for handling single-capture failures. + */ + private void onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras) { + final int requestId = resultExtras.getRequestId(); + final int subsequenceId = resultExtras.getSubsequenceId(); + final long frameNumber = resultExtras.getFrameNumber(); + final String errorPhysicalCameraId = resultExtras.getErrorPhysicalCameraId(); + final CaptureCallbackHolder holder = + CameraOfflineSessionImpl.this.mCaptureCallbackMap.get(requestId); + + if (holder == null) { + Log.e(TAG, String.format("Receive capture error on unknown request ID %d", + requestId)); + return; + } + + final CaptureRequest request = holder.getRequest(subsequenceId); + + Runnable failureDispatch = null; + if (errorCode == ERROR_CAMERA_BUFFER) { + // Because 1 stream id could map to multiple surfaces, we need to specify both + // streamId and surfaceId. + OutputConfiguration config = mOfflineOutputs.get( + resultExtras.getErrorStreamId()); + if (config == null) { + Log.v(TAG, String.format( + "Stream %d has been removed. Skipping buffer lost callback", + resultExtras.getErrorStreamId())); + return; + } + for (Surface surface : config.getSurfaces()) { + if (!request.containsTarget(surface)) { + continue; + } + final Executor executor = holder.getCallback().getExecutor(); + failureDispatch = new Runnable() { + @Override + public void run() { + final CameraCaptureSession.CaptureCallback callback = + holder.getCallback().getSessionCallback(); + if (!CameraOfflineSessionImpl.this.isClosed() && (callback != null)) { + callback.onCaptureBufferLost( CameraOfflineSessionImpl.this, + request, surface, frameNumber); + } + } + }; + if (executor != null) { + // Dispatch the failure callback + final long ident = Binder.clearCallingIdentity(); + try { + executor.execute(failureDispatch); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + } else { + boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT); + int reason = CaptureFailure.REASON_ERROR; + + final CaptureFailure failure = new CaptureFailure( + request, + reason, + /*dropped*/ mayHaveBuffers, + requestId, + frameNumber, + errorPhysicalCameraId); + + final Executor executor = holder.getCallback().getExecutor(); + failureDispatch = new Runnable() { + @Override + public void run() { + final CameraCaptureSession.CaptureCallback callback = + holder.getCallback().getSessionCallback(); + if (!CameraOfflineSessionImpl.this.isClosed() && (callback != null)) { + callback.onCaptureFailed(CameraOfflineSessionImpl.this, request, + failure); + } + } + }; + + // Fire onCaptureSequenceCompleted if appropriate + mFrameNumberTracker.updateTracker(frameNumber, + /*error*/true, request.getRequestType()); + checkAndFireSequenceComplete(); + + if (executor != null) { + // Dispatch the failure callback + final long ident = Binder.clearCallingIdentity(); + try { + executor.execute(failureDispatch); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + } + + } + + private void checkAndFireSequenceComplete() { + long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber(); + long completedReprocessFrameNumber = mFrameNumberTracker.getCompletedReprocessFrameNumber(); + long completedZslStillFrameNumber = mFrameNumberTracker.getCompletedZslStillFrameNumber(); + Iterator<RequestLastFrameNumbersHolder> iter = + mOfflineRequestLastFrameNumbersList.iterator(); + while (iter.hasNext()) { + final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next(); + boolean sequenceCompleted = false; + final int requestId = requestLastFrameNumbers.getRequestId(); + final CaptureCallbackHolder holder; + final Executor executor; + final CameraCaptureSession.CaptureCallback callback; + synchronized(mInterfaceLock) { + if (mRemoteSession == null) { + Log.w(TAG, "Camera closed while checking sequences"); + return; + } + + int index = mCaptureCallbackMap.indexOfKey(requestId); + holder = (index >= 0) ? + mCaptureCallbackMap.valueAt(index) : null; + if (holder != null) { + long lastRegularFrameNumber = + requestLastFrameNumbers.getLastRegularFrameNumber(); + long lastReprocessFrameNumber = + requestLastFrameNumbers.getLastReprocessFrameNumber(); + long lastZslStillFrameNumber = + requestLastFrameNumbers.getLastZslStillFrameNumber(); + executor = holder.getCallback().getExecutor(); + callback = holder.getCallback().getSessionCallback(); + // check if it's okay to remove request from mCaptureCallbackMap + if (lastRegularFrameNumber <= completedFrameNumber + && lastReprocessFrameNumber <= completedReprocessFrameNumber + && lastZslStillFrameNumber <= completedZslStillFrameNumber) { + sequenceCompleted = true; + mCaptureCallbackMap.removeAt(index); + } + } else { + executor = null; + callback = null; + } + } + + // If no callback is registered for this requestId or sequence completed, remove it + // from the frame number->request pair because it's not needed anymore. + if (holder == null || sequenceCompleted) { + iter.remove(); + } + + // Call onCaptureSequenceCompleted + if ((sequenceCompleted) && (callback != null) && (executor == null)) { + Runnable resultDispatch = new Runnable() { + @Override + public void run() { + if (!isClosed()) { + callback.onCaptureSequenceCompleted(CameraOfflineSessionImpl.this, + requestId, requestLastFrameNumbers.getLastFrameNumber()); + } + } + }; + + final long ident = Binder.clearCallingIdentity(); + try { + executor.execute(resultDispatch); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + } + + public void notifyFailedSwitch() { + synchronized(mInterfaceLock) { + Runnable switchFailDispatch = new Runnable() { + @Override + public void run() { + mOfflineCallback.onSwitchFailed(CameraOfflineSessionImpl.this); + } + }; + + final long ident = Binder.clearCallingIdentity(); + try { + mOfflineExecutor.execute(switchFailDispatch); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + /** + * Set remote session. + * + */ + public void setRemoteSession(ICameraOfflineSession remoteSession) throws CameraAccessException { + synchronized(mInterfaceLock) { + if (remoteSession == null) { + notifyFailedSwitch(); + return; + } + + mRemoteSession = remoteSession; + + IBinder remoteSessionBinder = remoteSession.asBinder(); + if (remoteSessionBinder == null) { + throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, + "The camera offline session has encountered a serious error"); + } + + try { + remoteSessionBinder.linkToDeath(this, /*flag*/ 0); + } catch (RemoteException e) { + throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, + "The camera offline session has encountered a serious error"); + } + + Runnable readyDispatch = new Runnable() { + @Override + public void run() { + if (!isClosed()) { + mOfflineCallback.onReady(CameraOfflineSessionImpl.this); + } + } + }; + + final long ident = Binder.clearCallingIdentity(); + try { + mOfflineExecutor.execute(readyDispatch); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + /** Whether the offline session has started to close (may not yet have finished) */ + private boolean isClosed() { + return mClosing.get(); + } + + private void disconnect() { + synchronized (mInterfaceLock) { + if (mClosing.getAndSet(true)) { + return; + } + + if (mRemoteSession != null) { + mRemoteSession.asBinder().unlinkToDeath(this, /*flags*/0); + + try { + mRemoteSession.disconnect(); + } catch (RemoteException e) { + Log.e(TAG, "Exception while disconnecting from offline session: ", e); + } + } else { + throw new IllegalStateException("Offline session is not yet ready"); + } + + mRemoteSession = null; + + Runnable closeDispatch = new Runnable() { + @Override + public void run() { + if (!isClosed()) { + mOfflineCallback.onClosed(CameraOfflineSessionImpl.this); + } + } + }; + + final long ident = Binder.clearCallingIdentity(); + try { + mOfflineExecutor.execute(closeDispatch); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + @Override + protected void finalize() throws Throwable { + try { + disconnect(); + } + finally { + super.finalize(); + } + } + + /** + * Listener for binder death. + * + * <p> Handle binder death for ICameraOfflineSession.</p> + */ + @Override + public void binderDied() { + Log.w(TAG, "CameraOfflineSession on device " + mCameraId + " died unexpectedly"); + disconnect(); + } + + @Override + public CameraDevice getDevice() { + throw new UnsupportedOperationException("Operation not supported in offline mode"); + } + + @Override + public void prepare(Surface surface) throws CameraAccessException { + throw new UnsupportedOperationException("Operation not supported in offline mode"); + } + + @Override + public void prepare(int maxCount, Surface surface) throws CameraAccessException { + throw new UnsupportedOperationException("Operation not supported in offline mode"); + } + + @Override + public void tearDown(Surface surface) throws CameraAccessException { + throw new UnsupportedOperationException("Operation not supported in offline mode"); + } + + @Override + public void finalizeOutputConfigurations( + List<OutputConfiguration> outputConfigs) throws CameraAccessException { + throw new UnsupportedOperationException("Operation not supported in offline mode"); + } + + @Override + public int capture(CaptureRequest request, CaptureCallback callback, + Handler handler) throws CameraAccessException { + throw new UnsupportedOperationException("Operation not supported in offline mode"); + } + + @Override + public int captureSingleRequest(CaptureRequest request, Executor executor, + CaptureCallback callback) throws CameraAccessException { + throw new UnsupportedOperationException("Operation not supported in offline mode"); + } + + @Override + public int captureBurst(List<CaptureRequest> requests, CaptureCallback callback, + Handler handler) throws CameraAccessException { + throw new UnsupportedOperationException("Operation not supported in offline mode"); + } + + @Override + public int captureBurstRequests(List<CaptureRequest> requests, Executor executor, + CaptureCallback callback) throws CameraAccessException { + throw new UnsupportedOperationException("Operation not supported in offline mode"); + } + + @Override + public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback, + Handler handler) throws CameraAccessException { + throw new UnsupportedOperationException("Operation not supported in offline mode"); + } + + @Override + public int setSingleRepeatingRequest(CaptureRequest request, Executor executor, + CaptureCallback callback) throws CameraAccessException { + throw new UnsupportedOperationException("Operation not supported in offline mode"); + } + + @Override + public int setRepeatingBurst(List<CaptureRequest> requests, + CaptureCallback callback, Handler handler) throws CameraAccessException { + throw new UnsupportedOperationException("Operation not supported in offline mode"); + } + + @Override + public int setRepeatingBurstRequests(List<CaptureRequest> requests, Executor executor, + CaptureCallback callback) throws CameraAccessException { + throw new UnsupportedOperationException("Operation not supported in offline mode"); + } + + @Override + public void stopRepeating() throws CameraAccessException { + throw new UnsupportedOperationException("Operation not supported in offline mode"); + } + + @Override + public void abortCaptures() throws CameraAccessException { + throw new UnsupportedOperationException("Operation not supported in offline mode"); + } + + @Override + public void updateOutputConfiguration(OutputConfiguration config) + throws CameraAccessException { + throw new UnsupportedOperationException("Operation not supported in offline mode"); + } + + @Override + public boolean isReprocessable() { + throw new UnsupportedOperationException("Operation not supported in offline mode"); + } + + @Override + public Surface getInputSurface() { + throw new UnsupportedOperationException("Operation not supported in offline mode"); + } + + @Override + public CameraOfflineSession switchToOffline(Collection<Surface> offlineOutputs, + Executor executor, CameraOfflineSessionCallback listener) throws CameraAccessException { + throw new UnsupportedOperationException("Operation not supported in offline mode"); + } + + @Override + public boolean supportsOfflineProcessing(Surface surface) { + throw new UnsupportedOperationException("Operation not supported in offline mode"); + } + + @Override + public void close() { + disconnect(); + } +} diff --git a/core/java/android/hardware/camera2/impl/CaptureCallback.java b/core/java/android/hardware/camera2/impl/CaptureCallback.java new file mode 100644 index 000000000000..6defe63b1766 --- /dev/null +++ b/core/java/android/hardware/camera2/impl/CaptureCallback.java @@ -0,0 +1,124 @@ +/* + * 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.hardware.camera2.impl; + +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CaptureFailure; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.view.Surface; + +import java.util.concurrent.Executor; + +/** + * <p>An internal callback for tracking the progress of a {@link CaptureRequest} + * submitted to the camera device.</p> + */ +public abstract class CaptureCallback { + + private Executor mExecutor; + private CameraCaptureSession.CaptureCallback mCallback; + + public CaptureCallback(Executor executor, CameraCaptureSession.CaptureCallback callback) { + mExecutor = executor; + mCallback = callback; + } + + /** + * Retrieve callback executor + * + */ + public Executor getExecutor() { + return mExecutor; + } + + /** + * Retrieve capture callback + * + */ + public CameraCaptureSession.CaptureCallback getSessionCallback() { + return mCallback; + } + + /** + * This method is called when the camera device has started capturing + * the output image for the request, at the beginning of image exposure. + * + * @see android.media.MediaActionSound + */ + public abstract void onCaptureStarted(CameraDevice camera, + CaptureRequest request, long timestamp, long frameNumber); + + /** + * This method is called when some results from an image capture are + * available. + * + * @hide + */ + public abstract void onCapturePartial(CameraDevice camera, + CaptureRequest request, CaptureResult result); + + /** + * This method is called when an image capture makes partial forward progress; some + * (but not all) results from an image capture are available. + * + */ + public abstract void onCaptureProgressed(CameraDevice camera, + CaptureRequest request, CaptureResult partialResult); + + /** + * This method is called when an image capture has fully completed and all the + * result metadata is available. + */ + public abstract void onCaptureCompleted(CameraDevice camera, + CaptureRequest request, TotalCaptureResult result); + + /** + * This method is called instead of {@link #onCaptureCompleted} when the + * camera device failed to produce a {@link CaptureResult} for the + * request. + */ + public abstract void onCaptureFailed(CameraDevice camera, + CaptureRequest request, CaptureFailure failure); + + /** + * This method is called independently of the others in CaptureCallback, + * when a capture sequence finishes and all {@link CaptureResult} + * or {@link CaptureFailure} for it have been returned via this callback. + */ + public abstract void onCaptureSequenceCompleted(CameraDevice camera, + int sequenceId, long frameNumber); + + /** + * This method is called independently of the others in CaptureCallback, + * when a capture sequence aborts before any {@link CaptureResult} + * or {@link CaptureFailure} for it have been returned via this callback. + */ + public abstract void onCaptureSequenceAborted(CameraDevice camera, + int sequenceId); + + /** + * This method is called independently of the others in CaptureCallback, if an output buffer + * is dropped for a particular capture request. + * + * Loss of metadata is communicated via onCaptureFailed, independently of any buffer loss. + */ + public abstract void onCaptureBufferLost(CameraDevice camera, + CaptureRequest request, Surface target, long frameNumber); +} diff --git a/core/java/android/hardware/camera2/impl/CaptureCallbackHolder.java b/core/java/android/hardware/camera2/impl/CaptureCallbackHolder.java new file mode 100644 index 000000000000..01c38907b7e3 --- /dev/null +++ b/core/java/android/hardware/camera2/impl/CaptureCallbackHolder.java @@ -0,0 +1,119 @@ +/* + * 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.hardware.camera2.impl; + +import android.hardware.camera2.CaptureRequest; +import android.view.Surface; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Executor; + +public class CaptureCallbackHolder { + + private final boolean mRepeating; + private final CaptureCallback mCallback; + private final List<CaptureRequest> mRequestList; + private final Executor mExecutor; + private final int mSessionId; + /** + * <p>Determine if the callback holder is for a constrained high speed request list that + * expects batched capture results. Capture results will be batched if the request list + * is interleaved with preview and video requests. Capture results won't be batched if the + * request list only contains preview requests, or if the request doesn't belong to a + * constrained high speed list. + */ + private final boolean mHasBatchedOutputs; + + CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList, + Executor executor, boolean repeating, int sessionId) { + if (callback == null || executor == null) { + throw new UnsupportedOperationException( + "Must have a valid handler and a valid callback"); + } + mRepeating = repeating; + mExecutor = executor; + mRequestList = new ArrayList<CaptureRequest>(requestList); + mCallback = callback; + mSessionId = sessionId; + + // Check whether this callback holder is for batched outputs. + // The logic here should match createHighSpeedRequestList. + boolean hasBatchedOutputs = true; + for (int i = 0; i < requestList.size(); i++) { + CaptureRequest request = requestList.get(i); + if (!request.isPartOfCRequestList()) { + hasBatchedOutputs = false; + break; + } + if (i == 0) { + Collection<Surface> targets = request.getTargets(); + if (targets.size() != 2) { + hasBatchedOutputs = false; + break; + } + } + } + mHasBatchedOutputs = hasBatchedOutputs; + } + + public boolean isRepeating() { + return mRepeating; + } + + public CaptureCallback getCallback() { + return mCallback; + } + + public CaptureRequest getRequest(int subsequenceId) { + if (subsequenceId >= mRequestList.size()) { + throw new IllegalArgumentException( + String.format( + "Requested subsequenceId %d is larger than request list size %d.", + subsequenceId, mRequestList.size())); + } else { + if (subsequenceId < 0) { + throw new IllegalArgumentException(String.format( + "Requested subsequenceId %d is negative", subsequenceId)); + } else { + return mRequestList.get(subsequenceId); + } + } + } + + public CaptureRequest getRequest() { + return getRequest(0); + } + + public Executor getExecutor() { + return mExecutor; + } + + public int getSessionId() { + return mSessionId; + } + + public int getRequestCount() { + return mRequestList.size(); + } + + public boolean hasBatchedOutputs() { + return mHasBatchedOutputs; + } +} diff --git a/core/java/android/hardware/camera2/impl/FrameNumberTracker.java b/core/java/android/hardware/camera2/impl/FrameNumberTracker.java new file mode 100644 index 000000000000..27f8a61b8999 --- /dev/null +++ b/core/java/android/hardware/camera2/impl/FrameNumberTracker.java @@ -0,0 +1,261 @@ +/* + * 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.hardware.camera2.impl; + +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.util.Log; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.TreeMap; + +/** + * This class tracks the last frame number for submitted requests. + */ +public class FrameNumberTracker { + private static final String TAG = "FrameNumberTracker"; + + /** the completed frame number for each type of capture results */ + private long[] mCompletedFrameNumber = new long[CaptureRequest.REQUEST_TYPE_COUNT]; + + /** the skipped frame numbers that don't belong to each type of capture results */ + private final LinkedList<Long>[] mSkippedOtherFrameNumbers = + new LinkedList[CaptureRequest.REQUEST_TYPE_COUNT]; + + /** the skipped frame numbers that belong to each type of capture results */ + private final LinkedList<Long>[] mSkippedFrameNumbers = + new LinkedList[CaptureRequest.REQUEST_TYPE_COUNT]; + + /** frame number -> request type */ + private final TreeMap<Long, Integer> mFutureErrorMap = new TreeMap<Long, Integer>(); + /** Map frame numbers to list of partial results */ + private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>(); + + public FrameNumberTracker() { + for (int i = 0; i < CaptureRequest.REQUEST_TYPE_COUNT; i++) { + mCompletedFrameNumber[i] = CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED; + mSkippedOtherFrameNumbers[i] = new LinkedList<Long>(); + mSkippedFrameNumbers[i] = new LinkedList<Long>(); + } + } + + private void update() { + Iterator iter = mFutureErrorMap.entrySet().iterator(); + while (iter.hasNext()) { + TreeMap.Entry pair = (TreeMap.Entry)iter.next(); + Long errorFrameNumber = (Long)pair.getKey(); + int requestType = (int) pair.getValue(); + Boolean removeError = false; + if (errorFrameNumber == mCompletedFrameNumber[requestType] + 1) { + mCompletedFrameNumber[requestType] = errorFrameNumber; + removeError = true; + } else { + if (!mSkippedFrameNumbers[requestType].isEmpty()) { + if (errorFrameNumber == mSkippedFrameNumbers[requestType].element()) { + mCompletedFrameNumber[requestType] = errorFrameNumber; + mSkippedFrameNumbers[requestType].remove(); + removeError = true; + } + } else { + for (int i = 1; i < CaptureRequest.REQUEST_TYPE_COUNT; i++) { + int otherType = (requestType + i) % CaptureRequest.REQUEST_TYPE_COUNT; + if (!mSkippedOtherFrameNumbers[otherType].isEmpty() && errorFrameNumber + == mSkippedOtherFrameNumbers[otherType].element()) { + mCompletedFrameNumber[requestType] = errorFrameNumber; + mSkippedOtherFrameNumbers[otherType].remove(); + removeError = true; + break; + } + } + } + } + if (removeError) { + iter.remove(); + } + } + } + + /** + * This function is called every time when a result or an error is received. + * @param frameNumber the frame number corresponding to the result or error + * @param isError true if it is an error, false if it is not an error + * @param requestType the type of capture request: Reprocess, ZslStill, or Regular. + */ + public void updateTracker(long frameNumber, boolean isError, int requestType) { + if (isError) { + mFutureErrorMap.put(frameNumber, requestType); + } else { + try { + updateCompletedFrameNumber(frameNumber, requestType); + } catch (IllegalArgumentException e) { + Log.e(TAG, e.getMessage()); + } + } + update(); + } + + /** + * This function is called every time a result has been completed. + * + * <p>It keeps a track of all the partial results already created for a particular + * frame number.</p> + * + * @param frameNumber the frame number corresponding to the result + * @param result the total or partial result + * @param partial {@true} if the result is partial, {@code false} if total + * @param requestType the type of capture request: Reprocess, ZslStill, or Regular. + */ + public void updateTracker(long frameNumber, CaptureResult result, boolean partial, + int requestType) { + if (!partial) { + // Update the total result's frame status as being successful + updateTracker(frameNumber, /*isError*/false, requestType); + // Don't keep a list of total results, we don't need to track them + return; + } + + if (result == null) { + // Do not record blank results; this also means there will be no total result + // so it doesn't matter that the partials were not recorded + return; + } + + // Partial results must be aggregated in-order for that frame number + List<CaptureResult> partials = mPartialResults.get(frameNumber); + if (partials == null) { + partials = new ArrayList<>(); + mPartialResults.put(frameNumber, partials); + } + + partials.add(result); + } + + /** + * Attempt to pop off all of the partial results seen so far for the {@code frameNumber}. + * + * <p>Once popped-off, the partial results are forgotten (unless {@code updateTracker} + * is called again with new partials for that frame number).</p> + * + * @param frameNumber the frame number corresponding to the result + * @return a list of partial results for that frame with at least 1 element, + * or {@code null} if there were no partials recorded for that frame + */ + public List<CaptureResult> popPartialResults(long frameNumber) { + return mPartialResults.remove(frameNumber); + } + + public long getCompletedFrameNumber() { + return mCompletedFrameNumber[CaptureRequest.REQUEST_TYPE_REGULAR]; + } + + public long getCompletedReprocessFrameNumber() { + return mCompletedFrameNumber[CaptureRequest.REQUEST_TYPE_REPROCESS]; + } + + public long getCompletedZslStillFrameNumber() { + return mCompletedFrameNumber[CaptureRequest.REQUEST_TYPE_ZSL_STILL]; + } + + /** + * Update the completed frame number for results of 3 categories + * (Regular/Reprocess/ZslStill). + * + * It validates that all previous frames of the same category have arrived. + * + * If there is a gap since previous frame number of the same category, assume the frames in + * the gap are other categories and store them in the skipped frame number queue to check + * against when frames of those categories arrive. + */ + private void updateCompletedFrameNumber(long frameNumber, + int requestType) throws IllegalArgumentException { + if (frameNumber <= mCompletedFrameNumber[requestType]) { + throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat"); + } + + // Assume there are only 3 different types of capture requests. + int otherType1 = (requestType + 1) % CaptureRequest.REQUEST_TYPE_COUNT; + int otherType2 = (requestType + 2) % CaptureRequest.REQUEST_TYPE_COUNT; + long maxOtherFrameNumberSeen = + Math.max(mCompletedFrameNumber[otherType1], mCompletedFrameNumber[otherType2]); + if (frameNumber < maxOtherFrameNumberSeen) { + // if frame number is smaller than completed frame numbers of other categories, + // it must be: + // - the head of mSkippedFrameNumbers for this category, or + // - in one of other mSkippedOtherFrameNumbers + if (!mSkippedFrameNumbers[requestType].isEmpty()) { + // frame number must be head of current type of mSkippedFrameNumbers if + // mSkippedFrameNumbers isn't empty. + if (frameNumber < mSkippedFrameNumbers[requestType].element()) { + throw new IllegalArgumentException("frame number " + frameNumber + + " is a repeat"); + } else if (frameNumber > mSkippedFrameNumbers[requestType].element()) { + throw new IllegalArgumentException("frame number " + frameNumber + + " comes out of order. Expecting " + + mSkippedFrameNumbers[requestType].element()); + } + // frame number matches the head of the skipped frame number queue. + mSkippedFrameNumbers[requestType].remove(); + } else { + // frame number must be in one of the other mSkippedOtherFrameNumbers. + int index1 = mSkippedOtherFrameNumbers[otherType1].indexOf(frameNumber); + int index2 = mSkippedOtherFrameNumbers[otherType2].indexOf(frameNumber); + boolean inSkippedOther1 = index1 != -1; + boolean inSkippedOther2 = index2 != -1; + if (!(inSkippedOther1 ^ inSkippedOther2)) { + throw new IllegalArgumentException("frame number " + frameNumber + + " is a repeat or invalid"); + } + + // We know the category of frame numbers in skippedOtherFrameNumbers leading up + // to the current frame number. Move them into the correct skippedFrameNumbers. + LinkedList<Long> srcList, dstList; + int index; + if (inSkippedOther1) { + srcList = mSkippedOtherFrameNumbers[otherType1]; + dstList = mSkippedFrameNumbers[otherType2]; + index = index1; + } else { + srcList = mSkippedOtherFrameNumbers[otherType2]; + dstList = mSkippedFrameNumbers[otherType1]; + index = index2; + } + for (int i = 0; i < index; i++) { + dstList.add(srcList.removeFirst()); + } + + // Remove current frame number from skippedOtherFrameNumbers + srcList.remove(); + } + } else { + // there is a gap of unseen frame numbers which should belong to the other + // 2 categories. Put all the skipped frame numbers in the queue. + for (long i = + Math.max(maxOtherFrameNumberSeen, mCompletedFrameNumber[requestType]) + 1; + i < frameNumber; i++) { + mSkippedOtherFrameNumbers[requestType].add(i); + } + } + + mCompletedFrameNumber[requestType] = frameNumber; + } +} + diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java index 397417ba5b4f..fa7301bb72c3 100644 --- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java +++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java @@ -110,10 +110,10 @@ public class ICameraDeviceUserWrapper { } } - public void endConfigure(int operatingMode, CameraMetadataNative sessionParams) + public int[] endConfigure(int operatingMode, CameraMetadataNative sessionParams) throws CameraAccessException { try { - mRemoteDevice.endConfigure(operatingMode, (sessionParams == null) ? + return mRemoteDevice.endConfigure(operatingMode, (sessionParams == null) ? new CameraMetadataNative() : sessionParams); } catch (Throwable t) { CameraManager.throwAsPublicException(t); @@ -251,10 +251,9 @@ public class ICameraDeviceUserWrapper { } public ICameraOfflineSession switchToOffline(ICameraDeviceCallbacks cbs, - Surface[] offlineOutputs) - throws CameraAccessException { + int[] offlineOutputIds) throws CameraAccessException { try { - return mRemoteDevice.switchToOffline(cbs, offlineOutputs); + return mRemoteDevice.switchToOffline(cbs, offlineOutputIds); } catch (Throwable t) { CameraManager.throwAsPublicException(t); throw new UnsupportedOperationException("Unexpected exception", t); diff --git a/core/java/android/hardware/camera2/impl/RequestLastFrameNumbersHolder.java b/core/java/android/hardware/camera2/impl/RequestLastFrameNumbersHolder.java new file mode 100644 index 000000000000..bd1df9e1ac7d --- /dev/null +++ b/core/java/android/hardware/camera2/impl/RequestLastFrameNumbersHolder.java @@ -0,0 +1,181 @@ +/* + * 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.hardware.camera2.impl; + +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.utils.SubmitInfo; + +import java.util.List; + +/** + * This class holds a capture ID and its expected last regular, zslStill, and reprocess + * frame number. + */ +public class RequestLastFrameNumbersHolder { + // request ID + private final int mRequestId; + // The last regular frame number for this request ID. It's + // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no regular request. + private final long mLastRegularFrameNumber; + // The last reprocess frame number for this request ID. It's + // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no reprocess request. + private final long mLastReprocessFrameNumber; + // The last ZSL still capture frame number for this request ID. It's + // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no zsl request. + private final long mLastZslStillFrameNumber; + + /** + * Create a request-last-frame-numbers holder with a list of requests, request ID, and + * the last frame number returned by camera service. + */ + public RequestLastFrameNumbersHolder(List<CaptureRequest> requestList, SubmitInfo requestInfo) { + long lastRegularFrameNumber = CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED; + long lastReprocessFrameNumber = CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED; + long lastZslStillFrameNumber = CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED; + long frameNumber = requestInfo.getLastFrameNumber(); + + if (requestInfo.getLastFrameNumber() < requestList.size() - 1) { + throw new IllegalArgumentException( + "lastFrameNumber: " + requestInfo.getLastFrameNumber() + + " should be at least " + (requestList.size() - 1) + " for the number of " + + " requests in the list: " + requestList.size()); + } + + // find the last regular, zslStill, and reprocess frame number + for (int i = requestList.size() - 1; i >= 0; i--) { + CaptureRequest request = requestList.get(i); + int requestType = request.getRequestType(); + if (requestType == CaptureRequest.REQUEST_TYPE_REPROCESS + && lastReprocessFrameNumber == + CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED) { + lastReprocessFrameNumber = frameNumber; + } else if (requestType == CaptureRequest.REQUEST_TYPE_ZSL_STILL + && lastZslStillFrameNumber == + CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED) { + lastZslStillFrameNumber = frameNumber; + } else if (requestType == CaptureRequest.REQUEST_TYPE_REGULAR + && lastRegularFrameNumber == + CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED) { + lastRegularFrameNumber = frameNumber; + } + + if (lastReprocessFrameNumber != CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED + && lastZslStillFrameNumber != + CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED + && lastRegularFrameNumber != + CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED) { + break; + } + + frameNumber--; + } + + mLastRegularFrameNumber = lastRegularFrameNumber; + mLastReprocessFrameNumber = lastReprocessFrameNumber; + mLastZslStillFrameNumber = lastZslStillFrameNumber; + mRequestId = requestInfo.getRequestId(); + } + + /** + * Create a request-last-frame-numbers holder with a request ID and last regular/ZslStill + * frame number. + */ + RequestLastFrameNumbersHolder(int requestId, long lastFrameNumber, + int[] repeatingRequestTypes) { + long lastRegularFrameNumber = CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED; + long lastZslStillFrameNumber = CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED; + + if (repeatingRequestTypes == null) { + throw new IllegalArgumentException( + "repeatingRequest list must not be null"); + } + if (lastFrameNumber < repeatingRequestTypes.length - 1) { + throw new IllegalArgumentException( + "lastFrameNumber: " + lastFrameNumber + " should be at least " + + (repeatingRequestTypes.length - 1) + + " for the number of requests in the list: " + + repeatingRequestTypes.length); + } + + long frameNumber = lastFrameNumber; + for (int i = repeatingRequestTypes.length - 1; i >= 0; i--) { + if (repeatingRequestTypes[i] == CaptureRequest.REQUEST_TYPE_ZSL_STILL + && lastZslStillFrameNumber == + CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED) { + lastZslStillFrameNumber = frameNumber; + } else if (repeatingRequestTypes[i] == CaptureRequest.REQUEST_TYPE_REGULAR + && lastRegularFrameNumber == + CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED) { + lastRegularFrameNumber = frameNumber; + } + + if (lastZslStillFrameNumber != CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED + && lastRegularFrameNumber != + CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED) { + break; + } + + frameNumber--; + } + + mLastRegularFrameNumber = lastRegularFrameNumber; + mLastZslStillFrameNumber = lastZslStillFrameNumber; + mLastReprocessFrameNumber = CameraCaptureSession.CaptureCallback.NO_FRAMES_CAPTURED; + mRequestId = requestId; + } + + /** + * Return the last regular frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if + * it contains no regular request. + */ + public long getLastRegularFrameNumber() { + return mLastRegularFrameNumber; + } + + /** + * Return the last reprocess frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if + * it contains no reprocess request. + */ + public long getLastReprocessFrameNumber() { + return mLastReprocessFrameNumber; + } + + /** + * Return the last ZslStill frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if + * it contains no Zsl request. + */ + public long getLastZslStillFrameNumber() { + return mLastZslStillFrameNumber; + } + + /** + * Return the last frame number overall. + */ + public long getLastFrameNumber() { + return Math.max(mLastZslStillFrameNumber, + Math.max(mLastRegularFrameNumber, mLastReprocessFrameNumber)); + } + + /** + * Return the request ID. + */ + public int getRequestId() { + return mRequestId; + } +} + diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java index 6ab0c294f5e4..cf8cab2cbc44 100644 --- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java +++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java @@ -541,7 +541,7 @@ public class CameraDeviceUserShim implements ICameraDeviceUser { } @Override - public void endConfigure(int operatingMode, CameraMetadataNative sessionParams) { + public int[] endConfigure(int operatingMode, CameraMetadataNative sessionParams) { if (DEBUG) { Log.d(TAG, "endConfigure called."); } @@ -576,6 +576,8 @@ public class CameraDeviceUserShim implements ICameraDeviceUser { mConfiguring = false; } mLegacyDevice.configureOutputs(surfaces); + + return new int[0]; // Offline mode is not supported } @Override @@ -791,8 +793,8 @@ public class CameraDeviceUserShim implements ICameraDeviceUser { @Override public ICameraOfflineSession switchToOffline(ICameraDeviceCallbacks cbs, - Surface[] offlineOutputs) { - throw new UnsupportedOperationException("Legacy device does not support switchToOffline"); + int[] offlineOutputIds) { + throw new UnsupportedOperationException("Legacy device does not support offline mode"); } @Override 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/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/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl index c8dbd16005ac..e32865ebeced 100644 --- a/core/java/android/hardware/usb/IUsbManager.aidl +++ b/core/java/android/hardware/usb/IUsbManager.aidl @@ -126,6 +126,9 @@ interface IUsbManager /* Gets the current screen unlocked functions. */ long getScreenUnlockedFunctions(); + /* Resets the USB gadget. */ + void resetUsbGadget(); + /* Get the functionfs control handle for the given function. Usb * descriptors will already be written, and the handle will be * ready to use. diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index 67fdda37ed3b..827353b1802c 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -794,6 +794,25 @@ public class UsbManager { } /** + * Resets the USB Gadget. + * <p> + * Performs USB data stack reset through USB Gadget HAL. + * It will force USB data connection reset. The connection will disconnect and reconnect. + * </p> + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MANAGE_USB) + public void resetUsbGadget() { + try { + mService.resetUsbGadget(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Returns a list of physical USB ports on the device. * <p> * This list is guaranteed to contain all dual-role USB Type C ports but it might diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index 4d5fabbae961..2a441de067c6 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -221,7 +221,9 @@ class IInputMethodWrapper extends IInputMethod.Stub inputMethod.revokeSession((InputMethodSession)msg.obj); return; case DO_SHOW_SOFT_INPUT: - inputMethod.showSoftInput(msg.arg1, (ResultReceiver)msg.obj); + SomeArgs args = (SomeArgs)msg.obj; + inputMethod.showSoftInputWithToken( + msg.arg1, (ResultReceiver) args.arg2, (IBinder) args.arg1); return; case DO_HIDE_SOFT_INPUT: inputMethod.hideSoftInput(msg.arg1, (ResultReceiver)msg.obj); @@ -230,10 +232,11 @@ class IInputMethodWrapper extends IInputMethod.Stub inputMethod.changeInputMethodSubtype((InputMethodSubtype)msg.obj); return; case DO_CREATE_INLINE_SUGGESTIONS_REQUEST: - SomeArgs args = (SomeArgs) msg.obj; + args = (SomeArgs) msg.obj; inputMethod.onCreateInlineSuggestionsRequest((ComponentName) args.arg1, (AutofillId) args.arg2, (IInlineSuggestionsRequestCallback) args.arg3); return; + } Log.w(TAG, "Unhandled message code: " + msg.what); } @@ -371,9 +374,9 @@ class IInputMethodWrapper extends IInputMethod.Stub @BinderThread @Override - public void showSoftInput(int flags, ResultReceiver resultReceiver) { - mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_SHOW_SOFT_INPUT, - flags, resultReceiver)); + public void showSoftInput(IBinder showInputToken, int flags, ResultReceiver resultReceiver) { + mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_SHOW_SOFT_INPUT, + flags, showInputToken, resultReceiver)); } @BinderThread diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 8e52ee944c1c..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; @@ -46,11 +46,13 @@ import android.database.ContentObserver; import android.graphics.Rect; import android.graphics.Region; import android.net.Uri; +import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.SystemClock; @@ -450,6 +452,16 @@ public class InputMethodService extends AbstractInputMethodService { @Nullable private InlineSuggestionsRequestInfo mInlineSuggestionsRequestInfo = null; + /** + * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput} + * The original app window token is passed from client app window. + * {@link com.android.server.inputmethod.InputMethodManagerService} creates a unique dummy + * token to identify this window. + * This dummy token is only valid for a single call to {@link InputMethodImpl#showSoftInput}, + * after which it is set null until next call. + */ + private IBinder mCurShowInputToken; + private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true); final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> { @@ -491,6 +503,9 @@ public class InputMethodService extends AbstractInputMethodService { * all of the standard behavior for an input method. */ public class InputMethodImpl extends AbstractInputMethodImpl { + + private boolean mSystemCallingShowSoftInput; + /** * {@inheritDoc} * @hide @@ -659,11 +674,33 @@ public class InputMethodService extends AbstractInputMethodService { /** * {@inheritDoc} + * @hide + */ + @MainThread + @Override + public void showSoftInputWithToken(int flags, ResultReceiver resultReceiver, + IBinder showInputToken) { + mSystemCallingShowSoftInput = true; + mCurShowInputToken = showInputToken; + showSoftInput(flags, resultReceiver); + mCurShowInputToken = null; + mSystemCallingShowSoftInput = false; + } + + /** + * {@inheritDoc} */ @MainThread @Override public void showSoftInput(int flags, ResultReceiver resultReceiver) { if (DEBUG) Log.v(TAG, "showSoftInput()"); + // TODO(b/148086656): Disallow IME developers from calling InputMethodImpl methods. + if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R + && !mSystemCallingShowSoftInput) { + Log.e(TAG," IME shouldn't call showSoftInput on itself." + + " Use requestShowSelf(int) itself"); + return; + } final boolean wasVisible = mIsPreRendered ? mDecorViewVisible && mWindowVisible : isInputViewShown(); if (dispatchOnShowInputRequested(flags, false)) { @@ -698,6 +735,15 @@ public class InputMethodService extends AbstractInputMethodService { public void changeInputMethodSubtype(InputMethodSubtype subtype) { dispatchOnCurrentInputMethodSubtypeChanged(subtype); } + + /** + * {@inheritDoc} + * @hide + */ + @Override + public void setCurrentShowInputToken(IBinder showInputToken) { + mCurShowInputToken = showInputToken; + } } // TODO(b/137800469): Add detailed docs explaining the inline suggestions process. @@ -1194,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( @@ -2181,7 +2227,7 @@ public class InputMethodService extends AbstractInputMethodService { if (!isVisibilityAppliedUsingInsetsConsumer()) { return; } - mPrivOps.applyImeVisibility(setVisible); + mPrivOps.applyImeVisibility(mCurShowInputToken, setVisible); } private boolean isVisibilityAppliedUsingInsetsConsumer() { diff --git a/core/java/android/net/CaptivePortalData.aidl b/core/java/android/net/CaptivePortalData.aidl new file mode 100644 index 000000000000..1d57ee759136 --- /dev/null +++ b/core/java/android/net/CaptivePortalData.aidl @@ -0,0 +1,19 @@ +/* + * 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.net; + +@JavaOnlyStableParcelable parcelable CaptivePortalData; diff --git a/core/java/android/net/CaptivePortalData.java b/core/java/android/net/CaptivePortalData.java new file mode 100644 index 000000000000..1357803a6cb8 --- /dev/null +++ b/core/java/android/net/CaptivePortalData.java @@ -0,0 +1,281 @@ +/* + * 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.net; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Metadata sent by captive portals, see https://www.ietf.org/id/draft-ietf-capport-api-03.txt. + * @hide + */ +@SystemApi +@TestApi +public final class CaptivePortalData implements Parcelable { + private final long mRefreshTimeMillis; + @Nullable + private final Uri mUserPortalUrl; + @Nullable + private final Uri mVenueInfoUrl; + private final boolean mIsSessionExtendable; + private final long mByteLimit; + private final long mExpiryTimeMillis; + private final boolean mCaptive; + + private CaptivePortalData(long refreshTimeMillis, Uri userPortalUrl, Uri venueInfoUrl, + boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive) { + mRefreshTimeMillis = refreshTimeMillis; + mUserPortalUrl = userPortalUrl; + mVenueInfoUrl = venueInfoUrl; + mIsSessionExtendable = isSessionExtendable; + mByteLimit = byteLimit; + mExpiryTimeMillis = expiryTimeMillis; + mCaptive = captive; + } + + private CaptivePortalData(Parcel p) { + this(p.readLong(), p.readParcelable(null), p.readParcelable(null), p.readBoolean(), + p.readLong(), p.readLong(), p.readBoolean()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeLong(mRefreshTimeMillis); + dest.writeParcelable(mUserPortalUrl, 0); + dest.writeParcelable(mVenueInfoUrl, 0); + dest.writeBoolean(mIsSessionExtendable); + dest.writeLong(mByteLimit); + dest.writeLong(mExpiryTimeMillis); + dest.writeBoolean(mCaptive); + } + + /** + * A builder to create new {@link CaptivePortalData}. + */ + public static class Builder { + private long mRefreshTime; + private Uri mUserPortalUrl; + private Uri mVenueInfoUrl; + private boolean mIsSessionExtendable; + private long mBytesRemaining = -1; + private long mExpiryTime = -1; + private boolean mCaptive; + + /** + * Create an empty builder. + */ + public Builder() {} + + /** + * Create a builder copying all data from existing {@link CaptivePortalData}. + */ + public Builder(@Nullable CaptivePortalData data) { + if (data == null) return; + setRefreshTime(data.mRefreshTimeMillis) + .setUserPortalUrl(data.mUserPortalUrl) + .setVenueInfoUrl(data.mVenueInfoUrl) + .setSessionExtendable(data.mIsSessionExtendable) + .setBytesRemaining(data.mByteLimit) + .setExpiryTime(data.mExpiryTimeMillis) + .setCaptive(data.mCaptive); + } + + /** + * Set the time at which data was last refreshed, as per {@link System#currentTimeMillis()}. + */ + @NonNull + public Builder setRefreshTime(long refreshTime) { + mRefreshTime = refreshTime; + return this; + } + + /** + * Set the URL to be used for users to login to the portal, if captive. + */ + @NonNull + public Builder setUserPortalUrl(@Nullable Uri userPortalUrl) { + mUserPortalUrl = userPortalUrl; + return this; + } + + /** + * Set the URL that can be used by users to view information about the network venue. + */ + @NonNull + public Builder setVenueInfoUrl(@Nullable Uri venueInfoUrl) { + mVenueInfoUrl = venueInfoUrl; + return this; + } + + /** + * Set whether the portal supports extending a user session on the portal URL page. + */ + @NonNull + public Builder setSessionExtendable(boolean sessionExtendable) { + mIsSessionExtendable = sessionExtendable; + return this; + } + + /** + * Set the number of bytes remaining on the network before the portal closes. + */ + @NonNull + public Builder setBytesRemaining(long bytesRemaining) { + mBytesRemaining = bytesRemaining; + return this; + } + + /** + * Set the time at the session will expire, as per {@link System#currentTimeMillis()}. + */ + @NonNull + public Builder setExpiryTime(long expiryTime) { + mExpiryTime = expiryTime; + return this; + } + + /** + * Set whether the network is captive (portal closed). + */ + @NonNull + public Builder setCaptive(boolean captive) { + mCaptive = captive; + return this; + } + + /** + * Create a new {@link CaptivePortalData}. + */ + @NonNull + public CaptivePortalData build() { + return new CaptivePortalData(mRefreshTime, mUserPortalUrl, mVenueInfoUrl, + mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive); + } + } + + /** + * Get the time at which data was last refreshed, as per {@link System#currentTimeMillis()}. + */ + public long getRefreshTimeMillis() { + return mRefreshTimeMillis; + } + + /** + * Get the URL to be used for users to login to the portal, or extend their session if + * {@link #isSessionExtendable()} is true. + */ + @Nullable + public Uri getUserPortalUrl() { + return mUserPortalUrl; + } + + /** + * Get the URL that can be used by users to view information about the network venue. + */ + @Nullable + public Uri getVenueInfoUrl() { + return mVenueInfoUrl; + } + + /** + * Indicates whether the user portal URL can be used to extend sessions, when the user is logged + * in and the session has a time or byte limit. + */ + public boolean isSessionExtendable() { + return mIsSessionExtendable; + } + + /** + * Get the remaining bytes on the captive portal session, at the time {@link CaptivePortalData} + * was refreshed. This may be different from the limit currently enforced by the portal. + * @return The byte limit, or -1 if not set. + */ + public long getByteLimit() { + return mByteLimit; + } + + /** + * Get the time at the session will expire, as per {@link System#currentTimeMillis()}. + * @return The expiry time, or -1 if unset. + */ + public long getExpiryTimeMillis() { + return mExpiryTimeMillis; + } + + /** + * Get whether the network is captive (portal closed). + */ + public boolean isCaptive() { + return mCaptive; + } + + @NonNull + public static final Creator<CaptivePortalData> CREATOR = new Creator<CaptivePortalData>() { + @Override + public CaptivePortalData createFromParcel(Parcel source) { + return new CaptivePortalData(source); + } + + @Override + public CaptivePortalData[] newArray(int size) { + return new CaptivePortalData[size]; + } + }; + + @Override + public int hashCode() { + return Objects.hash(mRefreshTimeMillis, mUserPortalUrl, mVenueInfoUrl, + mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof CaptivePortalData)) return false; + final CaptivePortalData other = (CaptivePortalData) obj; + return mRefreshTimeMillis == other.mRefreshTimeMillis + && Objects.equals(mUserPortalUrl, other.mUserPortalUrl) + && Objects.equals(mVenueInfoUrl, other.mVenueInfoUrl) + && mIsSessionExtendable == other.mIsSessionExtendable + && mByteLimit == other.mByteLimit + && mExpiryTimeMillis == other.mExpiryTimeMillis + && mCaptive == other.mCaptive; + } + + @Override + public String toString() { + return "CaptivePortalData {" + + "refreshTime: " + mRefreshTimeMillis + + ", userPortalUrl: " + mUserPortalUrl + + ", venueInfoUrl: " + mVenueInfoUrl + + ", isSessionExtendable: " + mIsSessionExtendable + + ", byteLimit: " + mByteLimit + + ", expiryTime: " + mExpiryTimeMillis + + ", captive: " + mCaptive + + "}"; + } +} 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..753e754602d1 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -33,6 +33,7 @@ import android.content.Context; import android.content.Intent; import android.net.IpSecManager.UdpEncapsulationSocket; import android.net.SocketKeepalive.Callback; +import android.net.TetheringManager.TetheringEventCallback; import android.os.Binder; import android.os.Build; import android.os.Build.VERSION_CODES; @@ -58,6 +59,7 @@ import android.util.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 +77,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 +487,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 +2372,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 +2387,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 +2408,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 +2422,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(); } @@ -2467,6 +2479,7 @@ public class ConnectivityManager { * {@hide} */ @UnsupportedAppUsage + @Deprecated public int untether(String iface) { return getTetheringManager().untether(iface); } @@ -2487,6 +2500,7 @@ public class ConnectivityManager { * * @return a boolean - {@code true} indicating Tethering is supported. * + * @deprecated Use {@link TetheringEventCallback#onTetheringSupported(boolean)} instead. * {@hide} */ @SystemApi @@ -2573,9 +2587,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 +2602,11 @@ public class ConnectivityManager { * Callback for use with {@link registerTetheringEventCallback} to find out tethering * upstream status. * - *@hide + * @deprecated Use {@line TetheringManager#OnTetheringEventCallback} instead. + * @hide */ @SystemApi + @Deprecated public abstract static class OnTetheringEventCallback { /** @@ -2600,6 +2619,10 @@ public 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 +2631,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 {@line TetheringManager#registerTetheringEventCallback} instead. * @hide */ @SystemApi + @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback( @NonNull @CallbackExecutor Executor executor, @NonNull final OnTetheringEventCallback callback) { Preconditions.checkNotNull(callback, "OnTetheringEventCallback cannot be null."); - getTetheringManager().registerTetheringEventCallback(executor, callback); + final TetheringEventCallback tetherCallback = + new TetheringEventCallback() { + @Override + public void onUpstreamChanged(@Nullable Network network) { + callback.onUpstreamChanged(network); + } + }; + + synchronized (mTetheringEventCallbacks) { + mTetheringEventCallbacks.put(callback, tetherCallback); + getTetheringManager().registerTetheringEventCallback(executor, tetherCallback); + } } /** @@ -2625,13 +2662,21 @@ 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 +2688,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 +2706,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 +2724,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(); } @@ -2705,37 +2757,104 @@ public class ConnectivityManager { return getTetheringManager().setUsbTethering(enable); } - /** {@hide} */ + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_NO_ERROR}. + * {@hide} + */ @SystemApi - public static final int TETHER_ERROR_NO_ERROR = 0; - /** {@hide} */ - public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; - /** {@hide} */ - public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; - /** {@hide} */ - public static final int TETHER_ERROR_UNSUPPORTED = 3; - /** {@hide} */ - public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; - /** {@hide} */ - public static final int TETHER_ERROR_MASTER_ERROR = 5; - /** {@hide} */ - public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; - /** {@hide} */ - public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; - /** {@hide} */ - public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; - /** {@hide} */ - public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; - /** {@hide} */ - public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; - /** {@hide} */ + @Deprecated + public static final int TETHER_ERROR_NO_ERROR = TetheringManager.TETHER_ERROR_NO_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_UNKNOWN_IFACE}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_UNKNOWN_IFACE = + TetheringManager.TETHER_ERROR_UNKNOWN_IFACE; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_SERVICE_UNAVAIL}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_SERVICE_UNAVAIL = + TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_UNSUPPORTED}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_UNSUPPORTED = TetheringManager.TETHER_ERROR_UNSUPPORTED; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_UNAVAIL_IFACE}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_UNAVAIL_IFACE = + TetheringManager.TETHER_ERROR_UNAVAIL_IFACE; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_MASTER_ERROR}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_MASTER_ERROR = TetheringManager.TETHER_ERROR_MASTER_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_TETHER_IFACE_ERROR}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_TETHER_IFACE_ERROR = + TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_UNTETHER_IFACE_ERROR}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = + TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_ENABLE_NAT_ERROR}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_ENABLE_NAT_ERROR = + TetheringManager.TETHER_ERROR_ENABLE_NAT_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_DISABLE_NAT_ERROR}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_DISABLE_NAT_ERROR = + TetheringManager.TETHER_ERROR_DISABLE_NAT_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_IFACE_CFG_ERROR}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_IFACE_CFG_ERROR = + TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_PROVISION_FAILED}. + * {@hide} + */ @SystemApi - public static final int TETHER_ERROR_PROVISION_FAILED = 11; - /** {@hide} */ - public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; - /** {@hide} */ + @Deprecated + public static final int TETHER_ERROR_PROVISION_FAILED = + TetheringManager.TETHER_ERROR_PROVISION_FAILED; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_DHCPSERVER_ERROR}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_DHCPSERVER_ERROR = + TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_ENTITLEMENT_UNKNOWN}. + * {@hide} + */ @SystemApi - public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; + @Deprecated + public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = + TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN; /** * Get a more detailed error code after a Tethering or Untethering @@ -2745,10 +2864,12 @@ 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 +2887,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 +2922,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/LinkProperties.java b/core/java/android/net/LinkProperties.java index be8e561fd044..e83f5e4e810d 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -72,6 +72,14 @@ public final class LinkProperties implements Parcelable { private String mTcpBufferSizes; private IpPrefix mNat64Prefix; private boolean mWakeOnLanSupported; + private Uri mCaptivePortalApiUrl; + private CaptivePortalData mCaptivePortalData; + + /** + * Indicates whether parceling should preserve fields that are set based on permissions of + * the process receiving the {@link LinkProperties}. + */ + private final transient boolean mParcelSensitiveFields; private static final int MIN_MTU = 68; private static final int MIN_MTU_V6 = 1280; @@ -146,6 +154,7 @@ public final class LinkProperties implements Parcelable { * Constructs a new {@code LinkProperties} with default values. */ public LinkProperties() { + mParcelSensitiveFields = false; } /** @@ -154,26 +163,32 @@ public final class LinkProperties implements Parcelable { @SystemApi @TestApi public LinkProperties(@Nullable LinkProperties source) { - if (source != null) { - mIfaceName = source.mIfaceName; - mLinkAddresses.addAll(source.mLinkAddresses); - mDnses.addAll(source.mDnses); - mValidatedPrivateDnses.addAll(source.mValidatedPrivateDnses); - mUsePrivateDns = source.mUsePrivateDns; - mPrivateDnsServerName = source.mPrivateDnsServerName; - mPcscfs.addAll(source.mPcscfs); - mDomains = source.mDomains; - mRoutes.addAll(source.mRoutes); - mHttpProxy = (source.mHttpProxy == null) ? null : new ProxyInfo(source.mHttpProxy); - for (LinkProperties l: source.mStackedLinks.values()) { - addStackedLink(l); - } - setMtu(source.mMtu); - setDhcpServerAddress(source.getDhcpServerAddress()); - mTcpBufferSizes = source.mTcpBufferSizes; - mNat64Prefix = source.mNat64Prefix; - mWakeOnLanSupported = source.mWakeOnLanSupported; + this(source, false /* parcelSensitiveFields */); + } + + private LinkProperties(@Nullable LinkProperties source, boolean parcelSensitiveFields) { + mParcelSensitiveFields = parcelSensitiveFields; + if (source == null) return; + mIfaceName = source.mIfaceName; + mLinkAddresses.addAll(source.mLinkAddresses); + mDnses.addAll(source.mDnses); + mValidatedPrivateDnses.addAll(source.mValidatedPrivateDnses); + mUsePrivateDns = source.mUsePrivateDns; + mPrivateDnsServerName = source.mPrivateDnsServerName; + mPcscfs.addAll(source.mPcscfs); + mDomains = source.mDomains; + mRoutes.addAll(source.mRoutes); + mHttpProxy = (source.mHttpProxy == null) ? null : new ProxyInfo(source.mHttpProxy); + for (LinkProperties l: source.mStackedLinks.values()) { + addStackedLink(l); } + setMtu(source.mMtu); + setDhcpServerAddress(source.getDhcpServerAddress()); + mTcpBufferSizes = source.mTcpBufferSizes; + mNat64Prefix = source.mNat64Prefix; + mWakeOnLanSupported = source.mWakeOnLanSupported; + mCaptivePortalApiUrl = source.mCaptivePortalApiUrl; + mCaptivePortalData = source.mCaptivePortalData; } /** @@ -832,6 +847,11 @@ public final class LinkProperties implements Parcelable { * Clears this object to its initial state. */ public void clear() { + if (mParcelSensitiveFields) { + throw new UnsupportedOperationException( + "Cannot clear LinkProperties when parcelSensitiveFields is set"); + } + mIfaceName = null; mLinkAddresses.clear(); mDnses.clear(); @@ -847,6 +867,8 @@ public final class LinkProperties implements Parcelable { mTcpBufferSizes = null; mNat64Prefix = null; mWakeOnLanSupported = false; + mCaptivePortalApiUrl = null; + mCaptivePortalData = null; } /** @@ -917,6 +939,14 @@ public final class LinkProperties implements Parcelable { resultJoiner.add(mDhcpServerAddress.toString()); } + if (mCaptivePortalApiUrl != null) { + resultJoiner.add("CaptivePortalApiUrl: " + mCaptivePortalApiUrl); + } + + if (mCaptivePortalData != null) { + resultJoiner.add("CaptivePortalData: " + mCaptivePortalData); + } + if (mTcpBufferSizes != null) { resultJoiner.add("TcpBufferSizes:"); resultJoiner.add(mTcpBufferSizes); @@ -1437,6 +1467,28 @@ public final class LinkProperties implements Parcelable { } /** + * Compares this {@code LinkProperties}'s CaptivePortalApiUrl against the target. + * + * @param target LinkProperties to compare. + * @return {@code true} if both are identical, {@code false} otherwise. + * @hide + */ + public boolean isIdenticalCaptivePortalApiUrl(LinkProperties target) { + return Objects.equals(mCaptivePortalApiUrl, target.mCaptivePortalApiUrl); + } + + /** + * Compares this {@code LinkProperties}'s CaptivePortalData against the target. + * + * @param target LinkProperties to compare. + * @return {@code true} if both are identical, {@code false} otherwise. + * @hide + */ + public boolean isIdenticalCaptivePortalData(LinkProperties target) { + return Objects.equals(mCaptivePortalData, target.mCaptivePortalData); + } + + /** * Set whether the network interface supports WakeOnLAN * * @param supported WakeOnLAN supported value @@ -1457,6 +1509,73 @@ public final class LinkProperties implements Parcelable { } /** + * Set the URL of the captive portal API endpoint to get more information about the network. + * @hide + */ + @SystemApi + @TestApi + public void setCaptivePortalApiUrl(@Nullable Uri url) { + mCaptivePortalApiUrl = url; + } + + /** + * Get the URL of the captive portal API endpoint to get more information about the network. + * + * <p>This is null unless the application has + * {@link android.Manifest.permission.NETWORK_SETTINGS} or + * {@link NetworkStack#PERMISSION_MAINLINE_NETWORK_STACK} permissions, and the network provided + * the URL. + * @hide + */ + @SystemApi + @TestApi + @Nullable + public Uri getCaptivePortalApiUrl() { + return mCaptivePortalApiUrl; + } + + /** + * Set the CaptivePortalData obtained from the captive portal API (RFC7710bis). + * @hide + */ + @SystemApi + @TestApi + public void setCaptivePortalData(@Nullable CaptivePortalData data) { + mCaptivePortalData = data; + } + + /** + * Get the CaptivePortalData obtained from the captive portal API (RFC7710bis). + * + * <p>This is null unless the application has + * {@link android.Manifest.permission.NETWORK_SETTINGS} or + * {@link NetworkStack#PERMISSION_MAINLINE_NETWORK_STACK} permissions. + * @hide + */ + @SystemApi + @TestApi + @Nullable + public CaptivePortalData getCaptivePortalData() { + return mCaptivePortalData; + } + + /** + * Create a copy of this {@link LinkProperties} that will preserve fields that were set + * based on the permissions of the process that received this {@link LinkProperties}. + * + * <p>By default {@link LinkProperties} does not preserve such fields during parceling, as + * they should not be shared outside of the process that receives them without appropriate + * checks. + * @hide + */ + @SystemApi + @TestApi + @NonNull + public LinkProperties makeSensitiveFieldsParcelingCopy() { + return new LinkProperties(this, true /* parcelSensitiveFields */); + } + + /** * Compares this {@code LinkProperties} instance against the target * LinkProperties in {@code obj}. Two LinkPropertieses are equal if * all their fields are equal in values. @@ -1495,7 +1614,9 @@ public final class LinkProperties implements Parcelable { && isIdenticalMtu(target) && isIdenticalTcpBufferSizes(target) && isIdenticalNat64Prefix(target) - && isIdenticalWakeOnLan(target); + && isIdenticalWakeOnLan(target) + && isIdenticalCaptivePortalApiUrl(target) + && isIdenticalCaptivePortalData(target); } /** @@ -1593,7 +1714,8 @@ public final class LinkProperties implements Parcelable { + mPcscfs.size() * 67 + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode()) + Objects.hash(mNat64Prefix) - + (mWakeOnLanSupported ? 71 : 0); + + (mWakeOnLanSupported ? 71 : 0) + + Objects.hash(mCaptivePortalApiUrl, mCaptivePortalData); } /** @@ -1632,6 +1754,8 @@ public final class LinkProperties implements Parcelable { dest.writeList(stackedLinks); dest.writeBoolean(mWakeOnLanSupported); + dest.writeParcelable(mParcelSensitiveFields ? mCaptivePortalApiUrl : null, 0); + dest.writeParcelable(mParcelSensitiveFields ? mCaptivePortalData : null, 0); } private static void writeAddresses(@NonNull Parcel dest, @NonNull List<InetAddress> list) { @@ -1723,6 +1847,9 @@ public final class LinkProperties implements Parcelable { netProp.addStackedLink(stackedLink); } netProp.setWakeOnLanSupported(in.readBoolean()); + + netProp.setCaptivePortalApiUrl(in.readParcelable(null)); + netProp.setCaptivePortalData(in.readParcelable(null)); return netProp; } 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/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 739e8178e68e..738070b09264 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -1283,6 +1283,7 @@ public final class NetworkCapabilities implements Parcelable { * Gets the SSID of this network, or null if none or unknown. * @hide */ + @SystemApi public @Nullable String getSSID() { return mSSID; } diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java deleted file mode 100644 index e27103755e6d..000000000000 --- a/core/java/android/net/NetworkFactory.java +++ /dev/null @@ -1,453 +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 android.net; - -import android.annotation.NonNull; -import android.compat.annotation.UnsupportedAppUsage; -import android.content.Context; -import android.os.Build; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.Messenger; -import android.util.Log; -import android.util.SparseArray; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IndentingPrintWriter; -import com.android.internal.util.Protocol; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * A NetworkFactory is an entity that creates NetworkAgent objects. - * The bearers register with ConnectivityService using {@link #register} and - * their factory will start receiving scored NetworkRequests. NetworkRequests - * can be filtered 3 ways: by NetworkCapabilities, by score and more complexly by - * overridden function. All of these can be dynamic - changing NetworkCapabilities - * or score forces re-evaluation of all current requests. - * - * If any requests pass the filter some overrideable functions will be called. - * If the bearer only cares about very simple start/stopNetwork callbacks, those - * functions can be overridden. If the bearer needs more interaction, it can - * override addNetworkRequest and removeNetworkRequest which will give it each - * request that passes their current filters. - * @hide - **/ -public class NetworkFactory extends Handler { - /* TODO: delete when all callers have migrated to NetworkProvider IDs. */ - public static class SerialNumber { - // Guard used by no network factory. - public static final int NONE = -1; - // A hardcoded serial number for NetworkAgents representing VPNs. These agents are - // not created by any factory, so they use this constant for clarity instead of NONE. - public static final int VPN = -2; - private static final AtomicInteger sNetworkFactorySerialNumber = new AtomicInteger(1); - /** Returns a unique serial number for a factory. */ - public static final int nextSerialNumber() { - return sNetworkFactorySerialNumber.getAndIncrement(); - } - } - - private static final boolean DBG = true; - private static final boolean VDBG = false; - - private static final int BASE = Protocol.BASE_NETWORK_FACTORY; - /** - * Pass a network request to the bearer. If the bearer believes it can - * satisfy the request it should connect to the network and create a - * NetworkAgent. Once the NetworkAgent is fully functional it will - * register itself with ConnectivityService using registerNetworkAgent. - * If the bearer cannot immediately satisfy the request (no network, - * user disabled the radio, lower-scored network) it should remember - * any NetworkRequests it may be able to satisfy in the future. It may - * disregard any that it will never be able to service, for example - * those requiring a different bearer. - * msg.obj = NetworkRequest - * msg.arg1 = score - the score of the network currently satisfying this - * request. If this bearer knows in advance it cannot - * exceed this score it should not try to connect, holding the request - * for the future. - * Note that subsequent events may give a different (lower - * or higher) score for this request, transmitted to each - * NetworkFactory through additional CMD_REQUEST_NETWORK msgs - * with the same NetworkRequest but an updated score. - * Also, network conditions may change for this bearer - * allowing for a better score in the future. - * msg.arg2 = the ID of the NetworkProvider currently responsible for the - * NetworkAgent handling this request, or NetworkProvider.ID_NONE if none. - */ - public static final int CMD_REQUEST_NETWORK = BASE; - - /** - * Cancel a network request - * msg.obj = NetworkRequest - */ - public static final int CMD_CANCEL_REQUEST = BASE + 1; - - /** - * Internally used to set our best-guess score. - * msg.arg1 = new score - */ - private static final int CMD_SET_SCORE = BASE + 2; - - /** - * Internally used to set our current filter for coarse bandwidth changes with - * technology changes. - * msg.obj = new filter - */ - private static final int CMD_SET_FILTER = BASE + 3; - - private final Context mContext; - private final ArrayList<Message> mPreConnectedQueue = new ArrayList<Message>(); - private final String LOG_TAG; - - private final SparseArray<NetworkRequestInfo> mNetworkRequests = - new SparseArray<NetworkRequestInfo>(); - - private int mScore; - private NetworkCapabilities mCapabilityFilter; - - private int mRefCount = 0; - private Messenger mMessenger = null; - private NetworkProvider mProvider = null; - private int mProviderId; - - @UnsupportedAppUsage - public NetworkFactory(Looper looper, Context context, String logTag, - NetworkCapabilities filter) { - super(looper); - LOG_TAG = logTag; - mContext = context; - mCapabilityFilter = filter; - } - - public void register() { - if (mProvider != null) { - Log.e(LOG_TAG, "Ignoring attempt to register already-registered NetworkFactory"); - return; - } - if (DBG) log("Registering NetworkFactory"); - - mProvider = new NetworkProvider(mContext, NetworkFactory.this.getLooper(), LOG_TAG) { - @Override - public void onNetworkRequested(@NonNull NetworkRequest request, int score, - int servingProviderId) { - handleAddRequest((NetworkRequest) request, score, servingProviderId); - } - - @Override - public void onRequestWithdrawn(@NonNull NetworkRequest request) { - handleRemoveRequest(request); - } - }; - - mMessenger = new Messenger(this); - mProviderId = ConnectivityManager.from(mContext).registerNetworkProvider(mProvider); - } - - public void unregister() { - if (mProvider == null) { - Log.e(LOG_TAG, "Ignoring attempt to unregister unregistered NetworkFactory"); - return; - } - if (DBG) log("Unregistering NetworkFactory"); - - ConnectivityManager.from(mContext).unregisterNetworkProvider(mProvider); - mProvider = null; - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case CMD_REQUEST_NETWORK: { - handleAddRequest((NetworkRequest) msg.obj, msg.arg1, msg.arg2); - break; - } - case CMD_CANCEL_REQUEST: { - handleRemoveRequest((NetworkRequest) msg.obj); - break; - } - case CMD_SET_SCORE: { - handleSetScore(msg.arg1); - break; - } - case CMD_SET_FILTER: { - handleSetFilter((NetworkCapabilities) msg.obj); - break; - } - } - } - - private class NetworkRequestInfo { - public final NetworkRequest request; - public int score; - public boolean requested; // do we have a request outstanding, limited by score - public int providerId; - - NetworkRequestInfo(NetworkRequest request, int score, int providerId) { - this.request = request; - this.score = score; - this.requested = false; - this.providerId = providerId; - } - - @Override - public String toString() { - return "{" + request + ", score=" + score + ", requested=" + requested + "}"; - } - } - - /** - * Add a NetworkRequest that the bearer may want to attempt to satisfy. - * @see #CMD_REQUEST_NETWORK - * - * @param request the request to handle. - * @param score the score of the NetworkAgent currently satisfying this request. - */ - // TODO : remove this method. It is a stopgap measure to help sheperding a number - // of dependent changes that would conflict throughout the automerger graph. Having this - // temporarily helps with the process of going through with all these dependent changes across - // the entire tree. - @VisibleForTesting - protected void handleAddRequest(NetworkRequest request, int score) { - handleAddRequest(request, score, NetworkProvider.ID_NONE); - } - - /** - * Add a NetworkRequest that the bearer may want to attempt to satisfy. - * @see #CMD_REQUEST_NETWORK - * - * @param request the request to handle. - * @param score the score of the NetworkAgent currently satisfying this request. - * @param servingProviderId the ID of the NetworkProvider that created the NetworkAgent - * currently satisfying this request. - */ - @VisibleForTesting - protected void handleAddRequest(NetworkRequest request, int score, int servingProviderId) { - NetworkRequestInfo n = mNetworkRequests.get(request.requestId); - if (n == null) { - if (DBG) { - log("got request " + request + " with score " + score - + " and providerId " + servingProviderId); - } - n = new NetworkRequestInfo(request, score, servingProviderId); - mNetworkRequests.put(n.request.requestId, n); - } else { - if (VDBG) { - log("new score " + score + " for exisiting request " + request - + " and providerId " + servingProviderId); - } - n.score = score; - n.providerId = servingProviderId; - } - if (VDBG) log(" my score=" + mScore + ", my filter=" + mCapabilityFilter); - - evalRequest(n); - } - - @VisibleForTesting - protected void handleRemoveRequest(NetworkRequest request) { - NetworkRequestInfo n = mNetworkRequests.get(request.requestId); - if (n != null) { - mNetworkRequests.remove(request.requestId); - if (n.requested) releaseNetworkFor(n.request); - } - } - - private void handleSetScore(int score) { - mScore = score; - evalRequests(); - } - - private void handleSetFilter(NetworkCapabilities netCap) { - mCapabilityFilter = netCap; - evalRequests(); - } - - /** - * Overridable function to provide complex filtering. - * Called for every request every time a new NetworkRequest is seen - * and whenever the filterScore or filterNetworkCapabilities change. - * - * acceptRequest can be overridden to provide complex filter behavior - * for the incoming requests - * - * For output, this class will call {@link #needNetworkFor} and - * {@link #releaseNetworkFor} for every request that passes the filters. - * If you don't need to see every request, you can leave the base - * implementations of those two functions and instead override - * {@link #startNetwork} and {@link #stopNetwork}. - * - * If you want to see every score fluctuation on every request, set - * your score filter to a very high number and watch {@link #needNetworkFor}. - * - * @return {@code true} to accept the request. - */ - public boolean acceptRequest(NetworkRequest request, int score) { - return true; - } - - private void evalRequest(NetworkRequestInfo n) { - if (VDBG) { - log("evalRequest"); - log(" n.requests = " + n.requested); - log(" n.score = " + n.score); - log(" mScore = " + mScore); - log(" n.providerId = " + n.providerId); - log(" mProviderId = " + mProviderId); - } - if (shouldNeedNetworkFor(n)) { - if (VDBG) log(" needNetworkFor"); - needNetworkFor(n.request, n.score); - n.requested = true; - } else if (shouldReleaseNetworkFor(n)) { - if (VDBG) log(" releaseNetworkFor"); - releaseNetworkFor(n.request); - n.requested = false; - } else { - if (VDBG) log(" done"); - } - } - - private boolean shouldNeedNetworkFor(NetworkRequestInfo n) { - // If this request is already tracked, it doesn't qualify for need - return !n.requested - // If the score of this request is higher or equal to that of this factory and some - // other factory is responsible for it, then this factory should not track the request - // because it has no hope of satisfying it. - && (n.score < mScore || n.providerId == mProviderId) - // If this factory can't satisfy the capability needs of this request, then it - // should not be tracked. - && n.request.networkCapabilities.satisfiedByNetworkCapabilities(mCapabilityFilter) - // Finally if the concrete implementation of the factory rejects the request, then - // don't track it. - && acceptRequest(n.request, n.score); - } - - private boolean shouldReleaseNetworkFor(NetworkRequestInfo n) { - // Don't release a request that's not tracked. - return n.requested - // The request should be released if it can't be satisfied by this factory. That - // means either of the following conditions are met : - // - Its score is too high to be satisfied by this factory and it's not already - // assigned to the factory - // - This factory can't satisfy the capability needs of the request - // - The concrete implementation of the factory rejects the request - && ((n.score > mScore && n.providerId != mProviderId) - || !n.request.networkCapabilities.satisfiedByNetworkCapabilities( - mCapabilityFilter) - || !acceptRequest(n.request, n.score)); - } - - private void evalRequests() { - for (int i = 0; i < mNetworkRequests.size(); i++) { - NetworkRequestInfo n = mNetworkRequests.valueAt(i); - evalRequest(n); - } - } - - /** - * Post a command, on this NetworkFactory Handler, to re-evaluate all - * oustanding requests. Can be called from a factory implementation. - */ - protected void reevaluateAllRequests() { - post(() -> { - evalRequests(); - }); - } - - /** - * Can be called by a factory to release a request as unfulfillable: the request will be - * removed, and the caller will get a - * {@link ConnectivityManager.NetworkCallback#onUnavailable()} callback after this function - * returns. - * - * Note: this should only be called by factory which KNOWS that it is the ONLY factory which - * is able to fulfill this request! - */ - protected void releaseRequestAsUnfulfillableByAnyFactory(NetworkRequest r) { - post(() -> { - if (DBG) log("releaseRequestAsUnfulfillableByAnyFactory: " + r); - ConnectivityManager.from(mContext).declareNetworkRequestUnfulfillable(r); - }); - } - - // override to do simple mode (request independent) - protected void startNetwork() { } - protected void stopNetwork() { } - - // override to do fancier stuff - protected void needNetworkFor(NetworkRequest networkRequest, int score) { - if (++mRefCount == 1) startNetwork(); - } - - protected void releaseNetworkFor(NetworkRequest networkRequest) { - if (--mRefCount == 0) stopNetwork(); - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public void setScoreFilter(int score) { - sendMessage(obtainMessage(CMD_SET_SCORE, score, 0)); - } - - public void setCapabilityFilter(NetworkCapabilities netCap) { - sendMessage(obtainMessage(CMD_SET_FILTER, new NetworkCapabilities(netCap))); - } - - @VisibleForTesting - protected int getRequestCount() { - return mNetworkRequests.size(); - } - - /* TODO: delete when all callers have migrated to NetworkProvider IDs. */ - public int getSerialNumber() { - return mProviderId; - } - - public int getProviderId() { - return mProviderId; - } - - protected void log(String s) { - Log.d(LOG_TAG, s); - } - - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { - final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); - pw.println(toString()); - pw.increaseIndent(); - for (int i = 0; i < mNetworkRequests.size(); i++) { - pw.println(mNetworkRequests.valueAt(i)); - } - pw.decreaseIndent(); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("{").append(LOG_TAG).append(" - mProviderId=") - .append(mProviderId).append(", ScoreFilter=") - .append(mScore).append(", Filter=").append(mCapabilityFilter).append(", requests=") - .append(mNetworkRequests.size()).append(", refCount=").append(mRefCount) - .append("}"); - return sb.toString(); - } -} diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java index d0c536316527..08fe159b276e 100644 --- a/core/java/android/net/NetworkInfo.java +++ b/core/java/android/net/NetworkInfo.java @@ -17,9 +17,11 @@ package android.net; import android.annotation.NonNull; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; +import android.telephony.Annotation.NetworkType; import com.android.internal.annotations.VisibleForTesting; @@ -150,10 +152,19 @@ public class NetworkInfo implements Parcelable { private boolean mIsRoaming; /** - * @hide + * Create a new instance of NetworkInfo. + * + * This may be useful for apps to write unit tests. + * + * @param type the legacy type of the network, as one of the ConnectivityManager.TYPE_* + * constants. + * @param subtype the subtype if applicable, as one of the TelephonyManager.NETWORK_TYPE_* + * constants. + * @param typeName a human-readable string for the network type, or an empty string or null. + * @param subtypeName a human-readable string for the subtype, or an empty string or null. */ - @UnsupportedAppUsage - public NetworkInfo(int type, int subtype, String typeName, String subtypeName) { + public NetworkInfo(int type, @NetworkType int subtype, + @Nullable String typeName, @Nullable String subtypeName) { if (!ConnectivityManager.isNetworkTypeValid(type) && type != ConnectivityManager.TYPE_NONE) { throw new IllegalArgumentException("Invalid network type: " + type); @@ -462,17 +473,19 @@ public class NetworkInfo implements Parcelable { /** * Sets the fine-grained state of the network. + * + * This is only useful for testing. + * * @param detailedState the {@link DetailedState}. * @param reason a {@code String} indicating the reason for the state change, * if one was supplied. May be {@code null}. * @param extraInfo an optional {@code String} providing addditional network state * information passed up from the lower networking layers. * @deprecated Use {@link NetworkCapabilities} instead. - * @hide */ @Deprecated - @UnsupportedAppUsage - public void setDetailedState(DetailedState detailedState, String reason, String extraInfo) { + public void setDetailedState(@NonNull DetailedState detailedState, @Nullable String reason, + @Nullable String extraInfo) { synchronized (this) { this.mDetailedState = detailedState; this.mState = stateMap.get(detailedState); 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/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index de962f8f25e3..01800c6751fb 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -18,6 +18,10 @@ package android.net; import static android.content.pm.PackageManager.GET_SIGNATURES; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.app.ActivityManager; import android.compat.annotation.UnsupportedAppUsage; @@ -38,24 +42,38 @@ import android.util.Range; import com.google.android.collect.Sets; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.time.ZonedDateTime; import java.util.HashSet; import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * Manager for creating and modifying network policy rules. * - * {@hide} + * @hide */ @SystemService(Context.NETWORK_POLICY_SERVICE) +@SystemApi public class NetworkPolicyManager { /* POLICY_* are masks and can be ORed, although currently they are not.*/ - /** No specific network policy, use system default. */ + /** + * No specific network policy, use system default. + * @hide + */ public static final int POLICY_NONE = 0x0; - /** Reject network usage on metered networks when application in background. */ + /** + * Reject network usage on metered networks when application in background. + * @hide + */ public static final int POLICY_REJECT_METERED_BACKGROUND = 0x1; - /** Allow metered network use in the background even when in data usage save mode. */ + /** + * Allow metered network use in the background even when in data usage save mode. + * @hide + */ public static final int POLICY_ALLOW_METERED_BACKGROUND = 0x4; /* @@ -74,49 +92,102 @@ public class NetworkPolicyManager { * * See network-policy-restrictions.md for more info. */ - /** No specific rule was set */ + + /** + * No specific rule was set + * @hide + */ public static final int RULE_NONE = 0; - /** Allow traffic on metered networks. */ + /** + * Allow traffic on metered networks. + * @hide + */ public static final int RULE_ALLOW_METERED = 1 << 0; - /** Temporarily allow traffic on metered networks because app is on foreground. */ + /** + * Temporarily allow traffic on metered networks because app is on foreground. + * @hide + */ public static final int RULE_TEMPORARY_ALLOW_METERED = 1 << 1; - /** Reject traffic on metered networks. */ + /** + * Reject traffic on metered networks. + * @hide + */ public static final int RULE_REJECT_METERED = 1 << 2; - /** Network traffic should be allowed on all networks (metered or non-metered), although - * metered-network restrictions could still apply. */ + /** + * Network traffic should be allowed on all networks (metered or non-metered), although + * metered-network restrictions could still apply. + * @hide + */ public static final int RULE_ALLOW_ALL = 1 << 5; - /** Reject traffic on all networks. */ + /** + * Reject traffic on all networks. + * @hide + */ public static final int RULE_REJECT_ALL = 1 << 6; - /** Mask used to get the {@code RULE_xxx_METERED} rules */ + + /** + * Mask used to get the {@code RULE_xxx_METERED} rules + * @hide + */ public static final int MASK_METERED_NETWORKS = 0b00001111; - /** Mask used to get the {@code RULE_xxx_ALL} rules */ + /** + * Mask used to get the {@code RULE_xxx_ALL} rules + * @hide + */ public static final int MASK_ALL_NETWORKS = 0b11110000; + /** @hide */ public static final int FIREWALL_RULE_DEFAULT = 0; - + /** @hide */ public static final String FIREWALL_CHAIN_NAME_NONE = "none"; + /** @hide */ public static final String FIREWALL_CHAIN_NAME_DOZABLE = "dozable"; + /** @hide */ public static final String FIREWALL_CHAIN_NAME_STANDBY = "standby"; + /** @hide */ public static final String FIREWALL_CHAIN_NAME_POWERSAVE = "powersave"; private static final boolean ALLOW_PLATFORM_APP_POLICY = true; + /** @hide */ public static final int FOREGROUND_THRESHOLD_STATE = ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; /** * {@link Intent} extra that indicates which {@link NetworkTemplate} rule it * applies to. + * @hide */ public static final String EXTRA_NETWORK_TEMPLATE = "android.net.NETWORK_TEMPLATE"; - public static final int OVERRIDE_UNMETERED = 1 << 0; - public static final int OVERRIDE_CONGESTED = 1 << 1; + /** + * Mask used to check if an override value is marked as unmetered. + */ + public static final int SUBSCRIPTION_OVERRIDE_UNMETERED = 1 << 0; + + /** + * Mask used to check if an override value is marked as congested. + */ + public static final int SUBSCRIPTION_OVERRIDE_CONGESTED = 1 << 1; + + /** + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = { "SUBSCRIPTION_OVERRIDE_" }, value = { + SUBSCRIPTION_OVERRIDE_UNMETERED, + SUBSCRIPTION_OVERRIDE_CONGESTED + }) + public @interface SubscriptionOverrideMask {} private final Context mContext; @UnsupportedAppUsage private INetworkPolicyManager mService; + private final Map<SubscriptionCallback, SubscriptionCallbackProxy> + mCallbackMap = new ConcurrentHashMap<>(); + + /** @hide */ public NetworkPolicyManager(Context context, INetworkPolicyManager service) { if (service == null) { throw new IllegalArgumentException("missing INetworkPolicyManager"); @@ -125,6 +196,7 @@ public class NetworkPolicyManager { mService = service; } + /** @hide */ @UnsupportedAppUsage public static NetworkPolicyManager from(Context context) { return (NetworkPolicyManager) context.getSystemService(Context.NETWORK_POLICY_SERVICE); @@ -135,6 +207,7 @@ public class NetworkPolicyManager { * * @param policy should be {@link #POLICY_NONE} or any combination of {@code POLICY_} flags, * although it is not validated. + * @hide */ @UnsupportedAppUsage public void setUidPolicy(int uid, int policy) { @@ -152,6 +225,7 @@ public class NetworkPolicyManager { * * @param policy should be {@link #POLICY_NONE} or any combination of {@code POLICY_} flags, * although it is not validated. + * @hide */ public void addUidPolicy(int uid, int policy) { try { @@ -168,6 +242,7 @@ public class NetworkPolicyManager { * * @param policy should be {@link #POLICY_NONE} or any combination of {@code POLICY_} flags, * although it is not validated. + * @hide */ public void removeUidPolicy(int uid, int policy) { try { @@ -177,6 +252,7 @@ public class NetworkPolicyManager { } } + /** @hide */ @UnsupportedAppUsage public int getUidPolicy(int uid) { try { @@ -186,6 +262,7 @@ public class NetworkPolicyManager { } } + /** @hide */ @UnsupportedAppUsage public int[] getUidsWithPolicy(int policy) { try { @@ -195,6 +272,7 @@ public class NetworkPolicyManager { } } + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public void registerListener(INetworkPolicyListener listener) { try { @@ -204,6 +282,7 @@ public class NetworkPolicyManager { } } + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public void unregisterListener(INetworkPolicyListener listener) { try { @@ -213,6 +292,36 @@ public class NetworkPolicyManager { } } + /** @hide */ + @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) + @SystemApi + public void registerSubscriptionCallback(@NonNull SubscriptionCallback callback) { + if (callback == null) { + throw new NullPointerException("Callback cannot be null."); + } + + final SubscriptionCallbackProxy callbackProxy = new SubscriptionCallbackProxy(callback); + if (null != mCallbackMap.putIfAbsent(callback, callbackProxy)) { + throw new IllegalArgumentException("Callback is already registered."); + } + registerListener(callbackProxy); + } + + /** @hide */ + @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) + @SystemApi + public void unregisterSubscriptionCallback(@NonNull SubscriptionCallback callback) { + if (callback == null) { + throw new NullPointerException("Callback cannot be null."); + } + + final SubscriptionCallbackProxy callbackProxy = mCallbackMap.remove(callback); + if (callbackProxy == null) return; + + unregisterListener(callbackProxy); + } + + /** @hide */ public void setNetworkPolicies(NetworkPolicy[] policies) { try { mService.setNetworkPolicies(policies); @@ -221,6 +330,7 @@ public class NetworkPolicyManager { } } + /** @hide */ @UnsupportedAppUsage public NetworkPolicy[] getNetworkPolicies() { try { @@ -230,6 +340,7 @@ public class NetworkPolicyManager { } } + /** @hide */ @UnsupportedAppUsage public void setRestrictBackground(boolean restrictBackground) { try { @@ -239,6 +350,7 @@ public class NetworkPolicyManager { } } + /** @hide */ @UnsupportedAppUsage public boolean getRestrictBackground() { try { @@ -249,6 +361,62 @@ public class NetworkPolicyManager { } /** + * Override connections to be temporarily marked as either unmetered or congested, + * along with automatic timeouts if desired. + * + * @param subId the subscriber ID this override applies to. + * @param overrideMask the bitmask that specifies which of the overrides is being + * set or cleared. + * @param overrideValue the override values to set or clear. + * @param timeoutMillis the timeout after which the requested override will + * be automatically cleared, or {@code 0} to leave in the + * requested state until explicitly cleared, or the next reboot, + * whichever happens first + * @param callingPackage the name of the package making the call. + */ + public void setSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask, + @SubscriptionOverrideMask int overrideValue, long timeoutMillis, + @NonNull String callingPackage) { + try { + mService.setSubscriptionOverride(subId, overrideMask, overrideValue, timeoutMillis, + callingPackage); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Set the subscription plans for a specific subscriber. + * + * @param subId the subscriber this relationship applies to. + * @param plans the list of plans. + * @param callingPackage the name of the package making the call + */ + public void setSubscriptionPlans(int subId, @NonNull SubscriptionPlan[] plans, + @NonNull String callingPackage) { + try { + mService.setSubscriptionPlans(subId, plans, callingPackage); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Get subscription plans for the given subscription id. + * + * @param subId the subscriber to get the subscription plans for. + * @param callingPackage the name of the package making the call. + */ + @NonNull + public SubscriptionPlan[] getSubscriptionPlans(int subId, @NonNull String callingPackage) { + try { + return mService.getSubscriptionPlans(subId, callingPackage); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Resets network policy settings back to factory defaults. * * @hide @@ -286,6 +454,7 @@ public class NetworkPolicyManager { /** * Check if given UID can have a {@link #setUidPolicy(int, int)} defined, * usually to protect critical system services. + * @hide */ @Deprecated public static boolean isUidValidForPolicy(Context context, int uid) { @@ -353,6 +522,7 @@ public class NetworkPolicyManager { /** * Returns true if {@param procState} is considered foreground and as such will be allowed * to access network when the device is idle or in battery saver mode. Otherwise, false. + * @hide */ public static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(int procState) { return procState <= FOREGROUND_THRESHOLD_STATE; @@ -361,20 +531,68 @@ public class NetworkPolicyManager { /** * Returns true if {@param procState} is considered foreground and as such will be allowed * to access network when the device is in data saver mode. Otherwise, false. + * @hide */ public static boolean isProcStateAllowedWhileOnRestrictBackground(int procState) { return procState <= FOREGROUND_THRESHOLD_STATE; } + /** @hide */ public static String resolveNetworkId(WifiConfiguration config) { return WifiInfo.removeDoubleQuotes(config.isPasspoint() ? config.providerFriendlyName : config.SSID); } + /** @hide */ public static String resolveNetworkId(String ssid) { return WifiInfo.removeDoubleQuotes(ssid); } + /** @hide */ + @SystemApi + public static class SubscriptionCallback { + /** + * Notify clients of a new override about a given subscription. + * + * @param subId the subscriber this override applies to. + * @param overrideMask a bitmask that specifies which of the overrides is set. + * @param overrideValue a bitmask that specifies the override values. + */ + public void onSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask, + @SubscriptionOverrideMask int overrideValue) {} + + /** + * Notify of subscription plans change about a given subscription. + * + * @param subId the subscriber id that got subscription plans change. + * @param plans the list of subscription plans. + */ + public void onSubscriptionPlansChanged(int subId, @NonNull SubscriptionPlan[] plans) {} + } + + /** + * SubscriptionCallback proxy for SubscriptionCallback object. + * @hide + */ + public class SubscriptionCallbackProxy extends Listener { + private final SubscriptionCallback mCallback; + + SubscriptionCallbackProxy(SubscriptionCallback callback) { + mCallback = callback; + } + + @Override + public void onSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask, + @SubscriptionOverrideMask int overrideValue) { + mCallback.onSubscriptionOverride(subId, overrideMask, overrideValue); + } + + @Override + public void onSubscriptionPlansChanged(int subId, SubscriptionPlan[] plans) { + mCallback.onSubscriptionPlansChanged(subId, plans); + } + } + /** {@hide} */ public static class Listener extends INetworkPolicyListener.Stub { @Override public void onUidRulesChanged(int uid, int uidRules) { } diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java index 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/RouteInfo.java b/core/java/android/net/RouteInfo.java index e08809457649..2b9e9fe81b1b 100644 --- a/core/java/android/net/RouteInfo.java +++ b/core/java/android/net/RouteInfo.java @@ -105,6 +105,11 @@ public final class RouteInfo implements Parcelable { */ private final int mType; + /** + * The maximum transmission unit size for this route. + */ + private final int mMtu; + // Derived data members. // TODO: remove these. @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @@ -133,6 +138,31 @@ public final class RouteInfo implements Parcelable { @TestApi public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway, @Nullable String iface, @RouteType int type) { + this(destination, gateway, iface, type, 0); + } + + /** + * Constructs a RouteInfo object. + * + * If destination is null, then gateway must be specified and the + * constructed route is either the IPv4 default route <code>0.0.0.0</code> + * if the gateway is an instance of {@link Inet4Address}, or the IPv6 default + * route <code>::/0</code> if gateway is an instance of + * {@link Inet6Address}. + * <p> + * destination and gateway may not both be null. + * + * @param destination the destination prefix + * @param gateway the IP address to route packets through + * @param iface the interface name to send packets on + * @param type the type of this route + * @param mtu the maximum transmission unit size for this route + * + * @hide + */ + @SystemApi + public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway, + @Nullable String iface, @RouteType int type, int mtu) { switch (type) { case RTN_UNICAST: case RTN_UNREACHABLE: @@ -162,7 +192,7 @@ public final class RouteInfo implements Parcelable { } else { // no destination, no gateway. invalid. throw new IllegalArgumentException("Invalid arguments passed in: " + gateway + "," + - destination); + destination); } } // TODO: set mGateway to null if there is no gateway. This is more correct, saves space, and @@ -177,10 +207,10 @@ public final class RouteInfo implements Parcelable { } mHasGateway = (!gateway.isAnyLocalAddress()); - if ((destination.getAddress() instanceof Inet4Address && - (gateway instanceof Inet4Address == false)) || - (destination.getAddress() instanceof Inet6Address && - (gateway instanceof Inet6Address == false))) { + if ((destination.getAddress() instanceof Inet4Address + && !(gateway instanceof Inet4Address)) + || (destination.getAddress() instanceof Inet6Address + && !(gateway instanceof Inet6Address))) { throw new IllegalArgumentException("address family mismatch in RouteInfo constructor"); } mDestination = destination; // IpPrefix objects are immutable. @@ -188,6 +218,7 @@ public final class RouteInfo implements Parcelable { mInterface = iface; // Strings are immutable. mType = type; mIsHost = isHost(); + mMtu = mtu; } /** @@ -374,6 +405,17 @@ public final class RouteInfo implements Parcelable { } /** + * Retrieves the MTU size for this route. + * + * @return The MTU size, or 0 if it has not been set. + * @hide + */ + @SystemApi + public int getMtu() { + return mMtu; + } + + /** * Indicates if this route is a default route (ie, has no destination specified). * * @return {@code true} if the destination has a prefix length of 0. @@ -463,6 +505,7 @@ public final class RouteInfo implements Parcelable { val += " unknown type " + mType; } } + val += " mtu " + mMtu; return val; } @@ -480,7 +523,7 @@ public final class RouteInfo implements Parcelable { return Objects.equals(mDestination, target.getDestination()) && Objects.equals(mGateway, target.getGateway()) && Objects.equals(mInterface, target.getInterface()) && - mType == target.getType(); + mType == target.getType() && mMtu == target.getMtu(); } /** @@ -490,7 +533,7 @@ public final class RouteInfo implements Parcelable { return (mDestination.hashCode() * 41) + (mGateway == null ? 0 :mGateway.hashCode() * 47) + (mInterface == null ? 0 :mInterface.hashCode() * 67) - + (mType * 71); + + (mType * 71) + (mMtu * 89); } /** @@ -509,6 +552,7 @@ public final class RouteInfo implements Parcelable { dest.writeByteArray(gatewayBytes); dest.writeString(mInterface); dest.writeInt(mType); + dest.writeInt(mMtu); } /** @@ -527,8 +571,9 @@ public final class RouteInfo implements Parcelable { String iface = in.readString(); int type = in.readInt(); + int mtu = in.readInt(); - return new RouteInfo(dest, gateway, iface, type); + return new RouteInfo(dest, gateway, iface, type, mtu); } public RouteInfo[] newArray(int size) { 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/net/Uri.aidl b/core/java/android/net/Uri.aidl index 6bd3be5e041b..b85f63bbcdd2 100644 --- a/core/java/android/net/Uri.aidl +++ b/core/java/android/net/Uri.aidl @@ -16,4 +16,4 @@ package android.net; -parcelable Uri; +@JavaOnlyStableParcelable parcelable Uri; diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java index 8bb2df0bba58..b9e6ff4a5a9e 100644 --- a/core/java/android/nfc/Tag.java +++ b/core/java/android/nfc/Tag.java @@ -455,12 +455,12 @@ public final class Tag implements Parcelable { * * @hide */ - public synchronized void setConnectedTechnology(int technology) { - if (mConnectedTechnology == -1) { - mConnectedTechnology = technology; - } else { - throw new IllegalStateException("Close other technology first!"); + public synchronized boolean setConnectedTechnology(int technology) { + if (mConnectedTechnology != -1) { + return false; } + mConnectedTechnology = technology; + return true; } /** diff --git a/core/java/android/nfc/tech/BasicTagTechnology.java b/core/java/android/nfc/tech/BasicTagTechnology.java index b6b347ca0619..ae468fead7a2 100644 --- a/core/java/android/nfc/tech/BasicTagTechnology.java +++ b/core/java/android/nfc/tech/BasicTagTechnology.java @@ -75,7 +75,10 @@ abstract class BasicTagTechnology implements TagTechnology { if (errorCode == ErrorCodes.SUCCESS) { // Store this in the tag object - mTag.setConnectedTechnology(mSelectedTechnology); + if (!mTag.setConnectedTechnology(mSelectedTechnology)) { + Log.e(TAG, "Close other technology first!"); + throw new IOException("Only one TagTechnology can be connected at a time."); + } mIsConnected = true; } else if (errorCode == ErrorCodes.ERROR_NOT_SUPPORTED) { throw new UnsupportedOperationException("Connecting to " + diff --git a/core/java/android/os/HidlMemory.java b/core/java/android/os/HidlMemory.java index aeb65892a425..02d1e0ce9109 100644 --- a/core/java/android/os/HidlMemory.java +++ b/core/java/android/os/HidlMemory.java @@ -85,11 +85,11 @@ public class HidlMemory implements Closeable { } /** - * Disowns the underlying handle and returns it. This object becomes invalid. + * Disowns the underlying handle and returns it. The underlying handle becomes null. * * @return The underlying handle. */ - @NonNull + @Nullable public NativeHandle releaseHandle() { NativeHandle handle = mHandle; mHandle = null; diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 185693e8e6e0..3ae570081f4d 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -80,6 +80,14 @@ interface IPowerManager // controls whether PowerManager should doze after the screen turns off or not void setDozeAfterScreenOff(boolean on); + // returns whether ambient display is available on the device. + boolean isAmbientDisplayAvailable(); + // suppresses the current ambient display configuration and disables ambient display. + void suppressAmbientDisplay(String token, boolean suppress); + // returns whether ambient display is suppressed by the calling app with the given token. + boolean isAmbientDisplaySuppressedForToken(String token); + // returns whether ambient display is suppressed by any app with any token. + boolean isAmbientDisplaySuppressed(); // Forces the system to suspend even if there are held wakelocks. boolean forceSuspend(); diff --git a/core/java/android/os/IncidentManager.java b/core/java/android/os/IncidentManager.java index f6563ebdbbf4..8dde117fd19a 100644 --- a/core/java/android/os/IncidentManager.java +++ b/core/java/android/os/IncidentManager.java @@ -36,6 +36,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.concurrent.Executor; /** @@ -426,10 +427,9 @@ public class IncidentManager { * * @see #registerSection * @see #unregisterSection - * - * @hide */ public static class DumpCallback { + private int mId; private Executor mExecutor; IIncidentDumpCallback.Stub mBinder = new IIncidentDumpCallback.Stub() { @@ -437,20 +437,25 @@ public class IncidentManager { public void onDumpSection(ParcelFileDescriptor pfd) { if (mExecutor != null) { mExecutor.execute(() -> { - DumpCallback.this.onDumpSection( + DumpCallback.this.onDumpSection(mId, new ParcelFileDescriptor.AutoCloseOutputStream(pfd)); }); } else { - DumpCallback.this.onDumpSection( + DumpCallback.this.onDumpSection(mId, new ParcelFileDescriptor.AutoCloseOutputStream(pfd)); } } }; /** - * Called when incidentd requests to dump this section. + * Dump the registered section as a protobuf message to the given OutputStream. Called when + * incidentd requests to dump this section. + * + * @param id the id of the registered section. The same id used in calling + * {@link #registerSection(int, String, DumpCallback)} will be passed in here. + * @param out the OutputStream to write the protobuf message */ - public void onDumpSection(OutputStream out) { + public void onDumpSection(int id, @NonNull OutputStream out) { } } @@ -563,12 +568,20 @@ public class IncidentManager { /** * Register a callback to dump an extended incident report section with the given id and name. - * The callback function will be invoked when an incident report with all sections or sections - * matching the given id is being taken. * - * @hide + * Calling <code>registerSection</code> with a duplicate id will override previous registration. + * However, the request must come from the same calling uid. + * + * @param id the ID of the extended section. It should be unique system-wide, and be + * different from IDs of all existing section in + * frameworks/base/core/proto/android/os/incident.proto. + * Also see incident.proto for other rules about the ID. + * @param name the name to display in logs and/or stderr when taking an incident report + * containing this section, mainly for debugging purpose + * @param callback the callback function to be invoked when an incident report with all sections + * or sections matching the given id is being taken */ - public void registerSection(int id, String name, @NonNull DumpCallback callback) { + public void registerSection(int id, @NonNull String name, @NonNull DumpCallback callback) { registerSection(id, name, mContext.getMainExecutor(), callback); } @@ -576,16 +589,27 @@ public class IncidentManager { * Register a callback to dump an extended incident report section with the given id and name, * running on the supplied executor. * - * @hide + * @param id the ID of the extended section. It should be unique system-wide, and be + * different from IDs of all existing section in + * frameworks/base/core/proto/android/os/incident.proto. + * Also see incident.proto for other rules about the ID. + * @param name the name to display in logs and/or stderr when taking an incident report + * containing this section, mainly for debugging purpose + * @param executor the executor used to run the callback + * @param callback the callback function to be invoked when an incident report with all sections + * or sections matching the given id is being taken */ - public void registerSection(int id, String name, @NonNull @CallbackExecutor Executor executor, - @NonNull DumpCallback callback) { + public void registerSection(int id, @NonNull String name, + @NonNull @CallbackExecutor Executor executor, @NonNull DumpCallback callback) { + Objects.requireNonNull(executor, "executor cannot be null"); + Objects.requireNonNull(callback, "callback cannot be null"); try { if (callback.mExecutor != null) { throw new RuntimeException("Do not reuse DumpCallback objects when calling" + " registerSection"); } callback.mExecutor = executor; + callback.mId = id; final IIncidentManager service = getIIncidentManagerLocked(); if (service == null) { Slog.e(TAG, "registerSection can't find incident binder service"); @@ -599,9 +623,7 @@ public class IncidentManager { /** * Unregister an extended section dump function. The section must be previously registered with - * {@link #registerSection(int, String, DumpCallback)} - * - * @hide + * {@link #registerSection(int, String, DumpCallback)} by the same calling uid. */ public void unregisterSection(int id) { try { @@ -719,7 +741,7 @@ public class IncidentManager { throw new RuntimeException("Invalid URI: No " + URI_PARAM_REPORT_ID + " parameter. " + uri); } - + try { getCompanionServiceLocked().deleteIncidentReports(pkg, cls, id); } catch (RemoteException ex) { diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 0414b14ae02d..6a80788c81cc 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -247,6 +247,19 @@ public final class PowerManager { */ public static final int BRIGHTNESS_DEFAULT = -1; + /** + * Brightness value for fully off in float. + * TODO: rename this to BRIGHTNES_OFF and remove the integer-based constant. + * @hide + */ + public static final float BRIGHTNESS_OFF_FLOAT = -1.0f; + + /** + * Invalid brightness value. + * @hide + */ + public static final float BRIGHTNESS_INVALID_FLOAT = Float.NaN; + // Note: Be sure to update android.os.BatteryStats and PowerManager.h // if adding or modifying user activity event constants. @@ -1870,6 +1883,77 @@ public final class PowerManager { } /** + * Returns true if ambient display is available on the device. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) + public boolean isAmbientDisplayAvailable() { + try { + return mService.isAmbientDisplayAvailable(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * If true, suppresses the current ambient display configuration and disables ambient display. + * + * <p>This method has no effect if {@link #isAmbientDisplayAvailable()} is false. + * + * @param token A persistable identifier for the ambient display suppression that is unique + * within the calling application. + * @param suppress If set to {@code true}, ambient display will be suppressed. If set to + * {@code false}, ambient display will no longer be suppressed for the given + * token. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) + public void suppressAmbientDisplay(@NonNull String token, boolean suppress) { + try { + mService.suppressAmbientDisplay(token, suppress); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns true if ambient display is suppressed by the calling app with the given + * {@code token}. + * + * <p>This method will return false if {@link #isAmbientDisplayAvailable()} is false. + * + * @param token The identifier of the ambient display suppression. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) + public boolean isAmbientDisplaySuppressedForToken(@NonNull String token) { + try { + return mService.isAmbientDisplaySuppressedForToken(token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns true if ambient display is suppressed by <em>any</em> app with <em>any</em> token. + * + * <p>This method will return false if {@link #isAmbientDisplayAvailable()} is false. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) + public boolean isAmbientDisplaySuppressed() { + try { + return mService.isAmbientDisplaySuppressed(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Returns the reason the phone was last shutdown. Calling app must have the * {@link android.Manifest.permission#DEVICE_POWER} permission to request this information. * @return Reason for shutdown as an int, {@link #SHUTDOWN_REASON_UNKNOWN} if the file could diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 5d80ab6453cc..c7a8474c8038 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -20,14 +20,20 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; +import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; +import android.system.StructPollfd; import android.util.Pair; import android.webkit.WebViewZygote; import dalvik.system.VMRuntime; +import libcore.io.IoUtils; + +import java.io.FileDescriptor; import java.util.Map; +import java.util.concurrent.TimeoutException; /** * Tools for managing OS processes. @@ -487,6 +493,15 @@ public class Process { private static long sStartElapsedRealtime; private static long sStartUptimeMillis; + private static final int PIDFD_UNKNOWN = 0; + private static final int PIDFD_SUPPORTED = 1; + private static final int PIDFD_UNSUPPORTED = 2; + + /** + * Whether or not the underlying OS supports pidfd + */ + private static int sPidFdSupported = PIDFD_UNKNOWN; + /** * State associated with the zygote process. * @hide @@ -1189,4 +1204,90 @@ public class Process { } } + + /** + * Wait for the death of the given process. + * + * @param pid The process ID to be waited on + * @param timeout The maximum time to wait in milliseconds, or -1 to wait forever + * @hide + */ + public static void waitForProcessDeath(int pid, int timeout) + throws InterruptedException, TimeoutException { + FileDescriptor pidfd = null; + if (sPidFdSupported == PIDFD_UNKNOWN) { + int fd = -1; + try { + fd = nativePidFdOpen(pid, 0); + sPidFdSupported = PIDFD_SUPPORTED; + } catch (ErrnoException e) { + sPidFdSupported = e.errno != OsConstants.ENOSYS + ? PIDFD_SUPPORTED : PIDFD_UNSUPPORTED; + } finally { + if (fd >= 0) { + pidfd = new FileDescriptor(); + pidfd.setInt$(fd); + } + } + } + boolean fallback = sPidFdSupported == PIDFD_UNSUPPORTED; + if (!fallback) { + try { + if (pidfd == null) { + int fd = nativePidFdOpen(pid, 0); + if (fd >= 0) { + pidfd = new FileDescriptor(); + pidfd.setInt$(fd); + } else { + fallback = true; + } + } + if (pidfd != null) { + StructPollfd[] fds = new StructPollfd[] { + new StructPollfd() + }; + fds[0].fd = pidfd; + fds[0].events = (short) OsConstants.POLLIN; + fds[0].revents = 0; + fds[0].userData = null; + int res = Os.poll(fds, timeout); + if (res > 0) { + return; + } else if (res == 0) { + throw new TimeoutException(); + } else { + // We should get an ErrnoException now + } + } + } catch (ErrnoException e) { + if (e.errno == OsConstants.EINTR) { + throw new InterruptedException(); + } + fallback = true; + } finally { + if (pidfd != null) { + IoUtils.closeQuietly(pidfd); + } + } + } + if (fallback) { + boolean infinity = timeout < 0; + long now = System.currentTimeMillis(); + final long end = now + timeout; + while (infinity || now < end) { + try { + Os.kill(pid, 0); + } catch (ErrnoException e) { + if (e.errno == OsConstants.ESRCH) { + return; + } + } + Thread.sleep(1); + now = System.currentTimeMillis(); + } + } + throw new TimeoutException(); + } + + private static native int nativePidFdOpen(int pid, int flags) throws ErrnoException; } diff --git a/core/java/android/os/TelephonyServiceManager.java b/core/java/android/os/TelephonyServiceManager.java index 064cf7d825b5..4f5f3d69b6f3 100644 --- a/core/java/android/os/TelephonyServiceManager.java +++ b/core/java/android/os/TelephonyServiceManager.java @@ -226,12 +226,4 @@ public class TelephonyServiceManager { public ServiceRegisterer getIccPhoneBookServiceRegisterer() { return new ServiceRegisterer("simphonebook"); } - - /** - * Returns {@link ServiceRegisterer} for the window service. - */ - @NonNull - public ServiceRegisterer getWindowServiceRegisterer() { - return new ServiceRegisterer(Context.WINDOW_SERVICE); - } } diff --git a/core/java/android/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/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 1453608c1518..bb1c8ed165c9 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -16,6 +16,8 @@ package android.provider; +import static android.annotation.SystemApi.Client.MODULE_APPS; + import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull; import static com.android.internal.util.Preconditions.checkCollectionNotEmpty; @@ -24,6 +26,7 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentInterface; +import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -47,6 +50,7 @@ import android.os.ParcelFileDescriptor.OnCloseListener; import android.os.Parcelable; import android.os.ParcelableException; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import com.android.internal.util.Preconditions; @@ -945,6 +949,20 @@ public final class DocumentsContract { return getBaseDocumentUriBuilder(authority).appendPath(documentId).build(); } + /** + * Builds URI as described in {@link #buildDocumentUri(String, String)}, but such that it will + * be associated with the given user. + * + * @hide + */ + @SystemApi(client = MODULE_APPS) + @NonNull + public static Uri buildDocumentUriAsUser( + @NonNull String authority, @NonNull String documentId, @NonNull UserHandle user) { + return ContentProvider.maybeAddUserId( + buildDocumentUri(authority, documentId), user.getIdentifier()); + } + /** {@hide} */ public static Uri buildBaseDocumentUri(String authority) { return getBaseDocumentUriBuilder(authority).build(); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 0742a20a9fd6..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; @@ -1969,6 +1970,21 @@ public final class Settings { "android.settings.REQUEST_SET_AUTOFILL_SERVICE"; /** + * Activity Action: Show screen for controlling the Quick Access Wallet. + * <p> + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. + * <p> + * Input: The Intent's data URI specifies the application package name + * to be shown, with the "package" scheme. That is "package:com.my.app". + * <p> + * Output: Nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_QUICK_ACCESS_WALLET_SETTINGS = + "android.settings.QUICK_ACCESS_WALLET_SETTINGS"; + + /** * Activity Action: Show screen for controlling which apps have access on volume directories. * <p> * Input: Nothing. @@ -2146,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"; @@ -2203,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> @@ -14140,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..ee9d8f55f40e 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"; } /** 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/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java index c84fbc7287bb..8464c6df4425 100644 --- a/core/java/android/service/notification/Adjustment.java +++ b/core/java/android/service/notification/Adjustment.java @@ -131,6 +131,13 @@ public final class Adjustment implements Parcelable { public static final String KEY_RANKING_SCORE = "key_ranking_score"; /** + * Data type: boolean, when true it suggests this is NOT a conversation notification. + * @hide + */ + @SystemApi + public static final String KEY_NOT_CONVERSATION = "key_not_conversation"; + + /** * Create a notification adjustment. * * @param pkg The package of the notification. diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index fd04f499a432..e053ed58a82b 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -1539,6 +1539,7 @@ public abstract class NotificationListenerService extends Service { private ArrayList<CharSequence> mSmartReplies; private boolean mCanBubble; private boolean mVisuallyInterruptive; + private boolean mIsConversation; private static final int PARCEL_VERSION = 2; @@ -1571,6 +1572,7 @@ public abstract class NotificationListenerService extends Service { out.writeCharSequenceList(mSmartReplies); out.writeBoolean(mCanBubble); out.writeBoolean(mVisuallyInterruptive); + out.writeBoolean(mIsConversation); } /** @hide */ @@ -1604,6 +1606,7 @@ public abstract class NotificationListenerService extends Service { mSmartReplies = in.readCharSequenceList(); mCanBubble = in.readBoolean(); mVisuallyInterruptive = in.readBoolean(); + mIsConversation = in.readBoolean(); } @@ -1801,6 +1804,14 @@ public abstract class NotificationListenerService extends Service { } /** + * Returns whether this notification is a conversation notification. + * @hide + */ + public boolean isConversation() { + return mIsConversation; + } + + /** * @hide */ @VisibleForTesting @@ -1812,7 +1823,7 @@ public abstract class NotificationListenerService extends Service { int userSentiment, boolean hidden, long lastAudiblyAlertedMs, boolean noisy, ArrayList<Notification.Action> smartActions, ArrayList<CharSequence> smartReplies, boolean canBubble, - boolean visuallyInterruptive) { + boolean visuallyInterruptive, boolean isConversation) { mKey = key; mRank = rank; mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW; @@ -1834,6 +1845,7 @@ public abstract class NotificationListenerService extends Service { mSmartReplies = smartReplies; mCanBubble = canBubble; mVisuallyInterruptive = visuallyInterruptive; + mIsConversation = isConversation; } /** @@ -1859,7 +1871,8 @@ public abstract class NotificationListenerService extends Service { other.mSmartActions, other.mSmartReplies, other.mCanBubble, - other.mVisuallyInterruptive); + other.mVisuallyInterruptive, + other.mIsConversation); } /** @@ -1912,7 +1925,8 @@ public abstract class NotificationListenerService extends Service { == (other.mSmartActions == null ? 0 : other.mSmartActions.size())) && Objects.equals(mSmartReplies, other.mSmartReplies) && Objects.equals(mCanBubble, other.mCanBubble) - && Objects.equals(mVisuallyInterruptive, other.mVisuallyInterruptive); + && Objects.equals(mVisuallyInterruptive, other.mVisuallyInterruptive) + && Objects.equals(mIsConversation, other.mIsConversation); } } diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsCallback.java b/core/java/android/service/quickaccesswallet/GetWalletCardsCallback.java new file mode 100644 index 000000000000..9d210cd82c23 --- /dev/null +++ b/core/java/android/service/quickaccesswallet/GetWalletCardsCallback.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.quickaccesswallet; + +import android.annotation.NonNull; +import android.os.Handler; +import android.os.RemoteException; +import android.util.Log; + +/** + * Handles response from the {@link QuickAccessWalletService} for {@link GetWalletCardsRequest} + */ +public final class GetWalletCardsCallback { + + private static final String TAG = "QAWalletCallback"; + + private final IQuickAccessWalletServiceCallbacks mCallback; + private final Handler mHandler; + private boolean mCalled; + + /** + * @hide + */ + GetWalletCardsCallback(IQuickAccessWalletServiceCallbacks callback, Handler handler) { + mCallback = callback; + mHandler = handler; + } + + /** + * Notifies the Android System that an {@link QuickAccessWalletService#onWalletCardsRequested} + * was successfully handled by the service. + * + * @param response The response contains the list of {@link WalletCard walletCards} to be shown + * to the user as well as the index of the card that should initially be + * presented as the selected card. + */ + public void onSuccess(@NonNull GetWalletCardsResponse response) { + mHandler.post(() -> onSuccessInternal(response)); + } + + /** + * Notifies the Android System that an {@link QuickAccessWalletService#onWalletCardsRequested} + * could not be handled by the service. + * + * @param error The error message. <b>Note: </b> this message should <b>not</b> contain PII + * (Personally Identifiable Information, such as username or email address). + * @throws IllegalStateException if this method or {@link #onSuccess} was already called. + */ + public void onFailure(@NonNull GetWalletCardsError error) { + mHandler.post(() -> onFailureInternal(error)); + } + + private void onSuccessInternal(GetWalletCardsResponse response) { + if (mCalled) { + Log.w(TAG, "already called"); + return; + } + mCalled = true; + try { + mCallback.onGetWalletCardsSuccess(response); + } catch (RemoteException e) { + Log.e(TAG, "Error returning wallet cards", e); + } + } + + private void onFailureInternal(GetWalletCardsError error) { + if (mCalled) { + Log.w(TAG, "already called"); + return; + } + mCalled = true; + try { + mCallback.onGetWalletCardsFailure(error); + } catch (RemoteException e) { + Log.e(TAG, "Error returning failure message", e); + } + } +} diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsError.aidl b/core/java/android/service/quickaccesswallet/GetWalletCardsError.aidl new file mode 100644 index 000000000000..847f5accbd8e --- /dev/null +++ b/core/java/android/service/quickaccesswallet/GetWalletCardsError.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.quickaccesswallet; + +parcelable GetWalletCardsError;
\ No newline at end of file diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsError.java b/core/java/android/service/quickaccesswallet/GetWalletCardsError.java new file mode 100644 index 000000000000..527d2b79b2de --- /dev/null +++ b/core/java/android/service/quickaccesswallet/GetWalletCardsError.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.quickaccesswallet; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.drawable.Icon; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +/** + * Error response for an {@link GetWalletCardsRequest}. + */ +public final class GetWalletCardsError implements Parcelable { + + private final Icon mIcon; + private final CharSequence mMessage; + + /** + * Construct a new error response. If provided, the icon and message will be displayed to the + * user. + * + * @param icon an icon to be shown to the user next to the message. Optional. + * @param message message to be shown to the user. Optional. + */ + public GetWalletCardsError(@Nullable Icon icon, @Nullable CharSequence message) { + mIcon = icon; + mMessage = message; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + if (mIcon == null) { + dest.writeByte((byte) 0); + } else { + dest.writeByte((byte) 1); + mIcon.writeToParcel(dest, flags); + } + TextUtils.writeToParcel(mMessage, dest, flags); + } + + private static GetWalletCardsError readFromParcel(Parcel source) { + Icon icon = source.readByte() == 0 ? null : Icon.CREATOR.createFromParcel(source); + CharSequence message = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); + return new GetWalletCardsError(icon, message); + } + + @NonNull + public static final Creator<GetWalletCardsError> CREATOR = + new Creator<GetWalletCardsError>() { + @Override + public GetWalletCardsError createFromParcel(Parcel source) { + return readFromParcel(source); + } + + @Override + public GetWalletCardsError[] newArray(int size) { + return new GetWalletCardsError[size]; + } + }; + + /** + * An icon that may be displayed with the message to provide a visual indication of why cards + * could not be provided in the Quick Access Wallet. + */ + @Nullable + public Icon getIcon() { + return mIcon; + } + + /** + * A localized message that may be shown to the user in the event that the wallet cards cannot + * be retrieved. <b>Note: </b> this message should <b>not</b> contain PII (Personally + * Identifiable Information, such as username or email address). + */ + @Nullable + public CharSequence getMessage() { + return mMessage; + } +} diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsRequest.aidl b/core/java/android/service/quickaccesswallet/GetWalletCardsRequest.aidl new file mode 100644 index 000000000000..e70a98258afb --- /dev/null +++ b/core/java/android/service/quickaccesswallet/GetWalletCardsRequest.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.quickaccesswallet; + +parcelable GetWalletCardsRequest;
\ No newline at end of file diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsRequest.java b/core/java/android/service/quickaccesswallet/GetWalletCardsRequest.java new file mode 100644 index 000000000000..2ba448fc03c4 --- /dev/null +++ b/core/java/android/service/quickaccesswallet/GetWalletCardsRequest.java @@ -0,0 +1,137 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.quickaccesswallet; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Represents a request to a {@link QuickAccessWalletService} for {@link WalletCard walletCards}. + * Wallet cards may represent anything that a user might carry in their wallet -- a credit card, + * library card, a transit pass, etc. This request contains the desired size of the card images and + * icons as well as the maximum number of cards that may be returned in the {@link + * GetWalletCardsResponse}. + * + * <p>Cards may be displayed with an optional icon and label. The icon and label should communicate + * the same idea. For example, if a card can be used at an NFC terminal, the icon could be an NFC + * icon and the label could inform the user how to interact with the NFC terminal. + * + * <p>The maximum number of cards that may be displayed in the wallet is provided in {@link + * #getMaxCards()}. The {@link QuickAccessWalletService} may provide up to this many cards in the + * {@link GetWalletCardsResponse#getWalletCards()}. If the list of cards provided exceeds this + * number, some of the cards may not be shown to the user. + */ +public final class GetWalletCardsRequest implements Parcelable { + + private final int mCardWidthPx; + private final int mCardHeightPx; + private final int mIconSizePx; + private final int mMaxCards; + + /** + * Creates a new GetWalletCardsRequest. + * + * @param cardWidthPx The width of the card image in pixels. + * @param cardHeightPx The height of the card image in pixels. + * @param iconSizePx The width and height of the optional card icon in pixels. + * @param maxCards The maximum number of cards that may be provided in the response. + */ + public GetWalletCardsRequest(int cardWidthPx, int cardHeightPx, int iconSizePx, int maxCards) { + this.mCardWidthPx = cardWidthPx; + this.mCardHeightPx = cardHeightPx; + this.mIconSizePx = iconSizePx; + this.mMaxCards = maxCards; + } + + /** + * {@inheritDoc} + */ + @Override + public int describeContents() { + return 0; + } + + /** + * {@inheritDoc} + */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mCardWidthPx); + dest.writeInt(mCardHeightPx); + dest.writeInt(mIconSizePx); + dest.writeInt(mMaxCards); + } + + @NonNull + public static final Creator<GetWalletCardsRequest> CREATOR = + new Creator<GetWalletCardsRequest>() { + @Override + public GetWalletCardsRequest createFromParcel(Parcel source) { + int cardWidthPx = source.readInt(); + int cardHeightPx = source.readInt(); + int iconSizePx = source.readInt(); + int maxCards = source.readInt(); + return new GetWalletCardsRequest(cardWidthPx, + cardHeightPx, + iconSizePx, + maxCards); + } + + @Override + public GetWalletCardsRequest[] newArray(int size) { + return new GetWalletCardsRequest[size]; + } + }; + + /** + * The desired width of the {@link WalletCard#getCardImage()}, in pixels. The dimensions of the + * card image are requested so that it may be rendered without scaling. + * <p> + * The {@code cardWidthPx} and {@code cardHeightPx} should be applied to the size of the {@link + * WalletCard#getCardImage()}. The size of the card image is specified so that it may be + * rendered accurately and without distortion caused by scaling. + */ + public int getCardWidthPx() { + return mCardWidthPx; + } + + /** + * The desired height of the {@link WalletCard#getCardImage()}, in pixels. The dimensions of the + * card image are requested so that it may be rendered without scaling. + */ + public int getCardHeightPx() { + return mCardHeightPx; + } + + /** + * Wallet cards may be displayed next to an icon. The icon can help to convey additional + * information about the state of the card. If the provided icon is a bitmap, its width and + * height should equal iconSizePx so that it is rendered without distortion caused by scaling. + */ + public int getIconSizePx() { + return mIconSizePx; + } + + /** + * The maximum size of the {@link GetWalletCardsResponse#getWalletCards()}. If the list of cards + * exceeds this number, not all cards may be displayed. + */ + public int getMaxCards() { + return mMaxCards; + } +} diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.aidl b/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.aidl new file mode 100644 index 000000000000..b0f25b3c7d72 --- /dev/null +++ b/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.quickaccesswallet; + +parcelable GetWalletCardsResponse;
\ No newline at end of file diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java b/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java new file mode 100644 index 000000000000..996622a3e5aa --- /dev/null +++ b/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java @@ -0,0 +1,100 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.quickaccesswallet; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; + +/** + * The response for an {@link GetWalletCardsRequest} contains a list of wallet cards and the index + * of the card that should initially be displayed in the 'selected' position. + */ +public final class GetWalletCardsResponse implements Parcelable { + + private final List<WalletCard> mWalletCards; + private final int mSelectedIndex; + + /** + * Construct a new response. + * + * @param walletCards The list of wallet cards. + * @param selectedIndex The index of the card that should be presented as the initially + * 'selected' card + */ + public GetWalletCardsResponse(@NonNull List<WalletCard> walletCards, int selectedIndex) { + this.mWalletCards = walletCards; + this.mSelectedIndex = selectedIndex; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mWalletCards.size()); + dest.writeParcelableList(mWalletCards, flags); + dest.writeInt(mSelectedIndex); + } + + private static GetWalletCardsResponse readFromParcel(Parcel source) { + int size = source.readInt(); + List<WalletCard> walletCards = + source.readParcelableList(new ArrayList<>(size), WalletCard.class.getClassLoader()); + int selectedIndex = source.readInt(); + return new GetWalletCardsResponse(walletCards, selectedIndex); + } + + @NonNull + public static final Creator<GetWalletCardsResponse> CREATOR = + new Creator<GetWalletCardsResponse>() { + @Override + public GetWalletCardsResponse createFromParcel(Parcel source) { + return readFromParcel(source); + } + + @Override + public GetWalletCardsResponse[] newArray(int size) { + return new GetWalletCardsResponse[size]; + } + }; + + /** + * The list of {@link WalletCard}s. The size of this list should not exceed {@link + * GetWalletCardsRequest#getMaxCards()}. + */ + @NonNull + public List<WalletCard> getWalletCards() { + return mWalletCards; + } + + /** + * The {@code selectedIndex} represents the index of the card that should be presented in the + * 'selected' position when the cards are initially displayed in the quick access wallet. The + * {@code selectedIndex} should be greater than or equal to zero and less than the size of the + * list of {@link WalletCard walletCards}, unless the list is empty in which case the {@code + * selectedIndex} can take any value. 0 is a nice round number for such cases. + */ + public int getSelectedIndex() { + return mSelectedIndex; + } +} diff --git a/core/java/android/service/quickaccesswallet/IQuickAccessWalletService.aidl b/core/java/android/service/quickaccesswallet/IQuickAccessWalletService.aidl new file mode 100644 index 000000000000..ee70be442d16 --- /dev/null +++ b/core/java/android/service/quickaccesswallet/IQuickAccessWalletService.aidl @@ -0,0 +1,44 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.quickaccesswallet; + +import android.service.quickaccesswallet.GetWalletCardsRequest; +import android.service.quickaccesswallet.IQuickAccessWalletServiceCallbacks; +import android.service.quickaccesswallet.SelectWalletCardRequest; +import android.service.quickaccesswallet.WalletServiceEvent; +import android.service.quickaccesswallet.WalletServiceEventListenerRequest; + +/** + * Implemented by QuickAccessWalletService in the payment application + * + * @hide + */ +interface IQuickAccessWalletService { + // Request to get cards, which should be provided using the callback. + oneway void onWalletCardsRequested( + in GetWalletCardsRequest request, in IQuickAccessWalletServiceCallbacks callback); + // Indicates that a card has been selected. + oneway void onWalletCardSelected(in SelectWalletCardRequest request); + // Sent when the wallet is dismissed or closed. + oneway void onWalletDismissed(); + // Register an event listener + oneway void registerWalletServiceEventListener( + in WalletServiceEventListenerRequest request, + in IQuickAccessWalletServiceCallbacks callback); + // Unregister an event listener + oneway void unregisterWalletServiceEventListener(in WalletServiceEventListenerRequest request); +}
\ No newline at end of file diff --git a/core/java/android/service/quickaccesswallet/IQuickAccessWalletServiceCallbacks.aidl b/core/java/android/service/quickaccesswallet/IQuickAccessWalletServiceCallbacks.aidl new file mode 100644 index 000000000000..f37b930aabce --- /dev/null +++ b/core/java/android/service/quickaccesswallet/IQuickAccessWalletServiceCallbacks.aidl @@ -0,0 +1,37 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.quickaccesswallet; + +import android.service.quickaccesswallet.GetWalletCardsError; +import android.service.quickaccesswallet.GetWalletCardsResponse; +import android.service.quickaccesswallet.WalletServiceEvent; + +/** + * Interface to receive the result of requests to the wallet application. + * + * @hide + */ +interface IQuickAccessWalletServiceCallbacks { + // Called in response to onWalletCardsRequested on success. May only be called once per request. + oneway void onGetWalletCardsSuccess(in GetWalletCardsResponse response); + // Called in response to onWalletCardsRequested when an error occurs. May only be called once + // per request. + oneway void onGetWalletCardsFailure(in GetWalletCardsError error); + // Called in response to registerWalletServiceEventListener. May be called multiple times as + // long as the event listener is registered. + oneway void onWalletServiceEvent(in WalletServiceEvent event); +}
\ No newline at end of file diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java new file mode 100644 index 000000000000..cfc6d5777e82 --- /dev/null +++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java @@ -0,0 +1,89 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.quickaccesswallet; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.content.Intent; + +import java.util.function.Consumer; + +/** + * Facilitates accessing cards from the {@link QuickAccessWalletService}. + * + * @hide + */ +public interface QuickAccessWalletClient { + + /** + * Create a client for accessing wallet cards from the {@link QuickAccessWalletService}. If the + * service is unavailable, {@link #isWalletServiceAvailable()} will return false. + */ + @NonNull + static QuickAccessWalletClient create(@NonNull Context context) { + return new QuickAccessWalletClientImpl(context); + } + + /** + * @return true if the {@link QuickAccessWalletService} is available. + */ + boolean isWalletServiceAvailable(); + + /** + * Get wallet cards from the {@link QuickAccessWalletService}. + */ + void getWalletCards( + @NonNull GetWalletCardsRequest request, + @NonNull Consumer<GetWalletCardsResponse> onSuccessListener, + @NonNull Consumer<GetWalletCardsError> onFailureListener); + + /** + * Notify the {@link QuickAccessWalletService} service that a wallet card was selected. + */ + void selectWalletCard(@NonNull SelectWalletCardRequest request); + + /** + * Notify the {@link QuickAccessWalletService} service that the Wallet was dismissed. + */ + void notifyWalletDismissed(); + + /** + * Unregister event listener. + */ + void registerWalletServiceEventListener(Consumer<WalletServiceEvent> listener); + + /** + * Unregister event listener + */ + void unregisterWalletServiceEventListener(Consumer<WalletServiceEvent> listener); + + /** + * The manifest entry for the QuickAccessWalletService may also publish information about the + * activity that hosts the Wallet view. This is typically the home screen of the Wallet + * application. + */ + @Nullable + Intent getWalletActivity(); + + /** + * The manifest entry for the {@link QuickAccessWalletService} may publish the activity that + * hosts the settings + */ + @Nullable + Intent getSettingsActivity(); +} diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java new file mode 100644 index 000000000000..17c287fa8eeb --- /dev/null +++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java @@ -0,0 +1,342 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.quickaccesswallet; + +import static android.service.quickaccesswallet.QuickAccessWalletService.SERVICE_INTERFACE; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.text.TextUtils; +import android.util.Log; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; +import java.util.UUID; +import java.util.function.Consumer; + +/** + * @hide + */ +@SuppressWarnings("AndroidJdkLibsChecker") +class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Handler.Callback, + ServiceConnection { + + private static final String TAG = "QAWalletSClient"; + private final Handler mHandler; + private final Context mContext; + private final Queue<ApiCaller> mRequestQueue; + private final Map<Consumer<WalletServiceEvent>, String> mEventListeners; + private boolean mIsConnected; + @Nullable + private IQuickAccessWalletService mService; + + + @Nullable + private final QuickAccessWalletServiceInfo mServiceInfo; + + private static final int MSG_CONNECT = 1; + private static final int MSG_CONNECTED = 2; + private static final int MSG_EXECUTE = 3; + private static final int MSG_DISCONNECT = 4; + + QuickAccessWalletClientImpl(@NonNull Context context) { + mContext = context.getApplicationContext(); + mServiceInfo = QuickAccessWalletServiceInfo.tryCreate(context); + mHandler = new Handler(Looper.getMainLooper(), this); + mRequestQueue = new LinkedList<>(); + mEventListeners = new HashMap<>(1); + } + + @Override + public boolean handleMessage(Message msg) { + switch (msg.what) { + case MSG_CONNECT: + connectInternal(); + break; + case MSG_CONNECTED: + onConnectedInternal((IQuickAccessWalletService) msg.obj); + break; + case MSG_EXECUTE: + executeInternal((ApiCaller) msg.obj); + break; + case MSG_DISCONNECT: + disconnectInternal(); + break; + default: + Log.w(TAG, "Unknown what: " + msg.what); + return false; + } + return true; + } + + private void connect() { + mHandler.sendMessage(mHandler.obtainMessage(MSG_CONNECT)); + } + + private void connectInternal() { + if (mServiceInfo == null) { + Log.w(TAG, "Wallet service unavailable"); + return; + } + if (mIsConnected) { + Log.w(TAG, "already connected"); + return; + } + mIsConnected = true; + Intent intent = new Intent(SERVICE_INTERFACE); + intent.setComponent(mServiceInfo.getComponentName()); + int flags = Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY; + mContext.bindService(intent, this, flags); + } + + private void onConnectedInternal(IQuickAccessWalletService service) { + if (!mIsConnected) { + Log.w(TAG, "onConnectInternal but connection closed"); + mService = null; + return; + } + mService = service; + for (ApiCaller apiCaller : new ArrayList<>(mRequestQueue)) { + try { + apiCaller.performApiCall(mService); + } catch (RemoteException e) { + Log.e(TAG, "onConnectedInternal error", e); + apiCaller.onApiError(); + disconnect(); + break; + } + mRequestQueue.remove(apiCaller); + } + } + + private void disconnect() { + mHandler.sendMessage(mHandler.obtainMessage(MSG_DISCONNECT)); + } + + private void disconnectInternal() { + if (!mIsConnected) { + Log.w(TAG, "already disconnected"); + return; + } + mIsConnected = false; + mContext.unbindService(/*conn=*/this); + mService = null; + mEventListeners.clear(); + mRequestQueue.clear(); + } + + private void execute(ApiCaller apiCaller) { + mHandler.sendMessage(mHandler.obtainMessage(MSG_EXECUTE, apiCaller)); + } + + private void executeInternal(ApiCaller apiCall) { + if (mIsConnected && mService != null) { + try { + apiCall.performApiCall(mService); + } catch (RemoteException e) { + Log.w(TAG, "executeInternal error", e); + apiCall.onApiError(); + disconnect(); + } + } else { + mRequestQueue.add(apiCall); + connect(); + } + } + + public boolean isWalletServiceAvailable() { + return mServiceInfo != null; + } + + private abstract static class ApiCaller { + abstract void performApiCall(IQuickAccessWalletService service) throws RemoteException; + + void onApiError() { + Log.w(TAG, "api error"); + } + } + + public void getWalletCards( + @NonNull GetWalletCardsRequest request, + @NonNull Consumer<GetWalletCardsResponse> onSuccessListener, + @NonNull Consumer<GetWalletCardsError> onFailureListener) { + + BaseCallbacks callback = new BaseCallbacks() { + @Override + public void onGetWalletCardsSuccess(GetWalletCardsResponse response) { + mHandler.post(() -> onSuccessListener.accept(response)); + } + + @Override + public void onGetWalletCardsFailure(GetWalletCardsError error) { + mHandler.post(() -> onFailureListener.accept(error)); + } + }; + + execute(new ApiCaller() { + @Override + public void performApiCall(IQuickAccessWalletService service) throws RemoteException { + service.onWalletCardsRequested(request, callback); + } + + @Override + public void onApiError() { + callback.onGetWalletCardsFailure(new GetWalletCardsError(null, null)); + } + }); + } + + public void selectWalletCard(@NonNull SelectWalletCardRequest request) { + execute(new ApiCaller() { + @Override + public void performApiCall(IQuickAccessWalletService service) throws RemoteException { + service.onWalletCardSelected(request); + } + }); + } + + public void notifyWalletDismissed() { + execute(new ApiCaller() { + @Override + public void performApiCall(IQuickAccessWalletService service) throws RemoteException { + service.onWalletDismissed(); + mHandler.sendMessage(mHandler.obtainMessage(MSG_DISCONNECT)); + } + }); + } + + @Override + public void registerWalletServiceEventListener(Consumer<WalletServiceEvent> listener) { + + BaseCallbacks callback = new BaseCallbacks() { + @Override + public void onWalletServiceEvent(WalletServiceEvent event) { + Log.i(TAG, "onWalletServiceEvent"); + mHandler.post(() -> listener.accept(event)); + } + }; + + execute(new ApiCaller() { + @Override + public void performApiCall(IQuickAccessWalletService service) throws RemoteException { + String listenerId = UUID.randomUUID().toString(); + WalletServiceEventListenerRequest request = + new WalletServiceEventListenerRequest(listenerId); + mEventListeners.put(listener, listenerId); + service.registerWalletServiceEventListener(request, callback); + } + }); + } + + @Override + public void unregisterWalletServiceEventListener(Consumer<WalletServiceEvent> listener) { + execute(new ApiCaller() { + @Override + public void performApiCall(IQuickAccessWalletService service) throws RemoteException { + String listenerId = mEventListeners.get(listener); + if (listenerId == null) { + return; + } + WalletServiceEventListenerRequest request = + new WalletServiceEventListenerRequest(listenerId); + service.unregisterWalletServiceEventListener(request); + } + }); + } + + @Override + @Nullable + public Intent getWalletActivity() { + if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getWalletActivity())) { + return null; + } + return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET) + .setComponent( + new ComponentName( + mServiceInfo.getComponentName().getPackageName(), + mServiceInfo.getWalletActivity())); + } + + @Override + @Nullable + public Intent getSettingsActivity() { + if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getSettingsActivity())) { + return null; + } + return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET_SETTINGS) + .setComponent( + new ComponentName( + mServiceInfo.getComponentName().getPackageName(), + mServiceInfo.getSettingsActivity())); + } + + /** + * Connection to the {@link QuickAccessWalletService} + */ + + + @Override + public void onServiceConnected(ComponentName name, IBinder binder) { + IQuickAccessWalletService service = IQuickAccessWalletService.Stub.asInterface(binder); + mHandler.sendMessage(mHandler.obtainMessage(MSG_CONNECTED, service)); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + // Do not disconnect, as we may later be re-connected + Log.w(TAG, "onServiceDisconnected"); + } + + @Override + public void onBindingDied(ComponentName name) { + // This is a recoverable error but the client will need to reconnect. + Log.w(TAG, "onBindingDied"); + disconnect(); + } + + @Override + public void onNullBinding(ComponentName name) { + Log.w(TAG, "onNullBinding"); + disconnect(); + } + + private static class BaseCallbacks extends IQuickAccessWalletServiceCallbacks.Stub { + public void onGetWalletCardsSuccess(GetWalletCardsResponse response) { + throw new IllegalStateException(); + } + + public void onGetWalletCardsFailure(GetWalletCardsError error) { + throw new IllegalStateException(); + } + + public void onWalletServiceEvent(WalletServiceEvent event) { + throw new IllegalStateException(); + } + } +} diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java new file mode 100644 index 000000000000..d968405002e3 --- /dev/null +++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java @@ -0,0 +1,337 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.quickaccesswallet; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SdkConstant; +import android.app.Service; +import android.content.Intent; +import android.os.Build; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.provider.Settings; +import android.util.Log; + +/** + * A {@code QuickAccessWalletService} provides a list of {@code WalletCard}s shown in the Quick + * Access Wallet. The Quick Access Wallet allows the user to change their selected payment method + * and access other important passes, such as tickets and transit passes, without leaving the + * context of their current app. + * + * <p>An {@code QuickAccessWalletService} is only bound to the Android System for the purposes of + * showing wallet cards if: + * <ol> + * <li>The application hosting the QuickAccessWalletService is also the default NFC payment + * application. This means that the same application must also have a + * {@link android.nfc.cardemulation.HostApduService} or + * {@link android.nfc.cardemulation.OffHostApduService} that requires the + * android.permission.BIND_NFC_SERVICE permission. + * <li>The user explicitly selected the application as the default payment application in + * the Tap & pay settings screen. + * <li>The application requires the {@code android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE} + * permission in its manifest. + * <li>The user explicitly enables it using Android Settings (the + * {@link Settings#ACTION_QUICK_ACCESS_WALLET_SETTINGS} intent can be used to launch it). + * </ol> + * + * <a name="BasicUsage"></a> + * <h3>Basic usage</h3> + * + * <p>The basic Quick Access Wallet process is defined by the workflow below: + * <ol> + * <li>User performs a gesture to bring up the Quick Access Wallet, which is displayed by the + * Android System. + * <li>The Android System creates a {@link GetWalletCardsRequest}, binds to the + * {@link QuickAccessWalletService}, and delivers the request. + * <li>The service receives the request through {@link #onWalletCardsRequested} + * <li>The service responds by calling {@link GetWalletCardsCallback#onSuccess} with a + * {@link GetWalletCardsResponse response} that contains between 1 and + * {@link GetWalletCardsRequest#getMaxCards() maxCards} cards. + * <li>The Android System displays the Quick Access Wallet containing the provided cards. The + * card at the {@link GetWalletCardsResponse#getSelectedIndex() selectedIndex} will initially + * be presented as the 'selected' card. + * <li>As soon as the cards are displayed, the Android System will notify the service that the + * card at the selected index has been selected through {@link #onWalletCardSelected}. + * <li>The user interacts with the wallet and may select one or more cards in sequence. Each time + * a new card is selected, the Android System will notify the service through + * {@link #onWalletCardSelected} and will provide the {@link WalletCard#getCardId() cardId} of the + * card that is now selected. + * <li>When the wallet is dismissed, the Android System will notify the service through + * {@link #onWalletDismissed}. + * </ol> + * + * <p>The workflow is designed to minimize the time that the Android System is bound to the + * service, but connections may be cached and reused to improve performance and conserve memory. + * All calls should be considered stateless: if the service needs to keep state between calls, it + * must do its own state management (keeping in mind that the service's process might be killed + * by the Android System when unbound; for example, if the device is running low in memory). + * + * <p> + * <a name="ErrorHandling"></a> + * <h3>Error handling</h3> + * <p>If the service encountered an error processing the request, it should call + * {@link GetWalletCardsCallback#onFailure}. + * For performance reasons, it's paramount that the service calls either + * {@link GetWalletCardsCallback#onSuccess} or + * {@link GetWalletCardsCallback#onFailure} for each + * {@link #onWalletCardsRequested} received - if it doesn't, the request will eventually time out + * and be discarded by the Android System. + * + * <p> + * <a name="ManifestEntry"></a> + * <h3>Manifest entry</h3> + * + * <p>QuickAccessWalletService must require the permission + * "android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE". + * + * <pre class="prettyprint"> + * {@literal + * <service + * android:name=".MyQuickAccessWalletService" + * android:label="@string/my_default_tile_label" + * android:icon="@drawable/my_default_icon_label" + * android:permission="android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE"> + * <intent-filter> + * <action android:name="android.service.quickaccesswallet.QuickAccessWalletService" /> + * </intent-filter> + * <meta-data android:name="android.quickaccesswallet" + * android:resource="@xml/quickaccesswallet_configuration" />; + * </service>} + * </pre> + * <p> + * The {@literal <meta-data>} element includes an android:resource attribute that points to an + * XML resource with further details about the service. The {@code quickaccesswallet_configuration} + * in the example above specifies an activity that allows the users to view the entire wallet. + * The following example shows the quickaccesswallet_configuration XML resource: + * <p> + * <pre class="prettyprint"> + * {@literal + * <quickaccesswallet-service + * xmlns:android="http://schemas.android.com/apk/res/android" + * android:settingsActivity="com.example.android.SettingsActivity" + * android:targetActivity="com.example.android.WalletActivity"/> + * } + * </pre> + * + * <p>The entry for {@code settingsActivity} should contain the fully qualified class name of an + * activity that allows the user to modify the settings for this service. The {@code targetActivity} + * entry should contain the fully qualified class name of an activity that allows the user to view + * their entire wallet. If specified, the wallet activity will be started with the Intent action + * {@link #ACTION_VIEW_WALLET} and the settings activity will be started with the Intent action + * {@link #ACTION_VIEW_WALLET_SETTINGS}. + */ +public abstract class QuickAccessWalletService extends Service { + + private static final String TAG = "QAWalletService"; + + /** + * The {@link Intent} that must be declared as handled by the service. To be supported, the + * service must also require the + * {@link android.Manifest.permission#BIND_QUICK_ACCESS_WALLET_SERVICE} + * permission so that other applications can not abuse it. + */ + @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) + public static final String SERVICE_INTERFACE = + "android.service.quickaccesswallet.QuickAccessWalletService"; + + /** + * Intent action to launch an activity to display the wallet. + */ + @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_VIEW_WALLET = + "android.service.quickaccesswallet.action.VIEW_WALLET"; + + /** + * Intent action to launch an activity to display quick access wallet settings. + */ + @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_VIEW_WALLET_SETTINGS = + "android.service.quickaccesswallet.action.VIEW_WALLET_SETTINGS"; + + /** + * Broadcast Action: Sent by the wallet application to dismiss the Quick Access Wallet. + * <p> + * The Quick Access Wallet may be shown in a system window on top of other Activities. If the + * user selects a payment card from the Quick Access Wallet and then holds their phone to an NFC + * terminal, the wallet application will need to show a payment Activity. But if the Quick + * Access Wallet is still being shown, it may obscure the payment Activity. To avoid this, the + * wallet application can send a broadcast to the Android System with this action to request + * that the Quick Access Wallet be dismissed. + * <p> + * This broadcast must use the {@code android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE} + * permission to ensure that it is only delivered to System UI. Furthermore, your application + * must require the {@code android.permission.DISMISS_QUICK_ACCESS_WALLET} + * <p> + * <pre class="prettyprint"> + * context.sendBroadcast( + * new Intent(ACTION_DISMISS_WALLET), Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE); + * </pre> + */ + @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DISMISS_WALLET = + "android.service.quickaccesswallet.action.DISMISS_WALLET"; + + /** + * Name under which a QuickAccessWalletService component publishes information about itself. + * This meta-data should reference an XML resource containing a + * <code><{@link + * android.R.styleable#QuickAccessWalletService quickaccesswallet-service}></code> tag. This + * is a a sample XML file configuring an QuickAccessWalletService: + * <pre> <quickaccesswallet-service + * android:walletActivity="foo.bar.WalletActivity" + * . . . + * /></pre> + */ + public static final String SERVICE_META_DATA = "android.quickaccesswallet"; + + private final Handler mHandler = new Handler(Looper.getMainLooper()); + @Nullable + private String mEventListenerId; + @Nullable + private IQuickAccessWalletServiceCallbacks mEventListener; + + private final IQuickAccessWalletService mInterface = new IQuickAccessWalletService.Stub() { + @Override + public void onWalletCardsRequested( + @NonNull GetWalletCardsRequest request, + @NonNull IQuickAccessWalletServiceCallbacks callback) { + mHandler.post(() -> onWalletCardsRequestedInternal(request, callback)); + } + + @Override + public void onWalletCardSelected(@NonNull SelectWalletCardRequest request) { + mHandler.post(() -> QuickAccessWalletService.this.onWalletCardSelected(request)); + } + + @Override + public void onWalletDismissed() { + mHandler.post(QuickAccessWalletService.this::onWalletDismissed); + } + + public void registerWalletServiceEventListener( + @NonNull WalletServiceEventListenerRequest request, + @NonNull IQuickAccessWalletServiceCallbacks callback) { + mHandler.post(() -> registerDismissWalletListenerInternal(request, callback)); + } + + public void unregisterWalletServiceEventListener( + @NonNull WalletServiceEventListenerRequest request) { + mHandler.post(() -> unregisterDismissWalletListenerInternal(request)); + } + }; + + private void onWalletCardsRequestedInternal( + GetWalletCardsRequest request, + IQuickAccessWalletServiceCallbacks callback) { + onWalletCardsRequested(request, new GetWalletCardsCallback(callback, mHandler)); + } + + @Override + @Nullable + public IBinder onBind(@NonNull Intent intent) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + // Binding to the QuickAccessWalletService is protected by the + // android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE permission, which is defined in + // R. Pre-R devices can have other side-loaded applications that claim this permission. + // This ensures that the service is only available when properly permission protected. + Log.w(TAG, "Warning: binding on pre-R device"); + } + if (SERVICE_INTERFACE.equals(intent.getAction())) { + return mInterface.asBinder(); + } + Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent); + return null; + } + + /** + * Called when the user requests the service to provide wallet cards. + * + * <p>This method will be called on the main thread, but the callback may be called from any + * thread. The callback should be called as quickly as possible. The service must always call + * either {@link GetWalletCardsCallback#onSuccess(GetWalletCardsResponse)} or {@link + * GetWalletCardsCallback#onFailure(GetWalletCardsError)}. Calling multiple times or calling + * both methods will cause an exception to be thrown. + */ + public abstract void onWalletCardsRequested( + @NonNull GetWalletCardsRequest request, + @NonNull GetWalletCardsCallback callback); + + /** + * A wallet card was selected. Sent when the user selects a wallet card from the list of cards. + * Selection may indicate that the card is now in the center of the screen, or highlighted in + * some other fashion. It does not mean that the user clicked on the card -- clicking on the + * card will cause the {@link WalletCard#getPendingIntent()} to be sent. + * + * <p>Card selection events are especially important to NFC payment applications because + * many NFC terminals can only accept one payment card at a time. If the user has several NFC + * cards in their wallet, selecting different cards can change which payment method is presented + * to the terminal. + */ + public abstract void onWalletCardSelected(@NonNull SelectWalletCardRequest request); + + /** + * Indicates that the wallet was dismissed. This is received when the Quick Access Wallet is no + * longer visible. + */ + public abstract void onWalletDismissed(); + + /** + * Send a {@link WalletServiceEvent} to the Quick Access Wallet. + * <p> + * Background events may require that the Quick Access Wallet view be updated. For example, if + * the wallet application hosting this service starts to handle an NFC payment while the Quick + * Access Wallet is being shown, the Quick Access Wallet will need to be dismissed so that the + * Activity showing the payment can be displayed to the user. + */ + public final void sendWalletServiceEvent(@NonNull WalletServiceEvent serviceEvent) { + mHandler.post(() -> sendWalletServiceEventInternal(serviceEvent)); + } + + private void sendWalletServiceEventInternal(WalletServiceEvent serviceEvent) { + if (mEventListener == null) { + Log.i(TAG, "No dismiss listener registered"); + return; + } + try { + mEventListener.onWalletServiceEvent(serviceEvent); + } catch (RemoteException e) { + Log.w(TAG, "onWalletServiceEvent error", e); + mEventListenerId = null; + mEventListener = null; + } + } + + private void registerDismissWalletListenerInternal( + @NonNull WalletServiceEventListenerRequest request, + @NonNull IQuickAccessWalletServiceCallbacks callback) { + mEventListenerId = request.getListenerId(); + mEventListener = callback; + } + + private void unregisterDismissWalletListenerInternal( + @NonNull WalletServiceEventListenerRequest request) { + if (mEventListenerId != null && mEventListenerId.equals(request.getListenerId())) { + mEventListenerId = null; + mEventListener = null; + } else { + Log.w(TAG, "dismiss listener missing or replaced"); + } + } +} diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java new file mode 100644 index 000000000000..8793f28bc708 --- /dev/null +++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.quickaccesswallet; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.provider.Settings; +import android.util.AttributeSet; +import android.util.Log; +import android.util.Xml; + +import com.android.internal.R; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.List; + +/** + * {@link ServiceInfo} and meta-data about a {@link QuickAccessWalletService}. + * + * @hide + */ +class QuickAccessWalletServiceInfo { + + private static final String TAG = "QAWalletSInfo"; + private static final String TAG_WALLET_SERVICE = "quickaccesswallet-service"; + + private final ServiceInfo mServiceInfo; + private final ServiceMetadata mServiceMetadata; + + private QuickAccessWalletServiceInfo( + @NonNull ServiceInfo serviceInfo, + @NonNull ServiceMetadata metadata) { + mServiceInfo = serviceInfo; + mServiceMetadata = metadata; + } + + @Nullable + static QuickAccessWalletServiceInfo tryCreate(@NonNull Context context) { + ComponentName defaultPaymentApp = getDefaultPaymentApp(context); + if (defaultPaymentApp == null) { + Log.d(TAG, "create: default payment app not set"); + return null; + } + + ServiceInfo serviceInfo = getWalletServiceInfo(context, defaultPaymentApp.getPackageName()); + if (serviceInfo == null) { + Log.d(TAG, "create: unable to resolve service intent"); + return null; + } + + if (!Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE.equals(serviceInfo.permission)) { + Log.w(TAG, String.format("QuickAccessWalletService from %s does not have permission %s", + serviceInfo.packageName, Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE)); + return null; + } + + ServiceMetadata metadata = parseServiceMetadata(context, serviceInfo); + return new QuickAccessWalletServiceInfo(serviceInfo, metadata); + } + + private static ComponentName getDefaultPaymentApp(Context context) { + ContentResolver cr = context.getContentResolver(); + String comp = Settings.Secure.getString(cr, Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT); + return comp == null ? null : ComponentName.unflattenFromString(comp); + } + + private static ServiceInfo getWalletServiceInfo(Context context, String packageName) { + Intent intent = new Intent(QuickAccessWalletService.SERVICE_INTERFACE); + intent.setPackage(packageName); + List<ResolveInfo> resolveInfos = + context.getPackageManager().queryIntentServices(intent, + PackageManager.MATCH_DEFAULT_ONLY); + return resolveInfos.isEmpty() ? null : resolveInfos.get(0).serviceInfo; + } + + private static class ServiceMetadata { + @Nullable + private final String mSettingsActivity; + @Nullable + private final String mWalletActivity; + + private ServiceMetadata(String settingsActivity, String walletActivity) { + this.mSettingsActivity = settingsActivity; + this.mWalletActivity = walletActivity; + } + } + + private static ServiceMetadata parseServiceMetadata(Context context, ServiceInfo serviceInfo) { + PackageManager pm = context.getPackageManager(); + final XmlResourceParser parser = + serviceInfo.loadXmlMetaData(pm, QuickAccessWalletService.SERVICE_META_DATA); + + if (parser == null) { + return new ServiceMetadata(null, null); + } + + try { + Resources resources = pm.getResourcesForApplication(serviceInfo.applicationInfo); + int type = 0; + while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { + type = parser.next(); + } + + if (TAG_WALLET_SERVICE.equals(parser.getName())) { + final AttributeSet allAttributes = Xml.asAttributeSet(parser); + TypedArray afsAttributes = null; + try { + afsAttributes = resources.obtainAttributes(allAttributes, + R.styleable.QuickAccessWalletService); + String settingsActivity = afsAttributes.getString( + R.styleable.QuickAccessWalletService_settingsActivity); + String walletActivity = afsAttributes.getString( + R.styleable.QuickAccessWalletService_targetActivity); + return new ServiceMetadata(settingsActivity, walletActivity); + } finally { + if (afsAttributes != null) { + afsAttributes.recycle(); + } + } + } else { + Log.e(TAG, "Meta-data does not start with quickaccesswallet-service tag"); + } + + } catch (PackageManager.NameNotFoundException + | IOException + | XmlPullParserException e) { + Log.e(TAG, "Error parsing quickaccesswallet service meta-data", e); + } + return new ServiceMetadata(null, null); + } + + /** + * @return the component name of the {@link QuickAccessWalletService} + */ + @NonNull + ComponentName getComponentName() { + return mServiceInfo.getComponentName(); + } + + /** + * @return the fully qualified name of the activity that hosts the full wallet. If available, + * this intent should be started with the action + * {@link QuickAccessWalletService#ACTION_VIEW_WALLET} + */ + @Nullable + String getWalletActivity() { + return mServiceMetadata.mWalletActivity; + } + + /** + * @return the fully qualified name of the activity that allows the user to change quick access + * wallet settings. If available, this intent should be started with the action {@link + * QuickAccessWalletService#ACTION_VIEW_WALLET_SETTINGS} + */ + @Nullable + String getSettingsActivity() { + return mServiceMetadata.mSettingsActivity; + } +} diff --git a/core/java/android/service/quickaccesswallet/SelectWalletCardRequest.aidl b/core/java/android/service/quickaccesswallet/SelectWalletCardRequest.aidl new file mode 100644 index 000000000000..97a0d41ff635 --- /dev/null +++ b/core/java/android/service/quickaccesswallet/SelectWalletCardRequest.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.quickaccesswallet; + +parcelable SelectWalletCardRequest;
\ No newline at end of file diff --git a/core/java/android/service/quickaccesswallet/SelectWalletCardRequest.java b/core/java/android/service/quickaccesswallet/SelectWalletCardRequest.java new file mode 100644 index 000000000000..cb69eee03427 --- /dev/null +++ b/core/java/android/service/quickaccesswallet/SelectWalletCardRequest.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.quickaccesswallet; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Represents a request to a {@link QuickAccessWalletService} to select a particular {@link + * WalletCard walletCard}. Card selection events are transmitted to the WalletService so that the + * selected card may be used by the NFC payment service. + */ +public final class SelectWalletCardRequest implements Parcelable { + + private final String mCardId; + + /** + * Creates a new GetWalletCardsRequest. + * + * @param cardId The {@link WalletCard#getCardId() cardId} of the wallet card that is currently + * selected. + */ + public SelectWalletCardRequest(@NonNull String cardId) { + this.mCardId = cardId; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mCardId); + } + + @NonNull + public static final Creator<SelectWalletCardRequest> CREATOR = + new Creator<SelectWalletCardRequest>() { + @Override + public SelectWalletCardRequest createFromParcel(Parcel source) { + String cardId = source.readString(); + return new SelectWalletCardRequest(cardId); + } + + @Override + public SelectWalletCardRequest[] newArray(int size) { + return new SelectWalletCardRequest[size]; + } + }; + + /** + * The {@link WalletCard#getCardId() cardId} of the wallet card that is currently selected. + */ + @NonNull + public String getCardId() { + return mCardId; + } +} diff --git a/core/java/android/service/quickaccesswallet/WalletCard.aidl b/core/java/android/service/quickaccesswallet/WalletCard.aidl new file mode 100644 index 000000000000..115213da2752 --- /dev/null +++ b/core/java/android/service/quickaccesswallet/WalletCard.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.quickaccesswallet; + +parcelable WalletCard;
\ No newline at end of file diff --git a/core/java/android/service/quickaccesswallet/WalletCard.java b/core/java/android/service/quickaccesswallet/WalletCard.java new file mode 100644 index 000000000000..c3b1a4b0b85f --- /dev/null +++ b/core/java/android/service/quickaccesswallet/WalletCard.java @@ -0,0 +1,245 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.quickaccesswallet; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.PendingIntent; +import android.graphics.drawable.Icon; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +/** + * A {@link WalletCard} can represent anything that a user might carry in their wallet -- a credit + * card, library card, transit pass, etc. Cards are identified by a String identifier and contain a + * card image, card image content description, and a {@link PendingIntent} to be used if the user + * clicks on the card. Cards may be displayed with an icon and label, though these are optional. + */ +public final class WalletCard implements Parcelable { + + private final String mCardId; + private final Icon mCardImage; + private final CharSequence mContentDescription; + private final PendingIntent mPendingIntent; + private final Icon mCardIcon; + private final CharSequence mCardLabel; + + private WalletCard(Builder builder) { + this.mCardId = builder.mCardId; + this.mCardImage = builder.mCardImage; + this.mContentDescription = builder.mContentDescription; + this.mPendingIntent = builder.mPendingIntent; + this.mCardIcon = builder.mCardIcon; + this.mCardLabel = builder.mCardLabel; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mCardId); + mCardImage.writeToParcel(dest, flags); + TextUtils.writeToParcel(mContentDescription, dest, flags); + PendingIntent.writePendingIntentOrNullToParcel(mPendingIntent, dest); + if (mCardIcon == null) { + dest.writeByte((byte) 0); + } else { + dest.writeByte((byte) 1); + mCardIcon.writeToParcel(dest, flags); + } + TextUtils.writeToParcel(mCardLabel, dest, flags); + } + + private static WalletCard readFromParcel(Parcel source) { + String cardId = source.readString(); + Icon cardImage = Icon.CREATOR.createFromParcel(source); + CharSequence contentDesc = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); + PendingIntent pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(source); + Icon cardIcon = source.readByte() == 0 ? null : Icon.CREATOR.createFromParcel(source); + CharSequence cardLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); + return new Builder(cardId, cardImage, contentDesc, pendingIntent) + .setCardIcon(cardIcon) + .setCardLabel(cardLabel) + .build(); + } + + @NonNull + public static final Creator<WalletCard> CREATOR = + new Creator<WalletCard>() { + @Override + public WalletCard createFromParcel(Parcel source) { + return readFromParcel(source); + } + + @Override + public WalletCard[] newArray(int size) { + return new WalletCard[size]; + } + }; + + /** + * The card id must be unique within the list of cards returned. + */ + @NonNull + public String getCardId() { + return mCardId; + } + + /** + * The visual representation of the card. If the card image Icon is a bitmap, it should have a + * width of {@link GetWalletCardsRequest#getCardWidthPx()} and a height of {@link + * GetWalletCardsRequest#getCardHeightPx()}. + */ + @NonNull + public Icon getCardImage() { + return mCardImage; + } + + /** + * The content description of the card image. + */ + @NonNull + public CharSequence getContentDescription() { + return mContentDescription; + } + + /** + * If the user performs a click on the card, this PendingIntent will be sent. If the device is + * locked, the wallet will first request device unlock before sending the pending intent. + */ + @NonNull + public PendingIntent getPendingIntent() { + return mPendingIntent; + } + + /** + * An icon may be shown alongside the card image to convey information about how the card can be + * used, or if some other action must be taken before using the card. For example, an NFC logo + * could indicate that the card is NFC-enabled and will be provided to an NFC terminal if the + * phone is held in close proximity to the NFC reader. + * + * <p>If the supplied Icon is backed by a bitmap, it should have width and height + * {@link GetWalletCardsRequest#getIconSizePx()}. + */ + @Nullable + public Icon getCardIcon() { + return mCardIcon; + } + + /** + * A card label may be shown alongside the card image to convey information about how the card + * can be used, or if some other action must be taken before using the card. For example, an + * NFC-enabled card could be labeled "Hold near reader" to inform the user of how to use NFC + * cards when interacting with an NFC reader. + * + * <p>If the provided label is too long to fit on one line, it may be truncated and ellipsized. + */ + @Nullable + public CharSequence getCardLabel() { + return mCardLabel; + } + + /** + * Builder for {@link WalletCard} objects. You must to provide cardId, cardImage, + * contentDescription, and pendingIntent. If the card is opaque and should be shown with + * elevation, set hasShadow to true. cardIcon and cardLabel are optional. + */ + public static final class Builder { + private String mCardId; + private Icon mCardImage; + private CharSequence mContentDescription; + private PendingIntent mPendingIntent; + private Icon mCardIcon; + private CharSequence mCardLabel; + + /** + * @param cardId The card id must be non-null and unique within the list of + * cards returned. <b>Note: + * </b> this card ID should <b>not</b> contain PII (Personally + * Identifiable Information, * such as username or email + * address). + * @param cardImage The visual representation of the card. If the card image Icon + * is a bitmap, it should have a width of {@link + * GetWalletCardsRequest#getCardWidthPx()} and a height of {@link + * GetWalletCardsRequest#getCardHeightPx()}. If the card image + * does not have these dimensions, it may appear distorted when it + * is scaled to fit these dimensions on screen. + * @param contentDescription The content description of the card image. This field is + * required. + * <b>Note: </b> this message should <b>not</b> contain PII + * (Personally Identifiable Information, such as username or email + * address). + * @param pendingIntent If the user performs a click on the card, this PendingIntent + * will be sent. If the device is locked, the wallet will first + * request device unlock before sending the pending intent. + */ + public Builder(@NonNull String cardId, + @NonNull Icon cardImage, + @NonNull CharSequence contentDescription, + @NonNull PendingIntent pendingIntent) { + mCardId = cardId; + mCardImage = cardImage; + mContentDescription = contentDescription; + mPendingIntent = pendingIntent; + } + + /** + * An icon may be shown alongside the card image to convey information about how the card + * can be used, or if some other action must be taken before using the card. For example, an + * NFC logo could indicate that the card is NFC-enabled and will be provided to an NFC + * terminal if the phone is held in close proximity to the NFC reader. This field is + * optional. + * + * <p>If the supplied Icon is backed by a bitmap, it should have width and height + * {@link GetWalletCardsRequest#getIconSizePx()}. + */ + @NonNull + public Builder setCardIcon(@Nullable Icon cardIcon) { + mCardIcon = cardIcon; + return this; + } + + /** + * A card label may be shown alongside the card image to convey information about how the + * card can be used, or if some other action must be taken before using the card. For + * example, an NFC-enabled card could be labeled "Hold near reader" to inform the user of + * how to use NFC cards when interacting with an NFC reader. This field is optional. + * <b>Note: </b> this card label should <b>not</b> contain PII (Personally Identifiable + * Information, such as username or email address). If the provided label is too long to fit + * on one line, it may be truncated and ellipsized. + */ + @NonNull + public Builder setCardLabel(@Nullable CharSequence cardLabel) { + mCardLabel = cardLabel; + return this; + } + + /** + * Builds a new {@link WalletCard} instance. + * + * @return A built response. + */ + @NonNull + public WalletCard build() { + return new WalletCard(this); + } + } +} diff --git a/core/java/android/service/quickaccesswallet/WalletServiceEvent.aidl b/core/java/android/service/quickaccesswallet/WalletServiceEvent.aidl new file mode 100644 index 000000000000..891cf1d585bf --- /dev/null +++ b/core/java/android/service/quickaccesswallet/WalletServiceEvent.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.quickaccesswallet; + +parcelable WalletServiceEvent;
\ No newline at end of file diff --git a/core/java/android/service/quickaccesswallet/WalletServiceEvent.java b/core/java/android/service/quickaccesswallet/WalletServiceEvent.java new file mode 100644 index 000000000000..fb524be852fa --- /dev/null +++ b/core/java/android/service/quickaccesswallet/WalletServiceEvent.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.quickaccesswallet; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Represents a request from the {@link QuickAccessWalletService wallet app} to the Quick Access + * Wallet in System UI. Background events may necessitate that the Quick Access Wallet update its + * view. For example, if the wallet application handles an NFC payment while the Quick Access Wallet + * is being shown, it needs to tell the Quick Access Wallet so that the wallet can be dismissed and + * Activity showing the payment can be displayed to the user. + */ +public final class WalletServiceEvent implements Parcelable { + + /** + * An NFC payment has started. If the Quick Access Wallet is in a system window, it will need to + * be dismissed so that an Activity showing the payment can be displayed. + */ + public static final int TYPE_NFC_PAYMENT_STARTED = 1; + + /** + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({TYPE_NFC_PAYMENT_STARTED}) + public @interface EventType { + } + + @EventType + private final int mEventType; + + /** + * Creates a new DismissWalletRequest. + */ + public WalletServiceEvent(@EventType int eventType) { + this.mEventType = eventType; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mEventType); + } + + @NonNull + public static final Creator<WalletServiceEvent> CREATOR = + new Creator<WalletServiceEvent>() { + @Override + public WalletServiceEvent createFromParcel(Parcel source) { + int eventType = source.readInt(); + return new WalletServiceEvent(eventType); + } + + @Override + public WalletServiceEvent[] newArray(int size) { + return new WalletServiceEvent[size]; + } + }; + + /** + * @return the event type + */ + @EventType + public int getEventType() { + return mEventType; + } +} diff --git a/core/java/android/service/quickaccesswallet/WalletServiceEventListenerRequest.aidl b/core/java/android/service/quickaccesswallet/WalletServiceEventListenerRequest.aidl new file mode 100644 index 000000000000..155f92e8acc6 --- /dev/null +++ b/core/java/android/service/quickaccesswallet/WalletServiceEventListenerRequest.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.quickaccesswallet; + +parcelable WalletServiceEventListenerRequest;
\ No newline at end of file diff --git a/core/java/android/service/quickaccesswallet/WalletServiceEventListenerRequest.java b/core/java/android/service/quickaccesswallet/WalletServiceEventListenerRequest.java new file mode 100644 index 000000000000..223110e45d01 --- /dev/null +++ b/core/java/android/service/quickaccesswallet/WalletServiceEventListenerRequest.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.quickaccesswallet; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Register a dismiss request listener with the QuickAccessWalletService. This allows the service to + * dismiss the wallet if it needs to show a payment activity in response to an NFC event. + * + * @hide + */ +public final class WalletServiceEventListenerRequest implements Parcelable { + + private final String mListenerId; + + /** + * Construct a new {@code DismissWalletListenerRequest}. + * + * @param listenerKey A unique key that identifies the listener. + */ + public WalletServiceEventListenerRequest(@NonNull String listenerKey) { + mListenerId = listenerKey; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mListenerId); + } + + private static WalletServiceEventListenerRequest readFromParcel(Parcel source) { + String listenerId = source.readString(); + return new WalletServiceEventListenerRequest(listenerId); + } + + @NonNull + public static final Creator<WalletServiceEventListenerRequest> CREATOR = + new Creator<WalletServiceEventListenerRequest>() { + @Override + public WalletServiceEventListenerRequest createFromParcel(Parcel source) { + return readFromParcel(source); + } + + @Override + public WalletServiceEventListenerRequest[] newArray(int size) { + return new WalletServiceEventListenerRequest[size]; + } + }; + + /** + * Returns the unique key that identifies the wallet dismiss request listener. + */ + @NonNull + public String getListenerId() { + return mListenerId; + } +} 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/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl index 00e0b7c170be..84b6869bf620 100644 --- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl +++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl @@ -37,4 +37,5 @@ oneway interface IWallpaperEngine { void requestWallpaperColors(); @UnsupportedAppUsage void destroy(); + void setZoomOut(float scale); } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index e3fd8d297316..dd78c78654c3 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -16,6 +16,7 @@ package android.service.wallpaper; +import android.annotation.FloatRange; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -122,6 +123,9 @@ public abstract class WallpaperService extends Service { private static final int MSG_WINDOW_MOVED = 10035; private static final int MSG_TOUCH_EVENT = 10040; private static final int MSG_REQUEST_WALLPAPER_COLORS = 10050; + private static final int MSG_SCALE = 10100; + + private static final float MAX_SCALE = 1.15f; private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000; @@ -170,6 +174,7 @@ public abstract class WallpaperService extends Service { int mType; int mCurWidth; int mCurHeight; + float mZoom = 0f; int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; int mWindowPrivateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS; @@ -496,6 +501,15 @@ public abstract class WallpaperService extends Service { } /** + * Returns the current scale of the surface + * @hide + */ + @VisibleForTesting + public float getZoom() { + return mZoom; + } + + /** * Called once to initialize the engine. After returning, the * engine's surface will be created by the framework. */ @@ -623,6 +637,16 @@ public abstract class WallpaperService extends Service { } /** + * Called when the zoom level of the wallpaper changed. + * This method will be called with the initial zoom level when the surface is created. + * + * @param zoom the zoom level, between 0 indicating fully zoomed in and 1 indicating fully + * zoomed out. + */ + public void onZoomChanged(@FloatRange(from = 0f, to = 1f) float zoom) { + } + + /** * Notifies the engine that wallpaper colors changed significantly. * This will trigger a {@link #onComputeColors()} call. */ @@ -706,6 +730,7 @@ public abstract class WallpaperService extends Service { out.print(prefix); out.print("mConfiguration="); out.println(mMergedConfiguration.getMergedConfiguration()); out.print(prefix); out.print("mLayout="); out.println(mLayout); + out.print(prefix); out.print("mZoom="); out.println(mZoom); synchronized (mLock) { out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset); out.print(" mPendingXOffset="); out.println(mPendingXOffset); @@ -721,6 +746,37 @@ public abstract class WallpaperService extends Service { } } + /** + * Set the wallpaper zoom to the given value. This value will be ignored when in ambient + * mode (and zoom will be reset to 0). + * @hide + * @param zoom between 0 and 1 (inclusive) indicating fully zoomed in to fully zoomed out + * respectively. + */ + @VisibleForTesting + public void setZoom(float zoom) { + if (DEBUG) { + Log.v(TAG, "set zoom received: " + zoom); + } + boolean updated = false; + synchronized (mLock) { + if (DEBUG) { + Log.v(TAG, "mZoom: " + mZoom + " updated: " + zoom); + } + if (mIsInAmbientMode) { + mZoom = 0; + } + if (Float.compare(zoom, mZoom) != 0) { + mZoom = zoom; + updated = true; + } + } + if (DEBUG) Log.v(TAG, "setZoom updated? " + updated); + if (updated && !mDestroyed) { + onZoomChanged(mZoom); + } + } + private void dispatchPointer(MotionEvent event) { if (event.isTouchEvent()) { synchronized (mLock) { @@ -813,7 +869,7 @@ 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; @@ -920,6 +976,7 @@ public abstract class WallpaperService extends Service { c.surfaceCreated(mSurfaceHolder); } } + onZoomChanged(0f); } redrawNeeded |= creating || (relayoutResult @@ -1080,6 +1137,7 @@ public abstract class WallpaperService extends Service { mIsInAmbientMode = inAmbientMode; if (mCreated) { onAmbientModeChanged(inAmbientMode, animationDuration); + setZoom(0); } } } @@ -1354,6 +1412,11 @@ public abstract class WallpaperService extends Service { } } + public void setZoomOut(float scale) { + Message msg = mCaller.obtainMessageI(MSG_SCALE, Float.floatToIntBits(scale)); + mCaller.sendMessage(msg); + } + public void reportShown() { if (!mShownReported) { mShownReported = true; @@ -1426,6 +1489,9 @@ public abstract class WallpaperService extends Service { case MSG_UPDATE_SURFACE: mEngine.updateSurface(true, false, false); break; + case MSG_SCALE: + mEngine.setZoom(Float.intBitsToFloat(message.arg1)); + break; case MSG_VISIBILITY_CHANGED: if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine + ": " + message.arg1); diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index 4def80eb34a0..67cac0eb3b21 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -188,6 +188,10 @@ public class PhoneStateListener { * Listen for {@link android.telephony.Annotation.PreciseCallStates} of ringing, * background and foreground calls. * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * * @hide */ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) @@ -197,13 +201,13 @@ public class PhoneStateListener { /** * Listen for {@link PreciseDataConnectionState} on the data connection (cellular). * - * <p>Requires permission {@link android.Manifest.permission#MODIFY_PHONE_STATE} + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} * or the calling app has carrier privileges * (see {@link TelephonyManager#hasCarrierPrivileges}). * * @see #onPreciseDataConnectionStateChanged */ - @RequiresPermission((android.Manifest.permission.MODIFY_PHONE_STATE)) + @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 0x00001000; /** @@ -328,26 +332,36 @@ public class PhoneStateListener { * Listen for call disconnect causes which contains {@link DisconnectCause} and * {@link PreciseDisconnectCause}. * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * */ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) public static final int LISTEN_CALL_DISCONNECT_CAUSES = 0x02000000; /** * Listen for changes to the call attributes of a currently active call. - * {@more} - * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE - * READ_PRECISE_PHONE_STATE} + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). * * @see #onCallAttributesChanged * @hide */ @SystemApi + @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 0x04000000; /** * Listen for IMS call disconnect causes which contains * {@link android.telephony.ims.ImsReasonInfo} * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * * @see #onImsCallDisconnectCauseChanged(ImsReasonInfo) */ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index e25826c25e35..57375919e6cd 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -28,8 +28,10 @@ import android.telephony.Annotation.ApnType; import android.telephony.Annotation.CallState; import android.telephony.Annotation.DataActivityType; import android.telephony.Annotation.DataFailureCause; +import android.telephony.Annotation.DisconnectCauses; import android.telephony.Annotation.NetworkType; import android.telephony.Annotation.PreciseCallStates; +import android.telephony.Annotation.PreciseDisconnectCauses; import android.telephony.Annotation.RadioPowerState; import android.telephony.Annotation.SimActivationState; import android.telephony.Annotation.SrvccState; @@ -56,6 +58,7 @@ import java.util.concurrent.Executor; * @hide */ @SystemApi +@TestApi public class TelephonyRegistryManager { private static final String TAG = "TelephonyRegistryManager"; @@ -225,11 +228,9 @@ public class TelephonyRegistryManager { * invalid. * @param state latest call state. e.g, offhook, ringing * @param incomingNumber incoming phone number. - * - * @hide */ public void notifyCallStateChanged(int subId, int slotIndex, @CallState int state, - String incomingNumber) { + @Nullable String incomingNumber) { try { sRegistry.notifyCallState(slotIndex, subId, state, incomingNumber); } catch (RemoteException ex) { @@ -263,10 +264,8 @@ public class TelephonyRegistryManager { * @param slotIndex for which the service state changed. Can be derived from subId except * subId is invalid. * @param state service state e.g, in service, out of service or roaming status. - * - * @hide */ - public void notifyServiceStateChanged(int subId, int slotIndex, ServiceState state) { + public void notifyServiceStateChanged(int subId, int slotIndex, @NonNull ServiceState state) { try { sRegistry.notifyServiceStateForPhoneId(slotIndex, subId, state); } catch (RemoteException ex) { @@ -281,11 +280,9 @@ public class TelephonyRegistryManager { * @param slotIndex for which the signalstrength changed. Can be derived from subId except when * subId is invalid. * @param signalStrength e.g, signalstrength level {@see SignalStrength#getLevel()} - * - * @hide */ public void notifySignalStrengthChanged(int subId, int slotIndex, - SignalStrength signalStrength) { + @NonNull SignalStrength signalStrength) { try { sRegistry.notifySignalStrengthForPhoneId(slotIndex, subId, signalStrength); } catch (RemoteException ex) { @@ -302,8 +299,6 @@ public class TelephonyRegistryManager { * except when subId is invalid. * @param msgWaitingInd {@code true} indicates there is message-waiting indicator, {@code false} * otherwise. - * - * @hide */ public void notifyMessageWaitingChanged(int subId, int slotIndex, boolean msgWaitingInd) { try { @@ -319,8 +314,6 @@ public class TelephonyRegistryManager { * @param subId for which call forwarding status changed. * @param callForwardInd {@code true} indicates there is call forwarding, {@code false} * otherwise. - * - * @hide */ public void notifyCallForwardingChanged(int subId, boolean callForwardInd) { try { @@ -336,8 +329,6 @@ public class TelephonyRegistryManager { * @param subId for which data activity state changed. * @param dataActivityType indicates the latest data activity type e.g, {@link * TelephonyManager#DATA_ACTIVITY_IN} - * - * @hide */ public void notifyDataActivityChanged(int subId, @DataActivityType int dataActivityType) { try { @@ -358,10 +349,9 @@ public class TelephonyRegistryManager { * * @see android.telephony.PreciseDataConnection * @see TelephonyManager#DATA_DISCONNECTED - * @hide */ public void notifyDataConnectionForSubscriber(int slotIndex, int subId, - @ApnType int apnType, PreciseDataConnectionState preciseState) { + @ApnType int apnType, @Nullable PreciseDataConnectionState preciseState) { try { sRegistry.notifyDataConnectionForSubscriber( slotIndex, subId, apnType, preciseState); @@ -378,10 +368,8 @@ public class TelephonyRegistryManager { * subId is invalid. * @param callQuality Information about call quality e.g, call quality level * @param networkType associated with this data connection. e.g, LTE - * - * @hide */ - public void notifyCallQualityChanged(int subId, int slotIndex, CallQuality callQuality, + public void notifyCallQualityChanged(int subId, int slotIndex, @NonNull CallQuality callQuality, @NetworkType int networkType) { try { sRegistry.notifyCallQualityChanged(callQuality, slotIndex, subId, networkType); @@ -396,8 +384,6 @@ public class TelephonyRegistryManager { * @param subId for which emergency number list changed. * @param slotIndex for which emergency number list changed. Can be derived from subId except * when subId is invalid. - * - * @hide */ public void notifyEmergencyNumberList(int subId, int slotIndex) { try { @@ -414,8 +400,6 @@ public class TelephonyRegistryManager { * @param slotIndex for which radio power state changed. Can be derived from subId except when * subId is invalid. * @param radioPowerState the current modem radio state. - * - * @hide */ public void notifyRadioPowerStateChanged(int subId, int slotIndex, @RadioPowerState int radioPowerState) { @@ -430,10 +414,8 @@ public class TelephonyRegistryManager { * Notify {@link PhoneCapability} changed. * * @param phoneCapability the capability of the modem group. - * - * @hide */ - public void notifyPhoneCapabilityChanged(PhoneCapability phoneCapability) { + public void notifyPhoneCapabilityChanged(@NonNull PhoneCapability phoneCapability) { try { sRegistry.notifyPhoneCapabilityChanged(phoneCapability); } catch (RemoteException ex) { @@ -462,8 +444,6 @@ public class TelephonyRegistryManager { * @param slotIndex for which data activation state changed. Can be derived from subId except * when subId is invalid. * @param activationState sim activation state e.g, activated. - * - * @hide */ public void notifyDataActivationStateChanged(int subId, int slotIndex, @SimActivationState int activationState) { @@ -483,8 +463,6 @@ public class TelephonyRegistryManager { * @param slotIndex for which voice activation state changed. Can be derived from subId except * subId is invalid. * @param activationState sim activation state e.g, activated. - * - * @hide */ public void notifyVoiceActivationStateChanged(int subId, int slotIndex, @SimActivationState int activationState) { @@ -504,8 +482,6 @@ public class TelephonyRegistryManager { * @param slotIndex for which mobile data state has changed. Can be derived from subId except * when subId is invalid. * @param state {@code true} indicates mobile data is enabled/on. {@code false} otherwise. - * - * @hide */ public void notifyUserMobileDataStateChanged(int slotIndex, int subId, boolean state) { try { @@ -516,31 +492,12 @@ public class TelephonyRegistryManager { } /** - * TODO: this is marked as deprecated, can we move this one safely? - * - * @param subId - * @param slotIndex - * @param rawData - * - * @hide - */ - public void notifyOemHookRawEventForSubscriber(int subId, int slotIndex, byte[] rawData) { - try { - sRegistry.notifyOemHookRawEventForSubscriber(slotIndex, subId, rawData); - } catch (RemoteException ex) { - // system process is dead - } - } - - /** * Notify IMS call disconnect causes which contains {@link android.telephony.ims.ImsReasonInfo}. * * @param subId for which ims call disconnect. * @param imsReasonInfo the reason for ims call disconnect. - * - * @hide */ - public void notifyImsDisconnectCause(int subId, ImsReasonInfo imsReasonInfo) { + public void notifyImsDisconnectCause(int subId, @NonNull ImsReasonInfo imsReasonInfo) { try { sRegistry.notifyImsDisconnectCause(subId, imsReasonInfo); } catch (RemoteException ex) { @@ -557,11 +514,9 @@ public class TelephonyRegistryManager { * @param apnType the apn type bitmask, defined with {@code ApnSetting#TYPE_*} flags. * @param apn the APN {@link ApnSetting#getApnName()} of this data connection. * @param failCause data fail cause. - * - * @hide */ public void notifyPreciseDataConnectionFailed(int subId, int slotIndex, @ApnType int apnType, - String apn, @DataFailureCause int failCause) { + @Nullable String apn, @DataFailureCause int failCause) { try { sRegistry.notifyPreciseDataConnectionFailed(slotIndex, subId, apnType, apn, failCause); } catch (RemoteException ex) { @@ -575,8 +530,6 @@ public class TelephonyRegistryManager { * * @param subId for which srvcc state changed. * @param state srvcc state - * - * @hide */ public void notifySrvccStateChanged(int subId, @SrvccState int state) { try { @@ -596,8 +549,6 @@ public class TelephonyRegistryManager { * @param ringCallPreciseState ringCall state. * @param foregroundCallPreciseState foreground call state. * @param backgroundCallPreciseState background call state. - * - * @hide */ public void notifyPreciseCallState(int subId, int slotIndex, @PreciseCallStates int ringCallPreciseState, @@ -621,10 +572,9 @@ public class TelephonyRegistryManager { * @param cause {@link DisconnectCause} for the disconnected call. * @param preciseCause {@link android.telephony.PreciseDisconnectCause} for the disconnected * call. - * - * @hide */ - public void notifyDisconnectCause(int slotIndex, int subId, int cause, int preciseCause) { + public void notifyDisconnectCause(int slotIndex, int subId, @DisconnectCauses int cause, + @PreciseDisconnectCauses int preciseCause) { try { sRegistry.notifyDisconnectCause(slotIndex, subId, cause, preciseCause); } catch (RemoteException ex) { @@ -637,10 +587,8 @@ public class TelephonyRegistryManager { * * <p>To be compatible with {@link TelephonyRegistry}, use {@link CellIdentity} which is * parcelable, and convert to CellLocation in client code. - * - * @hide */ - public void notifyCellLocation(int subId, CellIdentity cellLocation) { + public void notifyCellLocation(int subId, @NonNull CellIdentity cellLocation) { try { sRegistry.notifyCellLocationForSubscriber(subId, cellLocation); } catch (RemoteException ex) { @@ -654,10 +602,8 @@ public class TelephonyRegistryManager { * * @param subId for which cellinfo changed. * @param cellInfo A list of cellInfo associated with the given subscription. - * - * @hide */ - public void notifyCellInfoChanged(int subId, List<CellInfo> cellInfo) { + public void notifyCellInfoChanged(int subId, @NonNull List<CellInfo> cellInfo) { try { sRegistry.notifyCellInfoForSubscriber(subId, cellInfo); } catch (RemoteException ex) { @@ -666,8 +612,8 @@ public class TelephonyRegistryManager { } /** - * @param activeDataSubId - * @hide + * Notify that the active data subscription ID has changed. + * @param activeDataSubId The new subscription ID for active data */ public void notifyActiveDataSubIdChanged(int activeDataSubId) { try { diff --git a/core/java/android/timezone/CountryTimeZones.java b/core/java/android/timezone/CountryTimeZones.java index ada59d6d7d55..e5bbdf4b6eea 100644 --- a/core/java/android/timezone/CountryTimeZones.java +++ b/core/java/android/timezone/CountryTimeZones.java @@ -43,6 +43,7 @@ public final class CountryTimeZones { @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public static final class TimeZoneMapping { + @NonNull private libcore.timezone.CountryTimeZones.TimeZoneMapping mDelegate; TimeZoneMapping(libcore.timezone.CountryTimeZones.TimeZoneMapping delegate) { diff --git a/core/java/android/timezone/TelephonyLookup.java b/core/java/android/timezone/TelephonyLookup.java index 39dbe85cb485..eebccf4aa577 100644 --- a/core/java/android/timezone/TelephonyLookup.java +++ b/core/java/android/timezone/TelephonyLookup.java @@ -36,12 +36,8 @@ public class TelephonyLookup { @GuardedBy("sLock") private static TelephonyLookup sInstance; - @NonNull - private final libcore.timezone.TelephonyLookup mDelegate; - /** - * Obtains an instance for use when resolving telephony time zone information. This method never - * returns {@code null}. + * Obtains an instance for use when resolving telephony time zone information. */ @NonNull public static TelephonyLookup getInstance() { @@ -53,6 +49,9 @@ public class TelephonyLookup { } } + @NonNull + private final libcore.timezone.TelephonyLookup mDelegate; + private TelephonyLookup(@NonNull libcore.timezone.TelephonyLookup delegate) { mDelegate = Objects.requireNonNull(delegate); } diff --git a/core/java/android/timezone/TelephonyNetworkFinder.java b/core/java/android/timezone/TelephonyNetworkFinder.java index a81a516c4b33..079d0882f191 100644 --- a/core/java/android/timezone/TelephonyNetworkFinder.java +++ b/core/java/android/timezone/TelephonyNetworkFinder.java @@ -23,7 +23,7 @@ import android.annotation.SystemApi; import java.util.Objects; /** - * A class that can find telephony networks loaded via {@link TelephonyLookup}. + * A class that can find telephony network information loaded via {@link TelephonyLookup}. * * @hide */ diff --git a/core/java/android/timezone/TimeZoneFinder.java b/core/java/android/timezone/TimeZoneFinder.java index 15dfe62bb789..9327b001a9c8 100644 --- a/core/java/android/timezone/TimeZoneFinder.java +++ b/core/java/android/timezone/TimeZoneFinder.java @@ -22,8 +22,10 @@ import android.annotation.SystemApi; import com.android.internal.annotations.GuardedBy; +import java.util.Objects; + /** - * A class that can be used to find time zones. + * A class that can be used to find time zones using information like country and offset. * * @hide */ @@ -34,15 +36,8 @@ public final class TimeZoneFinder { @GuardedBy("sLock") private static TimeZoneFinder sInstance; - private final libcore.timezone.TimeZoneFinder mDelegate; - - private TimeZoneFinder(libcore.timezone.TimeZoneFinder delegate) { - mDelegate = delegate; - } - /** - * Obtains an instance for use when resolving telephony time zone information. This method never - * returns {@code null}. + * Obtains the singleton instance. */ @NonNull public static TimeZoneFinder getInstance() { @@ -54,6 +49,22 @@ public final class TimeZoneFinder { return sInstance; } + @NonNull + private final libcore.timezone.TimeZoneFinder mDelegate; + + private TimeZoneFinder(@NonNull libcore.timezone.TimeZoneFinder delegate) { + mDelegate = Objects.requireNonNull(delegate); + } + + /** + * Returns the IANA rules version associated with the data. If there is no version information + * or there is a problem reading the file then {@code null} is returned. + */ + @Nullable + public String getIanaVersion() { + return mDelegate.getIanaVersion(); + } + /** * Returns a {@link CountryTimeZones} object associated with the specified country code. * Caching is handled as needed. If the country code is not recognized or there is an error diff --git a/core/java/android/timezone/TzDataSetVersion.java b/core/java/android/timezone/TzDataSetVersion.java new file mode 100644 index 000000000000..aba7c4c15903 --- /dev/null +++ b/core/java/android/timezone/TzDataSetVersion.java @@ -0,0 +1,154 @@ +/* + * 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.timezone; + +import android.annotation.NonNull; +import android.annotation.SystemApi; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.IOException; +import java.util.Objects; + +/** + * Version information associated with the set of time zone data on a device. + * + * <p>Time Zone Data Sets have a major ({@link #getFormatMajorVersion()}) and minor + * ({@link #currentFormatMinorVersion()}) version number: + * <ul> + * <li>Major version numbers are mutually incompatible. e.g. v2 is not compatible with a v1 or a + * v3 device.</li> + * <li>Minor version numbers are backwards compatible. e.g. a v2.2 data set will work + * on a v2.1 device but not a v2.3 device. The minor version is reset to 1 when the major version + * is incremented.</li> + * </ul> + * + * <p>Data sets contain time zone rules and other data associated wtih a tzdb release + * ({@link #getRulesVersion()}) and an additional Android-specific revision number + * ({@link #getRevision()}). + * + * <p>See platform/system/timezone/README.android for more information. + * @hide + */ +@VisibleForTesting +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public final class TzDataSetVersion { + + /** + * Returns the major tz data format version supported by this device. + */ + public static int currentFormatMajorVersion() { + return libcore.timezone.TzDataSetVersion.currentFormatMajorVersion(); + } + + /** + * Returns the minor tz data format version supported by this device. + */ + public static int currentFormatMinorVersion() { + return libcore.timezone.TzDataSetVersion.currentFormatMinorVersion(); + } + + /** + * Returns true if the version information provided would be compatible with this device, i.e. + * with the current system image, and set of active modules. + */ + public static boolean isCompatibleWithThisDevice(TzDataSetVersion tzDataSetVersion) { + return libcore.timezone.TzDataSetVersion.isCompatibleWithThisDevice( + tzDataSetVersion.mDelegate); + } + + /** + * Reads the current Android time zone data set version file. + */ + @NonNull + public static TzDataSetVersion read() throws IOException, TzDataSetException { + try { + return new TzDataSetVersion( + libcore.timezone.TzDataSetVersion.readTimeZoneModuleVersion()); + } catch (libcore.timezone.TzDataSetVersion.TzDataSetException e) { + throw new TzDataSetException(e.getMessage(), e); + } + } + + /** + * A checked exception used in connection with time zone data sets. + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static class TzDataSetException extends Exception { + + /** Creates an instance with a message. */ + public TzDataSetException(String message) { + super(message); + } + + /** Creates an instance with a message and a cause. */ + public TzDataSetException(String message, Throwable cause) { + super(message, cause); + } + } + + @NonNull + private final libcore.timezone.TzDataSetVersion mDelegate; + + private TzDataSetVersion(@NonNull libcore.timezone.TzDataSetVersion delegate) { + mDelegate = Objects.requireNonNull(delegate); + } + + /** Returns the major version number. See {@link TzDataSetVersion}. */ + public int getFormatMajorVersion() { + return mDelegate.formatMajorVersion; + } + + /** Returns the minor version number. See {@link TzDataSetVersion}. */ + public int getFormatMinorVersion() { + return mDelegate.formatMinorVersion; + } + + /** Returns the tzdb version string. See {@link TzDataSetVersion}. */ + @NonNull + public String getRulesVersion() { + return mDelegate.rulesVersion; + } + + /** Returns the Android revision. See {@link TzDataSetVersion}. */ + public int getRevision() { + return mDelegate.revision; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TzDataSetVersion that = (TzDataSetVersion) o; + return mDelegate.equals(that.mDelegate); + } + + @Override + public int hashCode() { + return Objects.hash(mDelegate); + } + + @Override + public String toString() { + return mDelegate.toString(); + } +} diff --git a/core/java/android/timezone/ZoneInfoDb.java b/core/java/android/timezone/ZoneInfoDb.java new file mode 100644 index 000000000000..eb191e8e3272 --- /dev/null +++ b/core/java/android/timezone/ZoneInfoDb.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.timezone; + +import android.annotation.NonNull; +import android.annotation.SystemApi; + +import com.android.internal.annotations.GuardedBy; + +import java.util.Objects; + +/** + * Android's internal factory for java.util.TimeZone objects. Provides access to core library time + * zone metadata not available via {@link java.util.TimeZone}. + * + * @hide + */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public final class ZoneInfoDb { + + private static Object sLock = new Object(); + @GuardedBy("sLock") + private static ZoneInfoDb sInstance; + + /** + * Obtains the singleton instance. + */ + @NonNull + public static ZoneInfoDb getInstance() { + synchronized (sLock) { + if (sInstance == null) { + sInstance = new ZoneInfoDb(libcore.timezone.ZoneInfoDB.getInstance()); + } + } + return sInstance; + } + + @NonNull + private final libcore.timezone.ZoneInfoDB mDelegate; + + private ZoneInfoDb(libcore.timezone.ZoneInfoDB delegate) { + mDelegate = Objects.requireNonNull(delegate); + } + + /** + * Returns the tzdb version in use. + */ + @NonNull + public String getVersion() { + return mDelegate.getVersion(); + } +} 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/util/TimeUtils.java b/core/java/android/util/TimeUtils.java index 0558204af4c9..37dd781d4468 100644 --- a/core/java/android/util/TimeUtils.java +++ b/core/java/android/util/TimeUtils.java @@ -89,7 +89,7 @@ public class TimeUtils { * * <p>The list returned may be different from other on-device sources like * {@link android.icu.util.TimeZone#getRegion(String)} as it can be curated to avoid - * contentious mappings. + * contentious or obsolete mappings. * * @param countryCode the ISO 3166-1 alpha-2 code for the country as can be obtained using * {@link java.util.Locale#getCountry()} 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 5c494c17669a..8d58ee83cd67 100644 --- a/core/java/android/view/ImeFocusController.java +++ b/core/java/android/view/ImeFocusController.java @@ -38,14 +38,32 @@ public final class ImeFocusController { private boolean mHasImeFocus = false; private View mServedView; private View mNextServedView; + private InputMethodManagerDelegate mDelegate; @UiThread ImeFocusController(@NonNull ViewRootImpl viewRootImpl) { mViewRootImpl = viewRootImpl; } + @NonNull private InputMethodManagerDelegate getImmDelegate() { - return mViewRootImpl.mContext.getSystemService(InputMethodManager.class).getDelegate(); + InputMethodManagerDelegate delegate = mDelegate; + if (delegate != null) { + return delegate; + } + 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 @@ -98,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; } @@ -106,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; } @@ -123,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; } @@ -164,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/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..0f2d2c21d28d 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#setOnContentApplyWindowInsets} with {@code null} or a listener that doesn't + * fit the navigation bar on the window content level. */ public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 0x00000200; @@ -3646,7 +3685,13 @@ 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#setOnContentApplyWindowInsets} with {@code null} or a listener that doesn't + * fit the status bar on the window content level. */ + @Deprecated public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400; /** @@ -3656,7 +3701,10 @@ 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..c8dea43e8f4c 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 @@ -1243,11 +1288,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 +1320,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 +1366,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 +1563,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 +1590,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 +1624,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 +1948,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 +2057,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 +2157,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 +2456,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 +2786,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 +2806,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 +2826,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 +2836,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 +3027,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 +3088,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 +3337,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 +3510,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 +3561,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/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/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java index cf494aec26f6..91e15c1949b6 100644 --- a/core/java/android/view/inputmethod/InputMethod.java +++ b/core/java/android/view/inputmethod/InputMethod.java @@ -27,6 +27,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.ResultReceiver; import android.util.Log; +import android.view.View; import android.view.autofill.AutofillId; import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; @@ -298,7 +299,30 @@ public interface InputMethod { * until deliberated dismissed by the user in its UI. */ public static final int SHOW_FORCED = 0x00002; - + + /** + * Request that any soft input part of the input method be shown to the user. + * + * @param flags Provides additional information about the show request. + * Currently may be 0 or have the bit {@link #SHOW_EXPLICIT} set. + * @param resultReceiver The client requesting the show may wish to + * be told the impact of their request, which should be supplied here. + * The result code should be + * {@link InputMethodManager#RESULT_UNCHANGED_SHOWN InputMethodManager.RESULT_UNCHANGED_SHOWN}, + * {@link InputMethodManager#RESULT_UNCHANGED_HIDDEN InputMethodManager.RESULT_UNCHANGED_HIDDEN}, + * {@link InputMethodManager#RESULT_SHOWN InputMethodManager.RESULT_SHOWN}, or + * {@link InputMethodManager#RESULT_HIDDEN InputMethodManager.RESULT_HIDDEN}. + * @param showInputToken an opaque {@link android.os.Binder} token to identify which API call + * of {@link InputMethodManager#showSoftInput(View, int)} is associated with + * this callback. + * @hide + */ + @MainThread + default public void showSoftInputWithToken(int flags, ResultReceiver resultReceiver, + IBinder showInputToken) { + showSoftInput(flags, resultReceiver); + } + /** * Request that any soft input part of the input method be shown to the user. * @@ -314,7 +338,7 @@ public interface InputMethod { */ @MainThread public void showSoftInput(int flags, ResultReceiver resultReceiver); - + /** * Request that any soft input part of the input method be hidden from the user. * @param flags Provides additional information about the show request. @@ -336,4 +360,12 @@ public interface InputMethod { */ @MainThread public void changeInputMethodSubtype(InputMethodSubtype subtype); + + /** + * Update token of the client window requesting {@link #showSoftInput(int, ResultReceiver)} + * @param showInputToken dummy app window token for window requesting + * {@link InputMethodManager#showSoftInput(View, int)} + * @hide + */ + public void setCurrentShowInputToken(IBinder showInputToken); } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 904e736d214e..307abd2f5f65 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -59,12 +59,12 @@ import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.SparseArray; import android.view.Display; +import android.view.ImeFocusController; import android.view.ImeInsetsSourceConsumer; import android.view.InputChannel; import android.view.InputEvent; import android.view.InputEventSender; import android.view.KeyEvent; -import android.view.ImeFocusController; import android.view.View; import android.view.ViewRootImpl; import android.view.WindowManager.LayoutParams.SoftInputModeFlags; @@ -562,14 +562,23 @@ public final class InputMethodManager { public boolean startInput(@StartInputReason int startInputReason, View focusedView, @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode, int windowFlags) { + final View servedView; synchronized (mH) { mCurrentTextBoxAttribute = null; mCompletions = null; mServedConnecting = true; - if (getServedViewLocked() != null && !getServedViewLocked().onCheckIsTextEditor()) { - // servedView has changed and it's not editable. - maybeCallServedViewChangedLocked(null); - } + servedView = getServedViewLocked(); + } + if (servedView != null && servedView.getHandler() != null) { + // Make sure View checks should be on the UI thread. + servedView.getHandler().post(() -> { + if (!servedView.onCheckIsTextEditor()) { + // servedView has changed and it's not editable. + synchronized (mH) { + maybeCallServedViewChangedLocked(null); + } + } + }); } return startInputInner(startInputReason, focusedView != null ? focusedView.getWindowToken() : null, startInputFlags, @@ -607,21 +616,22 @@ public final class InputMethodManager { mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */); } mWindowFocusGainFuture = mStartInputWorker.submit(() -> { - synchronized (mH) { - if (mCurRootView == null) { + final ImeFocusController controller = getFocusController(); + if (controller == null) { + return; + } + if (controller.checkFocus(forceNewFocus1, false)) { + // We need to restart input on the current focus view. This + // should be done in conjunction with telling the system service + // about the window gaining focus, to help make the transition + // smooth. + if (startInput(StartInputReason.WINDOW_FOCUS_GAIN, + focusedView, startInputFlags, softInputMode, windowFlags)) { return; } - if (mCurRootView.getImeFocusController().checkFocus(forceNewFocus1, false)) { - // We need to restart input on the current focus view. This - // should be done in conjunction with telling the system service - // about the window gaining focus, to help make the transition - // smooth. - if (startInput(StartInputReason.WINDOW_FOCUS_GAIN, - focusedView, startInputFlags, softInputMode, windowFlags)) { - return; - } - } + } + synchronized (mH) { // For some reason we didn't do a startInput + windowFocusGain, so // we'll just do a window focus gain and call it a day. try { @@ -716,6 +726,15 @@ public final class InputMethodManager { } } + private ImeFocusController getFocusController() { + synchronized (mH) { + if (mCurRootView != null) { + return mCurRootView.getImeFocusController(); + } + return null; + } + } + /** * Returns {@code true} when the given view has been served by Input Method. */ @@ -1617,7 +1636,8 @@ public final class InputMethodManager { } try { - return mService.showSoftInput(mClient, flags, resultReceiver); + return mService.showSoftInput( + mClient, view.getWindowToken(), flags, resultReceiver); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1639,7 +1659,8 @@ public final class InputMethodManager { Log.w(TAG, "showSoftInputUnchecked() is a hidden method, which will be removed " + "soon. If you are using android.support.v7.widget.SearchView, please update " + "to version 26.0 or newer version."); - mService.showSoftInput(mClient, flags, resultReceiver); + mService.showSoftInput( + mClient, mCurRootView.getView().getWindowToken(), flags, resultReceiver); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1793,6 +1814,12 @@ public final class InputMethodManager { startInputInner(StartInputReason.APP_CALLED_RESTART_INPUT_API, null, 0, 0, 0); } + /** + * Called when {@link DelegateImpl#startInput}, {@link #restartInput(View)}, + * {@link #MSG_BIND} or {@link #MSG_UNBIND}. + * Note that this method should *NOT* be called inside of {@code mH} lock to prevent start input + * background thread may blocked by other methods which already inside {@code mH} lock. + */ boolean startInputInner(@StartInputReason int startInputReason, @Nullable IBinder windowGainingFocus, @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode, int windowFlags) { @@ -1976,15 +2003,16 @@ public final class InputMethodManager { } /** + * Check the next served view from {@link ImeFocusController} if needs to start input. + * Note that this method should *NOT* be called inside of {@code mH} lock to prevent start input + * background thread may blocked by other methods which already inside {@code mH} lock. * @hide */ @UnsupportedAppUsage public void checkFocus() { - synchronized (mH) { - if (mCurRootView != null) { - mCurRootView.getImeFocusController().checkFocus(false /* forceNewFocus */, - true /* startInput */); - } + final ImeFocusController controller = getFocusController(); + if (controller != null) { + controller.checkFocus(false /* forceNewFocus */, true /* startInput */); } } diff --git a/core/java/android/webkit/PacProcessor.java b/core/java/android/webkit/PacProcessor.java index fe8bbb8b6c82..5ef450fa65dd 100644 --- a/core/java/android/webkit/PacProcessor.java +++ b/core/java/android/webkit/PacProcessor.java @@ -54,5 +54,5 @@ public interface PacProcessor { * For example: "PROXY xxx.xxx.xxx.xxx:xx; SOCKS yyy.yyy.yyy:yy". */ @Nullable - String makeProxyRequest(@NonNull String url); + String findProxyForUrl(@NonNull String url); } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 513e72f27901..cdf8c686ef00 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -220,7 +220,9 @@ public class Editor { private final boolean mHapticTextHandleEnabled; - private final MagnifierMotionAnimator mMagnifierAnimator; + @Nullable + private MagnifierMotionAnimator mMagnifierAnimator; + private final Runnable mUpdateMagnifierRunnable = new Runnable() { @Override public void run() { @@ -387,7 +389,10 @@ public class Editor { // Specifies whether the cursor control feature set is enabled. // This can only be true if the text view is editable. - private final boolean mCursorControlEnabled; + private boolean mCursorControlEnabled; + + // Specifies whether the new magnifier (with fish-eye effect) is enabled. + private final boolean mNewMagnifierEnabled; Editor(TextView textView) { mTextView = textView; @@ -397,20 +402,89 @@ public class Editor { mHapticTextHandleEnabled = mTextView.getContext().getResources().getBoolean( com.android.internal.R.bool.config_enableHapticTextHandle); - if (FLAG_USE_MAGNIFIER) { - final Magnifier magnifier = - Magnifier.createBuilderWithOldMagnifierDefaults(mTextView).build(); - mMagnifierAnimator = new MagnifierMotionAnimator(magnifier); - } - 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"); } } + @VisibleForTesting + public void setCursorControlEnabled(boolean enabled) { + mCursorControlEnabled = enabled; + } + + @VisibleForTesting + public boolean getCursorControlEnabled() { + return mCursorControlEnabled; + } + + // Lazy creates the magnifier animator. + private MagnifierMotionAnimator getMagnifierAnimator() { + 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 = mNewMagnifierEnabled + ? createBuilderWithInlineMagnifierDefaults() + : Magnifier.createBuilderWithOldMagnifierDefaults(mTextView); + mMagnifierAnimator = new MagnifierMotionAnimator(builder.build()); + } + return mMagnifierAnimator; + } + + 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; + // Slightly increase the height to avoid tooLargeTextForMagnifier() returns true. + int height = (int)(sourceHeight * zoom) + 2; + int width = (int)(aspectRatio * height); + + params.setFishEyeStyle() + .setSize(width, height) + .setSourceSize(width, Math.round(sourceHeight)) + .setElevation(0) + .setInitialZoom(zoom) + .setClippingEnabled(false); + + final Context context = mTextView.getContext(); + final TypedArray a = context.obtainStyledAttributes( + null, com.android.internal.R.styleable.Magnifier, + com.android.internal.R.attr.magnifierStyle, 0); + params.setDefaultSourceToMagnifierOffset( + a.getDimensionPixelSize( + com.android.internal.R.styleable.Magnifier_magnifierHorizontalOffset, 0), + a.getDimensionPixelSize( + com.android.internal.R.styleable.Magnifier_magnifierVerticalOffset, 0)); + a.recycle(); + + return params.setSourceBounds( + Magnifier.SOURCE_BOUND_MAX_VISIBLE, + Magnifier.SOURCE_BOUND_MAX_IN_SURFACE, + Magnifier.SOURCE_BOUND_MAX_VISIBLE, + Magnifier.SOURCE_BOUND_MAX_IN_SURFACE); + } + ParcelableParcel saveInstanceState() { ParcelableParcel state = new ParcelableParcel(getClass().getClassLoader()); Parcel parcel = state.getParcel(); @@ -1204,7 +1278,7 @@ public class Editor { } // Long press in empty space moves cursor and starts the insertion action mode. if (!handled && !isPositionOnText(mTouchState.getLastDownX(), mTouchState.getLastDownY()) - && mInsertionControllerEnabled) { + && !mTouchState.isOnHandle() && mInsertionControllerEnabled) { final int offset = mTextView.getOffsetForPosition(mTouchState.getLastDownX(), mTouchState.getLastDownY()); Selection.setSelection((Spannable) mTextView.getText(), offset); @@ -4617,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); } @@ -5012,7 +5086,7 @@ public class Editor { } protected final void updateMagnifier(@NonNull final MotionEvent event) { - if (mMagnifierAnimator == null) { + if (getMagnifierAnimator() == null) { return; } @@ -5026,6 +5100,16 @@ public class Editor { mTextView.invalidateCursorPath(); suspendBlink(); + 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(); + mMagnifierAnimator.mMagnifier.setSourceHorizontalBounds(lineLeft, lineRight); + } mMagnifierAnimator.show(showPosInView.x, showPosInView.y); updateHandlesVisibility(); } else { @@ -5135,8 +5219,60 @@ public class Editor { private float mLastDownRawX, mLastDownRawY; private Runnable mHider; - public InsertionHandleView(Drawable drawable) { + // Members for fake-dismiss effect in touch through mode. + // It is to make InsertionHandleView can receive the MOVE/UP events after calling dismiss(), + // which could happen in case of long-press (making selection will dismiss the insertion + // handle). + + // Whether the finger is down and hasn't been up yet. + private boolean mIsTouchDown = false; + // 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; + + // Members for toggling the insertion menu in touch through mode. + + // The coordinate for the touch down event, which is used for transforming the coordinates + // of the events to the text view. + private float mTouchDownX; + private float mTouchDownY; + // The cursor offset when touch down. This is to detect whether the cursor is moved when + // finger move/up. + private int mOffsetDown; + // Whether the cursor offset has been changed on the move/up events. + private boolean mOffsetChanged; + // Whether it is in insertion action mode when finger down. + private boolean mIsInActionMode; + // The timestamp for the last up event, which is used for double tap detection. + private long mLastUpTime; + // The text height of the font of the text view, which is used to calculate the Y coordinate + // of the touch through events. + private float mTextHeight; + + // 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() { @@ -5189,7 +5325,23 @@ 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. + // Otherwise the insertion handle view cannot be moved. + return touchThrough(ev); + } final boolean result = super.onTouchEvent(ev); switch (ev.getActionMasked()) { @@ -5235,6 +5387,115 @@ public class Editor { return result; } + // Handles the touch events in touch through mode. + private boolean touchThrough(MotionEvent ev) { + final int actionType = ev.getActionMasked(); + switch (actionType) { + case MotionEvent.ACTION_DOWN: + mIsTouchDown = true; + mOffsetChanged = false; + mOffsetDown = mTextView.getSelectionStart(); + mTouchDownX = ev.getX(); + mTouchDownY = ev.getY(); + mIsInActionMode = mTextActionMode != null; + if (ev.getEventTime() - mLastUpTime < ViewConfiguration.getDoubleTapTimeout()) { + stopTextActionMode(); // Avoid crash when double tap and drag backwards. + } + final Paint.FontMetrics fontMetrics = mTextView.getPaint().getFontMetrics(); + mTextHeight = fontMetrics.descent - fontMetrics.ascent; + mTouchState.setIsOnHandle(true); + break; + case MotionEvent.ACTION_UP: + mLastUpTime = ev.getEventTime(); + break; + } + // Performs the touch through by forward the events to the text view. + boolean ret = mTextView.onTouchEvent(transformEventForTouchThrough(ev)); + + if (actionType == MotionEvent.ACTION_UP || actionType == MotionEvent.ACTION_CANCEL) { + mIsTouchDown = false; + if (mPendingDismissOnUp) { + dismiss(); + } + mTouchState.setIsOnHandle(false); + } + + // Checks for cursor offset change. + if (!mOffsetChanged) { + int start = mTextView.getSelectionStart(); + int end = mTextView.getSelectionEnd(); + if (start != end || mOffsetDown != start) { + mOffsetChanged = true; + } + } + + // Toggling the insertion action mode on finger up. + if (!mOffsetChanged && actionType == MotionEvent.ACTION_UP) { + if (mIsInActionMode) { + stopTextActionMode(); + } else { + startInsertionActionMode(); + } + } + return ret; + } + + private MotionEvent transformEventForTouchThrough(MotionEvent ev) { + // Transforms the touch events to screen coordinates. + // And also shift up to make the hit point is on the text. + // Note: + // - The revised X should reflect the distance to the horizontal center of touch down. + // - The revised Y should be at the top of the text. + Matrix m = new Matrix(); + m.setTranslate(ev.getRawX() - ev.getX() + (getMeasuredWidth() >> 1) - mTouchDownX, + ev.getRawY() - ev.getY() - mTouchDownY - mTextHeight); + ev.transform(m); + // Transforms the touch events to text view coordinates. + mTextView.toLocalMotionEvent(ev); + if (TextView.DEBUG_CURSOR) { + logCursor("InsertionHandleView#transformEventForTouchThrough", + "Touch through: %d, (%f, %f)", + ev.getAction(), ev.getX(), ev.getY()); + } + return ev; + } + + @Override + public boolean isShowing() { + if (mPendingDismissOnUp) { + return false; + } + return super.isShowing(); + } + + @Override + public void show() { + super.show(); + mPendingDismissOnUp = false; + mDrawable.setAlpha(mDrawableOpacity); + } + + @Override + public void dismiss() { + if (mIsTouchDown) { + if (TextView.DEBUG_CURSOR) { + logCursor("InsertionHandleView#dismiss", + "Suppressed the real dismiss, only become invisible"); + } + mPendingDismissOnUp = true; + mDrawable.setAlpha(0); + } else { + super.dismiss(); + mPendingDismissOnUp = false; + } + } + + @Override + protected void updateDrawable(final boolean updateDrawableWhenDragging) { + super.updateDrawable(updateDrawableWhenDragging); + mDrawable.setAlpha(mDrawableOpacity); + } + @Override public int getCurrentCursorOffset() { return mTextView.getSelectionStart(); @@ -6039,8 +6300,8 @@ public class Editor { eventX, eventY); // Double tap detection - if (mTouchState.isMultiTapInSameArea() - && (isMouse || isPositionOnText(eventX, eventY))) { + if (mTouchState.isMultiTapInSameArea() && (isMouse + || mTouchState.isOnHandle() || isPositionOnText(eventX, eventY))) { if (TextView.DEBUG_CURSOR) { logCursor("SelectionModifierCursorController: onTouchEvent", "ACTION_DOWN: select and start drag"); diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java index b13ca4210612..ff3ac0732aa2 100644 --- a/core/java/android/widget/EditorTouchState.java +++ b/core/java/android/widget/EditorTouchState.java @@ -42,6 +42,7 @@ public class EditorTouchState { private long mLastDownMillis; private float mLastUpX, mLastUpY; private long mLastUpMillis; + private boolean mIsOnHandle; @IntDef({MultiTapStatus.NONE, MultiTapStatus.FIRST_TAP, MultiTapStatus.DOUBLE_TAP, MultiTapStatus.TRIPLE_CLICK}) @@ -98,7 +99,15 @@ public class EditorTouchState { } public boolean isDragCloseToVertical() { - return mIsDragCloseToVertical; + return mIsDragCloseToVertical && !mIsOnHandle; + } + + public void setIsOnHandle(boolean onHandle) { + mIsOnHandle = onHandle; + } + + public boolean isOnHandle() { + return mIsOnHandle; } /** diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java index 2924dd9bf954..57b63a7a9f0d 100644 --- a/core/java/android/widget/Magnifier.java +++ b/core/java/android/widget/Magnifier.java @@ -140,6 +140,18 @@ public final class Magnifier { // The lock used to synchronize the UI and render threads when a #dismiss is performed. private final Object mDestroyLock = new Object(); + // Members for new styled magnifier (Eloquent style). + + // Whether the magnifier is in new style. + private boolean mIsFishEyeStyle; + // The width of the cut region on the left edge of the pixel copy source rect. + private int mLeftCutWidth = 0; + // The width of the cut region on the right edge of the pixel copy source rect. + private int mRightCutWidth = 0; + // The horizontal bounds of the content source in pixels, relative to the view. + private int mLeftBound = Integer.MIN_VALUE; + private int mRightBound = Integer.MAX_VALUE; + /** * Initializes a magnifier. * @@ -198,8 +210,14 @@ public final class Magnifier { mWindowWidth = params.mWidth; mWindowHeight = params.mHeight; mZoom = params.mZoom; - mSourceWidth = Math.round(mWindowWidth / mZoom); - mSourceHeight = Math.round(mWindowHeight / mZoom); + mIsFishEyeStyle = params.mIsFishEyeStyle; + if (params.mSourceWidth > 0 && params.mSourceHeight > 0) { + mSourceWidth = params.mSourceWidth; + mSourceHeight = params.mSourceHeight; + } else { + mSourceWidth = Math.round(mWindowWidth / mZoom); + mSourceHeight = Math.round(mWindowHeight / mZoom); + } mWindowElevation = params.mElevation; mWindowCornerRadius = params.mCornerRadius; mOverlay = params.mOverlay; @@ -221,6 +239,18 @@ public final class Magnifier { } /** + * Sets the horizontal bounds of the source when showing the magnifier. + * This is used for new style magnifier. e.g. limit the source bounds by the text line bounds. + * + * @param left the left of the bounds, relative to the view. + * @param right the right of the bounds, relative to the view. + */ + void setSourceHorizontalBounds(int left, int right) { + mLeftBound = left; + mRightBound = right; + } + + /** * Shows the magnifier on the screen. The method takes the coordinates of the center * of the content source going to be magnified and copied to the magnifier. The coordinates * are relative to the top left corner of the magnified view. The magnifier will be @@ -265,20 +295,37 @@ public final class Magnifier { obtainSurfaces(); obtainContentCoordinates(sourceCenterX, sourceCenterY); - obtainWindowCoordinates(magnifierCenterX, magnifierCenterY); - final int startX = mClampedCenterZoomCoords.x - mSourceWidth / 2; + int startX = mClampedCenterZoomCoords.x - mSourceWidth / 2; final int startY = mClampedCenterZoomCoords.y - mSourceHeight / 2; + + if (mIsFishEyeStyle) { + // The magnifier center is the same as source center in new style. + magnifierCenterX = mClampedCenterZoomCoords.x - mViewCoordinatesInSurface[0]; + magnifierCenterY = mClampedCenterZoomCoords.y - mViewCoordinatesInSurface[1]; + // Gets the startX for new style, which should be bounded by the horizontal bounds. + // Also calculates the left/right cut width for pixel copy. + final int left = startX; + final int right = startX + mSourceWidth; + final int leftBound = mViewCoordinatesInSurface[0] + Math.max(0, mLeftBound); + final int rightBound = + mViewCoordinatesInSurface[0] + Math.min(mView.getWidth(), mRightBound); + startX = Math.max(left, leftBound); + mLeftCutWidth = Math.max(0, leftBound - left); + mRightCutWidth = Math.max(0, right - rightBound); + } + obtainWindowCoordinates(magnifierCenterX, magnifierCenterY); + if (sourceCenterX != mPrevShowSourceCoords.x || sourceCenterY != mPrevShowSourceCoords.y || mDirtyState) { if (mWindow == null) { synchronized (mLock) { mWindow = new InternalPopupWindow(mView.getContext(), mView.getDisplay(), - mParentSurface.mSurfaceControl, mWindowWidth, mWindowHeight, + mParentSurface.mSurfaceControl, mWindowWidth, mWindowHeight, mZoom, mWindowElevation, mWindowCornerRadius, mOverlay != null ? mOverlay : new ColorDrawable(Color.TRANSPARENT), Handler.getMain() /* draw the magnifier on the UI thread */, mLock, - mCallback); + mCallback, mIsFishEyeStyle); } } performPixelCopy(startX, startY, true /* update window position */); @@ -387,7 +434,7 @@ public final class Magnifier { public void setZoom(@FloatRange(from = 0f) float zoom) { Preconditions.checkArgumentPositive(zoom, "Zoom should be positive"); mZoom = zoom; - mSourceWidth = Math.round(mWindowWidth / mZoom); + mSourceWidth = mIsFishEyeStyle ? mWindowWidth : Math.round(mWindowWidth / mZoom); mSourceHeight = Math.round(mWindowHeight / mZoom); mDirtyState = true; } @@ -634,8 +681,10 @@ public final class Magnifier { resolvedBottom = Math.max(resolvedBottom, resolvedTop + mSourceHeight); // Finally compute the coordinates of the source center. - mClampedCenterZoomCoords.x = Math.max(resolvedLeft + mSourceWidth / 2, Math.min( - zoomCenterX, resolvedRight - mSourceWidth / 2)); + mClampedCenterZoomCoords.x = mIsFishEyeStyle + ? Math.max(resolvedLeft, Math.min(zoomCenterX, resolvedRight)) + : Math.max(resolvedLeft + mSourceWidth / 2, Math.min( + zoomCenterX, resolvedRight - mSourceWidth / 2)); mClampedCenterZoomCoords.y = Math.max(resolvedTop + mSourceHeight / 2, Math.min( zoomCenterY, resolvedBottom - mSourceHeight / 2)); } @@ -678,11 +727,22 @@ public final class Magnifier { // Perform the pixel copy. mPixelCopyRequestRect.set(startXInSurface, startYInSurface, - startXInSurface + mSourceWidth, + startXInSurface + mSourceWidth - mLeftCutWidth - mRightCutWidth, startYInSurface + mSourceHeight); + mPrevStartCoordsInSurface.x = startXInSurface; + mPrevStartCoordsInSurface.y = startYInSurface; + mDirtyState = false; + final InternalPopupWindow currentWindowInstance = mWindow; + if (mPixelCopyRequestRect.width() == 0) { + // If the copy rect is empty, updates an empty bitmap to the window. + mWindow.updateContent( + Bitmap.createBitmap(mSourceWidth, mSourceHeight, Bitmap.Config.ALPHA_8)); + return; + } final Bitmap bitmap = - Bitmap.createBitmap(mSourceWidth, mSourceHeight, Bitmap.Config.ARGB_8888); + Bitmap.createBitmap(mSourceWidth - mLeftCutWidth - mRightCutWidth, + mSourceHeight, Bitmap.Config.ARGB_8888); PixelCopy.request(mContentCopySurface.mSurface, mPixelCopyRequestRect, bitmap, result -> { if (result != PixelCopy.SUCCESS) { @@ -696,15 +756,25 @@ public final class Magnifier { } if (updateWindowPosition) { // TODO: pull the position update outside #performPixelCopy - mWindow.setContentPositionForNextDraw(windowCoords.x, windowCoords.y); + mWindow.setContentPositionForNextDraw(windowCoords.x, + windowCoords.y); + } + if (bitmap.getWidth() < mSourceWidth) { + // When bitmap width has been cut, re-fills it with full width bitmap. + // This only happens in new styled magnifier. + final Bitmap newBitmap = Bitmap.createBitmap( + mSourceWidth, bitmap.getHeight(), bitmap.getConfig()); + final Canvas can = new Canvas(newBitmap); + final Rect dstRect = new Rect(mLeftCutWidth, 0, + mSourceWidth - mRightCutWidth, bitmap.getHeight()); + can.drawBitmap(bitmap, null, dstRect, null); + mWindow.updateContent(newBitmap); + } else { + mWindow.updateContent(bitmap); } - mWindow.updateContent(bitmap); } }, sPixelCopyHandlerThread.getThreadHandler()); - mPrevStartCoordsInSurface.x = startXInSurface; - mPrevStartCoordsInSurface.y = startYInSurface; - mDirtyState = false; } private void onPixelCopyFailed() { @@ -790,9 +860,6 @@ public final class Magnifier { // The size of the content of the magnifier. private final int mContentWidth; private final int mContentHeight; - // The size of the allocated surface. - private final int mSurfaceWidth; - private final int mSurfaceHeight; // The insets of the content inside the allocated surface. private final int mOffsetX; private final int mOffsetY; @@ -815,9 +882,6 @@ public final class Magnifier { private final Handler mHandler; // The callback to be run after the next draw. private Callback mCallback; - // The position of the magnifier content when the last draw was requested. - private int mLastDrawContentPositionX; - private int mLastDrawContentPositionY; // Members below describe the state of the magnifier. Reads/writes to them // have to be synchronized between the UI thread and the thread that handles @@ -838,10 +902,19 @@ public final class Magnifier { // The current content of the magnifier. It is mBitmap + mOverlay, only used for testing. private Bitmap mCurrentContent; + private final float mZoom; + // Whether is in the new magnifier style. + private boolean mIsFishEyeStyle; + // The mesh matrix for the fish-eye effect. + private float[] mMesh; + private int mMeshWidth; + private int mMeshHeight; + InternalPopupWindow(final Context context, final Display display, final SurfaceControl parentSurfaceControl, final int width, final int height, - final float elevation, final float cornerRadius, final Drawable overlay, - final Handler handler, final Object lock, final Callback callback) { + final float zoom, final float elevation, final float cornerRadius, + final Drawable overlay, final Handler handler, final Object lock, + final Callback callback, final boolean isFishEyeStyle) { mDisplay = display; mOverlay = overlay; mLock = lock; @@ -849,15 +922,16 @@ public final class Magnifier { mContentWidth = width; mContentHeight = height; + mZoom = zoom; mOffsetX = (int) (1.05f * elevation); mOffsetY = (int) (1.05f * elevation); // Setup the surface we will use for drawing the content and shadow. - mSurfaceWidth = mContentWidth + 2 * mOffsetX; - mSurfaceHeight = mContentHeight + 2 * mOffsetY; + final int surfaceWidth = mContentWidth + 2 * mOffsetX; + final int surfaceHeight = mContentHeight + 2 * mOffsetY; mSurfaceSession = new SurfaceSession(); mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession) .setFormat(PixelFormat.TRANSLUCENT) - .setBufferSize(mSurfaceWidth, mSurfaceHeight) + .setBufferSize(surfaceWidth, surfaceHeight) .setName("magnifier surface") .setFlags(SurfaceControl.HIDDEN) .setParent(parentSurfaceControl) @@ -904,6 +978,38 @@ public final class Magnifier { mHandler = handler; mMagnifierUpdater = this::doDraw; mFrameDrawScheduled = false; + mIsFishEyeStyle = isFishEyeStyle; + + if (mIsFishEyeStyle) { + createMeshMatrixForFishEyeEffect(); + } + } + + private void createMeshMatrixForFishEyeEffect() { + mMeshWidth = mZoom < 1.5f ? 5 : 4; + mMeshHeight = 6; + final float w = mContentWidth; + final float h = mContentHeight; + final float dx = (w - mZoom * w * (mMeshWidth - 2) / mMeshWidth) / 2; + mMesh = new float[2 * (mMeshWidth + 1) * (mMeshHeight + 1)]; + for (int i = 0; i < 2 * (mMeshWidth + 1) * (mMeshHeight + 1); i += 2) { + // Calculates X value. + final int colIndex = i % (2 * (mMeshWidth + 1)) / 2; + if (colIndex == 0) { + mMesh[i] = 0; + } else if (colIndex == mMeshWidth) { + mMesh[i] = w; + } else { + mMesh[i] = (colIndex - 1) * (w - 2 * dx) / (mMeshWidth - 2) + dx; + } + // Calculates Y value. + final int rowIndex = i / 2 / (mMeshWidth + 1); + final float y0 = colIndex == 0 || colIndex == mMeshWidth + ? (h - h / mZoom) / 2 : 0; + final float dy = colIndex == 0 || colIndex == mMeshWidth + ? h / mZoom / mMeshHeight : h / mMeshHeight; + mMesh[i + 1] = y0 + rowIndex * dy; + } } private RenderNode createRenderNodeForBitmap(final String name, @@ -1060,15 +1166,19 @@ public final class Magnifier { final RecordingCanvas canvas = mBitmapRenderNode.beginRecording(mContentWidth, mContentHeight); try { - final Rect srcRect = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); - final Rect dstRect = new Rect(0, 0, mContentWidth, mContentHeight); - final Paint paint = new Paint(); - paint.setFilterBitmap(true); - canvas.drawBitmap(mBitmap, srcRect, dstRect, paint); + if (mIsFishEyeStyle) { + canvas.drawBitmapMesh( + mBitmap, mMeshWidth, mMeshHeight, mMesh, 0, null, 0, null); + } else { + final Rect srcRect = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); + final Rect dstRect = new Rect(0, 0, mContentWidth, mContentHeight); + final Paint paint = new Paint(); + paint.setFilterBitmap(true); + canvas.drawBitmap(mBitmap, srcRect, dstRect, paint); + } } finally { mBitmapRenderNode.endRecording(); } - if (mPendingWindowPositionUpdate || mFirstDraw) { // If the window has to be shown or moved, defer this until the next draw. final boolean firstDraw = mFirstDraw; @@ -1094,13 +1204,14 @@ public final class Magnifier { } mTransaction.apply(); }; - mRenderer.setLightCenter(mDisplay, pendingX, pendingY); + if (!mIsFishEyeStyle) { + // The new style magnifier doesn't need the light/shadow. + mRenderer.setLightCenter(mDisplay, pendingX, pendingY); + } } else { callback = null; } - mLastDrawContentPositionX = mWindowPositionX + mOffsetX; - mLastDrawContentPositionY = mWindowPositionY + mOffsetY; mFrameDrawScheduled = false; } @@ -1149,6 +1260,9 @@ public final class Magnifier { private @SourceBound int mTopContentBound; private @SourceBound int mRightContentBound; private @SourceBound int mBottomContentBound; + private boolean mIsFishEyeStyle; + private int mSourceWidth; + private int mSourceHeight; /** * Construct a new builder for {@link Magnifier} objects. @@ -1177,6 +1291,7 @@ public final class Magnifier { mTopContentBound = SOURCE_BOUND_MAX_VISIBLE; mRightContentBound = SOURCE_BOUND_MAX_VISIBLE; mBottomContentBound = SOURCE_BOUND_MAX_VISIBLE; + mIsFishEyeStyle = false; } /** @@ -1339,6 +1454,25 @@ public final class Magnifier { } /** + * Sets the source width/height. + */ + @NonNull + Builder setSourceSize(int width, int height) { + mSourceWidth = width; + mSourceHeight = height; + return this; + } + + /** + * Sets the magnifier as the new fish-eye style. + */ + @NonNull + Builder setFishEyeStyle() { + mIsFishEyeStyle = true; + return this; + } + + /** * Builds a {@link Magnifier} instance based on the configuration of this {@link Builder}. */ public @NonNull Magnifier build() { 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/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl index e27ff0076054..20cd7c21d512 100644 --- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl +++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl @@ -41,5 +41,5 @@ interface IInputMethodPrivilegedOperations { boolean shouldOfferSwitchingToNextInputMethod(); void notifyUserAction(); void reportPreRendered(in EditorInfo info); - void applyImeVisibility(boolean setVisible); + void applyImeVisibility(IBinder showInputToken, boolean setVisible); } diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java index d42c607b98bf..9eeef963de7f 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java +++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java @@ -23,6 +23,7 @@ import android.net.Uri; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; +import android.view.View; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodSubtype; @@ -368,18 +369,20 @@ public final class InputMethodPrivilegedOperations { } /** - * Calls {@link IInputMethodPrivilegedOperations#applyImeVisibility(boolean)}. + * Calls {@link IInputMethodPrivilegedOperations#applyImeVisibility(IBinder, boolean)}. * + * @param showInputToken dummy token that maps to window requesting + * {@link android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} * @param setVisible {@code true} to set IME visible, else hidden. */ @AnyThread - public void applyImeVisibility(boolean setVisible) { + public void applyImeVisibility(IBinder showInputToken, boolean setVisible) { final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); if (ops == null) { return; } try { - ops.applyImeVisibility(setVisible); + ops.applyImeVisibility(showInputToken, setVisible); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/com/android/internal/logging/InstanceId.java b/core/java/com/android/internal/logging/InstanceId.java new file mode 100644 index 000000000000..85dbac3f59bb --- /dev/null +++ b/core/java/com/android/internal/logging/InstanceId.java @@ -0,0 +1,50 @@ +/* + * 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.logging; + +import android.annotation.Nullable; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * An opaque identifier used to disambiguate which logs refer to a particular instance of some + * UI element. Useful when there might be multiple instances simultaneously active. + * Obtain from InstanceIdSequence. + */ +public class InstanceId { + private int mId; + protected InstanceId(int id) { + mId = id; + } + @VisibleForTesting + public int getId() { + return mId; + } + + @Override + public int hashCode() { + return mId; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof InstanceId)) { + return false; + } + return mId == ((InstanceId) obj).mId; + } +} diff --git a/core/java/com/android/internal/logging/InstanceIdSequence.java b/core/java/com/android/internal/logging/InstanceIdSequence.java new file mode 100644 index 000000000000..2e78ed8c5185 --- /dev/null +++ b/core/java/com/android/internal/logging/InstanceIdSequence.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.logging; + +import static java.lang.Math.max; +import static java.lang.Math.min; + +import java.security.SecureRandom; +import java.util.Random; + +/** + * Generates random InstanceIds in range [0, instanceIdMax) for passing to + * UiEventLogger.logWithInstanceId(). Holds a SecureRandom, which self-seeds on + * first use; try to give it a long lifetime. Safe for concurrent use. + */ +public class InstanceIdSequence { + // At most 20 bits: ~1m possibilities, ~0.5% probability of collision in 100 values + private static final int INSTANCE_ID_MAX = 1 << 20; + protected final int mInstanceIdMax; + private final Random mRandom = new SecureRandom(); + + /** + * Constructs a sequence with identifiers [0, instanceIdMax). Capped at INSTANCE_ID_MAX. + * @param instanceIdMax Limiting value of identifiers. Normally positive: otherwise you get + * an all-zero sequence. + */ + public InstanceIdSequence(int instanceIdMax) { + mInstanceIdMax = min(max(0, instanceIdMax), INSTANCE_ID_MAX); + } + + /** + * Gets the next instance from the sequence. Safe for concurrent use. + * @return new InstanceId + */ + public InstanceId newInstanceId() { + return new InstanceId(mRandom.nextInt(mInstanceIdMax)); + } +} diff --git a/core/java/com/android/internal/logging/UiEventLogger.java b/core/java/com/android/internal/logging/UiEventLogger.java index 3a450de6c8e6..48d2bc2ae58d 100644 --- a/core/java/com/android/internal/logging/UiEventLogger.java +++ b/core/java/com/android/internal/logging/UiEventLogger.java @@ -49,4 +49,15 @@ public interface UiEventLogger { * @param packageName the package name of the relevant app, if known (null otherwise). */ void log(@NonNull UiEventEnum event, int uid, @Nullable String packageName); + + /** + * Log an event with package information and an instance ID. + * Does nothing if event.getId() <= 0. + * @param event an enum implementing UiEventEnum interface. + * @param uid the uid of the relevant app, if known (0 otherwise). + * @param packageName the package name of the relevant app, if known (null otherwise). + * @param instance An identifier obtained from an InstanceIdSequence. + */ + void logWithInstanceId(@NonNull UiEventEnum event, int uid, @Nullable String packageName, + @NonNull InstanceId instance); } diff --git a/core/java/com/android/internal/logging/UiEventLoggerImpl.java b/core/java/com/android/internal/logging/UiEventLoggerImpl.java index bdf460c710c3..fe758a8e6cb7 100644 --- a/core/java/com/android/internal/logging/UiEventLoggerImpl.java +++ b/core/java/com/android/internal/logging/UiEventLoggerImpl.java @@ -36,4 +36,14 @@ public class UiEventLoggerImpl implements UiEventLogger { StatsLog.write(StatsLog.UI_EVENT_REPORTED, eventID, uid, packageName); } } + + @Override + public void logWithInstanceId(UiEventEnum event, int uid, String packageName, + InstanceId instance) { + final int eventID = event.getId(); + if (eventID > 0) { + StatsLog.write(StatsLog.UI_EVENT_REPORTED, eventID, uid, packageName, + instance.getId()); + } + } } diff --git a/core/java/com/android/internal/logging/testing/InstanceIdSequenceFake.java b/core/java/com/android/internal/logging/testing/InstanceIdSequenceFake.java new file mode 100644 index 000000000000..0fd40b9ceb06 --- /dev/null +++ b/core/java/com/android/internal/logging/testing/InstanceIdSequenceFake.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.logging.testing; + +import android.annotation.SuppressLint; + +import com.android.internal.logging.InstanceId; +import com.android.internal.logging.InstanceIdSequence; + +/** + * A fake implementation of InstanceIdSequence that returns 0, 1, 2, ... + */ +public class InstanceIdSequenceFake extends InstanceIdSequence { + + public InstanceIdSequenceFake(int instanceIdMax) { + super(instanceIdMax); + } + + /** + * Extend InstanceId to add a constructor we can call, strictly for testing purposes. + * Public so that tests can check whether the InstanceIds they see are fake. + */ + public static class InstanceIdFake extends InstanceId { + @SuppressLint("VisibleForTests") // This is test infrastructure, which ought to count + InstanceIdFake(int id) { + super(id); + } + } + + private int mNextId = 0; + + @Override + public InstanceId newInstanceId() { + synchronized (this) { + ++mNextId; + if (mNextId >= mInstanceIdMax) { + mNextId = 0; + } + return new InstanceIdFake(mNextId); + } + } +} diff --git a/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java b/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java index 6be5b81afee2..130ee64ac887 100644 --- a/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java +++ b/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java @@ -16,6 +16,7 @@ package com.android.internal.logging.testing; +import com.android.internal.logging.InstanceId; import com.android.internal.logging.UiEventLogger; import java.util.LinkedList; @@ -34,15 +35,24 @@ public class UiEventLoggerFake implements UiEventLogger { public final int eventId; public final int uid; public final String packageName; + public final InstanceId instanceId; // Used only for WithInstanceId variant - public FakeUiEvent(int eventId, int uid, String packageName) { + FakeUiEvent(int eventId, int uid, String packageName) { this.eventId = eventId; this.uid = uid; this.packageName = packageName; + this.instanceId = null; + } + + FakeUiEvent(int eventId, int uid, String packageName, InstanceId instanceId) { + this.eventId = eventId; + this.uid = uid; + this.packageName = packageName; + this.instanceId = instanceId; } } - private Queue<FakeUiEvent> mLogs = new LinkedList<FakeUiEvent>(); + private Queue<FakeUiEvent> mLogs = new LinkedList<>(); public Queue<FakeUiEvent> getLogs() { return mLogs; @@ -60,4 +70,13 @@ public class UiEventLoggerFake implements UiEventLogger { mLogs.offer(new FakeUiEvent(eventId, uid, packageName)); } } + + @Override + public void logWithInstanceId(UiEventLogger.UiEventEnum event, int uid, String packageName, + InstanceId instance) { + final int eventId = event.getId(); + if (eventId > 0) { + mLogs.offer(new FakeUiEvent(eventId, uid, packageName, instance)); + } + } } 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/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl index 58aaa80b51be..475a3214096d 100644 --- a/core/java/com/android/internal/view/IInputMethod.aidl +++ b/core/java/com/android/internal/view/IInputMethod.aidl @@ -54,7 +54,7 @@ oneway interface IInputMethod { void revokeSession(IInputMethodSession session); - void showSoftInput(int flags, in ResultReceiver resultReceiver); + void showSoftInput(in IBinder showInputToken, int flags, in ResultReceiver resultReceiver); void hideSoftInput(int flags, in ResultReceiver resultReceiver); diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index c29e823218eb..0337ddd1ab49 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -41,7 +41,7 @@ interface IInputMethodManager { boolean allowsImplicitlySelectedSubtypes); InputMethodSubtype getLastInputMethodSubtype(); - boolean showSoftInput(in IInputMethodClient client, int flags, + boolean showSoftInput(in IInputMethodClient client, IBinder windowToken, int flags, in ResultReceiver resultReceiver); boolean hideSoftInput(in IInputMethodClient client, int flags, in ResultReceiver resultReceiver); diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java index 07d0d7d91997..f6089589a994 100644 --- a/core/java/com/android/internal/widget/MessagingLayout.java +++ b/core/java/com/android/internal/widget/MessagingLayout.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.annotation.StyleRes; import android.app.Notification; import android.app.Person; +import android.app.RemoteInputHistoryItem; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -161,8 +162,9 @@ public class MessagingLayout extends FrameLayout implements ImageMessageConsumer if (headerText != null) { mConversationTitle = headerText.getText(); } - addRemoteInputHistoryToMessages(newMessages, - extras.getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY)); + RemoteInputHistoryItem[] history = (RemoteInputHistoryItem[]) + extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); + addRemoteInputHistoryToMessages(newMessages, history); boolean showSpinner = extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false); bind(newMessages, newHistoricMessages, showSpinner); @@ -175,14 +177,18 @@ public class MessagingLayout extends FrameLayout implements ImageMessageConsumer private void addRemoteInputHistoryToMessages( List<Notification.MessagingStyle.Message> newMessages, - CharSequence[] remoteInputHistory) { + RemoteInputHistoryItem[] remoteInputHistory) { if (remoteInputHistory == null || remoteInputHistory.length == 0) { return; } for (int i = remoteInputHistory.length - 1; i >= 0; i--) { - CharSequence message = remoteInputHistory[i]; - newMessages.add(new Notification.MessagingStyle.Message( - message, 0, (Person) null, true /* remoteHistory */)); + RemoteInputHistoryItem historyMessage = remoteInputHistory[i]; + Notification.MessagingStyle.Message message = new Notification.MessagingStyle.Message( + historyMessage.getText(), 0, (Person) null, true /* remoteHistory */); + if (historyMessage.getUri() != null) { + message.setData(historyMessage.getMimeType(), historyMessage.getUri()); + } + newMessages.add(message); } } diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 593728350037..32a7cf32bc0a 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -243,7 +243,8 @@ Bitmap* GraphicsJNI::getNativeBitmap(JNIEnv* env, jobject bitmap) { return localBitmap.valid() ? &localBitmap->bitmap() : nullptr; } -SkImageInfo GraphicsJNI::getBitmapInfo(JNIEnv* env, jobject bitmap, uint32_t* outRowBytes) { +SkImageInfo GraphicsJNI::getBitmapInfo(JNIEnv* env, jobject bitmap, uint32_t* outRowBytes, + bool* isHardware) { SkASSERT(env); SkASSERT(bitmap); SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class)); @@ -252,6 +253,9 @@ SkImageInfo GraphicsJNI::getBitmapInfo(JNIEnv* env, jobject bitmap, uint32_t* ou if (outRowBytes) { *outRowBytes = localBitmap->rowBytes(); } + if (isHardware) { + *isHardware = localBitmap->isHardware(); + } return localBitmap->info(); } diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index 99034edaadf7..1e497654f18d 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -62,7 +62,8 @@ public: static android::Canvas* getNativeCanvas(JNIEnv*, jobject canvas); static android::Bitmap* getNativeBitmap(JNIEnv*, jobject bitmap); - static SkImageInfo getBitmapInfo(JNIEnv*, jobject bitmap, uint32_t* outRowBytes); + static SkImageInfo getBitmapInfo(JNIEnv*, jobject bitmap, uint32_t* outRowBytes, + bool* isHardware); static SkRegion* getNativeRegion(JNIEnv*, jobject region); /* diff --git a/core/jni/android/graphics/apex/android_bitmap.cpp b/core/jni/android/graphics/apex/android_bitmap.cpp index b8e04a7e9a2b..66285292743b 100644 --- a/core/jni/android/graphics/apex/android_bitmap.cpp +++ b/core/jni/android/graphics/apex/android_bitmap.cpp @@ -78,7 +78,7 @@ static SkColorType getColorType(AndroidBitmapFormat format) { } } -static uint32_t getInfoFlags(const SkImageInfo& info) { +static uint32_t getAlphaFlags(const SkImageInfo& info) { switch (info.alphaType()) { case kUnknown_SkAlphaType: LOG_ALWAYS_FATAL("Bitmap has no alpha type"); @@ -92,6 +92,14 @@ static uint32_t getInfoFlags(const SkImageInfo& info) { } } +static uint32_t getInfoFlags(const SkImageInfo& info, bool isHardware) { + uint32_t flags = getAlphaFlags(info); + if (isHardware) { + flags |= ANDROID_BITMAP_FLAGS_IS_HARDWARE; + } + return flags; +} + ABitmap* ABitmap_copy(ABitmap* srcBitmapHandle, AndroidBitmapFormat dstFormat) { SkColorType dstColorType = getColorType(dstFormat); if (srcBitmapHandle && dstColorType != kUnknown_SkColorType) { @@ -108,119 +116,32 @@ ABitmap* ABitmap_copy(ABitmap* srcBitmapHandle, AndroidBitmapFormat dstFormat) { return nullptr; } -static AndroidBitmapInfo getInfo(const SkImageInfo& imageInfo, uint32_t rowBytes) { +static AndroidBitmapInfo getInfo(const SkImageInfo& imageInfo, uint32_t rowBytes, bool isHardware) { AndroidBitmapInfo info; info.width = imageInfo.width(); info.height = imageInfo.height(); info.stride = rowBytes; info.format = getFormat(imageInfo); - info.flags = getInfoFlags(imageInfo); + info.flags = getInfoFlags(imageInfo, isHardware); return info; } AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmapHandle) { Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle); - return getInfo(bitmap->info(), bitmap->rowBytes()); -} - -namespace { -static bool nearlyEqual(float a, float b) { - // By trial and error, this is close enough to match for the ADataSpaces we - // compare for. - return ::fabs(a - b) < .002f; -} - -static bool nearlyEqual(const skcms_TransferFunction& x, const skcms_TransferFunction& y) { - return nearlyEqual(x.g, y.g) - && nearlyEqual(x.a, y.a) - && nearlyEqual(x.b, y.b) - && nearlyEqual(x.c, y.c) - && nearlyEqual(x.d, y.d) - && nearlyEqual(x.e, y.e) - && nearlyEqual(x.f, y.f); + return getInfo(bitmap->info(), bitmap->rowBytes(), bitmap->isHardware()); } -static bool nearlyEqual(const skcms_Matrix3x3& x, const skcms_Matrix3x3& y) { - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - if (!nearlyEqual(x.vals[i][j], y.vals[i][j])) return false; - } - } - return true; -} - -static constexpr skcms_TransferFunction k2Dot6 = {2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; - -// Skia's SkNamedGamut::kDCIP3 is based on a white point of D65. This gamut -// matches the white point used by ColorSpace.Named.DCIP3. -static constexpr skcms_Matrix3x3 kDCIP3 = {{ - {0.486143, 0.323835, 0.154234}, - {0.226676, 0.710327, 0.0629966}, - {0.000800549, 0.0432385, 0.78275}, -}}; -} // anonymous namespace - ADataSpace ABitmap_getDataSpace(ABitmap* bitmapHandle) { Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle); const SkImageInfo& info = bitmap->info(); - SkColorSpace* colorSpace = info.colorSpace(); - if (!colorSpace) { - return ADATASPACE_UNKNOWN; - } - - if (colorSpace->isSRGB()) { - if (info.colorType() == kRGBA_F16_SkColorType) { - return ADATASPACE_SCRGB; - } - return ADATASPACE_SRGB; - } - - skcms_TransferFunction fn; - LOG_ALWAYS_FATAL_IF(!colorSpace->isNumericalTransferFn(&fn)); - - skcms_Matrix3x3 gamut; - LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&gamut)); - - if (nearlyEqual(gamut, SkNamedGamut::kSRGB)) { - if (nearlyEqual(fn, SkNamedTransferFn::kLinear)) { - // Skia doesn't differentiate amongst the RANGES. In Java, we associate - // LINEAR_EXTENDED_SRGB with F16, and LINEAR_SRGB with other Configs. - // Make the same association here. - if (info.colorType() == kRGBA_F16_SkColorType) { - return ADATASPACE_SCRGB_LINEAR; - } - return ADATASPACE_SRGB_LINEAR; - } - - if (nearlyEqual(fn, SkNamedTransferFn::kRec2020)) { - return ADATASPACE_BT709; - } - } - - if (nearlyEqual(fn, SkNamedTransferFn::kSRGB) && nearlyEqual(gamut, SkNamedGamut::kDCIP3)) { - return ADATASPACE_DISPLAY_P3; - } - - if (nearlyEqual(fn, SkNamedTransferFn::k2Dot2) && nearlyEqual(gamut, SkNamedGamut::kAdobeRGB)) { - return ADATASPACE_ADOBE_RGB; - } - - if (nearlyEqual(fn, SkNamedTransferFn::kRec2020) && - nearlyEqual(gamut, SkNamedGamut::kRec2020)) { - return ADATASPACE_BT2020; - } - - if (nearlyEqual(fn, k2Dot6) && nearlyEqual(gamut, kDCIP3)) { - return ADATASPACE_DCI_P3; - } - - return ADATASPACE_UNKNOWN; + return (ADataSpace)uirenderer::ColorSpaceToADataSpace(info.colorSpace(), info.colorType()); } AndroidBitmapInfo ABitmap_getInfoFromJava(JNIEnv* env, jobject bitmapObj) { uint32_t rowBytes = 0; - SkImageInfo imageInfo = GraphicsJNI::getBitmapInfo(env, bitmapObj, &rowBytes); - return getInfo(imageInfo, rowBytes); + bool isHardware = false; + SkImageInfo imageInfo = GraphicsJNI::getBitmapInfo(env, bitmapObj, &rowBytes, &isHardware); + return getInfo(imageInfo, rowBytes, isHardware); } void* ABitmap_getPixels(ABitmap* bitmapHandle) { @@ -263,7 +184,7 @@ SkAlphaType getAlphaType(const AndroidBitmapInfo* info) { class CompressWriter : public SkWStream { public: - CompressWriter(void* userContext, AndroidBitmap_compress_write_fn fn) + CompressWriter(void* userContext, AndroidBitmap_CompressWriteFunc fn) : mUserContext(userContext), mFn(fn), mBytesWritten(0) {} bool write(const void* buffer, size_t size) override { @@ -278,7 +199,7 @@ public: private: void* mUserContext; - AndroidBitmap_compress_write_fn mFn; + AndroidBitmap_CompressWriteFunc mFn; size_t mBytesWritten; }; @@ -286,7 +207,7 @@ private: int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const void* pixels, AndroidBitmapCompressFormat inFormat, int32_t quality, void* userContext, - AndroidBitmap_compress_write_fn fn) { + AndroidBitmap_CompressWriteFunc fn) { Bitmap::JavaCompressFormat format; switch (inFormat) { case ANDROID_BITMAP_COMPRESS_FORMAT_JPEG: @@ -378,3 +299,12 @@ int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const return ANDROID_BITMAP_RESULT_JNI_EXCEPTION; } } + +AHardwareBuffer* ABitmap_getHardwareBuffer(ABitmap* bitmapHandle) { + Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle); + AHardwareBuffer* buffer = bitmap->hardwareBuffer(); + if (buffer) { + AHardwareBuffer_acquire(buffer); + } + return buffer; +} diff --git a/core/jni/android/graphics/apex/include/android/graphics/bitmap.h b/core/jni/android/graphics/apex/include/android/graphics/bitmap.h index 683851d09d93..45fec2ab7b43 100644 --- a/core/jni/android/graphics/apex/include/android/graphics/bitmap.h +++ b/core/jni/android/graphics/apex/include/android/graphics/bitmap.h @@ -21,6 +21,8 @@ #include <jni.h> #include <sys/cdefs.h> +struct AHardwareBuffer; + __BEGIN_DECLS /** @@ -61,7 +63,19 @@ jobject ABitmapConfig_getConfigFromFormat(JNIEnv* env, AndroidBitmapFormat forma // NDK access int ABitmap_compress(const AndroidBitmapInfo* info, ADataSpace dataSpace, const void* pixels, AndroidBitmapCompressFormat format, int32_t quality, void* userContext, - AndroidBitmap_compress_write_fn); + AndroidBitmap_CompressWriteFunc); +/** + * Retrieve the native object associated with a HARDWARE Bitmap. + * + * Client must not modify it while a Bitmap is wrapping it. + * + * @param bitmap Handle to an android.graphics.Bitmap. + * @return on success, a pointer to the + * AHardwareBuffer associated with bitmap. This acquires + * a reference on the buffer, and the client must call + * AHardwareBuffer_release when finished with it. + */ +AHardwareBuffer* ABitmap_getHardwareBuffer(ABitmap* bitmap); __END_DECLS @@ -116,6 +130,7 @@ namespace graphics { ADataSpace getDataSpace() const { return ABitmap_getDataSpace(mBitmap); } void* getPixels() const { return ABitmap_getPixels(mBitmap); } void notifyPixelsChanged() const { ABitmap_notifyPixelsChanged(mBitmap); } + AHardwareBuffer* getHardwareBuffer() const { return ABitmap_getHardwareBuffer(mBitmap); } private: // takes ownership of the provided ABitmap diff --git a/core/jni/android_media_AudioAttributes.cpp b/core/jni/android_media_AudioAttributes.cpp index b87a34d6c7aa..b616ffc111a3 100644 --- a/core/jni/android_media_AudioAttributes.cpp +++ b/core/jni/android_media_AudioAttributes.cpp @@ -48,11 +48,14 @@ static struct { jfieldID mFormattedTags; // AudioAttributes.mFormattedTags } gAudioAttributesFields; +static struct { jmethodID isSystemUsage; } gAudioAttributesClassMethods; + static jclass gAudioAttributesBuilderClass; static jmethodID gAudioAttributesBuilderCstor; static struct { jmethodID build; jmethodID setUsage; + jmethodID setSystemUsage; jmethodID setInternalCapturePreset; jmethodID setContentType; jmethodID setFlags; @@ -109,9 +112,17 @@ static jint nativeAudioAttributesToJavaAudioAttributes( if (jAttributeBuilder.get() == nullptr) { return (jint)AUDIO_JAVA_ERROR; } - env->CallObjectMethod(jAttributeBuilder.get(), - gAudioAttributesBuilderMethods.setUsage, - attributes.usage); + + const bool isSystemUsage = env->CallStaticBooleanMethod(gAudioAttributesClass, + gAudioAttributesClassMethods.isSystemUsage, + attributes.usage); + if (isSystemUsage) { + env->CallObjectMethod(jAttributeBuilder.get(), + gAudioAttributesBuilderMethods.setSystemUsage, attributes.usage); + } else { + env->CallObjectMethod(jAttributeBuilder.get(), gAudioAttributesBuilderMethods.setUsage, + attributes.usage); + } env->CallObjectMethod(jAttributeBuilder.get(), gAudioAttributesBuilderMethods.setInternalCapturePreset, attributes.source); @@ -168,6 +179,9 @@ int register_android_media_AudioAttributes(JNIEnv *env) { jclass audioAttributesClass = FindClassOrDie(env, kClassPathName); gAudioAttributesClass = MakeGlobalRefOrDie(env, audioAttributesClass); + gAudioAttributesClassMethods.isSystemUsage = + GetStaticMethodIDOrDie(env, gAudioAttributesClass, "isSystemUsage", "(I)Z"); + gAudioAttributesFields.mUsage = GetFieldIDOrDie(env, audioAttributesClass, "mUsage", "I"); gAudioAttributesFields.mSource = GetFieldIDOrDie(env, audioAttributesClass, "mSource", "I"); gAudioAttributesFields.mContentType = @@ -186,6 +200,9 @@ int register_android_media_AudioAttributes(JNIEnv *env) gAudioAttributesBuilderMethods.setUsage = GetMethodIDOrDie( env, audioAttributesBuilderClass, "setUsage", "(I)Landroid/media/AudioAttributes$Builder;"); + gAudioAttributesBuilderMethods.setSystemUsage = + GetMethodIDOrDie(env, audioAttributesBuilderClass, "setSystemUsage", + "(I)Landroid/media/AudioAttributes$Builder;"); gAudioAttributesBuilderMethods.setInternalCapturePreset = GetMethodIDOrDie( env, audioAttributesBuilderClass, "setInternalCapturePreset", "(I)Landroid/media/AudioAttributes$Builder;"); diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index c4ee19519951..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) @@ -2239,6 +2269,31 @@ android_media_AudioSystem_isHapticPlaybackSupported(JNIEnv *env, jobject thiz) return AudioSystem::isHapticPlaybackSupported(); } +static jint android_media_AudioSystem_setSupportedSystemUsages(JNIEnv *env, jobject thiz, + jintArray systemUsages) { + std::vector<audio_usage_t> nativeSystemUsagesVector; + + if (systemUsages == nullptr) { + return (jint) AUDIO_JAVA_BAD_VALUE; + } + + int *nativeSystemUsages = nullptr; + nativeSystemUsages = env->GetIntArrayElements(systemUsages, 0); + + if (nativeSystemUsages != nullptr) { + jsize len = env->GetArrayLength(systemUsages); + for (size_t i = 0; i < len; i++) { + audio_usage_t nativeAudioUsage = + static_cast<audio_usage_t>(nativeSystemUsages[i]); + nativeSystemUsagesVector.push_back(nativeAudioUsage); + } + env->ReleaseIntArrayElements(systemUsages, nativeSystemUsages, 0); + } + + status_t status = AudioSystem::setSupportedSystemUsages(nativeSystemUsagesVector); + return (jint)nativeToJavaStatus(status); +} + static jint android_media_AudioSystem_setAllowedCapturePolicy(JNIEnv *env, jobject thiz, jint uid, jint flags) { return AudioSystem::setAllowedCapturePolicy(uid, flags); @@ -2430,6 +2485,7 @@ static const JNINativeMethod gMethods[] = { {"isHapticPlaybackSupported", "()Z", (void *)android_media_AudioSystem_isHapticPlaybackSupported}, {"getHwOffloadEncodingFormatsSupportedForA2DP", "(Ljava/util/ArrayList;)I", (void*)android_media_AudioSystem_getHwOffloadEncodingFormatsSupportedForA2DP}, + {"setSupportedSystemUsages", "([I)I", (void *)android_media_AudioSystem_setSupportedSystemUsages}, {"setAllowedCapturePolicy", "(II)I", (void *)android_media_AudioSystem_setAllowedCapturePolicy}, {"setRttEnabled", "(Z)I", (void *)android_media_AudioSystem_setRttEnabled}, {"setAudioHalPids", "([I)I", (void *)android_media_AudioSystem_setAudioHalPids}, @@ -2437,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_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 041019ec4841..a888b43a4854 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -1355,6 +1355,71 @@ static void android_media_AudioTrack_set_delay_padding(JNIEnv *env, jobject thi lpTrack->setParameters(param.toString()); } +static jint android_media_AudioTrack_setAudioDescriptionMixLeveldB(JNIEnv *env, jobject thiz, + jfloat level) { + sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); + if (lpTrack == nullptr) { + jniThrowException(env, "java/lang/IllegalStateException", "AudioTrack not initialized"); + return (jint)AUDIO_JAVA_ERROR; + } + + // TODO: replace in r-dev or r-tv-dev with code if HW is able to set audio mix level. + return (jint)AUDIO_JAVA_ERROR; +} + +static jint android_media_AudioTrack_getAudioDescriptionMixLeveldB(JNIEnv *env, jobject thiz, + jfloatArray level) { + sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); + if (lpTrack == nullptr) { + ALOGE("%s: AudioTrack not initialized", __func__); + return (jint)AUDIO_JAVA_ERROR; + } + jfloat *nativeLevel = (jfloat *)env->GetPrimitiveArrayCritical(level, NULL); + if (nativeLevel == nullptr) { + ALOGE("%s: Cannot retrieve level pointer", __func__); + return (jint)AUDIO_JAVA_ERROR; + } + + // TODO: replace in r-dev or r-tv-dev with code if HW is able to set audio mix level. + // By contract we can return -infinity if unsupported. + *nativeLevel = -std::numeric_limits<float>::infinity(); + env->ReleasePrimitiveArrayCritical(level, nativeLevel, 0 /* mode */); + nativeLevel = nullptr; + return (jint)AUDIO_JAVA_SUCCESS; +} + +static jint android_media_AudioTrack_setDualMonoMode(JNIEnv *env, jobject thiz, jint dualMonoMode) { + sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); + if (lpTrack == nullptr) { + jniThrowException(env, "java/lang/IllegalStateException", "AudioTrack not initialized"); + return (jint)AUDIO_JAVA_ERROR; + } + + // TODO: replace in r-dev or r-tv-dev with code if HW is able to set audio mix level. + return (jint)AUDIO_JAVA_ERROR; +} + +static jint android_media_AudioTrack_getDualMonoMode(JNIEnv *env, jobject thiz, + jintArray dualMonoMode) { + sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); + if (lpTrack == nullptr) { + ALOGE("%s: AudioTrack not initialized", __func__); + return (jint)AUDIO_JAVA_ERROR; + } + jfloat *nativeDualMonoMode = (jfloat *)env->GetPrimitiveArrayCritical(dualMonoMode, NULL); + if (nativeDualMonoMode == nullptr) { + ALOGE("%s: Cannot retrieve dualMonoMode pointer", __func__); + return (jint)AUDIO_JAVA_ERROR; + } + + // TODO: replace in r-dev or r-tv-dev with code if HW is able to select dual mono mode. + // By contract we can return DUAL_MONO_MODE_OFF if unsupported. + *nativeDualMonoMode = 0; // DUAL_MONO_MODE_OFF for now. + env->ReleasePrimitiveArrayCritical(dualMonoMode, nativeDualMonoMode, 0 /* mode */); + nativeDualMonoMode = nullptr; + return (jint)AUDIO_JAVA_SUCCESS; +} + // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- static const JNINativeMethod gMethods[] = { @@ -1425,6 +1490,12 @@ static const JNINativeMethod gMethods[] = { {"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation}, {"native_getPortId", "()I", (void *)android_media_AudioTrack_get_port_id}, {"native_set_delay_padding", "(II)V", (void *)android_media_AudioTrack_set_delay_padding}, + {"native_set_audio_description_mix_level_db", "(F)I", + (void *)android_media_AudioTrack_setAudioDescriptionMixLeveldB}, + {"native_get_audio_description_mix_level_db", "([F)I", + (void *)android_media_AudioTrack_getAudioDescriptionMixLeveldB}, + {"native_set_dual_mono_mode", "(I)I", (void *)android_media_AudioTrack_setDualMonoMode}, + {"native_get_dual_mono_mode", "([I)I", (void *)android_media_AudioTrack_getDualMonoMode}, }; // field names found in android/media/AudioTrack.java diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 566c3850bf14..b52381113895 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -193,7 +193,8 @@ static int read_memtrack_memory(struct memtrack_proc* p, int pid, { int err = memtrack_proc_get(p, pid); if (err != 0) { - ALOGW("failed to get memory consumption info: %d", err); + // The memtrack HAL may not be available, do not log to avoid flooding + // logcat. return err; } diff --git a/core/jni/android_service_DataLoaderService.cpp b/core/jni/android_service_DataLoaderService.cpp index a62d127a968f..b307a73ed05b 100644 --- a/core/jni/android_service_DataLoaderService.cpp +++ b/core/jni/android_service_DataLoaderService.cpp @@ -35,24 +35,24 @@ static jboolean nativeCreateDataLoader(JNIEnv* env, static jboolean nativeStartDataLoader(JNIEnv* env, jobject thiz, jint storageId) { - return DataLoaderService_OnStart(storageId); + return DataLoaderService_OnStart(env, storageId); } static jboolean nativeStopDataLoader(JNIEnv* env, jobject thiz, jint storageId) { - return DataLoaderService_OnStop(storageId); + return DataLoaderService_OnStop(env, storageId); } static jboolean nativeDestroyDataLoader(JNIEnv* env, jobject thiz, jint storageId) { - return DataLoaderService_OnDestroy(storageId); + return DataLoaderService_OnDestroy(env, storageId); } static jboolean nativePrepareImage(JNIEnv* env, jobject thiz, jint storageId, jobject addedFiles, jobject removedFiles) { - return DataLoaderService_OnPrepareImage(storageId, addedFiles, removedFiles); + return DataLoaderService_OnPrepareImage(env, storageId, addedFiles, removedFiles); } static void nativeWriteData(JNIEnv* env, diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index ee6e8c487bad..89dbca816d61 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -50,11 +50,14 @@ #include <pwd.h> #include <signal.h> #include <string.h> +#include <sys/epoll.h> #include <sys/errno.h> #include <sys/resource.h> #include <sys/stat.h> +#include <sys/syscall.h> #include <sys/sysinfo.h> #include <sys/types.h> +#include <time.h> #include <unistd.h> #define GUARD_THREAD_PRIORITY 0 @@ -1271,39 +1274,78 @@ void android_os_Process_removeAllProcessGroups(JNIEnv* env, jobject clazz) return removeAllProcessGroups(); } +static void throwErrnoException(JNIEnv* env, const char* functionName, int error) { + ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName)); + if (detailMessage.get() == NULL) { + // Not really much we can do here. We're probably dead in the water, + // but let's try to stumble on... + env->ExceptionClear(); + } + static jclass errnoExceptionClass = + MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/system/ErrnoException")); + + static jmethodID errnoExceptionCtor = + GetMethodIDOrDie(env, errnoExceptionClass, "<init>", "(Ljava/lang/String;I)V"); + + jobject exception = + env->NewObject(errnoExceptionClass, errnoExceptionCtor, detailMessage.get(), error); + env->Throw(reinterpret_cast<jthrowable>(exception)); +} + +// Wrapper function to the syscall pidfd_open, which creates a file +// descriptor that refers to the process whose PID is specified in pid. +static inline int sys_pidfd_open(pid_t pid, unsigned int flags) { + return syscall(__NR_pidfd_open, pid, flags); +} + +static jboolean android_os_Process_nativePidFdOpen(JNIEnv* env, jobject, jint pid, jint flags) { + int fd = sys_pidfd_open(pid, flags); + if (fd < 0) { + throwErrnoException(env, "nativePidFdOpen", errno); + return -1; + } + return fd; +} + static const JNINativeMethod methods[] = { - {"getUidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getUidForName}, - {"getGidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getGidForName}, - {"setThreadPriority", "(II)V", (void*)android_os_Process_setThreadPriority}, - {"setThreadScheduler", "(III)V", (void*)android_os_Process_setThreadScheduler}, - {"setCanSelfBackground", "(Z)V", (void*)android_os_Process_setCanSelfBackground}, - {"setThreadPriority", "(I)V", (void*)android_os_Process_setCallingThreadPriority}, - {"getThreadPriority", "(I)I", (void*)android_os_Process_getThreadPriority}, - {"getThreadScheduler", "(I)I", (void*)android_os_Process_getThreadScheduler}, - {"setThreadGroup", "(II)V", (void*)android_os_Process_setThreadGroup}, - {"setThreadGroupAndCpuset", "(II)V", (void*)android_os_Process_setThreadGroupAndCpuset}, - {"setProcessGroup", "(II)V", (void*)android_os_Process_setProcessGroup}, - {"getProcessGroup", "(I)I", (void*)android_os_Process_getProcessGroup}, - {"getExclusiveCores", "()[I", (void*)android_os_Process_getExclusiveCores}, - {"setSwappiness", "(IZ)Z", (void*)android_os_Process_setSwappiness}, - {"setArgV0", "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0}, - {"setUid", "(I)I", (void*)android_os_Process_setUid}, - {"setGid", "(I)I", (void*)android_os_Process_setGid}, - {"sendSignal", "(II)V", (void*)android_os_Process_sendSignal}, - {"sendSignalQuiet", "(II)V", (void*)android_os_Process_sendSignalQuiet}, - {"getFreeMemory", "()J", (void*)android_os_Process_getFreeMemory}, - {"getTotalMemory", "()J", (void*)android_os_Process_getTotalMemory}, - {"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V", (void*)android_os_Process_readProcLines}, - {"getPids", "(Ljava/lang/String;[I)[I", (void*)android_os_Process_getPids}, - {"readProcFile", "(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z", (void*)android_os_Process_readProcFile}, - {"parseProcLine", "([BII[I[Ljava/lang/String;[J[F)Z", (void*)android_os_Process_parseProcLine}, - {"getElapsedCpuTime", "()J", (void*)android_os_Process_getElapsedCpuTime}, - {"getPss", "(I)J", (void*)android_os_Process_getPss}, - {"getRss", "(I)[J", (void*)android_os_Process_getRss}, - {"getPidsForCommands", "([Ljava/lang/String;)[I", (void*)android_os_Process_getPidsForCommands}, - //{"setApplicationObject", "(Landroid/os/IBinder;)V", (void*)android_os_Process_setApplicationObject}, - {"killProcessGroup", "(II)I", (void*)android_os_Process_killProcessGroup}, - {"removeAllProcessGroups", "()V", (void*)android_os_Process_removeAllProcessGroups}, + {"getUidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getUidForName}, + {"getGidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getGidForName}, + {"setThreadPriority", "(II)V", (void*)android_os_Process_setThreadPriority}, + {"setThreadScheduler", "(III)V", (void*)android_os_Process_setThreadScheduler}, + {"setCanSelfBackground", "(Z)V", (void*)android_os_Process_setCanSelfBackground}, + {"setThreadPriority", "(I)V", (void*)android_os_Process_setCallingThreadPriority}, + {"getThreadPriority", "(I)I", (void*)android_os_Process_getThreadPriority}, + {"getThreadScheduler", "(I)I", (void*)android_os_Process_getThreadScheduler}, + {"setThreadGroup", "(II)V", (void*)android_os_Process_setThreadGroup}, + {"setThreadGroupAndCpuset", "(II)V", (void*)android_os_Process_setThreadGroupAndCpuset}, + {"setProcessGroup", "(II)V", (void*)android_os_Process_setProcessGroup}, + {"getProcessGroup", "(I)I", (void*)android_os_Process_getProcessGroup}, + {"getExclusiveCores", "()[I", (void*)android_os_Process_getExclusiveCores}, + {"setSwappiness", "(IZ)Z", (void*)android_os_Process_setSwappiness}, + {"setArgV0", "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0}, + {"setUid", "(I)I", (void*)android_os_Process_setUid}, + {"setGid", "(I)I", (void*)android_os_Process_setGid}, + {"sendSignal", "(II)V", (void*)android_os_Process_sendSignal}, + {"sendSignalQuiet", "(II)V", (void*)android_os_Process_sendSignalQuiet}, + {"getFreeMemory", "()J", (void*)android_os_Process_getFreeMemory}, + {"getTotalMemory", "()J", (void*)android_os_Process_getTotalMemory}, + {"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V", + (void*)android_os_Process_readProcLines}, + {"getPids", "(Ljava/lang/String;[I)[I", (void*)android_os_Process_getPids}, + {"readProcFile", "(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z", + (void*)android_os_Process_readProcFile}, + {"parseProcLine", "([BII[I[Ljava/lang/String;[J[F)Z", + (void*)android_os_Process_parseProcLine}, + {"getElapsedCpuTime", "()J", (void*)android_os_Process_getElapsedCpuTime}, + {"getPss", "(I)J", (void*)android_os_Process_getPss}, + {"getRss", "(I)[J", (void*)android_os_Process_getRss}, + {"getPidsForCommands", "([Ljava/lang/String;)[I", + (void*)android_os_Process_getPidsForCommands}, + //{"setApplicationObject", "(Landroid/os/IBinder;)V", + //(void*)android_os_Process_setApplicationObject}, + {"killProcessGroup", "(II)I", (void*)android_os_Process_killProcessGroup}, + {"removeAllProcessGroups", "()V", (void*)android_os_Process_removeAllProcessGroups}, + {"nativePidFdOpen", "(II)I", (void*)android_os_Process_nativePidFdOpen}, }; int register_android_os_Process(JNIEnv* env) diff --git a/core/proto/android/app/job/enums.proto b/core/proto/android/app/job/enums.proto index f702b3e37bda..d2bf20591f92 100644 --- a/core/proto/android/app/job/enums.proto +++ b/core/proto/android/app/job/enums.proto @@ -34,4 +34,5 @@ enum StopReasonEnum { STOP_REASON_TIMEOUT = 3; STOP_REASON_DEVICE_IDLE = 4; STOP_REASON_DEVICE_THERMAL = 5; + STOP_REASON_RESTRAINED = 6; } diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index da8c944ba278..d08cbed5909d 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -56,6 +56,7 @@ import "frameworks/base/core/proto/android/util/event_log_tags.proto"; import "frameworks/base/core/proto/android/util/log.proto"; import "frameworks/base/core/proto/android/privacy.proto"; import "frameworks/base/core/proto/android/section.proto"; +import "frameworks/base/proto/src/ipconnectivity.proto"; package android.os; @@ -480,6 +481,11 @@ message IncidentProto { (section).args = "cpuinfo --proto" ]; + optional .clearcut.connectivity.IpConnectivityLog ip_connectivity_metrics = 3049 [ + (section).type = SECTION_DUMPSYS, + (section).args = "connmetrics --proto" + ]; + // Reserved for OEMs. extensions 50000 to 100000; } diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto index b71e5395730e..303d62dbb30a 100644 --- a/core/proto/android/server/jobscheduler.proto +++ b/core/proto/android/server/jobscheduler.proto @@ -282,6 +282,10 @@ message ConstantsProto { // expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past // WINDOW_SIZE_MS. optional int64 rare_window_size_ms = 6; + // The quota window size of the particular standby bucket. Apps in this standby bucket are + // expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past + // WINDOW_SIZE_MS. + optional int64 restricted_window_size_ms = 20; // The maximum amount of time an app can have its jobs running within a 24 hour window. optional int64 max_execution_time_ms = 7; // The maximum number of jobs an app can run within this particular standby bucket's @@ -296,6 +300,9 @@ message ConstantsProto { // The maximum number of jobs an app can run within this particular standby bucket's // window size. optional int32 max_job_count_rare = 11; + // The maximum number of jobs an app can run within this particular standby bucket's + // window size. + optional int32 max_job_count_restricted = 21; // The period of time used to rate limit recently run jobs. optional int32 rate_limiting_window_ms = 19; // The maximum number of jobs that should be allowed to run in the past @@ -313,12 +320,17 @@ message ConstantsProto { // The maximum number of timing sessions an app can run within this particular standby // bucket's window size. optional int32 max_session_count_rare = 16; + // The maximum number of timing sessions an app can run within this particular standby + // bucket's window size. + optional int32 max_session_count_restricted = 22; // The maximum number of timing sessions that should be allowed to run in the past // rate_limiting_window_ms. optional int32 max_session_count_per_rate_limiting_window = 17; // Treat two distinct {@link TimingSession}s as the same if they start and end within this // amount of time of each other. optional int64 timing_session_coalescing_duration_ms = 18; + + // Next tag: 23 } optional QuotaController quota_controller = 24; @@ -943,6 +955,9 @@ message JobStatusDumpProto { optional JobInfo job_info = 6; repeated ConstraintEnum required_constraints = 7; + // Dynamic constraints are additional constraints imposed by the system that MUST be met before + // the app can run if the app does not have quota. + repeated ConstraintEnum dynamic_constraints = 31; repeated ConstraintEnum satisfied_constraints = 8; repeated ConstraintEnum unsatisfied_constraints = 9; optional bool is_doze_whitelisted = 10; @@ -956,6 +971,8 @@ message JobStatusDumpProto { // Battery Saver). This implicit constraint must be satisfied for the // job to run. optional bool is_not_restricted_in_bg = 2; + // True if dynamic constraints have been satisfied. + optional bool is_dynamic_satisfied = 3; } optional ImplicitConstraints implicit_constraints = 25; @@ -998,6 +1015,7 @@ message JobStatusDumpProto { FREQUENT = 2; RARE = 3; NEVER = 4; + RESTRICTED = 5; } optional Bucket standby_bucket = 17; optional bool is_exempted_from_app_standby = 27; @@ -1028,7 +1046,7 @@ message JobStatusDumpProto { // was no attempt. optional int64 time_since_first_force_batch_attempt_ms = 29; - // Next tag: 31 + // Next tag: 32 } // Dump from com.android.server.job.JobConcurrencyManager. diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index da2e078fd4ef..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 --> @@ -3231,6 +3248,13 @@ <permission android:name="android.permission.BIND_NFC_SERVICE" android:protectionLevel="signature" /> + <!-- Must be required by a {@link android.service.quickaccesswallet.QuickAccessWalletService} + to ensure that only the system can bind to it. + <p>Protection level: signature + --> + <permission android:name="android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE" + android:protectionLevel="signature" /> + <!-- Must be required by the PrintSpooler to ensure that only the system can bind to it. @hide --> <permission android:name="android.permission.BIND_PRINT_SPOOLER_SERVICE" @@ -3708,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 --> @@ -3786,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/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 e0d849223552..940e9f1ef88b 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -8329,6 +8329,26 @@ <attr name="successColor" format="color|reference"/> </declare-styleable> + <!-- =============================== --> + <!-- QuickAccessWallet attributes --> + <!-- =============================== --> + <eat-comment /> + + <!-- Use <code>quickaccesswallet-service</code> as the root tag of the XML resource + that describes a {@link android.service.quickaccesswallet.QuickAccessWalletService}, + which is referenced from its + {@link android.service.quickaccesswallet.QuickAccessWalletService#SERVICE_META_DATA} + meta-data entry. + --> + <declare-styleable name="QuickAccessWalletService"> + <!-- Fully qualified class name of an activity that allows the user to modify + the settings for this service. --> + <attr name="settingsActivity"/> + <!-- Fully qualified class name of an activity that allows the user to view + their entire wallet --> + <attr name="targetActivity"/> + </declare-styleable> + <!-- Use <code>recognition-service</code> as the root tag of the XML resource that describes a {@link android.speech.RecognitionService}, which is referenced from its {@link android.speech.RecognitionService#SERVICE_META_DATA} meta-data entry. 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..42127e77317f 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> @@ -4326,6 +4288,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/strings.xml b/core/res/res/values/strings.xml index f25f97c39f7e..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> @@ -5271,6 +5277,10 @@ <!-- Description of media type: presentation file, such as PPT. The 'extension' variable is the file name extension. [CHAR LIMIT=32] --> <string name="mime_type_presentation_ext"><xliff:g id="extension" example="PDF">%1$s</xliff:g> presentation</string> + <!-- Strings for Bluetooth service --> + <!-- toast message informing user that Bluetooth stays on after airplane mode is turned on. [CHAR LIMIT=NONE] --> + <string name="bluetooth_airplane_mode_toast">Bluetooth will stay on during airplane mode</string> + <!-- Strings for car --> <!-- String displayed when loading a user in the car [CHAR LIMIT=30] --> <string name="car_loading_profile">Loading</string> @@ -5311,4 +5321,12 @@ <!-- Accessibility description of caption view --> <string name="accessibility_freeform_caption">Caption bar of <xliff:g id="app_name">%1$s</xliff:g>.</string> + + <!-- Text to tell the user that a package has been forced by themselves in the RESTRICTED bucket. [CHAR LIMIT=NONE] --> + <string name="as_app_forced_to_restricted_bucket"> + <xliff:g id="package_name" example="com.android.example">%1$s</xliff:g> has been put into the RESTRICTED bucket</string> + + <!-- ResolverActivity - profile tabs --> + <string name="resolver_personal_tab">Personal</string> + <string name="resolver_work_tab">Work</string> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 527798d45b96..ea8ca9aa1c49 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" /> @@ -1209,6 +1212,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 +1874,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 +2023,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" /> @@ -3723,6 +3726,9 @@ <java-symbol type="string" name="mime_type_presentation" /> <java-symbol type="string" name="mime_type_presentation_ext" /> + <!-- For Bluetooth service --> + <java-symbol type="string" name="bluetooth_airplane_mode_toast" /> + <!-- For high refresh rate displays --> <java-symbol type="integer" name="config_defaultPeakRefreshRate" /> <java-symbol type="integer" name="config_defaultRefreshRateInZone" /> @@ -3807,6 +3813,9 @@ <java-symbol type="string" name="config_rawContactsLocalAccountName" /> <java-symbol type="string" name="config_rawContactsLocalAccountType" /> + <!-- For App Standby --> + <java-symbol type="string" name="as_app_forced_to_restricted_bucket" /> + <!-- Assistant handles --> <java-symbol type="dimen" name="assist_handle_shadow_radius" /> @@ -3816,6 +3825,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" /> @@ -3827,4 +3839,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/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/content/ApexContextTest.java b/core/tests/coretests/src/android/content/ApexContextTest.java new file mode 100644 index 000000000000..d15c64d0935d --- /dev/null +++ b/core/tests/coretests/src/android/content/ApexContextTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content; + +import static org.junit.Assert.assertEquals; + +import android.os.UserHandle; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class ApexContextTest { + + @Test + public void dataDirectoryPathsAreAsExpected() { + ApexContext apexContext = ApexContext.getApexContext("my.apex"); + + assertEquals("/data/misc/apexdata/my.apex", + apexContext.getDeviceProtectedDataDir().getAbsolutePath()); + + assertEquals("/data/misc_de/5/apexdata/my.apex", + apexContext.getDeviceProtectedDataDirForUser(UserHandle.of(5)).getAbsolutePath()); + + assertEquals("/data/misc_ce/16/apexdata/my.apex", + apexContext.getCredentialProtectedDataDirForUser( + UserHandle.of(16)).getAbsolutePath()); + } +} 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/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java index fbe4c1a10fe1..0c38e7136655 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java @@ -27,9 +27,13 @@ import static android.widget.espresso.FloatingToolbarEspressoUtils.sleepForFloat import static android.widget.espresso.TextViewActions.Handle; import static android.widget.espresso.TextViewActions.clickOnTextAtIndex; import static android.widget.espresso.TextViewActions.doubleClickOnTextAtIndex; +import static android.widget.espresso.TextViewActions.doubleTapAndDragHandle; import static android.widget.espresso.TextViewActions.doubleTapAndDragOnText; +import static android.widget.espresso.TextViewActions.doubleTapHandle; import static android.widget.espresso.TextViewActions.dragHandle; import static android.widget.espresso.TextViewActions.longPressAndDragOnText; +import static android.widget.espresso.TextViewActions.longPressAndDragHandle; +import static android.widget.espresso.TextViewActions.longPressHandle; import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex; import static android.widget.espresso.TextViewAssertions.doesNotHaveStyledText; import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex; @@ -511,6 +515,111 @@ public class TextViewActivityTest { } @Test + public void testInsertionHandle_touchThrough() { + final TextView textView = mActivity.findViewById(R.id.textview); + boolean cursorControlEnabled = textView.getEditorForTesting().getCursorControlEnabled(); + boolean cursorDragEnabled = Editor.FLAG_ENABLE_CURSOR_DRAG; + textView.getEditorForTesting().setCursorControlEnabled(true); + Editor.FLAG_ENABLE_CURSOR_DRAG = true; + + testInsertionHandle(); + testInsertionHandle_multiLine(); + + textView.getEditorForTesting().setCursorControlEnabled(cursorControlEnabled); + Editor.FLAG_ENABLE_CURSOR_DRAG = cursorDragEnabled; + } + + @Test + public void testInsertionHandle_longPressToSelect() { + // This test only makes sense when Cursor Control flag is enabled. + final TextView textView = mActivity.findViewById(R.id.textview); + boolean cursorControlEnabled = textView.getEditorForTesting().getCursorControlEnabled(); + boolean cursorDragEnabled = Editor.FLAG_ENABLE_CURSOR_DRAG; + textView.getEditorForTesting().setCursorControlEnabled(true); + Editor.FLAG_ENABLE_CURSOR_DRAG = true; + + final String text = "hello the world"; + onView(withId(R.id.textview)).perform(replaceText(text)); + + onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length())); + onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length())); + + onHandleView(com.android.internal.R.id.insertion_handle).perform(longPressHandle(textView)); + onView(withId(R.id.textview)).check(hasSelection("world")); + + textView.getEditorForTesting().setCursorControlEnabled(cursorControlEnabled); + Editor.FLAG_ENABLE_CURSOR_DRAG = cursorDragEnabled; + } + + @Test + public void testInsertionHandle_longPressAndDragToSelect() { + // This test only makes sense when Cursor Control flag is enabled. + final TextView textView = mActivity.findViewById(R.id.textview); + boolean cursorControlEnabled = textView.getEditorForTesting().getCursorControlEnabled(); + boolean cursorDragEnabled = Editor.FLAG_ENABLE_CURSOR_DRAG; + textView.getEditorForTesting().setCursorControlEnabled(true); + Editor.FLAG_ENABLE_CURSOR_DRAG = true; + + final String text = "hello the world"; + onView(withId(R.id.textview)).perform(replaceText(text)); + + onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length())); + onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length())); + + onHandleView(com.android.internal.R.id.insertion_handle) + .perform(longPressAndDragHandle(textView, Handle.INSERTION, text.indexOf('t'))); + onView(withId(R.id.textview)).check(hasSelection("the world")); + + textView.getEditorForTesting().setCursorControlEnabled(cursorControlEnabled); + Editor.FLAG_ENABLE_CURSOR_DRAG = cursorDragEnabled; + } + + @Test + public void testInsertionHandle_doubleTapToSelect() { + // This test only makes sense when Cursor Control flag is enabled. + final TextView textView = mActivity.findViewById(R.id.textview); + boolean cursorControlEnabled = textView.getEditorForTesting().getCursorControlEnabled(); + boolean cursorDragEnabled = Editor.FLAG_ENABLE_CURSOR_DRAG; + textView.getEditorForTesting().setCursorControlEnabled(true); + Editor.FLAG_ENABLE_CURSOR_DRAG = true; + + final String text = "hello the world"; + onView(withId(R.id.textview)).perform(replaceText(text)); + + onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length())); + onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length())); + + onHandleView(com.android.internal.R.id.insertion_handle).perform(doubleTapHandle(textView)); + onView(withId(R.id.textview)).check(hasSelection("world")); + + textView.getEditorForTesting().setCursorControlEnabled(cursorControlEnabled); + Editor.FLAG_ENABLE_CURSOR_DRAG = cursorDragEnabled; + } + + @Test + public void testInsertionHandle_doubleTapAndDragToSelect() { + // This test only makes sense when Cursor Control flag is enabled. + final TextView textView = mActivity.findViewById(R.id.textview); + boolean cursorControlEnabled = textView.getEditorForTesting().getCursorControlEnabled(); + boolean cursorDragEnabled = Editor.FLAG_ENABLE_CURSOR_DRAG; + textView.getEditorForTesting().setCursorControlEnabled(true); + Editor.FLAG_ENABLE_CURSOR_DRAG = true; + + final String text = "hello the world"; + onView(withId(R.id.textview)).perform(replaceText(text)); + + onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length())); + onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length())); + + onHandleView(com.android.internal.R.id.insertion_handle) + .perform(doubleTapAndDragHandle(textView, Handle.INSERTION, text.indexOf('t'))); + onView(withId(R.id.textview)).check(hasSelection("the world")); + + textView.getEditorForTesting().setCursorControlEnabled(cursorControlEnabled); + Editor.FLAG_ENABLE_CURSOR_DRAG = cursorDragEnabled; + } + + @Test public void testSelectionHandles() { final String text = "abcd efg hijk lmn"; onView(withId(R.id.textview)).perform(replaceText(text)); diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java index 4808a0b0656a..d4c9971613e5 100644 --- a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java +++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java @@ -199,6 +199,86 @@ public final class TextViewActions { } /** + * Returns an action that long presses then drags on handle from the current position to + * endIndex on the TextView.<br> + * <br> + * View constraints: + * <ul> + * <li>must be a TextView's drag-handle displayed on screen + * <ul> + * + * @param textView TextView the handle is on + * @param handleType Type of the handle + * @param endIndex The index of the TextView's text to end the drag at + */ + public static ViewAction longPressAndDragHandle(TextView textView, Handle handleType, + int endIndex) { + return actionWithAssertions( + new DragAction( + DragAction.Drag.LONG_PRESS, + new CurrentHandleCoordinates(textView), + new HandleCoordinates(textView, handleType, endIndex, true), + Press.FINGER, + Editor.HandleView.class)); + } + + /** + * Returns an action that long presses on the current handle.<br> + * <br> + * View constraints: + * <ul> + * <li>must be a TextView's drag-handle displayed on screen + * <ul> + * + * @param textView TextView the handle is on + */ + public static ViewAction longPressHandle(TextView textView) { + return actionWithAssertions( + new ViewClickAction(Tap.LONG, new CurrentHandleCoordinates(textView), + Press.FINGER)); + } + + /** + * Returns an action that double tap then drags on handle from the current position to + * endIndex on the TextView.<br> + * <br> + * View constraints: + * <ul> + * <li>must be a TextView's drag-handle displayed on screen + * <ul> + * + * @param textView TextView the handle is on + * @param handleType Type of the handle + * @param endIndex The index of the TextView's text to end the drag at + */ + public static ViewAction doubleTapAndDragHandle(TextView textView, Handle handleType, + int endIndex) { + return actionWithAssertions( + new DragAction( + DragAction.Drag.DOUBLE_TAP, + new CurrentHandleCoordinates(textView), + new HandleCoordinates(textView, handleType, endIndex, true), + Press.FINGER, + Editor.HandleView.class)); + } + + /** + * Returns an action that double tap on the current handle.<br> + * <br> + * View constraints: + * <ul> + * <li>must be a TextView's drag-handle displayed on screen + * <ul> + * + * @param textView TextView the handle is on + */ + public static ViewAction doubleTapHandle(TextView textView) { + return actionWithAssertions( + new ViewClickAction(Tap.DOUBLE, new CurrentHandleCoordinates(textView), + Press.FINGER)); + } + + /** * Returns an action that double taps then drags on text from startIndex to endIndex on the * TextView.<br> * <br> 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/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/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 18b5f5d6ac38..3677b8f9e66e 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -63,6 +63,7 @@ applications that come with the platform <privapp-permissions package="com.android.location.fused"> <permission name="android.permission.INSTALL_LOCATION_PROVIDER"/> + <permission name="android.permission.UPDATE_DEVICE_STATS"/> </privapp-permissions> <privapp-permissions package="com.android.managedprovisioning"> diff --git a/drm/java/android/drm/DrmConvertedStatus.java b/drm/java/android/drm/DrmConvertedStatus.java index 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/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java index fab96a1e9fbd..928e607abbbe 100644 --- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java +++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java @@ -16,6 +16,7 @@ package android.graphics.drawable; +import android.annotation.DrawableRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; @@ -223,6 +224,7 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback final int deviceDensity = Drawable.resolveDensity(r, 0); state.setDensity(deviceDensity); state.mSrcDensityOverride = mSrcDensityOverride; + state.mSourceDrawableId = Resources.getAttributeSetSourceResId(attrs); final ChildDrawable[] array = state.mChildren; for (int i = 0; i < state.mChildren.length; i++) { @@ -446,6 +448,17 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback } /** + * If the drawable was inflated from XML, this returns the resource ID for the drawable + * + * @hide + */ + @DrawableRes + public int getSourceDrawableResId() { + final LayerState state = mLayerState; + return state == null ? Resources.ID_NULL : state.mSourceDrawableId; + } + + /** * Inflates child layers using the specified parser. */ private void inflateLayers(@NonNull Resources r, @NonNull XmlPullParser parser, @@ -944,6 +957,8 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback @Config int mChangingConfigurations; @Config int mChildrenChangingConfigurations; + @DrawableRes int mSourceDrawableId = Resources.ID_NULL; + private boolean mCheckedOpacity; private int mOpacity; diff --git a/identity/java/android/security/identity/WritableIdentityCredential.java b/identity/java/android/security/identity/WritableIdentityCredential.java index 5f575b9d56f3..e2a389bfd4da 100644 --- a/identity/java/android/security/identity/WritableIdentityCredential.java +++ b/identity/java/android/security/identity/WritableIdentityCredential.java @@ -31,6 +31,11 @@ import java.util.Collection; */ public abstract class WritableIdentityCredential { /** + * @hide + */ + protected WritableIdentityCredential() {} + + /** * Generates and returns an X.509 certificate chain for the CredentialKey which identifies this * credential to the issuing authority. The certificate contains an * <a href="https://source.android.com/security/keystore/attestation">Android Keystore</a> diff --git a/packages/WindowManager/OWNERS b/libs/WindowManager/OWNERS index 063d4594f2fa..063d4594f2fa 100644 --- a/packages/WindowManager/OWNERS +++ b/libs/WindowManager/OWNERS diff --git a/packages/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index b8934dc8c583..b8934dc8c583 100644 --- a/packages/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp diff --git a/packages/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml index ea8a5c305029..ea8a5c305029 100644 --- a/packages/WindowManager/Shell/AndroidManifest.xml +++ b/libs/WindowManager/Shell/AndroidManifest.xml diff --git a/packages/WindowManager/Shell/OWNERS b/libs/WindowManager/Shell/OWNERS index 4390004f5f93..4390004f5f93 100644 --- a/packages/WindowManager/Shell/OWNERS +++ b/libs/WindowManager/Shell/OWNERS diff --git a/packages/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index c894eb0133b5..c894eb0133b5 100644 --- a/packages/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml diff --git a/packages/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java index 273bd27a221b..273bd27a221b 100644 --- a/packages/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShell.java diff --git a/packages/WindowManager/Shell/tests/Android.bp b/libs/WindowManager/Shell/tests/Android.bp index 78fa45ebdf94..78fa45ebdf94 100644 --- a/packages/WindowManager/Shell/tests/Android.bp +++ b/libs/WindowManager/Shell/tests/Android.bp diff --git a/packages/WindowManager/Shell/tests/AndroidManifest.xml b/libs/WindowManager/Shell/tests/AndroidManifest.xml index a8f795ec8a8d..a8f795ec8a8d 100644 --- a/packages/WindowManager/Shell/tests/AndroidManifest.xml +++ b/libs/WindowManager/Shell/tests/AndroidManifest.xml diff --git a/packages/WindowManager/Shell/tests/AndroidTest.xml b/libs/WindowManager/Shell/tests/AndroidTest.xml index 4dce4db360e4..4dce4db360e4 100644 --- a/packages/WindowManager/Shell/tests/AndroidTest.xml +++ b/libs/WindowManager/Shell/tests/AndroidTest.xml diff --git a/packages/WindowManager/Shell/tests/res/values/config.xml b/libs/WindowManager/Shell/tests/res/values/config.xml index c894eb0133b5..c894eb0133b5 100644 --- a/packages/WindowManager/Shell/tests/res/values/config.xml +++ b/libs/WindowManager/Shell/tests/res/values/config.xml diff --git a/packages/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java index 376875b143a1..376875b143a1 100644 --- a/packages/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java +++ b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java 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/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp index a6c4e9db7280..4b2857f6c290 100644 --- a/libs/hwui/hwui/ImageDecoder.cpp +++ b/libs/hwui/hwui/ImageDecoder.cpp @@ -31,7 +31,7 @@ ImageDecoder::ImageDecoder(std::unique_ptr<SkAndroidCodec> codec, sk_sp<SkPngChu , mDecodeSize(mTargetSize) , mOutColorType(mCodec->computeOutputColorType(kN32_SkColorType)) , mUnpremultipliedRequired(false) - , mOutColorSpace(mCodec->getInfo().refColorSpace()) + , mOutColorSpace(mCodec->computeOutputColorSpace(mOutColorType, nullptr)) , mSampleSize(1) { } @@ -111,7 +111,6 @@ bool ImageDecoder::setOutColorType(SkColorType colorType) { if (!gray()) { return false; } - mOutColorSpace = nullptr; break; case kN32_SkColorType: break; @@ -137,9 +136,15 @@ void ImageDecoder::setOutColorSpace(sk_sp<SkColorSpace> colorSpace) { mOutColorSpace = std::move(colorSpace); } +sk_sp<SkColorSpace> ImageDecoder::getOutputColorSpace() const { + // kGray_8 is used for ALPHA_8, which ignores the color space. + return mOutColorType == kGray_8_SkColorType ? nullptr : mOutColorSpace; +} + + SkImageInfo ImageDecoder::getOutputInfo() const { SkISize size = mCropRect ? mCropRect->size() : mTargetSize; - return SkImageInfo::Make(size, mOutColorType, getOutAlphaType(), mOutColorSpace); + return SkImageInfo::Make(size, mOutColorType, getOutAlphaType(), getOutputColorSpace()); } bool ImageDecoder::opaque() const { @@ -154,7 +159,7 @@ SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) { void* decodePixels = pixels; size_t decodeRowBytes = rowBytes; auto decodeInfo = SkImageInfo::Make(mDecodeSize, mOutColorType, getOutAlphaType(), - mOutColorSpace); + getOutputColorSpace()); // Used if we need a temporary before scaling or subsetting. // FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380. SkBitmap tmp; diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h index 96f97e5421f3..0c99f84cbb72 100644 --- a/libs/hwui/hwui/ImageDecoder.h +++ b/libs/hwui/hwui/ImageDecoder.h @@ -66,6 +66,7 @@ private: ImageDecoder& operator=(const ImageDecoder&) = delete; SkAlphaType getOutAlphaType() const; + sk_sp<SkColorSpace> getOutputColorSpace() const; }; } // namespace android diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp index 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/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp index c445885c5c63..71a27ced2e09 100644 --- a/libs/hwui/utils/Color.cpp +++ b/libs/hwui/utils/Color.cpp @@ -108,7 +108,9 @@ SkColorType PixelFormatToColorType(android::PixelFormat format) { } } -// FIXME: Share with the version in android_bitmap.cpp? +namespace { +static constexpr skcms_TransferFunction k2Dot6 = {2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; + // Skia's SkNamedGamut::kDCIP3 is based on a white point of D65. This gamut // matches the white point used by ColorSpace.Named.DCIP3. static constexpr skcms_Matrix3x3 kDCIP3 = {{ @@ -117,6 +119,87 @@ static constexpr skcms_Matrix3x3 kDCIP3 = {{ {0.000800549, 0.0432385, 0.78275}, }}; +static bool nearlyEqual(float a, float b) { + // By trial and error, this is close enough to match for the ADataSpaces we + // compare for. + return ::fabs(a - b) < .002f; +} + +static bool nearlyEqual(const skcms_TransferFunction& x, const skcms_TransferFunction& y) { + return nearlyEqual(x.g, y.g) + && nearlyEqual(x.a, y.a) + && nearlyEqual(x.b, y.b) + && nearlyEqual(x.c, y.c) + && nearlyEqual(x.d, y.d) + && nearlyEqual(x.e, y.e) + && nearlyEqual(x.f, y.f); +} + +static bool nearlyEqual(const skcms_Matrix3x3& x, const skcms_Matrix3x3& y) { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (!nearlyEqual(x.vals[i][j], y.vals[i][j])) return false; + } + } + return true; +} + +} // anonymous namespace + +android_dataspace ColorSpaceToADataSpace(SkColorSpace* colorSpace, SkColorType colorType) { + if (!colorSpace) { + return HAL_DATASPACE_UNKNOWN; + } + + if (colorSpace->isSRGB()) { + if (colorType == kRGBA_F16_SkColorType) { + return HAL_DATASPACE_V0_SCRGB; + } + return HAL_DATASPACE_V0_SRGB; + } + + skcms_TransferFunction fn; + LOG_ALWAYS_FATAL_IF(!colorSpace->isNumericalTransferFn(&fn)); + + skcms_Matrix3x3 gamut; + LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&gamut)); + + if (nearlyEqual(gamut, SkNamedGamut::kSRGB)) { + if (nearlyEqual(fn, SkNamedTransferFn::kLinear)) { + // Skia doesn't differentiate amongst the RANGES. In Java, we associate + // LINEAR_EXTENDED_SRGB with F16, and LINEAR_SRGB with other Configs. + // Make the same association here. + if (colorType == kRGBA_F16_SkColorType) { + return HAL_DATASPACE_V0_SCRGB_LINEAR; + } + return HAL_DATASPACE_V0_SRGB_LINEAR; + } + + if (nearlyEqual(fn, SkNamedTransferFn::kRec2020)) { + return HAL_DATASPACE_V0_BT709; + } + } + + if (nearlyEqual(fn, SkNamedTransferFn::kSRGB) && nearlyEqual(gamut, SkNamedGamut::kDCIP3)) { + return HAL_DATASPACE_DISPLAY_P3; + } + + if (nearlyEqual(fn, SkNamedTransferFn::k2Dot2) && nearlyEqual(gamut, SkNamedGamut::kAdobeRGB)) { + return HAL_DATASPACE_ADOBE_RGB; + } + + if (nearlyEqual(fn, SkNamedTransferFn::kRec2020) && + nearlyEqual(gamut, SkNamedGamut::kRec2020)) { + return HAL_DATASPACE_BT2020; + } + + if (nearlyEqual(fn, k2Dot6) && nearlyEqual(gamut, kDCIP3)) { + return HAL_DATASPACE_DCI_P3; + } + + return HAL_DATASPACE_UNKNOWN; +} + sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) { if (dataspace == HAL_DATASPACE_UNKNOWN) { return SkColorSpace::MakeSRGB(); @@ -126,7 +209,7 @@ sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) { // needs to use the locally-defined kDCIP3 gamut, rather than the one in // Skia (SkNamedGamut), which is used for other data spaces with // HAL_DATASPACE_STANDARD_DCI_P3 (e.g. HAL_DATASPACE_DISPLAY_P3). - return SkColorSpace::MakeRGB({2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, kDCIP3); + return SkColorSpace::MakeRGB(k2Dot6, kDCIP3); } skcms_Matrix3x3 gamut; @@ -165,7 +248,7 @@ sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) { case HAL_DATASPACE_TRANSFER_GAMMA2_2: return SkColorSpace::MakeRGB({2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut); case HAL_DATASPACE_TRANSFER_GAMMA2_6: - return SkColorSpace::MakeRGB({2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut); + return SkColorSpace::MakeRGB(k2Dot6, gamut); case HAL_DATASPACE_TRANSFER_GAMMA2_8: return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut); case HAL_DATASPACE_TRANSFER_ST2084: diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h index 07b5ec8fe0f0..a76f7e499c37 100644 --- a/libs/hwui/utils/Color.h +++ b/libs/hwui/utils/Color.h @@ -105,6 +105,22 @@ ANDROID_API SkColorType PixelFormatToColorType(android::PixelFormat format); ANDROID_API sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace); +/** + * Return the android_dataspace corresponding to colorSpace. + * + * Note: This currently only returns android_dataspaces with corresponding + * ADataSpaces. The NDK relies on this, so if you need to update it to return + * an android_dataspace *without* an ADataSpace, the NDK methods need to be + * updated. + * + * @param colorSpace May be null, in which case this will return + * HAL_DATASPACE_UNKNOWN. + * @param colorType Some SkColorSpaces are associated with more than one + * android_dataspace. In that case, the SkColorType is used to + * determine which one to return. + */ +ANDROID_API android_dataspace ColorSpaceToADataSpace(SkColorSpace*, SkColorType); + struct Lab { float L; float a; diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java index 70abbb3019fc..3eeb3a2051e9 100644 --- a/location/java/android/location/GnssMeasurement.java +++ b/location/java/android/location/GnssMeasurement.java @@ -778,10 +778,12 @@ public final class GnssMeasurement implements Parcelable { /** * Gets the Carrier-to-noise density in dB-Hz. * - * <p>Typical range: 10-50 db-Hz. + * <p>Typical range: 10-50 dB-Hz. The range of possible C/N0 values is 0-63 dB-Hz to handle + * some edge cases. * * <p>The value contains the measured C/N0 for the signal at the antenna input. */ + @FloatRange(from = 0, to = 63) public double getCn0DbHz() { return mCn0DbHz; } @@ -805,13 +807,14 @@ public final class GnssMeasurement implements Parcelable { /** * Gets the baseband carrier-to-noise density in dB-Hz. * - * <p>Typical range: 0-50 dB-Hz. + * <p>Typical range: 10-50 dB-Hz. The range of possible baseband C/N0 values is 0-63 dB-Hz to + * handle some edge cases. * * <p>The value contains the measured C/N0 for the signal at the baseband. This is typically * a few dB weaker than the value estimated for C/N0 at the antenna port, which is reported * in {@link #getCn0DbHz()}. */ - @FloatRange(from = 0, to = 50) + @FloatRange(from = 0, to = 63) public double getBasebandCn0DbHz() { return mBasebandCn0DbHz; } diff --git a/location/java/android/location/GnssNavigationMessage.java b/location/java/android/location/GnssNavigationMessage.java index a83db3fcf384..ca0bfb1added 100644 --- a/location/java/android/location/GnssNavigationMessage.java +++ b/location/java/android/location/GnssNavigationMessage.java @@ -16,9 +16,9 @@ package android.location; -import android.annotation.TestApi; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -39,7 +39,8 @@ public final class GnssNavigationMessage implements Parcelable { */ @Retention(RetentionPolicy.SOURCE) @IntDef({TYPE_UNKNOWN, TYPE_GPS_L1CA, TYPE_GPS_L2CNAV, TYPE_GPS_L5CNAV, TYPE_GPS_CNAV2, - TYPE_GLO_L1CA, TYPE_BDS_D1, TYPE_BDS_D2, TYPE_GAL_I, TYPE_GAL_F}) + TYPE_SBS, TYPE_GLO_L1CA, TYPE_QZS_L1CA, TYPE_BDS_D1, TYPE_BDS_D2, TYPE_BDS_CNAV1, + TYPE_BDS_CNAV2, TYPE_GAL_I, TYPE_GAL_F, TYPE_IRN_L5CA}) public @interface GnssNavigationMessageType {} // The following enumerations must be in sync with the values declared in gps.h @@ -54,16 +55,26 @@ public final class GnssNavigationMessage implements Parcelable { public static final int TYPE_GPS_L5CNAV = 0x0103; /** GPS CNAV-2 message contained in the structure. */ public static final int TYPE_GPS_CNAV2 = 0x0104; + /** SBAS message contained in the structure. */ + public static final int TYPE_SBS = 0x0201; /** Glonass L1 CA message contained in the structure. */ public static final int TYPE_GLO_L1CA = 0x0301; + /** QZSS L1 C/A message contained in the structure. */ + public static final int TYPE_QZS_L1CA = 0x0401; /** Beidou D1 message contained in the structure. */ public static final int TYPE_BDS_D1 = 0x0501; /** Beidou D2 message contained in the structure. */ public static final int TYPE_BDS_D2 = 0x0502; + /** Beidou CNAV1 message contained in the structure. */ + public static final int TYPE_BDS_CNAV1 = 0x0503; + /** Beidou CNAV2 message contained in the structure. */ + public static final int TYPE_BDS_CNAV2 = 0x0504; /** Galileo I/NAV message contained in the structure. */ public static final int TYPE_GAL_I = 0x0601; /** Galileo F/NAV message contained in the structure. */ public static final int TYPE_GAL_F = 0x0602; + /** IRNSS L5 C/A message contained in the structure. */ + public static final int TYPE_IRN_L5CA = 0x0701; /** * The Navigation Message Status is 'unknown'. @@ -199,16 +210,26 @@ public final class GnssNavigationMessage implements Parcelable { return "GPS L5-CNAV"; case TYPE_GPS_CNAV2: return "GPS CNAV2"; + case TYPE_SBS: + return "SBS"; case TYPE_GLO_L1CA: return "Glonass L1 C/A"; + case TYPE_QZS_L1CA: + return "QZSS L1 C/A"; case TYPE_BDS_D1: return "Beidou D1"; case TYPE_BDS_D2: return "Beidou D2"; + case TYPE_BDS_CNAV1: + return "Beidou CNAV1"; + case TYPE_BDS_CNAV2: + return "Beidou CNAV2"; case TYPE_GAL_I: return "Galileo I"; case TYPE_GAL_F: return "Galileo F"; + case TYPE_IRN_L5CA: + return "IRNSS L5 C/A"; default: return "<Invalid:" + mType + ">"; } diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java index 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 8d8df4533ebe..572fbc373730 100644 --- a/location/java/com/android/internal/location/ProviderRequest.java +++ b/location/java/com/android/internal/location/ProviderRequest.java @@ -23,11 +23,10 @@ import android.os.Parcelable; import android.os.WorkSource; import android.util.TimeUtils; -import com.android.internal.util.Preconditions; - import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; /** @hide */ public final class ProviderRequest implements Parcelable { @@ -76,8 +75,8 @@ public final class ProviderRequest implements Parcelable { this.interval = interval; this.lowPowerMode = lowPowerMode; this.locationSettingsIgnored = locationSettingsIgnored; - this.locationRequests = Preconditions.checkNotNull(locationRequests); - this.workSource = Preconditions.checkNotNull(workSource); + this.locationRequests = Objects.requireNonNull(locationRequests); + this.workSource = Objects.requireNonNull(workSource); } public static final Parcelable.Creator<ProviderRequest> CREATOR = @@ -119,6 +118,7 @@ public final class ProviderRequest implements Parcelable { for (LocationRequest request : locationRequests) { request.writeToParcel(parcel, flags); } + parcel.writeParcelable(workSource, flags); } @Override @@ -155,40 +155,50 @@ public final class ProviderRequest implements Parcelable { return mInterval; } - public void setInterval(long interval) { + /** Sets the request interval. */ + public Builder setInterval(long interval) { this.mInterval = interval; + return this; } public boolean isLowPowerMode() { return mLowPowerMode; } - public void setLowPowerMode(boolean lowPowerMode) { + /** Sets whether low power mode is enabled. */ + public Builder setLowPowerMode(boolean lowPowerMode) { this.mLowPowerMode = lowPowerMode; + return this; } public boolean isLocationSettingsIgnored() { return mLocationSettingsIgnored; } - public void setLocationSettingsIgnored(boolean locationSettingsIgnored) { + /** Sets whether location settings should be ignored. */ + public Builder setLocationSettingsIgnored(boolean locationSettingsIgnored) { this.mLocationSettingsIgnored = locationSettingsIgnored; + return this; } public List<LocationRequest> getLocationRequests() { return mLocationRequests; } - public void setLocationRequests(List<LocationRequest> locationRequests) { - this.mLocationRequests = Preconditions.checkNotNull(locationRequests); + /** Sets the {@link LocationRequest}s associated with this request. */ + public Builder setLocationRequests(List<LocationRequest> locationRequests) { + this.mLocationRequests = Objects.requireNonNull(locationRequests); + return this; } public WorkSource getWorkSource() { return mWorkSource; } - public void setWorkSource(WorkSource workSource) { - mWorkSource = Preconditions.checkNotNull(workSource); + /** Sets the work source. */ + public Builder setWorkSource(WorkSource workSource) { + mWorkSource = Objects.requireNonNull(workSource); + return this; } /** diff --git a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java index d12d6b777856..b650efc91907 100644 --- a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java +++ b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java @@ -34,6 +34,7 @@ import java.util.List; * of this package for more information. */ public final class ProviderRequestUnbundled { + private final ProviderRequest mRequest; /** @hide */ diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index 8cd3c6e64b78..114c0f1f6bf3 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -99,6 +99,10 @@ public final class AudioAttributes implements Parcelable { public final static int CONTENT_TYPE_SONIFICATION = 4; /** + * Invalid value, only ever used for an uninitialized usage value + */ + private static final int USAGE_INVALID = -1; + /** * Usage value to use when the usage is unknown. */ public final static int USAGE_UNKNOWN = 0; @@ -184,9 +188,43 @@ public final class AudioAttributes implements Parcelable { * Usage value to use for assistant voice interaction with remote caller on Cell and VoIP calls. */ @SystemApi - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresPermission(allOf = { + android.Manifest.permission.MODIFY_PHONE_STATE, + android.Manifest.permission.MODIFY_AUDIO_ROUTING + }) public static final int USAGE_CALL_ASSISTANT = 17; + private static final int SYSTEM_USAGE_OFFSET = 1000; + + /** + * @hide + * Usage value to use when the usage is an emergency. + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public static final int USAGE_EMERGENCY = SYSTEM_USAGE_OFFSET; + /** + * @hide + * Usage value to use when the usage is a safety sound. + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public static final int USAGE_SAFETY = SYSTEM_USAGE_OFFSET + 1; + /** + * @hide + * Usage value to use when the usage is a vehicle status. + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public static final int USAGE_VEHICLE_STATUS = SYSTEM_USAGE_OFFSET + 2; + /** + * @hide + * Usage value to use when the usage is an announcement. + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public static final int USAGE_ANNOUNCEMENT = SYSTEM_USAGE_OFFSET + 3; + /** * IMPORTANT: when adding new usage types, add them to SDK_USAGES and update SUPPRESSIBLE_USAGES * if applicable, as well as audioattributes.proto. @@ -489,6 +527,20 @@ public final class AudioAttributes implements Parcelable { * @return one of the values that can be set in {@link Builder#setUsage(int)} */ public int getUsage() { + if (isSystemUsage(mUsage)) { + return USAGE_UNKNOWN; + } + return mUsage; + } + + /** + * @hide + * Return the system usage. + * @return one of the values that can be set in {@link Builder#setUsage(int)} or + * {@link Builder#setSystemUsage(int)} + */ + @SystemApi + public int getSystemUsage() { return mUsage; } @@ -591,7 +643,8 @@ public final class AudioAttributes implements Parcelable { * {@link MediaPlayer} will use a default usage of {@link AudioAttributes#USAGE_MEDIA}. */ public static class Builder { - private int mUsage = USAGE_UNKNOWN; + private int mUsage = USAGE_INVALID; + private int mSystemUsage = USAGE_INVALID; private int mContentType = CONTENT_TYPE_UNKNOWN; private int mSource = MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID; private int mFlags = 0x0; @@ -637,7 +690,22 @@ public final class AudioAttributes implements Parcelable { public AudioAttributes build() { AudioAttributes aa = new AudioAttributes(); aa.mContentType = mContentType; - aa.mUsage = mUsage; + + if (mUsage == USAGE_INVALID) { + if (mSystemUsage == USAGE_INVALID) { + aa.mUsage = USAGE_UNKNOWN; + } else { + aa.mUsage = mSystemUsage; + } + } else { + if (mSystemUsage == USAGE_INVALID) { + aa.mUsage = mUsage; + } else { + throw new IllegalArgumentException( + "Cannot set both usage and system usage on same builder"); + } + } + aa.mSource = mSource; aa.mFlags = mFlags; if (mMuteHapticChannels) { @@ -667,26 +735,26 @@ public final class AudioAttributes implements Parcelable { } /** - * Sets the attribute describing what is the intended use of the the audio signal, + * Sets the attribute describing what is the intended use of the audio signal, * such as alarm or ringtone. - * @param usage one of {@link AudioAttributes#USAGE_UNKNOWN}, - * {@link AudioAttributes#USAGE_MEDIA}, - * {@link AudioAttributes#USAGE_VOICE_COMMUNICATION}, - * {@link AudioAttributes#USAGE_VOICE_COMMUNICATION_SIGNALLING}, - * {@link AudioAttributes#USAGE_ALARM}, {@link AudioAttributes#USAGE_NOTIFICATION}, - * {@link AudioAttributes#USAGE_NOTIFICATION_RINGTONE}, - * {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_REQUEST}, - * {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_INSTANT}, - * {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_DELAYED}, - * {@link AudioAttributes#USAGE_NOTIFICATION_EVENT}, - * {@link AudioAttributes#USAGE_ASSISTANT}, - * {@link AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY}, - * {@link AudioAttributes#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE}, - * {@link AudioAttributes#USAGE_ASSISTANCE_SONIFICATION}, - * {@link AudioAttributes#USAGE_GAME}. + * @param usage one of {@link AttributeSdkUsage#USAGE_UNKNOWN}, + * {@link AttributeSdkUsage#USAGE_MEDIA}, + * {@link AttributeSdkUsage#USAGE_VOICE_COMMUNICATION}, + * {@link AttributeSdkUsage#USAGE_VOICE_COMMUNICATION_SIGNALLING}, + * {@link AttributeSdkUsage#USAGE_ALARM}, {@link AudioAttributes#USAGE_NOTIFICATION}, + * {@link AttributeSdkUsage#USAGE_NOTIFICATION_RINGTONE}, + * {@link AttributeSdkUsage#USAGE_NOTIFICATION_COMMUNICATION_REQUEST}, + * {@link AttributeSdkUsage#USAGE_NOTIFICATION_COMMUNICATION_INSTANT}, + * {@link AttributeSdkUsage#USAGE_NOTIFICATION_COMMUNICATION_DELAYED}, + * {@link AttributeSdkUsage#USAGE_NOTIFICATION_EVENT}, + * {@link AttributeSdkUsage#USAGE_ASSISTANT}, + * {@link AttributeSdkUsage#USAGE_ASSISTANCE_ACCESSIBILITY}, + * {@link AttributeSdkUsage#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE}, + * {@link AttributeSdkUsage#USAGE_ASSISTANCE_SONIFICATION}, + * {@link AttributeSdkUsage#USAGE_GAME}. * @return the same Builder instance. */ - public Builder setUsage(@AttributeUsage int usage) { + public Builder setUsage(@AttributeSdkUsage int usage) { switch (usage) { case USAGE_UNKNOWN: case USAGE_MEDIA: @@ -705,7 +773,6 @@ public final class AudioAttributes implements Parcelable { case USAGE_GAME: case USAGE_VIRTUAL_SOURCE: case USAGE_ASSISTANT: - case USAGE_CALL_ASSISTANT: mUsage = usage; break; default: @@ -715,6 +782,28 @@ public final class AudioAttributes implements Parcelable { } /** + * @hide + * Sets the attribute describing what is the intended use of the audio signal for categories + * of sounds restricted to the system, such as vehicle status or emergency. + * + * <p>Note that the AudioAttributes have a single usage value, therefore it is illegal to + * call both this method and {@link #setUsage(int)}. + * @param systemUsage the system-restricted usage. + * @return the same Builder instance. + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public @NonNull Builder setSystemUsage(@AttributeSystemUsage int systemUsage) { + if (isSystemUsage(systemUsage)) { + mSystemUsage = systemUsage; + } else { + throw new IllegalArgumentException("Invalid system usage " + systemUsage); + } + + return this; + } + + /** * Sets the attribute describing the content type of the audio signal, such as speech, * or music. * @param contentType the content type values, one of @@ -1175,6 +1264,14 @@ public final class AudioAttributes implements Parcelable { return new String("USAGE_ASSISTANT"); case USAGE_CALL_ASSISTANT: return new String("USAGE_CALL_ASSISTANT"); + case USAGE_EMERGENCY: + return new String("USAGE_EMERGENCY"); + case USAGE_SAFETY: + return new String("USAGE_SAFETY"); + case USAGE_VEHICLE_STATUS: + return new String("USAGE_VEHICLE_STATUS"); + case USAGE_ANNOUNCEMENT: + return new String("USAGE_ANNOUNCEMENT"); default: return new String("unknown usage " + usage); } @@ -1221,6 +1318,25 @@ public final class AudioAttributes implements Parcelable { } /** + * @param usage one of {@link AttributeSystemUsage}, + * {@link AttributeSystemUsage#USAGE_CALL_ASSISTANT}, + * {@link AttributeSystemUsage#USAGE_EMERGENCY}, + * {@link AttributeSystemUsage#USAGE_SAFETY}, + * {@link AttributeSystemUsage#USAGE_VEHICLE_STATUS}, + * {@link AttributeSystemUsage#USAGE_ANNOUNCEMENT} + * @return boolean indicating if the usage is a system usage or not + * @hide + */ + @SystemApi + public static boolean isSystemUsage(@AttributeSystemUsage int usage) { + return (usage == USAGE_CALL_ASSISTANT + || usage == USAGE_EMERGENCY + || usage == USAGE_SAFETY + || usage == USAGE_VEHICLE_STATUS + || usage == USAGE_ANNOUNCEMENT); + } + + /** * Returns the stream type matching this {@code AudioAttributes} instance for volume control. * Use this method to derive the stream type needed to configure the volume * control slider in an {@link android.app.Activity} with @@ -1295,6 +1411,10 @@ public final class AudioAttributes implements Parcelable { return AudioSystem.STREAM_NOTIFICATION; case USAGE_ASSISTANCE_ACCESSIBILITY: return AudioSystem.STREAM_ACCESSIBILITY; + case USAGE_EMERGENCY: + case USAGE_SAFETY: + case USAGE_VEHICLE_STATUS: + case USAGE_ANNOUNCEMENT: case USAGE_UNKNOWN: return AudioSystem.STREAM_MUSIC; default: @@ -1327,6 +1447,39 @@ public final class AudioAttributes implements Parcelable { /** @hide */ @IntDef({ + USAGE_CALL_ASSISTANT, + USAGE_EMERGENCY, + USAGE_SAFETY, + USAGE_VEHICLE_STATUS, + USAGE_ANNOUNCEMENT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AttributeSystemUsage {} + + /** @hide */ + @IntDef({ + USAGE_UNKNOWN, + USAGE_MEDIA, + USAGE_VOICE_COMMUNICATION, + USAGE_VOICE_COMMUNICATION_SIGNALLING, + USAGE_ALARM, + USAGE_NOTIFICATION, + USAGE_NOTIFICATION_RINGTONE, + USAGE_NOTIFICATION_COMMUNICATION_REQUEST, + USAGE_NOTIFICATION_COMMUNICATION_INSTANT, + USAGE_NOTIFICATION_COMMUNICATION_DELAYED, + USAGE_NOTIFICATION_EVENT, + USAGE_ASSISTANCE_ACCESSIBILITY, + USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, + USAGE_ASSISTANCE_SONIFICATION, + USAGE_GAME, + USAGE_ASSISTANT, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AttributeSdkUsage {} + + /** @hide */ + @IntDef({ USAGE_UNKNOWN, USAGE_MEDIA, USAGE_VOICE_COMMUNICATION, @@ -1344,6 +1497,10 @@ public final class AudioAttributes implements Parcelable { USAGE_GAME, USAGE_ASSISTANT, USAGE_CALL_ASSISTANT, + USAGE_EMERGENCY, + USAGE_SAFETY, + USAGE_VEHICLE_STATUS, + USAGE_ANNOUNCEMENT, }) @Retention(RetentionPolicy.SOURCE) public @interface AttributeUsage {} 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 1b870e8e94ef..861b76d2b153 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -37,6 +37,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.media.AudioAttributes.AttributeSystemUsage; import android.media.audiopolicy.AudioPolicy; import android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener; import android.media.audiopolicy.AudioProductStrategy; @@ -1268,6 +1269,39 @@ public class AudioManager { } /** + * Set the system usages to be supported on this device. + * @param systemUsages array of system usages to support {@link AttributeSystemUsage} + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public void setSupportedSystemUsages(@NonNull @AttributeSystemUsage int[] systemUsages) { + Objects.requireNonNull(systemUsages, "systemUsages must not be null"); + final IAudioService service = getService(); + try { + service.setSupportedSystemUsages(systemUsages); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Get the system usages supported on this device. + * @return array of supported system usages {@link AttributeSystemUsage} + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public @NonNull @AttributeSystemUsage int[] getSupportedSystemUsages() { + final IAudioService service = getService(); + try { + return service.getSupportedSystemUsages(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Solo or unsolo a particular stream. * <p> * Do not use. This method has been deprecated and is now a no-op. @@ -4533,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/AudioMetadata.java b/media/java/android/media/AudioMetadata.java new file mode 100644 index 000000000000..7245aab41eec --- /dev/null +++ b/media/java/android/media/AudioMetadata.java @@ -0,0 +1,406 @@ +/* + * 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; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.Pair; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +/** + * AudioMetadata class is used to manage typed key-value pairs for + * configuration and capability requests within the Audio Framework. + */ +public final class AudioMetadata { + /** + * Key interface for the map. + * + * The presence of this {@code Key} interface on an object allows + * it to be used to reference metadata in the Audio Framework. + * + * @param <T> type of value associated with {@code Key}. + */ + // Conceivably metadata keys exposing multiple interfaces + // could be eligible to work in multiple framework domains. + public interface Key<T> { + /** + * Returns the internal name of the key. + */ + @NonNull + String getName(); + + /** + * Returns the class type of the associated value. + */ + @NonNull + Class<T> getValueClass(); + + // TODO: consider adding bool isValid(@NonNull T value) + + /** + * Do not allow non-framework apps to create their own keys + * by implementing this interface; keep a method hidden. + * + * @hide + */ + boolean isFromFramework(); + } + + /** + * A read only {@code Map} interface of {@link Key} value pairs. + * + * Using a {@link Key} interface, look up the corresponding value. + */ + public interface ReadMap { + /** + * Returns true if the key exists in the map. + * + * @param key interface for requesting the value. + * @param <T> type of value. + * @return true if key exists in the Map. + */ + <T> boolean containsKey(@NonNull Key<T> key); + + /** + * Returns a copy of the map. + * + * This is intended for safe conversion between a {@link ReadMap} + * interface and a {@link Map} interface. + * Currently only simple objects are used for key values which + * means a shallow copy is sufficient. + * + * @return a Map copied from the existing map. + */ + @NonNull + Map dup(); // lint checker doesn't like clone(). + + /** + * Returns the value associated with the key. + * + * @param key interface for requesting the value. + * @param <T> type of value. + * @return returns the value of associated with key or null if it doesn't exist. + */ + @Nullable + <T> T get(@NonNull Key<T> key); + + /** + * Returns a {@code Set} of keys associated with the map. + * @hide + */ + @NonNull + Set<Key<?>> keySet(); + + /** + * Returns the number of elements in the map. + */ + int size(); + } + + /** + * A writeable {@link Map} interface of {@link Key} value pairs. + * This interface is not guaranteed to be thread-safe + * unless the supplier for the {@code Map} states it as thread safe. + */ + // TODO: Create a wrapper like java.util.Collections.synchronizedMap? + public interface Map extends ReadMap { + /** + * Removes the value associated with the key. + * @param key interface for storing the value. + * @param <T> type of value. + * @return the value of the key, null if it doesn't exist. + */ + @Nullable + <T> T remove(@NonNull Key<T> key); + + /** + * Sets a value for the key. + * + * @param key interface for storing the value. + * @param <T> type of value. + * @param value a non-null value of type T. + * @return the previous value associated with key or null if it doesn't exist. + */ + // See automatic Kotlin overloading for Java interoperability. + // https://kotlinlang.org/docs/reference/java-interop.html#operators + // See also Kotlin set for overloaded operator indexing. + // https://kotlinlang.org/docs/reference/operator-overloading.html#indexed + // Also the Kotlin mutable-list set. + // https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-list/set.html + @Nullable + <T> T set(@NonNull Key<T> key, @NonNull T value); + } + + /** + * Creates a {@link Map} suitable for adding keys. + * @return an empty {@link Map} instance. + */ + @NonNull + public static Map createMap() { + return new BaseMap(); + } + + /** + * A container class for AudioMetadata Format keys. + * + * @see AudioTrack.OnCodecFormatChangedListener + */ + public static class Format { + // The key name strings used here must match that of the native framework, but are + // allowed to change between API releases. This due to the Java specification + // on what is a compile time constant. + // + // Key<?> are final variables but not constant variables (per Java spec 4.12.4) because + // the keys are not a primitive type nor a String initialized by a constant expression. + // Hence (per Java spec 13.1.3), they are not resolved at compile time, + // rather are picked up by applications at run time. + // + // So the contractual API behavior of AudioMetadata.Key<> are different than Strings + // initialized by a constant expression (for example MediaFormat.KEY_*). + + // See MediaFormat + /** + * A key representing the bitrate of the encoded stream used in + * + * If the stream is variable bitrate, this is the average bitrate of the stream. + * The unit is bits per second. + * + * An Integer value. + * + * @see MediaFormat#KEY_BIT_RATE + */ + @NonNull public static final Key<Integer> KEY_BIT_RATE = + createKey("bitrate", Integer.class); + + /** + * A key representing the audio channel mask of the stream. + * + * An Integer value. + * + * @see AudioTrack#getChannelConfiguration() + * @see MediaFormat#KEY_CHANNEL_MASK + */ + @NonNull public static final Key<Integer> KEY_CHANNEL_MASK = + createKey("channel-mask", Integer.class); + + + /** + * A key representing the codec mime string. + * + * A String value. + * + * @see MediaFormat#KEY_MIME + */ + @NonNull public static final Key<String> KEY_MIME = createKey("mime", String.class); + + /** + * A key representing the audio sample rate in Hz of the stream. + * + * An Integer value. + * + * @see AudioFormat#getSampleRate() + * @see MediaFormat#KEY_SAMPLE_RATE + */ + @NonNull public static final Key<Integer> KEY_SAMPLE_RATE = + createKey("sample-rate", Integer.class); + + // Unique to Audio + + /** + * A key representing the bit width of an element of decoded data. + * + * An Integer value. + */ + @NonNull public static final Key<Integer> KEY_BIT_WIDTH = + createKey("bit-width", Integer.class); + + /** + * A key representing the presence of Atmos in an E-AC3 stream. + * + * A Boolean value which is true if Atmos is present in an E-AC3 stream. + */ + @NonNull public static final Key<Boolean> KEY_ATMOS_PRESENT = + createKey("atmos-present", Boolean.class); + + /** + * A key representing the audio encoding used for the stream. + * This is the same encoding used in {@link AudioFormat#getEncoding()}. + * + * An Integer value. + * + * @see AudioFormat#getEncoding() + */ + @NonNull public static final Key<Integer> KEY_AUDIO_ENCODING = + createKey("audio-encoding", Integer.class); + + private Format() {} // delete constructor + } + + ///////////////////////////////////////////////////////////////////////// + // Hidden methods and functions. + + /** + * Returns a Key object with the correct interface for the AudioMetadata. + * + * An interface with the same name and type will be treated as + * identical for the purposes of value storage, even though + * other methods or hidden parameters may return different values. + * + * @param name The name of the key. + * @param type The class type of the value represented by the key. + * @param <T> The type of value. + * @return a new key interface. + * + * Creating keys is currently only allowed by the Framework. + * @hide + */ + @NonNull + public static <T> Key<T> createKey(String name, Class<T> type) { + // Implementation specific. + return new Key<T>() { + private final String mName = name; + private final Class<T> mType = type; + + @Override + @NonNull + public String getName() { + return mName; + } + + @Override + @NonNull + public Class<T> getValueClass() { + return mType; + } + + // hidden interface method to prevent user class implements the of Key interface. + @Override + public boolean isFromFramework() { + return true; + } + }; + } + + /** + * @hide + * + * AudioMetadata is based on interfaces in order to allow multiple inheritance + * and maximum flexibility in implementation. + * + * Here, we provide a simple implementation of {@link Map} interface; + * Note that the Keys are not specific to this Map implementation. + * + * It is possible to require the keys to be of a certain class + * before allowing a set or get operation. + */ + public static class BaseMap implements Map { + @Override + public <T> boolean containsKey(@NonNull Key<T> key) { + Pair<Key<?>, Object> valuePair = mHashMap.get(pairFromKey(key)); + return valuePair != null; + } + + @Override + @NonNull + public Map dup() { + BaseMap map = new BaseMap(); + map.mHashMap.putAll(this.mHashMap); + return map; + } + + @Override + @Nullable + public <T> T get(@NonNull Key<T> key) { + Pair<Key<?>, Object> valuePair = mHashMap.get(pairFromKey(key)); + return (T) getValueFromValuePair(valuePair); + } + + @Override + @NonNull + public Set<Key<?>> keySet() { + HashSet<Key<?>> set = new HashSet(); + for (Pair<Key<?>, Object> pair : mHashMap.values()) { + set.add(pair.first); + } + return set; + } + + @Override + @Nullable + public <T> T remove(@NonNull Key<T> key) { + Pair<Key<?>, Object> valuePair = mHashMap.remove(pairFromKey(key)); + return (T) getValueFromValuePair(valuePair); + } + + @Override + @Nullable + public <T> T set(@NonNull Key<T> key, @NonNull T value) { + Objects.requireNonNull(value); + Pair<Key<?>, Object> valuePair = mHashMap + .put(pairFromKey(key), new Pair<Key<?>, Object>(key, value)); + return (T) getValueFromValuePair(valuePair); + } + + @Override + public int size() { + return mHashMap.size(); + } + + /* + * Implementation specific. + * + * To store the value in the HashMap we need to convert the Key interface + * to a hashcode() / equals() compliant Pair. + */ + @NonNull + private static <T> Pair<String, Class<?>> pairFromKey(@NonNull Key<T> key) { + Objects.requireNonNull(key); + return new Pair<String, Class<?>>(key.getName(), key.getValueClass()); + } + + /* + * Implementation specific. + * + * We store in a Pair (valuePair) the key along with the Object value. + * This helper returns the Object value from the value pair. + */ + @Nullable + private static Object getValueFromValuePair(@Nullable Pair<Key<?>, Object> valuePair) { + if (valuePair == null) { + return null; + } + return valuePair.second; + } + + /* + * Implementation specific. + * + * We use a HashMap to back the AudioMetadata BaseMap object. + * This is not locked, so concurrent reads are permitted if all threads + * have a ReadMap; this is risky with a Map. + */ + private final HashMap<Pair<String, Class<?>>, Pair<Key<?>, Object>> mHashMap = + new HashMap(); + } + + // Delete the constructor as there is nothing to implement here. + private AudioMetadata() {} +} 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 fe57e71cca8c..02cb8aafea0c 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -1169,11 +1169,23 @@ 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); /** + * Communicate supported system usages to audio policy service. + */ + public static native int setSupportedSystemUsages(int[] systemUsages); + + /** * @see AudioManager#setAllowedCapturePolicy() */ public static native int setAllowedCapturePolicy(int uid, int flags); diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 4dbc79b54199..81275f6891ab 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -188,6 +188,10 @@ public class AudioTrack extends PlayerBase // Events: // to keep in sync with frameworks/av/include/media/AudioTrack.h + // Note: To avoid collisions with other event constants, + // do not define an event here that is the same value as + // AudioSystem.NATIVE_EVENT_ROUTING_CHANGE. + /** * Event id denotes when playback head has reached a previously set marker. */ @@ -210,6 +214,14 @@ public class AudioTrack extends PlayerBase * back (after stop is called) for an offloaded track. */ private static final int NATIVE_EVENT_STREAM_END = 7; + /** + * Event id denotes when the codec format changes. + * + * Note: Similar to a device routing change (AudioSystem.NATIVE_EVENT_ROUTING_CHANGE), + * this event comes from the AudioFlinger Thread / Output Stream management + * (not from buffer indications as above). + */ + private static final int NATIVE_EVENT_CODEC_FORMAT_CHANGE = 100; private final static String TAG = "android.media.AudioTrack"; @@ -244,6 +256,103 @@ 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 + * ITU T-REC-J.94-201610 A.6.2.3 Component descriptor. + */ + /** @hide */ + @IntDef({ + DUAL_MONO_MODE_OFF, + DUAL_MONO_MODE_LR, + DUAL_MONO_MODE_LL, + DUAL_MONO_MODE_RR, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DualMonoMode {} + // Important: The DUAL_MONO_MODE values must be kept in sync with native header files. + /** + * This mode disables any Dual Mono presentation effect. + * + */ + public static final int DUAL_MONO_MODE_OFF = 0; + + /** + * This mode indicates that a stereo stream should be presented + * with the left and right audio channels blended together + * and delivered to both channels. + * + * Behavior for non-stereo streams is implementation defined. + * A suggested guideline is that the left-right stereo symmetric + * channels are pairwise blended; + * the other channels such as center are left alone. + * + * The Dual Mono effect occurs before volume scaling. + */ + public static final int DUAL_MONO_MODE_LR = 1; + + /** + * This mode indicates that a stereo stream should be presented + * with the left audio channel replicated into the right audio channel. + * + * Behavior for non-stereo streams is implementation defined. + * A suggested guideline is that all channels with left-right + * stereo symmetry will have the left channel position replicated + * into the right channel position. + * The center channels (with no left/right symmetry) or unbalanced + * channels are left alone. + * + * The Dual Mono effect occurs before volume scaling. + */ + public static final int DUAL_MONO_MODE_LL = 2; + + /** + * This mode indicates that a stereo stream should be presented + * with the right audio channel replicated into the left audio channel. + * + * Behavior for non-stereo streams is implementation defined. + * A suggested guideline is that all channels with left-right + * stereo symmetry will have the right channel position replicated + * into the left channel position. + * The center channels (with no left/right symmetry) or unbalanced + * channels are left alone. + * + * The Dual Mono effect occurs before volume scaling. + */ + public static final int DUAL_MONO_MODE_RR = 3; + /** @hide */ @IntDef({ WRITE_BLOCKING, @@ -1343,6 +1452,140 @@ public class AudioTrack extends PlayerBase attributes.getContentType(), attributes.getUsage(), attributes.getFlags()); } + /* + * The MAX_LEVEL should be exactly representable by an IEEE 754-2008 base32 float. + * This means fractions must be divisible by a power of 2. For example, + * 10.25f is OK as 0.25 is 1/4, but 10.1f is NOT OK as 1/10 is not expressable by + * a finite binary fraction. + * + * 48.f is the nominal max for API level {@link android os.Build.VERSION_CODES#R}. + * We use this to suggest a baseline range for implementation. + * + * The API contract specification allows increasing this value in a future + * API release, but not decreasing this value. + */ + private static final float MAX_AUDIO_DESCRIPTION_MIX_LEVEL = 48.f; + + private static boolean isValidAudioDescriptionMixLevel(float level) { + return !(Float.isNaN(level) || level > MAX_AUDIO_DESCRIPTION_MIX_LEVEL); + } + + /** + * Sets the Audio Description mix level in dB. + * + * For AudioTracks incorporating a secondary Audio Description stream + * (where such contents may be sent through an Encapsulation Mode + * {@link #ENCAPSULATION_MODE_ELEMENTARY_STREAM} or {@link #ENCAPSULATION_MODE_HANDLE} + * or internally by a HW channel), + * the level of mixing of the Audio Description to the Main Audio stream + * is controlled by this method. + * + * Such mixing occurs <strong>prior</strong> to overall volume scaling. + * + * @param level a floating point value between + * {@code Float.NEGATIVE_INFINITY} to {@code +48.f}, + * where {@code Float.NEGATIVE_INFINITY} means the Audio Description is not mixed + * and a level of {@code 0.f} means the Audio Description is mixed without scaling. + * @return true on success, false on failure. + */ + public boolean setAudioDescriptionMixLeveldB( + @FloatRange(to = 48.f, toInclusive = true) float level) { + if (!isValidAudioDescriptionMixLevel(level)) { + throw new IllegalArgumentException("level is out of range" + level); + } + return native_set_audio_description_mix_level_db(level) == SUCCESS; + } + + /** + * Returns the Audio Description mix level in dB. + * + * If Audio Description mixing is unavailable from the hardware device, + * a value of {@code Float.NEGATIVE_INFINITY} is returned. + * + * @return the current Audio Description Mix Level in dB. + * A value of {@code Float.NEGATIVE_INFINITY} means + * that the audio description is not mixed or + * the hardware is not available. + * This should reflect the <strong>true</strong> internal device mix level; + * hence the application might receive any floating value + * except {@code Float.NaN}. + */ + public float getAudioDescriptionMixLeveldB() { + float[] level = { Float.NEGATIVE_INFINITY }; + try { + final int status = native_get_audio_description_mix_level_db(level); + if (status != SUCCESS || Float.isNaN(level[0])) { + return Float.NEGATIVE_INFINITY; + } + } catch (Exception e) { + return Float.NEGATIVE_INFINITY; + } + return level[0]; + } + + private static boolean isValidDualMonoMode(@DualMonoMode int dualMonoMode) { + switch (dualMonoMode) { + case DUAL_MONO_MODE_OFF: + case DUAL_MONO_MODE_LR: + case DUAL_MONO_MODE_LL: + case DUAL_MONO_MODE_RR: + return true; + default: + return false; + } + } + + /** + * Sets the Dual Mono mode presentation on the output device. + * + * The Dual Mono mode is generally applied to stereo audio streams + * where the left and right channels come from separate sources. + * + * For compressed audio, where the decoding is done in hardware, + * Dual Mono presentation needs to be performed + * by the hardware output device + * as the PCM audio is not available to the framework. + * + * @param dualMonoMode one of {@link #DUAL_MONO_MODE_OFF}, + * {@link #DUAL_MONO_MODE_LR}, + * {@link #DUAL_MONO_MODE_LL}, + * {@link #DUAL_MONO_MODE_RR}. + * + * @return true on success, false on failure if the output device + * does not support Dual Mono mode. + */ + public boolean setDualMonoMode(@DualMonoMode int dualMonoMode) { + if (!isValidDualMonoMode(dualMonoMode)) { + throw new IllegalArgumentException( + "Invalid Dual Mono mode " + dualMonoMode); + } + return native_set_dual_mono_mode(dualMonoMode) == SUCCESS; + } + + /** + * Returns the Dual Mono mode presentation setting. + * + * If no Dual Mono presentation is available for the output device, + * then {@link #DUAL_MONO_MODE_OFF} is returned. + * + * @return one of {@link #DUAL_MONO_MODE_OFF}, + * {@link #DUAL_MONO_MODE_LR}, + * {@link #DUAL_MONO_MODE_LL}, + * {@link #DUAL_MONO_MODE_RR}. + */ + public @DualMonoMode int getDualMonoMode() { + int[] dualMonoMode = { DUAL_MONO_MODE_OFF }; + try { + final int status = native_get_dual_mono_mode(dualMonoMode); + if (status != SUCCESS || !isValidDualMonoMode(dualMonoMode[0])) { + return DUAL_MONO_MODE_OFF; + } + } catch (Exception e) { + return DUAL_MONO_MODE_OFF; + } + return dualMonoMode[0]; + } + // mask of all the positional channels supported, however the allowed combinations // are further restricted by the matching left/right rule and // AudioSystem.OUT_CHANNEL_COUNT_MAX @@ -3409,6 +3652,67 @@ public class AudioTrack extends PlayerBase } } + //-------------------------------------------------------------------------- + // Codec notifications + //-------------------- + + // OnCodecFormatChangedListener notifications uses an instance + // of ListenerList to manage its listeners. + + private final Utils.ListenerList<AudioMetadata.ReadMap> mCodecFormatChangedListeners = + new Utils.ListenerList(); + + /** + * Interface definition for a listener for codec format changes. + */ + public interface OnCodecFormatChangedListener { + /** + * Called when the compressed codec format changes. + * + * @param audioTrack is the {@code AudioTrack} instance associated with the codec. + * @param info is a {@link AudioMetadata.ReadMap} of values which contains decoded format + * changes reported by the codec. Not all hardware + * codecs indicate codec format changes. Acceptable keys are taken from + * {@code AudioMetadata.Format.KEY_*} range, with the associated value type. + */ + void onCodecFormatChanged( + @NonNull AudioTrack audioTrack, @Nullable AudioMetadata.ReadMap info); + } + + /** + * Adds an {@link OnCodecFormatChangedListener} to receive notifications of + * codec format change events on this {@code AudioTrack}. + * + * @param executor Specifies the {@link Executor} object to control execution. + * + * @param listener The {@link OnCodecFormatChangedListener} interface to receive + * notifications of codec events. + */ + public void addOnCodecFormatChangedListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull OnCodecFormatChangedListener listener) { // NPE checks done by ListenerList. + mCodecFormatChangedListeners.add( + listener, /* key for removal */ + executor, + (int eventCode, AudioMetadata.ReadMap readMap) -> { + // eventCode is unused by this implementation. + listener.onCodecFormatChanged(this, readMap); + } + ); + } + + /** + * Removes an {@link OnCodecFormatChangedListener} which has been previously added + * to receive codec format change events. + * + * @param listener The previously added {@link OnCodecFormatChangedListener} interface + * to remove. + */ + public void removeOnCodecFormatChangedListener( + @NonNull OnCodecFormatChangedListener listener) { + mCodecFormatChangedListeners.remove(listener); // NPE checks done by ListenerList. + } + //--------------------------------------------------------- // Interface definitions //-------------------- @@ -3745,6 +4049,12 @@ public class AudioTrack extends PlayerBase return; } + if (what == NATIVE_EVENT_CODEC_FORMAT_CHANGE) { + track.mCodecFormatChangedListeners.notify( + 0 /* eventCode, unused */, (AudioMetadata.ReadMap) obj); + return; + } + if (what == NATIVE_EVENT_CAN_WRITE_MORE_DATA || what == NATIVE_EVENT_NEW_IAUDIOTRACK || what == NATIVE_EVENT_STREAM_END) { @@ -3868,6 +4178,11 @@ public class AudioTrack extends PlayerBase private native void native_set_delay_padding(int delayInFrames, int paddingInFrames); + private native int native_set_audio_description_mix_level_db(float level); + private native int native_get_audio_description_mix_level_db(float[] level); + private native int native_set_dual_mono_mode(int dualMonoMode); + private native int native_get_dual_mono_mode(int[] dualMonoMode); + //--------------------------------------------------------- // Utility methods //------------------ diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 8e8385d382fb..1f97be5c3f4d 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -105,6 +105,10 @@ interface IAudioService { int getLastAudibleStreamVolume(int streamType); + void setSupportedSystemUsages(in int[] systemUsages); + + int[] getSupportedSystemUsages(); + List<AudioProductStrategy> getAudioProductStrategies(); boolean isMicrophoneMuted(); @@ -262,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/MediaCodec.java b/media/java/android/media/MediaCodec.java index f780d40d349b..abc7e0b7be0e 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -23,6 +23,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.graphics.ImageFormat; import android.graphics.Rect; import android.graphics.SurfaceTexture; +import android.hardware.HardwareBuffer; import android.media.MediaCodecInfo.CodecCapabilities; import android.os.Build; import android.os.Bundle; @@ -39,9 +40,13 @@ import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.ReadOnlyBufferException; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.Set; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -1736,7 +1741,25 @@ final public class MediaCodec { { int index = msg.arg2; synchronized(mBufferLock) { - validateInputByteBuffer(mCachedInputBuffers, index); + switch (mBufferMode) { + case BUFFER_MODE_LEGACY: + validateInputByteBuffer(mCachedInputBuffers, index); + break; + case BUFFER_MODE_BLOCK: + while (mQueueRequests.size() <= index) { + mQueueRequests.add(null); + } + QueueRequest request = mQueueRequests.get(index); + if (request == null) { + request = new QueueRequest(mCodec, index); + mQueueRequests.set(index, request); + } + request.setAccessible(true); + break; + default: + throw new IllegalStateException( + "Unrecognized buffer mode: " + mBufferMode); + } } mCallback.onInputBufferAvailable(mCodec, index); break; @@ -1747,7 +1770,26 @@ final public class MediaCodec { int index = msg.arg2; BufferInfo info = (MediaCodec.BufferInfo) msg.obj; synchronized(mBufferLock) { - validateOutputByteBuffer(mCachedOutputBuffers, index, info); + switch (mBufferMode) { + case BUFFER_MODE_LEGACY: + validateOutputByteBuffer(mCachedOutputBuffers, index, info); + break; + case BUFFER_MODE_BLOCK: + while (mOutputFrames.size() <= index) { + mOutputFrames.add(null); + } + OutputFrame frame = mOutputFrames.get(index); + if (frame == null) { + frame = new OutputFrame(index); + mOutputFrames.set(index, frame); + } + frame.setBufferInfo(info); + frame.setAccessible(true); + break; + default: + throw new IllegalStateException( + "Unrecognized buffer mode: " + mBufferMode); + } } mCallback.onOutputBufferAvailable( mCodec, index, info); @@ -1913,8 +1955,33 @@ final public class MediaCodec { */ public static final int CONFIGURE_FLAG_ENCODE = 1; + /** + * If this codec is to be used with {@link LinearBlock} and/or {@link + * GraphicBlock}, pass this flag. + * <p> + * When this flag is set, the following APIs throw IllegalStateException. + * <ul> + * <li>{@link #getInputBuffer} + * <li>{@link #getInputImage} + * <li>{@link #getInputBuffers} + * <li>{@link #getOutputBuffer} + * <li>{@link #getOutputImage} + * <li>{@link #getOutputBuffers} + * <li>{@link #queueInputBuffer} + * <li>{@link #queueSecureInputBuffer} + * <li>{@link #dequeueInputBuffer} + * <li>{@link #dequeueOutputBuffer} + * </ul> + */ + public static final int CONFIGURE_FLAG_USE_BLOCK_MODEL = 2; + /** @hide */ - @IntDef(flag = true, value = { CONFIGURE_FLAG_ENCODE }) + @IntDef( + flag = true, + value = { + CONFIGURE_FLAG_ENCODE, + CONFIGURE_FLAG_USE_BLOCK_MODEL, + }) @Retention(RetentionPolicy.SOURCE) public @interface ConfigureFlag {} @@ -1984,6 +2051,11 @@ final public class MediaCodec { descrambler != null ? descrambler.getBinder() : null, flags); } + private static final int BUFFER_MODE_INVALID = -1; + private static final int BUFFER_MODE_LEGACY = 0; + private static final int BUFFER_MODE_BLOCK = 1; + private int mBufferMode = BUFFER_MODE_INVALID; + private void configure( @Nullable MediaFormat format, @Nullable Surface surface, @Nullable MediaCrypto crypto, @Nullable IHwBinder descramblerBinder, @@ -2022,6 +2094,13 @@ final public class MediaCodec { mHasSurface = surface != null; mCrypto = crypto; + synchronized (mBufferLock) { + if ((flags & CONFIGURE_FLAG_USE_BLOCK_MODEL) != 0) { + mBufferMode = BUFFER_MODE_BLOCK; + } else { + mBufferMode = BUFFER_MODE_LEGACY; + } + } native_configure(keys, values, surface, crypto, descramblerBinder, flags); } @@ -2446,6 +2525,9 @@ final public class MediaCodec { int offset, int size, long presentationTimeUs, int flags) throws CryptoException { synchronized(mBufferLock) { + if (mBufferMode != BUFFER_MODE_LEGACY) { + throw new IllegalStateException(); + } invalidateByteBuffer(mCachedInputBuffers, index); mDequeuedInputBuffers.remove(index); } @@ -2695,6 +2777,9 @@ final public class MediaCodec { long presentationTimeUs, int flags) throws CryptoException { synchronized(mBufferLock) { + if (mBufferMode != BUFFER_MODE_LEGACY) { + throw new IllegalStateException(); + } invalidateByteBuffer(mCachedInputBuffers, index); mDequeuedInputBuffers.remove(index); } @@ -2726,6 +2811,11 @@ final public class MediaCodec { * @throws MediaCodec.CodecException upon codec error. */ public final int dequeueInputBuffer(long timeoutUs) { + synchronized (mBufferLock) { + if (mBufferMode != BUFFER_MODE_LEGACY) { + throw new IllegalStateException(); + } + } int res = native_dequeueInputBuffer(timeoutUs); if (res >= 0) { synchronized(mBufferLock) { @@ -2738,6 +2828,654 @@ final public class MediaCodec { private native final int native_dequeueInputBuffer(long timeoutUs); /** + * Section of memory that represents a linear block. Applications may + * acquire a block via {@link LinearBlock#obtain} and queue all or part + * of the block as an input buffer to a codec, or get a block allocated by + * codec as an output buffer from {@link OutputFrame}. + * + * {@see QueueRequest#setLinearBlock} + * {@see QueueRequest#setEncryptedLinearBlock} + * {@see OutputFrame#getLinearBlock} + */ + public static final class LinearBlock { + // No public constructors. + private LinearBlock() {} + + /** + * Returns true if the buffer is mappable. + * @throws IllegalStateException if invalid + */ + public boolean isMappable() { + synchronized (mLock) { + if (!mValid) { + throw new IllegalStateException(); + } + return mMappable; + } + } + + /** + * Map the memory and return the mapped region. + * <p> + * The returned memory region becomes inaccessible after + * {@link #recycle}, or the buffer is queued to the codecs and not + * returned to the client yet. + * + * @return mapped memory region as {@link ByteBuffer} object + * @throws IllegalStateException if not mappable or invalid + */ + public @NonNull ByteBuffer map() { + synchronized (mLock) { + if (!mValid) { + throw new IllegalStateException(); + } + if (!mMappable) { + throw new IllegalStateException(); + } + if (mMapped == null) { + mMapped = native_map(); + } + return mMapped; + } + } + + private native ByteBuffer native_map(); + + /** + * Mark this block as ready to be recycled by the framework once it is + * no longer in use. All operations to this object after + * this call will cause exceptions, as well as attempt to access the + * previously mapped memory region. Caller should clear all references + * to this object after this call. + * <p> + * To avoid excessive memory consumption, it is recommended that callers + * recycle buffers as soon as they no longer need the buffers + * + * @throws IllegalStateException if invalid + */ + public void recycle() { + synchronized (mLock) { + if (!mValid) { + throw new IllegalStateException(); + } + if (mMapped != null) { + mMapped.setAccessible(false); + mMapped = null; + } + native_recycle(); + mValid = false; + mNativeContext = 0; + } + sPool.offer(this); + } + + private native void native_recycle(); + + private native void native_obtain(int capacity, String[] codecNames); + + @Override + protected void finalize() { + native_recycle(); + } + + /** + * Returns true if it is possible to allocate a linear block that can be + * passed to all listed codecs as input buffers without copying the + * content. + * <p> + * Note that even if this function returns true, {@link #obtain} may + * still throw due to invalid arguments or allocation failure. + * + * @param codecNames list of codecs that the client wants to use a + * linear block without copying. Null entries are + * ignored. + */ + public static boolean isCodecCopyFreeCompatible(@NonNull String[] codecNames) { + return native_checkCompatible(codecNames); + } + + private static native boolean native_checkCompatible(@NonNull String[] codecNames); + + /** + * Obtain a linear block object no smaller than {@code capacity}. + * If {@link #isCodecCopyFreeCompatible} with the same + * {@code codecNames} returned true, the returned + * {@link LinearBlock} object can be queued to the listed codecs without + * copying. The returned {@link LinearBlock} object is always + * read/write mappable. + * + * @param capacity requested capacity of the linear block in bytes + * @param codecNames list of codecs that the client wants to use this + * linear block without copying. Null entries are + * ignored. + * @return a linear block object. + * @throws IllegalArgumentException if the capacity is invalid or + * codecNames contains invalid name + * @throws IOException if an error occurred while allocating a buffer + */ + public static @Nullable LinearBlock obtain( + int capacity, @NonNull String[] codecNames) { + LinearBlock buffer = sPool.poll(); + if (buffer == null) { + buffer = new LinearBlock(); + } + synchronized (buffer.mLock) { + buffer.native_obtain(capacity, codecNames); + } + return buffer; + } + + // Called from native + private void setInternalStateLocked(long context, boolean isMappable) { + mNativeContext = context; + mMappable = isMappable; + mValid = (context != 0); + } + + private static final BlockingQueue<LinearBlock> sPool = + new LinkedBlockingQueue<>(); + + private final Object mLock = new Object(); + private boolean mValid = false; + private boolean mMappable = false; + private ByteBuffer mMapped = null; + private long mNativeContext = 0; + } + + /** + * Section of memory that represents a graphic block. Applications may + * acquire a block via {@link GraphicBlock#obtain} and queue + * the block as an input buffer to a codec, or get a block allocated by + * codec as an output buffer from {@link OutputFrame}. + * + * {@see QueueRequest#setGraphicBlock} + * {@see OutputFrame#getGraphicBlock} + */ + public static final class GraphicBlock { + // No public constructors. + private GraphicBlock() {} + + /** + * Returns true if the buffer is mappable. + * @throws IllegalStateException if invalid + */ + public boolean isMappable() { + synchronized (mLock) { + if (!mValid) { + throw new IllegalStateException(); + } + return mMappable; + } + } + + /** + * Map the memory and return the mapped region. + * <p> + * Calling {@link #recycle} or + * {@link QueueRequest#setGraphicBlock} causes the returned + * {@link Image} object to be closed, if not already. + * + * @return mapped memory region as {@link Image} object + * @throws IllegalStateException if not mappable or invalid + */ + public @NonNull Image map() { + synchronized (mLock) { + if (!mValid) { + throw new IllegalStateException(); + } + if (!mMappable) { + throw new IllegalStateException(); + } + if (mMapped == null) { + mMapped = native_map(); + } + return mMapped; + } + } + + private native Image native_map(); + + /** + * Mark this block as ready to be recycled by the framework once it is + * no longer in use. All operations to this object after + * this call will cause exceptions, as well as attempt to access the + * previously mapped memory region. Caller should clear all references + * to this object after this call. + * <p> + * To avoid excessive memory consumption, it is recommended that callers + * recycle buffers as soon as they no longer need the buffers. + * + * @throws IllegalStateException if invalid + */ + public void recycle() { + synchronized (mLock) { + if (!mValid) { + throw new IllegalStateException(); + } + if (mMapped != null) { + mMapped.close(); + mMapped = null; + } + native_recycle(); + mValid = false; + mNativeContext = 0; + } + sPool.offer(this); + } + + private native void native_recycle(); + + /** + * Returns true if it is possible to allocate a graphic block that + * can be passed to all listed codecs as an input buffer without + * copying. + * <p> + * Note that even if this function returns true, {@link #obtain} + * may still throw due to invalid arguments or allocation failure. + * In addition, choosing a format that is not natively supported by the + * codec may cause color conversion. + * + * @param codecNames list of codecs that the client wants to use a + * graphic block without copying. Null entries are + * ignored. + */ + public static boolean isCodecCopyFreeCompatible(@NonNull String[] codecNames) { + return native_checkCompatible(codecNames); + } + + private static native boolean native_checkCompatible(@NonNull String[] codecNames); + + // Called from native + private void setInternalStateLocked(long context, boolean isMappable) { + mNativeContext = context; + mMappable = isMappable; + mValid = (context != 0); + } + + private static final BlockingQueue<GraphicBlock> sPool = + new LinkedBlockingQueue<>(); + + /** + * Obtain a graphic block object of dimension + * {@code width}x{@code height}. + * If {@link #isCodecCopyFreeCompatible} with the same + * {@code codecNames} returned true, the returned + * {@link GraphicBlock} object can be queued to the listed codecs + * without copying. The returned {@link GraphicBlock} object is always + * read/write mappable. + * + * @param width requested width of the graphic block + * @param height requested height of the graphic block + * @param format the format of pixels. One of the {@code COLOR_Format} + * values from {@link MediaCodecInfo.CodecCapabilities}. + * @param usage the usage of the buffer. @HardwareBuffer.Usage + * @param codecNames list of codecs that the client wants to use this + * graphic block without copying. Null entries are + * ignored. + * @return a graphic block object. + * @throws IllegalArgumentException if the parameters are invalid or + * not supported + * @throws IOException if an error occurred while allocating a buffer + */ + public static @NonNull GraphicBlock obtain( + int width, + int height, + int format, + @HardwareBuffer.Usage long usage, + @NonNull String[] codecNames) { + GraphicBlock buffer = sPool.poll(); + if (buffer == null) { + buffer = new GraphicBlock(); + } + if (width < 0 || height < 0) { + throw new IllegalArgumentException(); + } + synchronized (buffer.mLock) { + buffer.native_obtain(width, height, format, usage, codecNames); + } + return buffer; + } + + private native void native_obtain( + int width, + int height, + int format, + @HardwareBuffer.Usage long usage, + @NonNull String[] codecNames); + + @Override + protected void finalize() { + native_recycle(); + } + + private final Object mLock = new Object(); + private boolean mValid = false; + private boolean mMappable = false; + private Image mMapped = null; + private long mNativeContext = 0; + } + + /** + * Builder-like class for queue requests. Use this class to prepare a + * queue request and send it. + */ + public final class QueueRequest { + // No public constructor + private QueueRequest(@NonNull MediaCodec codec, int index) { + mCodec = codec; + mIndex = index; + } + + /** + * Set a linear block to this queue request. Exactly one buffer must be + * set for a queue request before calling {@link #queue}. It is possible + * to use the same {@link LinearBlock} object for multiple queue + * requests. The behavior is undefined if the range of the buffer + * overlaps for multiple requests, or the application writes into the + * region being processed by the codec. + * + * @param block The linear block object + * @param offset The byte offset into the input buffer at which the data starts. + * @param size The number of bytes of valid input data. + * @param presentationTimeUs The presentation timestamp in microseconds for this + * buffer. This is normally the media time at which this + * buffer should be presented (rendered). When using an output + * surface, this will be propagated as the {@link + * SurfaceTexture#getTimestamp timestamp} for the frame (after + * conversion to nanoseconds). + * @param flags A bitmask of flags + * {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}. + * While not prohibited, most codecs do not use the + * {@link #BUFFER_FLAG_KEY_FRAME} flag for input buffers. + * @return this object + * @throws IllegalStateException if a buffer is already set + */ + public @NonNull QueueRequest setLinearBlock( + @NonNull LinearBlock block, + int offset, + int size, + long presentationTimeUs, + @BufferFlag int flags) { + if (!isAccessible()) { + throw new IllegalStateException(); + } + if (mLinearBlock != null || mGraphicBlock != null) { + throw new IllegalStateException(); + } + mLinearBlock = block; + mOffset = offset; + mSize = size; + mPresentationTimeUs = presentationTimeUs; + mFlags = flags; + return this; + } + + /** + * Set an encrypted linear block to this queue request. Exactly one + * buffer must be set for a queue request before calling {@link #queue}. + * + * @param block The linear block object + * @param offset The byte offset into the input buffer at which the data starts. + * @param presentationTimeUs The presentation timestamp in microseconds for this + * buffer. This is normally the media time at which this + * buffer should be presented (rendered). When using an output + * surface, this will be propagated as the {@link + * SurfaceTexture#getTimestamp timestamp} for the frame (after + * conversion to nanoseconds). + * @param cryptoInfo Metadata describing the structure of the encrypted input sample. + * @param flags A bitmask of flags + * {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}. + * While not prohibited, most codecs do not use the + * {@link #BUFFER_FLAG_KEY_FRAME} flag for input buffers. + * @return this object + * @throws IllegalStateException if a buffer is already set + */ + public @NonNull QueueRequest setEncryptedLinearBlock( + @NonNull LinearBlock block, + int offset, + @NonNull MediaCodec.CryptoInfo cryptoInfo, + long presentationTimeUs, + @BufferFlag int flags) { + if (!isAccessible()) { + throw new IllegalStateException(); + } + if (mLinearBlock != null || mGraphicBlock != null) { + throw new IllegalStateException(); + } + mLinearBlock = block; + mOffset = offset; + mCryptoInfo = cryptoInfo; + mPresentationTimeUs = presentationTimeUs; + mFlags = flags; + return this; + } + + /** + * Set a graphic block to this queue request. Exactly one buffer must + * be set for a queue request before calling {@link #queue}. + * + * @param block The graphic block object + * @param presentationTimeUs The presentation timestamp in microseconds for this + * buffer. This is normally the media time at which this + * buffer should be presented (rendered). When using an output + * surface, this will be propagated as the {@link + * SurfaceTexture#getTimestamp timestamp} for the frame (after + * conversion to nanoseconds). + * @param flags A bitmask of flags + * {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}. + * While not prohibited, most codecs do not use the + * {@link #BUFFER_FLAG_KEY_FRAME} flag for input buffers. + * @return this object + * @throws IllegalStateException if a buffer is already set + */ + public @NonNull QueueRequest setGraphicBlock( + @NonNull GraphicBlock block, + long presentationTimeUs, + @BufferFlag int flags) { + if (!isAccessible()) { + throw new IllegalStateException(); + } + if (mLinearBlock != null || mGraphicBlock != null) { + throw new IllegalStateException(); + } + mGraphicBlock = block; + mPresentationTimeUs = presentationTimeUs; + mFlags = flags; + return this; + } + + /** + * Add a integer parameter. See {@link MediaFormat} for the list of + * supported tunings. If there was {@link MediaCodec#setParameters} + * call with the same key which is not processed by the codec yet, the + * value set from this method will override the unprocessed value. + */ + public @NonNull QueueRequest setIntegerParameter( + @NonNull String key, int value) { + if (!isAccessible()) { + throw new IllegalStateException(); + } + mTuningKeys.add(key); + mTuningValues.add(Integer.valueOf(value)); + return this; + } + + /** + * Add a long parameter. See {@link MediaFormat} for the list of + * supported tunings. If there was {@link MediaCodec#setParameters} + * call with the same key which is not processed by the codec yet, the + * value set from this method will override the unprocessed value. + */ + public @NonNull QueueRequest setLongParameter( + @NonNull String key, long value) { + if (!isAccessible()) { + throw new IllegalStateException(); + } + mTuningKeys.add(key); + mTuningValues.add(Long.valueOf(value)); + return this; + } + + /** + * Add a float parameter. See {@link MediaFormat} for the list of + * supported tunings. If there was {@link MediaCodec#setParameters} + * call with the same key which is not processed by the codec yet, the + * value set from this method will override the unprocessed value. + */ + public @NonNull QueueRequest setFloatParameter( + @NonNull String key, float value) { + if (!isAccessible()) { + throw new IllegalStateException(); + } + mTuningKeys.add(key); + mTuningValues.add(Float.valueOf(value)); + return this; + } + + /** + * Add a {@link ByteBuffer} parameter. See {@link MediaFormat} for the list of + * supported tunings. If there was {@link MediaCodec#setParameters} + * call with the same key which is not processed by the codec yet, the + * value set from this method will override the unprocessed value. + */ + public @NonNull QueueRequest setByteBufferParameter( + @NonNull String key, @NonNull ByteBuffer value) { + if (!isAccessible()) { + throw new IllegalStateException(); + } + mTuningKeys.add(key); + mTuningValues.add(value); + return this; + } + + /** + * Add a string parameter. See {@link MediaFormat} for the list of + * supported tunings. If there was {@link MediaCodec#setParameters} + * call with the same key which is not processed by the codec yet, the + * value set from this method will override the unprocessed value. + */ + public @NonNull QueueRequest setStringParameter( + @NonNull String key, @NonNull String value) { + if (!isAccessible()) { + throw new IllegalStateException(); + } + mTuningKeys.add(key); + mTuningValues.add(value); + return this; + } + + /** + * Finish building a queue request and queue the buffers with tunings. + */ + public void queue() { + if (!isAccessible()) { + throw new IllegalStateException(); + } + if (mLinearBlock == null && mGraphicBlock == null) { + throw new IllegalStateException(); + } + setAccessible(false); + if (mLinearBlock != null) { + mCodec.native_queueLinearBlock( + mIndex, mLinearBlock, mOffset, mSize, mCryptoInfo, + mPresentationTimeUs, mFlags, + mTuningKeys, mTuningValues); + } else if (mGraphicBlock != null) { + mCodec.native_queueGraphicBlock( + mIndex, mGraphicBlock, mPresentationTimeUs, mFlags, + mTuningKeys, mTuningValues); + } + clear(); + } + + @NonNull QueueRequest clear() { + mLinearBlock = null; + mOffset = 0; + mSize = 0; + mCryptoInfo = null; + mGraphicBlock = null; + mPresentationTimeUs = 0; + mFlags = 0; + mTuningKeys.clear(); + mTuningValues.clear(); + return this; + } + + boolean isAccessible() { + return mAccessible; + } + + @NonNull QueueRequest setAccessible(boolean accessible) { + mAccessible = accessible; + return this; + } + + private final MediaCodec mCodec; + private final int mIndex; + private LinearBlock mLinearBlock = null; + private int mOffset = 0; + private int mSize = 0; + private MediaCodec.CryptoInfo mCryptoInfo = null; + private GraphicBlock mGraphicBlock = null; + private long mPresentationTimeUs = 0; + private @BufferFlag int mFlags = 0; + private final ArrayList<String> mTuningKeys = new ArrayList<>(); + private final ArrayList<Object> mTuningValues = new ArrayList<>(); + + private boolean mAccessible = false; + } + + private native void native_queueLinearBlock( + int index, + @NonNull LinearBlock block, + int offset, + int size, + @Nullable CryptoInfo cryptoInfo, + long presentationTimeUs, + int flags, + @NonNull ArrayList<String> keys, + @NonNull ArrayList<Object> values); + + private native void native_queueGraphicBlock( + int index, + @NonNull GraphicBlock block, + long presentationTimeUs, + int flags, + @NonNull ArrayList<String> keys, + @NonNull ArrayList<Object> values); + + private final ArrayList<QueueRequest> mQueueRequests = new ArrayList<>(); + + /** + * Return a clear {@link QueueRequest} object for an input slot index. + * + * @param index input slot index from + * {@link Callback#onInputBufferAvailable} + * @return queue request object + * @throws IllegalStateException if not using block model + * @throws IllegalArgumentException if the input slot is not available or + * the index is out of range + */ + public @NonNull QueueRequest getQueueRequest(int index) { + synchronized (mBufferLock) { + if (mBufferMode != BUFFER_MODE_BLOCK) { + throw new IllegalStateException(); + } + if (index < 0 || index >= mQueueRequests.size()) { + throw new IllegalArgumentException(); + } + QueueRequest request = mQueueRequests.get(index); + if (request == null) { + throw new IllegalArgumentException(); + } + if (!request.isAccessible()) { + throw new IllegalArgumentException(); + } + return request.clear(); + } + } + + /** * If a non-negative timeout had been specified in the call * to {@link #dequeueOutputBuffer}, indicates that the call timed out. */ @@ -2789,8 +3527,13 @@ final public class MediaCodec { @OutputBufferInfo public final int dequeueOutputBuffer( @NonNull BufferInfo info, long timeoutUs) { + synchronized (mBufferLock) { + if (mBufferMode != BUFFER_MODE_LEGACY) { + throw new IllegalStateException(); + } + } int res = native_dequeueOutputBuffer(info, timeoutUs); - synchronized(mBufferLock) { + synchronized (mBufferLock) { if (res == INFO_OUTPUT_BUFFERS_CHANGED) { cacheBuffers(false /* input */); } else if (res >= 0) { @@ -2826,15 +3569,7 @@ final public class MediaCodec { * @throws MediaCodec.CodecException upon codec error. */ public final void releaseOutputBuffer(int index, boolean render) { - BufferInfo info = null; - synchronized(mBufferLock) { - invalidateByteBuffer(mCachedOutputBuffers, index); - mDequeuedOutputBuffers.remove(index); - if (mHasSurface) { - info = mDequeuedOutputInfos.remove(index); - } - } - releaseOutputBuffer(index, render, false /* updatePTS */, 0 /* dummy */); + releaseOutputBufferInternal(index, render, false /* updatePTS */, 0 /* dummy */); } /** @@ -2887,16 +3622,33 @@ final public class MediaCodec { * @throws MediaCodec.CodecException upon codec error. */ public final void releaseOutputBuffer(int index, long renderTimestampNs) { + releaseOutputBufferInternal( + index, true /* render */, true /* updatePTS */, renderTimestampNs); + } + + private void releaseOutputBufferInternal( + int index, boolean render, boolean updatePts, long renderTimestampNs) { BufferInfo info = null; synchronized(mBufferLock) { - invalidateByteBuffer(mCachedOutputBuffers, index); - mDequeuedOutputBuffers.remove(index); - if (mHasSurface) { - info = mDequeuedOutputInfos.remove(index); + switch (mBufferMode) { + case BUFFER_MODE_LEGACY: + invalidateByteBuffer(mCachedOutputBuffers, index); + mDequeuedOutputBuffers.remove(index); + if (mHasSurface) { + info = mDequeuedOutputInfos.remove(index); + } + break; + case BUFFER_MODE_BLOCK: + OutputFrame frame = mOutputFrames.get(index); + frame.setAccessible(false); + frame.clear(); + break; + default: + throw new IllegalStateException(); } } releaseOutputBuffer( - index, true /* render */, true /* updatePTS */, renderTimestampNs); + index, render, updatePts, renderTimestampNs); } @UnsupportedAppUsage @@ -3116,6 +3868,8 @@ final public class MediaCodec { mCachedOutputBuffers = null; mDequeuedInputBuffers.clear(); mDequeuedOutputBuffers.clear(); + mQueueRequests.clear(); + mOutputFrames.clear(); } } @@ -3154,11 +3908,16 @@ final public class MediaCodec { */ @NonNull public ByteBuffer[] getInputBuffers() { - if (mCachedInputBuffers == null) { - throw new IllegalStateException(); + synchronized (mBufferLock) { + if (mBufferMode != BUFFER_MODE_LEGACY) { + throw new IllegalStateException(); + } + if (mCachedInputBuffers == null) { + throw new IllegalStateException(); + } + // FIXME: check codec status + return mCachedInputBuffers; } - // FIXME: check codec status - return mCachedInputBuffers; } /** @@ -3185,11 +3944,16 @@ final public class MediaCodec { */ @NonNull public ByteBuffer[] getOutputBuffers() { - if (mCachedOutputBuffers == null) { - throw new IllegalStateException(); + synchronized (mBufferLock) { + if (mBufferMode != BUFFER_MODE_LEGACY) { + throw new IllegalStateException(); + } + if (mCachedOutputBuffers == null) { + throw new IllegalStateException(); + } + // FIXME: check codec status + return mCachedOutputBuffers; } - // FIXME: check codec status - return mCachedOutputBuffers; } /** @@ -3212,8 +3976,13 @@ final public class MediaCodec { */ @Nullable public ByteBuffer getInputBuffer(int index) { + synchronized (mBufferLock) { + if (mBufferMode != BUFFER_MODE_LEGACY) { + throw new IllegalStateException(); + } + } ByteBuffer newBuffer = getBuffer(true /* input */, index); - synchronized(mBufferLock) { + synchronized (mBufferLock) { invalidateByteBuffer(mCachedInputBuffers, index); mDequeuedInputBuffers.put(index, newBuffer); } @@ -3241,8 +4010,13 @@ final public class MediaCodec { */ @Nullable public Image getInputImage(int index) { + synchronized (mBufferLock) { + if (mBufferMode != BUFFER_MODE_LEGACY) { + throw new IllegalStateException(); + } + } Image newImage = getImage(true /* input */, index); - synchronized(mBufferLock) { + synchronized (mBufferLock) { invalidateByteBuffer(mCachedInputBuffers, index); mDequeuedInputBuffers.put(index, newImage); } @@ -3270,8 +4044,13 @@ final public class MediaCodec { */ @Nullable public ByteBuffer getOutputBuffer(int index) { + synchronized (mBufferLock) { + if (mBufferMode != BUFFER_MODE_LEGACY) { + throw new IllegalStateException(); + } + } ByteBuffer newBuffer = getBuffer(false /* input */, index); - synchronized(mBufferLock) { + synchronized (mBufferLock) { invalidateByteBuffer(mCachedOutputBuffers, index); mDequeuedOutputBuffers.put(index, newBuffer); } @@ -3298,8 +4077,13 @@ final public class MediaCodec { */ @Nullable public Image getOutputImage(int index) { + synchronized (mBufferLock) { + if (mBufferMode != BUFFER_MODE_LEGACY) { + throw new IllegalStateException(); + } + } Image newImage = getImage(false /* input */, index); - synchronized(mBufferLock) { + synchronized (mBufferLock) { invalidateByteBuffer(mCachedOutputBuffers, index); mDequeuedOutputBuffers.put(index, newImage); } @@ -3307,6 +4091,149 @@ final public class MediaCodec { } /** + * A single output frame and its associated metadata. + */ + public static final class OutputFrame { + // No public constructor + OutputFrame(int index) { + mIndex = index; + } + + /** + * Returns the output linear block, or null if this frame is empty. + * + * @throws IllegalStateException if this output frame is not linear. + */ + public @Nullable LinearBlock getLinearBlock() { + if (mGraphicBlock != null) { + throw new IllegalStateException(); + } + return mLinearBlock; + } + + /** + * Returns the output graphic block, or null if this frame is empty. + * + * @throws IllegalStateException if this output frame is not graphic. + */ + public @Nullable GraphicBlock getGraphicBlock() { + if (mLinearBlock != null) { + throw new IllegalStateException(); + } + return mGraphicBlock; + } + + /** + * Returns the presentation timestamp in microseconds. + */ + public long getPresentationTimeUs() { + return mPresentationTimeUs; + } + + /** + * Returns the buffer flags. + */ + public @BufferFlag int getFlags() { + return mFlags; + } + + /** + * Returns a read-only {@link MediaFormat} for this frame. The returned + * object is valid only while the client is holding the output frame. + */ + public @NonNull MediaFormat getFormat() { + return mFormat; + } + + /** + * Populate {@code keys} with the name of entries that has changed from + * the previous frame. The entries may have been removed/changed/added. + * Client can find out what the change is by querying {@link MediaFormat} + * object returned from {@link #getFormat}. + */ + public void getChangedKeys(@NonNull Set<String> keys) { + keys.clear(); + keys.addAll(mChangedKeys); + } + + void clear() { + mLinearBlock = null; + mGraphicBlock = null; + mFormat = null; + mChangedKeys.clear(); + mLoaded = false; + } + + boolean isAccessible() { + return mAccessible; + } + + void setAccessible(boolean accessible) { + mAccessible = accessible; + } + + void setBufferInfo(MediaCodec.BufferInfo info) { + mPresentationTimeUs = info.presentationTimeUs; + mFlags = info.flags; + } + + boolean isLoaded() { + return mLoaded; + } + + void setLoaded(boolean loaded) { + mLoaded = loaded; + } + + private final int mIndex; + private LinearBlock mLinearBlock = null; + private GraphicBlock mGraphicBlock = null; + private long mPresentationTimeUs = 0; + private @BufferFlag int mFlags = 0; + private MediaFormat mFormat = null; + private final ArrayList<String> mChangedKeys = new ArrayList<>(); + private boolean mAccessible = false; + private boolean mLoaded = false; + } + + private final ArrayList<OutputFrame> mOutputFrames = new ArrayList<>(); + + /** + * Returns an {@link OutputFrame} object. + * + * @param index output buffer index from + * {@link Callback#onOutputBufferAvailable} + * @return {@link OutputFrame} object describing the output buffer + * @throws IllegalStateException if not using block model + * @throws IllegalArgumentException if the output buffer is not available or + * the index is out of range + */ + public @NonNull OutputFrame getOutputFrame(int index) { + synchronized (mBufferLock) { + if (mBufferMode != BUFFER_MODE_BLOCK) { + throw new IllegalStateException(); + } + if (index < 0 || index >= mOutputFrames.size()) { + throw new IllegalArgumentException(); + } + OutputFrame frame = mOutputFrames.get(index); + if (frame == null) { + throw new IllegalArgumentException(); + } + if (!frame.isAccessible()) { + throw new IllegalArgumentException(); + } + if (!frame.isLoaded()) { + native_getOutputFrame(frame, index); + frame.setLoaded(true); + } + return frame; + } + } + + private native void native_getOutputFrame(OutputFrame frame, int index); + + /** * The content is scaled to the surface dimensions */ public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1; @@ -3889,7 +4816,9 @@ final public class MediaCodec { @Override public void close() { if (mIsImageValid) { - java.nio.NioUtils.freeDirectBuffer(mBuffer); + if (mBuffer != null) { + java.nio.NioUtils.freeDirectBuffer(mBuffer); + } mIsImageValid = false; } } @@ -3908,7 +4837,6 @@ final public class MediaCodec { super.setCropRect(cropRect); } - public MediaImage( @NonNull ByteBuffer buffer, @NonNull ByteBuffer info, boolean readOnly, long timestamp, int xOffset, int yOffset, @Nullable Rect cropRect) { @@ -3963,7 +4891,6 @@ final public class MediaCodec { throw new UnsupportedOperationException("unexpected strides: " + colInc + " pixel, " + rowInc + " row on plane " + ix); } - buffer.clear(); buffer.position(mBuffer.position() + planeOffset + (xOffset / horiz) * colInc + (yOffset / vert) * rowInc); @@ -3983,6 +4910,30 @@ final public class MediaCodec { super.setCropRect(cropRect); } + public MediaImage( + @NonNull Image.Plane[] planes, int width, int height, int format, boolean readOnly, + long timestamp, int xOffset, int yOffset, @Nullable Rect cropRect) { + mWidth = width; + mHeight = height; + mFormat = format; + mTimestamp = timestamp; + mIsImageValid = true; + mIsReadOnly = readOnly; + mBuffer = null; + mInfo = null; + mPlanes = planes; + + // save offsets and info + mXOffset = xOffset; + mYOffset = yOffset; + + if (cropRect == null) { + cropRect = new Rect(0, 0, mWidth, mHeight); + } + cropRect.offset(-xOffset, -yOffset); + super.setCropRect(cropRect); + } + private class MediaPlane extends Plane { public MediaPlane(@NonNull ByteBuffer buffer, int rowInc, int colInc) { mData = buffer; diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index 9908e042c6a4..668908079c4d 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -200,6 +200,84 @@ public class MediaFile { return null; } + /** + * Check whether the mime type is document or not. + * @param mimeType the mime type to check + * @return true, if the mimeType is matched. Otherwise, false. + */ + public static boolean isDocumentMimeType(@Nullable String mimeType) { + if (mimeType == null) { + return false; + } + + final String normalizedMimeType = normalizeMimeType(mimeType); + if (normalizedMimeType.startsWith("text/")) { + return true; + } + + switch (normalizedMimeType) { + case "application/epub+zip": + case "application/msword": + case "application/pdf": + case "application/rtf": + case "application/vnd.ms-excel": + case "application/vnd.ms-excel.addin.macroEnabled.12": + case "application/vnd.ms-excel.sheet.binary.macroEnabled.12": + case "application/vnd.ms-excel.sheet.macroEnabled.12": + case "application/vnd.ms-excel.template.macroEnabled.12": + case "application/vnd.ms-powerpoint": + case "application/vnd.ms-powerpoint.addin.macroEnabled.12": + case "application/vnd.ms-powerpoint.presentation.macroEnabled.12": + case "application/vnd.ms-powerpoint.slideshow.macroEnabled.12": + case "application/vnd.ms-powerpoint.template.macroEnabled.12": + case "application/vnd.ms-word.document.macroEnabled.12": + case "application/vnd.ms-word.template.macroEnabled.12": + case "application/vnd.oasis.opendocument.chart": + case "application/vnd.oasis.opendocument.database": + case "application/vnd.oasis.opendocument.formula": + case "application/vnd.oasis.opendocument.graphics": + case "application/vnd.oasis.opendocument.graphics-template": + case "application/vnd.oasis.opendocument.presentation": + case "application/vnd.oasis.opendocument.presentation-template": + case "application/vnd.oasis.opendocument.spreadsheet": + case "application/vnd.oasis.opendocument.spreadsheet-template": + case "application/vnd.oasis.opendocument.text": + case "application/vnd.oasis.opendocument.text-master": + case "application/vnd.oasis.opendocument.text-template": + case "application/vnd.oasis.opendocument.text-web": + case "application/vnd.openxmlformats-officedocument.presentationml.presentation": + case "application/vnd.openxmlformats-officedocument.presentationml.slideshow": + case "application/vnd.openxmlformats-officedocument.presentationml.template": + case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": + case "application/vnd.openxmlformats-officedocument.spreadsheetml.template": + case "application/vnd.openxmlformats-officedocument.wordprocessingml.document": + case "application/vnd.openxmlformats-officedocument.wordprocessingml.template": + case "application/vnd.stardivision.calc": + case "application/vnd.stardivision.chart": + case "application/vnd.stardivision.draw": + case "application/vnd.stardivision.impress": + case "application/vnd.stardivision.impress-packed": + case "application/vnd.stardivision.mail": + case "application/vnd.stardivision.math": + case "application/vnd.stardivision.writer": + case "application/vnd.stardivision.writer-global": + case "application/vnd.sun.xml.calc": + case "application/vnd.sun.xml.calc.template": + case "application/vnd.sun.xml.draw": + case "application/vnd.sun.xml.draw.template": + case "application/vnd.sun.xml.impress": + case "application/vnd.sun.xml.impress.template": + case "application/vnd.sun.xml.math": + case "application/vnd.sun.xml.writer": + case "application/vnd.sun.xml.writer.global": + case "application/vnd.sun.xml.writer.template": + case "application/x-mspublisher": + return true; + default: + return false; + } + } + public static boolean isExifMimeType(@Nullable String mimeType) { // For simplicity, assume that all image files might have EXIF data return isImageMimeType(mimeType); diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index 1a0f13943694..f408ac344d7c 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -294,6 +294,14 @@ public final class MediaFormat { public static final String KEY_BIT_RATE = "bitrate"; /** + * A key describing the hardware AV sync id. + * The associated value is an integer + * + * @see android.media.tv.tuner.Tuner#getAvSyncHwId + */ + public static final String KEY_HARDWARE_AV_SYNC_ID = "hw-av-sync-id"; + + /** * A key describing the max bitrate in bits/sec. * This is usually over a one-second sliding window (e.g. over any window of one second). * The associated value is an integer diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 71c97534c216..2d820e7c3c98 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -632,7 +632,6 @@ public class MediaPlayer extends PlayerBase private boolean mScreenOnWhilePlaying; private boolean mStayAwake; private int mStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE; - private int mUsage = -1; // Modular DRM private UUID mDrmUUID; @@ -2220,7 +2219,6 @@ public class MediaPlayer extends PlayerBase throw new IllegalArgumentException(msg); } baseUpdateAudioAttributes(attributes); - mUsage = attributes.getUsage(); Parcel pattributes = Parcel.obtain(); attributes.writeToParcel(pattributes, AudioAttributes.FLATTEN_TAGS); setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, pattributes); diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 5cb32d6ab550..61e2f77b7d2e 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -178,8 +178,9 @@ public class MediaRouter2Manager { /** * Gets routing controllers of an application with the given package name. - * If the application isn't running or it doesn't use {@link MediaRouter2}, an empty list - * will be returned. + * The first element of the returned list is the system routing controller. + * + * @see MediaRouter2#getSystemController() */ @NonNull public List<RoutingController> getRoutingControllers(@NonNull String packageName) { @@ -188,7 +189,8 @@ public class MediaRouter2Manager { List<RoutingController> controllers = new ArrayList<>(); for (RoutingSessionInfo sessionInfo : getActiveSessions()) { - if (TextUtils.equals(sessionInfo.getClientPackageName(), packageName)) { + if (sessionInfo.isSystemSession() + || TextUtils.equals(sessionInfo.getClientPackageName(), packageName)) { controllers.add(new RoutingController(sessionInfo)); } } @@ -196,8 +198,11 @@ public class MediaRouter2Manager { } /** - * Gets the list of all active routing sessions. It doesn't include default routing sessions - * of applications. + * Gets the list of all active routing sessions. + * The first element of the list is the system routing session containing + * phone speakers, wired headset, Bluetooth devices. + * The system routing session is shared by apps such that controlling it will affect + * all apps. */ @NonNull public List<RoutingSessionInfo> getActiveSessions() { diff --git a/media/java/android/media/Utils.java b/media/java/android/media/Utils.java index d942bb653127..7a4e7b897de1 100644 --- a/media/java/android/media/Utils.java +++ b/media/java/android/media/Utils.java @@ -16,12 +16,17 @@ package android.media; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.TestApi; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.net.Uri; +import android.os.Binder; import android.os.Environment; import android.os.FileUtils; +import android.os.Handler; import android.provider.OpenableColumns; import android.util.Log; import android.util.Pair; @@ -29,14 +34,26 @@ import android.util.Range; import android.util.Rational; import android.util.Size; +import com.android.internal.annotations.GuardedBy; + import java.io.File; import java.io.FileNotFoundException; import java.util.Arrays; import java.util.Comparator; +import java.util.HashMap; +import java.util.Objects; import java.util.Vector; +import java.util.concurrent.Executor; -// package private -class Utils { +/** + * Media Utilities + * + * This class is hidden but public to allow CTS testing and verification + * of the static methods and classes. + * + * @hide + */ +public class Utils { private static final String TAG = "Utils"; /** @@ -381,4 +398,265 @@ class Utils { // it already represents the file's name. return uri.toString(); } + + /** + * {@code ListenerList} is a helper class that delivers events to listeners. + * + * It is written to isolate the <strong>mechanics</strong> of event delivery from the + * <strong>details</strong> of those events. + * + * The {@code ListenerList} is parameterized on the generic type {@code V} + * of the object delivered by {@code notify()}. + * This gives compile time type safety over run-time casting of a general {@code Object}, + * much like {@code HashMap<String, Object>} does not give type safety of the + * stored {@code Object} value and may allow + * permissive storage of {@code Object}s that are not expected by users of the + * {@code HashMap}, later resulting in run-time cast exceptions that + * could have been caught by replacing + * {@code Object} with a more precise type to enforce a compile time contract. + * + * The {@code ListenerList} is implemented as a single method callback + * - or a "listener" according to Android style guidelines. + * + * The {@code ListenerList} can be trivially extended by a suitable lambda to implement + * a <strong> multiple method abstract class</strong> "callback", + * in which the generic type {@code V} could be an {@code Object} + * to encapsulate the details of the parameters of each callback method, and + * {@code instanceof} could be used to disambiguate which callback method to use. + * A {@link Bundle} could alternatively encapsulate those generic parameters, + * perhaps more conveniently. + * Again, this is a detail of the event, not the mechanics of the event delivery, + * which this class is concerned with. + * + * For details on how to use this class to implement a <strong>single listener</strong> + * {@code ListenerList}, see notes on {@link #add}. + * + * For details on how to optimize this class to implement + * a listener based on {@link Handler}s + * instead of {@link Executor}s, see{@link #ListenerList(boolean, boolean, boolean)}. + * + * This is a TestApi for CTS Unit Testing, not exposed for general Application use. + * @hide + * + * @param <V> The class of the object returned to the listener. + */ + @TestApi + public static class ListenerList<V> { + /** + * The Listener interface for callback. + * + * @param <V> The class of the object returned to the listener + */ + public interface Listener<V> { + /** + * General event listener interface which is managed by the {@code ListenerList}. + * + * @param eventCode is an integer representing the event type. This is an + * implementation defined parameter. + * @param info is the object returned to the listener. It is expected + * that the listener makes a private copy of the {@code info} object before + * modification, as it is the same instance passed to all listeners. + * This is an implementation defined parameter that may be null. + */ + void onEvent(int eventCode, @Nullable V info); + } + + private interface ListenerWithCancellation<V> extends Listener<V> { + void cancel(); + } + + /** + * Default {@code ListenerList} constructor for {@link Executor} based implementation. + * + * TODO: consider adding a "name" for debugging if this is used for + * multiple listener implementations. + */ + public ListenerList() { + this(true /* restrictSingleCallerOnEvent */, + true /* clearCallingIdentity */, + false /* forceRemoveConsistency*/); + } + + /** + * Specific {@code ListenerList} constructor for customization. + * + * See the internal notes for the corresponding private variables on the behavior of + * the boolean configuration parameters. + * + * {@code ListenerList(true, true, false)} is the default and used for + * {@link Executor} based notification implementation. + * + * {@code ListenerList(false, false, false)} may be used for as an optimization + * where the {@link Executor} is actually a {@link Handler} post. + * + * @param restrictSingleCallerOnEvent whether the listener will only be called by + * a single thread at a time. + * @param clearCallingIdentity whether the binder calling identity on + * {@link #notify} is cleared. + * @param forceRemoveConsistency whether remove() guarantees no more callbacks to + * the listener immediately after the call. + */ + public ListenerList(boolean restrictSingleCallerOnEvent, + boolean clearCallingIdentity, + boolean forceRemoveConsistency) { + mRestrictSingleCallerOnEvent = restrictSingleCallerOnEvent; + mClearCallingIdentity = clearCallingIdentity; + mForceRemoveConsistency = forceRemoveConsistency; + } + + /** + * Adds a listener to the {@code ListenerList}. + * + * The {@code ListenerList} is most often used to hold {@code multiple} listeners. + * + * Per Android style, for a single method Listener interface, the add and remove + * would be wrapped in "addSomeListener" or "removeSomeListener"; + * or a lambda implemented abstract class callback, wrapped in + * "registerSomeCallback" or "unregisterSomeCallback". + * + * We allow a general {@code key} to be attached to add and remove that specific + * listener. It could be the {@code listener} object itself. + * + * For some implementations, there may be only a {@code single} listener permitted. + * + * Per Android style, for a single listener {@code ListenerList}, + * the naming of the wrapping call to {@link #add} would be + * "setSomeListener" with a nullable listener, which would be null + * to call {@link #remove}. + * + * In that case, the caller may use this {@link #add} with a single constant object for + * the {@code key} to enforce only one Listener in the {@code ListenerList}. + * Likewise on remove it would use that + * same single constant object to remove the listener. + * That {@code key} object could be the {@code ListenerList} itself for convenience. + * + * @param key is a unique object that is used to identify the listener + * when {@code remove()} is called. It can be the listener itself. + * @param executor is used to execute the callback. + * @param listener is the {@link AudioTrack.ListenerList.Listener} + * interface to be called upon {@link notify}. + */ + public void add( + @NonNull Object key, @NonNull Executor executor, @NonNull Listener<V> listener) { + Objects.requireNonNull(key); + Objects.requireNonNull(executor); + Objects.requireNonNull(listener); + + // construct wrapper outside of lock. + ListenerWithCancellation<V> listenerWithCancellation = + new ListenerWithCancellation<V>() { + private final Object mLock = new Object(); // our lock is per Listener. + private volatile boolean mCancelled = false; // atomic rmw not needed. + + @Override + public void onEvent(int eventCode, V info) { + executor.execute(() -> { + // Note deep execution of locking and cancellation + // so this works after posting on different threads. + if (mRestrictSingleCallerOnEvent || mForceRemoveConsistency) { + synchronized (mLock) { + if (mCancelled) return; + listener.onEvent(eventCode, info); + } + } else { + if (mCancelled) return; + listener.onEvent(eventCode, info); + } + }); + } + + @Override + public void cancel() { + if (mForceRemoveConsistency) { + synchronized (mLock) { + mCancelled = true; + } + } else { + mCancelled = true; + } + } + }; + + synchronized (mListeners) { + // TODO: consider an option to check the existence of the key + // and throw an ISE if it exists. + mListeners.put(key, listenerWithCancellation); // replaces old value + } + } + + /** + * Removes a listener from the {@code ListenerList}. + * + * @param key the unique object associated with the listener during {@link #add}. + */ + public void remove(@NonNull Object key) { + Objects.requireNonNull(key); + + ListenerWithCancellation<V> listener; + synchronized (mListeners) { + listener = mListeners.get(key); + if (listener == null) { // TODO: consider an option to throw ISE Here. + return; + } + mListeners.remove(key); // removes if exist + } + + // cancel outside of lock + listener.cancel(); + } + + /** + * Notifies all listeners on the List. + * + * @param eventCode to pass to all listeners. + * @param info to pass to all listeners. This is an implemention defined parameter + * which may be {@code null}. + */ + public void notify(int eventCode, @Nullable V info) { + Object[] listeners; // note we can't cast an object array to a listener array + synchronized (mListeners) { + if (mListeners.size() == 0) { + return; + } + listeners = mListeners.values().toArray(); // guarantees a copy. + } + + // notify outside of lock. + final Long identity = mClearCallingIdentity ? Binder.clearCallingIdentity() : null; + try { + for (Object object : listeners) { + final ListenerWithCancellation<V> listener = + (ListenerWithCancellation<V>) object; + listener.onEvent(eventCode, info); + } + } finally { + if (identity != null) { + Binder.restoreCallingIdentity(identity); + } + } + } + + @GuardedBy("mListeners") + private HashMap<Object, ListenerWithCancellation<V>> mListeners = new HashMap<>(); + + // An Executor may run in multiple threads, whereas a Handler runs on a single Looper. + // Should be true for an Executor to avoid concurrent calling into the same listener, + // can be false for a Handler as a Handler forces single thread caller for each listener. + private final boolean mRestrictSingleCallerOnEvent; // default true + + // An Executor may run in the calling thread, whereas a handler will post to the Looper. + // Should be true for an Executor to prevent privilege escalation, + // can be false for a Handler as its thread is not the calling binder thread. + private final boolean mClearCallingIdentity; // default true + + // Guaranteeing no listener callbacks after removal requires taking the same lock for the + // remove as the callback; this is a reversal in calling layers, + // hence the risk of lock order inversion is great. + // + // Set to true only if you can control the caller's listen and remove methods and/or + // the threading of the Executor used for each listener. + // When set to false, we do not lock, but still do a best effort to cancel messages + // on the fly. + private final boolean mForceRemoveConsistency; // default false + } } 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/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl index b5e9d1b2939f..c199c6f8b761 100644 --- a/media/java/android/media/tv/ITvInputManager.aidl +++ b/media/java/android/media/tv/ITvInputManager.aidl @@ -95,7 +95,7 @@ interface ITvInputManager { // For TV input hardware binding List<TvInputHardwareInfo> getHardwareList(); ITvInputHardware acquireTvInputHardware(int deviceId, in ITvInputHardwareCallback callback, - in TvInputInfo info, int userId); + in TvInputInfo info, int userId, String tvInputSessionId, int priorityHint); void releaseTvInputHardware(int deviceId, in ITvInputHardware hardware, int userId); // For TV input capturing diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index fc5d67d50ee5..9cdfa2aa0807 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -1735,8 +1735,14 @@ public final class TvInputManager { /** * Acquires {@link Hardware} object for the given device ID. * - * <p>A subsequent call to this method on the same {@code deviceId} will release the currently - * acquired Hardware. + * <p>A subsequent call to this method on the same {@code deviceId} could release the currently + * acquired Hardware if TunerResourceManager(TRM) detects higher priority from the current + * request. + * + * <p>If the client would like to provide information for the TRM to compare, use + * {@link #acquireTvInputHardware(int, TvInputInfo, HardwareCallback, String, int)} instead. + * + * <p>Otherwise default priority will be applied. * * @param deviceId The device ID to acquire Hardware for. * @param callback A callback to receive updates on Hardware. @@ -1747,8 +1753,49 @@ public final class TvInputManager { */ @SystemApi @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) - public Hardware acquireTvInputHardware(int deviceId, TvInputInfo info, - final HardwareCallback callback) { + public Hardware acquireTvInputHardware(int deviceId, @NonNull TvInputInfo info, + @NonNull final HardwareCallback callback) { + Preconditions.checkNotNull(info); + Preconditions.checkNotNull(callback); + return acquireTvInputHardwareInternal(deviceId, info, callback, null, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE); + } + + /** + * Acquires {@link Hardware} object for the given device ID. + * + * <p>A subsequent call to this method on the same {@code deviceId} could release the currently + * acquired Hardware if TunerResourceManager(TRM) detects higher priority from the current + * request. + * + * @param deviceId The device ID to acquire Hardware for. + * @param callback A callback to receive updates on Hardware. + * @param info The TV input which will use the acquired Hardware. + * @param tvInputSessionId a String returned to TIS when the session was created. + * {@see TvInputService#onCreateSession(String, String)}. If null, the client will be + * treated as a background app. + * @param priorityHint The use case of the client. {@see TvInputService#PriorityHintUseCaseType} + * @return Hardware on success, {@code null} otherwise. When the TRM decides to not grant + * resource, null is returned and the {@link IllegalStateException} is thrown with + * "No enough resources". + * + * @hide + */ + @SystemApi + @Nullable + @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) + public Hardware acquireTvInputHardware(int deviceId, @NonNull TvInputInfo info, + @NonNull final HardwareCallback callback, + @Nullable String tvInputSessionId, + @TvInputService.PriorityHintUseCaseType int priorityHint) { + Preconditions.checkNotNull(info); + Preconditions.checkNotNull(callback); + return acquireTvInputHardwareInternal(deviceId, info, callback, + tvInputSessionId, priorityHint); + } + + private Hardware acquireTvInputHardwareInternal(int deviceId, TvInputInfo info, + final HardwareCallback callback, String tvInputSessionId, int priorityHint) { try { return new Hardware( mService.acquireTvInputHardware(deviceId, new ITvInputHardwareCallback.Stub() { @@ -1761,7 +1808,7 @@ public final class TvInputManager { public void onStreamConfigChanged(TvStreamConfig[] configs) { callback.onStreamConfigChanged(configs); } - }, info, mUserId)); + }, info, mUserId, tvInputSessionId, priorityHint)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } 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/DemuxCapabilities.java b/media/java/android/media/tv/tuner/DemuxCapabilities.java index 2c08e5bcdb19..364516cf488d 100644 --- a/media/java/android/media/tv/tuner/DemuxCapabilities.java +++ b/media/java/android/media/tv/tuner/DemuxCapabilities.java @@ -58,11 +58,13 @@ public class DemuxCapabilities { private final long mSectionFilterLength; private final int mFilterCaps; private final int[] mLinkCaps; + private final boolean mSupportTimeFilter; // Used by JNI private DemuxCapabilities(int demuxCount, int recordCount, int playbackCount, int tsFilterCount, int sectionFilterCount, int audioFilterCount, int videoFilterCount, int pesFilterCount, - int pcrFilterCount, long sectionFilterLength, int filterCaps, int[] linkCaps) { + int pcrFilterCount, long sectionFilterLength, int filterCaps, int[] linkCaps, + boolean timeFilter) { mDemuxCount = demuxCount; mRecordCount = recordCount; mPlaybackCount = playbackCount; @@ -75,6 +77,7 @@ public class DemuxCapabilities { mSectionFilterLength = sectionFilterLength; mFilterCaps = filterCaps; mLinkCaps = linkCaps; + mSupportTimeFilter = timeFilter; } /** @@ -161,4 +164,10 @@ public class DemuxCapabilities { public int[] getLinkCapabilities() { return mLinkCaps; } + /** + * Is {@link android.media.tv.tuner.filter.TimeFilter} supported. + */ + public boolean isTimeFilterSupported() { + return mSupportTimeFilter; + } } diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 62ab2c94afeb..5e012447e9dd 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -23,6 +23,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; +import android.media.tv.TvInputService; import android.media.tv.tuner.TunerConstants.Result; import android.media.tv.tuner.dvr.DvrPlayback; import android.media.tv.tuner.dvr.DvrRecorder; @@ -55,7 +56,7 @@ import java.util.concurrent.Executor; * @hide */ @SystemApi -public final class Tuner implements AutoCloseable { +public class Tuner implements AutoCloseable { private static final String TAG = "MediaTvTuner"; private static final boolean DEBUG = false; @@ -88,25 +89,13 @@ public final class Tuner implements AutoCloseable { /** * Constructs a Tuner instance. * - * @param context context of the caller. - */ - public Tuner(@NonNull Context context) { - mContext = context; - nativeSetup(); - } - - /** - * Constructs a Tuner instance. - * * @param context the context of the caller. * @param tvInputSessionId the session ID of the TV input. * @param useCase the use case of this Tuner instance. - * - * @hide - * TODO: replace the other constructor */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) - public Tuner(@NonNull Context context, @NonNull String tvInputSessionId, int useCase, + public Tuner(@NonNull Context context, @NonNull String tvInputSessionId, + @TvInputService.PriorityHintUseCaseType int useCase, @Nullable OnResourceLostListener listener) { mContext = context; } @@ -115,18 +104,38 @@ public final class Tuner implements AutoCloseable { * Shares the frontend resource with another Tuner instance * * @param tuner the Tuner instance to share frontend resource with. - * - * @hide */ + @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void shareFrontendFromTuner(@NonNull Tuner tuner) { + // TODO: implementation. } + /** + * Updates client priority with an arbitrary value along with a nice value. + * + * <p>Tuner resource manager (TRM) uses the client priority value to decide whether it is able + * to reclaim insufficient resources from another client. + * <p>The nice value represents how much the client intends to give up the resource when an + * insufficient resource situation happens. + * + * @param priority the new priority. + * @param niceValue the nice value. + */ + @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) + public void updateResourcePriority(int priority, int niceValue) { + // TODO: implementation. + } private long mNativeContext; // used by native jMediaTuner - /** @hide */ + /** + * Releases the Tuner instance. + */ + @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @Override - public void close() {} + public void close() { + // TODO: implementation. + } /** * Native Initialization. @@ -176,7 +185,7 @@ public final class Tuner implements AutoCloseable { /** * Listener for resource lost. * - * @hide + * <p>Resource is reclaimed and tuner instance is forced to close. */ public interface OnResourceLostListener { /** @@ -269,8 +278,8 @@ public final class Tuner implements AutoCloseable { * start a new tuning. * * <p> - * Tune is an async call, with {@link OnTuneEventListener#LOCKED LOCKED} and {@link - * OnTuneEventListener#NO_SIGNAL NO_SIGNAL} events sent to the {@link OnTuneEventListener} + * Tune is an async call, with {@link OnTuneEventListener#SIGNAL_LOCKED} and {@link + * OnTuneEventListener#SIGNAL_NO_SIGNAL} events sent to the {@link OnTuneEventListener} * specified in {@link #setOnTuneEventListener(Executor, OnTuneEventListener)}. * * @param settings Signal delivery information the frontend uses to @@ -357,13 +366,9 @@ public final class Tuner implements AutoCloseable { * @param lnb the LNB instance. * * @return result status of the operation. - * - * @hide */ - @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @Result - public int setLnb(@NonNull Lnb lnb) { - TunerUtils.checkTunerPermission(mContext); + private int setLnb(@NonNull Lnb lnb) { return nativeSetLnb(lnb.mId); } @@ -373,8 +378,6 @@ public final class Tuner implements AutoCloseable { * @param enable {@code true} to activate LNA module; {@code false} to deactivate LNA. * * @return result status of the operation. - * - * @hide */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @Result @@ -390,10 +393,9 @@ public final class Tuner implements AutoCloseable { * * @param statusTypes an array of status types which the caller requests. * @return statuses which response the caller's requests. - * @hide */ @Nullable - public FrontendStatus getFrontendStatus(int[] statusTypes) { + public FrontendStatus getFrontendStatus(@NonNull int[] statusTypes) { return nativeGetFrontendStatus(statusTypes); } @@ -402,8 +404,6 @@ public final class Tuner implements AutoCloseable { * * @param filter the filter instance for the hardware sync ID. * @return the id of hardware A/V sync. - * - * @hide */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int getAvSyncHwId(@NonNull Filter filter) { @@ -419,8 +419,6 @@ public final class Tuner implements AutoCloseable { * * @param avSyncHwId the hardware id of A/V sync. * @return the current timestamp of hardware A/V sync. - * - * @hide */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public long getAvSyncTime(int avSyncHwId) { @@ -436,8 +434,6 @@ public final class Tuner implements AutoCloseable { * * @param ciCamId specify CI-CAM Id to connect. * @return result status of the operation. - * - * @hide */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @Result @@ -452,8 +448,6 @@ public final class Tuner implements AutoCloseable { * <p>The demux will use the output from the frontend as the input after this call. * * @return result status of the operation. - * - * @hide */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @Result @@ -466,8 +460,6 @@ public final class Tuner implements AutoCloseable { * Gets the frontend information. * * @return The frontend information. {@code null} if the operation failed. - * - * @hide */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @Nullable @@ -480,23 +472,7 @@ public final class Tuner implements AutoCloseable { } /** - * Gets the frontend ID. - * - * @hide - */ - @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) - public int getFrontendId() { - TunerUtils.checkTunerPermission(mContext); - if (mFrontend == null) { - throw new IllegalStateException("frontend is not initialized"); - } - return mFrontend.mId; - } - - /** * Gets Demux capabilities. - * - * @hide */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @Nullable @@ -622,19 +598,6 @@ public final class Tuner implements AutoCloseable { } /** - * This class is used to interact with descramblers. - * - * <p> Descrambler is a hardware component used to descramble data. - * - * <p> This class controls the TIS interaction with Tuner HAL. - * TODO: Remove - */ - public class Descrambler { - private Descrambler() { - } - } - - /** * Opens a Descrambler in tuner. * * @return a {@link Descrambler} object. diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java index c03061948fe4..f54808dbe0b0 100644 --- a/media/java/android/media/tv/tuner/TunerConstants.java +++ b/media/java/android/media/tv/tuner/TunerConstants.java @@ -17,14 +17,8 @@ package android.media.tv.tuner; import android.annotation.IntDef; -import android.annotation.LongDef; import android.annotation.SystemApi; import android.hardware.tv.tuner.V1_0.Constants; -import android.media.tv.tuner.frontend.DvbcFrontendSettings; -import android.media.tv.tuner.frontend.DvbsFrontendSettings; -import android.media.tv.tuner.frontend.Isdbs3FrontendSettings; -import android.media.tv.tuner.frontend.IsdbsFrontendSettings; -import android.media.tv.tuner.frontend.IsdbtFrontendSettings; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -38,12 +32,10 @@ import java.lang.annotation.RetentionPolicy; public final class TunerConstants { /** * Invalid TS packet ID. - * @hide */ public static final int INVALID_TS_PID = Constants.Constant.INVALID_TS_PID; /** * Invalid stream ID. - * @hide */ public static final int INVALID_STREAM_ID = Constants.Constant.INVALID_STREAM_ID; @@ -58,7 +50,7 @@ public final class TunerConstants { /** * Scan type auto. * - * <p> Tuner will send {@link #onLocked} + * <p> Tuner will send {@link android.media.tv.tuner.frontend.ScanCallback#onLocked} */ public static final int SCAN_TYPE_AUTO = Constants.FrontendScanType.SCAN_AUTO; /** @@ -70,350 +62,6 @@ public final class TunerConstants { public static final int SCAN_TYPE_BLIND = Constants.FrontendScanType.SCAN_BLIND; /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = "INDEX_TYPE_", value = - {INDEX_TYPE_NONE, INDEX_TYPE_SC, INDEX_TYPE_SC_HEVC}) - public @interface ScIndexType {} - - /** - * Start Code Index is not used. - * @hide - */ - public static final int INDEX_TYPE_NONE = Constants.DemuxRecordScIndexType.NONE; - /** - * Start Code index. - * @hide - */ - public static final int INDEX_TYPE_SC = Constants.DemuxRecordScIndexType.SC; - /** - * Start Code index for HEVC. - * @hide - */ - public static final int INDEX_TYPE_SC_HEVC = Constants.DemuxRecordScIndexType.SC_HEVC; - - /** - * Indexes can be tagged by Start Code in PES (Packetized Elementary Stream) - * according to ISO/IEC 13818-1. - * @hide - */ - @IntDef(flag = true, value = {SC_INDEX_I_FRAME, SC_INDEX_P_FRAME, SC_INDEX_B_FRAME, - SC_INDEX_SEQUENCE}) - @Retention(RetentionPolicy.SOURCE) - public @interface ScIndex {} - - /** - * SC index for a new I-frame. - */ - public static final int SC_INDEX_I_FRAME = Constants.DemuxScIndex.I_FRAME; - /** - * SC index for a new P-frame. - */ - public static final int SC_INDEX_P_FRAME = Constants.DemuxScIndex.P_FRAME; - /** - * SC index for a new B-frame. - */ - public static final int SC_INDEX_B_FRAME = Constants.DemuxScIndex.B_FRAME; - /** - * SC index for a new sequence. - */ - public static final int SC_INDEX_SEQUENCE = Constants.DemuxScIndex.SEQUENCE; - - - /** - * Indexes can be tagged by NAL unit group in HEVC according to ISO/IEC 23008-2. - * - * @hide - */ - @IntDef(flag = true, - value = {SC_HEVC_INDEX_SPS, SC_HEVC_INDEX_AUD, SC_HEVC_INDEX_SLICE_CE_BLA_W_LP, - SC_HEVC_INDEX_SLICE_BLA_W_RADL, SC_HEVC_INDEX_SLICE_BLA_N_LP, - SC_HEVC_INDEX_SLICE_IDR_W_RADL, SC_HEVC_INDEX_SLICE_IDR_N_LP, - SC_HEVC_INDEX_SLICE_TRAIL_CRA}) - @Retention(RetentionPolicy.SOURCE) - public @interface ScHevcIndex {} - - /** - * SC HEVC index SPS. - */ - public static final int SC_HEVC_INDEX_SPS = Constants.DemuxScHevcIndex.SPS; - /** - * SC HEVC index AUD. - */ - public static final int SC_HEVC_INDEX_AUD = Constants.DemuxScHevcIndex.AUD; - /** - * SC HEVC index SLICE_CE_BLA_W_LP. - */ - public static final int SC_HEVC_INDEX_SLICE_CE_BLA_W_LP = - Constants.DemuxScHevcIndex.SLICE_CE_BLA_W_LP; - /** - * SC HEVC index SLICE_BLA_W_RADL. - */ - public static final int SC_HEVC_INDEX_SLICE_BLA_W_RADL = - Constants.DemuxScHevcIndex.SLICE_BLA_W_RADL; - /** - * SC HEVC index SLICE_BLA_N_LP. - */ - public static final int SC_HEVC_INDEX_SLICE_BLA_N_LP = - Constants.DemuxScHevcIndex.SLICE_BLA_N_LP; - /** - * SC HEVC index SLICE_IDR_W_RADL. - */ - public static final int SC_HEVC_INDEX_SLICE_IDR_W_RADL = - Constants.DemuxScHevcIndex.SLICE_IDR_W_RADL; - /** - * SC HEVC index SLICE_IDR_N_LP. - */ - public static final int SC_HEVC_INDEX_SLICE_IDR_N_LP = - Constants.DemuxScHevcIndex.SLICE_IDR_N_LP; - /** - * SC HEVC index SLICE_TRAIL_CRA. - */ - public static final int SC_HEVC_INDEX_SLICE_TRAIL_CRA = - Constants.DemuxScHevcIndex.SLICE_TRAIL_CRA; - - /** @hide */ - @LongDef({FEC_UNDEFINED, FEC_AUTO, FEC_1_2, FEC_1_3, FEC_1_4, FEC_1_5, FEC_2_3, FEC_2_5, - FEC_2_9, FEC_3_4, FEC_3_5, FEC_4_5, FEC_4_15, FEC_5_6, FEC_5_9, FEC_6_7, FEC_7_8, - FEC_7_9, FEC_7_15, FEC_8_9, FEC_8_15, FEC_9_10, FEC_9_20, FEC_11_15, FEC_11_20, - FEC_11_45, FEC_13_18, FEC_13_45, FEC_14_45, FEC_23_36, FEC_25_36, FEC_26_45, FEC_28_45, - FEC_29_45, FEC_31_45, FEC_32_45, FEC_77_90}) - @Retention(RetentionPolicy.SOURCE) - public @interface FrontendInnerFec {} - - /** - * FEC not defined - * @hide - */ - public static final long FEC_UNDEFINED = Constants.FrontendInnerFec.FEC_UNDEFINED; - /** - * hardware is able to detect and set FEC automatically - * @hide - */ - public static final long FEC_AUTO = Constants.FrontendInnerFec.AUTO; - /** - * 1/2 conv. code rate - * @hide - */ - public static final long FEC_1_2 = Constants.FrontendInnerFec.FEC_1_2; - /** - * 1/3 conv. code rate - * @hide - */ - public static final long FEC_1_3 = Constants.FrontendInnerFec.FEC_1_3; - /** - * 1/4 conv. code rate - * @hide - */ - public static final long FEC_1_4 = Constants.FrontendInnerFec.FEC_1_4; - /** - * 1/5 conv. code rate - * @hide - */ - public static final long FEC_1_5 = Constants.FrontendInnerFec.FEC_1_5; - /** - * 2/3 conv. code rate - * @hide - */ - public static final long FEC_2_3 = Constants.FrontendInnerFec.FEC_2_3; - /** - * 2/5 conv. code rate - * @hide - */ - public static final long FEC_2_5 = Constants.FrontendInnerFec.FEC_2_5; - /** - * 2/9 conv. code rate - * @hide - */ - public static final long FEC_2_9 = Constants.FrontendInnerFec.FEC_2_9; - /** - * 3/4 conv. code rate - * @hide - */ - public static final long FEC_3_4 = Constants.FrontendInnerFec.FEC_3_4; - /** - * 3/5 conv. code rate - * @hide - */ - public static final long FEC_3_5 = Constants.FrontendInnerFec.FEC_3_5; - /** - * 4/5 conv. code rate - * @hide - */ - public static final long FEC_4_5 = Constants.FrontendInnerFec.FEC_4_5; - /** - * 4/15 conv. code rate - * @hide - */ - public static final long FEC_4_15 = Constants.FrontendInnerFec.FEC_4_15; - /** - * 5/6 conv. code rate - * @hide - */ - public static final long FEC_5_6 = Constants.FrontendInnerFec.FEC_5_6; - /** - * 5/9 conv. code rate - * @hide - */ - public static final long FEC_5_9 = Constants.FrontendInnerFec.FEC_5_9; - /** - * 6/7 conv. code rate - * @hide - */ - public static final long FEC_6_7 = Constants.FrontendInnerFec.FEC_6_7; - /** - * 7/8 conv. code rate - * @hide - */ - public static final long FEC_7_8 = Constants.FrontendInnerFec.FEC_7_8; - /** - * 7/9 conv. code rate - * @hide - */ - public static final long FEC_7_9 = Constants.FrontendInnerFec.FEC_7_9; - /** - * 7/15 conv. code rate - * @hide - */ - public static final long FEC_7_15 = Constants.FrontendInnerFec.FEC_7_15; - /** - * 8/9 conv. code rate - * @hide - */ - public static final long FEC_8_9 = Constants.FrontendInnerFec.FEC_8_9; - /** - * 8/15 conv. code rate - * @hide - */ - public static final long FEC_8_15 = Constants.FrontendInnerFec.FEC_8_15; - /** - * 9/10 conv. code rate - * @hide - */ - public static final long FEC_9_10 = Constants.FrontendInnerFec.FEC_9_10; - /** - * 9/20 conv. code rate - * @hide - */ - public static final long FEC_9_20 = Constants.FrontendInnerFec.FEC_9_20; - /** - * 11/15 conv. code rate - * @hide - */ - public static final long FEC_11_15 = Constants.FrontendInnerFec.FEC_11_15; - /** - * 11/20 conv. code rate - * @hide - */ - public static final long FEC_11_20 = Constants.FrontendInnerFec.FEC_11_20; - /** - * 11/45 conv. code rate - * @hide - */ - public static final long FEC_11_45 = Constants.FrontendInnerFec.FEC_11_45; - /** - * 13/18 conv. code rate - * @hide - */ - public static final long FEC_13_18 = Constants.FrontendInnerFec.FEC_13_18; - /** - * 13/45 conv. code rate - * @hide - */ - public static final long FEC_13_45 = Constants.FrontendInnerFec.FEC_13_45; - /** - * 14/45 conv. code rate - * @hide - */ - public static final long FEC_14_45 = Constants.FrontendInnerFec.FEC_14_45; - /** - * 23/36 conv. code rate - * @hide - */ - public static final long FEC_23_36 = Constants.FrontendInnerFec.FEC_23_36; - /** - * 25/36 conv. code rate - * @hide - */ - public static final long FEC_25_36 = Constants.FrontendInnerFec.FEC_25_36; - /** - * 26/45 conv. code rate - * @hide - */ - public static final long FEC_26_45 = Constants.FrontendInnerFec.FEC_26_45; - /** - * 28/45 conv. code rate - * @hide - */ - public static final long FEC_28_45 = Constants.FrontendInnerFec.FEC_28_45; - /** - * 29/45 conv. code rate - * @hide - */ - public static final long FEC_29_45 = Constants.FrontendInnerFec.FEC_29_45; - /** - * 31/45 conv. code rate - * @hide - */ - public static final long FEC_31_45 = Constants.FrontendInnerFec.FEC_31_45; - /** - * 32/45 conv. code rate - * @hide - */ - public static final long FEC_32_45 = Constants.FrontendInnerFec.FEC_32_45; - /** - * 77/90 conv. code rate - * @hide - */ - public static final long FEC_77_90 = Constants.FrontendInnerFec.FEC_77_90; - - - /** @hide */ - @IntDef(value = { - DvbcFrontendSettings.MODULATION_UNDEFINED, - DvbcFrontendSettings.MODULATION_AUTO, - DvbcFrontendSettings.MODULATION_MOD_16QAM, - DvbcFrontendSettings.MODULATION_MOD_32QAM, - DvbcFrontendSettings.MODULATION_MOD_64QAM, - DvbcFrontendSettings.MODULATION_MOD_128QAM, - DvbcFrontendSettings.MODULATION_MOD_256QAM, - DvbsFrontendSettings.MODULATION_UNDEFINED, - DvbsFrontendSettings.MODULATION_AUTO, - DvbsFrontendSettings.MODULATION_MOD_QPSK, - DvbsFrontendSettings.MODULATION_MOD_8PSK, - DvbsFrontendSettings.MODULATION_MOD_16QAM, - DvbsFrontendSettings.MODULATION_MOD_16PSK, - DvbsFrontendSettings.MODULATION_MOD_32PSK, - DvbsFrontendSettings.MODULATION_MOD_ACM, - DvbsFrontendSettings.MODULATION_MOD_8APSK, - DvbsFrontendSettings.MODULATION_MOD_16APSK, - DvbsFrontendSettings.MODULATION_MOD_32APSK, - DvbsFrontendSettings.MODULATION_MOD_64APSK, - DvbsFrontendSettings.MODULATION_MOD_128APSK, - DvbsFrontendSettings.MODULATION_MOD_256APSK, - DvbsFrontendSettings.MODULATION_MOD_RESERVED, - IsdbsFrontendSettings.MODULATION_UNDEFINED, - IsdbsFrontendSettings.MODULATION_AUTO, - IsdbsFrontendSettings.MODULATION_MOD_BPSK, - IsdbsFrontendSettings.MODULATION_MOD_QPSK, - IsdbsFrontendSettings.MODULATION_MOD_TC8PSK, - Isdbs3FrontendSettings.MODULATION_UNDEFINED, - Isdbs3FrontendSettings.MODULATION_AUTO, - Isdbs3FrontendSettings.MODULATION_MOD_BPSK, - Isdbs3FrontendSettings.MODULATION_MOD_QPSK, - Isdbs3FrontendSettings.MODULATION_MOD_8PSK, - Isdbs3FrontendSettings.MODULATION_MOD_16APSK, - Isdbs3FrontendSettings.MODULATION_MOD_32APSK, - IsdbtFrontendSettings.MODULATION_UNDEFINED, - IsdbtFrontendSettings.MODULATION_AUTO, - IsdbtFrontendSettings.MODULATION_MOD_DQPSK, - IsdbtFrontendSettings.MODULATION_MOD_QPSK, - IsdbtFrontendSettings.MODULATION_MOD_16QAM, - IsdbtFrontendSettings.MODULATION_MOD_64QAM}) - @Retention(RetentionPolicy.SOURCE) - public @interface FrontendModulation {} - - - /** @hide */ @IntDef({RESULT_SUCCESS, RESULT_UNAVAILABLE, RESULT_NOT_INITIALIZED, RESULT_INVALID_STATE, RESULT_INVALID_ARGUMENT, RESULT_OUT_OF_MEMORY, RESULT_UNKNOWN_ERROR}) @Retention(RetentionPolicy.SOURCE) diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java index eb2f4a9533ad..b6bd86befd87 100644 --- a/media/java/android/media/tv/tuner/filter/MediaEvent.java +++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java @@ -19,6 +19,7 @@ package android.media.tv.tuner.filter; import android.annotation.BytesLong; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.media.MediaCodec.LinearBlock; /** * Filter event sent from {@link Filter} objects with media type. @@ -32,7 +33,7 @@ public class MediaEvent extends FilterEvent{ private final long mPts; private final long mDataLength; private final long mOffset; - private final Object mLinearBuffer; + private final LinearBlock mLinearBlock; private final boolean mIsSecureMemory; private final long mDataId; private final int mMpuSequenceNumber; @@ -41,14 +42,14 @@ public class MediaEvent extends FilterEvent{ // This constructor is used by JNI code only private MediaEvent(int streamId, boolean isPtsPresent, long pts, long dataLength, long offset, - Object buffer, boolean isSecureMemory, long dataId, int mpuSequenceNumber, + LinearBlock buffer, boolean isSecureMemory, long dataId, int mpuSequenceNumber, boolean isPrivateData, AudioDescriptor extraMetaData) { mStreamId = streamId; mIsPtsPresent = isPtsPresent; mPts = pts; mDataLength = dataLength; mOffset = offset; - mLinearBuffer = buffer; + mLinearBlock = buffer; mIsSecureMemory = isSecureMemory; mDataId = dataId; mMpuSequenceNumber = mpuSequenceNumber; @@ -96,13 +97,11 @@ public class MediaEvent extends FilterEvent{ } /** - * Gets a linear buffer associated to the memory where audio or video data stays. - * TODO: use LinearBuffer when it's ready. - * - * @hide + * Gets a linear block associated to the memory where audio or video data stays. */ - public Object getLinearBuffer() { - return mLinearBuffer; + @Nullable + public LinearBlock getLinearBlock() { + return mLinearBlock; } /** @@ -125,6 +124,21 @@ public class MediaEvent extends FilterEvent{ } /** + * Gets the audio handle. + * + * <p>Client gets audio handle from {@link MediaEvent}, and queues it to + * {@link android.media.AudioTrack} in + * {@link android.media.AudioTrack#ENCAPSULATION_MODE_HANDLE} format. + * + * @return the audio handle. + * @see android.media.AudioTrack#ENCAPSULATION_MODE_HANDLE + */ + public long getAudioHandle() { + // TODO: implement + return mDataId; + } + + /** * Gets MPU sequence number of filtered data. */ public int getMpuSequenceNumber() { diff --git a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java index 093dc6ff7d64..466fa3ecb6e7 100644 --- a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java +++ b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java @@ -18,6 +18,7 @@ package android.media.tv.tuner.filter; import android.annotation.BytesLong; import android.annotation.SystemApi; +import android.media.tv.tuner.filter.RecordSettings.ScHevcIndex; /** * Filter event sent from {@link Filter} objects with MMTP type. @@ -38,6 +39,7 @@ public class MmtpRecordEvent extends FilterEvent { /** * Gets indexes which can be tagged by NAL unit group in HEVC according to ISO/IEC 23008-2. */ + @ScHevcIndex public int getScHevcIndexMask() { return mScHevcIndexMask; } diff --git a/media/java/android/media/tv/tuner/filter/RecordSettings.java b/media/java/android/media/tv/tuner/filter/RecordSettings.java index 209ed67f23ff..0c812ab051bd 100644 --- a/media/java/android/media/tv/tuner/filter/RecordSettings.java +++ b/media/java/android/media/tv/tuner/filter/RecordSettings.java @@ -22,8 +22,6 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; import android.hardware.tv.tuner.V1_0.Constants; -import android.media.tv.tuner.TunerConstants; -import android.media.tv.tuner.TunerConstants.ScIndexType; import android.media.tv.tuner.TunerUtils; import java.lang.annotation.Retention; @@ -112,29 +110,130 @@ public class RecordSettings extends Settings { */ public static final int TS_INDEX_ADAPTATION_EXTENSION_FLAG = Constants.DemuxTsIndex.ADAPTATION_EXTENSION_FLAG; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "INDEX_TYPE_", value = + {INDEX_TYPE_NONE, INDEX_TYPE_SC, INDEX_TYPE_SC_HEVC}) + public @interface ScIndexType {} + + /** + * Start Code Index is not used. + */ + public static final int INDEX_TYPE_NONE = Constants.DemuxRecordScIndexType.NONE; + /** + * Start Code index. + */ + public static final int INDEX_TYPE_SC = Constants.DemuxRecordScIndexType.SC; + /** + * Start Code index for HEVC. + */ + public static final int INDEX_TYPE_SC_HEVC = Constants.DemuxRecordScIndexType.SC_HEVC; + + /** + * Indexes can be tagged by Start Code in PES (Packetized Elementary Stream) + * according to ISO/IEC 13818-1. + * @hide + */ + @IntDef(flag = true, value = {SC_INDEX_I_FRAME, SC_INDEX_P_FRAME, SC_INDEX_B_FRAME, + SC_INDEX_SEQUENCE}) + @Retention(RetentionPolicy.SOURCE) + public @interface ScIndex {} + + /** + * SC index for a new I-frame. + */ + public static final int SC_INDEX_I_FRAME = Constants.DemuxScIndex.I_FRAME; + /** + * SC index for a new P-frame. + */ + public static final int SC_INDEX_P_FRAME = Constants.DemuxScIndex.P_FRAME; + /** + * SC index for a new B-frame. + */ + public static final int SC_INDEX_B_FRAME = Constants.DemuxScIndex.B_FRAME; + /** + * SC index for a new sequence. + */ + public static final int SC_INDEX_SEQUENCE = Constants.DemuxScIndex.SEQUENCE; + + + /** + * Indexes can be tagged by NAL unit group in HEVC according to ISO/IEC 23008-2. + * + * @hide + */ + @IntDef(flag = true, + value = {SC_HEVC_INDEX_SPS, SC_HEVC_INDEX_AUD, SC_HEVC_INDEX_SLICE_CE_BLA_W_LP, + SC_HEVC_INDEX_SLICE_BLA_W_RADL, SC_HEVC_INDEX_SLICE_BLA_N_LP, + SC_HEVC_INDEX_SLICE_IDR_W_RADL, SC_HEVC_INDEX_SLICE_IDR_N_LP, + SC_HEVC_INDEX_SLICE_TRAIL_CRA}) + @Retention(RetentionPolicy.SOURCE) + public @interface ScHevcIndex {} + + /** + * SC HEVC index SPS. + */ + public static final int SC_HEVC_INDEX_SPS = Constants.DemuxScHevcIndex.SPS; + /** + * SC HEVC index AUD. + */ + public static final int SC_HEVC_INDEX_AUD = Constants.DemuxScHevcIndex.AUD; + /** + * SC HEVC index SLICE_CE_BLA_W_LP. + */ + public static final int SC_HEVC_INDEX_SLICE_CE_BLA_W_LP = + Constants.DemuxScHevcIndex.SLICE_CE_BLA_W_LP; + /** + * SC HEVC index SLICE_BLA_W_RADL. + */ + public static final int SC_HEVC_INDEX_SLICE_BLA_W_RADL = + Constants.DemuxScHevcIndex.SLICE_BLA_W_RADL; + /** + * SC HEVC index SLICE_BLA_N_LP. + */ + public static final int SC_HEVC_INDEX_SLICE_BLA_N_LP = + Constants.DemuxScHevcIndex.SLICE_BLA_N_LP; + /** + * SC HEVC index SLICE_IDR_W_RADL. + */ + public static final int SC_HEVC_INDEX_SLICE_IDR_W_RADL = + Constants.DemuxScHevcIndex.SLICE_IDR_W_RADL; + /** + * SC HEVC index SLICE_IDR_N_LP. + */ + public static final int SC_HEVC_INDEX_SLICE_IDR_N_LP = + Constants.DemuxScHevcIndex.SLICE_IDR_N_LP; + /** + * SC HEVC index SLICE_TRAIL_CRA. + */ + public static final int SC_HEVC_INDEX_SLICE_TRAIL_CRA = + Constants.DemuxScHevcIndex.SLICE_TRAIL_CRA; + /** * @hide */ @IntDef(flag = true, prefix = "SC_", value = { - TunerConstants.SC_INDEX_I_FRAME, - TunerConstants.SC_INDEX_P_FRAME, - TunerConstants.SC_INDEX_B_FRAME, - TunerConstants.SC_INDEX_SEQUENCE, - TunerConstants.SC_HEVC_INDEX_SPS, - TunerConstants.SC_HEVC_INDEX_AUD, - TunerConstants.SC_HEVC_INDEX_SLICE_CE_BLA_W_LP, - TunerConstants.SC_HEVC_INDEX_SLICE_BLA_W_RADL, - TunerConstants.SC_HEVC_INDEX_SLICE_BLA_N_LP, - TunerConstants.SC_HEVC_INDEX_SLICE_IDR_W_RADL, - TunerConstants.SC_HEVC_INDEX_SLICE_IDR_N_LP, - TunerConstants.SC_HEVC_INDEX_SLICE_TRAIL_CRA, + SC_INDEX_I_FRAME, + SC_INDEX_P_FRAME, + SC_INDEX_B_FRAME, + SC_INDEX_SEQUENCE, + SC_HEVC_INDEX_SPS, + SC_HEVC_INDEX_AUD, + SC_HEVC_INDEX_SLICE_CE_BLA_W_LP, + SC_HEVC_INDEX_SLICE_BLA_W_RADL, + SC_HEVC_INDEX_SLICE_BLA_N_LP, + SC_HEVC_INDEX_SLICE_IDR_W_RADL, + SC_HEVC_INDEX_SLICE_IDR_N_LP, + SC_HEVC_INDEX_SLICE_TRAIL_CRA, }) @Retention(RetentionPolicy.SOURCE) public @interface ScIndexMask {} + private final int mTsIndexMask; private final int mScIndexType; private final int mScIndexMask; diff --git a/media/java/android/media/tv/tuner/filter/SectionSettings.java b/media/java/android/media/tv/tuner/filter/SectionSettings.java index 70788a7515ca..947013840bc6 100644 --- a/media/java/android/media/tv/tuner/filter/SectionSettings.java +++ b/media/java/android/media/tv/tuner/filter/SectionSettings.java @@ -16,6 +16,7 @@ package android.media.tv.tuner.filter; +import android.annotation.NonNull; import android.annotation.SystemApi; import android.media.tv.tuner.TunerUtils; @@ -25,9 +26,81 @@ import android.media.tv.tuner.TunerUtils; * @hide */ @SystemApi -public class SectionSettings extends Settings { +public abstract class SectionSettings extends Settings { + final boolean mCrcEnabled; + final boolean mIsRepeat; + final boolean mIsRaw; - SectionSettings(int mainType) { + SectionSettings(int mainType, boolean crcEnabled, boolean isRepeat, boolean isRaw) { super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_SECTION)); + mCrcEnabled = crcEnabled; + mIsRepeat = isRepeat; + mIsRaw = isRaw; + } + + /** + * Returns whether the filter enables CRC (Cyclic redundancy check) and discards data which + * doesn't pass the check. + */ + public boolean isCrcEnabled() { + return mCrcEnabled; + } + /** + * Returns whether the filter repeats the data with the same version. + */ + public boolean isRepeat() { + return mIsRepeat; + } + /** + * Returns whether the filter sends {@link FilterCallback#onFilterStatusChanged} instead of + * {@link FilterCallback#onFilterEvent}. + */ + public boolean isRaw() { + return mIsRaw; + } + + /** + * Builder for {@link SectionSettings}. + * + * @param <T> The subclass to be built. + */ + public abstract static class Builder<T extends Builder<T>> + extends Settings.Builder<Builder<T>> { + boolean mCrcEnabled; + boolean mIsRepeat; + boolean mIsRaw; + + Builder(int mainType) { + super(mainType); + } + + /** + * Sets whether the filter enables CRC (Cyclic redundancy check) and discards data which + * doesn't pass the check. + */ + @NonNull + public T setCrcEnabled(boolean crcEnabled) { + mCrcEnabled = crcEnabled; + return self(); + } + /** + * Sets whether the filter repeats the data with the same version. + */ + @NonNull + public T setRepeat(boolean isRepeat) { + mIsRepeat = isRepeat; + return self(); + } + /** + * Sets whether the filter send onFilterStatus instead of + * {@link FilterCallback#onFilterEvent}. + */ + @NonNull + public T setRaw(boolean isRaw) { + mIsRaw = isRaw; + return self(); + } + + /* package */ abstract T self(); } } diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java index eeeabdfecc01..cb547ec8ae9a 100644 --- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java +++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java @@ -34,8 +34,9 @@ public class SectionSettingsWithSectionBits extends SectionSettings { private final byte[] mMode; - private SectionSettingsWithSectionBits(int mainType, byte[] filter, byte[] mask, byte[] mode) { - super(mainType); + private SectionSettingsWithSectionBits(int mainType, boolean isCheckCrc, boolean isRepeat, + boolean isRaw, byte[] filter, byte[] mask, byte[] mode) { + super(mainType, isCheckCrc, isRepeat, isRaw); mFilter = filter; mMask = mask; mMode = mode; @@ -86,7 +87,7 @@ public class SectionSettingsWithSectionBits extends SectionSettings { /** * Builder for {@link SectionSettingsWithSectionBits}. */ - public static class Builder extends Settings.Builder<Builder> { + public static class Builder extends SectionSettings.Builder<Builder> { private byte[] mFilter; private byte[] mMask; private byte[] mMode; @@ -125,7 +126,8 @@ public class SectionSettingsWithSectionBits extends SectionSettings { */ @NonNull public SectionSettingsWithSectionBits build() { - return new SectionSettingsWithSectionBits(mMainType, mFilter, mMask, mMode); + return new SectionSettingsWithSectionBits( + mMainType, mCrcEnabled, mIsRepeat, mIsRaw, mFilter, mMask, mMode); } @Override diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java index c5ff45cfa7de..09d1dae971b7 100644 --- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java +++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java @@ -32,8 +32,9 @@ public class SectionSettingsWithTableInfo extends SectionSettings { private final int mTableId; private final int mVersion; - private SectionSettingsWithTableInfo(int mainType, int tableId, int version) { - super(mainType); + private SectionSettingsWithTableInfo(int mainType, boolean isCheckCrc, boolean isRepeat, + boolean isRaw, int tableId, int version) { + super(mainType, isCheckCrc, isRepeat, isRaw); mTableId = tableId; mVersion = version; } @@ -67,7 +68,7 @@ public class SectionSettingsWithTableInfo extends SectionSettings { /** * Builder for {@link SectionSettingsWithTableInfo}. */ - public static class Builder extends Settings.Builder<Builder> { + public static class Builder extends SectionSettings.Builder<Builder> { private int mTableId; private int mVersion; @@ -97,7 +98,8 @@ public class SectionSettingsWithTableInfo extends SectionSettings { */ @NonNull public SectionSettingsWithTableInfo build() { - return new SectionSettingsWithTableInfo(mMainType, mTableId, mVersion); + return new SectionSettingsWithTableInfo( + mMainType, mCrcEnabled, mIsRepeat, mIsRaw, mTableId, mVersion); } @Override diff --git a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java index b6878e6a13a8..3d83a74a500c 100644 --- a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java +++ b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java @@ -106,7 +106,7 @@ public class TlvFilterConfiguration extends FilterConfiguration { * Sets whether the data is compressed IP packet. */ @NonNull - public Builder setIsCompressedIpPacket(boolean isCompressedIpPacket) { + public Builder setCompressedIpPacket(boolean isCompressedIpPacket) { mIsCompressedIpPacket = isCompressedIpPacket; return this; } diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java index aa64df56f556..096bc679f7f8 100644 --- a/media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java +++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java @@ -16,11 +16,14 @@ package android.media.tv.tuner.frontend; +import android.annotation.SystemApi; + /** * Capabilities for analog tuners. * * @hide */ +@SystemApi public class AnalogFrontendCapabilities extends FrontendCapabilities { @AnalogFrontendSettings.SignalType private final int mTypeCap; diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java index 61880ab5f553..7b85fa81adb6 100644 --- a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java @@ -195,7 +195,7 @@ public class AnalogFrontendSettings extends FrontendSettings { /** * Creates a builder for {@link AnalogFrontendSettings}. * - * @param the context of the caller. + * @param context the context of the caller. */ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) @NonNull @@ -223,7 +223,7 @@ public class AnalogFrontendSettings extends FrontendSettings { * Sets analog signal type. */ @NonNull - public Builder setASignalType(@SignalType int signalType) { + public Builder setSignalType(@SignalType int signalType) { mSignalType = signalType; return this; } diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendCapabilities.java index 1fd1f63c775a..77309127a464 100644 --- a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendCapabilities.java +++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendCapabilities.java @@ -16,10 +16,14 @@ package android.media.tv.tuner.frontend; +import android.annotation.SystemApi; + /** * ATSC-3 Capabilities. + * * @hide */ +@SystemApi public class Atsc3FrontendCapabilities extends FrontendCapabilities { private final int mBandwidthCap; private final int mModulationCap; @@ -63,7 +67,7 @@ public class Atsc3FrontendCapabilities extends FrontendCapabilities { * Gets code rate capability. */ @Atsc3FrontendSettings.CodeRate - public int getCodeRateCapability() { + public int getPlpCodeRateCapability() { return mCodeRateCap; } /** diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java index 5e1ba722c9fc..85f3f72020d0 100644 --- a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java @@ -19,6 +19,7 @@ package android.media.tv.tuner.frontend; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.content.Context; import android.hardware.tv.tuner.V1_0.Constants; import android.media.tv.tuner.TunerUtils; @@ -28,8 +29,10 @@ import java.lang.annotation.RetentionPolicy; /** * Frontend settings for ATSC-3. + * * @hide */ +@SystemApi public class Atsc3FrontendSettings extends FrontendSettings { /** @hide */ @@ -273,9 +276,9 @@ public class Atsc3FrontendSettings extends FrontendSettings { public static final int DEMOD_OUTPUT_FORMAT_BASEBAND_PACKET = Constants.FrontendAtsc3DemodOutputFormat.BASEBAND_PACKET; - public final int mBandwidth; - public final int mDemodOutputFormat; - public final Atsc3PlpSettings[] mPlpSettings; + private final int mBandwidth; + private final int mDemodOutputFormat; + private final Atsc3PlpSettings[] mPlpSettings; private Atsc3FrontendSettings(int frequency, int bandwidth, int demodOutputFormat, Atsc3PlpSettings[] plpSettings) { @@ -302,6 +305,7 @@ public class Atsc3FrontendSettings extends FrontendSettings { /** * Gets PLP Settings. */ + @NonNull public Atsc3PlpSettings[] getPlpSettings() { return mPlpSettings; } @@ -349,7 +353,7 @@ public class Atsc3FrontendSettings extends FrontendSettings { * Sets PLP Settings. */ @NonNull - public Builder setPlpSettings(Atsc3PlpSettings[] plpSettings) { + public Builder setPlpSettings(@NonNull Atsc3PlpSettings[] plpSettings) { mPlpSettings = plpSettings; return this; } diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3PlpSettings.java b/media/java/android/media/tv/tuner/frontend/Atsc3PlpSettings.java index 43a68a088c33..fe61dbc1b86f 100644 --- a/media/java/android/media/tv/tuner/frontend/Atsc3PlpSettings.java +++ b/media/java/android/media/tv/tuner/frontend/Atsc3PlpSettings.java @@ -18,13 +18,16 @@ package android.media.tv.tuner.frontend; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.content.Context; import android.media.tv.tuner.TunerUtils; /** - * PLP settings for ATSC-3. + * Physical Layer Pipe (PLP) settings for ATSC-3. + * * @hide */ +@SystemApi public class Atsc3PlpSettings { private final int mPlpId; private final int mModulation; diff --git a/media/java/android/media/tv/tuner/frontend/AtscFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/AtscFrontendCapabilities.java index 0ff516de3603..eb21caa0f6db 100644 --- a/media/java/android/media/tv/tuner/frontend/AtscFrontendCapabilities.java +++ b/media/java/android/media/tv/tuner/frontend/AtscFrontendCapabilities.java @@ -16,10 +16,14 @@ package android.media.tv.tuner.frontend; +import android.annotation.SystemApi; + /** * ATSC Capabilities. + * * @hide */ +@SystemApi public class AtscFrontendCapabilities extends FrontendCapabilities { private final int mModulationCap; diff --git a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java index 32901d8aadec..fc82a1c7a6a0 100644 --- a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java @@ -19,6 +19,7 @@ package android.media.tv.tuner.frontend; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.content.Context; import android.hardware.tv.tuner.V1_0.Constants; import android.media.tv.tuner.TunerUtils; @@ -28,8 +29,10 @@ import java.lang.annotation.RetentionPolicy; /** * Frontend settings for ATSC. + * * @hide */ +@SystemApi public class AtscFrontendSettings extends FrontendSettings { /** @hide */ diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendCapabilities.java index f3fbdb5f3b18..faa54344573a 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbcFrontendCapabilities.java +++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendCapabilities.java @@ -16,12 +16,14 @@ package android.media.tv.tuner.frontend; -import android.media.tv.tuner.TunerConstants.FrontendInnerFec; +import android.annotation.SystemApi; /** * DVBC Capabilities. + * * @hide */ +@SystemApi public class DvbcFrontendCapabilities extends FrontendCapabilities { private final int mModulationCap; private final int mFecCap; @@ -43,7 +45,7 @@ public class DvbcFrontendCapabilities extends FrontendCapabilities { /** * Gets inner FEC capability. */ - @FrontendInnerFec + @FrontendSettings.InnerFec public int getFecCapability() { return mFecCap; } diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java index 3d212d3d55c3..bfa4f3f027a4 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java @@ -19,9 +19,9 @@ package android.media.tv.tuner.frontend; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.content.Context; import android.hardware.tv.tuner.V1_0.Constants; -import android.media.tv.tuner.TunerConstants.FrontendInnerFec; import android.media.tv.tuner.TunerUtils; import java.lang.annotation.Retention; @@ -29,8 +29,10 @@ import java.lang.annotation.RetentionPolicy; /** * Frontend settings for DVBC. + * * @hide */ +@SystemApi public class DvbcFrontendSettings extends FrontendSettings { /** @hide */ @@ -169,7 +171,7 @@ public class DvbcFrontendSettings extends FrontendSettings { /** * Gets Inner Forward Error Correction. */ - @FrontendInnerFec + @InnerFec public long getFec() { return mFec; } @@ -239,7 +241,7 @@ public class DvbcFrontendSettings extends FrontendSettings { * Sets Inner Forward Error Correction. */ @NonNull - public Builder setFec(@FrontendInnerFec long fec) { + public Builder setFec(@InnerFec long fec) { mFec = fec; return this; } diff --git a/media/java/android/media/tv/tuner/frontend/DvbsCodeRate.java b/media/java/android/media/tv/tuner/frontend/DvbsCodeRate.java index 04d33750849c..2bdd37985813 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbsCodeRate.java +++ b/media/java/android/media/tv/tuner/frontend/DvbsCodeRate.java @@ -18,22 +18,24 @@ package android.media.tv.tuner.frontend; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.content.Context; -import android.media.tv.tuner.TunerConstants.FrontendInnerFec; import android.media.tv.tuner.TunerUtils; /** * Code rate for DVBS. + * * @hide */ +@SystemApi public class DvbsCodeRate { - private final long mFec; + private final long mInnerFec; private final boolean mIsLinear; private final boolean mIsShortFrames; private final int mBitsPer1000Symbol; private DvbsCodeRate(long fec, boolean isLinear, boolean isShortFrames, int bitsPer1000Symbol) { - mFec = fec; + mInnerFec = fec; mIsLinear = isLinear; mIsShortFrames = isShortFrames; mBitsPer1000Symbol = bitsPer1000Symbol; @@ -42,9 +44,9 @@ public class DvbsCodeRate { /** * Gets inner FEC. */ - @FrontendInnerFec - public long getFec() { - return mFec; + @FrontendSettings.InnerFec + public long getInnerFec() { + return mInnerFec; } /** * Checks whether it's linear. @@ -93,7 +95,7 @@ public class DvbsCodeRate { * Sets inner FEC. */ @NonNull - public Builder setFec(@FrontendInnerFec long fec) { + public Builder setInnerFec(@FrontendSettings.InnerFec long fec) { mFec = fec; return this; } diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendCapabilities.java index bd615d033bc6..1e25cf2a9b00 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendCapabilities.java +++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendCapabilities.java @@ -16,12 +16,14 @@ package android.media.tv.tuner.frontend; -import android.media.tv.tuner.TunerConstants.FrontendInnerFec; +import android.annotation.SystemApi; /** * DVBS Capabilities. + * * @hide */ +@SystemApi public class DvbsFrontendCapabilities extends FrontendCapabilities { private final int mModulationCap; private final long mInnerFecCap; @@ -43,7 +45,7 @@ public class DvbsFrontendCapabilities extends FrontendCapabilities { /** * Gets inner FEC capability. */ - @FrontendInnerFec + @FrontendSettings.InnerFec public long getInnerFecCapability() { return mInnerFecCap; } diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java index 44dbcc01b16e..4a4fed596f21 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.content.Context; import android.hardware.tv.tuner.V1_0.Constants; import android.media.tv.tuner.TunerUtils; @@ -29,8 +30,10 @@ import java.lang.annotation.RetentionPolicy; /** * Frontend settings for DVBS. + * * @hide */ +@SystemApi public class DvbsFrontendSettings extends FrontendSettings { /** @hide */ @IntDef(flag = true, @@ -209,7 +212,7 @@ public class DvbsFrontendSettings extends FrontendSettings { private final int mModulation; - private final DvbsCodeRate mCoderate; + private final DvbsCodeRate mCodeRate; private final int mSymbolRate; private final int mRolloff; private final int mPilot; @@ -217,11 +220,11 @@ public class DvbsFrontendSettings extends FrontendSettings { private final int mStandard; private final int mVcmMode; - private DvbsFrontendSettings(int frequency, int modulation, DvbsCodeRate coderate, + private DvbsFrontendSettings(int frequency, int modulation, DvbsCodeRate codeRate, int symbolRate, int rolloff, int pilot, int inputStreamId, int standard, int vcm) { super(frequency); mModulation = modulation; - mCoderate = coderate; + mCodeRate = codeRate; mSymbolRate = symbolRate; mRolloff = rolloff; mPilot = pilot; @@ -241,8 +244,8 @@ public class DvbsFrontendSettings extends FrontendSettings { * Gets Code rate. */ @Nullable - public DvbsCodeRate getCoderate() { - return mCoderate; + public DvbsCodeRate getCodeRate() { + return mCodeRate; } /** * Gets Symbol Rate in symbols per second. @@ -302,7 +305,7 @@ public class DvbsFrontendSettings extends FrontendSettings { */ public static class Builder extends FrontendSettings.Builder<Builder> { private int mModulation; - private DvbsCodeRate mCoderate; + private DvbsCodeRate mCodeRate; private int mSymbolRate; private int mRolloff; private int mPilot; @@ -325,8 +328,8 @@ public class DvbsFrontendSettings extends FrontendSettings { * Sets Code rate. */ @NonNull - public Builder setCoderate(@Nullable DvbsCodeRate coderate) { - mCoderate = coderate; + public Builder setCodeRate(@Nullable DvbsCodeRate codeRate) { + mCodeRate = codeRate; return this; } /** @@ -383,7 +386,7 @@ public class DvbsFrontendSettings extends FrontendSettings { */ @NonNull public DvbsFrontendSettings build() { - return new DvbsFrontendSettings(mFrequency, mModulation, mCoderate, mSymbolRate, + return new DvbsFrontendSettings(mFrequency, mModulation, mCodeRate, mSymbolRate, mRolloff, mPilot, mInputStreamId, mStandard, mVcmMode); } diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendCapabilities.java index 0d4717903f19..524952dffcd4 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendCapabilities.java +++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendCapabilities.java @@ -16,27 +16,31 @@ package android.media.tv.tuner.frontend; +import android.annotation.SystemApi; + /** * DVBT Capabilities. + * * @hide */ +@SystemApi public class DvbtFrontendCapabilities extends FrontendCapabilities { private final int mTransmissionModeCap; private final int mBandwidthCap; private final int mConstellationCap; - private final int mCoderateCap; + private final int mCodeRateCap; private final int mHierarchyCap; private final int mGuardIntervalCap; private final boolean mIsT2Supported; private final boolean mIsMisoSupported; private DvbtFrontendCapabilities(int transmissionModeCap, int bandwidthCap, - int constellationCap, int coderateCap, int hierarchyCap, int guardIntervalCap, + int constellationCap, int codeRateCap, int hierarchyCap, int guardIntervalCap, boolean isT2Supported, boolean isMisoSupported) { mTransmissionModeCap = transmissionModeCap; mBandwidthCap = bandwidthCap; mConstellationCap = constellationCap; - mCoderateCap = coderateCap; + mCodeRateCap = codeRateCap; mHierarchyCap = hierarchyCap; mGuardIntervalCap = guardIntervalCap; mIsT2Supported = isT2Supported; @@ -67,9 +71,9 @@ public class DvbtFrontendCapabilities extends FrontendCapabilities { /** * Gets code rate capability. */ - @DvbtFrontendSettings.Coderate + @DvbtFrontendSettings.CodeRate public int getCodeRateCapability() { - return mCoderateCap; + return mCodeRateCap; } /** * Gets hierarchy capability. diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java index 9a82de0b6db3..e99fd36f98a7 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java @@ -19,6 +19,7 @@ package android.media.tv.tuner.frontend; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.content.Context; import android.hardware.tv.tuner.V1_0.Constants; import android.media.tv.tuner.TunerUtils; @@ -28,8 +29,10 @@ import java.lang.annotation.RetentionPolicy; /** * Frontend settings for DVBT. + * * @hide */ +@SystemApi public class DvbtFrontendSettings extends FrontendSettings { /** @hide */ @@ -220,7 +223,7 @@ public class DvbtFrontendSettings extends FrontendSettings { value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_1_2, CODERATE_2_3, CODERATE_3_4, CODERATE_5_6, CODERATE_7_8, CODERATE_3_5, CODERATE_4_5, CODERATE_6_7, CODERATE_8_9}) @Retention(RetentionPolicy.SOURCE) - public @interface Coderate {} + public @interface CodeRate {} /** * Code rate undefined. @@ -347,8 +350,7 @@ public class DvbtFrontendSettings extends FrontendSettings { public static final int STANDARD_T2 = Constants.FrontendDvbtStandard.T2; /** @hide */ - @IntDef(flag = true, - prefix = "PLP_MODE_", + @IntDef(prefix = "PLP_MODE_", value = {PLP_MODE_UNDEFINED, PLP_MODE_AUTO, PLP_MODE_MANUAL}) @Retention(RetentionPolicy.SOURCE) public @interface PlpMode {} @@ -371,8 +373,8 @@ public class DvbtFrontendSettings extends FrontendSettings { private final int mBandwidth; private final int mConstellation; private final int mHierarchy; - private final int mHpCoderate; - private final int mLpCoderate; + private final int mHpCodeRate; + private final int mLpCodeRate; private final int mGuardInterval; private final boolean mIsHighPriority; private final int mStandard; @@ -382,7 +384,7 @@ public class DvbtFrontendSettings extends FrontendSettings { private final int mPlpGroupId; private DvbtFrontendSettings(int frequency, int transmissionMode, int bandwidth, - int constellation, int hierarchy, int hpCoderate, int lpCoderate, int guardInterval, + int constellation, int hierarchy, int hpCodeRate, int lpCodeRate, int guardInterval, boolean isHighPriority, int standard, boolean isMiso, int plpMode, int plpId, int plpGroupId) { super(frequency); @@ -390,8 +392,8 @@ public class DvbtFrontendSettings extends FrontendSettings { mBandwidth = bandwidth; mConstellation = constellation; mHierarchy = hierarchy; - mHpCoderate = hpCoderate; - mLpCoderate = lpCoderate; + mHpCodeRate = hpCodeRate; + mLpCodeRate = lpCodeRate; mGuardInterval = guardInterval; mIsHighPriority = isHighPriority; mStandard = standard; @@ -432,16 +434,16 @@ public class DvbtFrontendSettings extends FrontendSettings { /** * Gets Code Rate for High Priority level. */ - @Coderate - public int getHpCoderate() { - return mHpCoderate; + @CodeRate + public int getHpCodeRate() { + return mHpCodeRate; } /** * Gets Code Rate for Low Priority level. */ - @Coderate - public int getLpCoderate() { - return mLpCoderate; + @CodeRate + public int getLpCodeRate() { + return mLpCodeRate; } /** * Gets Guard Interval. @@ -509,8 +511,8 @@ public class DvbtFrontendSettings extends FrontendSettings { private int mBandwidth; private int mConstellation; private int mHierarchy; - private int mHpCoderate; - private int mLpCoderate; + private int mHpCodeRate; + private int mLpCodeRate; private int mGuardInterval; private boolean mIsHighPriority; private int mStandard; @@ -558,16 +560,16 @@ public class DvbtFrontendSettings extends FrontendSettings { * Sets Code Rate for High Priority level. */ @NonNull - public Builder setHpCoderate(@Coderate int hpCoderate) { - mHpCoderate = hpCoderate; + public Builder setHpCodeRate(@CodeRate int hpCodeRate) { + mHpCodeRate = hpCodeRate; return this; } /** * Sets Code Rate for Low Priority level. */ @NonNull - public Builder setLpCoderate(@Coderate int lpCoderate) { - mLpCoderate = lpCoderate; + public Builder setLpCodeRate(@CodeRate int lpCodeRate) { + mLpCodeRate = lpCodeRate; return this; } /** @@ -633,7 +635,7 @@ public class DvbtFrontendSettings extends FrontendSettings { @NonNull public DvbtFrontendSettings build() { return new DvbtFrontendSettings(mFrequency, mTransmissionMode, mBandwidth, - mConstellation, mHierarchy, mHpCoderate, mLpCoderate, mGuardInterval, + mConstellation, mHierarchy, mHpCodeRate, mLpCodeRate, mGuardInterval, mIsHighPriority, mStandard, mIsMiso, mPlpMode, mPlpId, mPlpGroupId); } diff --git a/media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java index e4f66b80d008..c03133d26a30 100644 --- a/media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java +++ b/media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java @@ -16,10 +16,13 @@ package android.media.tv.tuner.frontend; +import android.annotation.SystemApi; + /** * Frontend capabilities. * * @hide */ +@SystemApi public abstract class FrontendCapabilities { } diff --git a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java index 360c84a383e0..696d8390fc11 100644 --- a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java +++ b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java @@ -17,6 +17,7 @@ package android.media.tv.tuner.frontend; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.media.tv.tuner.frontend.FrontendSettings.Type; import android.media.tv.tuner.frontend.FrontendStatus.FrontendStatusType; import android.util.Range; @@ -26,6 +27,7 @@ import android.util.Range; * * @hide */ +@SystemApi public class FrontendInfo { private final int mId; private final int mType; @@ -102,12 +104,14 @@ public class FrontendInfo { * @return An array of supported status types. */ @FrontendStatusType + @NonNull public int[] getStatusCapabilities() { return mStatusCaps; } /** * Gets frontend capabilities. */ + @NonNull public FrontendCapabilities getFrontendCapability() { return mFrontendCap; } diff --git a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java index b80b7cd5212a..9071526e79f4 100644 --- a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java @@ -18,6 +18,7 @@ package android.media.tv.tuner.frontend; import android.annotation.IntDef; import android.annotation.IntRange; +import android.annotation.LongDef; import android.annotation.NonNull; import android.annotation.SystemApi; import android.hardware.tv.tuner.V1_0.Constants; @@ -33,8 +34,9 @@ import java.lang.annotation.RetentionPolicy; @SystemApi public abstract class FrontendSettings { /** @hide */ - @IntDef({TYPE_UNDEFINED, TYPE_ANALOG, TYPE_ATSC, TYPE_ATSC3, TYPE_DVBC, TYPE_DVBS, TYPE_DVBT, - TYPE_ISDBS, TYPE_ISDBS3, TYPE_ISDBT}) + @IntDef(prefix = "TYPE_", + value = {TYPE_UNDEFINED, TYPE_ANALOG, TYPE_ATSC, TYPE_ATSC3, TYPE_DVBC, TYPE_DVBS, + TYPE_DVBT, TYPE_ISDBS, TYPE_ISDBS3, TYPE_ISDBT}) @Retention(RetentionPolicy.SOURCE) public @interface Type {} @@ -79,10 +81,173 @@ public abstract class FrontendSettings { */ public static final int TYPE_ISDBT = Constants.FrontendType.ISDBT; - private final int mFrequency; + /** @hide */ - public FrontendSettings(int frequency) { + @LongDef(flag = true, + prefix = "FEC_", + value = {FEC_UNDEFINED, FEC_AUTO, FEC_1_2, FEC_1_3, FEC_1_4, FEC_1_5, FEC_2_3, FEC_2_5, + FEC_2_9, FEC_3_4, FEC_3_5, FEC_4_5, FEC_4_15, FEC_5_6, FEC_5_9, FEC_6_7, FEC_7_8, + FEC_7_9, FEC_7_15, FEC_8_9, FEC_8_15, FEC_9_10, FEC_9_20, FEC_11_15, FEC_11_20, + FEC_11_45, FEC_13_18, FEC_13_45, FEC_14_45, FEC_23_36, FEC_25_36, FEC_26_45, FEC_28_45, + FEC_29_45, FEC_31_45, FEC_32_45, FEC_77_90}) + @Retention(RetentionPolicy.SOURCE) + public @interface InnerFec {} + + /** + * FEC not defined. + */ + public static final long FEC_UNDEFINED = Constants.FrontendInnerFec.FEC_UNDEFINED; + /** + * hardware is able to detect and set FEC automatically. + */ + public static final long FEC_AUTO = Constants.FrontendInnerFec.AUTO; + /** + * 1/2 conv. code rate. + */ + public static final long FEC_1_2 = Constants.FrontendInnerFec.FEC_1_2; + /** + * 1/3 conv. code rate. + */ + public static final long FEC_1_3 = Constants.FrontendInnerFec.FEC_1_3; + /** + * 1/4 conv. code rate. + */ + public static final long FEC_1_4 = Constants.FrontendInnerFec.FEC_1_4; + /** + * 1/5 conv. code rate. + */ + public static final long FEC_1_5 = Constants.FrontendInnerFec.FEC_1_5; + /** + * 2/3 conv. code rate. + */ + public static final long FEC_2_3 = Constants.FrontendInnerFec.FEC_2_3; + /** + * 2/5 conv. code rate. + */ + public static final long FEC_2_5 = Constants.FrontendInnerFec.FEC_2_5; + /** + * 2/9 conv. code rate. + */ + public static final long FEC_2_9 = Constants.FrontendInnerFec.FEC_2_9; + /** + * 3/4 conv. code rate. + */ + public static final long FEC_3_4 = Constants.FrontendInnerFec.FEC_3_4; + /** + * 3/5 conv. code rate. + */ + public static final long FEC_3_5 = Constants.FrontendInnerFec.FEC_3_5; + /** + * 4/5 conv. code rate. + */ + public static final long FEC_4_5 = Constants.FrontendInnerFec.FEC_4_5; + /** + * 4/15 conv. code rate. + */ + public static final long FEC_4_15 = Constants.FrontendInnerFec.FEC_4_15; + /** + * 5/6 conv. code rate. + */ + public static final long FEC_5_6 = Constants.FrontendInnerFec.FEC_5_6; + /** + * 5/9 conv. code rate. + */ + public static final long FEC_5_9 = Constants.FrontendInnerFec.FEC_5_9; + /** + * 6/7 conv. code rate. + */ + public static final long FEC_6_7 = Constants.FrontendInnerFec.FEC_6_7; + /** + * 7/8 conv. code rate. + */ + public static final long FEC_7_8 = Constants.FrontendInnerFec.FEC_7_8; + /** + * 7/9 conv. code rate. + */ + public static final long FEC_7_9 = Constants.FrontendInnerFec.FEC_7_9; + /** + * 7/15 conv. code rate. + */ + public static final long FEC_7_15 = Constants.FrontendInnerFec.FEC_7_15; + /** + * 8/9 conv. code rate. + */ + public static final long FEC_8_9 = Constants.FrontendInnerFec.FEC_8_9; + /** + * 8/15 conv. code rate. + */ + public static final long FEC_8_15 = Constants.FrontendInnerFec.FEC_8_15; + /** + * 9/10 conv. code rate. + */ + public static final long FEC_9_10 = Constants.FrontendInnerFec.FEC_9_10; + /** + * 9/20 conv. code rate. + */ + public static final long FEC_9_20 = Constants.FrontendInnerFec.FEC_9_20; + /** + * 11/15 conv. code rate. + */ + public static final long FEC_11_15 = Constants.FrontendInnerFec.FEC_11_15; + /** + * 11/20 conv. code rate. + */ + public static final long FEC_11_20 = Constants.FrontendInnerFec.FEC_11_20; + /** + * 11/45 conv. code rate. + */ + public static final long FEC_11_45 = Constants.FrontendInnerFec.FEC_11_45; + /** + * 13/18 conv. code rate. + */ + public static final long FEC_13_18 = Constants.FrontendInnerFec.FEC_13_18; + /** + * 13/45 conv. code rate. + */ + public static final long FEC_13_45 = Constants.FrontendInnerFec.FEC_13_45; + /** + * 14/45 conv. code rate. + */ + public static final long FEC_14_45 = Constants.FrontendInnerFec.FEC_14_45; + /** + * 23/36 conv. code rate. + */ + public static final long FEC_23_36 = Constants.FrontendInnerFec.FEC_23_36; + /** + * 25/36 conv. code rate. + */ + public static final long FEC_25_36 = Constants.FrontendInnerFec.FEC_25_36; + /** + * 26/45 conv. code rate. + */ + public static final long FEC_26_45 = Constants.FrontendInnerFec.FEC_26_45; + /** + * 28/45 conv. code rate. + */ + public static final long FEC_28_45 = Constants.FrontendInnerFec.FEC_28_45; + /** + * 29/45 conv. code rate. + */ + public static final long FEC_29_45 = Constants.FrontendInnerFec.FEC_29_45; + /** + * 31/45 conv. code rate. + */ + public static final long FEC_31_45 = Constants.FrontendInnerFec.FEC_31_45; + /** + * 32/45 conv. code rate. + */ + public static final long FEC_32_45 = Constants.FrontendInnerFec.FEC_32_45; + /** + * 77/90 conv. code rate. + */ + public static final long FEC_77_90 = Constants.FrontendInnerFec.FEC_77_90; + + + + private final int mFrequency; + + FrontendSettings(int frequency) { mFrequency = frequency; } diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java index 088adff630a4..c1b17d3615bb 100644 --- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java +++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java @@ -18,19 +18,19 @@ package android.media.tv.tuner.frontend; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.hardware.tv.tuner.V1_0.Constants; import android.media.tv.tuner.Lnb; -import android.media.tv.tuner.TunerConstants.FrontendInnerFec; -import android.media.tv.tuner.TunerConstants.FrontendModulation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * Frontend status + * Frontend status. * * @hide */ +@SystemApi public class FrontendStatus { /** @hide */ @@ -160,6 +160,52 @@ public class FrontendStatus { Constants.FrontendStatusType.ATSC3_PLP_INFO; + /** @hide */ + @IntDef(value = { + DvbcFrontendSettings.MODULATION_UNDEFINED, + DvbcFrontendSettings.MODULATION_AUTO, + DvbcFrontendSettings.MODULATION_MOD_16QAM, + DvbcFrontendSettings.MODULATION_MOD_32QAM, + DvbcFrontendSettings.MODULATION_MOD_64QAM, + DvbcFrontendSettings.MODULATION_MOD_128QAM, + DvbcFrontendSettings.MODULATION_MOD_256QAM, + DvbsFrontendSettings.MODULATION_UNDEFINED, + DvbsFrontendSettings.MODULATION_AUTO, + DvbsFrontendSettings.MODULATION_MOD_QPSK, + DvbsFrontendSettings.MODULATION_MOD_8PSK, + DvbsFrontendSettings.MODULATION_MOD_16QAM, + DvbsFrontendSettings.MODULATION_MOD_16PSK, + DvbsFrontendSettings.MODULATION_MOD_32PSK, + DvbsFrontendSettings.MODULATION_MOD_ACM, + DvbsFrontendSettings.MODULATION_MOD_8APSK, + DvbsFrontendSettings.MODULATION_MOD_16APSK, + DvbsFrontendSettings.MODULATION_MOD_32APSK, + DvbsFrontendSettings.MODULATION_MOD_64APSK, + DvbsFrontendSettings.MODULATION_MOD_128APSK, + DvbsFrontendSettings.MODULATION_MOD_256APSK, + DvbsFrontendSettings.MODULATION_MOD_RESERVED, + IsdbsFrontendSettings.MODULATION_UNDEFINED, + IsdbsFrontendSettings.MODULATION_AUTO, + IsdbsFrontendSettings.MODULATION_MOD_BPSK, + IsdbsFrontendSettings.MODULATION_MOD_QPSK, + IsdbsFrontendSettings.MODULATION_MOD_TC8PSK, + Isdbs3FrontendSettings.MODULATION_UNDEFINED, + Isdbs3FrontendSettings.MODULATION_AUTO, + Isdbs3FrontendSettings.MODULATION_MOD_BPSK, + Isdbs3FrontendSettings.MODULATION_MOD_QPSK, + Isdbs3FrontendSettings.MODULATION_MOD_8PSK, + Isdbs3FrontendSettings.MODULATION_MOD_16APSK, + Isdbs3FrontendSettings.MODULATION_MOD_32APSK, + IsdbtFrontendSettings.MODULATION_UNDEFINED, + IsdbtFrontendSettings.MODULATION_AUTO, + IsdbtFrontendSettings.MODULATION_MOD_DQPSK, + IsdbtFrontendSettings.MODULATION_MOD_QPSK, + IsdbtFrontendSettings.MODULATION_MOD_16QAM, + IsdbtFrontendSettings.MODULATION_MOD_64QAM}) + @Retention(RetentionPolicy.SOURCE) + public @interface FrontendModulation {} + + private Boolean mIsDemodLocked; private Integer mSnr; private Integer mBer; @@ -273,7 +319,7 @@ public class FrontendStatus { * Gets Inner Forward Error Correction type as specified in ETSI EN 300 468 V1.15.1 * and ETSI EN 302 307-2 V1.1.1. */ - @FrontendInnerFec + @FrontendSettings.InnerFec public long getFec() { if (mInnerFec == null) { throw new IllegalStateException(); diff --git a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendCapabilities.java index 61cba1c1e4f3..573f3798c8ae 100644 --- a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendCapabilities.java +++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendCapabilities.java @@ -16,17 +16,21 @@ package android.media.tv.tuner.frontend; +import android.annotation.SystemApi; + /** * ISDBS-3 Capabilities. + * * @hide */ +@SystemApi public class Isdbs3FrontendCapabilities extends FrontendCapabilities { private final int mModulationCap; - private final int mCoderateCap; + private final int mCodeRateCap; - private Isdbs3FrontendCapabilities(int modulationCap, int coderateCap) { + private Isdbs3FrontendCapabilities(int modulationCap, int codeRateCap) { mModulationCap = modulationCap; - mCoderateCap = coderateCap; + mCodeRateCap = codeRateCap; } /** @@ -39,8 +43,8 @@ public class Isdbs3FrontendCapabilities extends FrontendCapabilities { /** * Gets code rate capability. */ - @Isdbs3FrontendSettings.Coderate + @Isdbs3FrontendSettings.CodeRate public int getCodeRateCapability() { - return mCoderateCap; + return mCodeRateCap; } } diff --git a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java index a83d771af1a2..9b0e53351f59 100644 --- a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java @@ -19,6 +19,7 @@ package android.media.tv.tuner.frontend; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.content.Context; import android.hardware.tv.tuner.V1_0.Constants; import android.media.tv.tuner.TunerUtils; @@ -28,8 +29,10 @@ import java.lang.annotation.RetentionPolicy; /** * Frontend settings for ISDBS-3. + * * @hide */ +@SystemApi public class Isdbs3FrontendSettings extends FrontendSettings { /** @hide */ @IntDef(flag = true, @@ -76,7 +79,7 @@ public class Isdbs3FrontendSettings extends FrontendSettings { value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_1_3, CODERATE_2_5, CODERATE_1_2, CODERATE_3_5, CODERATE_2_3, CODERATE_3_4, CODERATE_7_9, CODERATE_4_5, CODERATE_5_6, CODERATE_7_8, CODERATE_9_10}) - public @interface Coderate {} + public @interface CodeRate {} /** * Code rate undefined. @@ -150,17 +153,17 @@ public class Isdbs3FrontendSettings extends FrontendSettings { private final int mStreamId; private final int mStreamIdType; private final int mModulation; - private final int mCoderate; + private final int mCodeRate; private final int mSymbolRate; private final int mRolloff; private Isdbs3FrontendSettings(int frequency, int streamId, int streamIdType, int modulation, - int coderate, int symbolRate, int rolloff) { + int codeRate, int symbolRate, int rolloff) { super(frequency); mStreamId = streamId; mStreamIdType = streamIdType; mModulation = modulation; - mCoderate = coderate; + mCodeRate = codeRate; mSymbolRate = symbolRate; mRolloff = rolloff; } @@ -188,9 +191,9 @@ public class Isdbs3FrontendSettings extends FrontendSettings { /** * Gets Code rate. */ - @Coderate - public int getCoderate() { - return mCoderate; + @CodeRate + public int getCodeRate() { + return mCodeRate; } /** * Gets Symbol Rate in symbols per second. @@ -225,7 +228,7 @@ public class Isdbs3FrontendSettings extends FrontendSettings { private int mStreamId; private int mStreamIdType; private int mModulation; - private int mCoderate; + private int mCodeRate; private int mSymbolRate; private int mRolloff; @@ -260,8 +263,8 @@ public class Isdbs3FrontendSettings extends FrontendSettings { * Sets Code rate. */ @NonNull - public Builder setCoderate(@Coderate int coderate) { - mCoderate = coderate; + public Builder setCodeRate(@CodeRate int codeRate) { + mCodeRate = codeRate; return this; } /** @@ -287,7 +290,7 @@ public class Isdbs3FrontendSettings extends FrontendSettings { @NonNull public Isdbs3FrontendSettings build() { return new Isdbs3FrontendSettings(mFrequency, mStreamId, mStreamIdType, mModulation, - mCoderate, mSymbolRate, mRolloff); + mCodeRate, mSymbolRate, mRolloff); } @Override diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendCapabilities.java index 8e5ecc4c3f59..38d2f1de5cc9 100644 --- a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendCapabilities.java +++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendCapabilities.java @@ -16,17 +16,21 @@ package android.media.tv.tuner.frontend; +import android.annotation.SystemApi; + /** * ISDBS Capabilities. + * * @hide */ +@SystemApi public class IsdbsFrontendCapabilities extends FrontendCapabilities { private final int mModulationCap; - private final int mCoderateCap; + private final int mCodeRateCap; - private IsdbsFrontendCapabilities(int modulationCap, int coderateCap) { + private IsdbsFrontendCapabilities(int modulationCap, int codeRateCap) { mModulationCap = modulationCap; - mCoderateCap = coderateCap; + mCodeRateCap = codeRateCap; } /** @@ -39,8 +43,8 @@ public class IsdbsFrontendCapabilities extends FrontendCapabilities { /** * Gets code rate capability. */ - @IsdbsFrontendSettings.Coderate + @IsdbsFrontendSettings.CodeRate public int getCodeRateCapability() { - return mCoderateCap; + return mCodeRateCap; } } diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java index bb809bf8d455..14c08b1af582 100644 --- a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java @@ -19,6 +19,7 @@ package android.media.tv.tuner.frontend; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.content.Context; import android.hardware.tv.tuner.V1_0.Constants; import android.media.tv.tuner.TunerUtils; @@ -28,8 +29,10 @@ import java.lang.annotation.RetentionPolicy; /** * Frontend settings for ISDBS. + * * @hide */ +@SystemApi public class IsdbsFrontendSettings extends FrontendSettings { /** @hide */ @Retention(RetentionPolicy.SOURCE) @@ -81,11 +84,10 @@ public class IsdbsFrontendSettings extends FrontendSettings { /** @hide */ @IntDef(flag = true, prefix = "CODERATE_", - value = {CODERATE_UNDEFINED, CODERATE_AUTO, - CODERATE_1_2, CODERATE_2_3, CODERATE_3_4, - CODERATE_5_6, CODERATE_7_8}) + value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_1_2, CODERATE_2_3, CODERATE_3_4, + CODERATE_5_6, CODERATE_7_8}) @Retention(RetentionPolicy.SOURCE) - public @interface Coderate {} + public @interface CodeRate {} /** * Code rate undefined. @@ -125,7 +127,7 @@ public class IsdbsFrontendSettings extends FrontendSettings { /** * Rolloff type undefined. */ - public static final int ROLLOFF_UNDEFINED = Constants.FrontendIsdbs3Rolloff.UNDEFINED; + public static final int ROLLOFF_UNDEFINED = Constants.FrontendIsdbsRolloff.UNDEFINED; /** * 0,35 rolloff. */ @@ -135,17 +137,17 @@ public class IsdbsFrontendSettings extends FrontendSettings { private final int mStreamId; private final int mStreamIdType; private final int mModulation; - private final int mCoderate; + private final int mCodeRate; private final int mSymbolRate; private final int mRolloff; private IsdbsFrontendSettings(int frequency, int streamId, int streamIdType, int modulation, - int coderate, int symbolRate, int rolloff) { + int codeRate, int symbolRate, int rolloff) { super(frequency); mStreamId = streamId; mStreamIdType = streamIdType; mModulation = modulation; - mCoderate = coderate; + mCodeRate = codeRate; mSymbolRate = symbolRate; mRolloff = rolloff; } @@ -173,9 +175,9 @@ public class IsdbsFrontendSettings extends FrontendSettings { /** * Gets Code rate. */ - @Coderate - public int getCoderate() { - return mCoderate; + @CodeRate + public int getCodeRate() { + return mCodeRate; } /** * Gets Symbol Rate in symbols per second. @@ -210,7 +212,7 @@ public class IsdbsFrontendSettings extends FrontendSettings { private int mStreamId; private int mStreamIdType; private int mModulation; - private int mCoderate; + private int mCodeRate; private int mSymbolRate; private int mRolloff; @@ -245,8 +247,8 @@ public class IsdbsFrontendSettings extends FrontendSettings { * Sets Code rate. */ @NonNull - public Builder setCoderate(@Coderate int coderate) { - mCoderate = coderate; + public Builder setCodeRate(@CodeRate int codeRate) { + mCodeRate = codeRate; return this; } /** @@ -272,7 +274,7 @@ public class IsdbsFrontendSettings extends FrontendSettings { @NonNull public IsdbsFrontendSettings build() { return new IsdbsFrontendSettings(mFrequency, mStreamId, mStreamIdType, mModulation, - mCoderate, mSymbolRate, mRolloff); + mCodeRate, mSymbolRate, mRolloff); } @Override diff --git a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendCapabilities.java index 19f04de6ef39..ffebc5a668c5 100644 --- a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendCapabilities.java +++ b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendCapabilities.java @@ -16,23 +16,27 @@ package android.media.tv.tuner.frontend; +import android.annotation.SystemApi; + /** * ISDBT Capabilities. + * * @hide */ +@SystemApi public class IsdbtFrontendCapabilities extends FrontendCapabilities { private final int mModeCap; private final int mBandwidthCap; private final int mModulationCap; - private final int mCoderateCap; + private final int mCodeRateCap; private final int mGuardIntervalCap; private IsdbtFrontendCapabilities(int modeCap, int bandwidthCap, int modulationCap, - int coderateCap, int guardIntervalCap) { + int codeRateCap, int guardIntervalCap) { mModeCap = modeCap; mBandwidthCap = bandwidthCap; mModulationCap = modulationCap; - mCoderateCap = coderateCap; + mCodeRateCap = codeRateCap; mGuardIntervalCap = guardIntervalCap; } @@ -60,9 +64,9 @@ public class IsdbtFrontendCapabilities extends FrontendCapabilities { /** * Gets code rate capability. */ - @DvbtFrontendSettings.Coderate + @DvbtFrontendSettings.CodeRate public int getCodeRateCapability() { - return mCoderateCap; + return mCodeRateCap; } /** * Gets guard interval capability. diff --git a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java index 15101930e440..de3c80dda440 100644 --- a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java @@ -19,17 +19,21 @@ package android.media.tv.tuner.frontend; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.content.Context; import android.hardware.tv.tuner.V1_0.Constants; import android.media.tv.tuner.TunerUtils; +import android.media.tv.tuner.frontend.DvbtFrontendSettings.CodeRate; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * Frontend settings for ISDBT. + * * @hide */ +@SystemApi public class IsdbtFrontendSettings extends FrontendSettings { /** @hide */ @IntDef(flag = true, @@ -125,16 +129,18 @@ public class IsdbtFrontendSettings extends FrontendSettings { private final int mModulation; private final int mBandwidth; - private final int mCoderate; + private final int mMode; + private final int mCodeRate; private final int mGuardInterval; private final int mServiceAreaId; - private IsdbtFrontendSettings(int frequency, int modulation, int bandwidth, int coderate, - int guardInterval, int serviceAreaId) { + private IsdbtFrontendSettings(int frequency, int modulation, int bandwidth, int mode, + int codeRate, int guardInterval, int serviceAreaId) { super(frequency); mModulation = modulation; mBandwidth = bandwidth; - mCoderate = coderate; + mMode = mode; + mCodeRate = codeRate; mGuardInterval = guardInterval; mServiceAreaId = serviceAreaId; } @@ -154,11 +160,18 @@ public class IsdbtFrontendSettings extends FrontendSettings { return mBandwidth; } /** + * Gets ISDBT mode. + */ + @Mode + public int getMode() { + return mMode; + } + /** * Gets Code rate. */ - @DvbtFrontendSettings.Coderate - public int getCoderate() { - return mCoderate; + @CodeRate + public int getCodeRate() { + return mCodeRate; } /** * Gets Guard Interval. @@ -192,7 +205,8 @@ public class IsdbtFrontendSettings extends FrontendSettings { public static class Builder extends FrontendSettings.Builder<Builder> { private int mModulation; private int mBandwidth; - private int mCoderate; + private int mMode; + private int mCodeRate; private int mGuardInterval; private int mServiceAreaId; @@ -216,11 +230,19 @@ public class IsdbtFrontendSettings extends FrontendSettings { return this; } /** + * Sets ISDBT mode. + */ + @NonNull + public Builder setMode(@Mode int mode) { + mMode = mode; + return this; + } + /** * Sets Code rate. */ @NonNull - public Builder setCoderate(@DvbtFrontendSettings.Coderate int coderate) { - mCoderate = coderate; + public Builder setCodeRate(@CodeRate int codeRate) { + mCodeRate = codeRate; return this; } /** @@ -245,8 +267,8 @@ public class IsdbtFrontendSettings extends FrontendSettings { */ @NonNull public IsdbtFrontendSettings build() { - return new IsdbtFrontendSettings( - mFrequency, mModulation, mBandwidth, mCoderate, mGuardInterval, mServiceAreaId); + return new IsdbtFrontendSettings(mFrequency, mModulation, mBandwidth, mMode, mCodeRate, + mGuardInterval, mServiceAreaId); } @Override 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/jni/Android.bp b/media/jni/Android.bp index aeacd8f63cb0..c17b1b773bd5 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -1,6 +1,8 @@ cc_library_shared { name: "libmedia_jni", + defaults: ["libcodec2-internal-defaults"], + srcs: [ "android_media_ImageWriter.cpp", "android_media_ImageReader.cpp", diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 20980a90e9fe..ab6966d5d1c3 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -18,6 +18,8 @@ #define LOG_TAG "MediaCodec-JNI" #include <utils/Log.h> +#include <type_traits> + #include "android_media_MediaCodec.h" #include "android_media_MediaCrypto.h" @@ -31,13 +33,20 @@ #include <nativehelper/JNIHelp.h> #include <nativehelper/ScopedLocalRef.h> +#include <C2Buffer.h> + #include <android/hardware/cas/native/1.0/IDescrambler.h> +#include <binder/MemoryHeapBase.h> + #include <cutils/compiler.h> #include <gui/Surface.h> +#include <hidlmemory/FrameworkUtils.h> + #include <media/MediaCodecBuffer.h> +#include <media/hardware/VideoAPI.h> #include <media/stagefright/MediaCodec.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> @@ -47,7 +56,6 @@ #include <media/stagefright/MediaErrors.h> #include <media/stagefright/PersistentSurface.h> #include <mediadrm/ICrypto.h> -#include <nativehelper/ScopedLocalRef.h> #include <system/window.h> @@ -110,6 +118,39 @@ static struct { jfieldID levelField; } gCodecInfo; +static struct { + jclass clazz; + jobject nativeByteOrder; + jmethodID orderId; + jmethodID asReadOnlyBufferId; + jmethodID positionId; + jmethodID limitId; +} gByteBufferInfo; + +static struct { + jmethodID sizeId; + jmethodID getId; + jmethodID addId; +} gArrayListInfo; + +static struct { + jclass clazz; + jmethodID ctorId; + jmethodID setInternalStateId; + jfieldID contextId; + jfieldID validId; + jfieldID lockId; +} gLinearBlockInfo; + +static struct { + jclass clazz; + jmethodID ctorId; + jmethodID setInternalStateId; + jfieldID contextId; + jfieldID validId; + jfieldID lockId; +} gGraphicBlockInfo; + struct fields_t { jmethodID postEventFromNativeID; jmethodID lockAndGetContextID; @@ -123,11 +164,65 @@ struct fields_t { jfieldID cryptoInfoPatternID; jfieldID patternEncryptBlocksID; jfieldID patternSkipBlocksID; + jfieldID queueRequestIndexID; + jfieldID outputFrameLinearBlockID; + jfieldID outputFrameGraphicBlockID; + jfieldID outputFrameChangedKeysID; + jfieldID outputFrameFormatID; }; static fields_t gFields; static const void *sRefBaseOwner; +struct JMediaCodecLinearBlock { + std::shared_ptr<C2Buffer> mBuffer; + std::shared_ptr<C2ReadView> mReadonlyMapping; + + std::shared_ptr<C2LinearBlock> mBlock; + std::shared_ptr<C2WriteView> mReadWriteMapping; + + sp<IMemoryHeap> mHeap; + sp<hardware::HidlMemory> mMemory; + + sp<MediaCodecBuffer> mLegacyBuffer; + + std::once_flag mCopyWarningFlag; + + std::shared_ptr<C2Buffer> toC2Buffer(size_t offset, size_t size) { + if (mBuffer) { + if (mBuffer->data().type() != C2BufferData::LINEAR) { + return nullptr; + } + C2ConstLinearBlock block = mBuffer->data().linearBlocks().front(); + if (offset == 0 && size == block.capacity()) { + return mBuffer; + } + return C2Buffer::CreateLinearBuffer(block.subBlock(offset, size)); + } + if (mBlock) { + return C2Buffer::CreateLinearBuffer(mBlock->share(offset, size, C2Fence{})); + } + return nullptr; + } + + sp<hardware::HidlMemory> toHidlMemory() { + if (mMemory) { + return mMemory; + } + return nullptr; + } +}; + +struct JMediaCodecGraphicBlock { + std::shared_ptr<C2Buffer> mBuffer; + std::shared_ptr<const C2GraphicView> mReadonlyMapping; + + std::shared_ptr<C2GraphicBlock> mBlock; + std::shared_ptr<C2GraphicView> mReadWriteMapping; + + sp<MediaCodecBuffer> mLegacyBuffer; +}; + //////////////////////////////////////////////////////////////////////////////// JMediaCodec::JMediaCodec( @@ -141,8 +236,6 @@ JMediaCodec::JMediaCodec( mClass = (jclass)env->NewGlobalRef(clazz); mObject = env->NewWeakGlobalRef(thiz); - cacheJavaObjects(env); - mLooper = new ALooper; mLooper->setName("MediaCodec_looper"); @@ -163,45 +256,6 @@ JMediaCodec::JMediaCodec( CHECK((mCodec != NULL) != (mInitStatus != OK)); } -void JMediaCodec::cacheJavaObjects(JNIEnv *env) { - jclass clazz = (jclass)env->FindClass("java/nio/ByteBuffer"); - mByteBufferClass = (jclass)env->NewGlobalRef(clazz); - CHECK(mByteBufferClass != NULL); - - ScopedLocalRef<jclass> byteOrderClass( - env, env->FindClass("java/nio/ByteOrder")); - CHECK(byteOrderClass.get() != NULL); - - jmethodID nativeOrderID = env->GetStaticMethodID( - byteOrderClass.get(), "nativeOrder", "()Ljava/nio/ByteOrder;"); - CHECK(nativeOrderID != NULL); - - jobject nativeByteOrderObj = - env->CallStaticObjectMethod(byteOrderClass.get(), nativeOrderID); - mNativeByteOrderObj = env->NewGlobalRef(nativeByteOrderObj); - CHECK(mNativeByteOrderObj != NULL); - env->DeleteLocalRef(nativeByteOrderObj); - nativeByteOrderObj = NULL; - - mByteBufferOrderMethodID = env->GetMethodID( - mByteBufferClass, - "order", - "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;"); - CHECK(mByteBufferOrderMethodID != NULL); - - mByteBufferAsReadOnlyBufferMethodID = env->GetMethodID( - mByteBufferClass, "asReadOnlyBuffer", "()Ljava/nio/ByteBuffer;"); - CHECK(mByteBufferAsReadOnlyBufferMethodID != NULL); - - mByteBufferPositionMethodID = env->GetMethodID( - mByteBufferClass, "position", "(I)Ljava/nio/Buffer;"); - CHECK(mByteBufferPositionMethodID != NULL); - - mByteBufferLimitMethodID = env->GetMethodID( - mByteBufferClass, "limit", "(I)Ljava/nio/Buffer;"); - CHECK(mByteBufferLimitMethodID != NULL); -} - status_t JMediaCodec::initCheck() const { return mInitStatus; } @@ -247,19 +301,6 @@ JMediaCodec::~JMediaCodec() { mObject = NULL; env->DeleteGlobalRef(mClass); mClass = NULL; - deleteJavaObjects(env); -} - -void JMediaCodec::deleteJavaObjects(JNIEnv *env) { - env->DeleteGlobalRef(mByteBufferClass); - mByteBufferClass = NULL; - env->DeleteGlobalRef(mNativeByteOrderObj); - mNativeByteOrderObj = NULL; - - mByteBufferOrderMethodID = NULL; - mByteBufferAsReadOnlyBufferMethodID = NULL; - mByteBufferPositionMethodID = NULL; - mByteBufferLimitMethodID = NULL; } status_t JMediaCodec::enableOnFrameRenderedListener(jboolean enable) { @@ -300,6 +341,12 @@ status_t JMediaCodec::configure( mSurfaceTextureClient.clear(); } + constexpr int32_t CONFIGURE_FLAG_ENCODE = 1; + AString mime; + CHECK(format->findString("mime", &mime)); + mGraphicOutput = (mime.startsWithIgnoreCase("video/") || mime.startsWithIgnoreCase("image/")) + && !(flags & CONFIGURE_FLAG_ENCODE); + return mCodec->configure( format, mSurfaceTextureClient, crypto, descrambler, flags); } @@ -370,6 +417,32 @@ status_t JMediaCodec::queueSecureInputBuffer( presentationTimeUs, flags, errorDetailMsg); } +status_t JMediaCodec::queueBuffer( + size_t index, const std::shared_ptr<C2Buffer> &buffer, int64_t timeUs, + uint32_t flags, const sp<AMessage> &tunings, AString *errorDetailMsg) { + return mCodec->queueBuffer( + index, buffer, timeUs, flags, tunings, errorDetailMsg); +} + +status_t JMediaCodec::queueEncryptedLinearBlock( + size_t index, + const sp<hardware::HidlMemory> &buffer, + size_t offset, + const CryptoPlugin::SubSample *subSamples, + size_t numSubSamples, + const uint8_t key[16], + const uint8_t iv[16], + CryptoPlugin::Mode mode, + const CryptoPlugin::Pattern &pattern, + int64_t presentationTimeUs, + uint32_t flags, + const sp<AMessage> &tunings, + AString *errorDetailMsg) { + return mCodec->queueEncryptedBuffer( + index, buffer, offset, subSamples, numSubSamples, key, iv, mode, pattern, + presentationTimeUs, flags, tunings, errorDetailMsg); +} + status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) { return mCodec->dequeueInputBuffer(index, timeoutUs); } @@ -444,7 +517,7 @@ status_t JMediaCodec::getBuffers( } *bufArray = (jobjectArray)env->NewObjectArray( - buffers.size(), mByteBufferClass, NULL); + buffers.size(), gByteBufferInfo.clazz, NULL); if (*bufArray == NULL) { return NO_MEMORY; } @@ -470,6 +543,39 @@ status_t JMediaCodec::getBuffers( return OK; } +template <typename T> +static jobject CreateByteBuffer( + JNIEnv *env, T *base, size_t capacity, size_t offset, size_t size, + bool readOnly, bool clearBuffer) { + jobject byteBuffer = + env->NewDirectByteBuffer( + const_cast<typename std::remove_const<T>::type *>(base), + capacity); + if (readOnly && byteBuffer != NULL) { + jobject readOnlyBuffer = env->CallObjectMethod( + byteBuffer, gByteBufferInfo.asReadOnlyBufferId); + env->DeleteLocalRef(byteBuffer); + byteBuffer = readOnlyBuffer; + } + if (byteBuffer == NULL) { + return nullptr; + } + jobject me = env->CallObjectMethod( + byteBuffer, gByteBufferInfo.orderId, gByteBufferInfo.nativeByteOrder); + env->DeleteLocalRef(me); + me = env->CallObjectMethod( + byteBuffer, gByteBufferInfo.limitId, + clearBuffer ? capacity : offset + size); + env->DeleteLocalRef(me); + me = env->CallObjectMethod( + byteBuffer, gByteBufferInfo.positionId, + clearBuffer ? 0 : offset); + env->DeleteLocalRef(me); + me = NULL; + return byteBuffer; +} + + // static template <typename T> status_t JMediaCodec::createByteBufferFromABuffer( @@ -488,29 +594,9 @@ status_t JMediaCodec::createByteBufferFromABuffer( return OK; } - jobject byteBuffer = - env->NewDirectByteBuffer(buffer->base(), buffer->capacity()); - if (readOnly && byteBuffer != NULL) { - jobject readOnlyBuffer = env->CallObjectMethod( - byteBuffer, mByteBufferAsReadOnlyBufferMethodID); - env->DeleteLocalRef(byteBuffer); - byteBuffer = readOnlyBuffer; - } - if (byteBuffer == NULL) { - return NO_MEMORY; - } - jobject me = env->CallObjectMethod( - byteBuffer, mByteBufferOrderMethodID, mNativeByteOrderObj); - env->DeleteLocalRef(me); - me = env->CallObjectMethod( - byteBuffer, mByteBufferLimitMethodID, - clearBuffer ? buffer->capacity() : (buffer->offset() + buffer->size())); - env->DeleteLocalRef(me); - me = env->CallObjectMethod( - byteBuffer, mByteBufferPositionMethodID, - clearBuffer ? 0 : buffer->offset()); - env->DeleteLocalRef(me); - me = NULL; + jobject byteBuffer = CreateByteBuffer( + env, buffer->base(), buffer->capacity(), buffer->offset(), buffer->size(), + readOnly, clearBuffer); *buf = byteBuffer; return OK; @@ -628,6 +714,92 @@ status_t JMediaCodec::getImage( return OK; } +status_t JMediaCodec::getOutputFrame( + JNIEnv *env, jobject frame, size_t index) const { + sp<MediaCodecBuffer> buffer; + + status_t err = mCodec->getOutputBuffer(index, &buffer); + if (err != OK) { + return err; + } + + if (buffer->size() > 0) { + // asC2Buffer clears internal reference, so set the reference again. + std::shared_ptr<C2Buffer> c2Buffer = buffer->asC2Buffer(); + buffer->copy(c2Buffer); + if (c2Buffer) { + switch (c2Buffer->data().type()) { + case C2BufferData::LINEAR: { + std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock}; + context->mBuffer = c2Buffer; + ScopedLocalRef<jobject> linearBlock{env, env->NewObject( + gLinearBlockInfo.clazz, gLinearBlockInfo.ctorId)}; + env->SetLongField( + linearBlock.get(), gLinearBlockInfo.contextId, (jlong)context.release()); + env->SetObjectField(frame, gFields.outputFrameLinearBlockID, linearBlock.get()); + break; + } + case C2BufferData::GRAPHIC: { + std::unique_ptr<JMediaCodecGraphicBlock> context{new JMediaCodecGraphicBlock}; + context->mBuffer = c2Buffer; + ScopedLocalRef<jobject> graphicBlock{env, env->NewObject( + gGraphicBlockInfo.clazz, gGraphicBlockInfo.ctorId)}; + env->SetLongField( + graphicBlock.get(), gGraphicBlockInfo.contextId, (jlong)context.release()); + env->SetObjectField(frame, gFields.outputFrameGraphicBlockID, graphicBlock.get()); + break; + } + case C2BufferData::LINEAR_CHUNKS: [[fallthrough]]; + case C2BufferData::GRAPHIC_CHUNKS: [[fallthrough]]; + case C2BufferData::INVALID: [[fallthrough]]; + default: + return INVALID_OPERATION; + } + } else { + if (!mGraphicOutput) { + std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock}; + context->mLegacyBuffer = buffer; + ScopedLocalRef<jobject> linearBlock{env, env->NewObject( + gLinearBlockInfo.clazz, gLinearBlockInfo.ctorId)}; + env->SetLongField( + linearBlock.get(), gLinearBlockInfo.contextId, (jlong)context.release()); + env->SetObjectField(frame, gFields.outputFrameLinearBlockID, linearBlock.get()); + } else { + std::unique_ptr<JMediaCodecGraphicBlock> context{new JMediaCodecGraphicBlock}; + context->mLegacyBuffer = buffer; + ScopedLocalRef<jobject> graphicBlock{env, env->NewObject( + gGraphicBlockInfo.clazz, gGraphicBlockInfo.ctorId)}; + env->SetLongField( + graphicBlock.get(), gGraphicBlockInfo.contextId, (jlong)context.release()); + env->SetObjectField(frame, gFields.outputFrameGraphicBlockID, graphicBlock.get()); + } + } + } + + jobject format; + err = getOutputFormat(env, index, &format); + if (err != OK) { + return err; + } + env->SetObjectField(frame, gFields.outputFrameFormatID, format); + env->DeleteLocalRef(format); + format = nullptr; + + sp<RefBase> obj; + if (buffer->meta()->findObject("changedKeys", &obj) && obj) { + sp<MediaCodec::WrapperObject<std::set<std::string>>> changedKeys{ + (decltype(changedKeys.get()))obj.get()}; + ScopedLocalRef<jobject> changedKeysObj{env, env->GetObjectField( + frame, gFields.outputFrameChangedKeysID)}; + for (const std::string &key : changedKeys->value) { + ScopedLocalRef<jstring> keyStr{env, env->NewStringUTF(key.c_str())}; + (void)env->CallBooleanMethod(changedKeysObj.get(), gArrayListInfo.addId, keyStr.get()); + } + } + return OK; +} + + status_t JMediaCodec::getName(JNIEnv *env, jstring *nameStr) const { AString name; @@ -1428,6 +1600,139 @@ static void android_media_MediaCodec_queueInputBuffer( env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str()); } +struct NativeCryptoInfo { + NativeCryptoInfo(JNIEnv *env, jobject cryptoInfoObj) + : mEnv{env}, + mIvObj{env, (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID)}, + mKeyObj{env, (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID)} { + mNumSubSamples = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID); + + ScopedLocalRef<jintArray> numBytesOfClearDataObj{env, (jintArray)env->GetObjectField( + cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID)}; + + ScopedLocalRef<jintArray> numBytesOfEncryptedDataObj{env, (jintArray)env->GetObjectField( + cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID)}; + + jint jmode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID); + if (jmode == gCryptoModes.Unencrypted) { + mMode = CryptoPlugin::kMode_Unencrypted; + } else if (jmode == gCryptoModes.AesCtr) { + mMode = CryptoPlugin::kMode_AES_CTR; + } else if (jmode == gCryptoModes.AesCbc) { + mMode = CryptoPlugin::kMode_AES_CBC; + } else { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return; + } + + ScopedLocalRef<jobject> patternObj{ + env, env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoPatternID)}; + + CryptoPlugin::Pattern pattern; + if (patternObj.get() == nullptr) { + pattern.mEncryptBlocks = 0; + pattern.mSkipBlocks = 0; + } else { + pattern.mEncryptBlocks = env->GetIntField( + patternObj.get(), gFields.patternEncryptBlocksID); + pattern.mSkipBlocks = env->GetIntField( + patternObj.get(), gFields.patternSkipBlocksID); + } + + mErr = OK; + if (mNumSubSamples <= 0) { + mErr = -EINVAL; + } else if (numBytesOfClearDataObj == nullptr + && numBytesOfEncryptedDataObj == nullptr) { + mErr = -EINVAL; + } else if (numBytesOfEncryptedDataObj != nullptr + && env->GetArrayLength(numBytesOfEncryptedDataObj.get()) < mNumSubSamples) { + mErr = -ERANGE; + } else if (numBytesOfClearDataObj != nullptr + && env->GetArrayLength(numBytesOfClearDataObj.get()) < mNumSubSamples) { + mErr = -ERANGE; + // subSamples array may silently overflow if number of samples are too large. Use + // INT32_MAX as maximum allocation size may be less than SIZE_MAX on some platforms + } else if (CC_UNLIKELY(mNumSubSamples >= (signed)(INT32_MAX / sizeof(*mSubSamples))) ) { + mErr = -EINVAL; + } else { + jint *numBytesOfClearData = + (numBytesOfClearDataObj == nullptr) + ? nullptr + : env->GetIntArrayElements(numBytesOfClearDataObj.get(), nullptr); + + jint *numBytesOfEncryptedData = + (numBytesOfEncryptedDataObj == nullptr) + ? nullptr + : env->GetIntArrayElements(numBytesOfEncryptedDataObj.get(), nullptr); + + mSubSamples = new CryptoPlugin::SubSample[mNumSubSamples]; + + for (jint i = 0; i < mNumSubSamples; ++i) { + mSubSamples[i].mNumBytesOfClearData = + (numBytesOfClearData == nullptr) ? 0 : numBytesOfClearData[i]; + + mSubSamples[i].mNumBytesOfEncryptedData = + (numBytesOfEncryptedData == nullptr) ? 0 : numBytesOfEncryptedData[i]; + } + + if (numBytesOfEncryptedData != nullptr) { + env->ReleaseIntArrayElements( + numBytesOfEncryptedDataObj.get(), numBytesOfEncryptedData, 0); + numBytesOfEncryptedData = nullptr; + } + + if (numBytesOfClearData != nullptr) { + env->ReleaseIntArrayElements( + numBytesOfClearDataObj.get(), numBytesOfClearData, 0); + numBytesOfClearData = nullptr; + } + } + + if (mErr == OK && mKeyObj.get() != nullptr) { + if (env->GetArrayLength(mKeyObj.get()) != 16) { + mErr = -EINVAL; + } else { + mKey = env->GetByteArrayElements(mKeyObj.get(), nullptr); + } + } + + if (mErr == OK && mIvObj.get() != nullptr) { + if (env->GetArrayLength(mIvObj.get()) != 16) { + mErr = -EINVAL; + } else { + mIv = env->GetByteArrayElements(mIvObj.get(), nullptr); + } + } + } + + ~NativeCryptoInfo() { + if (mIv != nullptr) { + mEnv->ReleaseByteArrayElements(mIvObj.get(), mIv, 0); + } + + if (mKey != nullptr) { + mEnv->ReleaseByteArrayElements(mKeyObj.get(), mKey, 0); + } + + if (mSubSamples != nullptr) { + delete[] mSubSamples; + } + } + + JNIEnv *mEnv{nullptr}; + ScopedLocalRef<jbyteArray> mIvObj; + ScopedLocalRef<jbyteArray> mKeyObj; + status_t mErr{OK}; + + CryptoPlugin::SubSample *mSubSamples{nullptr}; + int32_t mNumSubSamples{0}; + jbyte *mIv{nullptr}; + jbyte *mKey{nullptr}; + enum CryptoPlugin::Mode mMode; + CryptoPlugin::Pattern mPattern; +}; + static void android_media_MediaCodec_queueSecureInputBuffer( JNIEnv *env, jobject thiz, @@ -1445,152 +1750,274 @@ static void android_media_MediaCodec_queueSecureInputBuffer( return; } - jint numSubSamples = - env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID); - - jintArray numBytesOfClearDataObj = - (jintArray)env->GetObjectField( - cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID); + NativeCryptoInfo cryptoInfo{env, cryptoInfoObj}; + AString errorDetailMsg; - jintArray numBytesOfEncryptedDataObj = - (jintArray)env->GetObjectField( - cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID); + status_t err = cryptoInfo.mErr; + if (err == OK) { + err = codec->queueSecureInputBuffer( + index, offset, + cryptoInfo.mSubSamples, cryptoInfo.mNumSubSamples, + (const uint8_t *)cryptoInfo.mKey, (const uint8_t *)cryptoInfo.mIv, + cryptoInfo.mMode, + cryptoInfo.mPattern, + timestampUs, + flags, + &errorDetailMsg); + } - jbyteArray keyObj = - (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID); + throwExceptionAsNecessary( + env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str()); +} - jbyteArray ivObj = - (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID); +static status_t ConvertKeyValueListsToAMessage( + JNIEnv *env, jobject keys, jobject values, sp<AMessage> *msg) { + static struct Fields { + explicit Fields(JNIEnv *env) { + ScopedLocalRef<jclass> clazz{env, env->FindClass("java/lang/String")}; + CHECK(clazz.get() != NULL); + mStringClass = (jclass)env->NewGlobalRef(clazz.get()); - jint jmode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID); - enum CryptoPlugin::Mode mode; - if (jmode == gCryptoModes.Unencrypted) { - mode = CryptoPlugin::kMode_Unencrypted; - } else if (jmode == gCryptoModes.AesCtr) { - mode = CryptoPlugin::kMode_AES_CTR; - } else if (jmode == gCryptoModes.AesCbc) { - mode = CryptoPlugin::kMode_AES_CBC; - } else { - throwExceptionAsNecessary(env, INVALID_OPERATION); - return; - } + clazz.reset(env->FindClass("java/lang/Integer")); + CHECK(clazz.get() != NULL); + mIntegerClass = (jclass)env->NewGlobalRef(clazz.get()); - jobject patternObj = env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoPatternID); + mIntegerValueId = env->GetMethodID(clazz.get(), "intValue", "()I"); + CHECK(mIntegerValueId != NULL); - CryptoPlugin::Pattern pattern; - if (patternObj == NULL) { - pattern.mEncryptBlocks = 0; - pattern.mSkipBlocks = 0; - } else { - pattern.mEncryptBlocks = env->GetIntField(patternObj, gFields.patternEncryptBlocksID); - pattern.mSkipBlocks = env->GetIntField(patternObj, gFields.patternSkipBlocksID); - } - - status_t err = OK; - - CryptoPlugin::SubSample *subSamples = NULL; - jbyte *key = NULL; - jbyte *iv = NULL; - - if (numSubSamples <= 0) { - err = -EINVAL; - } else if (numBytesOfClearDataObj == NULL - && numBytesOfEncryptedDataObj == NULL) { - err = -EINVAL; - } else if (numBytesOfEncryptedDataObj != NULL - && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) { - err = -ERANGE; - } else if (numBytesOfClearDataObj != NULL - && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) { - err = -ERANGE; - // subSamples array may silently overflow if number of samples are too large. Use - // INT32_MAX as maximum allocation size may be less than SIZE_MAX on some platforms - } else if ( CC_UNLIKELY(numSubSamples >= (signed)(INT32_MAX / sizeof(*subSamples))) ) { - err = -EINVAL; - } else { - jboolean isCopy; + clazz.reset(env->FindClass("java/lang/Long")); + CHECK(clazz.get() != NULL); + mLongClass = (jclass)env->NewGlobalRef(clazz.get()); - jint *numBytesOfClearData = - (numBytesOfClearDataObj == NULL) - ? NULL - : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy); + mLongValueId = env->GetMethodID(clazz.get(), "longValue", "()J"); + CHECK(mLongValueId != NULL); - jint *numBytesOfEncryptedData = - (numBytesOfEncryptedDataObj == NULL) - ? NULL - : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy); + clazz.reset(env->FindClass("java/lang/Float")); + CHECK(clazz.get() != NULL); + mFloatClass = (jclass)env->NewGlobalRef(clazz.get()); - subSamples = new CryptoPlugin::SubSample[numSubSamples]; + mFloatValueId = env->GetMethodID(clazz.get(), "floatValue", "()F"); + CHECK(mFloatValueId != NULL); - for (jint i = 0; i < numSubSamples; ++i) { - subSamples[i].mNumBytesOfClearData = - (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i]; + clazz.reset(env->FindClass("java/util/ArrayList")); + CHECK(clazz.get() != NULL); - subSamples[i].mNumBytesOfEncryptedData = - (numBytesOfEncryptedData == NULL) - ? 0 : numBytesOfEncryptedData[i]; + mByteBufferArrayId = env->GetMethodID(gByteBufferInfo.clazz, "array", "()[B"); + CHECK(mByteBufferArrayId != NULL); } - if (numBytesOfEncryptedData != NULL) { - env->ReleaseIntArrayElements( - numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0); - numBytesOfEncryptedData = NULL; + jclass mStringClass; + jclass mIntegerClass; + jmethodID mIntegerValueId; + jclass mLongClass; + jmethodID mLongValueId; + jclass mFloatClass; + jmethodID mFloatValueId; + jmethodID mByteBufferArrayId; + } sFields{env}; + + jint size = env->CallIntMethod(keys, gArrayListInfo.sizeId); + if (size != env->CallIntMethod(values, gArrayListInfo.sizeId)) { + return BAD_VALUE; + } + + sp<AMessage> result{new AMessage}; + for (jint i = 0; i < size; ++i) { + ScopedLocalRef<jstring> jkey{ + env, (jstring)env->CallObjectMethod(keys, gArrayListInfo.getId, i)}; + const char *tmp = env->GetStringUTFChars(jkey.get(), nullptr); + AString key; + if (tmp) { + key.setTo(tmp); + } + env->ReleaseStringUTFChars(jkey.get(), tmp); + if (key.empty()) { + return NO_MEMORY; } - if (numBytesOfClearData != NULL) { - env->ReleaseIntArrayElements( - numBytesOfClearDataObj, numBytesOfClearData, 0); - numBytesOfClearData = NULL; + ScopedLocalRef<jobject> jvalue{ + env, env->CallObjectMethod(values, gArrayListInfo.getId, i)}; + + if (env->IsInstanceOf(jvalue.get(), sFields.mStringClass)) { + const char *tmp = env->GetStringUTFChars((jstring)jvalue.get(), nullptr); + AString value; + if (tmp) { + value.setTo(tmp); + } + env->ReleaseStringUTFChars((jstring)jvalue.get(), tmp); + if (value.empty()) { + return NO_MEMORY; + } + result->setString(key.c_str(), value); + } else if (env->IsInstanceOf(jvalue.get(), sFields.mIntegerClass)) { + jint value = env->CallIntMethod(jvalue.get(), sFields.mIntegerValueId); + result->setInt32(key.c_str(), value); + } else if (env->IsInstanceOf(jvalue.get(), sFields.mLongClass)) { + jlong value = env->CallLongMethod(jvalue.get(), sFields.mLongValueId); + result->setInt64(key.c_str(), value); + } else if (env->IsInstanceOf(jvalue.get(), sFields.mFloatClass)) { + jfloat value = env->CallFloatMethod(jvalue.get(), sFields.mFloatValueId); + result->setFloat(key.c_str(), value); + } else if (env->IsInstanceOf(jvalue.get(), gByteBufferInfo.clazz)) { + jint position = env->CallIntMethod(jvalue.get(), gByteBufferInfo.positionId); + jint limit = env->CallIntMethod(jvalue.get(), gByteBufferInfo.limitId); + sp<ABuffer> buffer{new ABuffer(limit - position)}; + void *data = env->GetDirectBufferAddress(jvalue.get()); + if (data != nullptr) { + memcpy(buffer->data(), + static_cast<const uint8_t *>(data) + position, + buffer->size()); + } else { + ScopedLocalRef<jbyteArray> byteArray{env, (jbyteArray)env->CallObjectMethod( + jvalue.get(), sFields.mByteBufferArrayId)}; + env->GetByteArrayRegion(byteArray.get(), position, buffer->size(), + reinterpret_cast<jbyte *>(buffer->data())); + } + result->setBuffer(key.c_str(), buffer); } } - if (err == OK && keyObj != NULL) { - if (env->GetArrayLength(keyObj) != 16) { - err = -EINVAL; - } else { - jboolean isCopy; - key = env->GetByteArrayElements(keyObj, &isCopy); - } + *msg = result; + return OK; +} + +static void android_media_MediaCodec_native_queueLinearBlock( + JNIEnv *env, jobject thiz, jint index, jobject bufferObj, + jint offset, jint size, jobject cryptoInfoObj, + jlong presentationTimeUs, jint flags, jobject keys, jobject values) { + ALOGV("android_media_MediaCodec_native_queueLinearBlock"); + + sp<JMediaCodec> codec = getMediaCodec(env, thiz); + + if (codec == nullptr) { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return; } - if (err == OK && ivObj != NULL) { - if (env->GetArrayLength(ivObj) != 16) { - err = -EINVAL; - } else { - jboolean isCopy; - iv = env->GetByteArrayElements(ivObj, &isCopy); + sp<AMessage> tunings; + status_t err = ConvertKeyValueListsToAMessage(env, keys, values, &tunings); + if (err != OK) { + throwExceptionAsNecessary(env, err); + return; + } + + std::shared_ptr<C2Buffer> buffer; + sp<hardware::HidlMemory> memory; + ScopedLocalRef<jobject> lock{env, env->GetObjectField(bufferObj, gLinearBlockInfo.lockId)}; + if (env->MonitorEnter(lock.get()) == JNI_OK) { + if (env->GetBooleanField(bufferObj, gLinearBlockInfo.validId)) { + JMediaCodecLinearBlock *context = + (JMediaCodecLinearBlock *)env->GetLongField(bufferObj, gLinearBlockInfo.contextId); + if (cryptoInfoObj != nullptr) { + memory = context->toHidlMemory(); + } else { + buffer = context->toC2Buffer(offset, size); + } } + env->MonitorExit(lock.get()); + } else { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return; } AString errorDetailMsg; + if (cryptoInfoObj != nullptr) { + if (!memory) { + throwExceptionAsNecessary(env, BAD_VALUE); + return; + } - if (err == OK) { - err = codec->queueSecureInputBuffer( - index, offset, - subSamples, numSubSamples, - (const uint8_t *)key, (const uint8_t *)iv, - mode, - pattern, - timestampUs, + NativeCryptoInfo cryptoInfo{env, cryptoInfoObj}; + size_t cryptoSize = 0; + for (int i = 0; i < cryptoInfo.mNumSubSamples; ++i) { + cryptoSize += cryptoInfo.mSubSamples[i].mNumBytesOfClearData; + cryptoSize += cryptoInfo.mSubSamples[i].mNumBytesOfEncryptedData; + } + err = codec->queueEncryptedLinearBlock( + index, + memory, + offset, + cryptoInfo.mSubSamples, cryptoInfo.mNumSubSamples, + (const uint8_t *)cryptoInfo.mKey, (const uint8_t *)cryptoInfo.mIv, + cryptoInfo.mMode, + cryptoInfo.mPattern, + presentationTimeUs, flags, + tunings, &errorDetailMsg); + } else { + if (!buffer) { + throwExceptionAsNecessary(env, BAD_VALUE); + return; + } + err = codec->queueBuffer( + index, buffer, presentationTimeUs, flags, tunings, &errorDetailMsg); } + throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, errorDetailMsg.c_str()); +} + +static void android_media_MediaCodec_native_queueGraphicBlock( + JNIEnv *env, jobject thiz, jint index, jobject bufferObj, + jlong presentationTimeUs, jint flags, jobject keys, jobject values) { + ALOGV("android_media_MediaCodec_native_queueGraphicBlock"); + + sp<JMediaCodec> codec = getMediaCodec(env, thiz); - if (iv != NULL) { - env->ReleaseByteArrayElements(ivObj, iv, 0); - iv = NULL; + if (codec == NULL) { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return; } - if (key != NULL) { - env->ReleaseByteArrayElements(keyObj, key, 0); - key = NULL; + sp<AMessage> tunings; + status_t err = ConvertKeyValueListsToAMessage(env, keys, values, &tunings); + if (err != OK) { + throwExceptionAsNecessary(env, err); + return; } - delete[] subSamples; - subSamples = NULL; + std::shared_ptr<C2Buffer> buffer; + std::shared_ptr<C2GraphicBlock> block; + ScopedLocalRef<jobject> lock{env, env->GetObjectField(bufferObj, gGraphicBlockInfo.lockId)}; + if (env->MonitorEnter(lock.get()) == JNI_OK) { + if (env->GetBooleanField(bufferObj, gGraphicBlockInfo.validId)) { + JMediaCodecGraphicBlock *context = (JMediaCodecGraphicBlock *)env->GetLongField( + bufferObj, gGraphicBlockInfo.contextId); + buffer = context->mBuffer; + block = context->mBlock; + } + env->MonitorExit(lock.get()); + } else { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return; + } - throwExceptionAsNecessary( - env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str()); + if (!block && !buffer) { + throwExceptionAsNecessary(env, BAD_VALUE); + return; + } + if (!buffer) { + buffer = C2Buffer::CreateGraphicBuffer(block->share(block->crop(), C2Fence{})); + } + AString errorDetailMsg; + err = codec->queueBuffer(index, buffer, presentationTimeUs, flags, tunings, &errorDetailMsg); + throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, errorDetailMsg.c_str()); +} + +static void android_media_MediaCodec_native_getOutputFrame( + JNIEnv *env, jobject thiz, jobject frame, jint index) { + ALOGV("android_media_MediaCodec_native_getOutputFrame"); + + sp<JMediaCodec> codec = getMediaCodec(env, thiz); + + if (codec == NULL) { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return; + } + + status_t err = codec->getOutputFrame(env, frame, index); + if (err != OK) { + throwExceptionAsNecessary(env, err); + } } static jint android_media_MediaCodec_dequeueInputBuffer( @@ -1991,6 +2418,31 @@ static void android_media_MediaCodec_native_init(JNIEnv *env) { gFields.patternSkipBlocksID = env->GetFieldID(clazz.get(), "mSkipBlocks", "I"); CHECK(gFields.patternSkipBlocksID != NULL); + clazz.reset(env->FindClass("android/media/MediaCodec$QueueRequest")); + CHECK(clazz.get() != NULL); + + gFields.queueRequestIndexID = env->GetFieldID(clazz.get(), "mIndex", "I"); + CHECK(gFields.queueRequestIndexID != NULL); + + clazz.reset(env->FindClass("android/media/MediaCodec$OutputFrame")); + CHECK(clazz.get() != NULL); + + gFields.outputFrameLinearBlockID = + env->GetFieldID(clazz.get(), "mLinearBlock", "Landroid/media/MediaCodec$LinearBlock;"); + CHECK(gFields.outputFrameLinearBlockID != NULL); + + gFields.outputFrameGraphicBlockID = + env->GetFieldID(clazz.get(), "mGraphicBlock", "Landroid/media/MediaCodec$GraphicBlock;"); + CHECK(gFields.outputFrameGraphicBlockID != NULL); + + gFields.outputFrameChangedKeysID = + env->GetFieldID(clazz.get(), "mChangedKeys", "Ljava/util/ArrayList;"); + CHECK(gFields.outputFrameChangedKeysID != NULL); + + gFields.outputFrameFormatID = + env->GetFieldID(clazz.get(), "mFormat", "Landroid/media/MediaFormat;"); + CHECK(gFields.outputFrameFormatID != NULL); + clazz.reset(env->FindClass("android/media/MediaCodec$CryptoException")); CHECK(clazz.get() != NULL); @@ -2105,6 +2557,96 @@ static void android_media_MediaCodec_native_init(JNIEnv *env) { field = env->GetFieldID(clazz.get(), "level", "I"); CHECK(field != NULL); gCodecInfo.levelField = field; + + clazz.reset(env->FindClass("java/nio/ByteBuffer")); + CHECK(clazz.get() != NULL); + gByteBufferInfo.clazz = (jclass)env->NewGlobalRef(clazz.get()); + + ScopedLocalRef<jclass> byteOrderClass( + env, env->FindClass("java/nio/ByteOrder")); + CHECK(byteOrderClass.get() != NULL); + + jmethodID nativeOrderID = env->GetStaticMethodID( + byteOrderClass.get(), "nativeOrder", "()Ljava/nio/ByteOrder;"); + CHECK(nativeOrderID != NULL); + + ScopedLocalRef<jobject> nativeByteOrderObj{ + env, env->CallStaticObjectMethod(byteOrderClass.get(), nativeOrderID)}; + gByteBufferInfo.nativeByteOrder = env->NewGlobalRef(nativeByteOrderObj.get()); + CHECK(gByteBufferInfo.nativeByteOrder != NULL); + nativeByteOrderObj.reset(); + + gByteBufferInfo.orderId = env->GetMethodID( + clazz.get(), + "order", + "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;"); + CHECK(gByteBufferInfo.orderId != NULL); + + gByteBufferInfo.asReadOnlyBufferId = env->GetMethodID( + clazz.get(), "asReadOnlyBuffer", "()Ljava/nio/ByteBuffer;"); + CHECK(gByteBufferInfo.asReadOnlyBufferId != NULL); + + gByteBufferInfo.positionId = env->GetMethodID( + clazz.get(), "position", "(I)Ljava/nio/Buffer;"); + CHECK(gByteBufferInfo.positionId != NULL); + + gByteBufferInfo.limitId = env->GetMethodID( + clazz.get(), "limit", "(I)Ljava/nio/Buffer;"); + CHECK(gByteBufferInfo.limitId != NULL); + + clazz.reset(env->FindClass("java/util/ArrayList")); + CHECK(clazz.get() != NULL); + + gArrayListInfo.sizeId = env->GetMethodID(clazz.get(), "size", "()I"); + CHECK(gArrayListInfo.sizeId != NULL); + + gArrayListInfo.getId = env->GetMethodID(clazz.get(), "get", "(I)Ljava/lang/Object;"); + CHECK(gArrayListInfo.getId != NULL); + + gArrayListInfo.addId = env->GetMethodID(clazz.get(), "add", "(Ljava/lang/Object;)Z"); + CHECK(gArrayListInfo.addId != NULL); + + clazz.reset(env->FindClass("android/media/MediaCodec$LinearBlock")); + CHECK(clazz.get() != NULL); + + gLinearBlockInfo.clazz = (jclass)env->NewGlobalRef(clazz.get()); + + gLinearBlockInfo.ctorId = env->GetMethodID(clazz.get(), "<init>", "()V"); + CHECK(gLinearBlockInfo.ctorId != NULL); + + gLinearBlockInfo.setInternalStateId = env->GetMethodID( + clazz.get(), "setInternalStateLocked", "(JZ)V"); + CHECK(gLinearBlockInfo.setInternalStateId != NULL); + + gLinearBlockInfo.contextId = env->GetFieldID(clazz.get(), "mNativeContext", "J"); + CHECK(gLinearBlockInfo.contextId != NULL); + + gLinearBlockInfo.validId = env->GetFieldID(clazz.get(), "mValid", "Z"); + CHECK(gLinearBlockInfo.validId != NULL); + + gLinearBlockInfo.lockId = env->GetFieldID(clazz.get(), "mLock", "Ljava/lang/Object;"); + CHECK(gLinearBlockInfo.lockId != NULL); + + clazz.reset(env->FindClass("android/media/MediaCodec$GraphicBlock")); + CHECK(clazz.get() != NULL); + + gGraphicBlockInfo.clazz = (jclass)env->NewGlobalRef(clazz.get()); + + gGraphicBlockInfo.ctorId = env->GetMethodID(clazz.get(), "<init>", "()V"); + CHECK(gGraphicBlockInfo.ctorId != NULL); + + gGraphicBlockInfo.setInternalStateId = env->GetMethodID( + clazz.get(), "setInternalStateLocked", "(JZ)V"); + CHECK(gGraphicBlockInfo.setInternalStateId != NULL); + + gGraphicBlockInfo.contextId = env->GetFieldID(clazz.get(), "mNativeContext", "J"); + CHECK(gGraphicBlockInfo.contextId != NULL); + + gGraphicBlockInfo.validId = env->GetFieldID(clazz.get(), "mValid", "Z"); + CHECK(gGraphicBlockInfo.validId != NULL); + + gGraphicBlockInfo.lockId = env->GetFieldID(clazz.get(), "mLock", "Ljava/lang/Object;"); + CHECK(gGraphicBlockInfo.lockId != NULL); } static void android_media_MediaCodec_native_setup( @@ -2155,6 +2697,345 @@ static void android_media_MediaCodec_native_finalize( android_media_MediaCodec_release(env, thiz); } +// MediaCodec.LinearBlock + +static jobject android_media_MediaCodec_LinearBlock_native_map( + JNIEnv *env, jobject thiz) { + JMediaCodecLinearBlock *context = + (JMediaCodecLinearBlock *)env->GetLongField(thiz, gLinearBlockInfo.contextId); + if (context->mBuffer) { + std::shared_ptr<C2Buffer> buffer = context->mBuffer; + if (!context->mReadonlyMapping) { + const C2BufferData data = buffer->data(); + if (data.type() != C2BufferData::LINEAR) { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return nullptr; + } + if (data.linearBlocks().size() != 1u) { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return nullptr; + } + C2ConstLinearBlock block = data.linearBlocks().front(); + context->mReadonlyMapping = + std::make_shared<C2ReadView>(block.map().get()); + } + return CreateByteBuffer( + env, + context->mReadonlyMapping->data(), // base + context->mReadonlyMapping->capacity(), // capacity + 0u, // offset + context->mReadonlyMapping->capacity(), // size + true, // readOnly + true /* clearBuffer */); + } else if (context->mBlock) { + std::shared_ptr<C2LinearBlock> block = context->mBlock; + if (!context->mReadWriteMapping) { + context->mReadWriteMapping = + std::make_shared<C2WriteView>(block->map().get()); + } + return CreateByteBuffer( + env, + context->mReadWriteMapping->base(), + context->mReadWriteMapping->capacity(), + context->mReadWriteMapping->offset(), + context->mReadWriteMapping->size(), + false, // readOnly + true /* clearBuffer */); + } else if (context->mLegacyBuffer) { + return CreateByteBuffer( + env, + context->mLegacyBuffer->base(), + context->mLegacyBuffer->capacity(), + context->mLegacyBuffer->offset(), + context->mLegacyBuffer->size(), + true, // readOnly + true /* clearBuffer */); + } else if (context->mHeap) { + return CreateByteBuffer( + env, + static_cast<uint8_t *>(context->mHeap->getBase()) + context->mHeap->getOffset(), + context->mHeap->getSize(), + 0, + context->mHeap->getSize(), + false, // readOnly + true /* clearBuffer */); + } + throwExceptionAsNecessary(env, INVALID_OPERATION); + return nullptr; +} + +static void android_media_MediaCodec_LinearBlock_native_recycle( + JNIEnv *env, jobject thiz) { + JMediaCodecLinearBlock *context = + (JMediaCodecLinearBlock *)env->GetLongField(thiz, gLinearBlockInfo.contextId); + env->CallVoidMethod(thiz, gLinearBlockInfo.setInternalStateId, 0, false); + delete context; +} + +static void PopulateNamesVector( + JNIEnv *env, jobjectArray codecNames, std::vector<std::string> *names) { + jsize length = env->GetArrayLength(codecNames); + for (jsize i = 0; i < length; ++i) { + jstring jstr = static_cast<jstring>(env->GetObjectArrayElement(codecNames, i)); + if (jstr == nullptr) { + // null entries are ignored + continue; + } + const char *cstr = env->GetStringUTFChars(jstr, nullptr); + if (cstr == nullptr) { + throwExceptionAsNecessary(env, BAD_VALUE); + return; + } + names->emplace_back(cstr); + env->ReleaseStringUTFChars(jstr, cstr); + } +} + +static void android_media_MediaCodec_LinearBlock_native_obtain( + JNIEnv *env, jobject thiz, jint capacity, jobjectArray codecNames) { + std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock}; + std::vector<std::string> names; + PopulateNamesVector(env, codecNames, &names); + bool hasSecure = false; + bool hasNonSecure = false; + for (const std::string &name : names) { + if (name.length() >= 7 && name.substr(name.length() - 7) == ".secure") { + hasSecure = true; + } else { + hasNonSecure = true; + } + } + if (hasSecure && !hasNonSecure) { + context->mHeap = new MemoryHeapBase(capacity); + context->mMemory = hardware::fromHeap(context->mHeap); + } else { + context->mBlock = MediaCodec::FetchLinearBlock(capacity, names); + if (!context->mBlock) { + jniThrowException(env, "java/io/IOException", nullptr); + return; + } + } + env->CallVoidMethod( + thiz, + gLinearBlockInfo.setInternalStateId, + (jlong)context.release(), + true /* isMappable */); +} + +static jboolean android_media_MediaCodec_LinearBlock_checkCompatible( + JNIEnv *env, jobjectArray codecNames) { + std::vector<std::string> names; + PopulateNamesVector(env, codecNames, &names); + bool isCompatible = false; + bool hasSecure = false; + bool hasNonSecure = false; + for (const std::string &name : names) { + if (name.length() >= 7 && name.substr(name.length() - 7) == ".secure") { + hasSecure = true; + } else { + hasNonSecure = true; + } + } + if (hasSecure && hasNonSecure) { + return false; + } + status_t err = MediaCodec::CanFetchLinearBlock(names, &isCompatible); + if (err != OK) { + throwExceptionAsNecessary(env, err); + } + return isCompatible; +} + +// MediaCodec.GraphicBlock + +template <class T> +static jobject CreateImage(JNIEnv *env, const std::shared_ptr<T> &view) { + bool readOnly = std::is_const<T>::value; + const C2PlanarLayout layout = view->layout(); + jint format = HAL_PIXEL_FORMAT_YCBCR_420_888; + switch (layout.type) { + case C2PlanarLayout::TYPE_YUV: { + if (layout.numPlanes != 3) { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return nullptr; + } + const C2PlaneInfo & yPlane = layout.planes[C2PlanarLayout::PLANE_Y]; + const C2PlaneInfo & uPlane = layout.planes[C2PlanarLayout::PLANE_U]; + const C2PlaneInfo & vPlane = layout.planes[C2PlanarLayout::PLANE_V]; + if (yPlane.rowSampling != 1 || yPlane.colSampling != 1) { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return nullptr; + } + if (uPlane.rowSampling != vPlane.rowSampling + || uPlane.colSampling != vPlane.colSampling) { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return nullptr; + } + if (uPlane.rowSampling == 2 && uPlane.colSampling == 2) { + format = HAL_PIXEL_FORMAT_YCBCR_420_888; + break; + } else if (uPlane.rowSampling == 1 && uPlane.colSampling == 2) { + format = HAL_PIXEL_FORMAT_YCBCR_422_888; + break; + } else if (uPlane.rowSampling == 1 && uPlane.colSampling == 1) { + format = HAL_PIXEL_FORMAT_YCBCR_444_888; + break; + } + throwExceptionAsNecessary(env, INVALID_OPERATION); + return nullptr; + } + case C2PlanarLayout::TYPE_RGB: { + if (layout.numPlanes != 3) { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return nullptr; + } + format = HAL_PIXEL_FORMAT_FLEX_RGB_888; + break; + } + case C2PlanarLayout::TYPE_RGBA: { + if (layout.numPlanes != 4) { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return nullptr; + } + format = HAL_PIXEL_FORMAT_FLEX_RGBA_8888; + break; + } + case C2PlanarLayout::TYPE_YUVA: + [[fallthrough]]; + default: + throwExceptionAsNecessary(env, INVALID_OPERATION); + return nullptr; + } + + ScopedLocalRef<jclass> planeClazz( + env, env->FindClass("android/media/MediaCodec$MediaImage$MediaPlane")); + ScopedLocalRef<jobjectArray> planeArray{ + env, env->NewObjectArray(layout.numPlanes, planeClazz.get(), NULL)}; + CHECK(planeClazz.get() != NULL); + jmethodID planeConstructID = env->GetMethodID(planeClazz.get(), "<init>", + "([Ljava/nio/ByteBuffer;IIIII)V"); + + // plane indices are happened to be Y-U-V and R-G-B(-A) order. + for (uint32_t i = 0; i < layout.numPlanes; ++i) { + const C2PlaneInfo &plane = layout.planes[i]; + if (plane.rowInc < 0 || plane.colInc < 0) { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return nullptr; + } + ssize_t minOffset = plane.minOffset(view->width(), view->height()); + ssize_t maxOffset = plane.maxOffset(view->width(), view->height()); + ScopedLocalRef<jobject> byteBuffer{env, CreateByteBuffer( + env, + view->data()[plane.rootIx] + plane.offset + minOffset, + maxOffset - minOffset + 1, + 0, + maxOffset - minOffset + 1, + readOnly, + true)}; + + ScopedLocalRef<jobject> jPlane{env, env->NewObject( + planeClazz.get(), planeConstructID, + byteBuffer.get(), plane.rowInc, plane.colInc)}; + } + + ScopedLocalRef<jclass> imageClazz( + env, env->FindClass("android/media/MediaCodec$MediaImage")); + CHECK(imageClazz.get() != NULL); + + jmethodID imageConstructID = env->GetMethodID(imageClazz.get(), "<init>", + "([Landroid/media/Image$Plane;IIIZJIILandroid/graphics/Rect;)V"); + + jobject img = env->NewObject(imageClazz.get(), imageConstructID, + planeArray.get(), + view->width(), + view->height(), + format, + (jboolean)readOnly /* readOnly */, + (jlong)0 /* timestamp */, + (jint)0 /* xOffset */, (jint)0 /* yOffset */, nullptr /* cropRect */); + + // if MediaImage creation fails, return null + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + return nullptr; + } + + return img; +} + +static jobject android_media_MediaCodec_GraphicBlock_native_map( + JNIEnv *env, jobject thiz) { + JMediaCodecGraphicBlock *context = + (JMediaCodecGraphicBlock *)env->GetLongField(thiz, gGraphicBlockInfo.contextId); + if (context->mBuffer) { + std::shared_ptr<C2Buffer> buffer = context->mBuffer; + if (!context->mReadonlyMapping) { + const C2BufferData data = buffer->data(); + if (data.type() != C2BufferData::GRAPHIC) { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return nullptr; + } + if (data.graphicBlocks().size() != 1u) { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return nullptr; + } + C2ConstGraphicBlock block = data.graphicBlocks().front(); + context->mReadonlyMapping = + std::make_shared<const C2GraphicView>(block.map().get()); + } + return CreateImage(env, context->mReadonlyMapping); + } else if (context->mBlock) { + std::shared_ptr<C2GraphicBlock> block = context->mBlock; + if (!context->mReadWriteMapping) { + context->mReadWriteMapping = + std::make_shared<C2GraphicView>(block->map().get()); + } + return CreateImage(env, context->mReadWriteMapping); + } else if (context->mLegacyBuffer) { + } + throwExceptionAsNecessary(env, INVALID_OPERATION); + return nullptr; +} + +static void android_media_MediaCodec_GraphicBlock_native_recycle( + JNIEnv *env, jobject thiz) { + JMediaCodecGraphicBlock *context = + (JMediaCodecGraphicBlock *)env->GetLongField(thiz, gGraphicBlockInfo.contextId); + env->CallVoidMethod(thiz, gGraphicBlockInfo.setInternalStateId, 0, false /* isMappable */); + delete context; +} + +static void android_media_MediaCodec_GraphicBlock_native_obtain( + JNIEnv *env, jobject thiz, + jint width, jint height, jint format, jlong usage, jobjectArray codecNames) { + std::unique_ptr<JMediaCodecGraphicBlock> context{new JMediaCodecGraphicBlock}; + std::vector<std::string> names; + PopulateNamesVector(env, codecNames, &names); + context->mBlock = MediaCodec::FetchGraphicBlock(width, height, format, usage, names); + if (!context->mBlock) { + jniThrowException(env, "java/io/IOException", nullptr); + return; + } + env->CallVoidMethod( + thiz, + gGraphicBlockInfo.setInternalStateId, + (jlong)context.release(), + true /*isMappable */); +} + +static jboolean android_media_MediaCodec_GraphicBlock_checkCompatible( + JNIEnv *env, jobjectArray codecNames) { + std::vector<std::string> names; + PopulateNamesVector(env, codecNames, &names); + bool isCompatible = false; + status_t err = MediaCodec::CanFetchGraphicBlock(names, &isCompatible); + if (err != OK) { + throwExceptionAsNecessary(env, err); + } + return isCompatible; +} + static const JNINativeMethod gMethods[] = { { "native_release", "()V", (void *)android_media_MediaCodec_release }, @@ -2200,6 +3081,19 @@ static const JNINativeMethod gMethods[] = { { "native_queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V", (void *)android_media_MediaCodec_queueSecureInputBuffer }, + { "native_queueLinearBlock", + "(ILandroid/media/MediaCodec$LinearBlock;IILandroid/media/MediaCodec$CryptoInfo;JI" + "Ljava/util/ArrayList;Ljava/util/ArrayList;)V", + (void *)android_media_MediaCodec_native_queueLinearBlock }, + + { "native_queueGraphicBlock", + "(ILandroid/media/MediaCodec$GraphicBlock;JILjava/util/ArrayList;Ljava/util/ArrayList;)V", + (void *)android_media_MediaCodec_native_queueGraphicBlock }, + + { "native_getOutputFrame", + "(Landroid/media/MediaCodec$OutputFrame;I)V", + (void *)android_media_MediaCodec_native_getOutputFrame }, + { "native_dequeueInputBuffer", "(J)I", (void *)android_media_MediaCodec_dequeueInputBuffer }, @@ -2254,7 +3148,50 @@ static const JNINativeMethod gMethods[] = { (void *)android_media_MediaCodec_native_finalize }, }; +static const JNINativeMethod gLinearBlockMethods[] = { + { "native_map", "()Ljava/nio/ByteBuffer;", + (void *)android_media_MediaCodec_LinearBlock_native_map }, + + { "native_recycle", "()V", + (void *)android_media_MediaCodec_LinearBlock_native_recycle }, + + { "native_obtain", "(I[Ljava/lang/String;)V", + (void *)android_media_MediaCodec_LinearBlock_native_obtain }, + + { "native_checkCompatible", "([Ljava/lang/String;)Z", + (void *)android_media_MediaCodec_LinearBlock_checkCompatible }, +}; + +static const JNINativeMethod gGraphicBlockMethods[] = { + { "native_map", "()Landroid/media/Image;", + (void *)android_media_MediaCodec_GraphicBlock_native_map }, + + { "native_recycle", "()V", + (void *)android_media_MediaCodec_GraphicBlock_native_recycle }, + + { "native_obtain", "(IIIJ[Ljava/lang/String;)V", + (void *)android_media_MediaCodec_GraphicBlock_native_obtain }, + + { "native_checkCompatible", "([Ljava/lang/String;)Z", + (void *)android_media_MediaCodec_GraphicBlock_checkCompatible }, +}; + int register_android_media_MediaCodec(JNIEnv *env) { - return AndroidRuntime::registerNativeMethods(env, + int result = AndroidRuntime::registerNativeMethods(env, "android/media/MediaCodec", gMethods, NELEM(gMethods)); + if (result != JNI_OK) { + return result; + } + result = AndroidRuntime::registerNativeMethods(env, + "android/media/MediaCodec$LinearBlock", + gLinearBlockMethods, + NELEM(gLinearBlockMethods)); + if (result != JNI_OK) { + return result; + } + result = AndroidRuntime::registerNativeMethods(env, + "android/media/MediaCodec$GraphicBlock", + gGraphicBlockMethods, + NELEM(gGraphicBlockMethods)); + return result; } diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h index ce1c805b6366..1d12e776d43e 100644 --- a/media/jni/android_media_MediaCodec.h +++ b/media/jni/android_media_MediaCodec.h @@ -27,6 +27,8 @@ #include <media/stagefright/foundation/AHandler.h> #include <utils/Errors.h> +class C2Buffer; + namespace android { struct ABuffer; @@ -39,6 +41,7 @@ struct MediaCodec; struct PersistentSurface; class Surface; namespace hardware { +class HidlMemory; namespace cas { namespace native { namespace V1_0 { @@ -97,6 +100,26 @@ struct JMediaCodec : public AHandler { uint32_t flags, AString *errorDetailMsg); + status_t queueBuffer( + size_t index, const std::shared_ptr<C2Buffer> &buffer, + int64_t timeUs, uint32_t flags, const sp<AMessage> &tunings, + AString *errorDetailMsg); + + status_t queueEncryptedLinearBlock( + size_t index, + const sp<hardware::HidlMemory> &buffer, + size_t offset, + const CryptoPlugin::SubSample *subSamples, + size_t numSubSamples, + const uint8_t key[16], + const uint8_t iv[16], + CryptoPlugin::Mode mode, + const CryptoPlugin::Pattern &pattern, + int64_t presentationTimeUs, + uint32_t flags, + const sp<AMessage> &tunings, + AString *errorDetailMsg); + status_t dequeueInputBuffer(size_t *index, int64_t timeoutUs); status_t dequeueOutputBuffer( @@ -120,6 +143,9 @@ struct JMediaCodec : public AHandler { status_t getImage( JNIEnv *env, bool input, size_t index, jobject *image) const; + status_t getOutputFrame( + JNIEnv *env, jobject frame, size_t index) const; + status_t getName(JNIEnv *env, jstring *name) const; status_t getCodecInfo(JNIEnv *env, jobject *codecInfo) const; @@ -147,17 +173,10 @@ private: jweak mObject; sp<Surface> mSurfaceTextureClient; - // java objects cached - jclass mByteBufferClass; - jobject mNativeByteOrderObj; - jmethodID mByteBufferOrderMethodID; - jmethodID mByteBufferPositionMethodID; - jmethodID mByteBufferLimitMethodID; - jmethodID mByteBufferAsReadOnlyBufferMethodID; - sp<ALooper> mLooper; sp<MediaCodec> mCodec; AString mNameAtCreation; + bool mGraphicOutput{false}; std::once_flag mReleaseFlag; sp<AMessage> mCallbackNotification; @@ -170,8 +189,6 @@ private: JNIEnv *env, bool readOnly, bool clearBuffer, const sp<T> &buffer, jobject *buf) const; - void cacheJavaObjects(JNIEnv *env); - void deleteJavaObjects(JNIEnv *env); void handleCallback(const sp<AMessage> &msg); void handleFrameRenderedNotification(const sp<AMessage> &msg); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaFileTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaFileTest.java index 481f4796951d..507dd4ae93e9 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaFileTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaFileTest.java @@ -19,6 +19,7 @@ package com.android.mediaframeworktest.unit; import static android.media.MediaFile.getFormatCode; import static android.media.MediaFile.getMimeType; import static android.media.MediaFile.isAudioMimeType; +import static android.media.MediaFile.isDocumentMimeType; import static android.media.MediaFile.isImageMimeType; import static android.media.MediaFile.isPlayListMimeType; import static android.media.MediaFile.isVideoMimeType; @@ -88,7 +89,15 @@ public class MediaFileTest { assertTrue(isPlayListMimeType(mimeMap.guessMimeTypeFromExtension("wpl"))); assertTrue(isPlayListMimeType(mimeMap.guessMimeTypeFromExtension("m3u"))); assertTrue(isPlayListMimeType(mimeMap.guessMimeTypeFromExtension("m3u8"))); - assertTrue(isPlayListMimeType(mimeMap.guessMimeTypeFromExtension("asf"))); + } + + @Test + public void testDocument() throws Exception { + assertTrue(isDocumentMimeType("text/csv")); + assertTrue(isDocumentMimeType("text/plain")); + assertTrue(isDocumentMimeType("application/pdf")); + assertTrue(isDocumentMimeType("application/msword")); + assertFalse(isDocumentMimeType("audio/mpeg")); } @Test diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java index daaa8688e203..e0b454559456 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java @@ -36,6 +36,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.testng.Assert.assertThrows; @@ -693,6 +694,14 @@ public class MediaRouter2Test { assertFalse(systemController.isReleased()); } + @Test + public void testControllers() { + List<RoutingController> controllers = mRouter2.getControllers(); + assertNotNull(controllers); + assertFalse(controllers.isEmpty()); + assertSame(mRouter2.getSystemController(), controllers.get(0)); + } + // Helper for getting routes easily static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) { Map<String, MediaRoute2Info> routeMap = new HashMap<>(); diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java index 7726e90674f2..4a2044af0431 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java @@ -246,7 +246,7 @@ public class MediaRouterManagerTest { } }); - assertEquals(0, mManager.getRoutingControllers(mPackageName).size()); + assertEquals(1, mManager.getRoutingControllers(mPackageName).size()); mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1)); latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); @@ -254,14 +254,14 @@ public class MediaRouterManagerTest { List<MediaRouter2Manager.RoutingController> controllers = mManager.getRoutingControllers(mPackageName); - assertEquals(1, controllers.size()); + assertEquals(2, controllers.size()); - MediaRouter2Manager.RoutingController routingController = controllers.get(0); + MediaRouter2Manager.RoutingController routingController = controllers.get(1); awaitOnRouteChangedManager( () -> routingController.release(), ROUTE_ID1, route -> TextUtils.equals(route.getClientPackageName(), null)); - assertEquals(0, mManager.getRoutingControllers(mPackageName).size()); + assertEquals(1, mManager.getRoutingControllers(mPackageName).size()); } /** @@ -290,8 +290,8 @@ public class MediaRouterManagerTest { List<MediaRouter2Manager.RoutingController> controllers = mManager.getRoutingControllers(mPackageName); - assertEquals(1, controllers.size()); - MediaRouter2Manager.RoutingController routingController = controllers.get(0); + assertEquals(2, controllers.size()); + MediaRouter2Manager.RoutingController routingController = controllers.get(1); awaitOnRouteChangedManager( () -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID5_TO_TRANSFER_TO)), diff --git a/native/graphics/jni/bitmap.cpp b/native/graphics/jni/bitmap.cpp index ea8a521c9d5f..b8eb543ff835 100644 --- a/native/graphics/jni/bitmap.cpp +++ b/native/graphics/jni/bitmap.cpp @@ -33,10 +33,14 @@ int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap, int32_t AndroidBitmap_getDataSpace(JNIEnv* env, jobject jbitmap) { if (NULL == env || NULL == jbitmap) { - return ADATASPACE_UNKNOWN; // Or return a real error? + return ADATASPACE_UNKNOWN; } android::graphics::Bitmap bitmap(env, jbitmap); + if (!bitmap.isValid()) { + return ADATASPACE_UNKNOWN; + } + return bitmap.getDataSpace(); } @@ -76,12 +80,27 @@ int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap) { return ANDROID_BITMAP_RESULT_SUCCESS; } +int AndroidBitmap_getHardwareBuffer(JNIEnv* env, jobject jbitmap, AHardwareBuffer** outBuffer) { + if (NULL == env || NULL == jbitmap || NULL == outBuffer) { + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + + android::graphics::Bitmap bitmap(env, jbitmap); + + if (!bitmap.isValid()) { + return ANDROID_BITMAP_RESULT_JNI_EXCEPTION; + } + + *outBuffer = bitmap.getHardwareBuffer(); + return *outBuffer == NULL ? ANDROID_BITMAP_RESULT_BAD_PARAMETER : ANDROID_BITMAP_RESULT_SUCCESS; +} + int AndroidBitmap_compress(const AndroidBitmapInfo* info, int32_t dataSpace, const void* pixels, int32_t format, int32_t quality, void* userContext, - AndroidBitmap_compress_write_fn fn) { + AndroidBitmap_CompressWriteFunc fn) { if (NULL == info || NULL == pixels || NULL == fn) { return ANDROID_BITMAP_RESULT_BAD_PARAMETER; } diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp index 51439672d404..79bcc15e1f0f 100644 --- a/native/graphics/jni/imagedecoder.cpp +++ b/native/graphics/jni/imagedecoder.cpp @@ -18,12 +18,14 @@ #include <android/asset_manager.h> #include <android/bitmap.h> +#include <android/data_space.h> #include <android/imagedecoder.h> #include <android/graphics/MimeType.h> #include <android/rect.h> #include <hwui/ImageDecoder.h> #include <log/log.h> #include <SkAndroidCodec.h> +#include <utils/Color.h> #include <fcntl.h> #include <optional> @@ -131,6 +133,10 @@ static ImageDecoder* toDecoder(AImageDecoder* d) { return reinterpret_cast<ImageDecoder*>(d); } +static const ImageDecoder* toDecoder(const AImageDecoder* d) { + return reinterpret_cast<const ImageDecoder*>(d); +} + // Note: This differs from the version in android_bitmap.cpp in that this // version returns kGray_8_SkColorType for ANDROID_BITMAP_FORMAT_A_8. SkCodec // allows decoding single channel images to gray, which Android then treats @@ -161,6 +167,18 @@ int AImageDecoder_setAndroidBitmapFormat(AImageDecoder* decoder, int32_t format) ? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_CONVERSION; } +int AImageDecoder_setDataSpace(AImageDecoder* decoder, int32_t dataspace) { + sk_sp<SkColorSpace> cs = uirenderer::DataSpaceToColorSpace((android_dataspace)dataspace); + // 0 is ADATASPACE_UNKNOWN. We need an explicit request for an ADataSpace. + if (!decoder || !dataspace || !cs) { + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + } + + ImageDecoder* imageDecoder = toDecoder(decoder); + imageDecoder->setOutColorSpace(std::move(cs)); + return ANDROID_IMAGE_DECODER_SUCCESS; +} + const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo(const AImageDecoder* decoder) { return reinterpret_cast<const AImageDecoderHeaderInfo*>(decoder); } @@ -190,11 +208,18 @@ const char* AImageDecoderHeaderInfo_getMimeType(const AImageDecoderHeaderInfo* i return getMimeType(toDecoder(info)->mCodec->getEncodedFormat()); } -bool AImageDecoderHeaderInfo_isAnimated(const AImageDecoderHeaderInfo* info) { +int32_t AImageDecoderHeaderInfo_getDataSpace(const AImageDecoderHeaderInfo* info) { if (!info) { - return false; + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; } - return toDecoder(info)->mCodec->codec()->getFrameCount() > 1; + + // Note: This recomputes the data space because it's possible the client has + // changed the output color space, so we cannot rely on it. Alternatively, + // we could store the ADataSpace in the ImageDecoder. + const ImageDecoder* imageDecoder = toDecoder(info); + SkColorType colorType = imageDecoder->mCodec->computeOutputColorType(kN32_SkColorType); + sk_sp<SkColorSpace> colorSpace = imageDecoder->mCodec->computeOutputColorSpace(colorType); + return uirenderer::ColorSpaceToADataSpace(colorSpace.get(), colorType); } // FIXME: Share with getFormat in android_bitmap.cpp? @@ -215,8 +240,7 @@ 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; } @@ -249,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; } @@ -258,6 +282,18 @@ int AImageDecoder_setTargetSize(AImageDecoder* decoder, int width, int height) { ? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_SCALE; } +int AImageDecoder_computeSampledSize(const AImageDecoder* decoder, int sampleSize, + int32_t* width, int32_t* height) { + if (!decoder || !width || !height || sampleSize < 1) { + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + } + + SkISize size = toDecoder(decoder)->mCodec->getSampledDimensions(sampleSize); + *width = size.width(); + *height = size.height(); + return ANDROID_IMAGE_DECODER_SUCCESS; +} + int AImageDecoder_setCrop(AImageDecoder* decoder, ARect crop) { if (!decoder) { return ANDROID_IMAGE_DECODER_BAD_PARAMETER; diff --git a/native/graphics/jni/libjnigraphics.map.txt b/native/graphics/jni/libjnigraphics.map.txt index 6843e7a8552f..01c14770bebd 100644 --- a/native/graphics/jni/libjnigraphics.map.txt +++ b/native/graphics/jni/libjnigraphics.map.txt @@ -6,22 +6,25 @@ LIBJNIGRAPHICS { AImageDecoder_delete; # introduced=30 AImageDecoder_setAndroidBitmapFormat; # introduced=30 AImageDecoder_setUnpremultipliedRequired; # introduced=30 + AImageDecoder_setDataSpace; # introduced=30 AImageDecoder_getHeaderInfo; # introduced=30 AImageDecoder_getMinimumStride; # introduced=30 AImageDecoder_decodeImage; # introduced=30 AImageDecoder_setTargetSize; # introduced=30 + AImageDecoder_computeSampledSize; # introduced=30 AImageDecoder_setCrop; # introduced=30 AImageDecoderHeaderInfo_getWidth; # introduced=30 AImageDecoderHeaderInfo_getHeight; # introduced=30 AImageDecoderHeaderInfo_getMimeType; # introduced=30 AImageDecoderHeaderInfo_getAlphaFlags; # introduced=30 - AImageDecoderHeaderInfo_isAnimated; # introduced=30 AImageDecoderHeaderInfo_getAndroidBitmapFormat; # introduced=30 + AImageDecoderHeaderInfo_getDataSpace; # introduced=30 AndroidBitmap_getInfo; AndroidBitmap_getDataSpace; AndroidBitmap_lockPixels; AndroidBitmap_unlockPixels; AndroidBitmap_compress; # introduced=30 + AndroidBitmap_getHardwareBuffer; #introduced=30 local: *; }; diff --git a/opengl/java/com/google/android/gles_jni/EGLImpl.java b/opengl/java/com/google/android/gles_jni/EGLImpl.java index 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/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..dc84935034bc 100644 --- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java @@ -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/FusedLocation/Android.bp b/packages/FusedLocation/Android.bp index e794f726dba6..c70ab716aa44 100644 --- a/packages/FusedLocation/Android.bp +++ b/packages/FusedLocation/Android.bp @@ -14,9 +14,33 @@ android_app { name: "FusedLocation", - srcs: ["**/*.java"], + srcs: ["src/**/*.java"], libs: ["com.android.location.provider"], platform_apis: true, certificate: "platform", privileged: true, } + +android_test { + name: "FusedLocationTests", + manifest: "test/AndroidManifest.xml", + test_config: "test/AndroidTest.xml", + srcs: [ + "test/src/**/*.java", + "src/**/*.java", // include real sources because we're forced to test this directly + ], + libs: [ + "android.test.base", + "android.test.runner", + "com.android.location.provider", + ], + static_libs: [ + "androidx.test.core", + "androidx.test.rules", + "androidx.test.ext.junit", + "androidx.test.ext.truth", + "mockito-target-minus-junit4", + "truth-prebuilt", + ], + test_suites: ["device-tests"] +}
\ No newline at end of file diff --git a/packages/FusedLocation/AndroidManifest.xml b/packages/FusedLocation/AndroidManifest.xml index a8319ab233ac..bad0497c487d 100644 --- a/packages/FusedLocation/AndroidManifest.xml +++ b/packages/FusedLocation/AndroidManifest.xml @@ -23,6 +23,8 @@ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> + <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" /> <uses-permission android:name="android.permission.INSTALL_LOCATION_PROVIDER" /> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> diff --git a/packages/FusedLocation/TEST_MAPPING b/packages/FusedLocation/TEST_MAPPING new file mode 100644 index 000000000000..e810d6aea8af --- /dev/null +++ b/packages/FusedLocation/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "FusedLocationTests" + } + ] +}
\ No newline at end of file diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java index be817d60e55b..fb7dbc8aca5c 100644 --- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java +++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java @@ -16,70 +16,307 @@ package com.android.location.fused; +import static android.content.Intent.ACTION_USER_SWITCHED; +import static android.location.LocationManager.GPS_PROVIDER; +import static android.location.LocationManager.NETWORK_PROVIDER; + +import android.annotation.Nullable; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.location.Criteria; -import android.os.Handler; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.location.LocationRequest; +import android.os.Bundle; import android.os.Looper; -import android.os.UserHandle; +import android.os.Parcelable; import android.os.WorkSource; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.location.ProviderRequest; import com.android.location.provider.LocationProviderBase; +import com.android.location.provider.LocationRequestUnbundled; import com.android.location.provider.ProviderPropertiesUnbundled; import com.android.location.provider.ProviderRequestUnbundled; -import java.io.FileDescriptor; import java.io.PrintWriter; -class FusedLocationProvider extends LocationProviderBase implements FusionEngine.Callback { +/** Basic fused location provider implementation. */ +public class FusedLocationProvider extends LocationProviderBase { + private static final String TAG = "FusedLocationProvider"; - private static ProviderPropertiesUnbundled PROPERTIES = ProviderPropertiesUnbundled.create( - false, false, false, false, true, true, true, Criteria.POWER_LOW, - Criteria.ACCURACY_FINE); + private static final ProviderPropertiesUnbundled PROPERTIES = + ProviderPropertiesUnbundled.create( + /* requiresNetwork = */ false, + /* requiresSatellite = */ false, + /* requiresCell = */ false, + /* hasMonetaryCost = */ false, + /* supportsAltitude = */ true, + /* supportsSpeed = */ true, + /* supportsBearing = */ true, + Criteria.POWER_LOW, + Criteria.ACCURACY_FINE + ); + + private static final long MAX_LOCATION_COMPARISON_NS = 11 * 1000000000L; // 11 seconds + + private final Object mLock = new Object(); private final Context mContext; - private final Handler mHandler; - private final FusionEngine mEngine; - - private final BroadcastReceiver mUserSwitchReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (Intent.ACTION_USER_SWITCHED.equals(action)) { - mEngine.switchUser(); - } - } - }; + private final LocationManager mLocationManager; + private final LocationListener mGpsListener; + private final LocationListener mNetworkListener; + private final BroadcastReceiver mUserChangeReceiver; - FusedLocationProvider(Context context) { - super(TAG, PROPERTIES); + @GuardedBy("mLock") + private ProviderRequestUnbundled mRequest; + @GuardedBy("mLock") + private WorkSource mWorkSource; + @GuardedBy("mLock") + private long mGpsInterval; + @GuardedBy("mLock") + private long mNetworkInterval; + @GuardedBy("mLock") + @Nullable private Location mFusedLocation; + @GuardedBy("mLock") + @Nullable private Location mGpsLocation; + @GuardedBy("mLock") + @Nullable private Location mNetworkLocation; + + public FusedLocationProvider(Context context) { + super(TAG, PROPERTIES); mContext = context; - mHandler = new Handler(Looper.myLooper()); - mEngine = new FusionEngine(context, Looper.myLooper(), this); + mLocationManager = context.getSystemService(LocationManager.class); + + mGpsListener = new LocationListener() { + @Override + public void onLocationChanged(Location location) { + synchronized (mLock) { + mGpsLocation = location; + reportBestLocationLocked(); + } + } + + @Override + public void onProviderDisabled(String provider) { + synchronized (mLock) { + // if satisfying a bypass request, don't clear anything + if (mRequest.getReportLocation() && mRequest.isLocationSettingsIgnored()) { + return; + } + + mGpsLocation = null; + } + } + }; + + mNetworkListener = new LocationListener() { + @Override + public void onLocationChanged(Location location) { + synchronized (mLock) { + mNetworkLocation = location; + reportBestLocationLocked(); + } + } + + @Override + public void onProviderDisabled(String provider) { + synchronized (mLock) { + // if satisfying a bypass request, don't clear anything + if (mRequest.getReportLocation() && mRequest.isLocationSettingsIgnored()) { + return; + } + + mNetworkLocation = null; + } + } + }; + + mUserChangeReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (!ACTION_USER_SWITCHED.equals(intent.getAction())) { + return; + } + + onUserChanged(); + } + }; + + mRequest = new ProviderRequestUnbundled(ProviderRequest.EMPTY_REQUEST); + mWorkSource = new WorkSource(); + mGpsInterval = Long.MAX_VALUE; + mNetworkInterval = Long.MAX_VALUE; } - void init() { - // listen for user change - mContext.registerReceiverAsUser(mUserSwitchReceiver, UserHandle.ALL, - new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler); + void start() { + mContext.registerReceiver(mUserChangeReceiver, new IntentFilter(ACTION_USER_SWITCHED)); } - void destroy() { - mContext.unregisterReceiver(mUserSwitchReceiver); - mHandler.post(() -> mEngine.setRequest(null)); + void stop() { + mContext.unregisterReceiver(mUserChangeReceiver); + + synchronized (mLock) { + mRequest = new ProviderRequestUnbundled(ProviderRequest.EMPTY_REQUEST); + updateRequirementsLocked(); + } } @Override - public void onSetRequest(ProviderRequestUnbundled request, WorkSource source) { - mHandler.post(() -> mEngine.setRequest(request)); + public void onSetRequest(ProviderRequestUnbundled request, WorkSource workSource) { + synchronized (mLock) { + mRequest = request; + mWorkSource = workSource; + updateRequirementsLocked(); + } } - @Override - public void onDump(FileDescriptor fd, PrintWriter pw, String[] args) { - mEngine.dump(fd, pw, args); + @GuardedBy("mLock") + private void updateRequirementsLocked() { + long gpsInterval = Long.MAX_VALUE; + long networkInterval = Long.MAX_VALUE; + if (mRequest.getReportLocation()) { + for (LocationRequestUnbundled request : mRequest.getLocationRequests()) { + switch (request.getQuality()) { + case LocationRequestUnbundled.ACCURACY_FINE: + case LocationRequestUnbundled.POWER_HIGH: + if (request.getInterval() < gpsInterval) { + gpsInterval = request.getInterval(); + } + if (request.getInterval() < networkInterval) { + networkInterval = request.getInterval(); + } + break; + case LocationRequestUnbundled.ACCURACY_BLOCK: + case LocationRequestUnbundled.ACCURACY_CITY: + case LocationRequestUnbundled.POWER_LOW: + if (request.getInterval() < networkInterval) { + networkInterval = request.getInterval(); + } + break; + } + } + } + + if (gpsInterval != mGpsInterval) { + resetProviderRequestLocked(GPS_PROVIDER, mGpsInterval, gpsInterval, mGpsListener); + mGpsInterval = gpsInterval; + } + if (networkInterval != mNetworkInterval) { + resetProviderRequestLocked(NETWORK_PROVIDER, mNetworkInterval, networkInterval, + mNetworkListener); + mNetworkInterval = networkInterval; + } + } + + @GuardedBy("mLock") + private void resetProviderRequestLocked(String provider, long oldInterval, long newInterval, + LocationListener listener) { + if (oldInterval != Long.MAX_VALUE) { + mLocationManager.removeUpdates(listener); + } + if (newInterval != Long.MAX_VALUE) { + LocationRequest request = LocationRequest.createFromDeprecatedProvider( + provider, newInterval, 0, false); + if (mRequest.isLocationSettingsIgnored()) { + request.setLocationSettingsIgnored(true); + } + request.setWorkSource(mWorkSource); + mLocationManager.requestLocationUpdates(request, listener, Looper.getMainLooper()); + } + } + + @GuardedBy("mLock") + private void reportBestLocationLocked() { + Location bestLocation = chooseBestLocation(mGpsLocation, mNetworkLocation); + if (bestLocation == mFusedLocation) { + return; + } + + mFusedLocation = bestLocation; + if (mFusedLocation == null) { + return; + } + + // copy NO_GPS_LOCATION extra from mNetworkLocation into mFusedLocation + if (mNetworkLocation != null) { + Bundle srcExtras = mNetworkLocation.getExtras(); + if (srcExtras != null) { + Parcelable srcParcelable = + srcExtras.getParcelable(LocationProviderBase.EXTRA_NO_GPS_LOCATION); + if (srcParcelable instanceof Location) { + Bundle dstExtras = mFusedLocation.getExtras(); + if (dstExtras == null) { + dstExtras = new Bundle(); + mFusedLocation.setExtras(dstExtras); + } + dstExtras.putParcelable(LocationProviderBase.EXTRA_NO_GPS_LOCATION, + srcParcelable); + } + } + } + + reportLocation(mFusedLocation); + } + + private void onUserChanged() { + // clear cached locations when the user changes to prevent leaking user information + synchronized (mLock) { + mFusedLocation = null; + mGpsLocation = null; + mNetworkLocation = null; + } + } + + void dump(PrintWriter writer) { + synchronized (mLock) { + writer.println("request: " + mRequest); + if (mGpsInterval != Long.MAX_VALUE) { + writer.println(" gps interval: " + mGpsInterval); + } + if (mNetworkInterval != Long.MAX_VALUE) { + writer.println(" network interval: " + mNetworkInterval); + } + if (mGpsLocation != null) { + writer.println(" last gps location: " + mGpsLocation); + } + if (mNetworkLocation != null) { + writer.println(" last network location: " + mNetworkLocation); + } + } + } + + @Nullable + private static Location chooseBestLocation( + @Nullable Location locationA, + @Nullable Location locationB) { + if (locationA == null) { + return locationB; + } + if (locationB == null) { + return locationA; + } + + if (locationA.getElapsedRealtimeNanos() + > locationB.getElapsedRealtimeNanos() + MAX_LOCATION_COMPARISON_NS) { + return locationA; + } + if (locationB.getElapsedRealtimeNanos() + > locationA.getElapsedRealtimeNanos() + MAX_LOCATION_COMPARISON_NS) { + return locationB; + } + + if (!locationA.hasAccuracy()) { + return locationB; + } + if (!locationB.hasAccuracy()) { + return locationA; + } + return locationA.getAccuracy() < locationB.getAccuracy() ? locationA : locationB; } } diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java index 75bb5eceab6d..1fa3824f2321 100644 --- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java +++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java @@ -16,19 +16,23 @@ package com.android.location.fused; +import android.annotation.Nullable; import android.app.Service; import android.content.Intent; import android.os.IBinder; +import java.io.FileDescriptor; +import java.io.PrintWriter; + public class FusedLocationService extends Service { - private FusedLocationProvider mProvider; + @Nullable private FusedLocationProvider mProvider; @Override public IBinder onBind(Intent intent) { if (mProvider == null) { mProvider = new FusedLocationProvider(this); - mProvider.init(); + mProvider.start(); } return mProvider.getBinder(); @@ -37,8 +41,15 @@ public class FusedLocationService extends Service { @Override public void onDestroy() { if (mProvider != null) { - mProvider.destroy(); + mProvider.stop(); mProvider = null; } } + + @Override + protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { + if (mProvider != null) { + mProvider.dump(writer); + } + } } diff --git a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java deleted file mode 100644 index e4610cf44636..000000000000 --- a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.location.fused; - -import android.content.Context; -import android.location.Location; -import android.location.LocationListener; -import android.location.LocationManager; -import android.os.Bundle; -import android.os.Looper; -import android.os.Parcelable; -import android.os.SystemClock; -import android.util.Log; - -import com.android.location.provider.LocationProviderBase; -import com.android.location.provider.LocationRequestUnbundled; -import com.android.location.provider.ProviderRequestUnbundled; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.HashMap; - -public class FusionEngine implements LocationListener { - public interface Callback { - void reportLocation(Location location); - } - - private static final String TAG = "FusedLocation"; - private static final String NETWORK = LocationManager.NETWORK_PROVIDER; - private static final String GPS = LocationManager.GPS_PROVIDER; - private static final String FUSED = LocationProviderBase.FUSED_PROVIDER; - - public static final long SWITCH_ON_FRESHNESS_CLIFF_NS = 11 * 1000000000L; // 11 seconds - - private final LocationManager mLocationManager; - private final Looper mLooper; - private final Callback mCallback; - - // all fields are only used on mLooper thread. except for in dump() which is not thread-safe - private Location mFusedLocation; - private Location mGpsLocation; - private Location mNetworkLocation; - - private ProviderRequestUnbundled mRequest; - - private final HashMap<String, ProviderStats> mStats = new HashMap<>(); - - FusionEngine(Context context, Looper looper, Callback callback) { - mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); - mNetworkLocation = new Location(""); - mNetworkLocation.setAccuracy(Float.MAX_VALUE); - mGpsLocation = new Location(""); - mGpsLocation.setAccuracy(Float.MAX_VALUE); - mLooper = looper; - mCallback = callback; - - mStats.put(GPS, new ProviderStats()); - mStats.put(NETWORK, new ProviderStats()); - } - - /** Called on mLooper thread */ - public void setRequest(ProviderRequestUnbundled request) { - mRequest = request; - updateRequirements(); - } - - private static class ProviderStats { - public boolean requested; - public long requestTime; - public long minTime; - - @Override - public String toString() { - return (requested ? " REQUESTED" : " ---"); - } - } - - private void enableProvider(String name, long minTime) { - ProviderStats stats = mStats.get(name); - if (stats == null) return; - - if (mLocationManager.isProviderEnabled(name)) { - if (!stats.requested) { - stats.requestTime = SystemClock.elapsedRealtime(); - stats.requested = true; - stats.minTime = minTime; - mLocationManager.requestLocationUpdates(name, minTime, 0, this, mLooper); - } else if (stats.minTime != minTime) { - stats.minTime = minTime; - mLocationManager.requestLocationUpdates(name, minTime, 0, this, mLooper); - } - } - } - - private void disableProvider(String name) { - ProviderStats stats = mStats.get(name); - if (stats == null) return; - - if (stats.requested) { - stats.requested = false; - mLocationManager.removeUpdates(this); //TODO GLOBAL - } - } - - private void updateRequirements() { - if (mRequest == null || !mRequest.getReportLocation()) { - mRequest = null; - disableProvider(NETWORK); - disableProvider(GPS); - return; - } - - long networkInterval = Long.MAX_VALUE; - long gpsInterval = Long.MAX_VALUE; - for (LocationRequestUnbundled request : mRequest.getLocationRequests()) { - switch (request.getQuality()) { - case LocationRequestUnbundled.ACCURACY_FINE: - case LocationRequestUnbundled.POWER_HIGH: - if (request.getInterval() < gpsInterval) { - gpsInterval = request.getInterval(); - } - if (request.getInterval() < networkInterval) { - networkInterval = request.getInterval(); - } - break; - case LocationRequestUnbundled.ACCURACY_BLOCK: - case LocationRequestUnbundled.ACCURACY_CITY: - case LocationRequestUnbundled.POWER_LOW: - if (request.getInterval() < networkInterval) { - networkInterval = request.getInterval(); - } - break; - } - } - - if (gpsInterval < Long.MAX_VALUE) { - enableProvider(GPS, gpsInterval); - } else { - disableProvider(GPS); - } - if (networkInterval < Long.MAX_VALUE) { - enableProvider(NETWORK, networkInterval); - } else { - disableProvider(NETWORK); - } - } - - /** - * Test whether one location (a) is better to use than another (b). - */ - private static boolean isBetterThan(Location locationA, Location locationB) { - if (locationA == null) { - return false; - } - if (locationB == null) { - return true; - } - // A provider is better if the reading is sufficiently newer. Heading - // underground can cause GPS to stop reporting fixes. In this case it's - // appropriate to revert to cell, even when its accuracy is less. - if (locationA.getElapsedRealtimeNanos() - > locationB.getElapsedRealtimeNanos() + SWITCH_ON_FRESHNESS_CLIFF_NS) { - return true; - } - - // A provider is better if it has better accuracy. Assuming both readings - // are fresh (and by that accurate), choose the one with the smaller - // accuracy circle. - if (!locationA.hasAccuracy()) { - return false; - } - if (!locationB.hasAccuracy()) { - return true; - } - return locationA.getAccuracy() < locationB.getAccuracy(); - } - - private void updateFusedLocation() { - // may the best location win! - if (isBetterThan(mGpsLocation, mNetworkLocation)) { - mFusedLocation = new Location(mGpsLocation); - } else { - mFusedLocation = new Location(mNetworkLocation); - } - mFusedLocation.setProvider(FUSED); - if (mNetworkLocation != null) { - // copy NO_GPS_LOCATION extra from mNetworkLocation into mFusedLocation - Bundle srcExtras = mNetworkLocation.getExtras(); - if (srcExtras != null) { - Parcelable srcParcelable = - srcExtras.getParcelable(LocationProviderBase.EXTRA_NO_GPS_LOCATION); - if (srcParcelable instanceof Location) { - Bundle dstExtras = mFusedLocation.getExtras(); - if (dstExtras == null) { - dstExtras = new Bundle(); - mFusedLocation.setExtras(dstExtras); - } - dstExtras.putParcelable(LocationProviderBase.EXTRA_NO_GPS_LOCATION, - srcParcelable); - } - } - } - - if (mCallback != null) { - mCallback.reportLocation(mFusedLocation); - } else { - Log.w(TAG, "Location updates received while fusion engine not started"); - } - } - - /** Called on mLooper thread */ - @Override - public void onLocationChanged(Location location) { - if (GPS.equals(location.getProvider())) { - mGpsLocation = location; - updateFusedLocation(); - } else if (NETWORK.equals(location.getProvider())) { - mNetworkLocation = location; - updateFusedLocation(); - } - } - - /** Called on mLooper thread */ - @Override - public void onStatusChanged(String provider, int status, Bundle extras) { - } - - /** Called on mLooper thread */ - @Override - public void onProviderEnabled(String provider) { - } - - /** Called on mLooper thread */ - @Override - public void onProviderDisabled(String provider) { - } - - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - StringBuilder s = new StringBuilder(); - s.append(mRequest).append('\n'); - s.append("fused=").append(mFusedLocation).append('\n'); - s.append(String.format("gps %s\n", mGpsLocation)); - s.append(" ").append(mStats.get(GPS)).append('\n'); - s.append(String.format("net %s\n", mNetworkLocation)); - s.append(" ").append(mStats.get(NETWORK)).append('\n'); - pw.append(s); - } - - /** Called on mLooper thread */ - public void switchUser() { - // reset state to prevent location data leakage - mFusedLocation = null; - mGpsLocation = null; - mNetworkLocation = null; - } -} diff --git a/packages/FusedLocation/test/AndroidManifest.xml b/packages/FusedLocation/test/AndroidManifest.xml new file mode 100644 index 000000000000..d6c4107a6e4a --- /dev/null +++ b/packages/FusedLocation/test/AndroidManifest.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.location.fused.tests"> + + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> + <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/> + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> + <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" /> + + <application android:label="FusedLocation Tests"> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.location.fused.tests" + android:label="FusedLocation Tests" /> +</manifest> diff --git a/packages/FusedLocation/test/AndroidTest.xml b/packages/FusedLocation/test/AndroidTest.xml new file mode 100644 index 000000000000..f88e595e9763 --- /dev/null +++ b/packages/FusedLocation/test/AndroidTest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<configuration description="FusedLocation Tests"> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="FusedLocationTests.apk" /> + </target_preparer> + + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="framework-base-presubmit" /> + <option name="test-tag" value="FusedLocationTests" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.location.fused.tests" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration>
\ No newline at end of file diff --git a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java new file mode 100644 index 000000000000..33126510bc53 --- /dev/null +++ b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.location.fused.tests; + +import static android.location.LocationManager.FUSED_PROVIDER; +import static android.location.LocationManager.GPS_PROVIDER; +import static android.location.LocationManager.NETWORK_PROVIDER; + +import static androidx.test.ext.truth.location.LocationSubject.assertThat; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.location.Criteria; +import android.location.Location; +import android.location.LocationManager; +import android.location.LocationRequest; +import android.os.ParcelFileDescriptor; +import android.os.SystemClock; +import android.os.WorkSource; +import android.util.Log; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.location.ILocationProvider; +import com.android.internal.location.ILocationProviderManager; +import com.android.internal.location.ProviderProperties; +import com.android.internal.location.ProviderRequest; +import com.android.location.fused.FusedLocationProvider; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +@RunWith(AndroidJUnit4.class) +public class FusedLocationServiceTest { + + private static final String TAG = "FusedLocationServiceTest"; + + private static final long TIMEOUT_MS = 5000; + + private Context mContext; + private Random mRandom; + private LocationManager mLocationManager; + + private ILocationProvider mProvider; + private LocationProviderManagerCapture mManager; + + @Before + public void setUp() throws Exception { + long seed = System.currentTimeMillis(); + Log.i(TAG, "location seed: " + seed); + + mContext = InstrumentationRegistry.getTargetContext(); + mRandom = new Random(seed); + mLocationManager = mContext.getSystemService(LocationManager.class); + + setMockLocation(true); + + mManager = new LocationProviderManagerCapture(); + mProvider = ILocationProvider.Stub.asInterface( + new FusedLocationProvider(mContext).getBinder()); + mProvider.setLocationProviderManager(mManager); + + mLocationManager.addTestProvider(NETWORK_PROVIDER, + true, + false, + true, + false, + false, + false, + false, + Criteria.POWER_MEDIUM, + Criteria.ACCURACY_FINE); + mLocationManager.setTestProviderEnabled(NETWORK_PROVIDER, true); + mLocationManager.addTestProvider(GPS_PROVIDER, + true, + false, + true, + false, + false, + false, + false, + Criteria.POWER_MEDIUM, + Criteria.ACCURACY_FINE); + mLocationManager.setTestProviderEnabled(GPS_PROVIDER, true); + } + + @After + public void tearDown() throws Exception { + for (String provider : mLocationManager.getAllProviders()) { + mLocationManager.removeTestProvider(provider); + } + + setMockLocation(false); + } + + @Test + public void testNetworkRequest() throws Exception { + LocationRequest request = LocationRequest.createFromDeprecatedProvider(FUSED_PROVIDER, 1000, + 0, false); + + mProvider.setRequest( + new ProviderRequest.Builder() + .setInterval(1000) + .setLocationRequests(Collections.singletonList(request)) + .build(), + new WorkSource()); + + Location location = createLocation(NETWORK_PROVIDER, mRandom); + mLocationManager.setTestProviderLocation(NETWORK_PROVIDER, location); + + assertThat(mManager.getNextLocation(TIMEOUT_MS)).isEqualTo(location); + } + + @Test + public void testGpsRequest() throws Exception { + LocationRequest request = LocationRequest.createFromDeprecatedProvider(FUSED_PROVIDER, 1000, + 0, false).setQuality(LocationRequest.POWER_HIGH); + + mProvider.setRequest( + new ProviderRequest.Builder() + .setInterval(1000) + .setLocationRequests(Collections.singletonList(request)) + .build(), + new WorkSource()); + + Location location = createLocation(GPS_PROVIDER, mRandom); + mLocationManager.setTestProviderLocation(GPS_PROVIDER, location); + + assertThat(mManager.getNextLocation(TIMEOUT_MS)).isEqualTo(location); + } + + @Test + public void testBypassRequest() throws Exception { + LocationRequest request = LocationRequest.createFromDeprecatedProvider(FUSED_PROVIDER, 1000, + 0, false).setQuality(LocationRequest.POWER_HIGH).setLocationSettingsIgnored(true); + + mProvider.setRequest( + new ProviderRequest.Builder() + .setInterval(1000) + .setLocationSettingsIgnored(true) + .setLocationRequests(Collections.singletonList(request)) + .build(), + new WorkSource()); + + boolean containsNetworkBypass = false; + for (LocationRequest iRequest : mLocationManager.getTestProviderCurrentRequests( + NETWORK_PROVIDER)) { + if (iRequest.isLocationSettingsIgnored()) { + containsNetworkBypass = true; + break; + } + } + + boolean containsGpsBypass = false; + for (LocationRequest iRequest : mLocationManager.getTestProviderCurrentRequests( + GPS_PROVIDER)) { + if (iRequest.isLocationSettingsIgnored()) { + containsGpsBypass = true; + break; + } + } + + assertThat(containsNetworkBypass).isTrue(); + assertThat(containsGpsBypass).isTrue(); + } + + private static class LocationProviderManagerCapture extends ILocationProviderManager.Stub { + + private final LinkedBlockingQueue<Location> mLocations; + + private LocationProviderManagerCapture() { + mLocations = new LinkedBlockingQueue<>(); + } + + @Override + public void onSetAdditionalProviderPackages(List<String> packageNames) { + + } + + @Override + public void onSetEnabled(boolean enabled) { + + } + + @Override + public void onSetProperties(ProviderProperties properties) { + + } + + @Override + public void onReportLocation(Location location) { + mLocations.add(location); + } + + public Location getNextLocation(long timeoutMs) throws InterruptedException { + return mLocations.poll(timeoutMs, TimeUnit.MILLISECONDS); + } + } + + private static final double MIN_LATITUDE = -90D; + private static final double MAX_LATITUDE = 90D; + private static final double MIN_LONGITUDE = -180D; + private static final double MAX_LONGITUDE = 180D; + + private static final float MIN_ACCURACY = 1; + private static final float MAX_ACCURACY = 100; + + private static Location createLocation(String provider, Random random) { + return createLocation(provider, + MIN_LATITUDE + random.nextDouble() * (MAX_LATITUDE - MIN_LATITUDE), + MIN_LONGITUDE + random.nextDouble() * (MAX_LONGITUDE - MIN_LONGITUDE), + MIN_ACCURACY + random.nextFloat() * (MAX_ACCURACY - MIN_ACCURACY)); + } + + private static Location createLocation(String provider, double latitude, double longitude, + float accuracy) { + Location location = new Location(provider); + location.setLatitude(latitude); + location.setLongitude(longitude); + location.setAccuracy(accuracy); + location.setTime(System.currentTimeMillis()); + location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); + return location; + } + + private static void setMockLocation(boolean allowed) throws IOException { + ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation() + .executeShellCommand("appops set " + + InstrumentationRegistry.getTargetContext().getPackageName() + + " android:mock_location " + (allowed ? "allow" : "deny")); + try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + byte[] buffer = new byte[32768]; + int count; + try { + while ((count = fis.read(buffer)) != -1) { + os.write(buffer, 0, count); + } + fis.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + Log.e(TAG, new String(os.toByteArray())); + } + } +} diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java index 50f858eb04c1..e87148e2559e 100644 --- a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java +++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java @@ -211,6 +211,12 @@ public class LocalTransport extends BackupTransport { private int performBackupInternal( PackageInfo packageInfo, ParcelFileDescriptor data, int flags) { + if ((flags & BackupTransport.FLAG_DATA_NOT_CHANGED) != 0) { + // For unchanged data notifications we do nothing and tell the + // caller everything was OK + return BackupTransport.TRANSPORT_OK; + } + boolean isIncremental = (flags & FLAG_INCREMENTAL) != 0; boolean isNonIncremental = (flags & FLAG_NON_INCREMENTAL) != 0; diff --git a/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml b/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml index ddf838ea8fe4..85a8d7342827 100644 --- a/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml @@ -17,5 +17,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="search_menu" msgid="1914043873178389845">"Pesquisar definições"</string> + <string name="search_menu" msgid="1914043873178389845">"Pesquisa de definições"</string> </resources> diff --git a/packages/SettingsLib/res/values-af/arrays.xml b/packages/SettingsLib/res/values-af/arrays.xml index d9aaf7dacafc..8a02c776751e 100644 --- a/packages/SettingsLib/res/values-af/arrays.xml +++ b/packages/SettingsLib/res/values-af/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (verstek)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Gebruik stelselkeuse (verstek)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 25903388c6ca..2b143e425589 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktiveer Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-weergawe"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Kies Bluetooth AVRCP-weergawe"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-weergawe"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Kies Bluetooth MAP-weergawe"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth-oudiokodek"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Gebruik Bluetooth-oudiokodek\nKeuse"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth-oudiovoorbeeldkoers"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Kontroleer programme wat via ADB/ADT geïnstalleer is vir skadelike gedrag."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bluetooth-toestelle sonder name (net MAC-adresse) sal gewys word"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Deaktiveer die Bluetooth-kenmerk vir absolute volume indien daar volumeprobleme met afgeleë toestelle is, soos onaanvaarbare harde klank of geen beheer nie."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Aktiveer die Bluetooth Gabeldorsche-kenmerkstapel."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Aktiveer die Bluetooth Gabeldorsche-kenmerkstapel."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Plaaslike terminaal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Aktiveer terminaalprogram wat plaaslike skermtoegang bied"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP-kontrolering"</string> diff --git a/packages/SettingsLib/res/values-am/arrays.xml b/packages/SettingsLib/res/values-am/arrays.xml index fff7cae377d4..6a9334e3b65f 100644 --- a/packages/SettingsLib/res/values-am/arrays.xml +++ b/packages/SettingsLib/res/values-am/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (ነባሪ)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"የስርዓቱን ምርጫ (ነባሪ) ተጠቀም"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 8277711a9870..2a93e01765bb 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorscheን አንቃ"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"የብሉቱዝ AVRCP ስሪት"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"የብሉቱዝ AVRCP ስሪት ይምረጡ"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"የብሉቱዝ MAP ስሪት"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"የብሉቱዝ MAP ስሪቱን ይምረጡ"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"የብሉቱዝ ኦዲዮ ኮዴክ"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"የብሉቱዝ ኦዲዮ ኮዴክ አስጀምር\nምርጫ"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"የብሉቱዝ ኦዲዮ ናሙና ፍጥነት"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"በADB/ADT በኩል የተጫኑ መተግበሪያዎች ጎጂ ባህሪ ካላቸው ያረጋግጡ።"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"የብሉቱዝ መሣሪያዎች ያለ ስሞች (MAC አድራሻዎች ብቻ) ይታያሉ"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"እንደ ተቀባይነት በሌለው ደረጃ ድምፁ ከፍ ማለት ወይም መቆጣጠር አለመቻል ያሉ ከሩቅ መሣሪያዎች ጋር የድምፅ ችግር በሚኖርበት ጊዜ የብሉቱዝ ፍጹማዊ ድምፅን ባሕሪ ያሰናክላል።"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"የብሉቱዝ Gabeldorche ባህሪ ቁልልን ያነቃል።"</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"የብሉቱዝ Gabeldorsche ባህሪ ቁልሉን ያነቃል።"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"አካባቢያዊ ተርሚናል"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"የአካባቢያዊ ሼል መዳረሻ የሚያቀርብ የተርሚናል መተግበሪያ አንቃ"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"የHDCP ምልከታ"</string> diff --git a/packages/SettingsLib/res/values-ar/arrays.xml b/packages/SettingsLib/res/values-ar/arrays.xml index b4f5253870c1..851a3d8f136a 100644 --- a/packages/SettingsLib/res/values-ar/arrays.xml +++ b/packages/SettingsLib/res/values-ar/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (الإعداد الافتراضي)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"استخدام اختيار النظام (تلقائي)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 43d2eceab51d..54856566e021 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"تفعيل Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"إصدار Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"اختيار إصدار Bluetooth AVRCP"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"إصدار Bluetooth MAP"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"اختيار إصدار Bluetooth MAP"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"برنامج ترميز صوت بلوتوث"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"اختيار برنامج ترميز الصوت لمشغّل\nالبلوتوث"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"معدّل عيّنة صوت بلوتوث"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"التحقق من التطبيقات المثبتة عبر ADB/ADT لكشف السلوك الضار"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"سيتم عرض أجهزة البلوتوث بدون أسماء (عناوين MAC فقط)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"لإيقاف ميزة مستوى الصوت المطلق للبلوتوث في حال حدوث مشاكل متعلقة بمستوى الصوت في الأجهزة البعيدة، مثل مستوى صوت عالٍ بشكل غير مقبول أو عدم إمكانية التحكّم في الصوت"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"تفعيل حِزم ميزة Bluetooth Gabeldorche"</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"تفعيل حِزم ميزة Bluetooth Gabeldorsche"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"تطبيق طرفي محلي"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"تفعيل تطبيق طرفي يوفر إمكانية الدخول إلى واجهة النظام المحلية"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"التحقق من HDCP"</string> @@ -435,7 +437,7 @@ <string name="screen_zoom_summary_extremely_large" msgid="1438045624562358554">"أكبر مستوى"</string> <string name="screen_zoom_summary_custom" msgid="3468154096832912210">"مخصص (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string> <string name="content_description_menu_button" msgid="6254844309171779931">"القائمة"</string> - <string name="retail_demo_reset_message" msgid="5392824901108195463">"إدخال كلمة المرور لإعادة الضبط بحسب بيانات المصنع في الوضع التجريبي"</string> + <string name="retail_demo_reset_message" msgid="5392824901108195463">"إدخال كلمة المرور لإعادة الضبط على الإعدادات الأصلية في الوضع التجريبي"</string> <string name="retail_demo_reset_next" msgid="3688129033843885362">"التالي"</string> <string name="retail_demo_reset_title" msgid="1866911701095959800">"يلزم توفر كلمة مرور"</string> <string name="active_input_method_subtypes" msgid="4232680535471633046">"طرق الإدخال النشطة"</string> diff --git a/packages/SettingsLib/res/values-as/arrays.xml b/packages/SettingsLib/res/values-as/arrays.xml index 1a044f5ade70..503c13edebba 100644 --- a/packages/SettingsLib/res/values-as/arrays.xml +++ b/packages/SettingsLib/res/values-as/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp১৫"</item> <item msgid="1963366694959681026">"avrcp১৬"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (ডিফ’ল্ট)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"ছিষ্টেমৰ বাছনি ব্যৱহাৰ কৰক (ডিফ\'ল্ট)"</item> <item msgid="4055460186095649420">"এছবিচি"</item> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index 3f85fe3dad37..fa26b9b93ef6 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche সক্ষম কৰক"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ব্লুটুথ AVRCP সংস্কৰণ"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ব্লুটুথ AVRCP সংস্কৰণ বাছনি কৰক"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ব্লুটুথ MAP সংস্কৰণ"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"ব্লুটুথ MAP সংস্কৰণ বাছনি কৰক"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"ব্লুটুথ অডিঅ’ ক’ডেক"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"ব্লুটুথ অডিঅ\' ক\'ডেকৰ বাছনি\nআৰম্ভ কৰক"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"ব্লুটুথ অডিঅ\' ছেম্পল ৰেইট"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ADB/ADTৰ যোগেৰে ইনষ্টল কৰা এপসমূহে কিবা ক্ষতিকাৰক আচৰণ কৰিছে নেকি পৰীক্ষা কৰক।"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"নামহীন ব্লুটুথ ডিভাইচসমূহ (মাত্ৰ MAC ঠিকনাযুক্ত) দেখুওৱা হ\'ব"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"ৰিম\'ট ডিভাইচবিলাকৰ সৈতে ভলিউম সম্পৰ্কীয় সমস্যা, যেনেকৈ অতি উচ্চ ভলিউম বা নিয়ন্ত্ৰণ কৰিবই নোৱাৰা অৱস্থাত ব্লুটুথৰ পূৰ্ণ ভলিউম সুবিধা অক্ষম কৰে।"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"ব্লুটুথ Gabeldorche সুবিধাৰ সমষ্টিটো সক্ষম কৰে।"</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"ব্লুটুথ Gabeldorche সুবিধাৰ সমষ্টিটো সক্ষম কৰে।"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"স্থানীয় টাৰ্মিনেল"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"স্থানীয় শ্বেল প্ৰৱেশাধিকাৰ দিয়া টাৰ্মিনেল এপ্ সক্ষম কৰক"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP পৰীক্ষণ"</string> diff --git a/packages/SettingsLib/res/values-az/arrays.xml b/packages/SettingsLib/res/values-az/arrays.xml index eb81381a4439..005bdf7e6fc0 100644 --- a/packages/SettingsLib/res/values-az/arrays.xml +++ b/packages/SettingsLib/res/values-az/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (Defolt)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Sistem Seçimini istifadə edin (Defolt)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 3a1a54341aaf..2e80fdcb8972 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche\'ni aktiv edin"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP Versiya"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP Versiyasını seçin"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP Versiyası"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Bluetooth MAP Versiyasını seçin"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth Audio Kodek"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Bluetooth Audio KodeK\nSeçimini aktiv edin"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth Audio Nümunə Göstəricisi"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ADB/ADT vasitəsi ilə quraşdırılmış tətbiqləri zərərli davranış üzrə yoxlayın."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Adsız Bluetooth cihazları (yalnız MAC ünvanları) göstəriləcək"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Uzaqdan idarə olunan cihazlarda dözülməz yüksək səs həcmi və ya nəzarət çatışmazlığı kimi səs problemləri olduqda Bluetooth mütləq səs həcmi xüsusiyyətini deaktiv edir."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Bluetooth Gabeldorche xüsusiyyətini aktiv edir."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Bluetooth Gabeldorsche funksiyasını aktiv edir."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Yerli terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Yerli örtük girişini təklif edən terminal tətbiqi aktiv edin"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP yoxlanılır"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml index c7b63b3f7f3e..10c0d6c62262 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (podrazumevano)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Koristi izbor sistema (podrazumevano)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 02e5e3b5c435..ae5c93670644 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogući Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verzija Bluetooth AVRCP-a"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Izaberite verziju Bluetooth AVRCP-a"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verzija Bluetooth MAP-a"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Izaberite verziju Bluetooth MAP-a"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth audio kodek"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Izaberite Bluetooth audio kodek\n"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Brzina uzorkovanja za Bluetooth audio"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Proverava da li su aplikacije instalirane preko ADB-a/ADT-a štetne."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Biće prikazani Bluetooth uređaji bez naziva (samo sa MAC adresama)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Onemogućava glavno podešavanje jačine zvuka na Bluetooth uređaju u slučaju problema sa jačinom zvuka na daljinskim uređajima, kao što su izuzetno velika jačina zvuka ili nedostatak kontrole."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Omogućava grupu Bluetooth Gabeldorche funkcija."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Omogućava grupu Bluetooth Gabeldorsche funkcija."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Lokalni terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Omogući apl. terminala za pristup lokalnom komandnom okruženju"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP provera"</string> diff --git a/packages/SettingsLib/res/values-be/arrays.xml b/packages/SettingsLib/res/values-be/arrays.xml index 9eaab6a6829a..e05fd602e303 100644 --- a/packages/SettingsLib/res/values-be/arrays.xml +++ b/packages/SettingsLib/res/values-be/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (стандартна)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Выбар сістэмы (стандартны)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 481dfb0fd697..ad201d081fce 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Уключыць Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версія Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Выбраць версію Bluetooth AVRCP"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Версія Bluetooth MAP"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Выбраць версію Bluetooth MAP"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Кодэк Bluetooth Audio"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Уключыць кодэк Bluetooth Audio\nВыбар"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Частата дыскрэтызацыі Bluetooth Audio"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Праверка бяспекі праграм, усталяваных з дапамогай ADB/ADT."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Прылады Bluetooth будуць паказаны без назваў (толькі MAC-адрасы)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Адключыць функцыю абсалютнага гуку Bluetooth у выпадку праблем з гукам на аддаленых прыладах, напрыклад, пры непрымальна высокай гучнасці або адсутнасці кіравання."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Уключае стос функцый Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Уключае стос функцый Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Лакальны тэрмінал"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Уключэнне прыкладання тэрмінала, якое прапануе доступ да лакальнай абалонкі"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Праверка HDCP"</string> diff --git a/packages/SettingsLib/res/values-bg/arrays.xml b/packages/SettingsLib/res/values-bg/arrays.xml index e7976fcc7bd5..a071baf97f14 100644 --- a/packages/SettingsLib/res/values-bg/arrays.xml +++ b/packages/SettingsLib/res/values-bg/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (по подразбиране)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Използване на сист. избор (стандартно)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 2a0264039a78..94f78ad30a67 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Активиране на Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версия на AVRCP за Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Избиране на версия на AVRCP за Bluetooth"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP версия за Bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Изберете MAP версия за Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Аудиокодек за Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Задействане на аудиокодек за Bluetooth\nИзбор"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Честота на дискретизация за звука през Bluetooth"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Проверка на инсталираните чрез ADB/ADT приложения за опасно поведение."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Ще бъдат показани устройствата с Bluetooth без имена (само MAC адресите)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Деактивира функцията на Bluetooth за пълна сила на звука в случай на проблеми със звука на отдалечени устройства, като например неприемливо висока сила на звука или липса на управление."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Активира стека на функциите на Bluetooth Gabeldorsche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Активира стека на функциите на Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Локален терминал"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Актив. на прил. за терминал с достъп до локалния команден ред"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Проверка с HDCP"</string> diff --git a/packages/SettingsLib/res/values-bn/arrays.xml b/packages/SettingsLib/res/values-bn/arrays.xml index a67b9eb493c4..a131a3b1ad91 100644 --- a/packages/SettingsLib/res/values-bn/arrays.xml +++ b/packages/SettingsLib/res/values-bn/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (ডিফল্ট)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 9bebe419470b..517191601d2f 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ফিচার চালু করুন"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ব্লুটুথ AVRCP ভার্সন"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ব্লুটুথ AVRCP ভার্সন বেছে নিন"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ব্লুটুথ MAP ভার্সন"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"ব্লুটুথ MAP ভার্সন বেছে নিন"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"ব্লুটুথ অডিও কোডেক"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"ব্লুটুথ অডিও কোডেক ট্রিগার করুন\nএটি বেছে নেওয়া আছে"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"ব্লুটুথ অডিওর নমুনা হার"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ক্ষতিকারক ক্রিয়াকলাপ করছে কিনা তার জন্য ADB/ADT মারফত ইনস্টল করা অ্যাপ্লিকেশানগুলি চেক করুন।"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"নামহীন ব্লুটুথ ডিভাইসগুলি দেখানো হবে (শুধুমাত্র MAC অ্যাড্রেস)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"অপ্রত্যাশিত উচ্চ ভলিউম বা নিয়ন্ত্রণের অভাবের মত দূরবর্তী ডিভাইসের ভলিউম সমস্যাগুলির ক্ষেত্রে, ব্লুটুথ চুড়ান্ত ভলিউম বৈশিষ্ট্য অক্ষম করে৷"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"ব্লুটুথ Gabeldorche ফিচার স্ট্যাক চালু করা হয়েছে।"</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"ব্লুটুথ Gabeldorche ফিচার স্ট্যাক চালু করে।"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"স্থানীয় টার্মিনাল"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"স্থানীয় শেল অ্যাক্সেসের প্রস্তাব করে এমন টার্মিনাল অ্যাপ্লিকেশন সক্ষম করুন"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP পরীক্ষণ"</string> diff --git a/packages/SettingsLib/res/values-bs/arrays.xml b/packages/SettingsLib/res/values-bs/arrays.xml index d8b2bcd25fc1..6489cefdc551 100644 --- a/packages/SettingsLib/res/values-bs/arrays.xml +++ b/packages/SettingsLib/res/values-bs/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (Zadano)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Korištenje odabira sistema (zadano)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index f2446ad7ba07..f74bcee6fd08 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogući Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP verzija"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Odaberite Bluetooth AVRCP verziju"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP verzija"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Odaberite Bluetooth MAP verziju"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth Audio kodek"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Aktivirajte Bluetooth Audio Codec\nOdabir"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Brzina uzorkovanja za Bluetooth audio"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Provjerava da li se u aplikacijama instaliranim putem ADB-a/ADT-a javlja zlonamjerno ponašanje."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Prikazat će se Bluetooth uređaji bez naziva (samo MAC adrese)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Onemogućava funkciju apsolutne jačine zvuka za Bluetooth u slučaju problema s jačinom zvuka na udaljenim uređajima, kao što je neprihvatljivo glasan zvuk ili nedostatak kontrole."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Omogućava grupisanje funkcije Bluetooth Gabeldorsche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Omogućava grupisanje funkcije Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Lokalni terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Omogući terminalnu aplik. koja nudi pristup lok. kom. okruženju"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP provjera"</string> diff --git a/packages/SettingsLib/res/values-ca/arrays.xml b/packages/SettingsLib/res/values-ca/arrays.xml index 600a7ce6c808..950e469cef46 100644 --- a/packages/SettingsLib/res/values-ca/arrays.xml +++ b/packages/SettingsLib/res/values-ca/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (predeterminada)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Utilitza selecció del sistema (predeterminada)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index e33fd29e9a8c..1b23ecfd904b 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activa Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versió AVRCP de Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona la versió AVRCP de Bluetooth"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versió MAP de Bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Selecciona la versió MAP de Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Còdec d\'àudio per Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Activa el còdec d\'àudio per Bluetooth\nSelecció"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Freqüència de mostratge d’àudio per Bluetooth"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Comprova les aplicacions instal·lades mitjançant ADB/ADT per detectar comportaments perillosos"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Es mostraran els dispositius Bluetooth sense el nom (només l\'adreça MAC)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Desactiva la funció de volum absolut del Bluetooth en cas que es produeixin problemes de volum amb dispositius remots, com ara un volum massa alt o una manca de control."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Activa el conjunt de funcions de Bluetooth Gabeldorsche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Activa el conjunt de funcions de Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal local"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Activa l\'aplicació de terminal que ofereix accés al shell local"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Comprovació d\'HDCP"</string> diff --git a/packages/SettingsLib/res/values-cs/arrays.xml b/packages/SettingsLib/res/values-cs/arrays.xml index e7474a92c972..16358ee7b106 100644 --- a/packages/SettingsLib/res/values-cs/arrays.xml +++ b/packages/SettingsLib/res/values-cs/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (výchozí)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Použít systémový výběr (výchozí)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index e7875107cadb..22603ccdd804 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Zapnout funkci Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verze profilu Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Vyberte verzi profilu Bluetooth AVRCP"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verze MAP pro Bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Vyberte verzi MAP pro Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth Audio – kodek"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Spustit zvukový kodek Bluetooth\nVýběr"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth Audio – vzorkovací frekvence"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Kontrolovat škodlivost aplikací nainstalovaných pomocí nástroje ADB/ADT"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Zařízení Bluetooth se budou zobrazovat bez názvů (pouze adresy MAC)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Zakáže funkci absolutní hlasitosti Bluetooth. Zabrání tak problémům s hlasitostí vzdálených zařízení (jako je příliš vysoká hlasitost nebo nemožnost ovládání)."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Zapnout sadu funkcí Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Zapne sadu funkcí Bluetooth Gabeldorche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Místní terminál"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Aktivovat terminálovou aplikaci pro místní přístup k prostředí shell"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Kontrola HDCP"</string> diff --git a/packages/SettingsLib/res/values-da/arrays.xml b/packages/SettingsLib/res/values-da/arrays.xml index 0394562207a9..b3ce2572dbcf 100644 --- a/packages/SettingsLib/res/values-da/arrays.xml +++ b/packages/SettingsLib/res/values-da/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (standard)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Brug systemvalg (standard)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 4e36e388026a..bef1855570a1 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktivér Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"AVRCP-version for Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Vælg AVRCP-version for Bluetooth"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP-version for Bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Vælg MAP-version for Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth-lydcodec"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Udløs codec for Bluetooth-lyd\nValg"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Eksempelfrekvens for Bluetooth-lyd"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Tjek apps, der er installeret via ADB/ADT, for skadelig adfærd."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bluetooth-enheder uden navne (kun MAC-adresser) vises"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Deaktiverer funktionen til absolut lydstyrke via Bluetooth i tilfælde af problemer med lydstyrken på eksterne enheder, f.eks. uacceptabel høj lyd eller manglende kontrol."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Aktiverer funktioner fra Bluetooth Gabeldorche"</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Aktiverer funktioner fra Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Lokal terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Aktivér terminalappen, der giver lokal shell-adgang"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP-kontrol"</string> diff --git a/packages/SettingsLib/res/values-de/arrays.xml b/packages/SettingsLib/res/values-de/arrays.xml index d7d3226d9bb4..fdd799c48913 100644 --- a/packages/SettingsLib/res/values-de/arrays.xml +++ b/packages/SettingsLib/res/values-de/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (Standard)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Systemauswahl verwenden (Standard)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 63699065a651..c8c97bdf3d42 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Bluetooth-Gabeldorsche aktivieren"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-Version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP-Version auswählen"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-Version"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Bluetooth MAP-Version auswählen"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth-Audio-Codec"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Bluetooth-Audio-Codec auslösen\nAuswahl"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth-Audio-Abtastrate"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Über ADB/ADT installierte Apps werden auf schädliches Verhalten geprüft"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bluetooth-Geräte werden ohne Namen und nur mit ihren MAC-Adressen angezeigt"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Deaktiviert die Funktion \"Absolute Lautstärkeregelung\" für Bluetooth-Geräte, falls auf Remote-Geräten Probleme mit der Lautstärke auftreten, wie beispielsweise übermäßig laute Wiedergabe oder fehlende Steuerungsmöglichkeiten."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Aktiviert das Bluetooth-Gabeldorsche-Funktionspaket."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Aktiviert das Bluetooth-Gabeldorsche-Funktionspaket."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Lokales Terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Terminal-App mit Zugriff auf lokale Shell aktivieren"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP-Prüfung"</string> diff --git a/packages/SettingsLib/res/values-el/arrays.xml b/packages/SettingsLib/res/values-el/arrays.xml index 5c4ef134776b..79f631fc27fc 100644 --- a/packages/SettingsLib/res/values-el/arrays.xml +++ b/packages/SettingsLib/res/values-el/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (Προεπιλογή)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Χρήση επιλογής συστήματος (Προεπιλογή)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 194324f7de5c..371075c7acc0 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ενεργοποίηση Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Έκδοση AVRCP Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Επιλογή έκδοσης AVRCP Bluetooth"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Έκδοση MAP Bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Επιλογή έκδοσης MAP Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Κωδικοποιητής ήχου Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Ενεργοποίηση κωδικοποιητή ήχου Bluetooth\nΕπιλογή"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Ρυθμός δειγματοληψίας ήχου Bluetooth"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Έλεγχος εφαρμογών που έχουν εγκατασταθεί μέσω ADB/ADT για επιβλαβή συμπεριφορά."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Θα εμφανιστούν οι συσκευές Bluetooth χωρίς ονόματα (μόνο διευθύνσεις MAC)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Απενεργοποιεί τη δυνατότητα απόλυτης έντασης του Bluetooth σε περίπτωση προβλημάτων έντασης με απομακρυσμένες συσκευές, όπως όταν υπάρχει μη αποδεκτά υψηλή ένταση ή απουσία ελέγχου."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Ενεργοποιεί τη στοίβα λειτουργιών Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Ενεργοποιεί τη στοίβα λειτουργιών Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Τοπική τερματική εφαρμογή"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Ενεργοπ.τερμ.εφαρμογής που προσφέρει πρόσβαση στο τοπικό κέλυφος"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Έλεγχος HDCP"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/arrays.xml b/packages/SettingsLib/res/values-en-rAU/arrays.xml index ae1fb78d97f6..97e598e7600b 100644 --- a/packages/SettingsLib/res/values-en-rAU/arrays.xml +++ b/packages/SettingsLib/res/values-en-rAU/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (Default)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Use system selection (default)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 92ed8e5359b6..b314d1782585 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Select Bluetooth MAP version"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth audio codec"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Trigger Bluetooth Audio Codec\nSelection"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth audio sample rate"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Check apps installed via ADB/ADT for harmful behaviour."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bluetooth devices without names (MAC addresses only) will be displayed"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Disables the Bluetooth absolute volume feature in case of volume issues with remote devices such as unacceptably loud volume or lack of control."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Enables the Bluetooth Gabeldorsche feature stack."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Enables the Bluetooth Gabeldorsche feature stack."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Local terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Enable terminal app that offers local shell access"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP checking"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/arrays.xml b/packages/SettingsLib/res/values-en-rCA/arrays.xml index ae1fb78d97f6..97e598e7600b 100644 --- a/packages/SettingsLib/res/values-en-rCA/arrays.xml +++ b/packages/SettingsLib/res/values-en-rCA/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (Default)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Use system selection (default)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index 92ed8e5359b6..b314d1782585 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Select Bluetooth MAP version"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth audio codec"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Trigger Bluetooth Audio Codec\nSelection"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth audio sample rate"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Check apps installed via ADB/ADT for harmful behaviour."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bluetooth devices without names (MAC addresses only) will be displayed"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Disables the Bluetooth absolute volume feature in case of volume issues with remote devices such as unacceptably loud volume or lack of control."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Enables the Bluetooth Gabeldorsche feature stack."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Enables the Bluetooth Gabeldorsche feature stack."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Local terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Enable terminal app that offers local shell access"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP checking"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/arrays.xml b/packages/SettingsLib/res/values-en-rGB/arrays.xml index ae1fb78d97f6..97e598e7600b 100644 --- a/packages/SettingsLib/res/values-en-rGB/arrays.xml +++ b/packages/SettingsLib/res/values-en-rGB/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (Default)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Use system selection (default)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 92ed8e5359b6..b314d1782585 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Select Bluetooth MAP version"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth audio codec"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Trigger Bluetooth Audio Codec\nSelection"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth audio sample rate"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Check apps installed via ADB/ADT for harmful behaviour."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bluetooth devices without names (MAC addresses only) will be displayed"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Disables the Bluetooth absolute volume feature in case of volume issues with remote devices such as unacceptably loud volume or lack of control."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Enables the Bluetooth Gabeldorsche feature stack."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Enables the Bluetooth Gabeldorsche feature stack."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Local terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Enable terminal app that offers local shell access"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP checking"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/arrays.xml b/packages/SettingsLib/res/values-en-rIN/arrays.xml index ae1fb78d97f6..97e598e7600b 100644 --- a/packages/SettingsLib/res/values-en-rIN/arrays.xml +++ b/packages/SettingsLib/res/values-en-rIN/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (Default)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Use system selection (default)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 92ed8e5359b6..b314d1782585 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Select Bluetooth MAP version"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth audio codec"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Trigger Bluetooth Audio Codec\nSelection"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth audio sample rate"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Check apps installed via ADB/ADT for harmful behaviour."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bluetooth devices without names (MAC addresses only) will be displayed"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Disables the Bluetooth absolute volume feature in case of volume issues with remote devices such as unacceptably loud volume or lack of control."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Enables the Bluetooth Gabeldorsche feature stack."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Enables the Bluetooth Gabeldorsche feature stack."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Local terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Enable terminal app that offers local shell access"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP checking"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/arrays.xml b/packages/SettingsLib/res/values-en-rXC/arrays.xml index af5d7f3d943e..eca7c75fc3b7 100644 --- a/packages/SettingsLib/res/values-en-rXC/arrays.xml +++ b/packages/SettingsLib/res/values-en-rXC/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (Default)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Use System Selection (Default)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index 9813efd14427..95944dc9eae1 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP Version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP Version"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Select Bluetooth MAP Version"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth Audio Codec"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Trigger Bluetooth Audio Codec\nSelection"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth Audio Sample Rate"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Check apps installed via ADB/ADT for harmful behavior."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bluetooth devices without names (MAC addresses only) will be displayed"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Disables the Bluetooth absolute volume feature in case of volume issues with remote devices such as unacceptably loud volume or lack of control."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Enables the Bluetooth Gabeldorche feature stack."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Enables the Bluetooth Gabeldorsche feature stack."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Local terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Enable terminal app that offers local shell access"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP checking"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/arrays.xml b/packages/SettingsLib/res/values-es-rUS/arrays.xml index d37ffb781087..ad58235abc04 100644 --- a/packages/SettingsLib/res/values-es-rUS/arrays.xml +++ b/packages/SettingsLib/res/values-es-rUS/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (predeterminado)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Usar selección del sistema (predeterminado)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 8899c072bf5b..c6dfdd3e3ac2 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Habilitar Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versión de AVRCP del Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona la versión de AVRCP del Bluetooth"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versión de MAP de Bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Seleccionar versión de MAP de Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Códec del audio Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Activar el códec de audio por Bluetooth\nSelección"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Frecuencia de muestreo del audio Bluetooth"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Comprobar que las aplicaciones instaladas mediante ADB/ADT no ocasionen daños"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Se mostrarán los dispositivos Bluetooth sin nombre (solo direcciones MAC)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Inhabilita la función de volumen absoluto de Bluetooth si se producen problemas de volumen con dispositivos remotos (por ejemplo, volumen demasiado alto o falta de control)."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Habilita la pila de funciones de Bluetooth Gabeldorsche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Habilita la pila de funciones de Bluetooth Gabeldorsche"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal local"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Habilitar aplicac. de terminal que ofrece acceso al shell local"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Comprobación HDCP"</string> diff --git a/packages/SettingsLib/res/values-es/arrays.xml b/packages/SettingsLib/res/values-es/arrays.xml index 04d4b2de5c87..a403e3e6519e 100644 --- a/packages/SettingsLib/res/values-es/arrays.xml +++ b/packages/SettingsLib/res/values-es/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (predeterminado)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Usar preferencia del sistema (predeterminado)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 83ae2df2e8aa..ba4a9ff9b245 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Habilitar Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versión AVRCP de Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona la versión AVRCP de Bluetooth"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versión de MAP de Bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Seleccionar versión de MAP de Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Códec de audio de Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Activar el códec de audio por Bluetooth\nSelección"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Frecuencia de muestreo de audio de Bluetooth"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Comprobar las aplicaciones instaladas mediante ADB/ADT para detectar comportamientos dañinos"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Mostrar dispositivos Bluetooth sin nombre (solo direcciones MAC)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Inhabilitar la función de volumen absoluto de Bluetooth si se producen problemas de volumen con dispositivos remotos (por ejemplo, volumen demasiado alto o falta de control)"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Habilita la pila de funciones de Bluetooth Gabeldorsche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Habilita la pila de funciones de Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal local"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Habilitar aplicación de terminal que ofrece acceso a shell local"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Comprobación de HDCP"</string> diff --git a/packages/SettingsLib/res/values-et/arrays.xml b/packages/SettingsLib/res/values-et/arrays.xml index 14a68b0e211a..eb5f3471b8e9 100644 --- a/packages/SettingsLib/res/values-et/arrays.xml +++ b/packages/SettingsLib/res/values-et/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (vaikeseade)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Süsteemi valiku kasutamine (vaikeseade)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 297e965ca476..df3b792b0e05 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Luba Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetoothi AVRCP versioon"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Valige Bluetoothi AVRCP versioon"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetoothi MAP-i versioon"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Bluetoothi MAP-i versiooni valimine"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetoothi heli kodek"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Bluetoothi helikodeki käivitamine\nValik"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetoothi heli diskreetimissagedus"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Kontrolli, kas ADB/ADT-ga installitud rakendused on ohtlikud."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Kuvatakse ilma nimedeta (ainult MAC-aadressidega) Bluetoothi seadmed"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Keelatakse Bluetoothi absoluutse helitugevuse funktsioon, kui kaugseadmetega on helitugevuse probleeme (nt liiga vali heli või juhitavuse puudumine)."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Lubab Bluetoothi Gabeldorche\'i funktsiooni virnastamise."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Lubab Bluetooth Gabeldorsche\'i funktsiooni virnastamise."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Kohalik terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Luba kohalikku turvalist juurdepääsu pakkuv terminalirakendus"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP-kontrollimine"</string> diff --git a/packages/SettingsLib/res/values-eu/arrays.xml b/packages/SettingsLib/res/values-eu/arrays.xml index e87413cd5a47..30ac525f72ff 100644 --- a/packages/SettingsLib/res/values-eu/arrays.xml +++ b/packages/SettingsLib/res/values-eu/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"1.2 MAPa (lehenetsia)"</item> + <item msgid="6817922176194686449">"1.3 MAPa"</item> + <item msgid="3423518690032737851">"1.4 MAPa"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Erabili sistema-hautapena (lehenetsia)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 5b84b1074b4c..fcb320ffe61f 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gaitu Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP bertsioa"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Hautatu Bluetooth AVRCP bertsioa"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAParen bertsioa"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Hautatu Bluetooth MAParen bertsioa"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth bidezko audioaren kodeka"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Abiarazi Bluetooth bidezko audio-kodeka\nHautapena"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth bidezko audioaren lagin-abiadura"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Egiaztatu ADB/ADT bidez instalatutako aplikazioak portaera kaltegarriak atzemateko"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bluetooth gailuak izenik gabe (MAC helbideak soilik) erakutsiko dira"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Bluetooth bidezko bolumen absolutuaren eginbidea desgaitu egiten du urruneko gailuetan arazoak hautematen badira; esaterako, bolumena ozenegia bada edo ezin bada kontrolatu"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Bluetooth Gabeldorsche eginbide sorta gaitzen du."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Bluetooth Gabeldorsche eginbide sorta gaitzen du."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Tokiko terminala"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Gaitu tokiko shell-sarbidea duen terminal-aplikazioa"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP egiaztapena"</string> diff --git a/packages/SettingsLib/res/values-fa/arrays.xml b/packages/SettingsLib/res/values-fa/arrays.xml index ba56d24974ba..3b05075c1d75 100644 --- a/packages/SettingsLib/res/values-fa/arrays.xml +++ b/packages/SettingsLib/res/values-fa/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP نسخه ۱.۲ (پیشفرض)"</item> + <item msgid="6817922176194686449">"MAP نسخه ۱.۳"</item> + <item msgid="3423518690032737851">"MAP نسخه ۱.۴"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"استفاده از انتخاب سیستم (پیشفرض)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 61fe37a81cf4..261a438a4774 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"فعال کردن Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"نسخه AVRCP بلوتوث"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"انتخاب نسخه AVRCP بلوتوث"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"نسخه MAP بلوتوث"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"انتخاب نسخه MAP بلوتوث"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"کدک بلوتوث صوتی"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"راهاندازی کدک صوتی بلوتوثی\nانتخاب"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"سرعت نمونه بلوتوث صوتی"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"برنامههای نصبشده ازطریق ADB/ADT را ازنظر رفتار مخاطرهآمیز بررسی کنید."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"دستگاههای بلوتوث بدون نام (فقط نشانیهای MAC) نشان داده خواهند شد"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"درصورت وجود مشکل در صدا با دستگاههای راه دور مثل صدای بلند ناخوشایند یا عدم کنترل صدا، ویژگی میزان صدای کامل بلوتوث را غیرفعال کنید."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"دسته ویژگی Gabeldorche، بلوتوث را فعال میکند."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"دسته ویژگی Gabeldorsche، بلوتوث را فعال میکند."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"ترمینال محلی"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"فعال کردن ترمینال برنامه کاربردی که دسترسی به برنامه محلی را پیشنهاد میکند"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"بررسی HDCP"</string> diff --git a/packages/SettingsLib/res/values-fi/arrays.xml b/packages/SettingsLib/res/values-fi/arrays.xml index 79a46ba71cff..c899d9c8daea 100644 --- a/packages/SettingsLib/res/values-fi/arrays.xml +++ b/packages/SettingsLib/res/values-fi/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (oletus)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Käytä järjestelmän valintaa (oletus)."</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 7e8b160c21b1..4ccf430131af 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ota Gabeldorsche käyttöön"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetoothin AVRCP-versio"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Valitse Bluetoothin AVRCP-versio"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetoothin MAP-versio"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Valitse Bluetoothin MAP-versio"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth-äänen koodekki"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Käynnistä Bluetooth-äänipakkaus\nValinta"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth-ääninäytteen siirtonopeus"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Tarkista ADB:n/ADT:n kautta asennetut sovellukset haitallisen toiminnan varalta."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Näytetään Bluetooth-laitteet, joilla ei ole nimiä (vain MAC-osoitteet)."</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Bluetoothin yleinen äänenvoimakkuuden säätö poistetaan käytöstä ongelmien välttämiseksi esimerkiksi silloin, kun laitteen äänenvoimakkuus on liian kova tai sitä ei voi säätää."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Bluetoothin Gabeldorche-ominaisuuspino tulee käyttöön."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Bluetoothin Gabeldorsche-ominaisuuspino otetaan käyttöön."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Paikallinen pääte"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Ota käyttöön päätesov. joka mahdollistaa paikall. liittymäkäytön"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP-tarkistus"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/arrays.xml b/packages/SettingsLib/res/values-fr-rCA/arrays.xml index 3ee320993876..02b374acdcd7 100644 --- a/packages/SettingsLib/res/values-fr-rCA/arrays.xml +++ b/packages/SettingsLib/res/values-fr-rCA/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (valeur par défaut)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Utiliser sélect. du système (par défaut)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index d21eeefe5bb2..84a97973d7c4 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activer le Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Version du profil Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Sélectionner la version du profil Bluetooth AVRCP"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Version du profil Bluetooth MAP"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Sélectionner la version du profil Bluetooth MAP"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Codec audio Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Déclencher le codec audio Bluetooth\nSélection"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Taux d\'échantillonnage pour l\'audio Bluetooth"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Vérifier que les applications installées par ADB/ADT ne présentent pas de comportement dangereux."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Les appareils Bluetooth sans nom (adresses MAC seulement) seront affichés"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Désactive la fonctionnalité de volume absolu par Bluetooth en cas de problème de volume sur les appareils à distance, par exemple si le volume est trop élevé ou s\'il ne peut pas être contrôlé."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Active la pile de la fonctionnalité Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Active la pile de la fonctionnalité Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal local"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Activer l\'application Terminal permettant l\'accès au shell local"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Vérification HDCP"</string> diff --git a/packages/SettingsLib/res/values-fr/arrays.xml b/packages/SettingsLib/res/values-fr/arrays.xml index e23fc6a594ad..9ccaf09d8b8d 100644 --- a/packages/SettingsLib/res/values-fr/arrays.xml +++ b/packages/SettingsLib/res/values-fr/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (valeur par défaut)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Utiliser la sélection du système (par défaut)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 23dd5a83e113..030a7f9b3b48 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activer Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Version Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Sélectionner la version Bluetooth AVRCP"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Version Bluetooth MAP"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Sélectionner la version Bluetooth MAP"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Codec audio Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Critère pour déclencher la sélection du codec audio\nBluetooth"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Taux d\'échantillonnage audio Bluetooth"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Vérifier que les applications installées par ADB/ADT ne présentent pas de comportement dangereux"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Les appareils Bluetooth sans nom (adresses MAC seulement) seront affichés"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Désactive la fonctionnalité de volume absolu du Bluetooth en cas de problème de volume sur les appareils à distance, par exemple si le volume est trop élevé ou s\'il ne peut pas être contrôlé"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Active la pile de fonctionnalités Bluetooth Gabeldorsche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Active la pile de fonctionnalités Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal local"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Activer l\'application Terminal permettant l\'accès au shell local"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Vérification HDCP"</string> diff --git a/packages/SettingsLib/res/values-gl/arrays.xml b/packages/SettingsLib/res/values-gl/arrays.xml index c0a5a80782c0..5fad943e16b7 100644 --- a/packages/SettingsLib/res/values-gl/arrays.xml +++ b/packages/SettingsLib/res/values-gl/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (predeterminada)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Usar selección do sistema (predeterminado)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index c8f6f81c3426..af033cf43328 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activar Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versión de Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona a versión de Bluetooth AVRCP"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versión de MAP de Bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Selecciona a versión de MAP de Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Códec de audio por Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Activar códec de audio por Bluetooth\nSelección"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Taxa de mostra de audio por Bluetooth"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Comproba as aplicacións instaladas a través de ADB/ADT para detectar comportamento perigoso"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Mostraranse dispositivos Bluetooth sen nomes (só enderezos MAC)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Desactiva a función do volume absoluto do Bluetooth en caso de que se produzan problemas de volume cos dispositivos remotos, como volume demasiado alto ou falta de control"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Activa a pilla de funcións Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Activa o conxunto de funcións de Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal local"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Activa a aplicación terminal que ofrece acceso ao shell local"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Comprobación HDCP"</string> diff --git a/packages/SettingsLib/res/values-gu/arrays.xml b/packages/SettingsLib/res/values-gu/arrays.xml index 75c904d5a158..8e28b8b67c2c 100644 --- a/packages/SettingsLib/res/values-gu/arrays.xml +++ b/packages/SettingsLib/res/values-gu/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (ડિફૉલ્ટ)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index d274674b238b..87fd8767b326 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ચાલુ કરો"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"બ્લૂટૂથ AVRCP સંસ્કરણ"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"બ્લૂટૂથ AVRCP સંસ્કરણ પસંદ કરો"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"બ્લૂટૂથ MAP વર્ઝન"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"બ્લૂટૂથ MAP વર્ઝન પસંદ કરો"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"બ્લૂટૂથ ઑડિઓ કોડેક"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"બ્લૂટૂથ ઑડિઓ કોડેક\nપસંદગી ટ્રિગર કરો"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"બ્લૂટૂથ ઑડિઓ નમૂના દર"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"હાનિકારક વર્તણૂંક માટે ADB/ADT મારફતે ઇન્સ્ટોલ કરવામાં આવેલી ઍપ્લિકેશનો તપાસો."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"નામ વગરના (ફક્ત MAC ઍડ્રેસવાળા) બ્લૂટૂથ ઉપકરણો બતાવવામાં આવશે"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"રિમોટ ઉપકરણોમાં વધુ પડતું ઊંચું વૉલ્યૂમ અથવા નિયંત્રણની કમી જેવી વૉલ્યૂમની સમસ્યાઓની સ્થિતિમાં બ્લૂટૂથ ચોક્કસ વૉલ્યૂમ સુવિધાને અક્ષમ કરે છે."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"બ્લૂટૂથ Gabeldorche સુવિધા સ્ટૅક ચાલુ કરે છે."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"બ્લૂટૂથ Gabeldorsche સુવિધાનું સ્ટૅક ચાલુ કરે છે."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"સ્થાનિક ટર્મિનલ"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"સ્થાનિક શેલ અૅક્સેસની ઑફર કરતી ટર્મિનલ એપ્લિકેશનને સક્ષમ કરો"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP તપાસણી"</string> diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml index ffaf80a21413..3c744e1f5587 100644 --- a/packages/SettingsLib/res/values-hi/arrays.xml +++ b/packages/SettingsLib/res/values-hi/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (डिफ़ॉल्ट)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index d0307ecef47b..02b5c9601ab4 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche चालू करें"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ब्लूटूथ एवीआरसीपी वर्शन"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ब्लूटूथ AVRCP वर्शन चुनें"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ब्लूटूथ का MAP वर्शन"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"ब्लूटूथ का MAP वर्शन चुनें"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"ब्लूटूथ ऑडियो कोडेक"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"ब्लूटूथ ऑडियो कोडेक का\nविकल्प चालू करें"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"ब्लूटूथ ऑडियो नमूना दर"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"नुकसानदेह व्यवहार के लिए ADB/ADT से इंस्टॉल किए गए ऐप्लिकेशन जाँचें."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"बिना नाम वाले ब्लूटूथ डिवाइस (केवल MAC पते वाले) दिखाए जाएंगे"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"दूर के डिवाइस पर आवाज़ बहुत बढ़ जाने या उससे नियंत्रण हटने जैसी समस्याएं होने पर, यह ब्लूटूथ के ज़रिए आवाज़ के नियंत्रण की सुविधा रोक देता है."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"ब्लूटूथ Gabeldorsche सुविधा का स्टैक चालू करें."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"ब्लूटूथ सेटिंग में Gabeldorsche सुविधा को चालू करता है."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"स्थानीय टर्मिनल"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"लोकल शेल तक पहुंचने की सुविधा देने वाले टर्मिनल ऐप को चालू करें"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"एचडीसीपी जाँच"</string> diff --git a/packages/SettingsLib/res/values-hr/arrays.xml b/packages/SettingsLib/res/values-hr/arrays.xml index ec979f6852dc..c573e6c01184 100644 --- a/packages/SettingsLib/res/values-hr/arrays.xml +++ b/packages/SettingsLib/res/values-hr/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (zadano)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Upotreba odabira sustava (zadano)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 84adcaab19eb..598cfe2aadb2 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogući Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verzija AVRCP-a za Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Odaberite verziju AVRCP-a za Bluetooth"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verzija MAP-a za Bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Odaberite verziju MAP-a za Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Kodek za Bluetooth Audio"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Pokreni odabir kodeka za Bluetooth\nAudio"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Brzina uzorka za Bluetooth Audio"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Provjerite uzrokuju li aplikacije instalirane putem ADB-a/ADT-a poteškoće"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Prikazivat će se Bluetooth uređaji bez naziva (samo MAC adrese)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Onemogućuje Bluetoothovu značajku apsolutne glasnoće ako udaljeni uređaji imaju poteškoća sa zvukom, kao što su neprihvatljiva glasnoća ili nepostojanje kontrole"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Omogućuje nizove značajke Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Omogućuje nizove značajke Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Lokalni terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Omogući aplikaciju terminala koja nudi pristup lokalnoj ovojnici"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP provjera"</string> diff --git a/packages/SettingsLib/res/values-hu/arrays.xml b/packages/SettingsLib/res/values-hu/arrays.xml index 64d92e4d7377..608a9e0323a8 100644 --- a/packages/SettingsLib/res/values-hu/arrays.xml +++ b/packages/SettingsLib/res/values-hu/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (Alapértelmezett)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Rendszerérték (alapértelmezett)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index cb87e5628d88..d970c737c3d6 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"A Gabeldorsche engedélyezése"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"A Bluetooth AVRCP-verziója"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"A Bluetooth AVRCP-verziójának kiválasztása"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"A Bluetooth MAP-verziója"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"A Bluetooth MAP-verziójának kiválasztása"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth hang – Kodek"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Bluetooth-hangkodek aktiválása\nKiválasztás"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth hang – mintavételezési gyakoriság"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Az ADB/ADT útján telepített alkalmazások ellenőrzése kártékony viselkedésre."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Név nélküli Bluetooth-eszközök jelennek meg (csak MAC-címekkel)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Letiltja a Bluetooth abszolút hangerő funkcióját a távoli eszközökkel kapcsolatos hangerőproblémák – például elfogadhatatlanul magas vagy nem vezérelhető hangerő – esetén."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Engedélyezi a Bluetooth Gabeldorche funkcióit."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Engedélyezi a Bluetooth Gabeldorsche funkcióit."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Helyi végpont"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Végalkalmazás engedélyezése a helyi rendszerhéj eléréséhez"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP ellenőrzés"</string> diff --git a/packages/SettingsLib/res/values-hy/arrays.xml b/packages/SettingsLib/res/values-hy/arrays.xml index b1226f4d76a6..a2de6dfc7387 100644 --- a/packages/SettingsLib/res/values-hy/arrays.xml +++ b/packages/SettingsLib/res/values-hy/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (կանխադրված)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Օգտագործել համակարգի կարգավորումը (կանխադրված)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 6c30d4ee942a..908693439de0 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Միացնել Gabeldorsche-ը"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP տարբերակը"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Ընտրել Bluetooth AVRCP տարբերակը"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-ի տարբերակ"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Ընտրել Bluetooth MAP-ի տարբերակը"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth աուդիո կոդեկ"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Գործարկել Bluetooth աուդիո կոդեկը\nԸնտրություն"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth աուդիոյի ընդհատավորման հաճախականությունը"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Ստուգել հավելվածների անվտանգությունը ADB/ADT-ի միջոցով տեղադրված լինելու դեպքում։"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bluetooth սարքերը կցուցադրվեն առանց անունների (միայն MAC հասցեները)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Կասեցնում է Bluetooth-ի ձայնի բացարձակ ուժգնության գործառույթը՝ հեռավոր սարքերի հետ ձայնի ուժգնությանը վերաբերող խնդիրներ ունենալու դեպքում (օրինակ՝ երբ ձայնի ուժգնությունն անընդունելի է կամ դրա կառավարումը հնարավոր չէ):"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Միացնել Bluetooth Gabeldorche գործառույթի զտիչը"</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Միացնել Bluetooth Gabeldorsche գործառույթի զտիչը"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Տեղային տերմինալ"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Միացնել տերմինալային հավելվածը, որն առաջարկում է մուտք տեղային խեցի"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP ստուգում"</string> diff --git a/packages/SettingsLib/res/values-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml index 9d2528a45255..e73febcb1aa4 100644 --- a/packages/SettingsLib/res/values-in/arrays.xml +++ b/packages/SettingsLib/res/values-in/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (Default)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Gunakan Pilihan Sistem (Default)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 654c06cc7b10..3c1504c7f14a 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktifkan Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versi AVRCP Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pilih Versi AVRCP Bluetooth"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versi MAP Bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Pilih Versi Map Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Codec Audio Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Aktifkan Codec Audio Bluetooth\nPilihan"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Frekuensi Sampel Audio Bluetooth"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Periksa perilaku membahayakan dalam aplikasi yang terpasang melalui ADB/ADT."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Perangkat Bluetooth tanpa nama (hanya alamat MAC) akan ditampilkan"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Menonaktifkan fitur volume absolut Bluetooth jika ada masalah volume dengan perangkat jarak jauh, misalnya volume terlalu keras atau kurangnya kontrol."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Aktifkan stack fitur Gabeldorche Bluetooth."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Mengaktifkan stack fitur Gabeldorsche Bluetooth."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal lokal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Aktifkan aplikasi terminal yang menawarkan akses kerangka lokal"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Pemeriksaan HDCP"</string> diff --git a/packages/SettingsLib/res/values-is/arrays.xml b/packages/SettingsLib/res/values-is/arrays.xml index 1ac19f1ecc48..07b2ef15ca29 100644 --- a/packages/SettingsLib/res/values-is/arrays.xml +++ b/packages/SettingsLib/res/values-is/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (sjálfgefið)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Nota val kerfisins (sjálfgefið)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index 86626152c077..438e900be4ba 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Virkja Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-útgáfa"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Velja Bluetooth AVRCP-útgáfu"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-útgáfa"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Veldu Bluetooth MAP-útgáfu"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth hljóðkóðari"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Virkja Bluetooth-hljóðkóðara\nVal"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth hljóðtökutíðni"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Kanna skaðlega hegðun forrita sem sett eru upp frá ADB/ADT."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bluetooth-tæki án heita (aðeins MAC-vistfang) verða birt"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Slekkur á samstillingu Bluetooth-hljóðstyrks ef vandamál koma upp með hljóðstyrk hjá fjartengdum tækjum, svo sem of hár hljóðstyrkur eða erfiðleikar við stjórnun."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Virkjar Bluetooth Gabeldorche eiginleikastafla."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Kveikir á eiginleikastafla Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Staðbundin skipanalína"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Virkja skipanalínuforrit sem leyfir staðbundinn skeljaraðgang"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP-athugun"</string> diff --git a/packages/SettingsLib/res/values-it/arrays.xml b/packages/SettingsLib/res/values-it/arrays.xml index 44c519b0c20c..0bca8ea75212 100644 --- a/packages/SettingsLib/res/values-it/arrays.xml +++ b/packages/SettingsLib/res/values-it/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (versione predefinita)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Usa selezione di sistema (predefinita)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 0e98d981859b..114b33b49206 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Attiva Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versione Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Seleziona versione Bluetooth AVRCP"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versione Bluetooth MAP"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Seleziona versione Bluetooth MAP"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Codec audio Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Attiva il codec audio Bluetooth\nSelezione"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Frequenza campionamento audio Bluetooth"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Controlla che le app installate tramite ADB/ADT non abbiano un comportamento dannoso"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Verranno mostrati solo dispositivi Bluetooth senza nome (solo indirizzo MAC)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Disattiva la funzione del volume assoluto Bluetooth in caso di problemi con il volume dei dispositivi remoti, ad esempio un volume troppo alto o la mancanza di controllo"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Consente di attivare lo stack delle funzionalità Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Consente di attivare lo stack delle funzionalità Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminale locale"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Abilita l\'app Terminale che offre l\'accesso alla shell locale"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Verifica HDCP"</string> diff --git a/packages/SettingsLib/res/values-iw/arrays.xml b/packages/SettingsLib/res/values-iw/arrays.xml index c8fc6d34c258..2f7f310eb8eb 100644 --- a/packages/SettingsLib/res/values-iw/arrays.xml +++ b/packages/SettingsLib/res/values-iw/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (ברירת מחדל)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"שימוש בבחירת המערכת (ברירת המחדל)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 30a6295d8e45..62085a85e243 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"הפעלת Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth גרסה AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"בחר Bluetooth גרסה AVRCP"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"גרסת Bluetooth MAP"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"יש לבחור גרסה של Bluetooth MAP"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Codec אודיו ל-Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"הפעלת Codec אודיו ל-Bluetooth\nבחירה"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"קצב דגימה של אודיו ל-Bluetooth"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"בדוק אפליקציות שהותקנו באמצעות ADB/ADT לאיתור התנהגות מזיקה."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"יוצגו מכשירי Bluetooth ללא שמות (כתובות MAC בלבד)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"משבית את תכונת עוצמת הקול המוחלטת ב-Bluetooth במקרה של בעיות בעוצמת הקול במכשירים מרוחקים, כגון עוצמת קול רמה מדי או חוסר שליטה ברמת העוצמה."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"הפעלת מקבץ הפיצ\'רים של Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"הפעלת מקבץ הפיצ\'רים של Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"מסוף מקומי"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"הפעל אפליקציית מסוף המציעה גישה מקומית למעטפת"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"בדיקת HDCP"</string> diff --git a/packages/SettingsLib/res/values-ja/arrays.xml b/packages/SettingsLib/res/values-ja/arrays.xml index fdc68c6864dc..2966f09c6c1b 100644 --- a/packages/SettingsLib/res/values-ja/arrays.xml +++ b/packages/SettingsLib/res/values-ja/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2(デフォルト)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"システムの選択(デフォルト)を使用"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 22efe44f2162..28b98ee7c387 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche を有効にする"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP バージョン"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP バージョンを選択する"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP バージョン"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Bluetooth MAP バージョンの選択"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth オーディオ コーデック"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Bluetooth オーディオ コーデックを起動\n選択"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth オーディオ サンプルレート"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ADB/ADT経由でインストールされたアプリに不正な動作がないかを確認する"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bluetooth デバイスを名前なしで(MAC アドレスのみで)表示します"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"リモートデバイスで音量に関する問題(音量が大きすぎる、制御できないなど)が発生した場合に、Bluetooth の絶対音量の機能を無効にする"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Bluetooth Gabeldorche 機能スタックを有効にします。"</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Bluetooth Gabeldorsche 機能スタックを有効にします。"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"ローカルターミナル"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"ローカルシェルアクセスを提供するターミナルアプリを有効にします"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCPチェック"</string> diff --git a/packages/SettingsLib/res/values-ka/arrays.xml b/packages/SettingsLib/res/values-ka/arrays.xml index dda3b0757d0e..5a86eaee772b 100644 --- a/packages/SettingsLib/res/values-ka/arrays.xml +++ b/packages/SettingsLib/res/values-ka/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (ნაგულისხმევი)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"სისტემის არჩეულის გამოყენება (ნაგულისხმევი)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 0139f586a6f9..6f5d0b39fc1f 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche-ის ჩართვა"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth-ის AVRCP-ის ვერსია"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"აირჩიეთ Bluetooth-ის AVRCP-ის ვერსია"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-ის ვერსია"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"აირჩიეთ Bluetooth MAP-ის ვერსია"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth აუდიოს კოდეკი"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Bluetooth-ის აუდიო კოდეკის გაშვება\nარჩევანი"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth აუდიოს დისკრეტიზაციის სიხშირე"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"შეამოწმეთ, რამდენად უსაფრთხოა ADB/ADT-ის საშუალებით ინსტალირებული აპლიკაციები."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bluetooth-მოწყობილობები ნაჩვენები იქნება სახელების გარეშე (მხოლოდ MAC-მისამართები)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"გათიშავს Bluetooth-ის ხმის აბსოლუტური სიძლიერის ფუნქციას დისტანციურ მოწყობილობებზე ხმასთან დაკავშირებული ისეთი პრობლემების არსებობის შემთხვევაში, როგორიცაა ხმის დაუშვებლად მაღალი სიძლიერე ან კონტროლის შეუძლებლობა."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"ჩართავს Bluetooth Gabeldorche-ის ფუნქციების დასტას."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"ჩართავს Bluetooth Gabeldorsche-ის ფუნქციების დასტას."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"ადგილობრივი ტერმინალი"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"ლოკალურ გარსზე წვდომის ტერმინალური აპლიკაციის ჩართვა"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP შემოწმება"</string> diff --git a/packages/SettingsLib/res/values-kk/arrays.xml b/packages/SettingsLib/res/values-kk/arrays.xml index 70119c870865..3c96f43ca548 100644 --- a/packages/SettingsLib/res/values-kk/arrays.xml +++ b/packages/SettingsLib/res/values-kk/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (әдепкі)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Жүйенің таңдағанын алу (әдепкі)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index f7a773a95aa9..3fe426e6d027 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche функциясын іске қосу"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP нұсқасы"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP нұсқасын таңдау"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP нұсқасы"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Bluetooth MAP нұсқасын таңдау"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth аудиокодегі"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Bluetooth аудиокодегін іске қосу\nТаңдау"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth арқылы дыбыс іріктеу жиілігі"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ADB/ADT арқылы орнатылған қолданбалардың қауіпсіздігін тексеру."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bluetooth құрылғылары атаусыз (тек MAC мекенжайымен) көрсетіледі"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Қашықтағы құрылғыларда дыбыстың тым қатты шығуы немесе реттеуге келмеуі сияқты дыбыс деңгейіне қатысты мәселелер туындағанда, Bluetooth абсолютті дыбыс деңгейі функциясын өшіреді."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Bluetooth Gabeldorche функциясын іске қосады."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Bluetooth Gabeldorsche функциясы стегін қосады."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Жергілікті терминал"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Жергілікті шелл-код қол жетімділігін ұсынатын терминалды қолданбаны қосу"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP тексеру"</string> diff --git a/packages/SettingsLib/res/values-km/arrays.xml b/packages/SettingsLib/res/values-km/arrays.xml index 327754bd43aa..24efd08b7724 100644 --- a/packages/SettingsLib/res/values-km/arrays.xml +++ b/packages/SettingsLib/res/values-km/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (លំនាំដើម)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"ប្រើការជ្រើសរើសប្រព័ន្ធ (លំនាំដើម)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 5c64a403cea8..24bfa35d2368 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"បើក Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"កំណែប្ល៊ូធូស AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ជ្រើសរើសកំណែប្ល៊ូធូស AVRCP"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"កំណែប៊្លូធូស MAP"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"ជ្រើសរើសកំណែប្ល៊ូធូស MAP"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"កូឌិកសំឡេងប៊្លូធូស"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"ជំរុញការជ្រើសរើសកូឌិកសំឡេង\nប៊្លូធូស"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"អត្រាគំរូសំឡេងប៊្លូធូស"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ពិនិត្យកម្មវិធីបានដំឡើងតាមរយៈ ADB/ADT សម្រាប់ឥរិយាបថដែលគ្រោះថ្នាក់។"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"ឧបករណ៍ប្ល៊ូធូសគ្មានឈ្មោះ (អាសយដ្ឋាន MAC តែប៉ុណ្ណោះ) នឹងបង្ហាញ"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"បិទមុខងារកម្រិតសំឡេងឮខ្លាំងពេលភ្ជាប់ប៊្លូធូសក្នុងករណីមានបញ្ហាជាមួយឧបករណ៍បញ្ជាពីចម្ងាយ ដូចជាកម្រិតសំឡេងឮខ្លាំងដែលមិនអាចទទួលយកបាន ឬខ្វះការគ្រប់គ្រង។"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"បើកជង់មុខងារប៊្លូធូស Gabeldorche។"</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"បើកជង់មុខងារប៊្លូធូស Gabeldorsche។"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"ស្ថានីយមូលដ្ឋាន"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"បើកកម្មវិធីស្ថានីយដែលផ្ដល់ការចូលសែលមូលដ្ឋាន"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"ពិនិត្យ HDCP"</string> diff --git a/packages/SettingsLib/res/values-kn/arrays.xml b/packages/SettingsLib/res/values-kn/arrays.xml index b06af1c13f45..4d8bde2d00ae 100644 --- a/packages/SettingsLib/res/values-kn/arrays.xml +++ b/packages/SettingsLib/res/values-kn/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (ಡೀಫಾಲ್ಟ್)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"ಸಿಸ್ಟಂ ಆಯ್ಕೆಯನ್ನು ಬಳಸಿ (ಡಿಫಾಲ್ಟ್)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 08a0db72df29..0699bbcd007a 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ಬ್ಲೂಟೂತ್ AVRCP ಆವೃತ್ತಿ"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ಬ್ಲೂಟೂತ್ AVRCP ಆವೃತ್ತಿಯನ್ನು ಆಯ್ಕೆ ಮಾಡಿ"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ಬ್ಲೂಟೂತ್ MAP ಆವೃತ್ತಿ"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"ಬ್ಲೂಟೂತ್ MAP ಆವೃತ್ತಿಯನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"ಬ್ಲೂಟೂತ್ ಆಡಿಯೋ ಕೋಡೆಕ್"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"ಬ್ಲೂಟೂತ್ ಆಡಿಯೋ ಕೋಡೆಕ್ ಅನ್ನು ಟ್ರಿಗ್ಗರ್ ಮಾಡಿ\nಆಯ್ಕೆ"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"ಬ್ಲೂಟೂತ್ ಆಡಿಯೋ ಮಾದರಿ ದರ"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ಹಾನಿಮಾಡುವಂತಹ ವರ್ತನೆಗಾಗಿ ADB/ADT ಮೂಲಕ ಸ್ಥಾಪಿಸಲಾದ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಪರಿಶೀಲಿಸಿ."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"ಹೆಸರುಗಳಿಲ್ಲದ (ಕೇವಲ MAC ವಿಳಾಸಗಳು ಮಾತ್ರ) ಬ್ಲೂಟೂತ್ ಸಾಧನಗಳನ್ನು ಪ್ರದರ್ಶಿಸಲಾಗುತ್ತದೆ"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"ರಿಮೋಟ್ ಸಾಧನಗಳಲ್ಲಿ ಕಂಡುಬರುವ ಸ್ವೀಕಾರಾರ್ಹವಲ್ಲದ ಜೋರಾದ ವಾಲ್ಯೂಮ್ ಅಥವಾ ನಿಯಂತ್ರಣದ ಕೊರತೆಯಂತಹ ವಾಲ್ಯೂಮ್ ಸಮಸ್ಯೆಗಳಂತಹ ಸಂದರ್ಭದಲ್ಲಿ ಬ್ಲೂಟೂತ್ನ ನಿಚ್ಚಳ ವಾಲ್ಯೂಮ್ ವೈಶಿಷ್ಟ್ಯವನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"ಬ್ಲೂಟೂತ್ Gabeldorche ವೈಶಿಷ್ಟ್ಯದ ಸ್ಟ್ಯಾಕ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"ಬ್ಲೂಟೂತ್ Gabeldorsche ವೈಶಿಷ್ಟ್ಯದ ಸ್ಟ್ಯಾಕ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"ಸ್ಥಳೀಯ ಟರ್ಮಿನಲ್"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"ಸ್ಥಳೀಯ ಶೆಲ್ ಪ್ರವೇಶವನ್ನು ಒದಗಿಸುವ ಟರ್ಮಿನಲ್ ಅಪ್ಲಿಕೇಶನ್ ಸಕ್ರಿಯಗೊಳಿಸಿ"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP ಪರೀಕ್ಷಿಸುವಿಕೆ"</string> diff --git a/packages/SettingsLib/res/values-ko/arrays.xml b/packages/SettingsLib/res/values-ko/arrays.xml index 9f7a751e3d62..999f3ae0381a 100644 --- a/packages/SettingsLib/res/values-ko/arrays.xml +++ b/packages/SettingsLib/res/values-ko/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2(기본)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"시스템 설정 사용(기본)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index d93b1cd7ea78..25e9cfe8fa1a 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche 사용 설정"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"블루투스 AVRCP 버전"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"블루투스 AVRCP 버전 선택"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"블루투스 MAP 버전"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"블루투스 MAP 버전 선택"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"블루투스 오디오 코덱"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"블루투스 오디오 코덱 실행\n선택"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"블루투스 오디오 샘플링 비율"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ADB/ADT를 통해 설치된 앱에 유해한 동작이 있는지 확인"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"이름이 없이 MAC 주소만 있는 블루투스 기기 표시"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"참기 어려울 정도로 볼륨이 크거나 제어가 되지 않는 등 원격 기기에서 볼륨 문제가 발생할 경우 블루투스 절대 볼륨 기능을 사용 중지"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"블루투스 Gabeldorche 기능 스택을 사용 설정합니다."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"블루투스 Gabeldorsche 기능 스택을 사용 설정합니다."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"로컬 터미널"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"로컬 셸 액세스를 제공하는 터미널 앱 사용"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP 확인"</string> diff --git a/packages/SettingsLib/res/values-ky/arrays.xml b/packages/SettingsLib/res/values-ky/arrays.xml index b2eaf9fee406..fd47dada3315 100644 --- a/packages/SettingsLib/res/values-ky/arrays.xml +++ b/packages/SettingsLib/res/values-ky/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"КАРТА 1.2 (Демейки)"</item> + <item msgid="6817922176194686449">"КАРТА 1.3"</item> + <item msgid="3423518690032737851">"КАРТА 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"карта12"</item> + <item msgid="7073042887003102964">"карта13"</item> + <item msgid="8147982633566548515">"карта14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Тутум тандаганды колдонуу (демейки)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index a24dd65ba8bb..3dfce1eb2a8a 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche функциясын иштетүү"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP версиясы"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP версиясын тандоо"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP версиясы"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Bluetooth MAP версиясын тандоо"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth аудио кодек"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Bluetooth Audio кодегин иштетүү\nТандоо"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth аудио үлгүсүнүн ылдамдыгы"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ADB/ADT аркылуу орнотулган колдонмолордун коопсуздугу текшерилет."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Аталышсыз Bluetooth түзмөктөрү (MAC даректери менен гана) көрсөтүлөт"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Алыскы түзмөктөр өтө катуу добуш чыгарып же көзөмөлдөнбөй жатса Bluetooth \"Үндүн абсолюттук деңгээли\" функциясын өчүрөт."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Bluetooth Gabeldorche функциясынын топтомун иштетет."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Bluetooth Gabeldorsche функциясынын топтомун иштетет."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Жергиликтүү терминал"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Жергиликтүү буйрук кабыгын сунуштаган терминалга уруксат берүү"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP текшерүү"</string> diff --git a/packages/SettingsLib/res/values-lo/arrays.xml b/packages/SettingsLib/res/values-lo/arrays.xml index 7e7ea1f86615..5e25ab0790c7 100644 --- a/packages/SettingsLib/res/values-lo/arrays.xml +++ b/packages/SettingsLib/res/values-lo/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (Default)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"ໃຊ້ການເລືອກຂອງລະບົບ (ຄ່າເລີ່ມຕົ້ນ)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 3f93483f5239..48e50933c0ce 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"ເປີດໃຊ້ Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ເວີຊັນ Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ເລືອກເວີຊັນ Bluetooth AVRCP"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ເວີຊັນ Bluetooth MAP"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"ເລືອກເວີຊັນ Bluetooth MAP"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth Audio Codec"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"ເປີດໃຊ້ Bluetooth Audio Codec\nການເລືອກ"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth Audio Sample Rate"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ກວດສອບແອັບທີ່ຕິດຕັ້ງແລ້ວຜ່ານທາງ ADB/ADT ເພື່ອກວດຫາພຶດຕິກຳທີ່ເປັນອັນຕະລາຍ."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"ຈະສະແດງອຸປະກອນ Bluetooth ທີ່ບໍ່ມີຊື່ (ທີ່ຢູ່ MAC ເທົ່ານັ້ນ)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"ປິດໃຊ້ຄຸນສົມບັດລະດັບສຽງສົມບູນຂອງ Bluetooth ໃນກໍລະນີເກີດບັນຫາລະດັບສຽງສົມບູນກັບອຸປະກອນທາງໄກ ເຊັ່ນວ່າ ລະດັບສຽງດັງເກີນຍອມຮັບໄດ້ ຫຼື ຄວບຄຸມບໍ່ໄດ້."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"ເປີດໃຊ້ການວາງຊ້ອນຄຸນສົມບັດ Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"ເປີດໃຊ້ສະແຕັກຄຸນສົມບັດ Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal ໃນໂຕເຄື່ອງ"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"ເປີດນຳໃຊ້ແອັບຯ Terminal ທີ່ໃຫ້ການເຂົ້າເຖິງ shell ໃນໂຕເຄື່ອງໄດ້"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"ການກວດສອບ HDCP"</string> diff --git a/packages/SettingsLib/res/values-lt/arrays.xml b/packages/SettingsLib/res/values-lt/arrays.xml index 3356efb94dd5..e4b55ab04b02 100644 --- a/packages/SettingsLib/res/values-lt/arrays.xml +++ b/packages/SettingsLib/res/values-lt/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"1.2 versijos MRK (numatytoji)"</item> + <item msgid="6817922176194686449">"1.3 versijos MRK"</item> + <item msgid="3423518690032737851">"1.4 versijos MRK"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Naudoti sistemos pasirink. (numatytasis)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 940909307b61..8b3fbadbfb39 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Įgalinti „Gabeldorsche“"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"„Bluetooth“ AVRCP versija"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pasirinkite „Bluetooth“ AVRCP versiją"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"„Bluetooth“ MRK versija"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Pasirinkite „Bluetooth“ MRK versiją"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"„Bluetooth“ garso kodekas"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Suaktyvinti „Bluetooth“ garso kodeką\nPasirinkimas"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"„Bluetooth“ garso pavyzdžio dažnis"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Patikrinkite, ar programų, įdiegtų naudojant ADB / ADT, veikimas nėra žalingas."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bus rodomi „Bluetooth“ įrenginiai be pavadinimų (tik MAC adresai)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Išjungiama „Bluetooth“ didžiausio garso funkcija, jei naudojant nuotolinio valdymo įrenginius kyla problemų dėl garso, pvz., garsas yra per didelis arba jo negalima tinkamai valdyti."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Įgalinama „Bluetooth Gabeldorche“ funkcijų grupė."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Įgalinama „Bluetooth Gabeldorsche“ funkcijų grupė."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Vietinis terminalas"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Įgal. terminalo progr., siūlančią prieigą prie viet. apvalkalo"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP tikrinimas"</string> diff --git a/packages/SettingsLib/res/values-lv/arrays.xml b/packages/SettingsLib/res/values-lv/arrays.xml index 2f0f5072f06f..b90cf225758e 100644 --- a/packages/SettingsLib/res/values-lv/arrays.xml +++ b/packages/SettingsLib/res/values-lv/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (noklusējums)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Sistēmas atlases izmantošana (nokl.)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index b51b69af472c..4fc5b223c384 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Iespējot Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP versija"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Atlasiet Bluetooth AVRCP versiju"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP versija"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Atlasiet Bluetooth MAP versiju"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth audio kodeks"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Aktivizēt Bluetooth audio kodeku\nAtlase"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth audio iztveršanas ātrums"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Pārbaudīt, vai lietotņu, kuru instalēšanai izmantots ADB/ADT, darbība nav kaitīga."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Tiks parādītas Bluetooth ierīces bez nosaukumiem (tikai MAC adreses)."</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Atspējo Bluetooth absolūtā skaļuma funkciju skaļuma problēmu gadījumiem attālajās ierīcēs, piemēram, ja ir nepieņemami liels skaļums vai nav iespējas kontrolēt skaļumu."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Šis iestatījums iespējo funkciju grupu Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Tiek iespējota funkciju grupa Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Vietējā beigu lietotne"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Iespējot beigu lietotni, kurā piedāvāta vietējā čaulas piekļuve"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP pārbaude"</string> diff --git a/packages/SettingsLib/res/values-mk/arrays.xml b/packages/SettingsLib/res/values-mk/arrays.xml index 3753a51ab674..90956ad99e9e 100644 --- a/packages/SettingsLib/res/values-mk/arrays.xml +++ b/packages/SettingsLib/res/values-mk/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (стандардна)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Користи избор на системот (стандардно)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index d38301e74f32..288e526be4c9 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Овозможи Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Верзија Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Изберете верзија Bluetooth AVRCP"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Верзија на Bluetooth MAP"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Изберете верзија на Bluetooth MAP"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Кодек за аудио преку Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Вклучете го аудио кодекот преку Bluetooth\nСелекција"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Стапка на семпл преку Bluetooth"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Провери апликации инсталирани преку ADB/ADT за штетно однесување."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Уредите со Bluetooth без имиња (само MAC-адреси) ќе се прикажуваат"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Ја оневозможува карактеристиката за апсолутна јачина на звук преку Bluetooth во случај кога ќе настанат проблеми со далечинските уреди, како на пр., неприфатливо силен звук или недоволна контрола."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Ја овозможува функцијата Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Ја овозможува функцијата Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Локален терминал"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Овозможи апликација на терминал што овозможува локален пристап кон школка."</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Проверување HDCP"</string> diff --git a/packages/SettingsLib/res/values-ml/arrays.xml b/packages/SettingsLib/res/values-ml/arrays.xml index 1e0799420f06..60eb24ecded9 100644 --- a/packages/SettingsLib/res/values-ml/arrays.xml +++ b/packages/SettingsLib/res/values-ml/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (ഡിഫോൾട്ട്)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ (ഡിഫോൾട്ട്)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 27019d105191..e775297982fd 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche പ്രവർത്തനക്ഷമമാക്കുക"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP പതിപ്പ്"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP പതിപ്പ് തിരഞ്ഞെടുക്കുക"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP പതിപ്പ്"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Bluetooth MAP പതിപ്പ് തിരഞ്ഞെടുക്കുക"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth ഓഡിയോ കോഡെക്"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Bluetooth Audio Codec\nSelection ട്രിഗ്ഗര് ചെയ്യുക"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth ഓഡിയോ സാമ്പിൾ നിരക്ക്"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ADB/ADT വഴി ഇൻസ്റ്റാൾ ചെയ്ത കേടാക്കുന്ന പ്രവർത്തനരീതിയുള്ള ആപ്പുകൾ പരിശോധിക്കുക."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"പേരില്ലാത്ത Bluetooth ഉപകരണങ്ങൾ (MAC വിലാസങ്ങൾ മാത്രം) പ്രദർശിപ്പിക്കും"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"അസ്വീകാര്യമായ തരത്തിൽ ഉയർന്ന വോളിയമോ ശബ്ദ നിയന്ത്രണത്തിന്റെ അഭാവമോ പോലെ, വിദൂര ഉപകരണങ്ങളുമായി ബന്ധപ്പെട്ട വോളിയം പ്രശ്നങ്ങൾ ഉണ്ടാകുന്ന സാഹചര്യത്തിൽ, Bluetooth അബ്സൊല്യൂട്ട് വോളിയം ഫീച്ചർ പ്രവർത്തനരഹിതമാക്കുന്നു."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Bluetooth Gabeldorche ഫീച്ചർ സ്റ്റാക്ക് പ്രവർത്തനക്ഷമമാക്കുന്നു."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Bluetooth Gabeldorsche ഫീച്ചർ സ്റ്റാക്ക് പ്രവർത്തനക്ഷമമാക്കുന്നു."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"പ്രാദേശിക ടെർമിനൽ"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"പ്രാദേശിക ഷെൽ ആക്സസ് നൽകുന്ന ടെർമിനൽ അപ്ലിക്കേഷൻ പ്രവർത്തനക്ഷമമാക്കുക"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP പരിശോധന"</string> diff --git a/packages/SettingsLib/res/values-mn/arrays.xml b/packages/SettingsLib/res/values-mn/arrays.xml index d1eca7be8321..ce868af26bc9 100644 --- a/packages/SettingsLib/res/values-mn/arrays.xml +++ b/packages/SettingsLib/res/values-mn/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (өгөгдмөл)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Системийн сонголтыг ашиглах (Өгөгдмөл)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index f0136c916758..ba69f9b654ab 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche-г идэвхжүүлэх"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP хувилбар"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP хувилбарыг сонгох"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP хувилбар"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Bluetooth MAP хувилбарыг сонгох"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth аудио кодлогч"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Bluetooth-н аудио кодлогчийг өдөөх\nСонголт"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth аудио жишээний үнэлгээ"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ADB/ADT-р суулгасан апп-уудыг хорлонтой авиртай эсэхийг шалгах."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Нэргүй Bluetooth төхөөрөмжийг (зөвхөн MAC хаяг) харуулна"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Хэт чанга дуугаралт эсвэл муу тохиргоо зэрэг алсын зайн төхөөрөмжийн дуугаралттай холбоотой асуудлын үед Bluetooth-ийн үнэмлэхүй дууны түвшинг идэвхгүй болго."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Bluetooth Gabeldorsche онцлогийн өрөлтийг идэвхжүүлдэг."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Bluetooth Gabeldorsche онцлогийн өрөлтийг идэвхжүүлдэг."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Локал терминал"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Локал суурьт хандалт хийх боломж олгодог терминалын апп-г идэвхжүүлэх"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP шалгах"</string> diff --git a/packages/SettingsLib/res/values-mr/arrays.xml b/packages/SettingsLib/res/values-mr/arrays.xml index e62e6ff53f0c..3e6e3d0edf17 100644 --- a/packages/SettingsLib/res/values-mr/arrays.xml +++ b/packages/SettingsLib/res/values-mr/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (डीफॉल्ट)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"सिस्टम निवड वापरा (डीफॉल्ट)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 4d8069e8b9ea..1930cdf75e38 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"गाबलडॉर्ष सुरू करा"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ब्लूटूथ AVRCP आवृत्ती"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ब्लूटूथ AVRCP आवृत्ती निवडा"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ब्लूटूथ MAP आवृत्ती"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"ब्लूटूथ MAP आवृत्ती निवडा"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"ब्लूटूथ ऑडिओ कोडेक"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"ब्लूटूथ ऑडिओ Codec ट्रिगर करा\nनिवड"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"ब्लूटूथ ऑडिओ पॅटर्न दर"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"हानिकारक वर्तनासाठी ADB/ADT द्वारे इंस्टॉल अॅप्स तपासा."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"नावांशिवाय ब्लूटूथ डीव्हाइस (फक्त MAC पत्ते) दाखवले जातील"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"रिमोट डिव्हाइसमध्ये सहन न होणारा मोठा आवाज किंवा नियंत्रणाचा अभाव यासारखी आवाजाची समस्या असल्यास ब्लूटूथ संपूर्ण आवाज वैशिष्ट्य बंद करते."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"ब्लूटूथ गाबलडॉर्ष वैशिष्ट्य स्टॅक सुरू करते."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"ब्लूटूथ गाबलडॉर्ष वैशिष्ट्य स्टॅक सुरू करा."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"स्थानिक टर्मिनल"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"स्थानिक शेल प्रवेश देणारा टर्मिनल अॅप सुरू करा"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP तपासणी"</string> diff --git a/packages/SettingsLib/res/values-ms/arrays.xml b/packages/SettingsLib/res/values-ms/arrays.xml index 91dd81c62432..a2d314b01e50 100644 --- a/packages/SettingsLib/res/values-ms/arrays.xml +++ b/packages/SettingsLib/res/values-ms/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (Lalai)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Gunakan Pilihan Sistem (Lalai)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 7efc987598c3..e4931883bacd 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Dayakan Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versi AVRCP Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pilih Versi AVRCP Bluetooth"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versi MAP Bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Pilih Versi MAP Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Codec Audio Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Cetuskan Codec Audio Bluetooth\nPilihan"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Kadar Sampel Audio Bluetooth"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Semak apl yang dipasang melalui ADB/ADT untuk tingkah laku yang berbahaya."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Peranti Bluetooth tanpa nama (alamat MAC sahaja) akan dipaparkan"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Lumpuhkan ciri kelantangan mutlak Bluetooth dalam kes isu kelantangan menggunakan peranti kawalan jauh seperti kelantangan yang sangat kuat atau tidak dapat mengawal."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Mendayakan tindanan ciri Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Mendayakan tindanan ciri Gabeldorche Bluetooth."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal setempat"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Dayakan apl terminal yang menawarkan akses shell tempatan"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Penyemakan HDCP"</string> diff --git a/packages/SettingsLib/res/values-my/arrays.xml b/packages/SettingsLib/res/values-my/arrays.xml index 9c4a2b933b04..dbeabc0b90da 100644 --- a/packages/SettingsLib/res/values-my/arrays.xml +++ b/packages/SettingsLib/res/values-my/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (မူရင်း)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index befdaa9e093d..2c4b32c04107 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ကို ဖွင့်ရန်"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ဘလူးတုသ် AVRCP ဗားရှင်း"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ဘလူးတုသ် AVRCP ဗားရှင်းကို ရွေးပါ"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ဘလူးတုသ် MAP ဗားရှင်း"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"ဘလူးတုသ် MAP ဗားရှင်းကို ရွေးပါ"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"ဘလူးတုသ်အသံ ကိုးဒက်ခ်"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"ဘလူးတုသ် အသံ LDAC ကိုးဒက်ခ် ဖွင့်ခြင်း\nရွေးချယ်မှု"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"ဘလူးတုသ်အသံနမူနာနှုန်း"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ADB/ADT မှတစ်ဆင့် ထည့်သွင်းသော အက်ပ်များ အန္တရာယ်ဖြစ်နိုင်ခြင်း ရှိမရှိ စစ်ဆေးသည်။"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"အမည်မရှိသော (MAC လိပ်စာများသာပါသော) ဘလူးတုသ်စက်ပစ္စည်းများကို ပြသပါမည်"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"ချိတ်ဆက်ထားသည့် ကိရိယာတွင် လက်မခံနိုင်လောက်အောင် ဆူညံ သို့မဟုတ် ထိန်းညှိမရနိုင်သော အသံပိုင်းပြဿနာ ရှိခဲ့လျှင် ဘလူးတုသ် ပကတိ အသံနှုန်းကို ပိတ်ပါ။"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"ဘလူးတုသ် Gabeldorche အထူးတည်းဖြတ်ခြင်းကို ဖွင့်သည်။"</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"ဘလူးတုသ် Gabeldorsche လုပ်ဆောင်ချက်အပိုင်းကို ဖွင့်သည်။"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"လိုကယ်တာမီနယ်"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"local shell အသုံးပြုခွင့်ကမ်းလှမ်းသော တာမင်နယ်အပလီကေးရှင်းဖွင့်ပါ"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP စစ်ဆေးမှု"</string> diff --git a/packages/SettingsLib/res/values-nb/arrays.xml b/packages/SettingsLib/res/values-nb/arrays.xml index ed045ad549dd..8d005b32c19b 100644 --- a/packages/SettingsLib/res/values-nb/arrays.xml +++ b/packages/SettingsLib/res/values-nb/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (standard)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Bruk systemvalg (standard)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index cb0931f60502..093c06f20132 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktiver Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-versjon"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Velg Bluetooth AVRCP-versjon"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-versjon"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Velg Bluetooth MAP-versjon"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Kodek for Bluetooth-lyd"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Utløs kodek for Bluetooth-lyd\nValg"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Samplefrekvens for Bluetooth-lyd"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Sjekk apper som er installert via ADB/ADT, for skadelig atferd."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bluetooth-enheter uten navn (bare MAC-adresser) vises"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Slår av funksjonen for absolutt volum via Bluetooth i tilfelle det oppstår volumrelaterte problemer med eksterne enheter, for eksempel uakseptabelt høyt volum eller mangel på kontroll."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Aktiverer funksjonsstabelen Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Aktiverer funksjonsstabelen Bluetooth Gabeldorsche"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Lokal terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Aktiver terminalappen som gir lokal kommandolistetilgang"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP-kontroll"</string> diff --git a/packages/SettingsLib/res/values-ne/arrays.xml b/packages/SettingsLib/res/values-ne/arrays.xml index c8ee48b86e5e..5d79e80608d0 100644 --- a/packages/SettingsLib/res/values-ne/arrays.xml +++ b/packages/SettingsLib/res/values-ne/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp १५"</item> <item msgid="1963366694959681026">"avrcp १६"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP १.२ (पूर्वनिर्धारित)"</item> + <item msgid="6817922176194686449">"MAP १.३"</item> + <item msgid="3423518690032737851">"MAP १.४"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"प्रणालीको चयन प्रयोग गर्नुहोस् (पूर्वनिर्धारित)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index c7ffc6400f1d..fb8b7377e35d 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche सक्षम पार्नुहोस्"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ब्लुटुथको AVRCP संस्करण"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ब्लुटुथको AVRCP संस्करण चयन गर्नुहोस्"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ब्लुटुथको MAP संस्करण"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"ब्लुटुथको MAP संस्करण चयन गर्नुहोस्"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"ब्लुटुथ अडियोको कोडेक"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"ब्लुटुथ अडियो कोडेक ट्रिगर गर्नुहोस्\nचयन"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"ब्लुटुथ अडियोको नमूना दर"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"हानिकारक व्यवहारको लागि ADB/ADT को माध्यमबाट स्थापित अनुप्रयोगहरूको जाँच गर्नुहोस्।"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"नामकरण नगरिएका ब्लुटुथ यन्त्रहरू (MAC ठेगाना भएका मात्र) देखाइनेछ"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"रिमोट यन्त्रहरूमा अस्वीकार्य चर्को आवाज वा नियन्त्रणमा कमी जस्ता आवाज सम्बन्धी समस्याहरूको अवस्थामा ब्लुटुथ निरपेक्ष आवाज सुविधालाई असक्षम गराउँछ।"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"ब्लुटुथ Gabeldorche सुविधाको स्ट्याक सक्षम पार्नुहोस्।"</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"ब्लुटुथ Gabeldorsche सुविधाको स्ट्याक सक्षम पार्नुहोस्।"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"स्थानीय टर्मिनल"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"स्थानीय सेल पहुँच प्रदान गर्ने टर्मिनल अनुप्रयोग सक्षम गर्नुहोस्"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP जाँच गर्दै"</string> diff --git a/packages/SettingsLib/res/values-nl/arrays.xml b/packages/SettingsLib/res/values-nl/arrays.xml index d86dab687530..9b94ae503798 100644 --- a/packages/SettingsLib/res/values-nl/arrays.xml +++ b/packages/SettingsLib/res/values-nl/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (standaard)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Systeemselectie gebruiken (standaard)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index a196ccf1c651..267dab4cdee8 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche inschakelen"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth-AVRCP-versie"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth-AVRCP-versie selecteren"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP-versie voor bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"MAP-versie voor bluetooth selecteren"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth-audiocodec"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Codec voor Bluetooth-audio activeren\nSelectie"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Sample rate van Bluetooth-audio"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Apps die zijn geïnstalleerd via ADB/ADT, controleren op schadelijk gedrag"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bluetooth-apparaten zonder namen (alleen MAC-adressen) worden weergegeven"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Hiermee wordt de functie voor absoluut volume van Bluetooth uitgeschakeld in geval van volumeproblemen met externe apparaten, zoals een onacceptabel hoog volume of geen volumeregeling."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Hierdoor wordt de Gabeldorsche-functiestack voor bluetooth ingeschakeld"</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Hierdoor wordt de Gabeldorsche-functiestack voor bluetooth ingeschakeld."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Lokale terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Terminal-app inschakelen die lokale shell-toegang biedt"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP-controle"</string> diff --git a/packages/SettingsLib/res/values-or/arrays.xml b/packages/SettingsLib/res/values-or/arrays.xml index 2553978a5742..a0214465125e 100644 --- a/packages/SettingsLib/res/values-or/arrays.xml +++ b/packages/SettingsLib/res/values-or/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (ଡିଫଲ୍ଟ)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"ସିଷ୍ଟମ୍ ଚୟନ ବ୍ୟବହାର କରନ୍ତୁ (ଡିଫଲ୍ଟ)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index faa399dfe336..d8ae3bfb9a72 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"ଗାବେଲ୍ଡୋର୍ସ ସକ୍ରିୟ କରନ୍ତୁ"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ବ୍ଲୁଟୂଥ୍ AVRCP ଭର୍ସନ୍"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ବ୍ଲୁଟୂଥ୍ AVRCP ଭର୍ସନ୍"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ବ୍ଲୁଟୁଥ୍ MAP ସଂସ୍କରଣ"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"ବ୍ଲୁଟୁଥ୍ MAP ସଂସ୍କରଣ ଚୟନ କରନ୍ତୁ"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"ବ୍ଲୁଟୁଥ୍ ଅଡିଓ କୋଡେକ୍"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"ବ୍ଲୁଟୂଥ୍ ଅଡିଓ କୋଡେକ୍\nସିଲେକ୍ସନ୍କୁ ଗତିଶୀଳ କରନ୍ତୁ"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"ବ୍ଲୁଟୂଥ୍ ଅଡିଓ ସାମ୍ପଲ୍ ରେଟ୍"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ADB/ADT ମାଧ୍ୟମରେ ଇନଷ୍ଟଲ ହୋଇଥିବା ଆପ୍ଗୁଡ଼ିକ କ୍ଷତିକାରକ କି ନୁହେଁ ଯାଞ୍ଚ କରନ୍ତୁ।"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"(କେବଳ MAC ଠିକଣା ଥାଇ) ନାମ ବିନା ବ୍ଲୁଟୂଥ ଡିଭାଇସଗୁଡ଼ିକ ପ୍ରଦର୍ଶିତ ହେବ"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"ରିମୋଟ୍ ଡିଭାଇସ୍ଗୁଡ଼ିକରେ ଯଦି ଅସ୍ୱୀକାର୍ଯ୍ୟ ଭାବେ ଉଚ୍ଚ ଭଲ୍ୟୁମ୍ କିମ୍ବା ନିୟନ୍ତ୍ରଣର ଅଭାବ ପରି ଭଲ୍ୟୁମ୍ ସମସ୍ୟା ଥାଏ, ବ୍ଲୁଟୂଥ୍ ପୂର୍ଣ୍ଣ ଭଲ୍ୟୁମ୍ ଫିଚର୍ ଅକ୍ଷମ କରିଥାଏ।"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"ବ୍ଲୁଟୁଥ୍ ଗାବେଲ୍ଡୋର୍ସ ଫିଚର୍ ଷ୍ଟକ୍ ସକ୍ଷମ କରେ।"</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"ବ୍ଲୁଟୁଥ୍ ଗାବେଲଡୋର୍ସ ଫିଚର୍ ଷ୍ଟକ୍ ସକ୍ଷମ କରେ।"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"ସ୍ଥାନୀୟ ଟର୍ମିନାଲ୍"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"ସ୍ଥାନୀୟ ଶେଲ୍କୁ ଆକସେସ୍ ଦେଉଥିବା ଟର୍ମିନଲ୍ ଆପ୍କୁ ସକ୍ଷମ କରନ୍ତୁ"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP ଯାଞ୍ଚ କରୁଛି"</string> diff --git a/packages/SettingsLib/res/values-pa/arrays.xml b/packages/SettingsLib/res/values-pa/arrays.xml index 8acc4391adac..48e7fb404f11 100644 --- a/packages/SettingsLib/res/values-pa/arrays.xml +++ b/packages/SettingsLib/res/values-pa/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (ਪੂਰਵ-ਨਿਰਧਾਰਤ)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਤ)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index cd83c2cb4bee..6a784865b12c 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ਨੂੰ ਚਾਲੂ ਕਰੋ"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ਬਲੂਟੁੱਥ AVRCP ਵਰਜਨ"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ਬਲੂਟੁੱਥ AVRCP ਵਰਜਨ ਚੁਣੋ"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP ਵਰਜਨ"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Bluetooth MAP ਵਰਜਨ ਚੁਣੋ"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"ਬਲੂਟੁੱਥ ਆਡੀਓ ਕੋਡੇਕ"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"ਬਲੂਟੁੱਥ ਆਡੀਓ ਕੋਡੇਕ\nਚੋਣ ਨੂੰ ਟ੍ਰਿਗਰ ਕਰੋ"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"ਬਲੂਟੁੱਥ ਆਡੀਓ ਸੈਂਪਲ ਰੇਟ"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ਹਾਨੀਕਾਰਕ ਵਿਵਹਾਰ ਲਈ ADB/ADT ਰਾਹੀਂ ਸਥਾਪਤ ਕੀਤੀਆਂ ਐਪਾਂ ਦੀ ਜਾਂਚ ਕਰੋ।"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"ਅਨਾਮ ਬਲੂਟੁੱਥ ਡੀਵਾਈਸਾਂ ਦਿਖਾਈਆਂ ਜਾਣਗੀਆਂ (ਸਿਰਫ਼ MAC ਪਤੇ)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"ਰਿਮੋਟ ਡੀਵਾਈਸਾਂ ਨਾਲ ਅਵਾਜ਼ੀ ਸਮੱਸਿਆਵਾਂ ਜਿਵੇਂ ਕਿ ਨਾ ਪਸੰਦ ਕੀਤੀ ਜਾਣ ਵਾਲੀ ਉੱਚੀ ਅਵਾਜ਼ ਜਾਂ ਕੰਟਰੋਲ ਦੀ ਕਮੀ ਵਰਗੀ ਹਾਲਤ ਵਿੱਚ ਬਲੂਟੁੱਥ ਪੂਰਨ ਅਵਾਜ਼ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਬੰਦ ਕਰਦਾ ਹੈ।"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"ਬਲੂਟੁੱਥ Gabeldorche ਵਿਸ਼ੇਸ਼ਤਾ ਸਟੈਕ ਨੂੰ ਚਾਲੂ ਕਰਦਾ ਹੈ।"</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"ਬਲੂਟੁੱਥ Gabeldorsche ਵਿਸ਼ੇਸ਼ਤਾ ਸਟੈਕ ਨੂੰ ਚਾਲੂ ਕਰਦਾ ਹੈ।"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"ਸਥਾਨਕ ਟਰਮੀਨਲ"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"ਟਰਮੀਨਲ ਐਪ ਨੂੰ ਚਾਲੂ ਕਰੋ ਜੋ ਸਥਾਨਕ ਸ਼ੈਲ ਪਹੁੰਚ ਪੇਸ਼ਕਸ਼ ਕਰਦਾ ਹੈ"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP ਜਾਂਚ"</string> diff --git a/packages/SettingsLib/res/values-pl/arrays.xml b/packages/SettingsLib/res/values-pl/arrays.xml index 00b23bca41ad..43b8f5f0ccba 100644 --- a/packages/SettingsLib/res/values-pl/arrays.xml +++ b/packages/SettingsLib/res/values-pl/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (domyślny)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Użyj wyboru systemu (domyślnie)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index b47365b49f5e..8c5547c2791d 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Włącz Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Wersja AVRCP Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Wybierz wersję AVRCP Bluetooth"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Wersja MAP Bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Wybierz wersję MAP Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Kodek dźwięku Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Uruchom kodek dźwięku Bluetooth\nWybór"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Dźwięk Bluetooth – współczynnik próbkowania"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Sprawdź, czy aplikacje zainstalowane przez ADB/ADT nie zachowują się w szkodliwy sposób"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Zostaną wyświetlone urządzenia Bluetooth bez nazw (tylko adresy MAC)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Wyłącza funkcję Głośność bezwzględna Bluetooth, jeśli występują problemy z urządzeniami zdalnymi, np. zbyt duża głośność lub brak kontroli."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Włącza funkcje Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Włącza funkcje Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal lokalny"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Włącz terminal, który umożliwia dostęp do powłoki lokalnej"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Sprawdzanie HDCP"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/arrays.xml b/packages/SettingsLib/res/values-pt-rBR/arrays.xml index 4e23c197d430..4658ffdd859a 100644 --- a/packages/SettingsLib/res/values-pt-rBR/arrays.xml +++ b/packages/SettingsLib/res/values-pt-rBR/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (padrão)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Usar seleção do sistema (padrão)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 4cccad4eab69..8c036164e140 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ativar Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versão do Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecionar versão do Bluetooth AVRCP"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versão MAP do Bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Selecionar versão MAP do Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Codec de áudio Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Acionar codec de áudio Bluetooth\nSeleção"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Taxa de amostra do áudio Bluetooth"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Verificar comportamento nocivo em apps instalados via ADB/ADT"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Dispositivos Bluetooth sem nomes (somente endereços MAC) serão exibidos"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Desativa o recurso Bluetooth de volume absoluto em caso de problemas com o volume em dispositivos remotos, como volume excessivamente alto ou falta de controle"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Ativa a pilha de recursos Bluetooth Gabeldorsche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Ativa a pilha de recursos Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal local"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Ativar o app terminal que oferece acesso ao shell local"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Verificação HDCP"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/arrays.xml b/packages/SettingsLib/res/values-pt-rPT/arrays.xml index 98e9c8746459..527f74061535 100644 --- a/packages/SettingsLib/res/values-pt-rPT/arrays.xml +++ b/packages/SettingsLib/res/values-pt-rPT/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (predefinição)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Utilizar seleção do sistema (predefinido)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 6e80bd2258b3..6aeff1cb271c 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ativar o Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versão de Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecionar versão de Bluetooth AVRCP"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versão do MAP do Bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Selecione a versão do MAP do Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Codec de áudio Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Acionar o codec de áudio Bluetooth\nSeleção"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Taxa de amostragem de áudio Bluetooth"</string> @@ -241,7 +243,7 @@ <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="2076949781460359589">"Acionar o codec de áudio Bluetooth\nSeleção: modo de canal"</string> <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3233402355917446304">"Codec LDAC de áudio Bluetooth: qualidade de reprodução"</string> <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="7274396574659784285">"Acionar a seleção do codec LDAC de áudio\nBluetooth: Qualidade de reprodução"</string> - <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="2040810756832027227">"Transmissão em fluxo contínuo: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string> + <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="2040810756832027227">"Stream: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string> <string name="select_private_dns_configuration_title" msgid="7887550926056143018">"DNS privado"</string> <string name="select_private_dns_configuration_dialog_title" msgid="3731422918335951912">"Selecionar modo DNS privado"</string> <string name="private_dns_mode_off" msgid="7065962499349997041">"Desativado"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Verificar as aplicações instaladas via ADB/ADT para detetar comportamento perigoso."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"São apresentados os dispositivos Bluetooth sem nomes (apenas endereços MAC)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Desativa a funcionalidade de volume absoluto do Bluetooth caso existam problemas de volume com dispositivos remotos, como um volume insuportavelmente alto ou a ausência de controlo."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Ativa a pilha de funcionalidades Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Ativa a pilha de funcionalidades Bluetooth Gabeldorche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal local"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Ativar aplicação terminal que oferece acesso local à shell"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Verificação HDCP"</string> diff --git a/packages/SettingsLib/res/values-pt/arrays.xml b/packages/SettingsLib/res/values-pt/arrays.xml index 4e23c197d430..4658ffdd859a 100644 --- a/packages/SettingsLib/res/values-pt/arrays.xml +++ b/packages/SettingsLib/res/values-pt/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (padrão)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Usar seleção do sistema (padrão)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 4cccad4eab69..8c036164e140 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ativar Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versão do Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecionar versão do Bluetooth AVRCP"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versão MAP do Bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Selecionar versão MAP do Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Codec de áudio Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Acionar codec de áudio Bluetooth\nSeleção"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Taxa de amostra do áudio Bluetooth"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Verificar comportamento nocivo em apps instalados via ADB/ADT"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Dispositivos Bluetooth sem nomes (somente endereços MAC) serão exibidos"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Desativa o recurso Bluetooth de volume absoluto em caso de problemas com o volume em dispositivos remotos, como volume excessivamente alto ou falta de controle"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Ativa a pilha de recursos Bluetooth Gabeldorsche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Ativa a pilha de recursos Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminal local"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Ativar o app terminal que oferece acesso ao shell local"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Verificação HDCP"</string> diff --git a/packages/SettingsLib/res/values-ro/arrays.xml b/packages/SettingsLib/res/values-ro/arrays.xml index befb771624dc..5d25101b5697 100644 --- a/packages/SettingsLib/res/values-ro/arrays.xml +++ b/packages/SettingsLib/res/values-ro/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (prestabilit)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Folosiți selectarea sistemului (prestabilit)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index a7429328ab06..387441f32b56 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activați Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versiunea AVRCP pentru Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selectați versiunea AVRCP pentru Bluetooth"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versiunea MAP pentru Bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Selectați versiunea MAP pentru Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Codec audio Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Declanșați codecul audio Bluetooth\nSelecție"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Rată de eșantionare audio Bluetooth"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Verificați aplicațiile instalate utilizând ADB/ADT, pentru a detecta un comportament dăunător."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Vor fi afișate dispozitivele Bluetooth fără nume (numai adresele MAC)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Dezactivează funcția Bluetooth de volum absolut în cazul problemelor de volum apărute la dispozitivele la distanță, cum ar fi volumul mult prea ridicat sau lipsa de control asupra acestuia."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Activează setul de funcții Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Activează setul de funcții Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Aplicație terminal locală"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Activați aplicația terminal care oferă acces la shell local"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Verificare HDCP"</string> diff --git a/packages/SettingsLib/res/values-ru/arrays.xml b/packages/SettingsLib/res/values-ru/arrays.xml index d0d04d6d5b6c..f5367a44cd91 100644 --- a/packages/SettingsLib/res/values-ru/arrays.xml +++ b/packages/SettingsLib/res/values-ru/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (по умолчанию)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Выбор системы (по умолчанию)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 769b5f3c2687..361e29ff452e 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Включить Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версия Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Выберите версию Bluetooth AVRCP"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Версия Bluetooth MAP"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Выберите версию Bluetooth MAP"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Аудиокодек Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Запустить аудиокодек для Bluetooth\nВыбор"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Частота дискретизации аудио Bluetooth"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Выполнять проверку безопасности приложений при установке через ADB/ADT"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Показывать Bluetooth-устройства без названий (только с MAC-адресами)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Отключить абсолютный уровень громкости Bluetooth при возникновении проблем на удаленных устройствах, например при слишком громком звучании или невозможности контролировать настройку"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Включить стек Bluetooth Gabeldorche"</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Включить стек Bluetooth Gabeldorsche"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Локальный терминальный доступ"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Разрешить терминальный доступ к локальной оболочке"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Проверка HDCP"</string> diff --git a/packages/SettingsLib/res/values-si/arrays.xml b/packages/SettingsLib/res/values-si/arrays.xml index 4764d4768ead..f8c871e99202 100644 --- a/packages/SettingsLib/res/values-si/arrays.xml +++ b/packages/SettingsLib/res/values-si/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (පෙරනිමි)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"පද්ධති තේරීම භාවිත කරන්න (පෙරනිමි)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 1d925bfda8d5..faa848fec830 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche සබල කරන්න"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"බ්ලූටූත් AVRCP අනුවාදය"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"බ්ලූටූත් AVRCP අනුවාදය තෝරන්න"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP අනුවාදය"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Bluetooth MAP අනුවාදය තෝරන්න"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"බ්ලූටූත් ශ්රව්ය Codec"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"බ්ලූටූත් ශ්රව්ය කෝඩෙක් ක්රියාරම්භ කරන්න\nතෝරා ගැනීම"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"බ්ලූටූත් ශ්රව්ය නියැදි අනුපාතය"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ADB/ADT හරහා ස්ථාපනය වූ යෙදුම්, විනාශකාරී ක්රියාවන් ඇත්දැයි පරික්ෂාකර බලන්න."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"නම් නොමැති බ්ලූටූත් උපාංග (MAC ලිපින පමණි) සංදර්ශනය කරනු ඇත"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"පිළිගත නොහැකි ලෙස වැඩි හඩ පරිමාව හෝ පාලනය නොමැති වීම යනාදී දුරස්ථ උපාංග සමගින් වන හඬ පරිමා ගැටලුවලදී බ්ලූටූත් නිරපේක්ෂ හඬ පරිමා විශේෂාංගය අබල කරයි."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"බ්ලූටූත් Gabeldorche විශේෂාංග අට්ටිය සබල කරයි."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Bluetooth Gabeldorsche විශේෂාංග අට්ටිය සබල කරයි."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"අභ්යන්තර අන්තය"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"දේශීය ෂෙල් ප්රවේශනය පිරිනමන ටර්මිනල් යෙදුම සබල කරන්න"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP පරික්ෂාව"</string> diff --git a/packages/SettingsLib/res/values-sk/arrays.xml b/packages/SettingsLib/res/values-sk/arrays.xml index 427ee45bcfa2..f862d88ff493 100644 --- a/packages/SettingsLib/res/values-sk/arrays.xml +++ b/packages/SettingsLib/res/values-sk/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (predvolené)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Použiť voľbu systému (predvolené)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index e022c363b7f8..2035d88c75f0 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Povoliť Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verzia rozhrania Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Zvoľte verziu rozhrania Bluetooth AVRCP"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verzia profilu Bluetooth MAP"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Výber verzie profilu Bluetooth MAP"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth Audio – kodek"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Spustiť zvukový kodek Bluetooth\nVýber"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth Audio – vzorkovacia frekvencia"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Kontrolovať škodlivosť aplikácií nainštalovaných pomocou nástroja ADB alebo ADT"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Zariadenia Bluetooth sa budú zobrazovať bez názvov (iba adresy MAC)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Umožňuje zakázať funkciu absolútnej hlasitosti rozhrania Bluetooth v prípade problémov s hlasitosťou vo vzdialených zariadeniach, ako je napríklad neprijateľne vysoká hlasitosť alebo absencia ovládacích prvkov."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Umožňuje povoliť skupiny funkcií Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Umožňuje povoliť skupinu funkcií Bluetooth Gabeldorche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Miestny terminál"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Povoliť terminálovú apl. na miestny prístup k prostrediu shell"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Kontrola HDCP"</string> diff --git a/packages/SettingsLib/res/values-sl/arrays.xml b/packages/SettingsLib/res/values-sl/arrays.xml index d946316af95d..eb860743b796 100644 --- a/packages/SettingsLib/res/values-sl/arrays.xml +++ b/packages/SettingsLib/res/values-sl/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (privzeto)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Uporabi sistemsko izbiro (privzeto)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index efd7c083eae4..288961947269 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogoči Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Različica profila AVRCP za Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Izberite različico profila AVRCP za Bluetooth"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Različica profila MAP za Bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Izbira različice profila MAP za Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Zvočni kodek za Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Sproži zvočni kodek za Bluetooth\nIzbor"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Hitrost vzorčenja zvoka prek Bluetootha"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Preveri, ali so aplikacije, nameščene prek ADB/ADT, škodljive."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Prikazane bodo naprave Bluetooth brez imen (samo z naslovi MAC)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Onemogoči funkcijo absolutne glasnosti za Bluetooth, če pride do težav z glasnostjo z oddaljenimi napravami, kot je nesprejemljivo visoka glasnost ali pomanjkanje nadzora."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Omogoči sklad funkcij Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Omogoči sklad funkcij Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Lokalni terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Omogočanje terminalske aplikacije za dostop do lokalne lupine"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Preverjanje HDCP"</string> diff --git a/packages/SettingsLib/res/values-sq/arrays.xml b/packages/SettingsLib/res/values-sq/arrays.xml index 26ba289b96fe..1363e8354ef9 100644 --- a/packages/SettingsLib/res/values-sq/arrays.xml +++ b/packages/SettingsLib/res/values-sq/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (parazgjedhja)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Përdor përzgjedhjen e sistemit (e parazgjedhur)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index c7c423018cdc..ccd4e3066d9f 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktivizo Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versioni AVRCP i Bluetooth-it"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Zgjidh versionin AVRCP të Bluetooth-it"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versioni MAP i Bluetooth-it"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Zgjidh versionin MAP të Bluetooth-it"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Kodeku Bluetooth Audio"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Aktivizo kodekun e audios me Bluetooth\nZgjedhja"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Shpejtësia e shembullit të Bluetooth Audio"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Kontrollo aplikacionet e instaluara nëpërmjet ADB/ADT për sjellje të dëmshme."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Pajisjet me Bluetooth do të shfaqen pa emra (vetëm adresat MAC)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Çaktivizon funksionin e volumit absolut të Bluetooth në rast të problemeve të volumit me pajisjet në largësi, si p.sh. një volum i lartë i papranueshëm ose mungesa e kontrollit."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Aktivizon grupin e veçorive të Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Aktivizon grupin e veçorive të Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Terminali lokal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Aktivizo aplikacionin terminal që ofron qasje në guaskën lokale"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Kontrolli HDCP"</string> diff --git a/packages/SettingsLib/res/values-sr/arrays.xml b/packages/SettingsLib/res/values-sr/arrays.xml index c543ac1a7fde..a4e9156c3a72 100644 --- a/packages/SettingsLib/res/values-sr/arrays.xml +++ b/packages/SettingsLib/res/values-sr/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (подразумевано)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Користи избор система (подразумевано)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index a395d7b96725..08e2bc85db4e 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Омогући Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Верзија Bluetooth AVRCP-а"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Изаберите верзију Bluetooth AVRCP-а"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Верзија Bluetooth MAP-а"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Изаберите верзију Bluetooth MAP-а"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth аудио кодек"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Изаберите Bluetooth аудио кодек\n"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Брзина узорковања за Bluetooth аудио"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Проверава да ли су апликације инсталиране преко ADB-а/ADT-а штетне."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Биће приказани Bluetooth уређаји без назива (само са MAC адресама)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Онемогућава главно подешавање јачине звука на Bluetooth уређају у случају проблема са јачином звука на даљинским уређајима, као што су изузетно велика јачина звука или недостатак контроле."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Омогућава групу Bluetooth Gabeldorche функција."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Омогућава групу Bluetooth Gabeldorsche функција."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Локални терминал"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Омогући апл. терминала за приступ локалном командном окружењу"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP провера"</string> diff --git a/packages/SettingsLib/res/values-sv/arrays.xml b/packages/SettingsLib/res/values-sv/arrays.xml index c31b80c0eac8..b5b1186aba3c 100644 --- a/packages/SettingsLib/res/values-sv/arrays.xml +++ b/packages/SettingsLib/res/values-sv/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (standard)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Använd systemval (standardinställning)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index f021cc2db25c..c0cdbc913aea 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktivera Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"AVRCP-version för Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Välj AVRCP-version för Bluetooth"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP-version för Bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Välj MAP-version för Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Ljudkodek för Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Aktivera ljudkodek för Bluetooth\nVal"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Samplingsfrekvens för Bluetooth-ljud"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Kontrollera om appar som installeras via ADB/ADT kan vara skadliga."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bluetooth-enheter utan namn (enbart MAC-adresser) visas"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Inaktivera Bluetooth-funktionen Absolute volume om det skulle uppstå problem med volymen på fjärrenheter, t.ex. alldeles för hög volym eller brist på kontroll."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Aktiverar funktionsgruppen Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Aktiverar funktionsgruppen Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Lokal terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Aktivera en terminalapp som ger åtkomst till hyllor lokalt"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP-kontroll"</string> diff --git a/packages/SettingsLib/res/values-sw/arrays.xml b/packages/SettingsLib/res/values-sw/arrays.xml index ff488583fa68..a29b74e6d17d 100644 --- a/packages/SettingsLib/res/values-sw/arrays.xml +++ b/packages/SettingsLib/res/values-sw/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"RAMANI YA 1.2 (Chaguomsingi)"</item> + <item msgid="6817922176194686449">"RAMANI YA 1.3"</item> + <item msgid="3423518690032737851">"RAMANI YA 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"ramani ya 12"</item> + <item msgid="7073042887003102964">"ramani ya 13"</item> + <item msgid="8147982633566548515">"ramani ya 14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Tumia Uteuzi wa Mfumo (Chaguomsingi)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 42422fb26133..f00dea3822cf 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Washa Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Toleo la Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Chagua Toleo la Bluetooth AVRCP"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Toleo la Ramani ya Bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Chagua Toleo la Ramani ya Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Kodeki ya Sauti ya Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Weka Kodeki ya Sauti ya Bluetooth\nUteuzi"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Kiwango cha Sampuli ya Sauti ya Bluetooth"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Kagua iwapo programu zilizosakinishwa kupitia ADB/ADT zina tabia ya kudhuru."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Itaonyesha vifaa vya Bluetooth bila majina (anwani za MAC pekee)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Huzima kipengele cha Bluetooth cha sauti kamili kunapotokea matatizo ya sauti katika vifaa vya mbali kama vile sauti ya juu mno au inaposhindikana kuidhibiti."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Huwasha rafu ya kipengele cha Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Huwasha rafu ya kipengele ya Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Kituo cha karibu"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Washa programu ya mwisho inayotoa ufikiaji mkuu wa karibu"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Inakagua HDCP"</string> diff --git a/packages/SettingsLib/res/values-ta/arrays.xml b/packages/SettingsLib/res/values-ta/arrays.xml index 5668b6d8c327..0f19148435d7 100644 --- a/packages/SettingsLib/res/values-ta/arrays.xml +++ b/packages/SettingsLib/res/values-ta/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (இயல்பாக)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"சாதனத் தேர்வைப் பயன்படுத்து (இயல்பு)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 295399d4a046..52e0363147da 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -227,10 +227,11 @@ <string name="tethering_hardware_offload" msgid="4116053719006939161">"வன்பொருள் விரைவுப்படுத்துதல் இணைப்பு முறை"</string> <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"பெயர்கள் இல்லாத புளூடூத் சாதனங்களைக் காட்டு"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"அப்சல்யூட் ஒலியளவு அம்சத்தை முடக்கு"</string> - <!-- no translation found for bluetooth_enable_gabeldorsche (9131730396242883416) --> - <skip /> + <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorscheவை இயக்கு"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"புளூடூத் AVRCP பதிப்பு"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"புளூடூத் AVRCP பதிப்பைத் தேர்ந்தெடு"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"புளூடூத்தின் MAP பதிப்பு"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"புளூடூத்தின் MAP பதிப்பைத் தேர்வுசெய்க"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"புளூடூத் ஆடியோ கோடெக்"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"புளூடூத் ஆடியோ கோடெக்கைத் தொடங்கு\nதேர்வு"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"புளூடூத் ஆடியோ சாம்பிள் ரேட்"</string> @@ -277,8 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"தீங்கு விளைவிக்கும் செயல்பாட்டை அறிய ADB/ADT மூலம் நிறுவப்பட்ட ஆப்ஸைச் சரிபார்."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"பெயர்கள் இல்லாத புளூடூத் சாதனங்கள் (MAC முகவரிகள் மட்டும்) காட்டப்படும்"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"மிகவும் அதிகமான ஒலியளவு அல்லது கட்டுப்பாடு இழப்பு போன்ற தொலைநிலைச் சாதனங்களில் ஏற்படும் ஒலி தொடர்பான சிக்கல்கள் இருக்கும் சமயங்களில், புளூடூத் அப்சல்யூட் ஒலியளவு அம்சத்தை முடக்கும்."</string> - <!-- no translation found for bluetooth_enable_gabeldorsche_summary (8472344901097607030) --> - <skip /> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"புளூடூத்தின் Gabeldorsche அம்சங்களை இயக்கும்."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"அக முனையம்"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"அக ஷெல் அணுகலை வழங்கும் இறுதிப் ஆப்ஸை இயக்கு"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP சரிபார்ப்பு"</string> diff --git a/packages/SettingsLib/res/values-te/arrays.xml b/packages/SettingsLib/res/values-te/arrays.xml index 70068bfe0033..23256eeb7d03 100644 --- a/packages/SettingsLib/res/values-te/arrays.xml +++ b/packages/SettingsLib/res/values-te/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (డిఫాల్ట్)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"సిస్టమ్ ఎంపికను ఉపయోగించండి (డిఫాల్ట్)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index f92b8af24cf7..a39c4e14cddd 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorscheను ఎనేబుల్ చేయి"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"బ్లూటూత్ AVRCP వెర్షన్"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"బ్లూటూత్ AVRCP సంస్కరణను ఎంచుకోండి"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"బ్లూటూత్ MAP వెర్షన్"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"బ్లూటూత్ MAP వెర్షన్ను ఎంచుకోండి"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"బ్లూటూత్ ఆడియో కోడెక్"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"బ్లూటూత్ ఆడియో కోడెక్ని సక్రియం చేయండి\nఎంపిక"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"బ్లూటూత్ ఆడియో నమూనా రేట్"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"హానికరమైన ప్రవర్తన కోసం ADB/ADT ద్వారా ఇన్స్టాల్ చేయబడిన యాప్లను తనిఖీ చేయి."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"పేర్లు (MAC చిరునామాలు మాత్రమే) లేని బ్లూటూత్ పరికరాలు ప్రదర్శించబడతాయి"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"రిమోట్ పరికరాల్లో ఆమోదించలేని స్థాయిలో అధిక వాల్యూమ్ ఉండటం లేదా వాల్యూమ్ నియంత్రణ లేకపోవడం వంటి సమస్యలు ఉంటే బ్లూటూత్ సంపూర్ణ వాల్యూమ్ ఫీచర్ని నిలిపివేస్తుంది."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"బ్లూటూత్ ఫీచర్ స్ట్యాక్ను ఎనేబుల్ చేస్తుంది."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"బ్లూటూత్ Gabeldorsche ఫీచర్ స్ట్యాక్ను ఎనేబుల్ చేస్తుంది."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"స్థానిక టెర్మినల్"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"స్థానిక షెల్ ప్రాప్యతను అందించే టెర్మినల్ అనువర్తనాన్ని ప్రారంభించు"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP తనిఖీ"</string> diff --git a/packages/SettingsLib/res/values-th/arrays.xml b/packages/SettingsLib/res/values-th/arrays.xml index 20333b72aff7..8aac165da6d3 100644 --- a/packages/SettingsLib/res/values-th/arrays.xml +++ b/packages/SettingsLib/res/values-th/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (ค่าเริ่มต้น)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"ใช้การเลือกของระบบ (ค่าเริ่มต้น)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 19ae491d4763..635d77a77a35 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"เปิดใช้ Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"เวอร์ชันของบลูทูธ AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"เลือกเวอร์ชันของบลูทูธ AVRCP"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"เวอร์ชัน MAP ของบลูทูธ"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"เลือกเวอร์ชัน MAP ของบลูทูธ"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"ตัวแปลงสัญญาณเสียงบลูทูธ"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"ทริกเกอร์การเลือกตัวแปลงรหัส\nเสียงบลูทูธ"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"อัตราตัวอย่างเสียงบลูทูธ"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ตรวจสอบแอปที่ติดตั้งผ่าน ADB/ADT เพื่อตรวจดูพฤติกรรมที่เป็นอันตราย"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"ระบบจะแสดงอุปกรณ์บลูทูธที่ไม่มีชื่อ (มีเฉพาะที่อยู่ MAC)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"ปิดใช้ฟีเจอร์การควบคุมระดับเสียงของอุปกรณ์อื่นผ่านบลูทูธในกรณีที่มีปัญหาเกี่ยวกับระดับเสียงของอุปกรณ์ระยะไกล เช่น ระดับเสียงที่ดังเกินไปหรือระดับเสียงที่ไม่มีการควบคุม"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"เปิดใช้สแต็กฟีเจอร์ Bluetooth Gabeldorche"</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"เปิดใช้สแต็กฟีเจอร์ Bluetooth Gabeldorsche"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"เทอร์มินัลในตัวเครื่อง"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"เปิดใช้งานแอปเทอร์มินัลที่ให้การเข้าถึงเชลล์ในตัวเครื่อง"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"การตรวจสอบ HDCP"</string> diff --git a/packages/SettingsLib/res/values-tl/arrays.xml b/packages/SettingsLib/res/values-tl/arrays.xml index 473480722213..9e08b8fd3ed8 100644 --- a/packages/SettingsLib/res/values-tl/arrays.xml +++ b/packages/SettingsLib/res/values-tl/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (Default)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Gamitin ang Pagpili ng System (Default)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index f6dfdba83b2e..3f7f0ff56f8a 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"I-enable ang Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bersyon ng AVRCP ng Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pumili ng Bersyon ng AVRCP ng Bluetooth"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bersyon ng MAP ng Bluetooth"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Pumili ng Bersyon ng MAP ng Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth Audio Codec"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"I-trigger ang Pagpili sa Audio Codec ng\nBluetooth"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Sample na Rate ng Bluetooth Audio"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Tingnan kung may nakakahamak na pagkilos sa apps na na-install sa pamamagitan ng ADB/ADT."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Ipapakita ang mga Bluetooth device na walang pangalan (mga MAC address lang)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Dini-disable ang absolute volume feature ng Bluetooth kung may mga isyu sa volume ang mga malayong device gaya ng hindi katanggap-tanggap na malakas na volume o kawalan ng kontrol."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Ine-enable ang stack ng feature ng Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Ine-enable ang stack ng feature ng Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Lokal na terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Paganahin ang terminal app na nag-aalok ng lokal na shell access"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Pagsusuring HDCP"</string> diff --git a/packages/SettingsLib/res/values-tr/arrays.xml b/packages/SettingsLib/res/values-tr/arrays.xml index ac6e0f502bfb..7ce6c24d321e 100644 --- a/packages/SettingsLib/res/values-tr/arrays.xml +++ b/packages/SettingsLib/res/values-tr/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (Varsayılan)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Sistem Seçimini Kullan (Varsayılan)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 554d62e7164d..7ad6fcd24ea1 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche\'yi etkileştir"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP Sürümü"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP Sürümünü seçin"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP Sürümü"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Bluetooth MAP Sürümünü seçin"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth Ses Codec\'i"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Bluetooth Ses Codec\'i Tetikleme\nSeçimi"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth Ses Örnek Hızı"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ADB/ADT üzerinden yüklenen uygulamaları zararlı davranışlara karşı denetle."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Adsız Bluetooth cihazları (yalnızca MAC adresleri) gösterilecek"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Uzak cihazda sesin aşırı yüksek olması veya kontrol edilememesi gibi ses sorunları olması ihtimaline karşı Bluetooh mutlak ses özelliğini iptal eder."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Bluetooth Gabeldorche özellik grubunu etkinleştirir."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Bluetooth Gabeldorsche özellik yığınını etkinleştirir."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Yerel terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Yerel kabuk erişimi sunan terminal uygulamasını etkinleştir"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP denetimi"</string> diff --git a/packages/SettingsLib/res/values-uk/arrays.xml b/packages/SettingsLib/res/values-uk/arrays.xml index effd49608921..2d0abe09a377 100644 --- a/packages/SettingsLib/res/values-uk/arrays.xml +++ b/packages/SettingsLib/res/values-uk/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (за умовчанням)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Використовувати вибір системи (за умовчанням)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 17f2393e9320..b5dd61898470 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Увімкнути Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версія Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Виберіть версію Bluetooth AVRCP"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Версія Bluetooth MAP"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Виберіть версію Bluetooth MAP"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Кодек для аудіо Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Активувати кодек для аудіо Bluetooth\nВибір"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Частота вибірки для аудіо Bluetooth"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Перевіряти безпеку додатків, установлених через ADB/ADT."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Пристрої Bluetooth відображатимуться без назв (лише MAC-адреси)"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Функція абсолютної гучності Bluetooth вимикається, якщо на віддалених пристроях виникають проблеми, як-от надто висока гучність або втрата контролю."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Вмикає функції Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Вмикає функції Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Локальний термінал"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Увімк. програму-термінал, що надає локальний доступ до оболонки"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Перевірка HDCP"</string> diff --git a/packages/SettingsLib/res/values-ur/arrays.xml b/packages/SettingsLib/res/values-ur/arrays.xml index d5a59ac78a5b..e056c1c1ecc1 100644 --- a/packages/SettingsLib/res/values-ur/arrays.xml +++ b/packages/SettingsLib/res/values-ur/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (ڈیفالٹ)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 6830c595ef2d..ef9b2a1b294e 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche فعال کریں"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"بلوٹوتھ AVRCP ورژن"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"بلوٹوتھ AVRCP ورژن منتخب کریں"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"بلوٹوتھ MAP ورژن"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"بلوٹوتھ MAP ورژن منتخب کریں"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"بلوٹوتھ آڈیو کوڈیک"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"بلوٹوتھ آڈیو کوڈیک کو ٹریگر کریں\nانتخاب"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"بلوٹوتھ آڈیو کے نمونے کی شرح"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"نقصان دہ رویے کے مدنظر ADB/ADT کی معرفت انسٹال شدہ ایپس کی جانچ کریں۔"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"بغیر نام والے بلوٹوتھ آلات (صرف MAC پتے) ڈسپلے کئے جائیں گے"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"ریموٹ آلات کے ساتھ والیوم کے مسائل مثلاً نا قابل قبول حد تک بلند والیوم یا کنٹرول نہ ہونے کی صورت میں بلو ٹوتھ مطلق والیوم والی خصوصیت کو غیر فعال کریں۔"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"بلوٹوتھ Gabeldorche خصوصیت کے انبار کو فعال کرتا ہے۔"</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"بلوٹوتھ Gabeldorsche خصوصیت کے انبار کو فعال کرتا ہے۔"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"مقامی ٹرمینل"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"مقامی شیل رسائی پیش کرنے والی ٹرمینل ایپ فعال کریں"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP چیکنگ"</string> diff --git a/packages/SettingsLib/res/values-uz/arrays.xml b/packages/SettingsLib/res/values-uz/arrays.xml index 4d30e4638902..892ebe0a6e9e 100644 --- a/packages/SettingsLib/res/values-uz/arrays.xml +++ b/packages/SettingsLib/res/values-uz/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (Standart)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Tizim tanlovi (birlamchi)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 52ec545d9e80..b202d6444965 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche funksiyasini yoqish"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP versiyasi"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP versiyasini tanlang"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP versiyasi"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Bluetooth MAP versiyasini tanlang"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth audio kodeki"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Bluetooth orqali uzatish uchun audiokodek\nTanlash"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Bluetooth audio namunasi chastotasi"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ADB/ADT orqali o‘rnatilgan ilovalar xavfsizligini tekshiring"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bluetooth qurilmalari nomsiz (faqat MAC manzillari) ko‘rsatiladi"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Masofadan ulanadigan qurilmalar bilan muammolar yuz berganda, jumladan, juda baland ovoz yoki sozlamalarni boshqarib bo‘lmaydigan holatlarda Bluetooth ovozi balandligining mutlaq darajasini o‘chirib qo‘yadi."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Bluetooth Gabeldorche funksiyasini ishga tushiradi."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Bluetooth Gabeldorsche funksiyasini ishga tushiradi."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Mahalliy terminal"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Mahalliy terminalga kirishga ruxsat beruvchi terminal ilovani faollashtirish"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP tekshiruvi"</string> diff --git a/packages/SettingsLib/res/values-vi/arrays.xml b/packages/SettingsLib/res/values-vi/arrays.xml index edfe89e0b0ee..db29bc86e8a8 100644 --- a/packages/SettingsLib/res/values-vi/arrays.xml +++ b/packages/SettingsLib/res/values-vi/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (Mặc định)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Sử dụng lựa chọn của hệ thống (Mặc định)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 2a124646cd5f..cda42d348c45 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Bật tính năng Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Phiên bản Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Chọn phiên bản Bluetooth AVRCP"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Phiên bản Bluetooth MAP"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Chọn phiên bản Bluetooth MAP"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Codec âm thanh Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Kích hoạt chế độ chọn codec\nâm thanh Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Tốc độ lấy mẫu âm thanh Bluetooth"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Kiểm tra các ứng dụng được cài đặt qua ADB/ADT để xem có hoạt động gây hại hay không."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Các thiết bị Bluetooth không có tên (chỉ có địa chỉ MAC) sẽ được hiển thị"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Vô hiệu hóa tính năng âm lượng tuyệt đối qua Bluetooth trong trường hợp xảy ra sự cố về âm lượng với các thiết bị từ xa, chẳng hạn như âm lượng lớn không thể chấp nhận được hoặc thiếu kiểm soát."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Bật ngăn xếp tính năng Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Bật ngăn xếp tính năng Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Dòng lệnh cục bộ"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Bật ứng dụng dòng lệnh cung cấp quyền truy cập vỏ cục bộ"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Kiểm tra HDCP"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/arrays.xml b/packages/SettingsLib/res/values-zh-rCN/arrays.xml index 992e3e083358..3016f65905e2 100644 --- a/packages/SettingsLib/res/values-zh-rCN/arrays.xml +++ b/packages/SettingsLib/res/values-zh-rCN/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2(默认)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"使用系统选择(默认)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 3fe925b11293..418370b916e6 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"启用“Gabeldorsche”"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"蓝牙 AVRCP 版本"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"选择蓝牙 AVRCP 版本"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"蓝牙 MAP 版本"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"选择蓝牙 MAP 版本"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"蓝牙音频编解码器"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"触发蓝牙音频编解码器\n选择"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"蓝牙音频采样率"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"检查通过 ADB/ADT 安装的应用是否存在有害行为。"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"系统将显示没有名称(只有 MAC 地址)的蓝牙设备"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"停用蓝牙绝对音量功能,即可避免在连接到远程设备时出现音量问题(例如音量高得让人无法接受或无法控制音量等)。"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"启用“蓝牙 Gabeldorche”功能堆栈。"</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"启用“蓝牙 Gabeldorsche”功能堆栈。"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"本地终端"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"启用终端应用,以便在本地访问 Shell"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP 检查"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/arrays.xml b/packages/SettingsLib/res/values-zh-rHK/arrays.xml index d91e61ec3032..0b57af9932d1 100644 --- a/packages/SettingsLib/res/values-zh-rHK/arrays.xml +++ b/packages/SettingsLib/res/values-zh-rHK/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (預設)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"使用系統選擇 (預設)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index ed6d5054ba8c..79b55796bda2 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"啟用 Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"藍牙 AVRCP 版本"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"選擇藍牙 AVRCP 版本"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"藍牙 MAP 版本"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"選擇藍牙 MAP 版本"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"藍牙音訊編解碼器"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"觸發藍牙音訊編解碼器\n選項"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"藍牙音訊取樣率"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"透過 ADB/ADT 檢查安裝的應用程式有否有害的行為。"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"系統將顯示沒有名稱 (只有 MAC 位址) 的藍牙裝置"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"連線至遠端裝置時,如發生音量過大或無法控制音量等問題,請停用藍牙絕對音量功能。"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"啟用藍牙 Gabeldorche 功能組合。"</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"啟用藍牙 Gabeldorsche 功能組合。"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"本機終端機"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"啟用可提供本機命令介面存取權的終端機應用程式"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP 檢查"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/arrays.xml b/packages/SettingsLib/res/values-zh-rTW/arrays.xml index f39ab84c56bc..7b25772056a2 100644 --- a/packages/SettingsLib/res/values-zh-rTW/arrays.xml +++ b/packages/SettingsLib/res/values-zh-rTW/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"avrcp15"</item> <item msgid="1963366694959681026">"avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"MAP 1.2 (預設)"</item> + <item msgid="6817922176194686449">"MAP 1.3"</item> + <item msgid="3423518690032737851">"MAP 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"map12"</item> + <item msgid="7073042887003102964">"map13"</item> + <item msgid="8147982633566548515">"map14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"系統自動選擇 (預設)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 45866d4fde92..47ab7641b7ad 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"啟用 Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"藍牙 AVRCP 版本"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"選取藍牙 AVRCP 版本"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"藍牙 MAP 版本"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"選取 Bluetooth MAP 版本"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"藍牙音訊轉碼器"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"觸發藍牙音訊轉碼器\n選項"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"藍牙音訊取樣率"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"檢查透過 ADB/ADT 安裝的應用程式是否具有有害行為。"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"系統會顯示沒有名稱 (僅具有 MAC 位址) 的藍牙裝置"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"只要停用藍牙絕對音量功能,即可避免在連線到遠端裝置時,發生音量過大或無法控制音量等問題。"</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"啟用藍牙 Gabeldorsche 功能堆疊。"</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"啟用藍牙 Gabeldorsche 功能堆疊。"</string> <string name="enable_terminal_title" msgid="3834790541986303654">"本機終端機"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"啟用可提供本機命令介面存取權的終端機應用程式"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP 檢查"</string> diff --git a/packages/SettingsLib/res/values-zu/arrays.xml b/packages/SettingsLib/res/values-zu/arrays.xml index 5c93cc5b16b1..517d1c85f966 100644 --- a/packages/SettingsLib/res/values-zu/arrays.xml +++ b/packages/SettingsLib/res/values-zu/arrays.xml @@ -75,6 +75,16 @@ <item msgid="4398977131424970917">"I-avrcp15"</item> <item msgid="1963366694959681026">"I-avrcp16"</item> </string-array> + <string-array name="bluetooth_map_versions"> + <item msgid="8786402640610987099">"IMEPHU 1.2 (Okuzenzakalelayo)"</item> + <item msgid="6817922176194686449">"IMEPHU 1.3"</item> + <item msgid="3423518690032737851">"IMEPHU 1.4"</item> + </string-array> + <string-array name="bluetooth_map_version_values"> + <item msgid="1164651830068248391">"Imephu12"</item> + <item msgid="7073042887003102964">"Imephu13"</item> + <item msgid="8147982633566548515">"Imephu14"</item> + </string-array> <string-array name="bluetooth_a2dp_codec_titles"> <item msgid="2494959071796102843">"Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)"</item> <item msgid="4055460186095649420">"SBC"</item> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 8b004f96d4f3..87f45de36c58 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -230,6 +230,8 @@ <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Nika amandla i-Gabeldorsche"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Inguqulo ye-Bluetooth ye-AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Khetha inguqulo ye-Bluetooth AVRCP"</string> + <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Inguqulo ye-Bluetooth MAP"</string> + <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Khetha inguqulo ye-Bluetooth MAP"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"I-Bluetooth Audio Codec"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Qalisa i-codec ye-bluetooth yomsindo\nUkukhethwa"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Isilinganiso sesampula yomsindo we-Bluetooth"</string> @@ -276,7 +278,7 @@ <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Hlola izinhlelo zokusebenza ezifakiwe nge-ADB/ADT ngokuziphatha okuyingozi."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Amadivayisi e-Bluetooth anganawo amagama (Amakheli e-MAC kuphela) azoboniswa"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Ikhubaza isici esiphelele sevolumu ye-Bluetooth uma kuba nezinkinga zevolumu ngamadivayisi esilawuli kude ezifana nevolumu ephezulu noma eshoda ngokulawuleka."</string> - <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Inika amandla isitaki sesici se-Bluetooth Gabeldorche."</string> + <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Inika amandla isitaki sesici se-Bluetooth Gabeldorsche."</string> <string name="enable_terminal_title" msgid="3834790541986303654">"Itheminali yasendaweni"</string> <string name="enable_terminal_summary" msgid="2481074834856064500">"Nika amandla uhlelo lokusebenza letheminali olunikeza ukufinyelela kwasendaweni kwe-shell"</string> <string name="hdcp_checking_title" msgid="3155692785074095986">"Ihlola i-HDCP"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractImsStatusPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractImsStatusPreferenceController.java index 4c3e605e3a94..d427f7a20dba 100644 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractImsStatusPreferenceController.java +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractImsStatusPreferenceController.java @@ -23,7 +23,9 @@ import android.net.wifi.WifiManager; import android.os.PersistableBundle; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; +import android.telephony.ims.ImsMmTelManager; +import android.telephony.ims.RegistrationManager; +import android.util.Log; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; @@ -32,15 +34,26 @@ import androidx.preference.PreferenceScreen; import com.android.settingslib.R; import com.android.settingslib.core.lifecycle.Lifecycle; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; + /** * Preference controller for IMS status */ public abstract class AbstractImsStatusPreferenceController extends AbstractConnectivityPreferenceController { + private static final String LOG_TAG = "AbstractImsPrefController"; + @VisibleForTesting static final String KEY_IMS_REGISTRATION_STATE = "ims_reg_state"; + private static final long MAX_THREAD_BLOCKING_TIME_MS = 2000; + private static final String[] CONNECTIVITY_INTENTS = { BluetoothAdapter.ACTION_STATE_CHANGED, ConnectivityManager.CONNECTIVITY_ACTION, @@ -57,8 +70,9 @@ public abstract class AbstractImsStatusPreferenceController @Override public boolean isAvailable() { - CarrierConfigManager configManager = mContext.getSystemService(CarrierConfigManager.class); - int subId = SubscriptionManager.getDefaultDataSubscriptionId(); + final CarrierConfigManager configManager = + mContext.getSystemService(CarrierConfigManager.class); + final int subId = SubscriptionManager.getDefaultDataSubscriptionId(); PersistableBundle config = null; if (configManager != null) { config = configManager.getConfigForSubId(subId); @@ -86,11 +100,57 @@ public abstract class AbstractImsStatusPreferenceController @Override protected void updateConnectivity() { - int subId = SubscriptionManager.getDefaultDataSubscriptionId(); - if (mImsStatus != null) { - TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); - mImsStatus.setSummary((tm != null && tm.isImsRegistered(subId)) ? - R.string.ims_reg_status_registered : R.string.ims_reg_status_not_registered); + if (mImsStatus == null) { + return; + } + final int subId = SubscriptionManager.getDefaultDataSubscriptionId(); + if (!SubscriptionManager.isValidSubscriptionId(subId)) { + mImsStatus.setSummary(R.string.ims_reg_status_not_registered); + return; + } + final ExecutorService executors = Executors.newSingleThreadExecutor(); + final StateCallback stateCallback = new StateCallback(); + + final ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(subId); + try { + imsMmTelManager.getRegistrationState(executors, stateCallback); + } catch (Exception ex) { + } + + mImsStatus.setSummary(stateCallback.waitUntilResult() + ? R.string.ims_reg_status_registered : R.string.ims_reg_status_not_registered); + + try { + executors.shutdownNow(); + } catch (Exception exception) { + } + } + + private final class StateCallback extends AtomicBoolean implements Consumer<Integer> { + private StateCallback() { + super(false); + mSemaphore = new Semaphore(0); + } + + private final Semaphore mSemaphore; + + public void accept(Integer state) { + set(state == RegistrationManager.REGISTRATION_STATE_REGISTERED); + try { + mSemaphore.release(); + } catch (Exception ex) { + } + } + + public boolean waitUntilResult() { + try { + if (!mSemaphore.tryAcquire(MAX_THREAD_BLOCKING_TIME_MS, TimeUnit.MILLISECONDS)) { + Log.w(LOG_TAG, "IMS registration state query timeout"); + return false; + } + } catch (Exception ex) { + } + return get(); } } } diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java index 05a6ce4578db..9d7e2c821297 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java @@ -38,7 +38,7 @@ public class DataUsageUtils { final SubscriptionManager subscriptionManager = context.getSystemService( SubscriptionManager.class); final NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll( - telephonyManager.createForSubscriptionId(subId).getSubscriberId()); + telephonyManager.getSubscriberId()); if (!subscriptionManager.isActiveSubscriptionId(subId)) { Log.i(TAG, "Subscription is not active: " + subId); diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp index 96a98dca8a5b..d67bd8da844f 100644 --- a/packages/SettingsProvider/Android.bp +++ b/packages/SettingsProvider/Android.bp @@ -30,7 +30,7 @@ android_test { "src/com/android/providers/settings/SettingsBackupAgent.java", "src/com/android/providers/settings/SettingsState.java", "src/com/android/providers/settings/SettingsHelper.java", - "src/com/android/providers/settings/WifiSoftApBandChangedNotifier.java", + "src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java", ], static_libs: [ "androidx.test.rules", diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index fb558abe3865..cdf97285a16f 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -968,18 +968,14 @@ public class SettingsBackupAgent extends BackupAgentHelper { } private void restoreSoftApConfiguration(byte[] data) { - SoftApConfiguration config = mWifiManager.restoreSoftApBackupData(data); - if (config != null) { - int originalApBand = config.getBand(); + SoftApConfiguration configInCloud = mWifiManager.restoreSoftApBackupData(data); + if (configInCloud != null) { if (DEBUG) Log.d(TAG, "Successfully unMarshaled SoftApConfiguration "); - - // Depending on device hardware, we may need to notify the user of a setting change for - // the apBand preference - boolean dualMode = mWifiManager.isDualModeSupported(); - int storedApBand = mWifiManager.getSoftApConfiguration().getBand(); - if (dualMode && storedApBand != originalApBand) { + // Depending on device hardware, we may need to notify the user of a setting change + SoftApConfiguration storedConfig = mWifiManager.getSoftApConfiguration(); + if (!storedConfig.equals(configInCloud)) { Log.d(TAG, "restored ap configuration requires a conversion, notify the user"); - WifiSoftApBandChangedNotifier.notifyUserOfApBandConversion(this); + WifiSoftApConfigChangedNotifier.notifyUserOfConfigConversion(this); } } } 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/SettingsProvider/src/com/android/providers/settings/WifiSoftApBandChangedNotifier.java b/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java index d0d4956725d4..1ee5f9093421 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApBandChangedNotifier.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java @@ -27,17 +27,17 @@ import com.android.internal.messages.nano.SystemMessageProto; import com.android.internal.notification.SystemNotificationChannels; /** - * Helper class for sending notifications when the user's Soft AP Band was changed upon restore. + * Helper class for sending notifications when the user's Soft AP config was changed upon restore. */ -public class WifiSoftApBandChangedNotifier { - private WifiSoftApBandChangedNotifier() {} +public class WifiSoftApConfigChangedNotifier { + private WifiSoftApConfigChangedNotifier() {} /** - * Send a notification informing the user that their' Soft AP Band was changed upon restore. + * Send a notification informing the user that their' Soft AP Config was changed upon restore. * When the user taps on the notification, they are taken to the Wifi Tethering page in * Settings. */ - public static void notifyUserOfApBandConversion(Context context) { + public static void notifyUserOfConfigConversion(Context context) { NotificationManager notificationManager = context.getSystemService(NotificationManager.class); diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 0bcadce7a9c6..6aeb0a159bea 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -174,6 +174,9 @@ <!-- Adding Quick Settings tiles --> <uses-permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE" /> + <!-- Access Quick Access Wallet cards --> + <uses-permission android:name="android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE" /> + <!-- Adding Controls to SystemUI --> <uses-permission android:name="android.permission.BIND_CONTROLS" /> diff --git a/packages/SystemUI/res/drawable/ic_screenrecord.xml b/packages/SystemUI/res/drawable/ic_screenrecord.xml new file mode 100644 index 000000000000..6d8bd0dd66fb --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_screenrecord.xml @@ -0,0 +1,25 @@ +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?android:attr/colorControlNormal"> + <path + android:pathData="M12,12m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0" + android:fillColor="#FFFFFFFF"/> +</vector> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 639005ba1c40..44a7fda6bce3 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -194,6 +194,10 @@ <!-- Power menu item for taking a screenshot [CHAR LIMIT=20]--> <string name="global_action_screenshot">Screenshot</string> + <!-- text to show in place of RemoteInput images when they cannot be shown. + [CHAR LIMIT=50] --> + <string name="remote_input_image_insertion_text">Image inserted</string> + <!-- Notification ticker displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=30] --> <string name="screenshot_saving_ticker">Saving screenshot\u2026</string> <!-- Notification title displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=50] --> @@ -222,6 +226,8 @@ <string name="screenrecord_mic_label">Record voiceover</string> <!-- Label for the checkbox to enable showing location of touches during screen recording [CHAR LIMIT=NONE]--> <string name="screenrecord_taps_label">Show taps</string> + <!-- Label for notification that the user can tap to stop and save the screen recording [CHAR LIMIT=NONE] --> + <string name="screenrecord_stop_text">Tap to stop</string> <!-- Label for notification action to stop and save the screen recording [CHAR LIMIT=35] --> <string name="screenrecord_stop_label">Stop</string> <!-- Label for notification action to pause screen recording [CHAR LIMIT=35] --> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/UniversalSmartspaceUtils.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/UniversalSmartspaceUtils.java index 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 new file mode 100644 index 000000000000..b2423b9bf252 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java @@ -0,0 +1,216 @@ +/* + * 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.keyguard; + +import android.annotation.Nullable; +import android.app.admin.IKeyguardCallback; +import android.app.admin.IKeyguardClient; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Log; +import android.view.SurfaceControl; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.ViewGroup; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * Encapsulates all logic for secondary lockscreen state management. + */ +public class AdminSecondaryLockScreenController { + private static final String TAG = "AdminSecondaryLockScreenController"; + private static final int REMOTE_CONTENT_READY_TIMEOUT_MILLIS = 500; + private final KeyguardUpdateMonitor mUpdateMonitor; + private final Context mContext; + private final ViewGroup mParent; + private AdminSecurityView mView; + private Handler mHandler; + private IKeyguardClient mClient; + private KeyguardSecurityCallback mKeyguardCallback; + private SurfaceControl.Transaction mTransaction; + + private final ServiceConnection mConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + mClient = IKeyguardClient.Stub.asInterface(service); + if (mView.isAttachedToWindow() && mClient != null) { + onSurfaceReady(); + + try { + service.linkToDeath(mKeyguardClientDeathRecipient, 0); + } catch (RemoteException e) { + // Failed to link to death, just dismiss and unbind the service for now. + Log.e(TAG, "Lost connection to secondary lockscreen service", e); + dismiss(KeyguardUpdateMonitor.getCurrentUser()); + } + } + } + + @Override + public void onServiceDisconnected(ComponentName className) { + mClient = null; + } + }; + + private final IBinder.DeathRecipient mKeyguardClientDeathRecipient = () -> { + hide(); // hide also takes care of unlinking to death. + Log.d(TAG, "KeyguardClient service died"); + }; + + private final IKeyguardCallback mCallback = new IKeyguardCallback.Stub() { + @Override + public void onDismiss() { + dismiss(UserHandle.getCallingUserId()); + } + + @Override + public void onSurfaceControlCreated(@Nullable SurfaceControl remoteSurfaceControl) { + if (mHandler != null) { + mHandler.removeCallbacksAndMessages(null); + } + if (remoteSurfaceControl != null) { + mTransaction.reparent(remoteSurfaceControl, mView.getSurfaceControl()) + .apply(); + } else { + dismiss(KeyguardUpdateMonitor.getCurrentUser()); + } + } + }; + + private final KeyguardUpdateMonitorCallback mUpdateCallback = + new KeyguardUpdateMonitorCallback() { + @Override + public void onSecondaryLockscreenRequirementChanged(int userId) { + Intent newIntent = mUpdateMonitor.getSecondaryLockscreenRequirement(userId); + if (newIntent == null) { + dismiss(userId); + } + } + }; + + @VisibleForTesting + protected SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() { + @Override + public void surfaceCreated(SurfaceHolder holder) { + final int userId = KeyguardUpdateMonitor.getCurrentUser(); + mUpdateMonitor.registerCallback(mUpdateCallback); + + if (mClient != null) { + onSurfaceReady(); + } + mHandler.postDelayed( + () -> { + // If the remote content is not readied within the timeout period, + // move on without the secondary lockscreen. + dismiss(userId); + }, + REMOTE_CONTENT_READY_TIMEOUT_MILLIS); + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + mUpdateMonitor.removeCallback(mUpdateCallback); + } + }; + + public AdminSecondaryLockScreenController(Context context, ViewGroup parent, + KeyguardUpdateMonitor updateMonitor, KeyguardSecurityCallback callback, + Handler handler, SurfaceControl.Transaction transaction) { + mContext = context; + mHandler = handler; + mParent = parent; + mTransaction = transaction; + mUpdateMonitor = updateMonitor; + mKeyguardCallback = callback; + mView = new AdminSecurityView(mContext, mSurfaceHolderCallback); + } + + /** + * Displays the Admin security Surface view. + */ + public void show(Intent serviceIntent) { + mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE); + mParent.addView(mView); + } + + /** + * Hides the Admin security Surface view. + */ + public void hide() { + if (mView.isAttachedToWindow()) { + mParent.removeView(mView); + } + if (mClient != null) { + mClient.asBinder().unlinkToDeath(mKeyguardClientDeathRecipient, 0); + mContext.unbindService(mConnection); + mClient = null; + } + } + + private void onSurfaceReady() { + try { + mClient.onSurfaceReady(mView.getHostToken(), mCallback); + } catch (RemoteException e) { + Log.e(TAG, "Error in onSurfaceReady", e); + dismiss(KeyguardUpdateMonitor.getCurrentUser()); + } + } + + private void dismiss(int userId) { + mHandler.removeCallbacksAndMessages(null); + if (mView != null && mView.isAttachedToWindow() + && userId == KeyguardUpdateMonitor.getCurrentUser()) { + hide(); + mKeyguardCallback.dismiss(true, userId); + } + } + + /** + * Custom {@link SurfaceView} used to allow a device admin to present an additional security + * screen. + */ + private class AdminSecurityView extends SurfaceView { + private SurfaceHolder.Callback mSurfaceHolderCallback; + + AdminSecurityView(Context context, SurfaceHolder.Callback surfaceHolderCallback) { + super(context); + mSurfaceHolderCallback = surfaceHolderCallback; + setZOrderOnTop(true); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + getHolder().addCallback(mSurfaceHolderCallback); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + getHolder().removeCallback(mSurfaceHolderCallback); + } + } +} 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/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 9ae446e58e13..ae787260adca 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -21,9 +21,12 @@ import android.app.Activity; import android.app.AlertDialog; import android.app.admin.DevicePolicyManager; import android.content.Context; +import android.content.Intent; import android.content.res.ColorStateList; import android.graphics.Rect; import android.metrics.LogMaker; +import android.os.Handler; +import android.os.Looper; import android.os.UserHandle; import android.util.AttributeSet; import android.util.Log; @@ -31,6 +34,7 @@ import android.util.Slog; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.MotionEvent; +import android.view.SurfaceControl; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; @@ -90,6 +94,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe private AlertDialog mAlertDialog; private InjectionInflationController mInjectionInflationController; private boolean mSwipeUpToRetry; + private AdminSecondaryLockScreenController mSecondaryLockScreenController; private final ViewConfiguration mViewConfiguration; private final SpringAnimation mSpringAnimation; @@ -137,6 +142,9 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe SystemUIFactory.getInstance().getRootComponent()); mViewConfiguration = ViewConfiguration.get(context); mKeyguardStateController = Dependency.get(KeyguardStateController.class); + mSecondaryLockScreenController = new AdminSecondaryLockScreenController(context, this, + mUpdateMonitor, mCallback, new Handler(Looper.myLooper()), + new SurfaceControl.Transaction()); } public void setSecurityCallback(SecurityCallback callback) { @@ -157,6 +165,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe mAlertDialog.dismiss(); mAlertDialog = null; } + mSecondaryLockScreenController.hide(); if (mCurrentSecuritySelection != SecurityMode.None) { getSecurityView(mCurrentSecuritySelection).onPause(); } @@ -532,6 +541,15 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe break; } } + // Check for device admin specified additional security measures. + if (finish) { + Intent secondaryLockscreenIntent = + mUpdateMonitor.getSecondaryLockscreenRequirement(targetUserId); + if (secondaryLockscreenIntent != null) { + mSecondaryLockScreenController.show(secondaryLockscreenIntent); + return false; + } + } if (eventSubtype != -1) { mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER) .setType(MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype)); @@ -751,6 +769,5 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe public void showUsabilityHint() { mSecurityViewFlipper.showUsabilityHint(); } - } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index 5d35169cf926..f61f585cfe7c 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -140,6 +140,13 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe mLayoutTransition.setAnimateParentHierarchy(false); } + // Temporary workaround to allow KeyguardStatusView to inflate a copy for Universal Smartspace. + // Eventually the existing copy will be reparented instead, and we won't need this. + public KeyguardSliceView(Context context, AttributeSet attributeSet) { + this(context, attributeSet, Dependency.get(ActivityStarter.class), + Dependency.get(ConfigurationController.class)); + } + @Override protected void onFinishInflate() { super.onFinishInflate(); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index 5a1c9976f021..61caf3bc5d8f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -18,10 +18,15 @@ package com.android.keyguard; import android.app.ActivityManager; import android.app.IActivityManager; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.res.Resources; import android.graphics.Color; +import android.graphics.PixelFormat; import android.os.Handler; +import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.text.TextUtils; @@ -30,7 +35,11 @@ import android.util.AttributeSet; import android.util.Log; import android.util.Slog; import android.util.TypedValue; +import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; import android.view.View; +import android.view.WindowManager; +import android.view.WindowlessWindowManager; import android.widget.GridLayout; import android.widget.LinearLayout; import android.widget.TextView; @@ -40,6 +49,7 @@ import androidx.core.graphics.ColorUtils; import com.android.internal.widget.LockPatternUtils; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.shared.system.UniversalSmartspaceUtils; import com.android.systemui.statusbar.policy.ConfigurationController; import java.io.FileDescriptor; @@ -76,6 +86,7 @@ public class KeyguardStatusView extends GridLayout implements private int mIconTopMargin; private int mIconTopMarginWithHeader; private boolean mShowingHeader; + private SurfaceControlViewHost mUniversalSmartspaceViewHost; private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { @@ -122,6 +133,38 @@ public class KeyguardStatusView extends GridLayout implements } }; + private BroadcastReceiver mUniversalSmartspaceBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent i) { + // TODO(b/148159743): Restrict to Pixel Launcher. + if (UniversalSmartspaceUtils.ACTION_REQUEST_SMARTSPACE_VIEW.equals(i.getAction())) { + if (mUniversalSmartspaceViewHost != null) { + mUniversalSmartspaceViewHost.die(); + } + SurfaceControl surfaceControl = UniversalSmartspaceUtils.getSurfaceControl(i); + if (surfaceControl != null) { + IBinder input = UniversalSmartspaceUtils.getInputToken(i); + + WindowlessWindowManager windowlessWindowManager = + new WindowlessWindowManager(context.getResources().getConfiguration(), + surfaceControl, input); + mUniversalSmartspaceViewHost = new SurfaceControlViewHost(context, + context.getDisplay(), windowlessWindowManager); + WindowManager.LayoutParams layoutParams = + new WindowManager.LayoutParams( + surfaceControl.getWidth(), + surfaceControl.getHeight(), + WindowManager.LayoutParams.TYPE_APPLICATION, + 0, + PixelFormat.TRANSPARENT); + + mUniversalSmartspaceViewHost.addView( + inflate(context, R.layout.keyguard_status_area, null), layoutParams); + } + } + } + };; + public KeyguardStatusView(Context context) { this(context, null, 0); } @@ -316,6 +359,8 @@ public class KeyguardStatusView extends GridLayout implements super.onAttachedToWindow(); Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mInfoCallback); Dependency.get(ConfigurationController.class).addCallback(this); + getContext().registerReceiver(mUniversalSmartspaceBroadcastReceiver, + new IntentFilter(UniversalSmartspaceUtils.ACTION_REQUEST_SMARTSPACE_VIEW)); } @Override @@ -323,6 +368,7 @@ public class KeyguardStatusView extends GridLayout implements super.onDetachedFromWindow(); Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mInfoCallback); Dependency.get(ConfigurationController.class).removeCallback(this); + getContext().unregisterReceiver(mUniversalSmartspaceBroadcastReceiver); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 65fc215f7505..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; @@ -113,6 +112,7 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Map.Entry; import java.util.TimeZone; import java.util.function.Consumer; @@ -334,6 +334,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private SparseBooleanArray mUserFingerprintAuthenticated = new SparseBooleanArray(); private SparseBooleanArray mUserFaceAuthenticated = new SparseBooleanArray(); private SparseBooleanArray mUserFaceUnlockRunning = new SparseBooleanArray(); + private Map<Integer, Intent> mSecondaryLockscreenRequirement = new HashMap<Integer, Intent>(); private static int sCurrentUser; private Runnable mUpdateBiometricListeningState = this::updateBiometricListeningState; @@ -928,6 +929,45 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return mUserTrustIsManaged.get(userId) && !isTrustDisabled(userId); } + private void updateSecondaryLockscreenRequirement(int userId) { + Intent oldIntent = mSecondaryLockscreenRequirement.get(userId); + boolean enabled = mDevicePolicyManager.isSecondaryLockscreenEnabled(userId); + boolean changed = false; + + if (enabled && (oldIntent == null)) { + ResolveInfo resolveInfo = + mContext.getPackageManager().resolveService( + new Intent( + DevicePolicyManager.ACTION_BIND_SECONDARY_LOCKSCREEN_SERVICE), + 0); + if (resolveInfo != null) { + Intent newIntent = new Intent(); + newIntent.setComponent(resolveInfo.serviceInfo.getComponentName()); + mSecondaryLockscreenRequirement.put(userId, newIntent); + changed = true; + } + } else if (!enabled && (oldIntent != null)) { + mSecondaryLockscreenRequirement.put(userId, null); + changed = true; + } + if (changed) { + for (int i = 0; i < mCallbacks.size(); i++) { + KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); + if (cb != null) { + cb.onSecondaryLockscreenRequirementChanged(userId); + } + } + } + } + + /** + * Returns an Intent by which to bind to a service that will provide additional security screen + * content that must be shown prior to dismissing the keyguard for this user. + */ + public Intent getSecondaryLockscreenRequirement(int userId) { + return mSecondaryLockscreenRequirement.get(userId); + } + /** * Cached version of {@link TrustManager#isTrustUsuallyManaged(int)}. */ @@ -1042,13 +1082,13 @@ 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. // SysUI should just ignore this broadcast because it was already received // and processed previously. - if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, false)) { + if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) { // Guarantee mTelephonyCapable state after SysUI crash and restart if (args.simState == TelephonyManager.SIM_STATE_ABSENT) { mHandler.obtainMessage(MSG_TELEPHONY_CAPABLE, true).sendToTarget(); @@ -1081,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)) { @@ -1113,7 +1153,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab getSendingUserId())); } else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED .equals(action)) { - mHandler.sendEmptyMessage(MSG_DPM_STATE_CHANGED); + mHandler.sendMessage(mHandler.obtainMessage(MSG_DPM_STATE_CHANGED, + getSendingUserId())); } else if (ACTION_USER_UNLOCKED.equals(action)) { mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_UNLOCKED, getSendingUserId(), 0)); @@ -1231,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); @@ -1530,7 +1571,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab handleDeviceProvisioned(); break; case MSG_DPM_STATE_CHANGED: - handleDevicePolicyManagerStateChanged(); + handleDevicePolicyManagerStateChanged(msg.arg1); break; case MSG_USER_SWITCHING: handleUserSwitching(msg.arg1, (IRemoteCallback) msg.obj); @@ -1631,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); @@ -1706,6 +1747,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mUserIsUnlocked.put(user, mUserManager.isUserUnlocked(user)); mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class); mLogoutEnabled = mDevicePolicyManager.isLogoutEnabled(); + updateSecondaryLockscreenRequirement(user); List<UserInfo> allUsers = mUserManager.getUsers(); for (UserInfo userInfo : allUsers) { mUserTrustIsUsuallyManaged.put(userInfo.id, @@ -2046,9 +2088,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab /** * Handle {@link #MSG_DPM_STATE_CHANGED} */ - private void handleDevicePolicyManagerStateChanged() { + private void handleDevicePolicyManagerStateChanged(int userId) { checkIsHandlerThread(); updateFingerprintListeningState(); + updateSecondaryLockscreenRequirement(userId); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java index 04502f054dad..8e87b7ad45b9 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java @@ -310,4 +310,9 @@ public class KeyguardUpdateMonitorCallback { */ public void onBiometricsCleared() { } + /** + * Called when the secondary lock screen requirement changes. + */ + public void onSecondaryLockscreenRequirementChanged(int userId) { } + } 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/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java index 7b4816f5262c..044c5a027dac 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java @@ -28,7 +28,6 @@ import android.hardware.biometrics.BiometricPrompt; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.os.UserManager; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; @@ -381,16 +380,7 @@ public abstract class AuthBiometricView extends LinearLayout { 0 /* animateDurationMs */); mSize = newSize; } else if (newSize == AuthDialog.SIZE_LARGE) { - final boolean isManagedProfile = Utils.isManagedProfile(mContext, mUserId); - - // If it's a managed profile, animate the contents and panel down, since the credential - // contents will be shown on the same "layer" as the background. If it's not a managed - // profile, animate the contents up and expand the panel to full-screen - the credential - // contents will be shown on the same "layer" as the panel. - final float translationY = isManagedProfile ? - -getResources().getDimension( - R.dimen.biometric_dialog_animation_translation_offset) - : getResources().getDimension( + final float translationY = getResources().getDimension( R.dimen.biometric_dialog_medium_to_large_translation_offset); final AuthBiometricView biometricView = this; @@ -421,26 +411,20 @@ public abstract class AuthBiometricView extends LinearLayout { biometricView.setAlpha(opacity); }); - if (!isManagedProfile) { - mPanelController.setUseFullScreen(true); - mPanelController.updateForContentDimensions( - mPanelController.getContainerWidth(), - mPanelController.getContainerHeight(), - mInjector.getMediumToLargeAnimationDurationMs()); - } + mPanelController.setUseFullScreen(true); + mPanelController.updateForContentDimensions( + mPanelController.getContainerWidth(), + mPanelController.getContainerHeight(), + mInjector.getMediumToLargeAnimationDurationMs()); // Start the animations together AnimatorSet as = new AnimatorSet(); List<Animator> animators = new ArrayList<>(); animators.add(translationAnimator); animators.add(opacityAnimator); - if (isManagedProfile) { - animators.add(mPanelController.getTranslationAnimator(translationY)); - animators.add(mPanelController.getAlphaAnimator(0)); - } + as.playTogether(animators); - as.setDuration(isManagedProfile ? mInjector.getMediumToLargeAnimationDurationMs() - : mInjector.getMediumToLargeAnimationDurationMs() * 2 / 3); + as.setDuration(mInjector.getMediumToLargeAnimationDurationMs() * 2 / 3); as.start(); } else { Log.e(TAG, "Unknown transition from: " + mSize + " to: " + newSize); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 4312a5201b08..b8d32aec30e3 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -19,11 +19,8 @@ package com.android.systemui.biometrics; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.admin.DevicePolicyManager; import android.content.Context; import android.graphics.PixelFormat; -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; import android.hardware.biometrics.BiometricAuthenticator; import android.os.Binder; import android.os.Bundle; @@ -168,9 +165,8 @@ public class AuthContainerView extends LinearLayout R.layout.auth_container_view, root, false /* attachToRoot */); } - AuthPanelController getPanelController(Context context, View panelView, - boolean isManagedProfile) { - return new AuthPanelController(context, panelView, isManagedProfile); + AuthPanelController getPanelController(Context context, View panelView) { + return new AuthPanelController(context, panelView); } ImageView getBackgroundView(FrameLayout parent) { @@ -256,10 +252,8 @@ public class AuthContainerView extends LinearLayout final LayoutInflater factory = LayoutInflater.from(mContext); mFrameLayout = mInjector.inflateContainerView(factory, this); - final boolean isManagedProfile = Utils.isManagedProfile(mContext, mConfig.mUserId); - mPanelView = mInjector.getPanelView(mFrameLayout); - mPanelController = mInjector.getPanelController(mContext, mPanelView, isManagedProfile); + mPanelController = mInjector.getPanelController(mContext, mPanelView); // Inflate biometric view only if necessary. if (Utils.isBiometricAllowed(mConfig.mBiometricPromptBundle)) { @@ -281,16 +275,6 @@ public class AuthContainerView extends LinearLayout mBiometricScrollView = mInjector.getBiometricScrollView(mFrameLayout); mBackgroundView = mInjector.getBackgroundView(mFrameLayout); - if (isManagedProfile) { - final Drawable image = getResources().getDrawable(R.drawable.work_challenge_background, - mContext.getTheme()); - final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); - image.setColorFilter(dpm.getOrganizationColorForUser(mConfig.mUserId), - PorterDuff.Mode.DARKEN); - mBackgroundView.setScaleType(ImageView.ScaleType.CENTER_CROP); - mBackgroundView.setImageDrawable(image); - } - addView(mFrameLayout); setOnKeyListener((v, keyCode, event) -> { @@ -608,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/AuthPanelController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java index 4acbadea3a37..11503fbd0b1d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java @@ -37,7 +37,6 @@ public class AuthPanelController extends ViewOutlineProvider { private final Context mContext; private final View mPanelView; - private final boolean mIsManagedProfile; private boolean mUseFullScreen; @@ -115,13 +114,6 @@ public class AuthPanelController extends ViewOutlineProvider { final float cornerRadius = mUseFullScreen ? 0 : mContext.getResources() .getDimension(R.dimen.biometric_dialog_corner_size); - // When going to full-screen for managed profiles, fade away so the managed profile - // background behind this view becomes visible. - final boolean shouldFadeAway = mUseFullScreen && mIsManagedProfile; - final int alpha = shouldFadeAway ? 0 : 255; - final float elevation = shouldFadeAway ? 0 : - mContext.getResources().getDimension(R.dimen.biometric_dialog_elevation); - if (animateDurationMs > 0) { // Animate margin ValueAnimator marginAnimator = ValueAnimator.ofInt(mMargin, margin); @@ -148,21 +140,11 @@ public class AuthPanelController extends ViewOutlineProvider { mContentWidth = (int) animation.getAnimatedValue(); }); - // Animate background - ValueAnimator alphaAnimator = ValueAnimator.ofInt( - mPanelView.getBackground().getAlpha(), alpha); - alphaAnimator.addUpdateListener((animation) -> { - if (shouldFadeAway) { - mPanelView.getBackground().setAlpha((int) animation.getAnimatedValue()); - } - }); - // Play together AnimatorSet as = new AnimatorSet(); as.setDuration(animateDurationMs); as.setInterpolator(new AccelerateDecelerateInterpolator()); - as.playTogether(cornerAnimator, heightAnimator, widthAnimator, marginAnimator, - alphaAnimator); + as.playTogether(cornerAnimator, heightAnimator, widthAnimator, marginAnimator); as.start(); } else { @@ -170,7 +152,6 @@ public class AuthPanelController extends ViewOutlineProvider { mCornerRadius = cornerRadius; mContentWidth = contentWidth; mContentHeight = contentHeight; - mPanelView.getBackground().setAlpha(alpha); mPanelView.invalidateOutline(); } } @@ -183,10 +164,9 @@ public class AuthPanelController extends ViewOutlineProvider { return mContainerHeight; } - AuthPanelController(Context context, View panelView, boolean isManagedProfile) { + AuthPanelController(Context context, View panelView) { mContext = context; mPanelView = panelView; - mIsManagedProfile = isManagedProfile; mCornerRadius = context.getResources() .getDimension(R.dimen.biometric_dialog_corner_size); mMargin = (int) context.getResources() diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index ccbbb2465742..cc0824ecc45c 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -218,7 +218,7 @@ public class BubbleData { } mPendingBubbles.remove(bubble); // No longer pending once we're here Bubble prevBubble = getBubbleWithKey(bubble.getKey()); - suppressFlyout |= !shouldShowFlyout(bubble.getEntry()); + suppressFlyout |= !bubble.getEntry().getRanking().visuallyInterruptive(); if (prevBubble == null) { // Create a new bubble @@ -329,14 +329,6 @@ public class BubbleData { return bubbleChildren; } - private boolean shouldShowFlyout(NotificationEntry notif) { - if (notif.getRanking().visuallyInterruptive()) { - return true; - } - return hasBubbleWithKey(notif.getKey()) - && !getBubbleWithKey(notif.getKey()).showInShade(); - } - private void doAdd(Bubble bubble) { if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "doAdd: " + bubble); 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/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java index f30c181b3c99..79a7df24e217 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java @@ -111,7 +111,9 @@ public class TileQueryHelper { final ArrayList<QSTile> tilesToAdd = new ArrayList<>(); for (String spec : possibleTiles) { - // Only add current and stock tiles that can be created from QSFactoryImpl + // Only add current and stock tiles that can be created from QSFactoryImpl. + // Do not include CustomTile. Those will be created by `addPackageTiles`. + if (spec.startsWith(CustomTile.PREFIX)) continue; final QSTile tile = host.createTile(spec); if (tile == null) { continue; diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java index 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/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index 1b321685e88b..b091ad8c0db8 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -34,6 +34,7 @@ import android.media.MediaRecorder; import android.media.projection.MediaProjection; import android.media.projection.MediaProjectionManager; import android.net.Uri; +import android.os.Bundle; import android.os.IBinder; import android.provider.MediaStore; import android.provider.Settings; @@ -72,9 +73,6 @@ public class RecordingService extends Service { private static final String ACTION_START = "com.android.systemui.screenrecord.START"; private static final String ACTION_STOP = "com.android.systemui.screenrecord.STOP"; - private static final String ACTION_PAUSE = "com.android.systemui.screenrecord.PAUSE"; - private static final String ACTION_RESUME = "com.android.systemui.screenrecord.RESUME"; - private static final String ACTION_CANCEL = "com.android.systemui.screenrecord.CANCEL"; private static final String ACTION_SHARE = "com.android.systemui.screenrecord.SHARE"; private static final String ACTION_DELETE = "com.android.systemui.screenrecord.DELETE"; @@ -94,6 +92,7 @@ public class RecordingService extends Service { private boolean mUseAudio; private boolean mShowTaps; + private boolean mOriginalShowTaps; private File mTempFile; @Inject @@ -145,38 +144,11 @@ public class RecordingService extends Service { } break; - case ACTION_CANCEL: - stopRecording(); - - // Delete temp file - if (!mTempFile.delete()) { - Log.e(TAG, "Error canceling screen recording!"); - Toast.makeText(this, R.string.screenrecord_delete_error, Toast.LENGTH_LONG) - .show(); - } else { - Toast.makeText(this, R.string.screenrecord_cancel_success, Toast.LENGTH_LONG) - .show(); - } - - // Close quick shade - sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - break; - case ACTION_STOP: stopRecording(); saveRecording(notificationManager); break; - case ACTION_PAUSE: - mMediaRecorder.pause(); - setNotificationActions(true, notificationManager); - break; - - case ACTION_RESUME: - mMediaRecorder.resume(); - setNotificationActions(false, notificationManager); - break; - case ACTION_SHARE: Uri shareUri = Uri.parse(intent.getStringExtra(EXTRA_PATH)); @@ -233,9 +205,14 @@ public class RecordingService extends Service { */ private void startRecording() { try { - mTempFile = File.createTempFile("temp", ".mp4"); + File cacheDir = getCacheDir(); + cacheDir.mkdirs(); + mTempFile = File.createTempFile("temp", ".mp4", cacheDir); Log.d(TAG, "Writing video output to: " + mTempFile.getAbsolutePath()); + mOriginalShowTaps = 1 == Settings.System.getInt( + getApplicationContext().getContentResolver(), + Settings.System.SHOW_TOUCHES, 0); setTapsVisible(mShowTaps); // Set up media recorder @@ -295,50 +272,33 @@ public class RecordingService extends Service { NotificationChannel channel = new NotificationChannel( CHANNEL_ID, getString(R.string.screenrecord_name), - NotificationManager.IMPORTANCE_LOW); + NotificationManager.IMPORTANCE_DEFAULT); channel.setDescription(getString(R.string.screenrecord_channel_description)); channel.enableVibration(true); NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.createNotificationChannel(channel); + Bundle extras = new Bundle(); + extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, + getResources().getString(R.string.screenrecord_name)); + mRecordingNotificationBuilder = new Notification.Builder(this, CHANNEL_ID) - .setSmallIcon(R.drawable.ic_android) + .setSmallIcon(R.drawable.ic_screenrecord) .setContentTitle(getResources().getString(R.string.screenrecord_name)) + .setContentText(getResources().getString(R.string.screenrecord_stop_text)) .setUsesChronometer(true) - .setOngoing(true); - setNotificationActions(false, notificationManager); - Notification notification = mRecordingNotificationBuilder.build(); - startForeground(NOTIFICATION_ID, notification); - } - - private void setNotificationActions(boolean isPaused, NotificationManager notificationManager) { - String pauseString = getResources() - .getString(isPaused ? R.string.screenrecord_resume_label - : R.string.screenrecord_pause_label); - Intent pauseIntent = isPaused ? getResumeIntent(this) : getPauseIntent(this); - - mRecordingNotificationBuilder.setActions( - new Notification.Action.Builder( - Icon.createWithResource(this, R.drawable.ic_android), - getResources().getString(R.string.screenrecord_stop_label), - PendingIntent - .getService(this, REQUEST_CODE, getStopIntent(this), - PendingIntent.FLAG_UPDATE_CURRENT)) - .build(), - new Notification.Action.Builder( - Icon.createWithResource(this, R.drawable.ic_android), pauseString, - PendingIntent.getService(this, REQUEST_CODE, pauseIntent, + .setColorized(true) + .setColor(getResources().getColor(R.color.GM2_red_700)) + .setOngoing(true) + .setContentIntent( + PendingIntent.getService( + this, REQUEST_CODE, getStopIntent(this), PendingIntent.FLAG_UPDATE_CURRENT)) - .build(), - new Notification.Action.Builder( - Icon.createWithResource(this, R.drawable.ic_android), - getResources().getString(R.string.screenrecord_cancel_label), - PendingIntent - .getService(this, REQUEST_CODE, getCancelIntent(this), - PendingIntent.FLAG_UPDATE_CURRENT)) - .build()); + .addExtras(extras); notificationManager.notify(NOTIFICATION_ID, mRecordingNotificationBuilder.build()); + Notification notification = mRecordingNotificationBuilder.build(); + startForeground(NOTIFICATION_ID, notification); } private Notification createSaveNotification(Uri uri) { @@ -347,7 +307,7 @@ public class RecordingService extends Service { .setDataAndType(uri, "video/mp4"); Notification.Action shareAction = new Notification.Action.Builder( - Icon.createWithResource(this, R.drawable.ic_android), + Icon.createWithResource(this, R.drawable.ic_screenrecord), getResources().getString(R.string.screenrecord_share_label), PendingIntent.getService( this, @@ -357,7 +317,7 @@ public class RecordingService extends Service { .build(); Notification.Action deleteAction = new Notification.Action.Builder( - Icon.createWithResource(this, R.drawable.ic_android), + Icon.createWithResource(this, R.drawable.ic_screenrecord), getResources().getString(R.string.screenrecord_delete_label), PendingIntent.getService( this, @@ -366,8 +326,12 @@ public class RecordingService extends Service { PendingIntent.FLAG_UPDATE_CURRENT)) .build(); + Bundle extras = new Bundle(); + extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, + getResources().getString(R.string.screenrecord_name)); + Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID) - .setSmallIcon(R.drawable.ic_android) + .setSmallIcon(R.drawable.ic_screenrecord) .setContentTitle(getResources().getString(R.string.screenrecord_name)) .setContentText(getResources().getString(R.string.screenrecord_save_message)) .setContentIntent(PendingIntent.getActivity( @@ -377,7 +341,8 @@ public class RecordingService extends Service { Intent.FLAG_GRANT_READ_URI_PERMISSION)) .addAction(shareAction) .addAction(deleteAction) - .setAutoCancel(true); + .setAutoCancel(true) + .addExtras(extras); // Add thumbnail if available Bitmap thumbnailBitmap = null; @@ -400,7 +365,7 @@ public class RecordingService extends Service { } private void stopRecording() { - setTapsVisible(false); + setTapsVisible(mOriginalShowTaps); mMediaRecorder.stop(); mMediaRecorder.release(); mMediaRecorder = null; @@ -459,18 +424,6 @@ public class RecordingService extends Service { return new Intent(context, RecordingService.class).setAction(ACTION_STOP); } - private static Intent getPauseIntent(Context context) { - return new Intent(context, RecordingService.class).setAction(ACTION_PAUSE); - } - - private static Intent getResumeIntent(Context context) { - return new Intent(context, RecordingService.class).setAction(ACTION_RESUME); - } - - private static Intent getCancelIntent(Context context) { - return new Intent(context, RecordingService.class).setAction(ACTION_CANCEL); - } - private static Intent getShareIntent(Context context, String path) { return new Intent(context, RecordingService.class).setAction(ACTION_SHARE) .putExtra(EXTRA_PATH, path); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index 50e9a51478ed..99a9dfeae1d6 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -240,7 +240,7 @@ 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(); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java index 16447fbdd4aa..a5baa7a49bd0 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java @@ -156,7 +156,7 @@ 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(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt index 083fbc92dc68..ab69d477c2ee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt @@ -22,6 +22,7 @@ import android.os.SystemProperties import android.util.MathUtils import android.view.SurfaceControl import android.view.ViewRootImpl +import androidx.annotation.VisibleForTesting import com.android.internal.util.IndentingPrintWriter import com.android.systemui.DumpController import com.android.systemui.Dumpable @@ -33,7 +34,7 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -class BlurUtils @Inject constructor( +open class BlurUtils @Inject constructor( @Main private val resources: Resources, val dumpController: DumpController ) : Dumpable { @@ -63,22 +64,28 @@ class BlurUtils @Inject constructor( * @param radius blur radius in pixels. */ fun applyBlur(viewRootImpl: ViewRootImpl?, radius: Int) { - if (viewRootImpl == null || !supportsBlursOnWindows()) { + if (viewRootImpl == null || !viewRootImpl.surfaceControl.isValid || + !supportsBlursOnWindows()) { return } - SurfaceControl.Transaction().use { + createTransaction().use { it.setBackgroundBlurRadius(viewRootImpl.surfaceControl, radius) it.apply() } } + @VisibleForTesting + open fun createTransaction(): SurfaceControl.Transaction { + return SurfaceControl.Transaction() + } + /** * If this device can render blurs. * * @see android.view.SurfaceControl.Transaction#setBackgroundBlurRadius(SurfaceControl, int) * @return {@code true} when supported. */ - fun supportsBlursOnWindows(): Boolean { + open fun supportsBlursOnWindows(): Boolean { return blurSysProp && ActivityManager.isHighEndGfx() } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java index 8cc45f2e17d4..4d6764e0a29e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java @@ -195,6 +195,7 @@ public class NotificationListener extends NotificationListenerWithPlugins { new ArrayList<>(), new ArrayList<>(), false, + false, false ); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index 667e721ae37d..f3783c83a301 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -25,8 +25,10 @@ import android.app.KeyguardManager; import android.app.Notification; import android.app.PendingIntent; import android.app.RemoteInput; +import android.app.RemoteInputHistoryItem; import android.content.Context; import android.content.Intent; +import android.net.Uri; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; @@ -345,7 +347,8 @@ public class NotificationRemoteInputManager implements Dumpable { }); mSmartReplyController.setCallback((entry, reply) -> { StatusBarNotification newSbn = - rebuildNotificationWithRemoteInput(entry, reply, true /* showSpinner */); + rebuildNotificationWithRemoteInput(entry, reply, true /* showSpinner */, + null /* mimeType */, null /* uri */); mEntryManager.updateNotification(newSbn, null /* ranking */); }); } @@ -527,28 +530,36 @@ public class NotificationRemoteInputManager implements Dumpable { StatusBarNotification rebuildNotificationForCanceledSmartReplies( NotificationEntry entry) { return rebuildNotificationWithRemoteInput(entry, null /* remoteInputTest */, - false /* showSpinner */); + false /* showSpinner */, null /* mimeType */, null /* uri */); } @VisibleForTesting StatusBarNotification rebuildNotificationWithRemoteInput(NotificationEntry entry, - CharSequence remoteInputText, boolean showSpinner) { + CharSequence remoteInputText, boolean showSpinner, String mimeType, Uri uri) { StatusBarNotification sbn = entry.getSbn(); Notification.Builder b = Notification.Builder .recoverBuilder(mContext, sbn.getNotification().clone()); - if (remoteInputText != null) { - CharSequence[] oldHistory = sbn.getNotification().extras - .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY); - CharSequence[] newHistory; - if (oldHistory == null) { - newHistory = new CharSequence[1]; + if (remoteInputText != null || uri != null) { + RemoteInputHistoryItem[] oldHistoryItems = (RemoteInputHistoryItem[]) + sbn.getNotification().extras.getParcelableArray( + Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); + RemoteInputHistoryItem[] newHistoryItems; + + if (oldHistoryItems == null) { + newHistoryItems = new RemoteInputHistoryItem[1]; } else { - newHistory = new CharSequence[oldHistory.length + 1]; - System.arraycopy(oldHistory, 0, newHistory, 1, oldHistory.length); + newHistoryItems = new RemoteInputHistoryItem[oldHistoryItems.length + 1]; + System.arraycopy(oldHistoryItems, 0, newHistoryItems, 1, oldHistoryItems.length); } - newHistory[0] = String.valueOf(remoteInputText); - b.setRemoteInputHistory(newHistory); + RemoteInputHistoryItem newItem; + if (uri != null) { + newItem = new RemoteInputHistoryItem(mimeType, uri, remoteInputText); + } else { + newItem = new RemoteInputHistoryItem(remoteInputText); + } + newHistoryItems[0] = newItem; + b.setRemoteInputHistory(newHistoryItems); } b.setShowRemoteInputSpinner(showSpinner); b.setHideSmartReplies(true); @@ -631,8 +642,11 @@ public class NotificationRemoteInputManager implements Dumpable { if (TextUtils.isEmpty(remoteInputText)) { remoteInputText = entry.remoteInputTextWhenReset; } + String remoteInputMimeType = entry.remoteInputMimeType; + Uri remoteInputUri = entry.remoteInputUri; StatusBarNotification newSbn = rebuildNotificationWithRemoteInput(entry, - remoteInputText, false /* showSpinner */); + remoteInputText, false /* showSpinner */, remoteInputMimeType, + remoteInputUri); entry.onRemoteInputInserted(); if (newSbn == null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index 2fcfb8c811aa..1f77ec229041 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -37,8 +37,10 @@ import android.app.Notification.MessagingStyle.Message; import android.app.NotificationChannel; import android.app.NotificationManager.Policy; import android.app.Person; +import android.app.RemoteInputHistoryItem; import android.content.Context; import android.graphics.drawable.Icon; +import android.net.Uri; import android.os.Bundle; import android.os.SystemClock; import android.service.notification.NotificationListenerService.Ranking; @@ -120,6 +122,8 @@ public final class NotificationEntry extends ListEntry { public int targetSdk; private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET; public CharSequence remoteInputText; + public String remoteInputMimeType; + public Uri remoteInputUri; private Notification.BubbleMetadata mBubbleMetadata; /** @@ -595,8 +599,8 @@ public final class NotificationEntry extends ListEntry { return false; } Bundle extras = mSbn.getNotification().extras; - CharSequence[] replyTexts = extras.getCharSequenceArray( - Notification.EXTRA_REMOTE_INPUT_HISTORY); + RemoteInputHistoryItem[] replyTexts = (RemoteInputHistoryItem[]) extras.getParcelableArray( + Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); if (!ArrayUtils.isEmpty(replyTexts)) { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt index f7fe064724a9..1eeeab3e93cb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.notification.collection -import android.app.NotificationChannel import android.app.NotificationManager.IMPORTANCE_HIGH import android.app.NotificationManager.IMPORTANCE_MIN import android.service.notification.NotificationListenerService.Ranking @@ -192,9 +191,7 @@ open class NotificationRankingManager @Inject constructor( } private fun NotificationEntry.isPeopleNotification() = - sbn.isPeopleNotification(channel) - private fun StatusBarNotification.isPeopleNotification(channel: NotificationChannel) = - peopleNotificationIdentifier.isPeopleNotification(this, channel) + peopleNotificationIdentifier.isPeopleNotification(sbn, ranking) private fun NotificationEntry.isHighPriority() = highPriorityProvider.isHighPriority(this) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java index ccd7fa3c6837..b49611688bc7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java @@ -100,7 +100,7 @@ public class HighPriorityProvider { private boolean isPeopleNotification(NotificationEntry entry) { return mPeopleNotificationIdentifier.isPeopleNotification( - entry.getSbn(), entry.getChannel()); + entry.getSbn(), entry.getRanking()); } private boolean hasUserSetImportance(NotificationEntry entry) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt index 5c90211ca6ad..4672de046c49 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt @@ -16,31 +16,20 @@ package com.android.systemui.statusbar.notification.people -import android.app.Notification -import android.content.Context -import android.app.NotificationChannel +import android.service.notification.NotificationListenerService.Ranking import android.service.notification.StatusBarNotification -import android.util.FeatureFlagUtils import javax.inject.Inject import javax.inject.Singleton interface PeopleNotificationIdentifier { - fun isPeopleNotification(sbn: StatusBarNotification, channel: NotificationChannel): Boolean + fun isPeopleNotification(sbn: StatusBarNotification, ranking: Ranking): Boolean } @Singleton class PeopleNotificationIdentifierImpl @Inject constructor( - private val personExtractor: NotificationPersonExtractor, - private val context: Context + private val personExtractor: NotificationPersonExtractor ) : PeopleNotificationIdentifier { - override fun isPeopleNotification(sbn: StatusBarNotification, channel: NotificationChannel) = - ((sbn.notification.notificationStyle == Notification.MessagingStyle::class.java && - (sbn.notification.shortcutId != null || - FeatureFlagUtils.isEnabled( - context, - FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ - ))) || - personExtractor.isPersonNotification(sbn)) && - !channel.isDemoted + override fun isPeopleNotification(sbn: StatusBarNotification, ranking: Ranking) = + ranking.isConversation || personExtractor.isPersonNotification(sbn) }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt index 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/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java index a3e13053d169..fa4bc2aba21a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java @@ -80,7 +80,15 @@ public class NotificationInlineImageResolver implements ImageResolver { public Drawable loadImage(Uri uri) { Drawable result = null; try { - result = hasCache() ? mImageCache.get(uri) : resolveImage(uri); + if (hasCache()) { + // if the uri isn't currently cached, try caching it first + if (!mImageCache.hasEntry(uri)) { + mImageCache.preload((uri)); + } + result = mImageCache.get(uri); + } else { + result = resolveImage(uri); + } } catch (IOException | SecurityException ex) { Log.d(TAG, "loadImage: Can't load image from " + uri, ex); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java index 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 00e38f814dff..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,10 +613,9 @@ 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(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, - false)) { + if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) { break; } updateSimState(intent); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java index 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 cca100fd48bd..3a1feaa2de14 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -27,6 +27,7 @@ import android.os.Looper; import android.os.Message; import android.provider.Settings.Global; import android.telephony.Annotation; +import android.telephony.CdmaEriInformation; import android.telephony.CellSignalStrength; import android.telephony.CellSignalStrengthCdma; import android.telephony.NetworkRegistrationInfo; @@ -40,8 +41,6 @@ import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.telephony.TelephonyIntents; -import com.android.internal.telephony.cdma.EriInfo; import com.android.settingslib.Utils; import com.android.settingslib.graph.SignalDrawable; import com.android.settingslib.net.SignalStrengthUtil; @@ -415,10 +414,10 @@ public class MobileSignalController extends SignalController< return false; } if (isCdma() && mServiceState != null) { - final int iconMode = mServiceState.getCdmaEriIconMode(); - return mServiceState.getCdmaEriIconIndex() != EriInfo.ROAMING_INDICATOR_OFF - && (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL - || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH); + final int iconMode = mPhone.getCdmaEriInformation().getEriIconMode(); + return mPhone.getCdmaEriInformation().getEriIconIndex() != CdmaEriInformation.ERI_OFF + && (iconMode == CdmaEriInformation.ERI_ICON_MODE_NORMAL + || iconMode == CdmaEriInformation.ERI_ICON_MODE_FLASH); } else { return mServiceState != null && mServiceState.getRoaming(); } @@ -430,12 +429,12 @@ public 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 6b3c5dc228bc..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,9 +524,9 @@ 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(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, false)) { + if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) { break; } // Might have different subscriptions now. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index 307e3bcbaba5..408b3a619ff1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -161,6 +161,11 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND); RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent, results); + + mEntry.remoteInputText = mEditText.getText(); + mEntry.remoteInputUri = null; + mEntry.remoteInputMimeType = null; + if (mEntry.editedSuggestionInfo == null) { RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_FREE_FORM_INPUT); } else { @@ -177,6 +182,10 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND); RemoteInput.addDataResultToIntent(mRemoteInput, fillInIntent, results); + mEntry.remoteInputText = mContext.getString(R.string.remote_input_image_insertion_text); + mEntry.remoteInputMimeType = contentType; + mEntry.remoteInputUri = data; + return fillInIntent; } @@ -184,7 +193,6 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mEditText.setEnabled(false); mSendButton.setVisibility(INVISIBLE); mProgressBar.setVisibility(VISIBLE); - mEntry.remoteInputText = mEditText.getText(); mEntry.lastRemoteInputSent = SystemClock.elapsedRealtime(); mController.addSpinning(mEntry.getKey(), mToken); mController.removeRemoteInput(mEntry, mToken); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java new file mode 100644 index 000000000000..1954b3936376 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java @@ -0,0 +1,207 @@ +/* + * 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.keyguard; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.AdditionalAnswers.answerVoid; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.admin.IKeyguardCallback; +import android.app.admin.IKeyguardClient; +import android.content.ComponentName; +import android.content.Intent; +import android.os.Handler; +import android.os.RemoteException; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.testing.TestableLooper.RunWithLooper; +import android.testing.ViewUtils; +import android.view.SurfaceControl; +import android.view.SurfaceView; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; + +@RunWithLooper +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { + + private static final int TARGET_USER_ID = KeyguardUpdateMonitor.getCurrentUser(); + + private AdminSecondaryLockScreenController mTestController; + private ComponentName mComponentName; + private Intent mServiceIntent; + private TestableLooper mTestableLooper; + private ViewGroup mParent; + + @Mock + private Handler mHandler; + @Mock + private IKeyguardClient.Stub mKeyguardClient; + @Mock + private KeyguardSecurityCallback mKeyguardCallback; + @Mock + private KeyguardUpdateMonitor mUpdateMonitor; + @Spy + private StubTransaction mTransaction; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mParent = spy(new FrameLayout(mContext)); + ViewUtils.attachView(mParent); + + mTestableLooper = TestableLooper.get(this); + mComponentName = new ComponentName(mContext, "FakeKeyguardClient.class"); + mServiceIntent = new Intent().setComponent(mComponentName); + + mContext.addMockService(mComponentName, mKeyguardClient); + // Have Stub.asInterface return the mocked interface. + when(mKeyguardClient.queryLocalInterface(anyString())).thenReturn(mKeyguardClient); + when(mKeyguardClient.asBinder()).thenReturn(mKeyguardClient); + + mTestController = new AdminSecondaryLockScreenController( + mContext, mParent, mUpdateMonitor, mKeyguardCallback, mHandler, mTransaction); + } + + @Test + public void testShow() throws Exception { + doAnswer(invocation -> { + IKeyguardCallback callback = (IKeyguardCallback) invocation.getArguments()[1]; + callback.onSurfaceControlCreated(new SurfaceControl()); + return null; + }).when(mKeyguardClient).onSurfaceReady(any(), any(IKeyguardCallback.class)); + + mTestController.show(mServiceIntent); + + verifySurfaceReady(); + verify(mTransaction).reparent(any(), any()); + assertThat(mContext.isBound(mComponentName)).isTrue(); + } + + @Test + public void testShow_dismissedByCallback() throws Exception { + doAnswer(invocation -> { + IKeyguardCallback callback = (IKeyguardCallback) invocation.getArguments()[1]; + callback.onDismiss(); + return null; + }).when(mKeyguardClient).onSurfaceReady(any(), any(IKeyguardCallback.class)); + + mTestController.show(mServiceIntent); + + verifyViewDismissed(verifySurfaceReady()); + } + + @Test + public void testHide() throws Exception { + // Show the view first, then hide. + doAnswer(invocation -> { + IKeyguardCallback callback = (IKeyguardCallback) invocation.getArguments()[1]; + callback.onSurfaceControlCreated(new SurfaceControl()); + return null; + }).when(mKeyguardClient).onSurfaceReady(any(), any(IKeyguardCallback.class)); + + mTestController.show(mServiceIntent); + SurfaceView v = verifySurfaceReady(); + + mTestController.hide(); + verify(mParent).removeView(v); + assertThat(mContext.isBound(mComponentName)).isFalse(); + } + + @Test + public void testHide_notShown() throws Exception { + mTestController.hide(); + // Nothing should happen if trying to hide when the view isn't attached yet. + verify(mParent, never()).removeView(any(SurfaceView.class)); + } + + @Test + public void testDismissed_onSurfaceReady_RemoteException() throws Exception { + doThrow(new RemoteException()).when(mKeyguardClient) + .onSurfaceReady(any(), any(IKeyguardCallback.class)); + + mTestController.show(mServiceIntent); + + verifyViewDismissed(verifySurfaceReady()); + } + + @Test + public void testDismissed_onSurfaceReady_timeout() throws Exception { + // Mocked KeyguardClient never handles the onSurfaceReady, so the operation times out, + // resulting in the view being dismissed. + doAnswer(answerVoid(Runnable::run)).when(mHandler) + .postDelayed(any(Runnable.class), anyLong()); + + mTestController.show(mServiceIntent); + + verifyViewDismissed(verifySurfaceReady()); + } + + private SurfaceView verifySurfaceReady() throws Exception { + mTestableLooper.processAllMessages(); + ArgumentCaptor<SurfaceView> captor = ArgumentCaptor.forClass(SurfaceView.class); + verify(mParent).addView(captor.capture()); + + mTestableLooper.processAllMessages(); + verify(mKeyguardClient).onSurfaceReady(any(), any(IKeyguardCallback.class)); + return captor.getValue(); + } + + private void verifyViewDismissed(SurfaceView v) throws Exception { + verify(mParent).removeView(v); + verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID); + assertThat(mContext.isBound(mComponentName)).isFalse(); + } + + /** + * Stubbed {@link SurfaceControl.Transaction} class that can be used when unit testing to + * avoid calls to native code. + */ + private class StubTransaction extends SurfaceControl.Transaction { + @Override + public void apply() { + } + + @Override + public SurfaceControl.Transaction reparent(SurfaceControl sc, SurfaceControl newParent) { + return this; + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 12da00678ac2..59eb6c59a2cd 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -34,12 +34,16 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.Activity; import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricSourceType; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; @@ -166,7 +170,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { Assert.assertTrue("onSimStateChanged not called", mKeyguardUpdateMonitor.hasSimStateJustChanged()); - intent.putExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, true); + intent.putExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, true); mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), intent); mTestableLooper.processAllMessages(); Assert.assertFalse("onSimStateChanged should have been skipped", @@ -519,6 +523,52 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { assertThat(mKeyguardUpdateMonitor.isTrustUsuallyManaged(user)).isFalse(); } + @Test + public void testSecondaryLockscreenRequirement() { + int user = KeyguardUpdateMonitor.getCurrentUser(); + String packageName = "fake.test.package"; + String cls = "FakeService"; + ServiceInfo serviceInfo = new ServiceInfo(); + serviceInfo.packageName = packageName; + serviceInfo.name = cls; + ResolveInfo resolveInfo = new ResolveInfo(); + resolveInfo.serviceInfo = serviceInfo; + when(mPackageManager.resolveService(any(Intent.class), eq(0))).thenReturn(resolveInfo); + when(mDevicePolicyManager.isSecondaryLockscreenEnabled(eq(user))).thenReturn(true, false); + + // Initially null. + assertThat(mKeyguardUpdateMonitor.getSecondaryLockscreenRequirement(user)).isNull(); + + // Set non-null after DPM change. + setBroadcastReceiverPendingResult(mKeyguardUpdateMonitor.mBroadcastAllReceiver); + Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); + mKeyguardUpdateMonitor.mBroadcastAllReceiver.onReceive(getContext(), intent); + mTestableLooper.processAllMessages(); + + Intent storedIntent = mKeyguardUpdateMonitor.getSecondaryLockscreenRequirement(user); + assertThat(storedIntent.getComponent().getClassName()).isEqualTo(cls); + assertThat(storedIntent.getComponent().getPackageName()).isEqualTo(packageName); + + // Back to null after another DPM change. + mKeyguardUpdateMonitor.mBroadcastAllReceiver.onReceive(getContext(), intent); + mTestableLooper.processAllMessages(); + assertThat(mKeyguardUpdateMonitor.getSecondaryLockscreenRequirement(user)).isNull(); + } + + private void setBroadcastReceiverPendingResult(BroadcastReceiver receiver) { + BroadcastReceiver.PendingResult pendingResult = + new BroadcastReceiver.PendingResult(Activity.RESULT_OK, + "resultData", + /* resultExtras= */ null, + BroadcastReceiver.PendingResult.TYPE_UNREGISTERED, + /* ordered= */ true, + /* sticky= */ false, + /* token= */ null, + UserHandle.myUserId(), + /* flags= */ 0); + receiver.setPendingResult(pendingResult); + } + private Intent putPhoneInfo(Intent intent, Bundle data, Boolean simInited) { int subscription = simInited ? 1/* mock subid=1 */ : SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE; diff --git a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java index 2ecc8ea8400c..b3071f957fdb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java +++ b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java @@ -19,8 +19,11 @@ import static org.mockito.Mockito.mock; import android.content.Context; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.Log; public class TestableDependency extends Dependency { + private static final String TAG = "TestableDependency"; + private final ArrayMap<Object, Object> mObjs = new ArrayMap<>(); private final ArraySet<Object> mInstantiatedObjects = new ArraySet<>(); @@ -44,7 +47,7 @@ public class TestableDependency extends Dependency { public <T> void injectTestDependency(Class<T> key, T obj) { if (mInstantiatedObjects.contains(key)) { - throw new IllegalStateException(key + " was already initialized"); + Log.d(TAG, key + " was already initialized but overriding with testDependency."); } mObjs.put(key, obj); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java index 25bcb54a9e50..f2642594802d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java @@ -211,8 +211,7 @@ public class AuthContainerViewTest extends SysuiTestCase { } @Override - public AuthPanelController getPanelController(Context context, View view, - boolean isManagedProfile) { + public AuthPanelController getPanelController(Context context, View view) { return mock(AuthPanelController.class); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java index 7daf92213c45..b09603d78ecb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java @@ -228,7 +228,7 @@ public class BubbleDataTest extends SysuiTestCase { } @Test - public void sameUpdate_NotInShade_showFlyout() { + public void sameUpdate_NotInShade_NotVisuallyInterruptive_dontShowFlyout() { // Setup mBubbleData.setListener(mListener); @@ -247,7 +247,7 @@ public class BubbleDataTest extends SysuiTestCase { // Verify BubbleData.Update update = mUpdateCaptor.getValue(); - assertThat(update.updatedBubble.showFlyout()).isTrue(); + assertThat(update.updatedBubble.showFlyout()).isFalse(); } // COLLAPSED / ADD diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java index 8a9a7a28efb7..4c681023175b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -77,12 +78,14 @@ public class TileQueryHelperTest extends SysuiTestCase { private static final Set<String> FACTORY_TILES = new ArraySet<>(); private static final String TEST_PKG = "test_pkg"; private static final String TEST_CLS = "test_cls"; + private static final String CUSTOM_TILE = "custom(" + TEST_PKG + "/" + TEST_CLS + ")"; static { FACTORY_TILES.addAll(Arrays.asList( new String[]{"wifi", "bt", "cell", "dnd", "inversion", "airplane", "work", "rotation", "flashlight", "location", "cast", "hotspot", "user", "battery", "saver", "night", "nfc"})); + FACTORY_TILES.add(CUSTOM_TILE); } @Mock @@ -223,6 +226,15 @@ public class TileQueryHelperTest extends SysuiTestCase { } @Test + public void testCustomTileNotCreated() { + Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES, + CUSTOM_TILE); + mTileQueryHelper.queryTiles(mQSTileHost); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mQSTileHost, never()).createTile(CUSTOM_TILE); + } + + @Test public void testThirdPartyTilesInactive() { ResolveInfo resolveInfo = new ResolveInfo(); ServiceInfo serviceInfo = mock(ServiceInfo.class, Answers.RETURNS_MOCKS); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt new file mode 100644 index 000000000000..c180a889a7ec --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar + +import android.content.res.Resources +import android.view.SurfaceControl +import android.view.ViewRootImpl +import androidx.test.filters.SmallTest +import com.android.systemui.DumpController +import com.android.systemui.SysuiTestCase +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito.eq +import org.mockito.Mockito.mock +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` +import org.mockito.MockitoAnnotations + +@SmallTest +class BlurUtilsTest : SysuiTestCase() { + + @Mock lateinit var resources: Resources + @Mock lateinit var dumpController: DumpController + @Mock lateinit var transaction: SurfaceControl.Transaction + lateinit var blurUtils: BlurUtils + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + blurUtils = TestableBlurUtils() + } + + @Test + fun testApplyBlur_noViewRoot_doesntCrash() { + blurUtils.applyBlur(null /* viewRootImple */, 10 /* radius */) + } + + @Test + fun testApplyBlur_invalidSurfaceControl() { + val surfaceControl = mock(SurfaceControl::class.java) + val viewRootImpl = mock(ViewRootImpl::class.java) + `when`(viewRootImpl.surfaceControl).thenReturn(surfaceControl) + blurUtils.applyBlur(viewRootImpl, 10 /* radius */) + } + + @Test + fun testApplyBlur_success() { + val radius = 10 + val surfaceControl = mock(SurfaceControl::class.java) + val viewRootImpl = mock(ViewRootImpl::class.java) + `when`(viewRootImpl.surfaceControl).thenReturn(surfaceControl) + `when`(surfaceControl.isValid).thenReturn(true) + blurUtils.applyBlur(viewRootImpl, radius) + verify(transaction).setBackgroundBlurRadius(eq(surfaceControl), eq(radius)) + verify(transaction).apply() + } + + inner class TestableBlurUtils() : BlurUtils(resources, dumpController) { + override fun supportsBlursOnWindows(): Boolean { + return true + } + + override fun createTransaction(): SurfaceControl.Transaction { + return transaction + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java index 64b10c816da7..1117646c482b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java @@ -10,7 +10,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Notification; +import android.app.RemoteInputHistoryItem; import android.content.Context; +import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.os.SystemClock; @@ -150,13 +152,30 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { } @Test + public void testRebuildWithRemoteInput_noExistingInput_image() { + Uri uri = mock(Uri.class); + String mimeType = "image/jpeg"; + String text = "image inserted"; + StatusBarNotification newSbn = + mRemoteInputManager.rebuildNotificationWithRemoteInput( + mEntry, text, false, mimeType, uri); + RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() + .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); + assertEquals(1, messages.length); + assertEquals(text, messages[0].getText()); + assertEquals(mimeType, messages[0].getMimeType()); + assertEquals(uri, messages[0].getUri()); + } + + @Test public void testRebuildWithRemoteInput_noExistingInputNoSpinner() { StatusBarNotification newSbn = - mRemoteInputManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", false); - CharSequence[] messages = newSbn.getNotification().extras - .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY); + mRemoteInputManager.rebuildNotificationWithRemoteInput( + mEntry, "A Reply", false, null, null); + RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() + .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); assertEquals(1, messages.length); - assertEquals("A Reply", messages[0]); + assertEquals("A Reply", messages[0].getText()); assertFalse(newSbn.getNotification().extras .getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false)); assertTrue(newSbn.getNotification().extras @@ -166,11 +185,12 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { @Test public void testRebuildWithRemoteInput_noExistingInputWithSpinner() { StatusBarNotification newSbn = - mRemoteInputManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", true); - CharSequence[] messages = newSbn.getNotification().extras - .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY); + mRemoteInputManager.rebuildNotificationWithRemoteInput( + mEntry, "A Reply", true, null, null); + RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() + .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); assertEquals(1, messages.length); - assertEquals("A Reply", messages[0]); + assertEquals("A Reply", messages[0].getText()); assertTrue(newSbn.getNotification().extras .getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false)); assertTrue(newSbn.getNotification().extras @@ -181,18 +201,45 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { public void testRebuildWithRemoteInput_withExistingInput() { // Setup a notification entry with 1 remote input. StatusBarNotification newSbn = - mRemoteInputManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", false); + mRemoteInputManager.rebuildNotificationWithRemoteInput( + mEntry, "A Reply", false, null, null); + NotificationEntry entry = new NotificationEntryBuilder() + .setSbn(newSbn) + .build(); + + // Try rebuilding to add another reply. + newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInput( + entry, "Reply 2", true, null, null); + RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() + .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); + assertEquals(2, messages.length); + assertEquals("Reply 2", messages[0].getText()); + assertEquals("A Reply", messages[1].getText()); + } + + @Test + public void testRebuildWithRemoteInput_withExistingInput_image() { + // Setup a notification entry with 1 remote input. + Uri uri = mock(Uri.class); + String mimeType = "image/jpeg"; + String text = "image inserted"; + StatusBarNotification newSbn = + mRemoteInputManager.rebuildNotificationWithRemoteInput( + mEntry, text, false, mimeType, uri); NotificationEntry entry = new NotificationEntryBuilder() .setSbn(newSbn) .build(); // Try rebuilding to add another reply. - newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInput(entry, "Reply 2", true); - CharSequence[] messages = newSbn.getNotification().extras - .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY); + newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInput( + entry, "Reply 2", true, null, null); + RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() + .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); assertEquals(2, messages.length); - assertEquals("Reply 2", messages[0]); - assertEquals("A Reply", messages[1]); + assertEquals("Reply 2", messages[0].getText()); + assertEquals(text, messages[1].getText()); + assertEquals(mimeType, messages[1].getMimeType()); + assertEquals(uri, messages[1].getUri()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java index 5310dd8a61f9..16f105d5a08c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java @@ -52,6 +52,7 @@ public class RankingBuilder { private ArrayList<CharSequence> mSmartReplies = new ArrayList<>(); private boolean mCanBubble = false; private boolean mIsVisuallyInterruptive = false; + private boolean mIsConversation = false; public RankingBuilder() { } @@ -77,6 +78,7 @@ public class RankingBuilder { mSmartReplies = copyList(ranking.getSmartReplies()); mCanBubble = ranking.canBubble(); mIsVisuallyInterruptive = ranking.visuallyInterruptive(); + mIsConversation = ranking.isConversation(); } public Ranking build() { @@ -101,7 +103,8 @@ public class RankingBuilder { mSmartActions, mSmartReplies, mCanBubble, - mIsVisuallyInterruptive); + mIsVisuallyInterruptive, + mIsConversation); return ranking; } @@ -181,6 +184,11 @@ public class RankingBuilder { return this; } + public RankingBuilder setIsConversation(boolean isConversation) { + mIsConversation = isConversation; + return this; + } + public RankingBuilder setImportance(@Importance int importance) { mImportance = importance; return this; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index 4b2ce0157b23..296d0cef715c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -45,7 +45,6 @@ import android.app.PendingIntent; import android.content.Intent; import android.graphics.drawable.Icon; import android.os.Handler; -import android.os.Looper; import android.os.UserHandle; import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.NotificationListenerService.RankingMap; @@ -88,7 +87,6 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotifRemoteViewCache; import com.android.systemui.statusbar.notification.row.NotificationContentInflater; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; -import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder; import com.android.systemui.statusbar.notification.row.RowInflaterTask; import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; @@ -99,6 +97,7 @@ import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.Assert; import com.android.systemui.util.leak.LeakDetector; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -162,7 +161,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase { 0, IMPORTANCE_DEFAULT, null, null, - null, null, null, true, sentiment, false, -1, false, null, null, false, false); + null, null, null, true, sentiment, false, -1, false, null, null, false, false, + false); return true; }).when(mRankingMap).getRanking(eq(key), any(Ranking.class)); } @@ -181,7 +181,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { null, null, null, null, null, true, Ranking.USER_SENTIMENT_NEUTRAL, false, -1, - false, smartActions, null, false, false); + false, smartActions, null, false, false, false); return true; }).when(mRankingMap).getRanking(eq(key), any(Ranking.class)); } @@ -189,15 +189,14 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); - if (!mDependency.hasInstantiatedDependency(SmartReplyController.class)) { - mDependency.injectMockDependency(SmartReplyController.class); - } + mDependency.injectMockDependency(SmartReplyController.class); mDependency.injectMockDependency(NotificationMediaManager.class); mCountDownLatch = new CountDownLatch(1); + Assert.sMainLooper = TestableLooper.get(this).getLooper(); mDependency.injectTestDependency(Dependency.MAIN_HANDLER, - Handler.createAsync(Looper.myLooper())); + Handler.createAsync(TestableLooper.get(this).getLooper())); when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController); when(mListContainer.getViewParentForNotification(any())).thenReturn( new FrameLayout(mContext)); @@ -207,9 +206,10 @@ public class NotificationEntryManagerTest extends SysuiTestCase { mEntry.expandedIcon = mock(StatusBarIconView.class); - NotificationRowContentBinder contentBinder = new NotificationContentInflater( + NotificationContentInflater contentBinder = new NotificationContentInflater( mock(NotifRemoteViewCache.class), mRemoteInputManager); + contentBinder.setInflateSynchronously(true); when(mNotificationRowComponentBuilder.activatableNotificationView(any())) .thenReturn(mNotificationRowComponentBuilder); @@ -262,6 +262,12 @@ public class NotificationEntryManagerTest extends SysuiTestCase { mEntry.getKey(), Ranking.USER_SENTIMENT_NEUTRAL); } + @After + public void tearDown() { + // CLEAN UP inflation tasks so they don't callback in a future test + mEntry.abortTask(); + } + @Test public void testAddNotification() throws Exception { TestableLooper.get(this).processAllMessages(); @@ -347,9 +353,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Test public void testRemoveNotification() { - // Row inflation happens off thread, so pretend that this test looper is main - Assert.sMainLooper = TestableLooper.get(this).getLooper(); - mEntry.setRow(mRow); mEntryManager.addActiveNotificationForTest(mEntry); @@ -467,9 +470,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Test public void testLifetimeExtenders_whenRetentionEndsNotificationIsRemoved() { - // Row inflation happens off thread, so pretend that this test looper is main - Assert.sMainLooper = TestableLooper.get(this).getLooper(); - // GIVEN an entry manager with a notification whose life has been extended mEntryManager.addActiveNotificationForTest(mEntry); final FakeNotificationLifetimeExtender extender = new FakeNotificationLifetimeExtender(); @@ -560,9 +560,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Test public void testRemoveInterceptor_notInterceptedGetsRemoved() { - // Row inflation happens off thread, so pretend that this test looper is main - Assert.sMainLooper = TestableLooper.get(this).getLooper(); - // GIVEN an entry manager with a notification mEntryManager.addActiveNotificationForTest(mEntry); @@ -625,7 +622,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Test public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications() { - Assert.sMainLooper = TestableLooper.get(this).getLooper(); Notification.Builder n = new Notification.Builder(mContext, "di") .setSmallIcon(R.drawable.ic_person) .setContentTitle("Title") diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java index 7c3665bfe6fb..e4865b633083 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java @@ -60,7 +60,7 @@ public class HighPriorityProviderTest extends SysuiTestCase { final NotificationEntry entry = new NotificationEntryBuilder() .setImportance(IMPORTANCE_HIGH) .build(); - when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel())) + when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getRanking())) .thenReturn(false); // THEN it has high priority @@ -76,7 +76,7 @@ public class HighPriorityProviderTest extends SysuiTestCase { .setNotification(notification) .setImportance(IMPORTANCE_LOW) .build(); - when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel())) + when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getRanking())) .thenReturn(true); // THEN it has high priority @@ -92,7 +92,7 @@ public class HighPriorityProviderTest extends SysuiTestCase { final NotificationEntry entry = new NotificationEntryBuilder() .setNotification(notification) .build(); - when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel())) + when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getRanking())) .thenReturn(false); // THEN it has high priority @@ -109,7 +109,7 @@ public class HighPriorityProviderTest extends SysuiTestCase { .setNotification(notification) .setImportance(IMPORTANCE_LOW) .build(); - when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel())) + when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getRanking())) .thenReturn(false); // THEN it has high priority @@ -126,7 +126,7 @@ public class HighPriorityProviderTest extends SysuiTestCase { .setNotification(notification) .setImportance(IMPORTANCE_MIN) .build(); - when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel())) + when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getRanking())) .thenReturn(false); // THEN it does NOT have high priority @@ -149,7 +149,7 @@ public class HighPriorityProviderTest extends SysuiTestCase { .setNotification(notification) .setChannel(channel) .build(); - when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel())) + when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getRanking())) .thenReturn(true); // THEN it does NOT have high priority diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index 13f3a5fbfff7..cc3c3ccdc316 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -42,6 +42,7 @@ import android.net.wifi.WifiManager; import android.os.Handler; import android.provider.Settings; import android.provider.Settings.Global; +import android.telephony.CdmaEriInformation; import android.telephony.NetworkRegistrationInfo; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; @@ -111,6 +112,8 @@ public class NetworkControllerBaseTest extends SysuiTestCase { private NetworkCapabilities mNetCapabilities; private ConnectivityManager.NetworkCallback mNetworkCallback; + private CdmaEriInformation mEriInformation; + @Rule public TestWatcher failWatcher = new TestWatcher() { @Override @@ -155,6 +158,10 @@ public class NetworkControllerBaseTest extends SysuiTestCase { mSignalStrength = mock(SignalStrength.class); mServiceState = mock(ServiceState.class); + mEriInformation = new CdmaEriInformation(CdmaEriInformation.ERI_OFF, + CdmaEriInformation.ERI_ICON_MODE_NORMAL); + when(mMockTm.getCdmaEriInformation()).thenReturn(mEriInformation); + mConfig = new Config(); mConfig.hspaDataDistinguishable = true; mCallbackHandler = mock(CallbackHandler.class); @@ -305,11 +312,9 @@ public class NetworkControllerBaseTest extends SysuiTestCase { } public void setCdmaRoaming(boolean isRoaming) { - when(mServiceState.getCdmaEriIconIndex()).thenReturn(isRoaming ? - EriInfo.ROAMING_INDICATOR_ON : EriInfo.ROAMING_INDICATOR_OFF); - when(mServiceState.getCdmaEriIconMode()).thenReturn(isRoaming ? - EriInfo.ROAMING_ICON_MODE_NORMAL : -1); - updateServiceState(); + mEriInformation.setEriIconIndex(isRoaming ? + CdmaEriInformation.ERI_ON : CdmaEriInformation.ERI_OFF); + when(mMockTm.getCdmaEriInformation()).thenReturn(mEriInformation); } public void setVoiceRegState(int voiceRegState) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java index 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/SystemUI/tests/src/com/android/systemui/util/animation/PhysicsAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/animation/PhysicsAnimatorTest.kt index a785d4ba2cb8..3f3f4d1681a0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/animation/PhysicsAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/animation/PhysicsAnimatorTest.kt @@ -23,6 +23,7 @@ import org.junit.Assert.assertFalse import org.junit.Assert.assertNotEquals import org.junit.Assert.assertTrue import org.junit.Before +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyFloat @@ -446,6 +447,7 @@ class PhysicsAnimatorTest : SysuiTestCase() { } @Test + @Ignore("Sporadically flaking.") fun testFlingThenSpring() { PhysicsAnimatorTestUtils.setAllAnimationsBlock(false) diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp index 264ce440f59f..a8d1239a3c30 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,9 +64,11 @@ 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/TetherStatesParcel.aidl", ], diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl index 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..e1b9c16b8185 100644 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java @@ -16,8 +16,12 @@ package android.net; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.annotation.TestApi; import android.content.Context; -import android.net.ConnectivityManager.OnTetheringEventCallback; +import android.os.Bundle; import android.os.ConditionVariable; import android.os.IBinder; import android.os.RemoteException; @@ -25,6 +29,11 @@ import android.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 +43,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 +54,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 +82,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 +128,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 +141,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 +212,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 +257,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 +280,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 +306,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) { @@ -340,7 +328,7 @@ public class TetheringManager { /** * Starts tethering and runs tether provisioning for the given type if needed. If provisioning * fails, stopTethering will be called automatically. - * + * @hide */ // TODO: improve the usage of ResultReceiver, b/145096122 public void startTethering(final int type, @NonNull final ResultReceiver receiver, @@ -375,11 +363,63 @@ 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. + * + * @param type the downstream type of tethering. Must be one of {@code #TETHERING_*} constants. + * @param showEntitlementUi a boolean indicating whether to run UI-based entitlement check. + * @param executor the executor on which callback will be invoked. + * @param listener an {@link OnTetheringEntitlementResultListener} which will be called to + * notify the caller of the result of entitlement check. The listener may be called zero + * or one time. + */ + @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) + public void requestLatestTetheringEntitlementResult(int type, boolean showEntitlementUi, + @NonNull Executor executor, + @NonNull final OnTetheringEntitlementResultListener listener) { + if (listener == null) { + throw new IllegalArgumentException( + "OnTetheringEntitlementResultListener cannot be null."); + } + + ResultReceiver wrappedListener = new ResultReceiver(null /* handler */) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + executor.execute(() -> { + listener.onTetheringEntitlementResult(resultCode); + }); + } + }; + + requestLatestTetheringEntitlementResult(type, wrappedListener, + showEntitlementUi); + } + + /** + * Helper function of #requestLatestTetheringEntitlementResult to remain backwards compatible + * with ConnectivityManager#getLatestTetheringEntitlementResult + * + * {@hide} */ // TODO: improve the usage of ResultReceiver, b/145096122 public void requestLatestTetheringEntitlementResult(final int type, @@ -396,9 +436,126 @@ 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. * @@ -406,15 +563,20 @@ public class TetheringManager { * @param callback the callback to be called when tethering has change events. */ public void registerTetheringEventCallback(@NonNull Executor executor, - @NonNull OnTetheringEventCallback callback) { + @NonNull TetheringEventCallback callback) { final String callerPkg = mContext.getOpPackageName(); Log.i(TAG, "registerTetheringEventCallback caller:" + callerPkg); synchronized (mTetheringEventCallbacks) { - if (!mTetheringEventCallbacks.containsKey(callback)) { + if (mTetheringEventCallbacks.containsKey(callback)) { throw new IllegalArgumentException("callback was already registered."); } final ITetheringEventCallback remoteCallback = new ITetheringEventCallback.Stub() { + // Only accessed with a lock on this object + private final HashMap<String, Integer> mErrorStates = new HashMap<>(); + private String[] mLastTetherableInterfaces = null; + private String[] mLastTetheredInterfaces = null; + @Override public void onUpstreamChanged(Network network) throws RemoteException { executor.execute(() -> { @@ -422,11 +584,45 @@ 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 +633,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 +669,7 @@ public class TetheringManager { * * @param callback previously registered callback. */ - public void unregisterTetheringEventCallback(@NonNull final OnTetheringEventCallback callback) { + public void unregisterTetheringEventCallback(@NonNull final TetheringEventCallback callback) { final String callerPkg = mContext.getOpPackageName(); Log.i(TAG, "unregisterTetheringEventCallback caller:" + callerPkg); @@ -482,6 +693,7 @@ 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 +715,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 +729,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 +743,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 +755,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 +768,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 +787,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 +800,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 +814,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(); 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..5370145f1992 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -74,6 +74,7 @@ import android.net.LinkProperties; import android.net.Network; import android.net.NetworkInfo; import android.net.TetherStatesParcel; +import android.net.TetheringCallbackStartedParcel; import android.net.TetheringConfigurationParcel; import android.net.ip.IpServer; import android.net.shared.NetdUtils; @@ -951,6 +952,7 @@ public class Tethering { mWrapper.showTetheredNotification( R.drawable.stat_sys_tether_general, false); mWrapper.untetherAll(); + // TODO(b/148139325): send tetheringSupported on restriction change } } } @@ -1844,9 +1846,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 +1887,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/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..9f0d8769b1f9 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -86,6 +86,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.RouteInfo; import android.net.TetherStatesParcel; +import android.net.TetheringCallbackStartedParcel; import android.net.TetheringConfigurationParcel; import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServingParamsParcel; @@ -1113,11 +1114,10 @@ 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 diff --git a/packages/services/PacProcessor/jni/Android.bp b/packages/services/PacProcessor/jni/Android.bp index 61f8143e68b5..ab21a76229ba 100644 --- a/packages/services/PacProcessor/jni/Android.bp +++ b/packages/services/PacProcessor/jni/Android.bp @@ -39,8 +39,5 @@ cc_library_shared { ], sanitize: { cfi: true, - diag: { - cfi: true, - }, }, } diff --git a/proto/Android.bp b/proto/Android.bp index 7cf6ce740969..01a72eaa6bf8 100644 --- a/proto/Android.bp +++ b/proto/Android.bp @@ -32,3 +32,8 @@ filegroup { name: "system-messages-proto-src", srcs: ["src/system_messages.proto"], } + +filegroup { + name: "ipconnectivity-proto-src", + srcs: ["src/ipconnectivity.proto"], +} 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/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/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java index 9cdb58d8c019..b54ec4ea9441 100644 --- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java +++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java @@ -27,6 +27,7 @@ import android.app.contentsuggestions.IContentSuggestionsManager; import android.app.contentsuggestions.ISelectionsCallback; import android.app.contentsuggestions.SelectionsRequest; import android.content.Context; +import android.graphics.Bitmap; import android.os.Binder; import android.os.Bundle; import android.os.RemoteException; @@ -61,6 +62,10 @@ public class ContentSuggestionsManagerService extends private static final boolean VERBOSE = false; // TODO: make dynamic private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes + /** + * Key into the extras Bundle passed to {@link #provideContextImage(int, Bundle)}. + */ + private static final String EXTRA_BITMAP = "android.contentsuggestions.extra.BITMAP"; private ActivityTaskManagerInternal mActivityTaskManagerInternal; @@ -111,6 +116,33 @@ public class ContentSuggestionsManagerService extends private class ContentSuggestionsManagerStub extends IContentSuggestionsManager.Stub { @Override + public void provideContextBitmap( + int userId, + @NonNull Bitmap bitmap, + @NonNull Bundle imageContextRequestExtras) { + if (bitmap == null) { + throw new IllegalArgumentException("Expected non-null bitmap"); + } + if (imageContextRequestExtras == null) { + throw new IllegalArgumentException("Expected non-null imageContextRequestExtras"); + } + enforceCaller(UserHandle.getCallingUserId(), "provideContextBitmap"); + + synchronized (mLock) { + final ContentSuggestionsPerUserService service = getServiceForUserLocked(userId); + if (service != null) { + // TODO(b/147324195): Temporarily pass bitmap until we change the service API. + imageContextRequestExtras.putParcelable(EXTRA_BITMAP, bitmap); + service.provideContextImageLocked(/* taskId = */ -1, imageContextRequestExtras); + } else { + if (VERBOSE) { + Slog.v(TAG, "provideContextImageLocked: no service for " + userId); + } + } + } + } + + @Override public void provideContextImage( int userId, int taskId, diff --git a/services/core/Android.bp b/services/core/Android.bp index 02d4f94662b5..a603fa975b74 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -83,6 +83,7 @@ java_library_static { ":storaged_aidl", ":vold_aidl", ":platform-compat-config", + ":display-device-config", "java/com/android/server/EventLogTags.logtags", "java/com/android/server/am/EventLogTags.logtags", "java/com/android/server/wm/EventLogTags.logtags", @@ -112,6 +113,7 @@ 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/BluetoothAirplaneModeListener.java b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java new file mode 100644 index 000000000000..31cd5d519d87 --- /dev/null +++ b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java @@ -0,0 +1,258 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.bluetooth.BluetoothA2dp; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothHearingAid; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothProfile.ServiceListener; +import android.content.Context; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.provider.Settings; +import android.util.Log; +import android.widget.Toast; + +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; + +/** + * The BluetoothAirplaneModeListener handles system airplane mode change callback and checks + * whether we need to inform BluetoothManagerService on this change. + * + * The information of airplane mode turns on would not be passed to the BluetoothManagerService + * when Bluetooth is on and Bluetooth is in one of the following situations: + * 1. Bluetooth A2DP is connected. + * 2. Bluetooth Hearing Aid profile is connected. + */ +class BluetoothAirplaneModeListener { + private static final String TAG = "BluetoothAirplaneModeListener"; + @VisibleForTesting static final String TOAST_COUNT = "bluetooth_airplane_toast_count"; + + private static final int MSG_AIRPLANE_MODE_CHANGED = 0; + + @VisibleForTesting static final int MAX_TOAST_COUNT = 10; // 10 times + + private final BluetoothManagerService mBluetoothManager; + private final BluetoothAirplaneModeHandler mHandler; + private AirplaneModeHelper mAirplaneHelper; + + @VisibleForTesting int mToastCount = 0; + + BluetoothAirplaneModeListener(BluetoothManagerService service, Looper looper, Context context) { + mBluetoothManager = service; + + mHandler = new BluetoothAirplaneModeHandler(looper); + context.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true, + mAirplaneModeObserver); + } + + private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) { + @Override + public void onChange(boolean unused) { + // Post from system main thread to android_io thread. + Message msg = mHandler.obtainMessage(MSG_AIRPLANE_MODE_CHANGED); + mHandler.sendMessage(msg); + } + }; + + private class BluetoothAirplaneModeHandler extends Handler { + BluetoothAirplaneModeHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_AIRPLANE_MODE_CHANGED: + handleAirplaneModeChange(); + break; + default: + Log.e(TAG, "Invalid message: " + msg.what); + break; + } + } + } + + /** + * Call after boot complete + */ + @VisibleForTesting + void start(AirplaneModeHelper helper) { + Log.i(TAG, "start"); + mAirplaneHelper = helper; + mToastCount = mAirplaneHelper.getSettingsInt(TOAST_COUNT); + } + + @VisibleForTesting + boolean shouldPopToast() { + if (mToastCount >= MAX_TOAST_COUNT) { + return false; + } + mToastCount++; + mAirplaneHelper.setSettingsInt(TOAST_COUNT, mToastCount); + return true; + } + + @VisibleForTesting + void handleAirplaneModeChange() { + if (shouldSkipAirplaneModeChange()) { + Log.i(TAG, "Ignore airplane mode change"); + // We have to store Bluetooth state here, so if user turns off Bluetooth + // after airplane mode is turned on, we don't forget to turn on Bluetooth + // when airplane mode turns off. + mAirplaneHelper.setSettingsInt(Settings.Global.BLUETOOTH_ON, + BluetoothManagerService.BLUETOOTH_ON_AIRPLANE); + if (shouldPopToast()) { + mAirplaneHelper.showToastMessage(); + } + return; + } + mAirplaneHelper.onAirplaneModeChanged(mBluetoothManager); + } + + @VisibleForTesting + boolean shouldSkipAirplaneModeChange() { + if (mAirplaneHelper == null) { + return false; + } + if (!mAirplaneHelper.isBluetoothOn() || !mAirplaneHelper.isAirplaneModeOn() + || !mAirplaneHelper.isA2dpOrHearingAidConnected()) { + return false; + } + return true; + } + + /** + * Helper class that handles callout and callback methods without + * complex logic. + */ + @VisibleForTesting + public static class AirplaneModeHelper { + private volatile BluetoothA2dp mA2dp; + private volatile BluetoothHearingAid mHearingAid; + private final BluetoothAdapter mAdapter; + private final Context mContext; + + AirplaneModeHelper(Context context) { + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mContext = context; + + mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP); + mAdapter.getProfileProxy(mContext, mProfileServiceListener, + BluetoothProfile.HEARING_AID); + } + + private final ServiceListener mProfileServiceListener = new ServiceListener() { + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + // Setup Bluetooth profile proxies + switch (profile) { + case BluetoothProfile.A2DP: + mA2dp = (BluetoothA2dp) proxy; + break; + case BluetoothProfile.HEARING_AID: + mHearingAid = (BluetoothHearingAid) proxy; + break; + default: + break; + } + } + + @Override + public void onServiceDisconnected(int profile) { + // Clear Bluetooth profile proxies + switch (profile) { + case BluetoothProfile.A2DP: + mA2dp = null; + break; + case BluetoothProfile.HEARING_AID: + mHearingAid = null; + break; + default: + break; + } + } + }; + + @VisibleForTesting + public boolean isA2dpOrHearingAidConnected() { + return isA2dpConnected() || isHearingAidConnected(); + } + + @VisibleForTesting + public boolean isBluetoothOn() { + final BluetoothAdapter adapter = mAdapter; + if (adapter == null) { + return false; + } + return adapter.getLeState() == BluetoothAdapter.STATE_ON; + } + + @VisibleForTesting + public boolean isAirplaneModeOn() { + return Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0) == 1; + } + + @VisibleForTesting + public void onAirplaneModeChanged(BluetoothManagerService managerService) { + managerService.onAirplaneModeChanged(); + } + + @VisibleForTesting + public int getSettingsInt(String name) { + return Settings.Global.getInt(mContext.getContentResolver(), + name, 0); + } + + @VisibleForTesting + public void setSettingsInt(String name, int value) { + Settings.Global.putInt(mContext.getContentResolver(), + name, value); + } + + @VisibleForTesting + public void showToastMessage() { + Resources r = mContext.getResources(); + final CharSequence text = r.getString( + R.string.bluetooth_airplane_mode_toast, 0); + Toast.makeText(mContext, text, Toast.LENGTH_LONG).show(); + } + + private boolean isA2dpConnected() { + final BluetoothA2dp a2dp = mA2dp; + if (a2dp == null) { + return false; + } + return a2dp.getConnectedDevices().size() > 0; + } + + private boolean isHearingAidConnected() { + final BluetoothHearingAid hearingAid = mHearingAid; + if (hearingAid == null) { + return false; + } + return hearingAid.getConnectedDevices().size() > 0; + } + }; +} diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 470300e6485c..3774b645339d 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -72,6 +72,7 @@ import android.util.Slog; import android.util.StatsLog; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.server.pm.UserRestrictionsUtils; @@ -143,7 +144,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Bluetooth persisted setting is on // but Airplane mode will affect Bluetooth state at start up // and Airplane mode will have higher priority. - private static final int BLUETOOTH_ON_AIRPLANE = 2; + @VisibleForTesting + static final int BLUETOOTH_ON_AIRPLANE = 2; private static final int SERVICE_IBLUETOOTH = 1; private static final int SERVICE_IBLUETOOTHGATT = 2; @@ -164,6 +166,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private boolean mBinding; private boolean mUnbinding; + private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener; + // used inside handler thread private boolean mQuietEnable = false; private boolean mEnable; @@ -262,68 +266,65 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; - private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) { - @Override - public void onChange(boolean unused) { - synchronized (this) { - if (isBluetoothPersistedStateOn()) { - if (isAirplaneModeOn()) { - persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE); - } else { - persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); - } + public void onAirplaneModeChanged() { + synchronized (this) { + if (isBluetoothPersistedStateOn()) { + if (isAirplaneModeOn()) { + persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE); + } else { + persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); } + } - int st = BluetoothAdapter.STATE_OFF; - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - st = mBluetooth.getState(); - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call getState", e); - return; - } finally { - mBluetoothLock.readLock().unlock(); + int st = BluetoothAdapter.STATE_OFF; + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) { + st = mBluetooth.getState(); } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to call getState", e); + return; + } finally { + mBluetoothLock.readLock().unlock(); + } - Slog.d(TAG, - "Airplane Mode change - current state: " + BluetoothAdapter.nameForState( - st) + ", isAirplaneModeOn()=" + isAirplaneModeOn()); + Slog.d(TAG, + "Airplane Mode change - current state: " + BluetoothAdapter.nameForState( + st) + ", isAirplaneModeOn()=" + isAirplaneModeOn()); - if (isAirplaneModeOn()) { - // Clear registered LE apps to force shut-off - clearBleApps(); + if (isAirplaneModeOn()) { + // Clear registered LE apps to force shut-off + clearBleApps(); - // If state is BLE_ON make sure we trigger disableBLE - if (st == BluetoothAdapter.STATE_BLE_ON) { - try { - mBluetoothLock.readLock().lock(); - if (mBluetooth != null) { - addActiveLog( - BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, - mContext.getPackageName(), false); - mBluetooth.onBrEdrDown(); - mEnable = false; - mEnableExternal = false; - } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to call onBrEdrDown", e); - } finally { - mBluetoothLock.readLock().unlock(); + // If state is BLE_ON make sure we trigger disableBLE + if (st == BluetoothAdapter.STATE_BLE_ON) { + try { + mBluetoothLock.readLock().lock(); + if (mBluetooth != null) { + addActiveLog( + BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, + mContext.getPackageName(), false); + mBluetooth.onBrEdrDown(); + mEnable = false; + mEnableExternal = false; } - } else if (st == BluetoothAdapter.STATE_ON) { - sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, - mContext.getPackageName()); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to call onBrEdrDown", e); + } finally { + mBluetoothLock.readLock().unlock(); } - } else if (mEnableExternal) { - sendEnableMsg(mQuietEnableExternal, - BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, + } else if (st == BluetoothAdapter.STATE_ON) { + sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, mContext.getPackageName()); } + } else if (mEnableExternal) { + sendEnableMsg(mQuietEnableExternal, + BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, + mContext.getPackageName()); } } - }; + } private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override @@ -435,9 +436,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Settings.Global.getString(mContentResolver, Settings.Global.AIRPLANE_MODE_RADIOS); if (airplaneModeRadios == null || airplaneModeRadios.contains( Settings.Global.RADIO_BLUETOOTH)) { - mContentResolver.registerContentObserver( - Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true, - mAirplaneModeObserver); + mBluetoothAirplaneModeListener = new BluetoothAirplaneModeListener( + this, IoThread.get().getLooper(), context); } int systemUiUid = -1; @@ -483,6 +483,17 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return state != BLUETOOTH_OFF; } + private boolean isBluetoothPersistedStateOnAirplane() { + if (!supportBluetoothPersistedState()) { + return false; + } + int state = Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, -1); + if (DBG) { + Slog.d(TAG, "Bluetooth persisted state: " + state); + } + return state == BLUETOOTH_ON_AIRPLANE; + } + /** * Returns true if the Bluetooth saved state is BLUETOOTH_ON_BLUETOOTH */ @@ -988,10 +999,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } synchronized (mReceiver) { - if (persist) { - persistBluetoothSetting(BLUETOOTH_OFF); + if (!isBluetoothPersistedStateOnAirplane()) { + if (persist) { + persistBluetoothSetting(BLUETOOTH_OFF); + } + mEnableExternal = false; } - mEnableExternal = false; sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName); } @@ -1219,6 +1232,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); mHandler.sendMessage(getMsg); } + if (mBluetoothAirplaneModeListener != null) { + mBluetoothAirplaneModeListener.start( + new BluetoothAirplaneModeListener.AirplaneModeHelper(mContext)); + } } /** diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 7ab345387fab..1c9f5dc9c2f1 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -1571,48 +1571,49 @@ public class ConnectivityService extends IConnectivityManager.Stub enforceAccessPermission(); final int uid = Binder.getCallingUid(); NetworkState state = getUnfilteredActiveNetworkState(uid); - return state.linkProperties; + if (state.linkProperties == null) return null; + return linkPropertiesRestrictedForCallerPermissions(state.linkProperties, + Binder.getCallingPid(), uid); } @Override public LinkProperties getLinkPropertiesForType(int networkType) { enforceAccessPermission(); NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType); - if (nai != null) { - synchronized (nai) { - return new LinkProperties(nai.linkProperties); - } - } - return null; + final LinkProperties lp = getLinkProperties(nai); + if (lp == null) return null; + return linkPropertiesRestrictedForCallerPermissions( + lp, Binder.getCallingPid(), Binder.getCallingUid()); } // TODO - this should be ALL networks @Override public LinkProperties getLinkProperties(Network network) { enforceAccessPermission(); - return getLinkProperties(getNetworkAgentInfoForNetwork(network)); + final LinkProperties lp = getLinkProperties(getNetworkAgentInfoForNetwork(network)); + if (lp == null) return null; + return linkPropertiesRestrictedForCallerPermissions( + lp, Binder.getCallingPid(), Binder.getCallingUid()); } - private LinkProperties getLinkProperties(NetworkAgentInfo nai) { + @Nullable + private LinkProperties getLinkProperties(@Nullable NetworkAgentInfo nai) { if (nai == null) { return null; } synchronized (nai) { - return new LinkProperties(nai.linkProperties); + return nai.linkProperties; } } private NetworkCapabilities getNetworkCapabilitiesInternal(NetworkAgentInfo nai) { - if (nai != null) { - synchronized (nai) { - if (nai.networkCapabilities != null) { - return networkCapabilitiesRestrictedForCallerPermissions( - nai.networkCapabilities, - Binder.getCallingPid(), Binder.getCallingUid()); - } - } + if (nai == null) return null; + synchronized (nai) { + if (nai.networkCapabilities == null) return null; + return networkCapabilitiesRestrictedForCallerPermissions( + nai.networkCapabilities, + Binder.getCallingPid(), Binder.getCallingUid()); } - return null; } @Override @@ -1634,6 +1635,29 @@ public class ConnectivityService extends IConnectivityManager.Stub return newNc; } + private LinkProperties linkPropertiesRestrictedForCallerPermissions( + LinkProperties lp, int callerPid, int callerUid) { + if (lp == null) return new LinkProperties(); + + // Only do a permission check if sanitization is needed, to avoid unnecessary binder calls. + final boolean needsSanitization = + (lp.getCaptivePortalApiUrl() != null || lp.getCaptivePortalData() != null); + if (!needsSanitization) { + return new LinkProperties(lp); + } + + if (checkSettingsPermission(callerPid, callerUid)) { + return lp.makeSensitiveFieldsParcelingCopy(); + } + + final LinkProperties newLp = new LinkProperties(lp); + // Sensitive fields would not be parceled anyway, but sanitize for consistency before the + // object gets parceled. + newLp.setCaptivePortalApiUrl(null); + newLp.setCaptivePortalData(null); + return newLp; + } + private void restrictRequestUidsForCaller(NetworkCapabilities nc) { if (!checkSettingsPermission()) { nc.setSingleUid(Binder.getCallingUid()); @@ -6145,7 +6169,8 @@ public class ConnectivityService extends IConnectivityManager.Stub case ConnectivityManager.CALLBACK_AVAILABLE: { putParcelable(bundle, networkCapabilitiesRestrictedForCallerPermissions( networkAgent.networkCapabilities, nri.mPid, nri.mUid)); - putParcelable(bundle, new LinkProperties(networkAgent.linkProperties)); + putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions( + networkAgent.linkProperties, nri.mPid, nri.mUid)); // For this notification, arg1 contains the blocked status. msg.arg1 = arg1; break; @@ -6162,7 +6187,8 @@ public class ConnectivityService extends IConnectivityManager.Stub break; } case ConnectivityManager.CALLBACK_IP_CHANGED: { - putParcelable(bundle, new LinkProperties(networkAgent.linkProperties)); + putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions( + networkAgent.linkProperties, nri.mPid, nri.mUid)); break; } case ConnectivityManager.CALLBACK_BLK_CHANGED: { diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index cbb914af7561..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; @@ -544,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); @@ -623,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); @@ -658,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); @@ -675,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(","); 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/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 32830aeacf06..4b4ce348385a 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -1701,8 +1701,15 @@ class StorageManagerService extends IStorageManager.Stub if (mIsFuseEnabled != settingsFuseFlag) { Slog.i(TAG, "Toggling persist.sys.fuse to " + settingsFuseFlag); SystemProperties.set(PROP_FUSE, Boolean.toString(settingsFuseFlag)); - // Perform hard reboot to kick policy into place - mContext.getSystemService(PowerManager.class).reboot("fuse_prop"); + + PowerManager powerManager = mContext.getSystemService(PowerManager.class); + if (powerManager.isRebootingUserspaceSupported()) { + // Perform userspace reboot to kick policy into place + powerManager.reboot(PowerManager.REBOOT_USERSPACE); + } else { + // Perform hard reboot to kick policy into place + powerManager.reboot("fuse_prop"); + } } } diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 1cae5f2fcc4d..68574f5fbc2c 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -289,7 +289,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { static final int PRECISE_PHONE_STATE_PERMISSION_MASK = PhoneStateListener.LISTEN_PRECISE_CALL_STATE - | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE; + | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE + | PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES + | PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED + | PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES; static final int READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK = PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL @@ -2518,8 +2521,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } if ((events & PRECISE_PHONE_STATE_PERMISSION_MASK) != 0) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.READ_PRECISE_PHONE_STATE, null); + // check if calling app has either permission READ_PRECISE_PHONE_STATE + // or with carrier privileges + try { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.READ_PRECISE_PHONE_STATE, null); + } catch (SecurityException se) { + TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mContext, subId, message); + } } if ((events & READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK) != 0) { @@ -2542,16 +2551,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null); } - if ((events & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.READ_PRECISE_PHONE_STATE, null); - } - - if ((events & PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) != 0) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.READ_PRECISE_PHONE_STATE, null); - } - if ((events & PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) != 0) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null); @@ -2562,11 +2561,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null); } - if ((events & PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES) != 0) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.READ_PRECISE_PHONE_STATE, null); - } - return true; } diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java index 7fd98e0043c9..c125b1baf860 100644 --- a/services/core/java/com/android/server/adb/AdbService.java +++ b/services/core/java/com/android/server/adb/AdbService.java @@ -17,6 +17,7 @@ package com.android.server.adb; import android.content.ContentResolver; import android.content.Context; +import android.content.pm.PackageManager; import android.database.ContentObserver; import android.debug.AdbManagerInternal; import android.debug.IAdbManager; @@ -260,6 +261,30 @@ public class AdbService extends IAdbManager.Stub { } } + /** + * @return true if the device supports secure ADB over Wi-Fi. + * @hide + */ + @Override + public boolean isAdbWifiSupported() { + mContext.enforceCallingPermission( + android.Manifest.permission.MANAGE_DEBUGGING, "AdbService"); + return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI); + } + + /** + * @return true if the device supports secure ADB over Wi-Fi and device pairing by + * QR code. + * @hide + */ + @Override + public boolean isAdbWifiQrSupported() { + mContext.enforceCallingPermission( + android.Manifest.permission.MANAGE_DEBUGGING, "AdbService"); + return isAdbWifiSupported() && mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_CAMERA_ANY); + } + private void setAdbEnabled(boolean enable) { if (DEBUG) Slog.d(TAG, "setAdbEnabled(" + enable + "), mAdbEnabled=" + mAdbEnabled); diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 3ffa5dea4d89..ac85bf57e9b0 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -31,6 +31,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE_E import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ActivityThread; @@ -2068,9 +2069,9 @@ public final class ActiveServices { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "retrieveServiceLocked: " + service + " type=" + resolvedType + " callingUid=" + callingUid); - userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false, - ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE, "service", - callingPackage); + userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, + /* allowAll= */false, getAllowMode(service, callingPackage), + /* name= */ "service", callingPackage); ServiceMap smap = getServiceMapLocked(userId); final ComponentName comp; @@ -2260,6 +2261,17 @@ public final class ActiveServices { return null; } + private int getAllowMode(Intent service, @Nullable String callingPackage) { + if (callingPackage == null || service.getComponent() == null) { + return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE; + } + if (callingPackage.equals(service.getComponent().getPackageName())) { + return ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE; + } else { + return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE; + } + } + private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, ">>> EXECUTING " + why + " of " + r + " in app " + r.app); 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/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 3f0e2ce9ed13..53a967b0ce50 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -2391,6 +2391,8 @@ final class ActivityManagerShellCommand extends ShellCommand { return UsageStatsManager.STANDBY_BUCKET_FREQUENT; } else if (lower.startsWith("ra")) { return UsageStatsManager.STANDBY_BUCKET_RARE; + } else if (lower.startsWith("re")) { + return UsageStatsManager.STANDBY_BUCKET_RESTRICTED; } else if (lower.startsWith("ne")) { return UsageStatsManager.STANDBY_BUCKET_NEVER; } else { diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java index 6e135d65f127..bab133f58c11 100644 --- a/services/core/java/com/android/server/am/AppExitInfoTracker.java +++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java @@ -189,12 +189,14 @@ public final class AppExitInfoTracker { } void onSystemReady() { - // Read the sysprop set by lmkd and set this to persist so app could read it. - SystemProperties.set("persist.sys.lmk.reportkills", - Boolean.toString(SystemProperties.getBoolean("sys.lmk.reportkills", false))); registerForUserRemoval(); registerForPackageRemoval(); - IoThread.getHandler().post(this::loadExistingProcessExitInfo); + IoThread.getHandler().post(() -> { + // Read the sysprop set by lmkd and set this to persist so app could read it. + SystemProperties.set("persist.sys.lmk.reportkills", + Boolean.toString(SystemProperties.getBoolean("sys.lmk.reportkills", false))); + loadExistingProcessExitInfo(); + }); } @GuardedBy("mService") diff --git a/services/core/java/com/android/server/am/CarUserSwitchingDialog.java b/services/core/java/com/android/server/am/CarUserSwitchingDialog.java index 549051df65ea..60754fbc5cd3 100644 --- a/services/core/java/com/android/server/am/CarUserSwitchingDialog.java +++ b/services/core/java/com/android/server/am/CarUserSwitchingDialog.java @@ -32,8 +32,8 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; -import android.os.Build; import android.os.UserManager; +import android.provider.Settings; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; @@ -83,9 +83,13 @@ final class CarUserSwitchingDialog extends UserSwitchingDialog { } TextView msgView = view.findViewById(R.id.user_loading); - // TODO: use developer settings instead - if (Build.IS_DEBUGGABLE) { - // TODO: use specific string + + // TODO(b/145132885): use constant from CarSettings + boolean showInfo = "true".equals(Settings.Global.getString( + getContext().getContentResolver(), + "android.car.ENABLE_USER_SWITCH_DEVELOPER_MESSAGE")); + + if (showInfo) { msgView.setText(res.getString(R.string.car_loading_profile) + " user\n(from " + mOldUser.id + " to " + mNewUser.id + ")"); } else { 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/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 63331fab4aef..04297828ebfc 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -125,7 +125,7 @@ public final class OomAdjuster { static final String OOM_ADJ_REASON_PROCESS_END = OOM_ADJ_REASON_METHOD + "_processEnd"; /** - * Flag {@link Context#BIND_INCLUDE_CAPABILITIES} is used + * Flag {@link android.content.Context#BIND_INCLUDE_CAPABILITIES} is used * to pass while-in-use capabilities from client process to bound service. In targetSdkVersion * R and above, if client is a TOP activity, when this flag is present, bound service gets all * while-in-use capabilities; when this flag is not present, bound service gets no while-in-use diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 7f9477ed5c9b..38cb501111ca 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -91,9 +91,7 @@ import android.os.UserHandle; import android.os.storage.StorageManager; import android.os.storage.StorageManagerInternal; import android.provider.DeviceConfig; -import android.system.ErrnoException; import android.system.Os; -import android.system.OsConstants; import android.text.TextUtils; import android.util.ArrayMap; import android.util.EventLog; @@ -327,12 +325,7 @@ public final class ProcessList { /** * How long between a process kill and we actually receive its death recipient */ - private static final long PROC_KILL_TIMEOUT = 2000; // 2 seconds; - - /** - * How long between polls to check if the given process is dead or not. - */ - private static final long PROC_DEATH_POLL_INTERVAL = 100; + private static final int PROC_KILL_TIMEOUT = 2000; // 2 seconds; ActivityManagerService mService = null; @@ -1571,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); @@ -1616,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 @@ -1633,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 @@ -2169,28 +2186,6 @@ public final class ProcessList { } /** - * A lite version of checking if a process is alive or not, by using kill(2) with signal 0. - * - * <p> - * Note that, zombie processes are stil "alive" in this case, use the {@link - * ActivityManagerService#isProcessAliveLocked} if zombie processes need to be excluded. - * </p> - */ - @GuardedBy("mService") - private boolean isProcessAliveLiteLocked(ProcessRecord app) { - // If somehow the pid is invalid, let's think it's dead. - if (app.pid <= 0) { - return false; - } - try { - Os.kill(app.pid, 0); - } catch (ErrnoException e) { - return e.errno != OsConstants.ESRCH; - } - return true; - } - - /** * Kill (if asked to) and wait for the given process died if necessary * @param app - The process record to kill * @param doKill - Kill the given process record @@ -2214,20 +2209,9 @@ public final class ProcessList { // wait for the death if (wait) { - boolean isAlive = true; - // ideally we should use pidfd_open(2) but it's available on kernel 5.3 or later - - final long timeout = SystemClock.uptimeMillis() + PROC_KILL_TIMEOUT; - isAlive = isProcessAliveLiteLocked(app); - while (timeout > SystemClock.uptimeMillis() && isAlive) { - try { - Thread.sleep(PROC_DEATH_POLL_INTERVAL); - } catch (InterruptedException e) { - } - isAlive = isProcessAliveLiteLocked(app); - } - - if (isAlive) { + try { + Process.waitForProcessDeath(app.pid, PROC_KILL_TIMEOUT); + } catch (Exception e) { // Maybe the process goes into zombie, use an expensive API to check again. if (mService.isProcessAliveLocked(app)) { Slog.w(TAG, String.format(formatString, @@ -2351,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); @@ -2532,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) { @@ -3985,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 eedeeea5cdb3..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; @@ -63,6 +64,7 @@ import android.hardware.input.InputManager; import android.hardware.usb.UsbManager; import android.hidl.manager.V1_0.IServiceManager; import android.media.AudioAttributes; +import android.media.AudioAttributes.AttributeSystemUsage; import android.media.AudioDeviceAddress; import android.media.AudioDeviceInfo; import android.media.AudioFocusInfo; @@ -88,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; @@ -570,6 +573,11 @@ public class AudioService extends IAudioService.Stub @GuardedBy("mSettingsLock") private int mAssistantUid; + private final Object mSupportedSystemUsagesLock = new Object(); + @GuardedBy("mSupportedSystemUsagesLock") + private @AttributeSystemUsage int[] mSupportedSystemUsages = + new int[]{AudioAttributes.USAGE_CALL_ASSISTANT}; + // Defines the format for the connection "address" for ALSA devices public static String makeAlsaAddressString(int card, int device) { return "card=" + card + ";device=" + device + ";"; @@ -1043,6 +1051,10 @@ public class AudioService extends IAudioService.Stub } } + synchronized (mSupportedSystemUsagesLock) { + AudioSystem.setSupportedSystemUsages(mSupportedSystemUsages); + } + synchronized (mAudioPolicies) { for (AudioPolicyProxy policy : mAudioPolicies.values()) { final int status = policy.connectMixes(); @@ -1099,6 +1111,37 @@ public class AudioService extends IAudioService.Stub } /** + * @see AudioManager#setSupportedSystemUsages(int[]) + */ + public void setSupportedSystemUsages(@NonNull @AttributeSystemUsage int[] systemUsages) { + enforceModifyAudioRoutingPermission(); + verifySystemUsages(systemUsages); + + synchronized (mSupportedSystemUsagesLock) { + AudioSystem.setSupportedSystemUsages(systemUsages); + mSupportedSystemUsages = systemUsages; + } + } + + /** + * @see AudioManager#getSupportedSystemUsages() + */ + public @NonNull @AttributeSystemUsage int[] getSupportedSystemUsages() { + enforceModifyAudioRoutingPermission(); + synchronized (mSupportedSystemUsagesLock) { + return Arrays.copyOf(mSupportedSystemUsages, mSupportedSystemUsages.length); + } + } + + private void verifySystemUsages(@NonNull int[] systemUsages) { + for (int i = 0; i < systemUsages.length; i++) { + if (!AudioAttributes.isSystemUsage(systemUsages[i])) { + throw new IllegalArgumentException("Non-system usage provided: " + systemUsages[i]); + } + } + } + + /** * @return the {@link android.media.audiopolicy.AudioProductStrategy} discovered from the * platform configuration file. */ @@ -5720,10 +5763,48 @@ public class AudioService extends IAudioService.Stub return false; } + private boolean isSupportedSystemUsage(@AudioAttributes.AttributeUsage int usage) { + synchronized (mSupportedSystemUsagesLock) { + for (int i = 0; i < mSupportedSystemUsages.length; i++) { + if (mSupportedSystemUsages[i] == usage) { + return true; + } + } + return false; + } + } + + private void validateAudioAttributesUsage(@NonNull AudioAttributes audioAttributes) { + @AudioAttributes.AttributeUsage int usage = audioAttributes.getSystemUsage(); + if (AudioAttributes.isSystemUsage(usage)) { + if (callerHasPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)) { + if (!isSupportedSystemUsage(usage)) { + throw new IllegalArgumentException( + "Unsupported usage " + AudioAttributes.usageToString(usage)); + } + } else { + throw new SecurityException("Missing MODIFY_AUDIO_ROUTING permission"); + } + } + } + + private boolean isValidAudioAttributesUsage(@NonNull AudioAttributes audioAttributes) { + @AudioAttributes.AttributeUsage int usage = audioAttributes.getSystemUsage(); + if (AudioAttributes.isSystemUsage(usage)) { + return callerHasPermission(Manifest.permission.MODIFY_AUDIO_ROUTING) + && isSupportedSystemUsage(usage); + } + return true; + } + public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags, IAudioPolicyCallback pcb, int sdk) { // permission checks + if (aa != null && !isValidAudioAttributesUsage(aa)) { + Log.w(TAG, "Request using unsupported usage."); + return AudioManager.AUDIOFOCUS_REQUEST_FAILED; + } if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) == AudioManager.AUDIOFOCUS_FLAG_LOCK) { if (AudioSystem.IN_VOICE_COMM_FOCUS_ID.equals(clientId)) { if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission( @@ -5754,6 +5835,10 @@ public class AudioService extends IAudioService.Stub public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, AudioAttributes aa, String callingPackageName) { + if (aa != null && !isValidAudioAttributesUsage(aa)) { + Log.w(TAG, "Request using unsupported usage."); + return AudioManager.AUDIOFOCUS_REQUEST_FAILED; + } return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa, callingPackageName); } @@ -6313,6 +6398,17 @@ public class AudioService extends IAudioService.Stub sForceUseLogger.dump(pw); pw.println("\n"); sVolumeLogger.dump(pw); + pw.println("\n"); + dumpSupportedSystemUsage(pw); + } + + private void dumpSupportedSystemUsage(PrintWriter pw) { + pw.println("Supported System Usages:"); + synchronized (mSupportedSystemUsagesLock) { + for (int i = 0; i < mSupportedSystemUsages.length; i++) { + pw.printf("\t%s\n", AudioAttributes.usageToString(mSupportedSystemUsages[i])); + } + } } private static String safeMediaVolumeStateToString(int state) { @@ -6680,6 +6776,7 @@ public class AudioService extends IAudioService.Stub boolean requireValidProjection = false; boolean requireCaptureAudioOrMediaOutputPerm = false; + boolean requireVoiceComunicationOutputPerm = false; boolean requireModifyRouting = false; if (hasFocusAccess || isVolumeController) { @@ -6689,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 @@ -6718,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; } @@ -6731,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; } @@ -6894,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) { @@ -6909,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()); @@ -7090,10 +7273,16 @@ public class AudioService extends IAudioService.Stub } public int trackPlayer(PlayerBase.PlayerIdCard pic) { + if (pic != null && pic.mAttributes != null) { + validateAudioAttributesUsage(pic.mAttributes); + } return mPlaybackMonitor.trackPlayer(pic); } public void playerAttributes(int piid, AudioAttributes attr) { + if (attr != null) { + validateAudioAttributesUsage(attr); + } mPlaybackMonitor.playerAttributes(piid, attr, Binder.getCallingUid()); } @@ -7138,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() { @@ -7329,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/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java index c845981fea7e..7bf2bc842176 100644 --- a/services/core/java/com/android/server/audio/MediaFocusControl.java +++ b/services/core/java/com/android/server/audio/MediaFocusControl.java @@ -727,6 +727,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer { case AudioAttributes.USAGE_ASSISTANT: case AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY: case AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: + case AudioAttributes.USAGE_ANNOUNCEMENT: return 700; case AudioAttributes.USAGE_VOICE_COMMUNICATION: case AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING: @@ -736,7 +737,10 @@ public class MediaFocusControl implements PlayerFocusEnforcer { case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED: case AudioAttributes.USAGE_NOTIFICATION_EVENT: case AudioAttributes.USAGE_ASSISTANCE_SONIFICATION: + case AudioAttributes.USAGE_VEHICLE_STATUS: return 500; + case AudioAttributes.USAGE_EMERGENCY: + case AudioAttributes.USAGE_SAFETY: case AudioAttributes.USAGE_UNKNOWN: default: return 0; diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java index a0c8e2325f77..a0573db680b4 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -118,14 +118,16 @@ public class FaceService extends BiometricServiceBase { private int mError; // Only valid if mError is ERROR_VENDOR private int mVendorError; + private int mUser; AuthenticationEvent(long startTime, long latency, boolean authenticated, int error, - int vendorError) { + int vendorError, int user) { mStartTime = startTime; mLatency = latency; mAuthenticated = authenticated; mError = error; mVendorError = vendorError; + mUser = user; } public String toString(Context context) { @@ -134,6 +136,7 @@ public class FaceService extends BiometricServiceBase { + "\tAuthenticated: " + mAuthenticated + "\tError: " + mError + "\tVendorCode: " + mVendorError + + "\tUser: " + mUser + "\t" + FaceManager.getErrorString(context, mError, mVendorError); } } @@ -242,7 +245,8 @@ public class FaceService extends BiometricServiceBase { System.currentTimeMillis() - getStartTimeMs() /* latency */, authenticated, 0 /* error */, - 0 /* vendorError */)); + 0 /* vendorError */, + getTargetUserId())); // For face, the authentication lifecycle ends either when // 1) Authenticated == true @@ -260,7 +264,8 @@ public class FaceService extends BiometricServiceBase { System.currentTimeMillis() - getStartTimeMs() /* latency */, false /* authenticated */, error, - vendorCode)); + vendorCode, + getTargetUserId())); return super.onError(deviceId, error, vendorCode); } diff --git a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java index 96a202fa2b10..4fb6607a6b08 100644 --- a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java +++ b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java @@ -81,10 +81,15 @@ public class DefaultNetworkMetrics { printEvent(localTimeMs, pw, mCurrentDefaultNetwork); } - public synchronized void listEventsAsProto(PrintWriter pw) { + /** + * Convert events in the ring buffer to a list of IpConnectivityEvent protos + */ + public synchronized List<IpConnectivityEvent> listEventsAsProto() { + List<IpConnectivityEvent> list = new ArrayList<>(); for (DefaultNetworkEvent ev : mEventsLog.toArray()) { - pw.print(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 33f6ed597204..2c06d8230f13 100644 --- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java +++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java @@ -41,7 +41,9 @@ import com.android.server.SystemService; import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent; import java.io.FileDescriptor; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -239,18 +241,37 @@ final public class IpConnectivityMetrics extends SystemService { mDefaultNetworkMetrics.listEvents(pw); } + private List<IpConnectivityEvent> listEventsAsProtos() { + final List<IpConnectivityEvent> events = IpConnectivityEventBuilder.toProto(getEvents()); + if (mNetdListener != null) { + events.addAll(mNetdListener.listAsProtos()); + } + events.addAll(mDefaultNetworkMetrics.listEventsAsProto()); + return events; + } + /* * Print the content of the rolling event buffer in text proto format. */ - private void cmdListAsProto(PrintWriter pw) { - final List<ConnectivityMetricsEvent> events = getEvents(); - for (IpConnectivityEvent ev : IpConnectivityEventBuilder.toProto(events)) { - pw.print(ev.toString()); + private void cmdListAsTextProto(PrintWriter pw) { + listEventsAsProtos().forEach(e -> pw.print(e.toString())); + } + + /* + * Write the content of the rolling event buffer in proto wire format to the given OutputStream. + */ + private void cmdListAsBinaryProto(OutputStream out) { + final int dropped; + synchronized (mLock) { + dropped = mDropped; } - if (mNetdListener != null) { - mNetdListener.listAsProtos(pw); + try { + byte[] data = IpConnectivityEventBuilder.serialize(dropped, listEventsAsProtos()); + out.write(data); + out.flush(); + } catch (IOException e) { + Log.e(TAG, "could not serialize events", e); } - mDefaultNetworkMetrics.listEventsAsProto(pw); } /* @@ -267,6 +288,9 @@ final public class IpConnectivityMetrics extends SystemService { static final String CMD_FLUSH = "flush"; // Dump the rolling buffer of metrics event in human readable proto text format. static final String CMD_PROTO = "proto"; + // Dump the rolling buffer of metrics event in proto wire format. See usage() of + // frameworks/native/cmds/dumpsys/dumpsys.cpp for details. + static final String CMD_PROTO_BIN = "--proto"; // Dump the rolling buffer of metrics event and pretty print events using a human readable // format. Also print network dns/connect statistics and default network event time series. static final String CMD_LIST = "list"; @@ -291,7 +315,10 @@ final public class IpConnectivityMetrics extends SystemService { cmdFlush(pw); return; case CMD_PROTO: - cmdListAsProto(pw); + cmdListAsTextProto(pw); + return; + case CMD_PROTO_BIN: + cmdListAsBinaryProto(new FileOutputStream(fd)); return; case CMD_LIST: default: diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java index dbc339b01c89..f2892cc81951 100644 --- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java +++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java @@ -366,16 +366,21 @@ public class NetdEventListenerService extends INetdEventListener.Stub { } } - public synchronized void listAsProtos(PrintWriter pw) { + /** + * Convert events in the buffer to a list of IpConnectivityEvent protos + */ + public synchronized List<IpConnectivityEvent> listAsProtos() { + List<IpConnectivityEvent> list = new ArrayList<>(); for (int i = 0; i < mNetworkMetrics.size(); i++) { - pw.print(IpConnectivityEventBuilder.toProto(mNetworkMetrics.valueAt(i).connectMetrics)); + list.add(IpConnectivityEventBuilder.toProto(mNetworkMetrics.valueAt(i).connectMetrics)); } for (int i = 0; i < mNetworkMetrics.size(); i++) { - pw.print(IpConnectivityEventBuilder.toProto(mNetworkMetrics.valueAt(i).dnsMetrics)); + list.add(IpConnectivityEventBuilder.toProto(mNetworkMetrics.valueAt(i).dnsMetrics)); } for (int i = 0; i < mWakeupStats.size(); i++) { - pw.print(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i))); + list.add(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i))); } + return list; } private long getTransports(int netId) { diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java index 28f67fe2d618..aa39926d2310 100644 --- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java +++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java @@ -109,11 +109,17 @@ public abstract class BrightnessMappingStrategy { return levels; } - private static float[] getFloatArray(TypedArray array) { + /** + * Extracts a float array from the specified {@link TypedArray}. + * + * @param array The array to convert. + * @return the given array as a float array. + */ + public static float[] getFloatArray(TypedArray array) { final int N = array.length(); float[] vals = new float[N]; for (int i = 0; i < N; i++) { - vals[i] = array.getFloat(i, -1.0f); + vals[i] = array.getFloat(i, PowerManager.BRIGHTNESS_OFF_FLOAT); } array.recycle(); return vals; @@ -335,7 +341,7 @@ public abstract class BrightnessMappingStrategy { } } - protected float normalizeAbsoluteBrightness(int brightness) { + protected static float normalizeAbsoluteBrightness(int brightness) { brightness = MathUtils.constrain(brightness, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON); return (float) brightness / PowerManager.BRIGHTNESS_ON; diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java new file mode 100644 index 000000000000..e09cf6178981 --- /dev/null +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + +import android.os.Environment; +import android.util.Slog; + +import com.android.server.display.config.DisplayConfiguration; +import com.android.server.display.config.NitsMap; +import com.android.server.display.config.Point; +import com.android.server.display.config.XmlParser; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.util.List; + +import javax.xml.datatype.DatatypeConfigurationException; + +/** + * Reads and stores display-specific configurations. + */ +public class DisplayDeviceConfig { + private static final String TAG = "DisplayDeviceConfig"; + + public static final float HIGH_BRIGHTNESS_MODE_UNSUPPORTED = Float.NaN; + + private static final String ETC_DIR = "etc"; + private static final String DISPLAY_CONFIG_DIR = "displayconfig"; + private static final String CONFIG_FILE_FORMAT = "display_%d.xml"; + + private float[] mNits; + private float[] mBrightness; + private BigDecimal mHighBrightnessModeStart; + + private DisplayDeviceConfig() { + } + + /** + * Creates an instance for the specified display. + * + * @param physicalDisplayId The display ID for which to load the configuration. + * @return A configuration instance for the specified display. + */ + public static DisplayDeviceConfig create(long physicalDisplayId) { + final DisplayDeviceConfig config = new DisplayDeviceConfig(); + final String filename = String.format(CONFIG_FILE_FORMAT, physicalDisplayId); + + config.initFromFile(Environment.buildPath( + Environment.getProductDirectory(), ETC_DIR, DISPLAY_CONFIG_DIR, filename)); + return config; + } + + /** + * Return the brightness mapping nits array if one is defined in the configuration file. + * + * @return The brightness mapping nits array. + */ + public float[] getNits() { + return mNits; + } + + /** + * Return the brightness mapping value array if one is defined in the configuration file. + * + * @return The brightness mapping value array. + */ + public float[] getBrightness() { + return mBrightness; + } + + /** + * Returns the point along the brightness value range {@link #getBrightness()} that + * high-brightness-mode begins. If high-brightness-mode is not supported, then + * Float.NaN is returned. + * + * @return The high brightness mode threshold, or Float.NaN if not supported. + */ + public float getHighBrightnessModeStart() { + return mHighBrightnessModeStart != null + ? mHighBrightnessModeStart.floatValue() : HIGH_BRIGHTNESS_MODE_UNSUPPORTED; + } + + private void initFromFile(File configFile) { + if (!configFile.exists()) { + // Display configuration files aren't required to exist. + return; + } + + if (!configFile.isFile()) { + Slog.e(TAG, "Display configuration is not a file: " + configFile + ", skipping"); + return; + } + + try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) { + final DisplayConfiguration config = XmlParser.read(in); + loadBrightnessMap(config); + } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) { + Slog.e(TAG, "Encountered an error while reading/parsing display config file: " + + configFile, e); + } + } + + private void loadBrightnessMap(DisplayConfiguration config) { + final NitsMap map = config.getScreenBrightnessMap(); + final List<Point> points = map.getPoint(); + final int size = points.size(); + + float[] nits = new float[size]; + float[] backlight = new float[size]; + + int i = 0; + for (Point point : points) { + nits[i] = point.getNits().floatValue(); + backlight[i] = point.getValue().floatValue(); + if (i > 0) { + if (nits[i] < nits[i - 1]) { + Slog.e(TAG, "screenBrightnessMap must be non-decreasing, ignoring rest " + + " of configuration. Nits: " + nits[i] + " < " + nits[i - 1]); + return; + } + + if (backlight[i] < backlight[i - 1]) { + Slog.e(TAG, "screenBrightnessMap must be non-decreasing, ignoring rest " + + " of configuration. Value: " + backlight[i] + " < " + + backlight[i - 1]); + return; + } + } + ++i; + } + final BigDecimal hbmStart = map.getHighBrightnessStart(); + + mHighBrightnessModeStart = hbmStart; + mNits = nits; + mBrightness = backlight; + } +} diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index fb8a4193286b..7e8fe3ae8428 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -30,6 +30,7 @@ import android.os.Trace; import android.util.LongSparseArray; import android.util.Slog; import android.util.SparseArray; +import android.util.Spline; import android.view.Display; import android.view.DisplayAddress; import android.view.DisplayCutout; @@ -37,10 +38,11 @@ import android.view.DisplayEventReceiver; import android.view.Surface; import android.view.SurfaceControl; +import com.android.internal.os.BackgroundThread; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; -import com.android.server.lights.Light; import com.android.server.lights.LightsManager; +import com.android.server.lights.LogicalLight; import java.io.PrintWriter; import java.util.ArrayList; @@ -162,7 +164,7 @@ 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; @@ -187,8 +189,10 @@ final class LocalDisplayAdapter extends DisplayAdapter { private boolean mGameContentTypeRequested; private boolean mSidekickActive; private SidekickInternal mSidekickInternal; - private SurfaceControl.PhysicalDisplayInfo[] mDisplayInfos; + private Spline mSystemBrightnessToNits; + private Spline mNitsToHalBrightness; + private boolean mHalBrightnessSupport; LocalDisplayDevice(IBinder displayToken, long physicalDisplayId, SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo, @@ -210,6 +214,11 @@ final class LocalDisplayAdapter extends DisplayAdapter { mHdrCapabilities = SurfaceControl.getHdrCapabilities(displayToken); mAllmSupported = SurfaceControl.getAutoLowLatencyModeSupport(displayToken); mGameContentTypeSupported = SurfaceControl.getGameContentTypeSupport(displayToken); + mHalBrightnessSupport = SurfaceControl.getDisplayBrightnessSupport(displayToken); + + // Defer configuration file loading + BackgroundThread.getHandler().sendMessage(PooledLambda.obtainMessage( + LocalDisplayDevice::loadDisplayConfigurationBrightnessMapping, this)); } @Override @@ -338,6 +347,41 @@ final class LocalDisplayAdapter extends DisplayAdapter { return true; } + private void loadDisplayConfigurationBrightnessMapping() { + Spline nitsToHal = null; + Spline sysToNits = null; + + // Load the mapping from nits to HAL brightness range (display-device-config.xml) + DisplayDeviceConfig config = DisplayDeviceConfig.create(mPhysicalDisplayId); + if (config == null) { + return; + } + final float[] halNits = config.getNits(); + final float[] halBrightness = config.getBrightness(); + if (halNits == null || halBrightness == null) { + return; + } + nitsToHal = Spline.createSpline(halNits, halBrightness); + + // Load the mapping from system brightness range to nits (config.xml) + final Resources res = getOverlayContext().getResources(); + final float[] sysNits = BrightnessMappingStrategy.getFloatArray(res.obtainTypedArray( + com.android.internal.R.array.config_screenBrightnessNits)); + final int[] sysBrightness = res.getIntArray( + com.android.internal.R.array.config_screenBrightnessBacklight); + if (sysNits.length == 0 || sysBrightness.length != sysNits.length) { + return; + } + final float[] sysBrightnessFloat = new float[sysBrightness.length]; + for (int i = 0; i < sysBrightness.length; i++) { + sysBrightnessFloat[i] = sysBrightness[i]; + } + sysToNits = Spline.createSpline(sysBrightnessFloat, sysNits); + + mNitsToHalBrightness = nitsToHal; + mSystemBrightnessToNits = sysToNits; + } + private boolean updateColorModesLocked(int[] colorModes, int activeColorMode) { List<Integer> pendingColorModes = new ArrayList<>(); @@ -628,13 +672,37 @@ final class LocalDisplayAdapter extends DisplayAdapter { Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness(" + "id=" + physicalDisplayId + ", brightness=" + brightness + ")"); try { - mBacklight.setBrightness(brightness); + if (mHalBrightnessSupport) { + mBacklight.setBrightnessFloat( + displayBrightnessToHalBrightness(brightness)); + } else { + mBacklight.setBrightness(brightness); + } Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenBrightness", brightness); } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } } + + /** + * Converts brightness range from the framework's brightness space to the + * Hal brightness space if the HAL brightness space has been provided via + * a display device configuration file. + */ + private float displayBrightnessToHalBrightness(int brightness) { + if (mSystemBrightnessToNits == null || mNitsToHalBrightness == null) { + return PowerManager.BRIGHTNESS_INVALID_FLOAT; + } + + if (brightness == 0) { + return PowerManager.BRIGHTNESS_OFF_FLOAT; + } + + final float nits = mSystemBrightnessToNits.interpolate(brightness); + final float halBrightness = mNitsToHalBrightness.interpolate(nits); + return halBrightness; + } }; } return null; diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java b/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java index 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/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 0bf65bd6f739..905a94f57262 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -758,6 +758,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>(); /** + * Map of generated token to windowToken that is requesting + * {@link InputMethodManager#showSoftInput(View, int)}. + * This map tracks origin of showSoftInput requests. + */ + @GuardedBy("mMethodMap") + private final WeakHashMap<IBinder, IBinder> mShowRequestWindowMap = new WeakHashMap<>(); + + /** * A ring buffer to store the history of {@link StartInputInfo}. */ private static final class StartInputHistory { @@ -974,7 +982,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub hideCurrentInputLocked(0, null); mShowRequested = showRequested; } else if (mShowRequested) { - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); + showCurrentInputLocked( + mCurFocusedWindow, InputMethodManager.SHOW_IMPLICIT, null); } } else { boolean enabledChanged = false; @@ -2075,7 +2084,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub startInputToken, session, mCurInputContext, mCurAttribute)); if (mShowRequested) { if (DEBUG) Slog.v(TAG, "Attach new input asks to show input"); - showCurrentInputLocked(getAppShowFlags(), null); + showCurrentInputLocked(mCurFocusedWindow, getAppShowFlags(), null); } return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION, session.session, (session.channel != null ? session.channel.dup() : null), @@ -2789,7 +2798,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @Override - public boolean showSoftInput(IInputMethodClient client, int flags, + public boolean showSoftInput(IInputMethodClient client, IBinder windowToken, int flags, ResultReceiver resultReceiver) { int uid = Binder.getCallingUid(); synchronized (mMethodMap) { @@ -2814,7 +2823,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } if (DEBUG) Slog.v(TAG, "Client requesting input be shown"); - return showCurrentInputLocked(flags, resultReceiver); + return showCurrentInputLocked(windowToken, flags, resultReceiver); } finally { Binder.restoreCallingIdentity(ident); } @@ -2822,7 +2831,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @GuardedBy("mMethodMap") - boolean showCurrentInputLocked(int flags, ResultReceiver resultReceiver) { + boolean showCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver) { mShowRequested = true; if (mAccessibilityRequestingNoSoftKeyboard) { return false; @@ -2842,9 +2851,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub boolean res = false; if (mCurMethod != null) { if (DEBUG) Slog.d(TAG, "showCurrentInputLocked: mCurToken=" + mCurToken); - executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO( + // create a dummy token for IMS so that IMS cannot inject windows into client app. + Binder showInputToken = new Binder(); + mShowRequestWindowMap.put(showInputToken, windowToken); + executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOOO( MSG_SHOW_SOFT_INPUT, getImeShowFlags(), mCurMethod, - resultReceiver)); + resultReceiver, showInputToken)); mInputShown = true; if (mHaveConnection && !mVisibleBound) { bindCurrentInputMethodServiceLocked( @@ -3145,7 +3157,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub attribute, startInputFlags, startInputReason); didStart = true; } - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); + showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null); } break; case LayoutParams.SOFT_INPUT_STATE_UNCHANGED: @@ -3171,7 +3183,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub attribute, startInputFlags, startInputReason); didStart = true; } - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); + showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null); } else { Slog.e(TAG, "SOFT_INPUT_STATE_VISIBLE is ignored because" + " there is no focused view that also returns true from" @@ -3188,7 +3200,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub attribute, startInputFlags, startInputReason); didStart = true; } - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null); + showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null); } else { Slog.e(TAG, "SOFT_INPUT_STATE_ALWAYS_VISIBLE is ignored because" + " there is no focused view that also returns true from" @@ -3627,7 +3639,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @BinderThread - private void applyImeVisibility(IBinder token, boolean setVisible) { + private void applyImeVisibility(IBinder token, IBinder windowToken, boolean setVisible) { synchronized (mMethodMap) { if (!calledWithValidTokenLocked(token)) { return; @@ -3644,7 +3656,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } else { // Send to window manager to show IME after IME layout finishes. - mWindowManagerInternal.showImePostLayout(mLastImeTargetWindow); + mWindowManagerInternal.showImePostLayout(mShowRequestWindowMap.get(windowToken)); } } } @@ -3695,7 +3707,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } long ident = Binder.clearCallingIdentity(); try { - showCurrentInputLocked(flags, null); + showCurrentInputLocked(mLastImeTargetWindow, flags, null); } finally { Binder.restoreCallingIdentity(ident); } @@ -3780,7 +3792,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub try { if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".showSoftInput(" + msg.arg1 + ", " + args.arg2 + ")"); - ((IInputMethod)args.arg1).showSoftInput(msg.arg1, (ResultReceiver)args.arg2); + ((IInputMethod) args.arg1).showSoftInput( + (IBinder) args.arg3, msg.arg1, (ResultReceiver) args.arg2); } catch (RemoteException e) { } args.recycle(); @@ -5346,8 +5359,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @Override - public void applyImeVisibility(boolean setVisible) { - mImms.applyImeVisibility(mToken, setVisible); + public void applyImeVisibility(IBinder windowToken, boolean setVisible) { + mImms.applyImeVisibility(mToken, windowToken, setVisible); } } } diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index f09795fbea01..d09c478e320f 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -1449,7 +1449,8 @@ public final class MultiClientInputMethodManagerService { @BinderThread @Override public boolean showSoftInput( - IInputMethodClient client, int flags, ResultReceiver resultReceiver) { + IInputMethodClient client, IBinder token, int flags, + ResultReceiver resultReceiver) { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); final int userId = UserHandle.getUserId(callingUid); diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java index b1639a948ffc..2926ec94417f 100644 --- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java @@ -67,7 +67,9 @@ import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; /** Implementation of {@link AppIntegrityManagerService}. */ @@ -221,6 +223,15 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { } String installerPackageName = getInstallerPackageName(intent); + + // Skip integrity verification if the verifier is doing the install. + if (isRuleProvider(installerPackageName)) { + Slog.i(TAG, "Verifier doing the install. Skipping integrity check."); + mPackageManagerInternal.setIntegrityVerificationResult( + verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW); + return; + } + String appCert = getCertificateFingerprint(packageInfo); AppInstallMetadata.Builder builder = new AppInstallMetadata.Builder(); @@ -271,7 +282,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { * Verify the UID and return the installer package name. * * @return the package name of the installer, or null if it cannot be determined or it is - * installed via adb. + * installed via adb. */ @Nullable private String getInstallerPackageName(Intent intent) { @@ -518,9 +529,17 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { } private String getCallerPackageNameOrThrow() { - final String[] allowedRuleProviders = - mContext.getResources() - .getStringArray(R.array.config_integrityRuleProviderPackages); + String callerPackageName = getCallerPackageName(); + if (callerPackageName == null) { + throw new SecurityException( + "Only system packages specified in config_integrityRuleProviderPackages are" + + " allowed to call this method."); + } + return callerPackageName; + } + + private String getCallerPackageName() { + final List<String> allowedRuleProviders = getAllowedRuleProviders(); for (String packageName : allowedRuleProviders) { try { // At least in tests, getPackageUid gives "NameNotFound" but getPackagesFromUid @@ -537,9 +556,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { Slog.i(TAG, "Rule provider package " + packageName + " not installed."); } } - throw new SecurityException( - "Only system packages specified in config_integrityRuleProviderPackages are" - + " allowed to call this method."); + return null; } private boolean isSystemApp(String packageName) { @@ -552,4 +569,14 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { return false; } } + + private List<String> getAllowedRuleProviders() { + return Arrays.asList(mContext.getResources().getStringArray( + R.array.config_integrityRuleProviderPackages)); + } + + private boolean isRuleProvider(String installerPackageName) { + return getAllowedRuleProviders().stream().anyMatch( + ruleProvider -> ruleProvider.equals(installerPackageName)); + } } diff --git a/services/core/java/com/android/server/lights/LightsManager.java b/services/core/java/com/android/server/lights/LightsManager.java index 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 eaae2ed40b2e..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); - private final class LightImpl extends Light { + for (int i = 0; i < lightIds.length; i++) { + session.setRequest(lightIds[i], lightStates[i]); + } + invalidateLightStatesLocked(); + } + } + @Override + public @Nullable LightState getLightState(int lightId) { + getContext().enforceCallingOrSelfPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS, + "getLightState(@TestApi) requires CONTROL_DEVICE_LIGHTS permission"); + + synchronized (LightsService.this) { + final LightImpl light = mLightsById.get(lightId); + if (light == null || isSystemLight(light.getHwLight())) { + throw new IllegalArgumentException("Invalid light: " + lightId); + } + return new LightState(light.getColor()); + } + } + + @Override + public void openSession(IBinder token) { + getContext().enforceCallingOrSelfPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS, + "openSession requires CONTROL_DEVICE_LIGHTS permission"); + Preconditions.checkNotNull(token); + + synchronized (LightsService.this) { + Preconditions.checkState(getSessionLocked(token) == null, "already registered"); + try { + token.linkToDeath(() -> closeSessionInternal(token), 0); + mSessions.add(new Session(token)); + } catch (RemoteException e) { + Slog.e(TAG, "Couldn't open session, client already died" , e); + throw new IllegalArgumentException("Client is already dead."); + } + } + } + + @Override + public void closeSession(IBinder token) { + getContext().enforceCallingOrSelfPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS, + "closeSession requires CONTROL_DEVICE_LIGHTS permission"); + Preconditions.checkNotNull(token); + closeSessionInternal(token); + } + + private void closeSessionInternal(IBinder token) { + synchronized (LightsService.this) { + final Session session = getSessionLocked(token); + if (session != null) { + mSessions.remove(session); + invalidateLightStatesLocked(); + } + } + } + + private void checkRequestIsValid(int[] lightIds) { + for (int i = 0; i < lightIds.length; i++) { + final LightImpl light = mLightsById.get(lightIds[i]); + final HwLight hwLight = light.getHwLight(); + Preconditions.checkState(light != null && !isSystemLight(hwLight), + "invalid lightId " + hwLight.id); + } + } + + /** + * Apply light state requests for all light IDs. + * + * <p>In case of conflict, the session that started earliest wins. + */ + private void invalidateLightStatesLocked() { + final Map<Integer, LightState> states = new HashMap<>(); + for (int i = mSessions.size() - 1; i >= 0; i--) { + SparseArray<LightState> requests = mSessions.get(i).mRequests; + for (int j = 0; j < requests.size(); j++) { + states.put(requests.keyAt(j), requests.valueAt(j)); + } + } + for (int i = 0; i < mLightsById.size(); i++) { + LightImpl light = mLightsById.valueAt(i); + HwLight hwLight = light.getHwLight(); + if (!isSystemLight(hwLight)) { + LightState state = states.get(hwLight.id); + if (state != null) { + light.setColor(state.getColor()); + } else { + light.turnOff(); + } + } + } + } + + private @Nullable Session getSessionLocked(IBinder token) { + for (int i = 0; i < mSessions.size(); i++) { + if (token.equals(mSessions.get(i).mToken)) { + return mSessions.get(i); + } + } + return null; + } + } + + private final class LightImpl extends LogicalLight { private final IBinder mDisplayToken; private final int mSurfaceControlMaximumBrightness; - private LightImpl(Context context, int id) { - mId = id; + private LightImpl(Context context, HwLight hwLight) { + mHwLight = hwLight; mDisplayToken = SurfaceControl.getInternalDisplayToken(); final boolean brightnessSupport = SurfaceControl.getDisplayBrightnessSupport( mDisplayToken); @@ -58,17 +249,28 @@ public class LightsService extends SystemService { } @Override + public void setBrightnessFloat(float brightness) { + if (!Float.isNaN(brightness)) { + setBrightness(brightness, 0, BRIGHTNESS_MODE_USER); + } + } + + @Override public void setBrightness(int brightness) { setBrightness(brightness, BRIGHTNESS_MODE_USER); } @Override public void setBrightness(int brightness, int brightnessMode) { + setBrightness(Float.NaN, brightness, brightnessMode); + } + + private void setBrightness(float brightnessFloat, int brightness, int brightnessMode) { synchronized (this) { // LOW_PERSISTENCE cannot be manually set if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) { - Slog.w(TAG, "setBrightness with LOW_PERSISTENCE unexpected #" + mId + - ": brightness=0x" + Integer.toHexString(brightness)); + Slog.w(TAG, "setBrightness with LOW_PERSISTENCE unexpected #" + mHwLight.id + + ": brightness=0x" + Integer.toHexString(brightness)); return; } // Ideally, we'd like to set the brightness mode through the SF/HWC as well, but @@ -84,7 +286,10 @@ public class LightsService extends SystemService { if (DEBUG) { Slog.d(TAG, "Using new setBrightness path!"); } - if (brightness == 0) { + + if (!Float.isNaN(brightnessFloat)) { + SurfaceControl.setDisplayBrightness(mDisplayToken, brightnessFloat); + } else if (brightness == 0) { SurfaceControl.setDisplayBrightness(mDisplayToken, -1.0f); } else { SurfaceControl.setDisplayBrightness(mDisplayToken, @@ -124,7 +329,7 @@ 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); } } } @@ -171,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; @@ -180,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); } @@ -194,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; @@ -209,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 @@ -235,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 717e3dae479d..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; /** @@ -49,10 +64,39 @@ public abstract class Light { */ public abstract void setBrightness(int brightness, int brightnessMode); + /** + * Set the brightness of a display using the brightness range defines in a + * display-device-configuration file. + */ + public abstract void setBrightnessFloat(float brightness); + + /** + * Set the color of a light. + */ public abstract void setColor(int color); + + /** + * Set the color of a light and control flashing. + */ public abstract void setFlashing(int color, int mode, int onMS, int offMS); + + /** + * Pulses the light. + */ public abstract void pulse(); + + /** + * Pulses the light with a specified color for a specified duration. + */ public abstract void pulse(int color, int onMS); + + /** + * Turns off the light. + */ public abstract void turnOff(); + + /** + * Set the VR mode of a display. + */ public abstract void setVrMode(boolean enabled); } diff --git a/services/core/java/com/android/server/lights/OWNERS b/services/core/java/com/android/server/lights/OWNERS index 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/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/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java index 837c489c0cac..8179c32ae2df 100644 --- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java +++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java @@ -29,12 +29,13 @@ import android.content.Intent; import android.content.IntentFilter; import android.media.MediaRoute2Info; import android.text.TextUtils; -import android.util.Log; +import android.util.Slog; import android.util.SparseBooleanArray; import com.android.internal.R; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -59,7 +60,6 @@ class BluetoothRouteProvider { private final BroadcastReceiver mBroadcastReceiver = new BluetoothBroadcastReceiver(); private final BluetoothProfileListener mProfileListener = new BluetoothProfileListener(); - // TODO: The mActiveDevice should be set when BluetoothRouteProvider is created. private BluetoothDevice mActiveDevice = null; static synchronized BluetoothRouteProvider getInstance(@NonNull Context context, @@ -104,6 +104,43 @@ class BluetoothRouteProvider { mContext.registerReceiver(mBroadcastReceiver, mIntentFilter, null, null); } + /** + * Clears the active device for all known profiles. + */ + public void clearActiveDevices() { + BluetoothA2dp a2dpProfile = mA2dpProfile; + BluetoothHearingAid hearingAidProfile = mHearingAidProfile; + if (a2dpProfile != null) { + a2dpProfile.setActiveDevice(null); + } + if (hearingAidProfile != null) { + hearingAidProfile.setActiveDevice(null); + } + } + + /** + * Sets the active device. + * @param deviceId the id of the Bluetooth device + */ + public void setActiveDevice(@NonNull String deviceId) { + BluetoothRouteInfo btRouteInfo = mBluetoothRoutes.get(deviceId); + if (btRouteInfo == null) { + Slog.w(TAG, "setActiveDevice: unknown device id=" + deviceId); + return; + } + BluetoothA2dp a2dpProfile = mA2dpProfile; + BluetoothHearingAid hearingAidProfile = mHearingAidProfile; + + if (a2dpProfile != null + && btRouteInfo.connectedProfiles.get(BluetoothProfile.A2DP, false)) { + a2dpProfile.setActiveDevice(btRouteInfo.btDevice); + } + if (hearingAidProfile != null + && btRouteInfo.connectedProfiles.get(BluetoothProfile.HEARING_AID, false)) { + hearingAidProfile.setActiveDevice(btRouteInfo.btDevice); + } + } + private void addEventReceiver(String action, BluetoothEventReceiver eventReceiver) { mEventReceiverMap.put(action, eventReceiver); mIntentFilter.addAction(action); @@ -157,12 +194,12 @@ class BluetoothRouteProvider { private void setRouteConnectionStateForDevice(BluetoothDevice device, @MediaRoute2Info.ConnectionState int state) { if (device == null) { - Log.w(TAG, "setRouteConnectionStateForDevice: device shouldn't be null"); + Slog.w(TAG, "setRouteConnectionStateForDevice: device shouldn't be null"); return; } BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress()); if (btRoute == null) { - Log.w(TAG, "setRouteConnectionStateForDevice: route shouldn't be null"); + Slog.w(TAG, "setRouteConnectionStateForDevice: route shouldn't be null"); return; } if (btRoute.route.getConnectionState() != state) { @@ -184,24 +221,36 @@ class BluetoothRouteProvider { // These callbacks run on the main thread. private final class BluetoothProfileListener implements BluetoothProfile.ServiceListener { public void onServiceConnected(int profile, BluetoothProfile proxy) { + List<BluetoothDevice> activeDevices; switch (profile) { case BluetoothProfile.A2DP: mA2dpProfile = (BluetoothA2dp) proxy; + // It may contain null. + activeDevices = Collections.singletonList(mA2dpProfile.getActiveDevice()); break; case BluetoothProfile.HEARING_AID: mHearingAidProfile = (BluetoothHearingAid) proxy; + activeDevices = mHearingAidProfile.getActiveDevices(); break; default: return; } + //TODO: Check a pair of HAP devices whether there exist two or more active devices. for (BluetoothDevice device : proxy.getConnectedDevices()) { BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress()); if (btRoute == null) { btRoute = createBluetoothRoute(device); mBluetoothRoutes.put(device.getAddress(), btRoute); } + if (activeDevices.contains(device)) { + mActiveDevice = device; + setRouteConnectionStateForDevice(device, + MediaRoute2Info.CONNECTION_STATE_CONNECTED); + } + btRoute.connectedProfiles.put(profile, true); } + notifyBluetoothRoutesUpdated(); } public void onServiceDisconnected(int profile) { diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index f9169ee69d41..9594659f23d0 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -760,17 +760,12 @@ class MediaRouter2ServiceImpl { Slog.w(TAG, "selectClientRouteLocked: Ignoring unknown manager."); return; } - //TODO: we shouldn't ignore selecting request for unknown clients. (RCN?) Client2Record clientRecord = managerRecord.mUserRecord.mHandler .findClientforSessionLocked(sessionId); - if (clientRecord == null) { - Slog.w(TAG, "selectClientRouteLocked: Ignoring unknown session."); - return; - } - clientRecord.mUserRecord.mHandler.sendMessage( + managerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::selectRouteOnHandler, - clientRecord.mUserRecord.mHandler, + managerRecord.mUserRecord.mHandler, clientRecord, sessionId, route)); } @@ -783,17 +778,12 @@ class MediaRouter2ServiceImpl { Slog.w(TAG, "deselectClientRouteLocked: Ignoring unknown manager."); return; } - //TODO: we shouldn't ignore selecting request for unknown clients. (RCN?) Client2Record clientRecord = managerRecord.mUserRecord.mHandler .findClientforSessionLocked(sessionId); - if (clientRecord == null) { - Slog.w(TAG, "deslectClientRouteLocked: Ignoring unknown session."); - return; - } - clientRecord.mUserRecord.mHandler.sendMessage( + managerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::deselectRouteOnHandler, - clientRecord.mUserRecord.mHandler, + managerRecord.mUserRecord.mHandler, clientRecord, sessionId, route)); } @@ -806,17 +796,12 @@ class MediaRouter2ServiceImpl { Slog.w(TAG, "transferClientRouteLocked: Ignoring unknown manager."); return; } - //TODO: we shouldn't ignore selecting request for unknown clients. (RCN?) Client2Record clientRecord = managerRecord.mUserRecord.mHandler .findClientforSessionLocked(sessionId); - if (clientRecord == null) { - Slog.w(TAG, "transferClientRouteLocked: Ignoring unknown session."); - return; - } - clientRecord.mUserRecord.mHandler.sendMessage( + managerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::transferToRouteOnHandler, - clientRecord.mUserRecord.mHandler, + managerRecord.mUserRecord.mHandler, clientRecord, sessionId, route)); } @@ -1166,7 +1151,7 @@ class MediaRouter2ServiceImpl { requestId, sessionHints); } - private void selectRouteOnHandler(@NonNull Client2Record clientRecord, + private void selectRouteOnHandler(@Nullable Client2Record clientRecord, String uniqueSessionId, MediaRoute2Info route) { if (!checkArgumentsForSessionControl(clientRecord, uniqueSessionId, route, "selecting")) { @@ -1182,7 +1167,7 @@ class MediaRouter2ServiceImpl { provider.selectRoute(getOriginalId(uniqueSessionId), route.getOriginalId()); } - private void deselectRouteOnHandler(@NonNull Client2Record clientRecord, + private void deselectRouteOnHandler(@Nullable Client2Record clientRecord, String uniqueSessionId, MediaRoute2Info route) { if (!checkArgumentsForSessionControl(clientRecord, uniqueSessionId, route, "deselecting")) { @@ -1198,7 +1183,7 @@ class MediaRouter2ServiceImpl { provider.deselectRoute(getOriginalId(uniqueSessionId), route.getOriginalId()); } - private void transferToRouteOnHandler(@NonNull Client2Record clientRecord, + private void transferToRouteOnHandler(Client2Record clientRecord, String uniqueSessionId, MediaRoute2Info route) { if (!checkArgumentsForSessionControl(clientRecord, uniqueSessionId, route, "transferring to")) { @@ -1215,7 +1200,7 @@ class MediaRouter2ServiceImpl { route.getOriginalId()); } - private boolean checkArgumentsForSessionControl(@NonNull Client2Record clientRecord, + private boolean checkArgumentsForSessionControl(@Nullable Client2Record clientRecord, String uniqueSessionId, MediaRoute2Info route, @NonNull String description) { if (route == null) { Slog.w(TAG, "Ignoring " + description + " null route"); @@ -1236,6 +1221,17 @@ class MediaRouter2ServiceImpl { return false; } + // Bypass checking client if it's the system session (clientRecord should be null) + if (TextUtils.equals(getProviderId(uniqueSessionId), mSystemProvider.getUniqueId())) { + return true; + } + + //TODO: Handle RCN case. + if (clientRecord == null) { + Slog.w(TAG, "Ignoring " + description + " route from unknown client."); + return false; + } + Client2Record matchingRecord = mSessionToClientMap.get(uniqueSessionId); if (matchingRecord != clientRecord) { Slog.w(TAG, "Ignoring " + description + " route from non-matching client. " 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/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index 6f6d8a1e8df4..558eb8d05783 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -64,7 +64,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { SystemMediaRoute2Provider.class.getPackageName$(), SystemMediaRoute2Provider.class.getName()); - //TODO: Clean up these when audio manager support multiple bt devices MediaRoute2Info mDefaultRoute; @NonNull List<MediaRoute2Info> mBluetoothRoutes = Collections.EMPTY_LIST; final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo(); @@ -91,6 +90,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { mAudioService = IAudioService.Stub.asInterface( ServiceManager.getService(Context.AUDIO_SERVICE)); + initializeDefaultRoute(); mBtRouteProvider = BluetoothRouteProvider.getInstance(context, (routes) -> { mBluetoothRoutes = routes; publishRoutes(); @@ -103,7 +103,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { notifySessionInfoUpdated(); } }); - initializeRoutes(); + initializeSessionInfo(); } @Override @@ -119,17 +119,21 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { @Override public void selectRoute(String sessionId, String routeId) { - //TODO: implement method + // Do nothing since we don't support multiple BT yet. } @Override public void deselectRoute(String sessionId, String routeId) { - //TODO: implement method + // Do nothing since we don't support multiple BT yet. } @Override public void transferToRoute(String sessionId, String routeId) { - //TODO: implement method + if (TextUtils.equals(routeId, mDefaultRoute.getId())) { + mBtRouteProvider.clearActiveDevices(); + } else { + mBtRouteProvider.setActiveDevice(routeId); + } } //TODO: implement method @@ -147,8 +151,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { public void requestUpdateVolume(String routeId, int delta) { } - void initializeRoutes() { - //TODO: adds necessary info + private void initializeDefaultRoute() { mDefaultRoute = new MediaRoute2Info.Builder( DEFAULT_ROUTE_ID, mContext.getResources().getText(R.string.default_audio_route_name).toString()) @@ -172,7 +175,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { // route yet. updateAudioRoutes(newAudioRoutes); } + } + private void initializeSessionInfo() { mBluetoothRoutes = mBtRouteProvider.getBluetoothRoutes(); MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(); @@ -183,11 +188,15 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { setProviderState(builder.build()); mHandler.post(() -> notifyProviderState()); - // Note: No lock needed when initializing. - updateSessionInfosIfNeededLocked(); + //TODO: clean up this + // This is required because it is not instantiated in the main thread and + // BluetoothRoutesUpdatedListener can be called before this function + synchronized (mLock) { + updateSessionInfosIfNeededLocked(); + } } - void updateAudioRoutes(AudioRoutesInfo newRoutes) { + private void updateAudioRoutes(AudioRoutesInfo newRoutes) { int name = R.string.default_audio_route_name; mCurAudioRoutesInfo.mainType = newRoutes.mainType; if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0 @@ -226,15 +235,22 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { .setSystemSession(true); String activeBtDeviceAddress = mBtRouteProvider.getActiveDeviceAddress(); - RoutingSessionInfo newSessionInfo; if (!TextUtils.isEmpty(activeBtDeviceAddress)) { // Bluetooth route. Set the route ID with the device's address. - newSessionInfo = builder.addSelectedRoute(activeBtDeviceAddress).build(); + builder.addSelectedRoute(activeBtDeviceAddress); + builder.addTransferrableRoute(mDefaultRoute.getId()); } else { // Default device - newSessionInfo = builder.addSelectedRoute(mDefaultRoute.getId()).build(); + builder.addSelectedRoute(mDefaultRoute.getId()); } + for (MediaRoute2Info route : mBluetoothRoutes) { + if (!TextUtils.equals(activeBtDeviceAddress, route.getId())) { + builder.addTransferrableRoute(route.getId()); + } + } + + RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build(); if (Objects.equals(oldSessionInfo, newSessionInfo)) { return false; } else { @@ -244,11 +260,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } } - /** - * The first route should be the currently selected system route. - * For example, if there are two system routes (BT and device speaker), - * BT will be the first route in the list. - */ void publishRoutes() { MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(); builder.addRoute(mDefaultRoute); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index d0e4fe6e8a40..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; @@ -378,8 +378,9 @@ public class NotificationManagerService extends SystemService { /** * Apps that post custom toasts in the background will have those blocked. Apps can - * still post toasts created with {@link Toast#makeText(Context, CharSequence, int)} and its - * variants while in the background. + * still post toasts created with + * {@link android.widget.Toast#makeText(Context, CharSequence, int)} and its variants while + * in the background. * * TODO(b/144152069): Add @EnabledAfter(Q) to target R+ after assessing impact on dogfood */ @@ -412,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; @@ -1752,7 +1753,7 @@ public class NotificationManagerService extends SystemService { } @VisibleForTesting - void setLights(Light light) { + void setLights(LogicalLight light) { mNotificationLight = light; mAttentionLight = light; mNotificationPulseEnabled = true; @@ -7874,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); } } @@ -8255,7 +8256,8 @@ public class NotificationManagerService extends SystemService { record.getSystemGeneratedSmartActions(), record.getSmartReplies(), record.canBubble(), - record.isInterruptive() + record.isInterruptive(), + record.isConversation() ); rankings.add(ranking); } diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index f344a8464f10..2bea21891f8f 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -54,6 +54,7 @@ import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.util.ArraySet; +import android.util.FeatureFlagUtils; import android.util.Log; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; @@ -164,6 +165,7 @@ public final class NotificationRecord { private boolean mShowBadge; private boolean mAllowBubble; private Light mLight; + private boolean mIsNotConversationOverride; /** * This list contains system generated smart actions from NAS, app-generated smart actions are * stored in Notification.actions with isContextual() set to true. @@ -660,6 +662,10 @@ public final class NotificationRecord { if (signals.containsKey(Adjustment.KEY_RANKING_SCORE)) { mRankingScore = signals.getFloat(Adjustment.KEY_RANKING_SCORE); } + if (signals.containsKey(Adjustment.KEY_NOT_CONVERSATION)) { + mIsNotConversationOverride = signals.getBoolean( + Adjustment.KEY_NOT_CONVERSATION); + } if (!signals.isEmpty() && adjustment.getIssuer() != null) { mAdjustmentIssuer = adjustment.getIssuer(); } @@ -1310,6 +1316,25 @@ public final class NotificationRecord { return hasCustomRemoteView && !hasDecoratedStyle; } + /** Whether this notification is a conversation notification. */ + public boolean isConversation() { + Notification notification = getNotification(); + if (mChannel.isDemoted() + || !Notification.MessagingStyle.class.equals(notification.getNotificationStyle())) { + return false; + } + if (notification.getShortcutId() == null + && !FeatureFlagUtils.isEnabled( + mContext, FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ)) { + return false; + } + if (mIsNotConversationOverride) { + return false; + } + // STOPSHIP b/137397357: Check shortcut to make a further decision + return true; + } + @VisibleForTesting static final class Light { public final int color; diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 28079099469a..c6d2b334bd71 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -66,7 +66,7 @@ import java.util.stream.Collectors; * ApexManager class handles communications with the apex service to perform operation and queries, * as well as providing caching to avoid unnecessary calls to the service. */ -abstract class ApexManager { +public abstract class ApexManager { private static final String TAG = "ApexManager"; @@ -265,6 +265,13 @@ abstract class ApexManager { abstract List<String> getApksInApex(String apexPackageName); /** + * Returns the apex module name for the given package name, if the package is an APEX. Otherwise + * returns {@code null}. + */ + @Nullable + public abstract String getApexModuleNameForPackageName(String apexPackageName); + + /** * Dumps various state information to the provided {@link PrintWriter} object. * * @param pw the {@link PrintWriter} object to send information to. @@ -646,6 +653,15 @@ abstract class ApexManager { } } + @Override + @Nullable + public String getApexModuleNameForPackageName(String apexPackageName) { + populatePackageNameToApexModuleNameIfNeeded(); + synchronized (mLock) { + return mPackageNameToApexModuleName.get(apexPackageName); + } + } + /** * Dump information about the packages contained in a particular cache * @param packagesCache the cache to print information about. @@ -843,6 +859,12 @@ abstract class ApexManager { } @Override + @Nullable + public String getApexModuleNameForPackageName(String apexPackageName) { + return null; + } + + @Override void dump(PrintWriter pw, String packageName) { // No-op } diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 3ed353483068..0f71959f8f41 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -213,6 +213,25 @@ public class AppsFilter { return false; } + private static boolean canQueryViaPackage(AndroidPackage querying, + AndroidPackage potentialTarget) { + return querying.getQueriesPackages() != null + && querying.getQueriesPackages().contains(potentialTarget.getPackageName()); + } + + private static boolean canQueryAsInstaller(PackageSetting querying, + AndroidPackage potentialTarget) { + final InstallSource installSource = querying.installSource; + if (potentialTarget.getPackageName().equals(installSource.installerPackageName)) { + return true; + } + if (!installSource.isInitiatingPackageUninstalled + && potentialTarget.getPackageName().equals(installSource.initiatingPackageName)) { + return true; + } + return false; + } + private static boolean matches(Intent intent, AndroidPackage potentialTarget) { for (int p = ArrayUtils.size(potentialTarget.getProviders()) - 1; p >= 0; p--) { ParsedProvider provider = potentialTarget.getProviders().get(p); @@ -331,8 +350,8 @@ public class AppsFilter { if (canQueryViaIntent(existingPkg, newPkg)) { mQueriesViaIntent.add(existingSetting.appId, newPkgSetting.appId); } - if (existingPkg.getQueriesPackages() != null - && existingPkg.getQueriesPackages().contains(newPkg.getPackageName())) { + if (canQueryViaPackage(existingPkg, newPkg) + || canQueryAsInstaller(existingSetting, newPkg)) { mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId); } } @@ -341,8 +360,8 @@ public class AppsFilter { if (canQueryViaIntent(newPkg, existingPkg)) { mQueriesViaIntent.add(newPkgSetting.appId, existingSetting.appId); } - if (newPkg.getQueriesPackages() != null - && newPkg.getQueriesPackages().contains(existingPkg.getPackageName())) { + if (canQueryViaPackage(newPkg, existingPkg) + || canQueryAsInstaller(newPkgSetting, existingPkg)) { mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId); } } @@ -492,6 +511,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; @@ -535,7 +558,6 @@ public class AppsFilter { try { Trace.beginSection("mQueriesViaPackage"); if (mQueriesViaPackage.contains(callingAppId, targetAppId)) { - // the calling package has explicitly declared the target package; allow if (DEBUG_LOGGING) { log(callingSetting, targetPkgSetting, "queries package"); } diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java index 3635004987e8..74d2efeceb63 100644 --- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java @@ -173,6 +173,50 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } @Override + public void startActivityAsUserByIntent( + IApplicationThread caller, + String callingPackage, + Intent intent, + @UserIdInt int userId) throws RemoteException { + Objects.requireNonNull(callingPackage); + Objects.requireNonNull(intent); + Objects.requireNonNull(intent.getComponent(), "The intent must have a Component set"); + + verifyCallingPackage(callingPackage); + + final int callerUserId = mInjector.getCallingUserId(); + final int callingUid = mInjector.getCallingUid(); + + List<UserHandle> allowedTargetUsers = getTargetUserProfilesUnchecked( + callingPackage, callerUserId); + if (callerUserId != userId && !allowedTargetUsers.contains(UserHandle.of(userId))) { + throw new SecurityException(callingPackage + " cannot access unrelated user " + userId); + } + + Intent launchIntent = new Intent(intent); + launchIntent.setPackage(callingPackage); + + if (!callingPackage.equals(launchIntent.getComponent().getPackageName())) { + throw new SecurityException( + callingPackage + " attempts to start an activity in other package - " + + launchIntent.getComponent().getPackageName()); + } + + if (callerUserId != userId) { + if (!hasInteractAcrossProfilesPermission(callingPackage)) { + throw new SecurityException("Attempt to launch activity without required " + + android.Manifest.permission.INTERACT_ACROSS_PROFILES + " permission" + + " or target user is not in the same profile group."); + } + } + + verifyActivityCanHandleIntent(launchIntent, callingUid, userId); + + mInjector.getActivityTaskManagerInternal().startActivityAsUser( + caller, callingPackage, launchIntent, /* options= */ null, userId); + } + + @Override public boolean canRequestInteractAcrossProfiles(String callingPackage) { Objects.requireNonNull(callingPackage); verifyCallingPackage(callingPackage); @@ -214,6 +258,11 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { if (targetUserProfiles.isEmpty()) { return false; } + + return hasInteractAcrossProfilesPermission(callingPackage); + } + + private boolean hasInteractAcrossProfilesPermission(String callingPackage) { final int callingUid = mInjector.getCallingUid(); final int callingPid = mInjector.getCallingPid(); return isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid) @@ -237,21 +286,21 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } private List<UserHandle> getTargetUserProfilesUnchecked( - String callingPackage, @UserIdInt int callingUserId) { + String packageName, @UserIdInt int userId) { final long ident = mInjector.clearCallingIdentity(); try { final int[] enabledProfileIds = - mInjector.getUserManager().getEnabledProfileIds(callingUserId); + mInjector.getUserManager().getEnabledProfileIds(userId); List<UserHandle> targetProfiles = new ArrayList<>(); - for (final int userId : enabledProfileIds) { - if (userId == callingUserId) { + for (final int profileId : enabledProfileIds) { + if (profileId == userId) { continue; } - if (!isPackageEnabled(callingPackage, userId)) { + if (!isPackageEnabled(packageName, profileId)) { continue; } - targetProfiles.add(UserHandle.of(userId)); + targetProfiles.add(UserHandle.of(profileId)); } return targetProfiles; } finally { @@ -275,6 +324,27 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } } + private void verifyActivityCanHandleIntent( + Intent launchIntent, int callingUid, @UserIdInt int userId) { + final long ident = mInjector.clearCallingIdentity(); + try { + final List<ResolveInfo> activities = + mInjector.getPackageManagerInternal().queryIntentActivities( + launchIntent, + launchIntent.resolveTypeIfNeeded(mContext.getContentResolver()), + MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, + callingUid, + userId); + + if (!activities.isEmpty()) { + return; + } + throw new SecurityException("Activity cannot handle intent"); + } finally { + mInjector.restoreCallingIdentity(ident); + } + } + /** * Verify that the specified intent does resolved to the specified component and the resolved * activity is exported. @@ -315,14 +385,9 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { "INTERACT_ACROSS_USERS or INTERACT_ACROSS_USERS_FULL is required to set the" + " app-op for interacting across profiles."); } - if (!isPermissionGranted(Manifest.permission.MANAGE_APP_OPS_MODES, callingUid)) { - throw new SecurityException( - "MANAGE_APP_OPS_MODES is required to set the app-op for interacting across" - + " profiles."); - } final int callingUserId = mInjector.getCallingUserId(); if (newMode == AppOpsManager.MODE_ALLOWED - && !canRequestInteractAcrossProfilesUnchecked(packageName, callingUserId)) { + && !canConfigureInteractAcrossProfiles(packageName)) { // The user should not be prompted for apps that cannot request to interact across // profiles. However, we return early here if required to avoid race conditions. Slog.e(TAG, "Tried to turn on the appop for interacting across profiles for invalid" @@ -371,7 +436,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { final int uid = mInjector.getPackageManager() .getPackageUidAsUser(packageName, /* flags= */ 0, userId); if (currentModeEquals(newMode, packageName, uid)) { - Slog.w(TAG,"Attempt to set mode to existing value of " + newMode + " for " + Slog.w(TAG, "Attempt to set mode to existing value of " + newMode + " for " + packageName + " on user ID " + userId); return; } @@ -392,18 +457,73 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { private void sendCanInteractAcrossProfilesChangedBroadcast( String packageName, int uid, UserHandle userHandle) { - final Intent intent = new Intent(ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED) - .setPackage(packageName); - if (!appDeclaresCrossProfileAttribute(uid)) { + final Intent intent = + new Intent(ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED).setPackage(packageName); + if (appDeclaresCrossProfileAttribute(uid)) { + intent.addFlags( + Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_RECEIVER_FOREGROUND); + } else { intent.addFlags(FLAG_RECEIVER_REGISTERED_ONLY); } - mInjector.sendBroadcastAsUser(intent, userHandle); + for (ResolveInfo receiver : findBroadcastReceiversForUser(intent, userHandle)) { + intent.setComponent(receiver.getComponentInfo().getComponentName()); + mInjector.sendBroadcastAsUser(intent, userHandle); + } + } + + private List<ResolveInfo> findBroadcastReceiversForUser(Intent intent, UserHandle userHandle) { + return mInjector.getPackageManager() + .queryBroadcastReceiversAsUser(intent, /* flags= */ 0, userHandle); } private boolean appDeclaresCrossProfileAttribute(int uid) { return mInjector.getPackageManagerInternal().getPackage(uid).isCrossProfile(); } + @Override + public boolean canConfigureInteractAcrossProfiles(String packageName) { + if (!hasOtherProfileWithPackageInstalled(packageName, mInjector.getCallingUserId())) { + return false; + } + if (!hasRequestedAppOpPermission( + AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName)) { + return false; + } + return isCrossProfilePackageWhitelisted(packageName); + } + + private boolean hasOtherProfileWithPackageInstalled(String packageName, @UserIdInt int userId) { + final long ident = mInjector.clearCallingIdentity(); + try { + final int[] profileIds = + mInjector.getUserManager().getProfileIds(userId, /* enabledOnly= */ false); + for (int profileId : profileIds) { + if (profileId != userId && isPackageInstalled(packageName, profileId)) { + return true; + } + } + } finally { + mInjector.restoreCallingIdentity(ident); + } + return false; + } + + @Override + public void resetInteractAcrossProfilesAppOps(List<String> packageNames) { + packageNames.forEach(this::resetInteractAcrossProfilesAppOp); + } + + private void resetInteractAcrossProfilesAppOp(String packageName) { + if (canConfigureInteractAcrossProfiles(packageName)) { + Slog.w(TAG, "Not resetting app-op for package " + packageName + + " since it is still configurable by users."); + return; + } + final String op = + AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES); + setInteractAcrossProfilesAppOp(packageName, AppOpsManager.opToDefaultMode(op)); + } + private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) { final long ident = mInjector.clearCallingIdentity(); try { diff --git a/services/core/java/com/android/server/pm/ModuleInfoProvider.java b/services/core/java/com/android/server/pm/ModuleInfoProvider.java index 69510d9e5565..06706cd06e11 100644 --- a/services/core/java/com/android/server/pm/ModuleInfoProvider.java +++ b/services/core/java/com/android/server/pm/ModuleInfoProvider.java @@ -57,9 +57,9 @@ public class ModuleInfoProvider { */ private static final String MODULE_METADATA_KEY = "android.content.pm.MODULE_METADATA"; - private final Context mContext; private final IPackageManager mPackageManager; + private final ApexManager mApexManager; private final Map<String, ModuleInfo> mModuleInfo; // TODO: Move this to an earlier boot phase if anybody requires it then. @@ -69,13 +69,16 @@ public class ModuleInfoProvider { ModuleInfoProvider(Context context, IPackageManager packageManager) { mContext = context; mPackageManager = packageManager; + mApexManager = ApexManager.getInstance(); mModuleInfo = new ArrayMap<>(); } @VisibleForTesting - public ModuleInfoProvider(XmlResourceParser metadata, Resources resources) { + public ModuleInfoProvider( + XmlResourceParser metadata, Resources resources, ApexManager apexManager) { mContext = null; mPackageManager = null; + mApexManager = apexManager; mModuleInfo = new ArrayMap<>(); loadModuleMetadata(metadata, resources); } @@ -150,6 +153,8 @@ public class ModuleInfoProvider { mi.setHidden(isHidden); mi.setPackageName(modulePackageName); mi.setName(moduleName); + mi.setApexModuleName( + mApexManager.getApexModuleNameForPackageName(modulePackageName)); mModuleInfo.put(modulePackageName, mi); } @@ -167,7 +172,7 @@ public class ModuleInfoProvider { * * @param flags Use {@link PackageManager#MATCH_ALL} flag to get all modules. */ - List<ModuleInfo> getInstalledModules(@PackageManager.ModuleInfoFlags int flags) { + List<ModuleInfo> getInstalledModules(@PackageManager.InstalledModulesFlags int flags) { if (!mMetadataLoaded) { throw new IllegalStateException("Call to getInstalledModules before metadata loaded"); } @@ -195,12 +200,19 @@ public class ModuleInfoProvider { return installedModules; } - ModuleInfo getModuleInfo(String packageName, int flags) { + ModuleInfo getModuleInfo(String name, @PackageManager.ModuleInfoFlags int flags) { if (!mMetadataLoaded) { throw new IllegalStateException("Call to getModuleInfo before metadata loaded"); } - - return mModuleInfo.get(packageName); + if ((flags & PackageManager.MODULE_APEX_NAME) != 0) { + for (ModuleInfo moduleInfo : mModuleInfo.values()) { + if (name.equals(moduleInfo.getApexModuleName())) { + return moduleInfo; + } + } + return null; + } + return mModuleInfo.get(name); } String getPackageName() { diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index b1c38d1ebed4..10f46fd808c8 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -259,11 +259,13 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements // atomic install which needs to query sessions, which requires lock on mSessions. boolean isDeviceUpgrading = mPm.isDeviceUpgrading(); for (PackageInstallerSession session : stagedSessionsToRestore) { - if (isDeviceUpgrading && !session.isStagedAndInTerminalState()) { + if (!session.isStagedAndInTerminalState() && session.hasParentSessionId() + && getSession(session.getParentSessionId()) == null) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, - "Build fingerprint has changed"); + "An orphan staged session " + session.sessionId + " is found, " + + "parent " + session.getParentSessionId() + " is missing"); } - mStagingManager.restoreSession(session); + mStagingManager.restoreSession(session, isDeviceUpgrading); } // Broadcasts are not sent while we restore sessions on boot, since no processes would be // ready to listen to them. From now on, we greedily assume that broadcasts requests are @@ -400,10 +402,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } finally { IoUtils.closeQuietly(fis); } - // Re-sealing the sealed sessions. + // After all of the sessions were loaded, they are ready to be sealed and validated for (int i = 0; i < mSessions.size(); ++i) { PackageInstallerSession session = mSessions.valueAt(i); - session.sealIfNecessary(); + session.sealAndValidateIfNecessary(); } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 78875da9e790..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; @@ -1360,13 +1370,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } /** - * If session should be sealed, then it's sealed to prevent further modification. - * If the session can't be sealed then it's destroyed. + * If session should be sealed, then it's sealed to prevent further modification + * and then it's validated. + * + * If the session was sealed but something went wrong then it's destroyed. * * <p> This is meant to be called after all of the sessions are loaded and added to * PackageInstallerService */ - void sealIfNecessary() { + void sealAndValidateIfNecessary() { synchronized (mLock) { if (!mShouldBeSealed || isStagedAndInTerminalState()) { return; @@ -1375,7 +1387,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { List<PackageInstallerSession> childSessions = getChildSessions(); synchronized (mLock) { try { - sealLocked(childSessions); + sealAndValidateLocked(childSessions); + } catch (StreamingException e) { + Slog.e(TAG, "Streaming failed", e); } catch (PackageManagerException e) { Slog.e(TAG, "Package not valid", e); } @@ -2321,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); @@ -2334,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."); @@ -2352,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))); } } @@ -2887,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); } } @@ -3020,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 07aec4ad41d4..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, @@ -13494,6 +13496,7 @@ public class PackageManagerService extends IPackageManager.Stub // Okay! targetPackageSetting.setInstallerPackageName(installerPackageName); mSettings.addInstallerPackageNames(targetPackageSetting.installSource); + mAppsFilter.addPackage(targetPackageSetting, mSettings.mPackages); scheduleWriteSettingsLocked(); } } @@ -17696,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. @@ -23486,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(); } @@ -23740,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 ad4f6ff2b003..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; @@ -1227,7 +1228,7 @@ class PackageManagerShellCommand extends ShellCommand { } abandonSession = false; - if (!params.sessionParams.isStaged || !params.waitForStagedSessionReady) { + if (!params.sessionParams.isStaged || !params.mWaitForStagedSessionReady) { pw.println("Success"); return 0; } @@ -1265,7 +1266,7 @@ class PackageManagerShellCommand extends ShellCommand { + si.getStagedSessionErrorMessage() + "]"); return 1; } - pw.println("Success"); + pw.println("Success. Reboot device to apply staged session"); return 0; } finally { if (abandonSession) { @@ -2615,7 +2616,7 @@ class PackageManagerShellCommand extends ShellCommand { SessionParams sessionParams; String installerPackageName; int userId = UserHandle.USER_ALL; - boolean waitForStagedSessionReady = false; + boolean mWaitForStagedSessionReady = true; long timeoutMs = DEFAULT_WAIT_MS; } @@ -2743,13 +2744,16 @@ class PackageManagerShellCommand extends ShellCommand { sessionParams.installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK; break; case "--wait": - params.waitForStagedSessionReady = true; + params.mWaitForStagedSessionReady = true; try { params.timeoutMs = Long.parseLong(peekNextArg()); getNextArg(); } catch (NumberFormatException ignore) { } break; + case "--no-wait": + params.mWaitForStagedSessionReady = false; + break; default: throw new IllegalArgumentException("Unknown option " + opt); } @@ -2967,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; } @@ -2991,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; } @@ -3001,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/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/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 7888d1f9612f..74c98f9ef444 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -889,7 +889,7 @@ public class StagingManager { return false; } - void restoreSession(@NonNull PackageInstallerSession session) { + void restoreSession(@NonNull PackageInstallerSession session, boolean isDeviceUpgrading) { PackageInstallerSession sessionToResume = session; synchronized (mStagedSessions) { mStagedSessions.append(session.sessionId, session); @@ -906,6 +906,13 @@ public class StagingManager { } } } + // The preconditions used during pre-reboot verification might have changed when device + // is upgrading. Updated staged sessions to activation failed before we resume the session. + if (isDeviceUpgrading && !sessionToResume.isStagedAndInTerminalState()) { + sessionToResume.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, + "Build fingerprint has changed"); + return; + } checkStateAndResume(sessionToResume); } 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/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index 8460ede91fd0..df8e30f9387a 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -46,6 +46,7 @@ import android.os.VibrationEffect; import android.os.Vibrator; import android.os.WorkSource; import android.provider.Settings; +import android.telephony.TelephonyManager; import android.util.EventLog; import android.util.Slog; import android.util.StatsLog; @@ -671,6 +672,8 @@ public class Notifier { } mUserActivityPending = false; } + TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); + tm.notifyUserActivity(); mPolicy.userActivity(); } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index c1b71aab38fd..3f3a13368ffc 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -16,6 +16,8 @@ package com.android.server.power; +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; @@ -72,6 +74,7 @@ import android.provider.Settings.SettingNotFoundException; import android.service.dreams.DreamManagerInternal; import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; +import android.util.ArraySet; import android.util.KeyValueListParser; import android.util.PrintWriterPrinter; import android.util.Slog; @@ -93,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; @@ -109,6 +112,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.Set; /** * The power manager service is responsible for coordinating power management @@ -253,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; @@ -565,6 +569,9 @@ public final class PowerManagerService extends SystemService // but the DreamService has not yet been told to start (it's an async process). private boolean mDozeStartInProgress; + // Set of all tokens suppressing ambient display. + private final Set<String> mAmbientDisplaySuppressionTokens = new ArraySet<>(); + private final class ForegroundProfileObserver extends SynchronousUserSwitchObserver { @Override public void onUserSwitching(@UserIdInt int newUserId) throws RemoteException { @@ -844,7 +851,8 @@ public final class PowerManagerService extends SystemService @Override public void onStart() { - publishBinderService(Context.POWER_SERVICE, mBinderService); + publishBinderService(Context.POWER_SERVICE, mBinderService, /* allowIsolated= */ false, + DUMP_FLAG_PRIORITY_DEFAULT | DUMP_FLAG_PRIORITY_CRITICAL); publishLocalService(PowerManagerInternal.class, mLocalService); Watchdog.getInstance().addMonitor(this); @@ -3339,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; @@ -3348,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) { @@ -3357,6 +3365,26 @@ public final class PowerManagerService extends SystemService } } + private void suppressAmbientDisplayInternal(String token, boolean suppress) { + if (DEBUG_SPEW) { + Slog.d(TAG, "Suppress ambient display for token " + token + ": " + suppress); + } + + if (suppress) { + mAmbientDisplaySuppressionTokens.add(token); + } else { + mAmbientDisplaySuppressionTokens.remove(token); + } + + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.SUPPRESS_DOZE, + Math.min(mAmbientDisplaySuppressionTokens.size(), 1)); + } + + private String createAmbientDisplayToken(String token, int callingUid) { + return callingUid + "_" + token; + } + private void boostScreenBrightnessInternal(long eventTime, int uid) { synchronized (mLock) { if (!mSystemReady || mWakefulness == WAKEFULNESS_ASLEEP @@ -5007,6 +5035,61 @@ public final class PowerManagerService extends SystemService } @Override // Binder call + public boolean isAmbientDisplayAvailable() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.READ_DREAM_STATE, null); + + final long ident = Binder.clearCallingIdentity(); + try { + return mAmbientDisplayConfiguration.ambientDisplayAvailable(); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call + public void suppressAmbientDisplay(@NonNull String token, boolean suppress) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.WRITE_DREAM_STATE, null); + + final int uid = Binder.getCallingUid(); + final long ident = Binder.clearCallingIdentity(); + try { + suppressAmbientDisplayInternal(createAmbientDisplayToken(token, uid), suppress); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call + public boolean isAmbientDisplaySuppressedForToken(@NonNull String token) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.READ_DREAM_STATE, null); + + final int uid = Binder.getCallingUid(); + final long ident = Binder.clearCallingIdentity(); + try { + return mAmbientDisplaySuppressionTokens.contains( + createAmbientDisplayToken(token, uid)); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call + public boolean isAmbientDisplaySuppressed() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.READ_DREAM_STATE, null); + + final long ident = Binder.clearCallingIdentity(); + try { + return mAmbientDisplaySuppressionTokens.size() > 0; + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call public void boostScreenBrightness(long eventTime) { if (eventTime > SystemClock.uptimeMillis()) { throw new IllegalArgumentException("event time must not be in the future"); diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java index 6686de95c05c..951f1a4663f1 100644 --- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -28,6 +28,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.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; @@ -45,20 +46,21 @@ import android.util.ArraySet; import android.util.Slog; import android.util.StatsLog; -import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.server.PackageWatchdog; import com.android.server.PackageWatchdog.FailureReasons; import com.android.server.PackageWatchdog.PackageHealthObserver; import com.android.server.PackageWatchdog.PackageHealthObserverImpact; -import libcore.io.IoUtils; - +import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; +import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Set; @@ -72,11 +74,12 @@ import java.util.Set; public final class RollbackPackageHealthObserver implements PackageHealthObserver { private static final String TAG = "RollbackPackageHealthObserver"; private static final String NAME = "rollback-observer"; - private static final int INVALID_ROLLBACK_ID = -1; + + private static final String LOGGING_PARENT_KEY = "android.content.pm.LOGGING_PARENT"; private final Context mContext; private final Handler mHandler; - private final File mLastStagedRollbackIdFile; + private final File mLastStagedRollbackIdsFile; // Staged rollback ids that have been committed but their session is not yet ready @GuardedBy("mPendingStagedRollbackIds") private final Set<Integer> mPendingStagedRollbackIds = new ArraySet<>(); @@ -88,7 +91,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve mHandler = handlerThread.getThreadHandler(); File dataDir = new File(Environment.getDataDirectory(), "rollback-observer"); dataDir.mkdirs(); - mLastStagedRollbackIdFile = new File(dataDir, "last-staged-rollback-id"); + mLastStagedRollbackIdsFile = new File(dataDir, "last-staged-rollback-ids"); PackageWatchdog.getInstance(mContext).registerHealthObserver(this); } @@ -150,22 +153,25 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve private void onBootCompleted() { RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); - PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller(); - String moduleMetadataPackageName = getModuleMetadataPackageName(); - if (!rollbackManager.getAvailableRollbacks().isEmpty()) { // TODO(gavincorkery): Call into Package Watchdog from outside the observer PackageWatchdog.getInstance(mContext).scheduleCheckAndMitigateNativeCrashes(); } - int rollbackId = popLastStagedRollbackId(); - if (rollbackId == INVALID_ROLLBACK_ID) { - // No staged rollback before reboot - return; + List<Integer> rollbackIds = popLastStagedRollbackIds(); + Iterator<Integer> rollbackIterator = rollbackIds.iterator(); + while (rollbackIterator.hasNext()) { + int rollbackId = rollbackIterator.next(); + logRollbackStatusOnBoot(rollbackId, rollbackManager.getRecentlyCommittedRollbacks()); } + } + + private void logRollbackStatusOnBoot(int rollbackId, + List<RollbackInfo> recentlyCommittedRollbacks) { + PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller(); RollbackInfo rollback = null; - for (RollbackInfo info : rollbackManager.getRecentlyCommittedRollbacks()) { + for (RollbackInfo info : recentlyCommittedRollbacks) { if (rollbackId == info.getRollbackId()) { rollback = info; break; @@ -177,13 +183,26 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve return; } - // Use the version of the metadata package that was installed before + // Identify the logging parent for this rollback. When all configurations are correct, each + // package in the rollback refers to the same logging parent, except for the logging parent + // itself. If a logging parent is missing for a package, we use the package itself for + // logging. This might result in over-logging, but we prefer this over no logging. + final Set<String> loggingPackageNames = new ArraySet<>(); + for (PackageRollbackInfo packageRollback : rollback.getPackages()) { + final String loggingParentName = getLoggingParentName(packageRollback.getPackageName()); + if (loggingParentName != null) { + loggingPackageNames.add(loggingParentName); + } else { + loggingPackageNames.add(packageRollback.getPackageName()); + } + } + + // Use the version of the logging parent that was installed before // we rolled back for logging purposes. - VersionedPackage oldLogPackage = null; + final List<VersionedPackage> oldLoggingPackages = new ArrayList<>(); for (PackageRollbackInfo packageRollback : rollback.getPackages()) { - if (packageRollback.getPackageName().equals(moduleMetadataPackageName)) { - oldLogPackage = packageRollback.getVersionRolledBackFrom(); - break; + if (loggingPackageNames.contains(packageRollback.getPackageName())) { + oldLoggingPackages.add(packageRollback.getVersionRolledBackFrom()); } } @@ -193,16 +212,17 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve Slog.e(TAG, "On boot completed, could not load session id " + sessionId); return; } - if (sessionInfo.isStagedSessionApplied()) { - logEvent(oldLogPackage, - StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, - WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, ""); - } else if (sessionInfo.isStagedSessionReady()) { - // TODO: What do for staged session ready but not applied - } else { - logEvent(oldLogPackage, - StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, - WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, ""); + + for (VersionedPackage oldLoggingPackage : oldLoggingPackages) { + if (sessionInfo.isStagedSessionApplied()) { + logEvent(oldLoggingPackage, + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, + WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, ""); + } else if (sessionInfo.isStagedSessionFailed()) { + logEvent(oldLoggingPackage, + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, + WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, ""); + } } } @@ -236,27 +256,17 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } @Nullable - private String getModuleMetadataPackageName() { - String packageName = mContext.getResources().getString( - R.string.config_defaultModuleMetadataProvider); - if (TextUtils.isEmpty(packageName)) { - return null; - } - return packageName; - } - - @Nullable - private VersionedPackage getModuleMetadataPackage() { - String packageName = getModuleMetadataPackageName(); - if (packageName == null) { - return null; - } - + private String getLoggingParentName(String packageName) { + PackageManager packageManager = mContext.getPackageManager(); try { - return new VersionedPackage(packageName, mContext.getPackageManager().getPackageInfo( - packageName, 0 /* flags */).getLongVersionCode()); - } catch (PackageManager.NameNotFoundException e) { - Slog.w(TAG, "Module metadata provider not found"); + ApplicationInfo ai = packageManager.getApplicationInfo(packageName, + PackageManager.GET_META_DATA); + if (ai.metaData == null) { + return null; + } + return ai.metaData.getString(LOGGING_PARENT_KEY); + } catch (Exception e) { + Slog.w(TAG, "Unable to discover logging parent package: " + packageName, e); return null; } } @@ -294,7 +304,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve if (logPackage != null) { // We save the rollback id so that after reboot, we can log if rollback was // successful or not. If logPackage is null, then there is nothing to log. - saveLastStagedRollbackId(rollbackId); + saveStagedRollbackId(rollbackId); } logEvent(logPackage, StatsLog @@ -338,34 +348,39 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } } - private void saveLastStagedRollbackId(int stagedRollbackId) { + private void saveStagedRollbackId(int stagedRollbackId) { try { - FileOutputStream fos = new FileOutputStream(mLastStagedRollbackIdFile); + FileOutputStream fos = new FileOutputStream( + mLastStagedRollbackIdsFile, /*append*/true); PrintWriter pw = new PrintWriter(fos); - pw.println(stagedRollbackId); + pw.append(",").append(String.valueOf(stagedRollbackId)); pw.flush(); FileUtils.sync(fos); pw.close(); } catch (IOException e) { Slog.e(TAG, "Failed to save last staged rollback id", e); - mLastStagedRollbackIdFile.delete(); + mLastStagedRollbackIdsFile.delete(); } } - private int popLastStagedRollbackId() { - int rollbackId = INVALID_ROLLBACK_ID; - if (!mLastStagedRollbackIdFile.exists()) { - return rollbackId; - } - - try { - rollbackId = Integer.parseInt( - IoUtils.readFileAsString(mLastStagedRollbackIdFile.getAbsolutePath()).trim()); - } catch (IOException | NumberFormatException e) { - Slog.e(TAG, "Failed to retrieve last staged rollback id", e); + private List<Integer> popLastStagedRollbackIds() { + try (BufferedReader reader = + new BufferedReader(new FileReader(mLastStagedRollbackIdsFile))) { + String line = reader.readLine(); + // line is of format : ",id1,id2,id3....,idn" + String[] sessionIdsStr = line.split(","); + ArrayList<Integer> result = new ArrayList<>(); + for (String sessionIdStr: sessionIdsStr) { + if (!TextUtils.isEmpty(sessionIdStr.trim())) { + result.add(Integer.parseInt(sessionIdStr)); + } + } + return result; + } catch (Exception ignore) { + return Collections.emptyList(); + } finally { + mLastStagedRollbackIdsFile.delete(); } - mLastStagedRollbackIdFile.delete(); - return rollbackId; } private static String rollbackTypeToString(int type) { @@ -383,16 +398,33 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } } + private static String rollbackReasonToString(int reason) { + switch (reason) { + case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH: + return "REASON_NATIVE_CRASH"; + case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK: + return "REASON_EXPLICIT_HEALTH_CHECK"; + case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH: + return "REASON_APP_CRASH"; + case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING: + return "REASON_APP_NOT_RESPONDING"; + default: + return "UNKNOWN"; + } + } + private static void logEvent(@Nullable VersionedPackage logPackage, int type, int rollbackReason, @NonNull String failingPackageName) { - Slog.i(TAG, "Watchdog event occurred of type: " + rollbackTypeToString(type)); + Slog.i(TAG, "Watchdog event occurred with type: " + rollbackTypeToString(type) + + " logPackage: " + logPackage + + " rollbackReason: " + rollbackReasonToString(rollbackReason) + + " failedPackageName: " + failingPackageName); if (logPackage != null) { StatsLog.logWatchdogRollbackOccurred(type, logPackage.getPackageName(), - logPackage.getVersionCode(), rollbackReason, failingPackageName); + logPackage.getLongVersionCode(), rollbackReason, failingPackageName); } } - /** * Returns true if the package name is the name of a module. */ @@ -422,10 +454,21 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } else { failedPackageToLog = failedPackage.getPackageName(); } - final VersionedPackage logPackage = isModule(failedPackage.getPackageName()) - ? getModuleMetadataPackage() - : null; + VersionedPackage logPackageTemp = null; + if (isModule(failedPackage.getPackageName())) { + String logPackageName = getLoggingParentName(failedPackage.getPackageName()); + if (logPackageName == null) { + logPackageName = failedPackage.getPackageName(); + } + try { + logPackageTemp = new VersionedPackage(logPackageName, mContext.getPackageManager() + .getPackageInfo(logPackageName, 0 /* flags */).getLongVersionCode()); + } catch (PackageManager.NameNotFoundException e) { + logPackageTemp = null; + } + } + final VersionedPackage logPackage = logPackageTemp; logEvent(logPackage, StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE, reasonToLog, failedPackageToLog); diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java index adf16fafc000..372c1f501d7b 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java @@ -174,7 +174,17 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient { for (Session session : mActiveSessions) { session.moduleDied(); } + reset(); + } + + /** + * Resets the transient state of this object. + */ + private void reset() { attachToHal(); + // We conservatively assume that external capture is active until explicitly told otherwise. + mRecognitionAvailable = mProperties.concurrentCapture; + mNumLoadedModels = 0; } /** diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 3c41c1077e5c..a0e6be4d9a6b 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -2274,20 +2274,104 @@ public class StatsPullAtomService extends SystemService { return StatsManager.PULL_SUCCESS; } + private final Object mDebugElapsedClockLock = new Object(); + private long mDebugElapsedClockPreviousValue = 0; + private long mDebugElapsedClockPullCount = 0; + private void registerDebugElapsedClock() { - // No op. + int tagId = StatsLog.DEBUG_ELAPSED_CLOCK; + PullAtomMetadata metadata = PullAtomMetadata.newBuilder() + .setAdditiveFields(new int[] {1, 2, 3, 4}) + .build(); + mStatsManager.registerPullAtomCallback( + tagId, + metadata, + (atomTag, data) -> pullDebugElapsedClock(atomTag, data), + BackgroundThread.getExecutor() + ); } - private void pullDebugElapsedClock() { - // No op. + private int pullDebugElapsedClock(int atomTag, List<StatsEvent> pulledData) { + final long elapsedMillis = SystemClock.elapsedRealtime(); + + synchronized (mDebugElapsedClockLock) { + final long clockDiffMillis = mDebugElapsedClockPreviousValue == 0 + ? 0 : elapsedMillis - mDebugElapsedClockPreviousValue; + + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeLong(mDebugElapsedClockPullCount) + .writeLong(elapsedMillis) + // Log it twice to be able to test multi-value aggregation from ValueMetric. + .writeLong(elapsedMillis) + .writeLong(clockDiffMillis) + .writeInt(1 /* always set */) + .build(); + pulledData.add(e); + + if (mDebugElapsedClockPullCount % 2 == 1) { + StatsEvent e2 = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeLong(mDebugElapsedClockPullCount) + .writeLong(elapsedMillis) + // Log it twice to be able to test multi-value aggregation from ValueMetric. + .writeLong(elapsedMillis) + .writeLong(clockDiffMillis) + .writeInt(2 /* set on odd pulls */) + .build(); + pulledData.add(e2); + } + + mDebugElapsedClockPullCount++; + mDebugElapsedClockPreviousValue = elapsedMillis; + } + + return StatsManager.PULL_SUCCESS; } + private final Object mDebugFailingElapsedClockLock = new Object(); + private long mDebugFailingElapsedClockPreviousValue = 0; + private long mDebugFailingElapsedClockPullCount = 0; + private void registerDebugFailingElapsedClock() { - // No op. + int tagId = StatsLog.DEBUG_FAILING_ELAPSED_CLOCK; + PullAtomMetadata metadata = PullAtomMetadata.newBuilder() + .setAdditiveFields(new int[] {1, 2, 3, 4}) + .build(); + mStatsManager.registerPullAtomCallback( + tagId, + metadata, + (atomTag, data) -> pullDebugFailingElapsedClock(atomTag, data), + BackgroundThread.getExecutor() + ); } - private void pullDebugFailingElapsedClock() { - // No op. + private int pullDebugFailingElapsedClock(int atomTag, List<StatsEvent> pulledData) { + final long elapsedMillis = SystemClock.elapsedRealtime(); + + synchronized (mDebugFailingElapsedClockLock) { + // Fails every 5 buckets. + if (mDebugFailingElapsedClockPullCount++ % 5 == 0) { + mDebugFailingElapsedClockPreviousValue = elapsedMillis; + Slog.e(TAG, "Failing debug elapsed clock"); + return StatsManager.PULL_SKIP; + } + + StatsEvent e = StatsEvent.newBuilder() + .setAtomId(atomTag) + .writeLong(mDebugFailingElapsedClockPullCount) + .writeLong(elapsedMillis) + // Log it twice to be able to test multi-value aggregation from ValueMetric. + .writeLong(elapsedMillis) + .writeLong(mDebugFailingElapsedClockPreviousValue == 0 + ? 0 : elapsedMillis - mDebugFailingElapsedClockPreviousValue) + .build(); + pulledData.add(e); + + mDebugFailingElapsedClockPreviousValue = elapsedMillis; + } + + return StatsManager.PULL_SUCCESS; } private void registerBuildInformation() { diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 6ea274d8a814..8f71943129fa 100755 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -44,6 +44,7 @@ import android.media.tv.ITvInputHardware; import android.media.tv.ITvInputHardwareCallback; import android.media.tv.TvInputHardwareInfo; import android.media.tv.TvInputInfo; +import android.media.tv.TvInputService.PriorityHintUseCaseType; import android.media.tv.TvStreamConfig; import android.os.Handler; import android.os.IBinder; @@ -363,7 +364,8 @@ class TvInputHardwareManager implements TvInputHal.Callback { * release is notified via ITvInputHardwareCallback.onReleased(). */ public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback, - TvInputInfo info, int callingUid, int resolvedUserId) { + TvInputInfo info, int callingUid, int resolvedUserId, + String tvInputSessionId, @PriorityHintUseCaseType int priorityHint) { if (callback == null) { throw new NullPointerException(); } @@ -373,6 +375,8 @@ class TvInputHardwareManager implements TvInputHal.Callback { Slog.e(TAG, "Invalid deviceId : " + deviceId); return null; } + // TODO: check with TRM to compare the client's priority with the current holder's + // priority. If lower, do nothing. if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) { TvInputHardwareImpl hardware = new TvInputHardwareImpl(connection.getHardwareInfoLocked()); diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index eb7c5caa62aa..e8704ab789f2 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -1736,8 +1736,9 @@ public final class TvInputManagerService extends SystemService { @Override public ITvInputHardware acquireTvInputHardware(int deviceId, - ITvInputHardwareCallback callback, TvInputInfo info, int userId) - throws RemoteException { + ITvInputHardwareCallback callback, TvInputInfo info, int userId, + String tvInputSessionId, + @TvInputService.PriorityHintUseCaseType int priorityHint) throws RemoteException { if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) != PackageManager.PERMISSION_GRANTED) { return null; @@ -1749,7 +1750,8 @@ public final class TvInputManagerService extends SystemService { userId, "acquireTvInputHardware"); try { return mTvInputHardwareManager.acquireHardware( - deviceId, callback, info, callingUid, resolvedUserId); + deviceId, callback, info, callingUid, resolvedUserId, + tvInputSessionId, priorityHint); } finally { Binder.restoreCallingIdentity(identity); } 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 03139d2e5e03..36e97755ab8a 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -2180,6 +2180,47 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } + /** + * Called when the wallpaper needs to zoom out. + * + * @param zoom from 0 to 1 (inclusive) where 1 means fully zoomed out, 0 means fully zoomed in. + * @param callingPackage package name calling this API. + * @param displayId id of the display whose zoom is updating. + */ + public void setWallpaperZoomOut(float zoom, String callingPackage, int displayId) { + if (!isWallpaperSupported(callingPackage)) { + return; + } + synchronized (mLock) { + if (!isValidDisplay(displayId)) { + throw new IllegalArgumentException("Cannot find display with id=" + displayId); + } + int userId = UserHandle.getCallingUserId(); + if (mCurrentUserId != userId) { + return; // Don't change the properties now + } + WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM); + if (zoom < 0 || zoom > 1f) { + throw new IllegalArgumentException("zoom must be between 0 and one: " + zoom); + } + + if (wallpaper.connection != null) { + final WallpaperConnection.DisplayConnector connector = wallpaper.connection + .getDisplayConnectorOrCreate(displayId); + final IWallpaperEngine engine = connector != null ? connector.mEngine : null; + if (engine != null) { + try { + engine.setZoomOut(zoom); + } catch (RemoteException e) { + if (DEBUG) { + Slog.w(TAG, "Couldn't set wallpaper zoom", e); + } + } + } + } + } + } + @Deprecated @Override public ParcelFileDescriptor getWallpaper(String callingPkg, IWallpaperManagerCallback cb, diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 2c325e56cebe..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; @@ -1958,7 +1955,7 @@ public class DisplayPolicy { } } else if (type == TYPE_WALLPAPER) { layoutWallpaper(displayFrames, pf, df, cf); - } else if (win == mStatusBar) { + } else if (win == mStatusBar || type == TYPE_NOTIFICATION_SHADE) { df.set(displayFrames.mUnrestricted); pf.set(displayFrames.mUnrestricted); cf.set(displayFrames.mStable); 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/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index d3da50060104..0b54245cd424 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2004,7 +2004,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final ActivityStack stack = display.getStackAt(stackNdx); stack.switchUser(userId); Task task = stack.getTopMostTask(); - if (task != null) { + if (task != null && task != stack) { stack.positionChildAtTop(task); } } diff --git a/services/core/java/com/android/server/wm/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/xsd/Android.bp b/services/core/xsd/Android.bp index 8b2cbbde2db8..b7d6424450f3 100644 --- a/services/core/xsd/Android.bp +++ b/services/core/xsd/Android.bp @@ -12,3 +12,11 @@ xsd_config { api_dir: "platform-compat-schema", package_name: "com.android.server.compat.config", } + + +xsd_config { + name: "display-device-config", + srcs: ["display-device-config/display-device-config.xsd"], + api_dir: "display-device-config/schema", + package_name: "com.android.server.display.config", +} diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd new file mode 100644 index 000000000000..5c7f30576741 --- /dev/null +++ b/services/core/xsd/display-device-config/display-device-config.xsd @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<!-- This defines the format of the XML file generated by + ~ com.android.compat.annotation.ChangeIdProcessor annotation processor (from + ~ tools/platform-compat), and is parsed in com/android/server/compat/CompatConfig.java. +--> +<xs:schema version="2.0" + elementFormDefault="qualified" + xmlns:xs="http://www.w3.org/2001/XMLSchema"> + <xs:element name="displayConfiguration"> + <xs:complexType> + <xs:sequence> + <xs:element type="nitsMap" name="screenBrightnessMap"/> + </xs:sequence> + </xs:complexType> + </xs:element> + + <!-- Type definitions --> + + <xs:complexType name="nitsMap"> + <xs:sequence> + <xs:element name="point" type="point" maxOccurs="unbounded" minOccurs="2"/> + <xs:element name="highBrightnessStart" minOccurs="0" type="nonNegativeDecimal"/> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="point"> + <xs:sequence> + <xs:element type="nonNegativeDecimal" name="value"/> + <xs:element type="nonNegativeDecimal" name="nits"/> + </xs:sequence> + </xs:complexType> + + <xs:simpleType name="nonNegativeDecimal"> + <xs:restriction base="xs:decimal"> + <xs:minInclusive value="0.0"/> + </xs:restriction> + </xs:simpleType> +</xs:schema> diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt new file mode 100644 index 000000000000..5a9c9457b423 --- /dev/null +++ b/services/core/xsd/display-device-config/schema/current.txt @@ -0,0 +1,33 @@ +// Signature format: 2.0 +package com.android.server.display.config { + + public class DisplayConfiguration { + ctor public DisplayConfiguration(); + method public com.android.server.display.config.NitsMap getScreenBrightnessMap(); + method public void setScreenBrightnessMap(com.android.server.display.config.NitsMap); + } + + public class NitsMap { + ctor public NitsMap(); + method public java.math.BigDecimal getHighBrightnessStart(); + method public java.util.List<com.android.server.display.config.Point> getPoint(); + method public void setHighBrightnessStart(java.math.BigDecimal); + } + + public class Point { + ctor public Point(); + method public java.math.BigDecimal getNits(); + method public java.math.BigDecimal getValue(); + method public void setNits(java.math.BigDecimal); + method public void setValue(java.math.BigDecimal); + } + + public class XmlParser { + ctor public XmlParser(); + method public static com.android.server.display.config.DisplayConfiguration read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + } + +} + diff --git a/services/core/xsd/display-device-config/schema/last_current.txt b/services/core/xsd/display-device-config/schema/last_current.txt new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/services/core/xsd/display-device-config/schema/last_current.txt diff --git a/services/core/xsd/display-device-config/schema/last_removed.txt b/services/core/xsd/display-device-config/schema/last_removed.txt new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/services/core/xsd/display-device-config/schema/last_removed.txt diff --git a/services/core/xsd/display-device-config/schema/removed.txt b/services/core/xsd/display-device-config/schema/removed.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/services/core/xsd/display-device-config/schema/removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 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 e939d84292b5..da107d04b551 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; @@ -156,6 +158,7 @@ import android.content.IntentFilter; import android.content.PermissionChecker; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.CrossProfileApps; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; @@ -253,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; @@ -302,6 +306,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -310,6 +315,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; +import java.util.stream.Collectors; /** * Implementation of the device policy APIs. @@ -370,6 +376,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final String TAG_PROTECTED_PACKAGES = "protected-packages"; + 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); @@ -440,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<>(); @@ -484,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); } /** @@ -522,11 +539,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { /** * For admin apps targeting R+, throw when the app sets password requirement * that is not taken into account at given quality. For example when quality is set - * to {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}, it doesn't make sense to - * require certain password length. If the intent is to require a password of certain length - * having at least NUMERIC quality, the admin should first call - * {@link #setPasswordQuality(ComponentName, int, boolean)} and only then call - * {@link #setPasswordMinimumLength(ComponentName, int, boolean)}. + * to {@link android.app.admin.DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}, it doesn't + * make sense to require certain password length. If the intent is to require a password of + * certain length having at least NUMERIC quality, the admin should first call + * {@link android.app.admin.DevicePolicyManager#setPasswordQuality} and only then call + * {@link android.app.admin.DevicePolicyManager#setPasswordMinimumLength}. * * <p>Conversely when an admin app targeting R+ lowers password quality, those * requirements that stop making sense are reset to default values. @@ -771,6 +788,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { boolean mCurrentInputMethodSet = false; + boolean mSecondaryLockscreenEnabled = false; + // TODO(b/35385311): Keep track of metadata in TrustedCertificateStore instead. Set<String> mOwnerInstalledCaCerts = new ArraySet<>(); @@ -780,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; } @@ -1009,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; @@ -1131,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; @@ -1340,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); @@ -1362,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 { @@ -1598,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); @@ -3322,6 +3354,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { out.endTag(null, TAG_LOCK_TASK_FEATURES); } + if (policy.mSecondaryLockscreenEnabled) { + out.startTag(null, TAG_SECONDARY_LOCK_SCREEN); + out.attribute(null, ATTR_VALUE, Boolean.toString(true)); + out.endTag(null, TAG_SECONDARY_LOCK_SCREEN); + } + if (policy.mStatusBarDisabled) { out.startTag(null, TAG_STATUS_BAR); out.attribute(null, ATTR_DISABLED, Boolean.toString(policy.mStatusBarDisabled)); @@ -3398,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(); @@ -3571,6 +3616,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } else if (TAG_LOCK_TASK_FEATURES.equals(tag)) { policy.mLockTaskFeatures = Integer.parseInt( parser.getAttributeValue(null, ATTR_VALUE)); + } else if (TAG_SECONDARY_LOCK_SCREEN.equals(tag)) { + policy.mSecondaryLockscreenEnabled = Boolean.parseBoolean( + parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_STATUS_BAR.equals(tag)) { policy.mStatusBarDisabled = Boolean.parseBoolean( parser.getAttributeValue(null, ATTR_DISABLED)); @@ -3611,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); @@ -3751,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. @@ -3758,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(); @@ -3870,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) { @@ -8601,6 +8683,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Clear delegations. policy.mDelegationMap.clear(); policy.mStatusBarDisabled = false; + policy.mSecondaryLockscreenEnabled = false; policy.mUserProvisioningState = DevicePolicyManager.STATE_USER_UNMANAGED; policy.mAffiliationIds.clear(); policy.mLockTaskPackages.clear(); @@ -9732,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); } @@ -10654,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; @@ -10685,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"); } } @@ -11154,6 +11258,33 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + public void setSecondaryLockscreenEnabled(ComponentName who, boolean enabled) { + enforceCanSetSecondaryLockscreenEnabled(who); + synchronized (getLockObject()) { + final int userId = mInjector.userHandleGetCallingUserId(); + DevicePolicyData policy = getUserData(userId); + policy.mSecondaryLockscreenEnabled = enabled; + saveSettingsLocked(userId); + } + } + + @Override + public boolean isSecondaryLockscreenEnabled(int userId) { + synchronized (getLockObject()) { + return getUserData(userId).mSecondaryLockscreenEnabled; + } + } + + private void enforceCanSetSecondaryLockscreenEnabled(ComponentName who) { + enforceProfileOrDeviceOwner(who); + final int userId = mInjector.userHandleGetCallingUserId(); + if (isManagedProfile(userId)) { + throw new SecurityException( + "User " + userId + " is not allowed to call setSecondaryLockscreenEnabled"); + } + } + + @Override public void setLockTaskPackages(ComponentName who, String[] packages) throws SecurityException { Objects.requireNonNull(who, "ComponentName is null"); @@ -12346,6 +12477,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) { @@ -14846,12 +14985,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(who, "ComponentName is null"); Objects.requireNonNull(packageNames, "Package names is null"); + final List<String> previousCrossProfilePackages; synchronized (getLockObject()) { final ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + previousCrossProfilePackages = admin.mCrossProfilePackages; admin.mCrossProfilePackages = packageNames; saveSettingsLocked(mInjector.userHandleGetCallingUserId()); } + final CrossProfileApps crossProfileApps = mContext.getSystemService(CrossProfileApps.class); + mInjector.binderWithCleanCallingIdentity( + () -> crossProfileApps.resetInteractAcrossProfilesAppOps( + previousCrossProfilePackages, new HashSet<>(packageNames))); } @Override @@ -14931,23 +15076,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 @@ -15116,4 +15262,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/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 0b7c359cd532..258d7628e502 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -223,14 +223,8 @@ public final class SystemServer { "com.android.server.usb.UsbService$Lifecycle"; private static final String MIDI_SERVICE_CLASS = "com.android.server.midi.MidiService$Lifecycle"; - private static final String WIFI_APEX_SERVICE_JAR_PATH = - "/apex/com.android.wifi/javalib/wifi-service.jar"; private static final String WIFI_SERVICE_CLASS = "com.android.server.wifi.WifiService"; - private static final String WIFI_SCANNING_SERVICE_CLASS = - "com.android.server.wifi.scanner.WifiScanningService"; - private static final String WIFI_RTT_SERVICE_CLASS = - "com.android.server.wifi.rtt.RttService"; private static final String WIFI_AWARE_SERVICE_CLASS = "com.android.server.wifi.aware.WifiAwareService"; private static final String WIFI_P2P_SERVICE_CLASS = @@ -1437,36 +1431,33 @@ public final class SystemServer { PackageManager.FEATURE_WIFI)) { // Wifi Service must be started first for wifi-related services. t.traceBegin("StartWifi"); - mSystemServiceManager.startServiceFromJar( - WIFI_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); + mSystemServiceManager.startService(WIFI_SERVICE_CLASS); t.traceEnd(); t.traceBegin("StartWifiScanning"); - mSystemServiceManager.startServiceFromJar( - WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); + mSystemServiceManager.startService( + "com.android.server.wifi.scanner.WifiScanningService"); t.traceEnd(); } if (context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_WIFI_RTT)) { t.traceBegin("StartRttService"); - mSystemServiceManager.startServiceFromJar( - WIFI_RTT_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); + mSystemServiceManager.startService( + "com.android.server.wifi.rtt.RttService"); t.traceEnd(); } if (context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_WIFI_AWARE)) { t.traceBegin("StartWifiAware"); - mSystemServiceManager.startServiceFromJar( - WIFI_AWARE_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); + mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS); t.traceEnd(); } if (context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_WIFI_DIRECT)) { t.traceBegin("StartWifiP2P"); - mSystemServiceManager.startServiceFromJar( - WIFI_P2P_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); + mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS); t.traceEnd(); } diff --git a/services/net/Android.bp b/services/net/Android.bp index cf84bdfb5b6f..dbc2df8369f8 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -16,6 +16,7 @@ java_library_static { "netd_aidl_interface-unstable-java", "netlink-client", "networkstack-client", + "net-utils-services-common", ], } 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/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java index 96ff9c1ba726..1a7b1d3f6039 100644 --- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java +++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java @@ -18,6 +18,8 @@ package com.android.server.pm; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.OP_INTERACT_ACROSS_PROFILES; +import static android.content.Intent.FLAG_RECEIVER_FOREGROUND; +import static android.content.Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND; import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY; import static android.content.pm.CrossProfileApps.ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED; @@ -37,11 +39,13 @@ import android.app.AppOpsManager.Mode; import android.app.admin.DevicePolicyManagerInternal; import android.content.ContextWrapper; import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; +import android.content.pm.ResolveInfo; import android.content.pm.parsing.AndroidPackage; import android.content.pm.parsing.PackageImpl; import android.os.Process; @@ -70,6 +74,7 @@ import org.robolectric.shadow.api.Shadow; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -106,6 +111,7 @@ public class CrossProfileAppsServiceImplRoboTest { MockitoAnnotations.initMocks(this); mockCrossProfileAppInstalledAndEnabledOnEachProfile(); mockCrossProfileAppRequestsInteractAcrossProfiles(); + mockCrossProfileAppRegistersBroadcastReceiver(); mockCrossProfileAppWhitelisted(); } @@ -113,19 +119,21 @@ public class CrossProfileAppsServiceImplRoboTest { // They are enabled by default, so we simply have to ensure that a package info with an // application info is returned. final PackageInfo packageInfo = buildTestPackageInfo(); + mockCrossProfileAppInstalledOnProfile( + packageInfo, PERSONAL_PROFILE_USER_ID, PERSONAL_PROFILE_UID); + mockCrossProfileAppInstalledOnProfile(packageInfo, WORK_PROFILE_USER_ID, WORK_PROFILE_UID); + } + + private void mockCrossProfileAppInstalledOnProfile( + PackageInfo packageInfo, @UserIdInt int userId, int uid) { when(mPackageManagerInternal.getPackageInfo( eq(CROSS_PROFILE_APP_PACKAGE_NAME), /* flags= */ anyInt(), /* filterCallingUid= */ anyInt(), - eq(PERSONAL_PROFILE_USER_ID))) + eq(userId))) .thenReturn(packageInfo); - when(mPackageManagerInternal.getPackageInfo( - eq(CROSS_PROFILE_APP_PACKAGE_NAME), - /* flags= */ anyInt(), - /* filterCallingUid= */ anyInt(), - eq(WORK_PROFILE_USER_ID))) - .thenReturn(packageInfo); - mockCrossProfileAndroidPackage(PackageImpl.forParsing(CROSS_PROFILE_APP_PACKAGE_NAME)); + when(mPackageManagerInternal.getPackage(uid)) + .thenReturn(PackageImpl.forParsing(CROSS_PROFILE_APP_PACKAGE_NAME)); } private PackageInfo buildTestPackageInfo() { @@ -140,6 +148,31 @@ public class CrossProfileAppsServiceImplRoboTest { .thenReturn(new String[] {CROSS_PROFILE_APP_PACKAGE_NAME}); } + private void mockCrossProfileAppRegistersBroadcastReceiver() { + final ShadowApplicationPackageManager shadowApplicationPackageManager = + Shadow.extract(mPackageManager); + final Intent baseIntent = + new Intent(ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED) + .setPackage(CROSS_PROFILE_APP_PACKAGE_NAME); + final Intent manifestIntent = + new Intent(baseIntent) + .setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND + | Intent.FLAG_RECEIVER_FOREGROUND); + final Intent registeredIntent = + new Intent(baseIntent).setFlags(FLAG_RECEIVER_REGISTERED_ONLY); + final List<ResolveInfo> resolveInfos = Lists.newArrayList(buildTestResolveInfo()); + shadowApplicationPackageManager.setResolveInfosForIntent(manifestIntent, resolveInfos); + shadowApplicationPackageManager.setResolveInfosForIntent(registeredIntent, resolveInfos); + } + + private ResolveInfo buildTestResolveInfo() { + final ResolveInfo resolveInfo = new ResolveInfo(); + resolveInfo.activityInfo = new ActivityInfo(); + resolveInfo.activityInfo.packageName = CROSS_PROFILE_APP_PACKAGE_NAME; + resolveInfo.activityInfo.name = CROSS_PROFILE_APP_PACKAGE_NAME + ".Receiver"; + return resolveInfo; + } + private void mockCrossProfileAppWhitelisted() { when(mDevicePolicyManagerInternal.getAllCrossProfilePackages()) .thenReturn(Lists.newArrayList(CROSS_PROFILE_APP_PACKAGE_NAME)); @@ -191,16 +224,6 @@ public class CrossProfileAppsServiceImplRoboTest { } @Test - public void setInteractAcrossProfilesAppOp_missingManageAppOpsModes_throwsSecurityException() { - denyPermissions(Manifest.permission.MANAGE_APP_OPS_MODES); - try { - mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( - CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); - fail(); - } catch (SecurityException expected) {} - } - - @Test public void setInteractAcrossProfilesAppOp_setsAppOp() { mCrossProfileAppsServiceImpl.setInteractAcrossProfilesAppOp( CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED); @@ -294,6 +317,54 @@ public class CrossProfileAppsServiceImplRoboTest { assertThat(receivedManifestCanInteractAcrossProfilesChangedBroadcast()).isTrue(); } + @Test + public void canConfigureInteractAcrossProfiles_packageNotInstalledInProfile_returnsFalse() { + mockUninstallCrossProfileAppFromWorkProfile(); + assertThat(mCrossProfileAppsServiceImpl + .canConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isFalse(); + } + + private void mockUninstallCrossProfileAppFromWorkProfile() { + when(mPackageManagerInternal.getPackageInfo( + eq(CROSS_PROFILE_APP_PACKAGE_NAME), + /* flags= */ anyInt(), + /* filterCallingUid= */ anyInt(), + eq(WORK_PROFILE_USER_ID))) + .thenReturn(null); + when(mPackageManagerInternal.getPackage(WORK_PROFILE_UID)).thenReturn(null); + } + + @Test + public void canConfigureInteractAcrossProfiles_packageDoesNotRequestInteractAcrossProfiles_returnsFalse() + throws Exception { + mockCrossProfileAppDoesNotRequestInteractAcrossProfiles(); + assertThat(mCrossProfileAppsServiceImpl + .canConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isFalse(); + } + + private void mockCrossProfileAppDoesNotRequestInteractAcrossProfiles() throws Exception { + final String permissionName = Manifest.permission.INTERACT_ACROSS_PROFILES; + when(mIPackageManager.getAppOpPermissionPackages(permissionName)) + .thenReturn(new String[] {}); + } + + @Test + public void canConfigureInteractAcrossProfiles_packageNotWhitelisted_returnsFalse() { + mockCrossProfileAppNotWhitelisted(); + assertThat(mCrossProfileAppsServiceImpl + .canConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isFalse(); + } + + @Test + public void canConfigureInteractAcrossProfiles_returnsTrue() { + assertThat(mCrossProfileAppsServiceImpl + .canConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isTrue(); + } + private void explicitlySetInteractAcrossProfilesAppOp(@Mode int mode) { explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, mode); } @@ -311,7 +382,6 @@ public class CrossProfileAppsServiceImplRoboTest { shadowOf(mContext).denyPermissions(Process.myPid(), CALLING_UID, permissions); } - private @Mode int getCrossProfileAppOp() { return getCrossProfileAppOp(PERSONAL_PROFILE_UID); } @@ -365,10 +435,12 @@ public class CrossProfileAppsServiceImplRoboTest { } private boolean isBroadcastManifestCanInteractAcrossProfilesChanged(Intent intent) { - // The manifest check is negative since the FLAG_RECEIVER_REGISTERED_ONLY flag means that - // manifest receivers can NOT receive the broadcast. return isBroadcastCanInteractAcrossProfilesChanged(intent) - && (intent.getFlags() & FLAG_RECEIVER_REGISTERED_ONLY) == 0; + && (intent.getFlags() & FLAG_RECEIVER_REGISTERED_ONLY) == 0 + && (intent.getFlags() & FLAG_RECEIVER_INCLUDE_BACKGROUND) != 0 + && (intent.getFlags() & FLAG_RECEIVER_FOREGROUND) != 0 + && intent.getComponent() != null + && intent.getComponent().getPackageName().equals(CROSS_PROFILE_APP_PACKAGE_NAME); } private void declareCrossProfileAttributeOnCrossProfileApp(boolean value) { diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java index 1443eabf07d5..aea36e555ad7 100644 --- a/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java +++ b/services/robotests/src/com/android/server/testing/shadows/ShadowApplicationPackageManager.java @@ -19,7 +19,10 @@ package com.android.server.testing.shadows; import static android.content.pm.PackageManager.NameNotFoundException; import android.app.ApplicationPackageManager; +import android.content.Intent; import android.content.pm.PackageInfo; +import android.content.pm.ResolveInfo; +import android.os.UserHandle; import android.util.ArrayMap; import org.robolectric.annotation.Implements; @@ -100,6 +103,13 @@ public class ShadowApplicationPackageManager return sPackageUids.get(packageName); } + @Override + protected List<ResolveInfo> queryBroadcastReceiversAsUser( + Intent intent, int flags, UserHandle userHandle) { + // Currently does not handle multi-user. + return queryBroadcastReceivers(intent, flags); + } + /** Clear package state. */ @Resetter public static void reset() { 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/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 710e8dfe6aa2..d2ddff3627b9 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -74,6 +74,8 @@ <uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" /> <uses-permission android:name="android.permission.DUMP" /> + <uses-permission android:name="android.permission.READ_DREAM_STATE"/> + <uses-permission android:name="android.permission.WRITE_DREAM_STATE"/> <!-- Uses API introduced in O (26) --> <uses-sdk android:minSdkVersion="1" diff --git a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java new file mode 100644 index 000000000000..968a402ff3b7 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java @@ -0,0 +1,125 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import static org.mockito.Mockito.*; + +import android.bluetooth.BluetoothAdapter; +import android.content.Context; +import android.os.Looper; +import android.provider.Settings; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.MediumTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.BluetoothAirplaneModeListener.AirplaneModeHelper; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +@MediumTest +@RunWith(AndroidJUnit4.class) +public class BluetoothAirplaneModeListenerTest { + private Context mContext; + private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener; + private BluetoothAdapter mBluetoothAdapter; + private AirplaneModeHelper mHelper; + + @Mock BluetoothManagerService mBluetoothManagerService; + + @Before + public void setUp() throws Exception { + mContext = InstrumentationRegistry.getTargetContext(); + + mHelper = mock(AirplaneModeHelper.class); + when(mHelper.getSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT)) + .thenReturn(BluetoothAirplaneModeListener.MAX_TOAST_COUNT); + doNothing().when(mHelper).setSettingsInt(anyString(), anyInt()); + doNothing().when(mHelper).showToastMessage(); + doNothing().when(mHelper).onAirplaneModeChanged(any(BluetoothManagerService.class)); + + mBluetoothAirplaneModeListener = new BluetoothAirplaneModeListener( + mBluetoothManagerService, Looper.getMainLooper(), mContext); + mBluetoothAirplaneModeListener.start(mHelper); + } + + @Test + public void testIgnoreOnAirplanModeChange() { + Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); + + when(mHelper.isBluetoothOn()).thenReturn(true); + Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); + + when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true); + Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); + + when(mHelper.isAirplaneModeOn()).thenReturn(true); + Assert.assertTrue(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); + } + + @Test + public void testHandleAirplaneModeChange_InvokeAirplaneModeChanged() { + mBluetoothAirplaneModeListener.handleAirplaneModeChange(); + verify(mHelper).onAirplaneModeChanged(mBluetoothManagerService); + } + + @Test + public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_NotPopToast() { + mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT; + when(mHelper.isBluetoothOn()).thenReturn(true); + when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true); + when(mHelper.isAirplaneModeOn()).thenReturn(true); + mBluetoothAirplaneModeListener.handleAirplaneModeChange(); + + verify(mHelper).setSettingsInt(Settings.Global.BLUETOOTH_ON, + BluetoothManagerService.BLUETOOTH_ON_AIRPLANE); + verify(mHelper, times(0)).showToastMessage(); + verify(mHelper, times(0)).onAirplaneModeChanged(mBluetoothManagerService); + } + + @Test + public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_PopToast() { + mBluetoothAirplaneModeListener.mToastCount = 0; + when(mHelper.isBluetoothOn()).thenReturn(true); + when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true); + when(mHelper.isAirplaneModeOn()).thenReturn(true); + mBluetoothAirplaneModeListener.handleAirplaneModeChange(); + + verify(mHelper).setSettingsInt(Settings.Global.BLUETOOTH_ON, + BluetoothManagerService.BLUETOOTH_ON_AIRPLANE); + verify(mHelper).showToastMessage(); + verify(mHelper, times(0)).onAirplaneModeChanged(mBluetoothManagerService); + } + + @Test + public void testIsPopToast_PopToast() { + mBluetoothAirplaneModeListener.mToastCount = 0; + Assert.assertTrue(mBluetoothAirplaneModeListener.shouldPopToast()); + verify(mHelper).setSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT, 1); + } + + @Test + public void testIsPopToast_NotPopToast() { + mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT; + Assert.assertFalse(mBluetoothAirplaneModeListener.shouldPopToast()); + verify(mHelper, times(0)).setSettingsInt(anyString(), anyInt()); + } +} diff --git a/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java b/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java index 6f2de7f50379..07b655652fac 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java @@ -22,6 +22,7 @@ import androidx.test.runner.AndroidJUnit4; import com.google.android.icing.proto.DocumentProto; import com.google.android.icing.proto.PropertyProto; import com.google.android.icing.proto.SearchResultProto; +import com.google.android.icing.proto.StatusProto; import org.junit.Test; import org.junit.runner.RunWith; @@ -117,6 +118,7 @@ public class FakeIcingTest { private static List<String> queryGetUris(FakeIcing icing, String term) { List<String> uris = new ArrayList<>(); SearchResultProto results = icing.query(term); + assertThat(results.getStatus().getCode()).isEqualTo(StatusProto.Code.OK); for (SearchResultProto.ResultProto result : results.getResultsList()) { uris.add(result.getDocument().getUri()); } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index aeba488b5f63..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); @@ -4189,6 +4246,52 @@ public class DevicePolicyManagerTest extends DpmTestBase { () -> dpm.setLockTaskFeatures(admin1, flags)); } + public void testSecondaryLockscreen_profileOwner() throws Exception { + mContext.binder.callingUid = DpmMockContext.CALLER_UID; + + // Initial state is disabled. + assertFalse(dpm.isSecondaryLockscreenEnabled(DpmMockContext.CALLER_USER_HANDLE)); + + // Profile owner can set enabled state. + setAsProfileOwner(admin1); + dpm.setSecondaryLockscreenEnabled(admin1, true); + assertTrue(dpm.isSecondaryLockscreenEnabled(DpmMockContext.CALLER_USER_HANDLE)); + + // Managed profile managed by different package is unaffiliated - cannot set enabled. + final int managedProfileUserId = 15; + final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 20456); + final ComponentName adminDifferentPackage = + new ComponentName("another.package", "whatever.class"); + addManagedProfile(adminDifferentPackage, managedProfileAdminUid, admin2); + mContext.binder.callingUid = managedProfileAdminUid; + assertExpectException(SecurityException.class, /* messageRegex= */ null, + () -> dpm.setSecondaryLockscreenEnabled(adminDifferentPackage, false)); + } + + public void testSecondaryLockscreen_deviceOwner() throws Exception { + mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; + + // Initial state is disabled. + assertFalse(dpm.isSecondaryLockscreenEnabled(UserHandle.USER_SYSTEM)); + + // Device owners can set enabled state. + setupDeviceOwner(); + dpm.setSecondaryLockscreenEnabled(admin1, true); + assertTrue(dpm.isSecondaryLockscreenEnabled(UserHandle.USER_SYSTEM)); + } + + public void testSecondaryLockscreen_nonOwner() throws Exception { + mContext.binder.callingUid = DpmMockContext.CALLER_UID; + + // Initial state is disabled. + assertFalse(dpm.isSecondaryLockscreenEnabled(DpmMockContext.CALLER_USER_HANDLE)); + + // Non-DO/PO cannot set enabled state. + assertExpectException(SecurityException.class, /* messageRegex= */ null, + () -> dpm.setSecondaryLockscreenEnabled(admin1, true)); + assertFalse(dpm.isSecondaryLockscreenEnabled(DpmMockContext.CALLER_USER_HANDLE)); + } + public void testIsDeviceManaged() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java index 6cf6b67430ae..12228c19ca00 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java @@ -221,6 +221,8 @@ public class DpmMockContext extends MockContext { return mMockSystemServices.telephonyManager; case Context.APP_OPS_SERVICE: return mMockSystemServices.appOpsManager; + case Context.CROSS_PROFILE_APPS_SERVICE: + return mMockSystemServices.crossProfileApps; } throw new UnsupportedOperationException(); } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java index b9fb1aab65ba..37d40811571f 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java @@ -42,6 +42,7 @@ import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.CrossProfileApps; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; @@ -120,6 +121,7 @@ public class MockSystemServices { public final TimeDetector timeDetector; public final TimeZoneDetector timeZoneDetector; public final KeyChain.KeyChainConnection keyChainConnection; + public final CrossProfileApps crossProfileApps; public final PersistentDataBlockManagerInternal persistentDataBlockManagerInternal; public final AppOpsManager appOpsManager; /** Note this is a partial mock, not a real mock. */ @@ -165,6 +167,7 @@ public class MockSystemServices { timeDetector = mock(TimeDetector.class); timeZoneDetector = mock(TimeZoneDetector.class); keyChainConnection = mock(KeyChain.KeyChainConnection.class, RETURNS_DEEP_STUBS); + crossProfileApps = mock(CrossProfileApps.class); persistentDataBlockManagerInternal = mock(PersistentDataBlockManagerInternal.class); appOpsManager = mock(AppOpsManager.class); diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java index 604efc4949fe..a6af9a99788f 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java @@ -92,7 +92,7 @@ public class AppIntegrityManagerServiceImplTest { private static final String PACKAGE_NAME = "com.test.app"; private static final int VERSION_CODE = 100; - private static final String INSTALLER = TEST_FRAMEWORK_PACKAGE; + private static final String INSTALLER = "com.long.random.test.installer.name"; // These are obtained by running the test and checking logcat. private static final String APP_CERT = "301AA3CB081134501C45F1422ABC66C24224FD5DED5FDC8F17E697176FD866AA"; @@ -100,7 +100,7 @@ public class AppIntegrityManagerServiceImplTest { "301AA3CB081134501C45F1422ABC66C24224FD5DED5FDC8F17E697176FD866AA"; // We use SHA256 for package names longer than 32 characters. private static final String INSTALLER_SHA256 = - "786933C28839603EB48C50B2A688DC6BE52C833627CB2731FF8466A2AE9F94CD"; + "30F41A7CBF96EE736A54DD6DF759B50ED3CC126ABCEF694E167C324F5976C227"; private static final String PLAY_STORE_PKG = "com.android.vending"; private static final String ADB_INSTALLER = "adb"; @@ -140,7 +140,7 @@ public class AppIntegrityManagerServiceImplTest { // setup mocks to prevent NPE when(mMockContext.getPackageManager()).thenReturn(mSpyPackageManager); when(mMockContext.getResources()).thenReturn(mMockResources); - when(mMockResources.getStringArray(anyInt())).thenReturn(new String[] {}); + when(mMockResources.getStringArray(anyInt())).thenReturn(new String[]{}); when(mIntegrityFileManager.initialized()).thenReturn(true); } @@ -267,6 +267,8 @@ public class AppIntegrityManagerServiceImplTest { @Test public void broadcastReceiverRegistration() throws Exception { + whitelistUsAsRuleProvider(); + makeUsSystemApp(); ArgumentCaptor<IntentFilter> intentFilterCaptor = ArgumentCaptor.forClass(IntentFilter.class); @@ -281,6 +283,8 @@ public class AppIntegrityManagerServiceImplTest { @Test public void handleBroadcast_correctArgs() throws Exception { + whitelistUsAsRuleProvider(); + makeUsSystemApp(); ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); verify(mMockContext) @@ -314,6 +318,8 @@ public class AppIntegrityManagerServiceImplTest { @Test public void handleBroadcast_allow() throws Exception { + whitelistUsAsRuleProvider(); + makeUsSystemApp(); ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); verify(mMockContext) @@ -331,6 +337,8 @@ public class AppIntegrityManagerServiceImplTest { @Test public void handleBroadcast_reject() throws Exception { + whitelistUsAsRuleProvider(); + makeUsSystemApp(); ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); verify(mMockContext) @@ -354,6 +362,8 @@ public class AppIntegrityManagerServiceImplTest { @Test public void handleBroadcast_notInitialized() throws Exception { + whitelistUsAsRuleProvider(); + makeUsSystemApp(); when(mIntegrityFileManager.initialized()).thenReturn(false); ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); @@ -371,10 +381,30 @@ public class AppIntegrityManagerServiceImplTest { verify(mSpyPackageManager, never()).getPackageArchiveInfo(any(), anyInt()); } + @Test + public void verifierAsInstaller_skipIntegrityVerification() throws Exception { + whitelistUsAsRuleProvider(); + makeUsSystemApp(); + ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + verify(mMockContext) + .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); + Intent intent = makeVerificationIntent(TEST_FRAMEWORK_PACKAGE); + when(mRuleEvaluationEngine.evaluate(any(), any())).thenReturn( + IntegrityCheckResult.deny(/* rule= */ null)); + + broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent); + runJobInHandler(); + + verify(mPackageManagerInternal) + .setIntegrityVerificationResult( + 1, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW); + } + private void whitelistUsAsRuleProvider() { Resources mockResources = mock(Resources.class); when(mockResources.getStringArray(R.array.config_integrityRuleProviderPackages)) - .thenReturn(new String[] {TEST_FRAMEWORK_PACKAGE}); + .thenReturn(new String[]{TEST_FRAMEWORK_PACKAGE}); when(mMockContext.getResources()).thenReturn(mockResources); } @@ -395,15 +425,28 @@ public class AppIntegrityManagerServiceImplTest { } private Intent makeVerificationIntent() throws Exception { + PackageInfo packageInfo = + mRealContext.getPackageManager().getPackageInfo(TEST_FRAMEWORK_PACKAGE, + PackageManager.GET_SIGNATURES); + doReturn(packageInfo) + .when(mSpyPackageManager) + .getPackageInfo(eq(INSTALLER), anyInt()); + doReturn(1) + .when(mSpyPackageManager) + .getPackageUid(eq(INSTALLER), anyInt()); + return makeVerificationIntent(INSTALLER); + } + + private Intent makeVerificationIntent(String installer) throws Exception { Intent intent = new Intent(); intent.setDataAndType(Uri.fromFile(mTestApk), PACKAGE_MIME_TYPE); intent.setAction(Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION); intent.putExtra(EXTRA_VERIFICATION_ID, 1); intent.putExtra(Intent.EXTRA_PACKAGE_NAME, PACKAGE_NAME); - intent.putExtra(EXTRA_VERIFICATION_INSTALLER_PACKAGE, INSTALLER); + intent.putExtra(EXTRA_VERIFICATION_INSTALLER_PACKAGE, installer); intent.putExtra( EXTRA_VERIFICATION_INSTALLER_UID, - mRealContext.getPackageManager().getPackageUid(INSTALLER, /* flags= */ 0)); + mMockContext.getPackageManager().getPackageUid(installer, /* flags= */ 0)); intent.putExtra(Intent.EXTRA_VERSION_CODE, VERSION_CODE); return intent; } diff --git a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java new file mode 100644 index 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/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java index cb9d816509b9..7f66f3c49185 100644 --- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java @@ -51,7 +51,6 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.Collections; -import java.util.List; import java.util.Map; import java.util.Set; @@ -511,6 +510,62 @@ public class AppsFilterTest { overlaySetting, 0)); } + @Test + public void testInitiatingApp_DoesntFilter() { + final AppsFilter appsFilter = + new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + appsFilter.onSystemReady(); + + PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"), + DUMMY_TARGET_UID); + PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"), + DUMMY_CALLING_UID, withInstallSource(target.name, null, null, false)); + + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + } + + @Test + public void testUninstalledInitiatingApp_Filters() { + final AppsFilter appsFilter = + new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + appsFilter.onSystemReady(); + + PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"), + DUMMY_TARGET_UID); + PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"), + DUMMY_CALLING_UID, withInstallSource(target.name, null, null, true)); + + assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + } + + @Test + public void testOriginatingApp_Filters() { + final AppsFilter appsFilter = + new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + appsFilter.onSystemReady(); + + PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"), + DUMMY_TARGET_UID); + PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"), + DUMMY_CALLING_UID, withInstallSource(null, target.name, null, false)); + + assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + } + + @Test + public void testInstallingApp_DoesntFilter() { + final AppsFilter appsFilter = + new AppsFilter(mFeatureConfigMock, new String[]{}, false, null); + appsFilter.onSystemReady(); + + PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"), + DUMMY_TARGET_UID); + PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"), + DUMMY_CALLING_UID, withInstallSource(null, null, target.name, false)); + + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + } + private interface WithSettingBuilder { PackageSettingBuilder withBuilder(PackageSettingBuilder builder); } @@ -538,5 +593,13 @@ public class AppsFilterTest { return setting; } + private WithSettingBuilder withInstallSource(String initiatingPackageName, + String originatingPackageName, String installerPackageName, + boolean isInitiatingPackageUninstalled) { + final InstallSource installSource = InstallSource.create(initiatingPackageName, + originatingPackageName, installerPackageName, + /* isOrphaned= */ false, isInitiatingPackageUninstalled); + return setting -> setting.setInstallSource(installSource); + } } diff --git a/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java b/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java index 3852b9fec001..6a9ef8a2b7bd 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/ModuleInfoProviderTest.java @@ -15,6 +15,9 @@ */ package com.android.server.pm; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + import android.content.Context; import android.content.pm.ModuleInfo; import android.content.pm.PackageManager; @@ -22,11 +25,25 @@ import android.test.InstrumentationTestCase; import com.android.frameworks.servicestests.R; +import org.mockito.Mock; + import java.util.Collections; import java.util.List; public class ModuleInfoProviderTest extends InstrumentationTestCase { + + @Mock private ApexManager mApexManager; + + public void setUp() { + initMocks(this); + } + public void testSuccessfulParse() { + when(mApexManager.getApexModuleNameForPackageName("com.android.module1")) + .thenReturn("com.module1.apex"); + when(mApexManager.getApexModuleNameForPackageName("com.android.module2")) + .thenReturn("com.module2.apex"); + ModuleInfoProvider provider = getProvider(R.xml.well_formed_metadata); List<ModuleInfo> mi = provider.getInstalledModules(PackageManager.MATCH_ALL); @@ -40,11 +57,13 @@ public class ModuleInfoProviderTest extends InstrumentationTestCase { ModuleInfo mi1 = provider.getModuleInfo("com.android.module1", 0); assertEquals("com.android.module1", mi1.getPackageName()); assertEquals("module_1_name", mi1.getName()); + assertEquals("com.module1.apex", mi1.getApexModuleName()); assertEquals(false, mi1.isHidden()); ModuleInfo mi2 = provider.getModuleInfo("com.android.module2", 0); assertEquals("com.android.module2", mi2.getPackageName()); assertEquals("module_2_name", mi2.getName()); + assertEquals("com.module2.apex", mi2.getApexModuleName()); assertEquals(true, mi2.isHidden()); } @@ -75,6 +94,7 @@ public class ModuleInfoProviderTest extends InstrumentationTestCase { */ private ModuleInfoProvider getProvider(int resourceId) { final Context ctx = getInstrumentation().getContext(); - return new ModuleInfoProvider(ctx.getResources().getXml(resourceId), ctx.getResources()); + return new ModuleInfoProvider( + ctx.getResources().getXml(resourceId), ctx.getResources(), mApexManager); } } 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/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java index 2473997a61c9..84414947056f 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java @@ -41,6 +41,7 @@ public class PackageSettingBuilder { private SparseArray<PackageUserState> mUserStates = new SparseArray<>(); private AndroidPackage mPkg; private int mAppId; + private InstallSource mInstallSource; public PackageSettingBuilder setPackage(AndroidPackage pkg) { this.mPkg = pkg; @@ -137,6 +138,11 @@ public class PackageSettingBuilder { return this; } + public PackageSettingBuilder setInstallSource(InstallSource installSource) { + mInstallSource = installSource; + return this; + } + public PackageSetting build() { final PackageSetting packageSetting = new PackageSetting(mName, mRealName, new File(mCodePath), new File(mResourcePath), @@ -146,6 +152,9 @@ public class PackageSettingBuilder { packageSetting.pkg = mPkg; packageSetting.appId = mAppId; packageSetting.volumeUuid = this.mVolumeUuid; + if (mInstallSource != null) { + packageSetting.installSource = mInstallSource; + } for (int i = 0; i < mUserStates.size(); i++) { packageSetting.setUserState(mUserStates.keyAt(i), mUserStates.valueAt(i)); } diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index 25cef56cd21e..6eef41aafc98 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -806,4 +806,173 @@ public class PowerManagerServiceTest { assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( DisplayPowerRequest.POLICY_BRIGHT); } + + @Test + public void testIsAmbientDisplayAvailable_available() throws Exception { + createService(); + when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true); + + assertThat(mService.getBinderServiceInstance().isAmbientDisplayAvailable()).isTrue(); + } + + @Test + public void testIsAmbientDisplayAvailable_unavailable() throws Exception { + createService(); + when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(false); + + assertThat(mService.getBinderServiceInstance().isAmbientDisplayAvailable()).isFalse(); + } + + @Test + public void testSuppressAmbientDisplay_suppressed() throws Exception { + createService(); + mService.getBinderServiceInstance().suppressAmbientDisplay("test", true); + + assertThat(Settings.Secure.getInt(mContextSpy.getContentResolver(), + Settings.Secure.SUPPRESS_DOZE)).isEqualTo(1); + } + + @Test + public void testSuppressAmbientDisplay_multipleCallers_suppressed() throws Exception { + createService(); + mService.getBinderServiceInstance().suppressAmbientDisplay("test1", true); + mService.getBinderServiceInstance().suppressAmbientDisplay("test2", false); + + assertThat(Settings.Secure.getInt(mContextSpy.getContentResolver(), + Settings.Secure.SUPPRESS_DOZE)).isEqualTo(1); + } + + @Test + public void testSuppressAmbientDisplay_suppressTwice_suppressed() throws Exception { + createService(); + mService.getBinderServiceInstance().suppressAmbientDisplay("test", true); + mService.getBinderServiceInstance().suppressAmbientDisplay("test", true); + + assertThat(Settings.Secure.getInt(mContextSpy.getContentResolver(), + Settings.Secure.SUPPRESS_DOZE)).isEqualTo(1); + } + + @Test + public void testSuppressAmbientDisplay_suppressTwiceThenUnsuppress_notSuppressed() + throws Exception { + createService(); + mService.getBinderServiceInstance().suppressAmbientDisplay("test", true); + mService.getBinderServiceInstance().suppressAmbientDisplay("test", true); + mService.getBinderServiceInstance().suppressAmbientDisplay("test", false); + + assertThat(Settings.Secure.getInt(mContextSpy.getContentResolver(), + Settings.Secure.SUPPRESS_DOZE)).isEqualTo(0); + } + + @Test + public void testSuppressAmbientDisplay_notSuppressed() throws Exception { + createService(); + mService.getBinderServiceInstance().suppressAmbientDisplay("test", false); + + assertThat(Settings.Secure.getInt(mContextSpy.getContentResolver(), + Settings.Secure.SUPPRESS_DOZE)).isEqualTo(0); + } + + @Test + public void testSuppressAmbientDisplay_unsuppressTwice_notSuppressed() throws Exception { + createService(); + mService.getBinderServiceInstance().suppressAmbientDisplay("test", false); + mService.getBinderServiceInstance().suppressAmbientDisplay("test", false); + + assertThat(Settings.Secure.getInt(mContextSpy.getContentResolver(), + Settings.Secure.SUPPRESS_DOZE)).isEqualTo(0); + } + + @Test + public void testIsAmbientDisplaySuppressed_default_notSuppressed() throws Exception { + createService(); + + assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressed()).isFalse(); + } + + @Test + public void testIsAmbientDisplaySuppressed_suppressed() throws Exception { + createService(); + mService.getBinderServiceInstance().suppressAmbientDisplay("test", true); + + assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressed()).isTrue(); + } + + @Test + public void testIsAmbientDisplaySuppressed_notSuppressed() throws Exception { + createService(); + mService.getBinderServiceInstance().suppressAmbientDisplay("test", false); + + assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressed()).isFalse(); + } + + @Test + public void testIsAmbientDisplaySuppressed_multipleTokens_suppressed() throws Exception { + createService(); + mService.getBinderServiceInstance().suppressAmbientDisplay("test1", false); + mService.getBinderServiceInstance().suppressAmbientDisplay("test2", true); + + assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressed()).isTrue(); + } + + @Test + public void testIsAmbientDisplaySuppressed_multipleTokens_notSuppressed() throws Exception { + createService(); + mService.getBinderServiceInstance().suppressAmbientDisplay("test1", false); + mService.getBinderServiceInstance().suppressAmbientDisplay("test2", false); + + assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressed()).isFalse(); + } + + @Test + public void testIsAmbientDisplaySuppressedForToken_default_notSuppressed() throws Exception { + createService(); + + assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressedForToken("test")) + .isFalse(); + } + + @Test + public void testIsAmbientDisplaySuppressedForToken_suppressed() throws Exception { + createService(); + mService.getBinderServiceInstance().suppressAmbientDisplay("test", true); + + assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressedForToken("test")) + .isTrue(); + } + + @Test + public void testIsAmbientDisplaySuppressedForToken_notSuppressed() throws Exception { + createService(); + mService.getBinderServiceInstance().suppressAmbientDisplay("test", false); + + assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressedForToken("test")) + .isFalse(); + } + + @Test + public void testIsAmbientDisplaySuppressedForToken_multipleTokens_suppressed() + throws Exception { + createService(); + mService.getBinderServiceInstance().suppressAmbientDisplay("test1", true); + mService.getBinderServiceInstance().suppressAmbientDisplay("test2", true); + + assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressedForToken("test1")) + .isTrue(); + assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressedForToken("test2")) + .isTrue(); + } + + @Test + public void testIsAmbientDisplaySuppressedForToken_multipleTokens_notSuppressed() + throws Exception { + createService(); + mService.getBinderServiceInstance().suppressAmbientDisplay("test1", true); + mService.getBinderServiceInstance().suppressAmbientDisplay("test2", false); + + assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressedForToken("test1")) + .isTrue(); + assertThat(mService.getBinderServiceInstance().isAmbientDisplaySuppressedForToken("test2")) + .isFalse(); + } } diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java index 4a13dce5642b..7af3ec62e651 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java @@ -16,18 +16,18 @@ package com.android.server.usage; +import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_SYSTEM; +import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER; import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT; import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE; +import static android.app.usage.UsageStatsManager.REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; +import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - import android.os.FileUtils; import android.test.AndroidTestCase; @@ -37,10 +37,12 @@ public class AppIdleHistoryTests extends AndroidTestCase { File mStorageDir; - final static String PACKAGE_1 = "com.android.testpackage1"; - final static String PACKAGE_2 = "com.android.testpackage2"; + private static final String PACKAGE_1 = "com.android.testpackage1"; + private static final String PACKAGE_2 = "com.android.testpackage2"; + private static final String PACKAGE_3 = "com.android.testpackage3"; + private static final String PACKAGE_4 = "com.android.testpackage4"; - final static int USER_ID = 0; + private static final int USER_ID = 0; @Override protected void setUp() throws Exception { @@ -100,16 +102,27 @@ public class AppIdleHistoryTests extends AndroidTestCase { aih.setAppStandbyBucket(PACKAGE_2, USER_ID, 2000, STANDBY_BUCKET_ACTIVE, REASON_MAIN_USAGE); + aih.setAppStandbyBucket(PACKAGE_3, USER_ID, 2500, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE); + aih.setAppStandbyBucket(PACKAGE_4, USER_ID, 2750, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_USER); aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 3000, STANDBY_BUCKET_RARE, REASON_MAIN_TIMEOUT); assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 3000), STANDBY_BUCKET_RARE); assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 3000), STANDBY_BUCKET_ACTIVE); assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000), REASON_MAIN_TIMEOUT); + assertEquals(aih.getAppStandbyBucket(PACKAGE_3, USER_ID, 3000), STANDBY_BUCKET_RESTRICTED); + assertEquals(aih.getAppStandbyReason(PACKAGE_3, USER_ID, 3000), + REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE); + assertEquals(aih.getAppStandbyReason(PACKAGE_4, USER_ID, 3000), + REASON_MAIN_FORCED_BY_USER); - // RARE is considered idle + // RARE and RESTRICTED are considered idle assertTrue(aih.isIdle(PACKAGE_1, USER_ID, 3000)); assertFalse(aih.isIdle(PACKAGE_2, USER_ID, 3000)); + assertTrue(aih.isIdle(PACKAGE_3, USER_ID, 3000)); + assertTrue(aih.isIdle(PACKAGE_4, USER_ID, 3000)); // Check persistence aih.writeAppIdleDurations(); @@ -118,6 +131,11 @@ public class AppIdleHistoryTests extends AndroidTestCase { assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 5000), STANDBY_BUCKET_RARE); assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 5000), STANDBY_BUCKET_ACTIVE); assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_MAIN_TIMEOUT); + assertEquals(aih.getAppStandbyBucket(PACKAGE_3, USER_ID, 3000), STANDBY_BUCKET_RESTRICTED); + assertEquals(aih.getAppStandbyReason(PACKAGE_3, USER_ID, 3000), + REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE); + assertEquals(aih.getAppStandbyReason(PACKAGE_4, USER_ID, 3000), + REASON_MAIN_FORCED_BY_USER); assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE)); assertFalse(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE)); diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 6aca58f400b3..03dc21370e24 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -28,11 +28,17 @@ import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER; import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED; import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT; import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE; +import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE; +import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND; +import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYNC_ADAPTER; +import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_INTERACTION; +import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; +import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; import static org.junit.Assert.assertEquals; @@ -46,6 +52,8 @@ import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import android.annotation.NonNull; +import android.app.ActivityManager; import android.app.usage.AppStandbyInfo; import android.app.usage.UsageEvents; import android.appwidget.AppWidgetManager; @@ -124,6 +132,13 @@ public class AppStandbyControllerTests { public PackageManager getPackageManager() { return mockPm; } + + public Object getSystemService(@NonNull String name) { + if (Context.ACTIVITY_SERVICE.equals(name)) { + return mock(ActivityManager.class); + } + return super.getSystemService(name); + } } static class MyInjector extends AppStandbyController.Injector { @@ -253,8 +268,11 @@ public class AppStandbyControllerTests { doReturn(packages).when(mockPm).getInstalledPackagesAsUser(anyInt(), anyInt()); try { + doReturn(UID_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_1), anyInt()); doReturn(UID_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_1), anyInt(), anyInt()); doReturn(UID_EXEMPTED_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_EXEMPTED_1), + anyInt()); + doReturn(UID_EXEMPTED_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_EXEMPTED_1), anyInt(), anyInt()); doReturn(pi.applicationInfo).when(mockPm).getApplicationInfo(eq(pi.packageName), anyInt()); @@ -468,7 +486,7 @@ public class AppStandbyControllerTests { } @Test - public void testPredictionTimedout() throws Exception { + public void testPredictionTimedOut() throws Exception { // Set it to timeout or usage, so that prediction can override it mInjector.mElapsedRealtime = HOUR_MS; mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, @@ -532,6 +550,79 @@ public class AppStandbyControllerTests { mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER, REASON_MAIN_PREDICTED); assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1)); + + // Prediction can't remove from RESTRICTED + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_USER); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET, + REASON_MAIN_PREDICTED); + assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1)); + + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_SYSTEM); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET, + REASON_MAIN_PREDICTED); + assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1)); + + // Force from user can remove from RESTRICTED + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_USER); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET, + REASON_MAIN_FORCED_BY_USER); + assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1)); + + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_SYSTEM); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET, + REASON_MAIN_FORCED_BY_USER); + assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1)); + + // Force from system can remove from RESTRICTED if it was put it in due to system + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_SYSTEM); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET, + REASON_MAIN_FORCED_BY_SYSTEM); + assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1)); + + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_USER); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET, + REASON_MAIN_FORCED_BY_SYSTEM); + assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1)); + + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_PREDICTED); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET, + REASON_MAIN_FORCED_BY_SYSTEM); + assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1)); + + // Non-user usage can't remove from RESTRICTED + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_SYSTEM); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET, + REASON_MAIN_USAGE); + assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1)); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET, + REASON_MAIN_USAGE | REASON_SUB_USAGE_SYSTEM_INTERACTION); + assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1)); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET, + REASON_MAIN_USAGE | REASON_SUB_USAGE_SYNC_ADAPTER); + assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1)); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET, + REASON_MAIN_USAGE | REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE); + assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1)); + + // Explicit user usage can remove from RESTRICTED + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_USER); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET, + REASON_MAIN_USAGE | REASON_SUB_USAGE_USER_INTERACTION); + assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1)); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_SYSTEM); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, + REASON_MAIN_USAGE | REASON_SUB_USAGE_MOVE_TO_FOREGROUND); + assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1)); } @Test @@ -556,6 +647,55 @@ public class AppStandbyControllerTests { assertBucket(STANDBY_BUCKET_RARE); } + /** + * Test that setAppStandbyBucket to RESTRICTED doesn't change the bucket until the usage + * timeout has passed. + */ + @Test + public void testTimeoutBeforeRestricted() throws Exception { + reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1); + assertBucket(STANDBY_BUCKET_ACTIVE); + + mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD; + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_SYSTEM); + // Bucket shouldn't change + assertBucket(STANDBY_BUCKET_ACTIVE); + + // bucketing works after timeout + mInjector.mElapsedRealtime += DAY_MS; + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_SYSTEM); + assertBucket(STANDBY_BUCKET_RESTRICTED); + + // Way past all timeouts. Make sure timeout processing doesn't raise bucket. + mInjector.mElapsedRealtime += RARE_THRESHOLD * 4; + mController.checkIdleStates(USER_ID); + assertBucket(STANDBY_BUCKET_RESTRICTED); + } + + /** + * Test that an app is put into the RESTRICTED bucket after enough time has passed. + */ + @Test + public void testRestrictedDelay() throws Exception { + reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1); + assertBucket(STANDBY_BUCKET_ACTIVE); + + mInjector.mElapsedRealtime += mInjector.getRestrictedBucketDelayMs() - 5000; + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, + REASON_MAIN_FORCED_BY_SYSTEM); + // Bucket shouldn't change + assertBucket(STANDBY_BUCKET_ACTIVE); + + // bucketing works after timeout + mInjector.mElapsedRealtime += 6000; + + Thread.sleep(6000); + // Enough time has passed. The app should automatically be put into the RESTRICTED bucket. + assertBucket(STANDBY_BUCKET_RESTRICTED); + } + @Test public void testCascadingTimeouts() throws Exception { reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java index 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/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java index a9fe1a62b558..da0e03dff65a 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java @@ -117,6 +117,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { assertEquals(getSmartReplies(key, i), ranking.getSmartReplies()); assertEquals(canBubble(i), ranking.canBubble()); assertEquals(visuallyInterruptive(i), ranking.visuallyInterruptive()); + assertEquals(isConversation(i), ranking.isConversation()); } } @@ -184,7 +185,8 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { (ArrayList) tweak.getSmartActions(), (ArrayList) tweak.getSmartReplies(), tweak.canBubble(), - tweak.visuallyInterruptive() + tweak.visuallyInterruptive(), + tweak.isConversation() ); assertNotEquals(nru, nru2); } @@ -261,7 +263,8 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { getSmartActions(key, i), getSmartReplies(key, i), canBubble(i), - visuallyInterruptive(i) + visuallyInterruptive(i), + isConversation(i) ); rankings[i] = ranking; } @@ -370,6 +373,10 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { return index % 4 == 0; } + private boolean isConversation(int index) { + return index % 4 == 0; + } + private void assertActionsEqual( List<Notification.Action> expecteds, List<Notification.Action> actuals) { assertEquals(expecteds.size(), actuals.size()); @@ -403,6 +410,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { assertEquals(comment, a.isNoisy(), b.isNoisy()); assertEquals(comment, a.getSmartReplies(), b.getSmartReplies()); assertEquals(comment, a.canBubble(), b.canBubble()); + assertEquals(comment, a.isConversation(), b.isConversation()); assertActionsEqual(a.getSmartActions(), b.getSmartActions()); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 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/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java index fab6b7fd0d77..2d4b5a73bea0 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java @@ -20,6 +20,7 @@ import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.service.notification.Adjustment.KEY_IMPORTANCE; +import static android.service.notification.Adjustment.KEY_NOT_CONVERSATION; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE; @@ -38,12 +39,15 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.IActivityManager; import android.app.Notification; import android.app.Notification.Builder; import android.app.NotificationChannel; import android.app.NotificationManager; +import android.app.Person; +import android.content.ContentResolver; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -58,6 +62,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.service.notification.Adjustment; import android.service.notification.StatusBarNotification; +import android.util.FeatureFlagUtils; import android.widget.RemoteViews; import androidx.test.filters.SmallTest; @@ -83,6 +88,7 @@ public class NotificationRecordTest extends UiServiceTestCase { private final Context mMockContext = mock(Context.class); @Mock private PackageManager mPm; + @Mock private ContentResolver mContentResolver; private final String pkg = PKG_N_MR1; private final int uid = 9583; @@ -116,6 +122,9 @@ public class NotificationRecordTest extends UiServiceTestCase { when(mMockContext.getResources()).thenReturn(getContext().getResources()); when(mMockContext.getPackageManager()).thenReturn(mPm); + when(mMockContext.getContentResolver()).thenReturn(mContentResolver); + Settings.Global.putString(mContentResolver, + FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false"); ApplicationInfo appInfo = new ApplicationInfo(); appInfo.targetSdkVersion = Build.VERSION_CODES.O; when(mMockContext.getApplicationInfo()).thenReturn(appInfo); @@ -194,6 +203,21 @@ public class NotificationRecordTest extends UiServiceTestCase { return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid); } + private StatusBarNotification getMessagingStyleNotification(@Nullable String shortcutId) { + final Builder builder = new Builder(mMockContext) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon); + + Person person = new Person.Builder().setName("Bob").build(); + builder.setStyle(new Notification.MessagingStyle(person)); + if (shortcutId != null) { + builder.setShortcutId(shortcutId); + } + + Notification n = builder.build(); + return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid); + } + // // Tests // @@ -1095,4 +1119,55 @@ public class NotificationRecordTest extends UiServiceTestCase { assertTrue("false negative detection", record.hasUndecoratedRemoteView()); } + + @Test + public void testIsConversation() { + StatusBarNotification sbn = getMessagingStyleNotification("test_shortcut_id"); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + + assertTrue(record.isConversation()); + } + + @Test + public void testIsConversation_nullShortcutId() { + StatusBarNotification sbn = getMessagingStyleNotification(null); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + + assertFalse(record.isConversation()); + } + + @Test + public void testIsConversation_bypassShortcutFlagEnabled() { + Settings.Global.putString(mContentResolver, + FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true"); + StatusBarNotification sbn = getMessagingStyleNotification(null); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + + assertTrue(record.isConversation()); + } + + @Test + public void testIsConversation_channelDemoted() { + StatusBarNotification sbn = getMessagingStyleNotification("test_shortcut_id"); + channel.setDemoted(true); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + + assertFalse(record.isConversation()); + } + + @Test + public void testIsConversation_withAdjustmentOverride() { + StatusBarNotification sbn = getMessagingStyleNotification("test_shortcut_id"); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + + Bundle bundle = new Bundle(); + bundle.putBoolean(KEY_NOT_CONVERSATION, true); + Adjustment adjustment = new Adjustment( + PKG_O, record.getKey(), bundle, "", record.getUser().getIdentifier()); + + record.addAdjustment(adjustment); + record.applyAdjustments(); + + assertFalse(record.isConversation()); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index 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/usb/Android.bp b/services/usb/Android.bp index d2c973abbc74..a9474c10017e 100644 --- a/services/usb/Android.bp +++ b/services/usb/Android.bp @@ -19,5 +19,6 @@ java_library_static { "android.hardware.usb-V1.1-java", "android.hardware.usb-V1.2-java", "android.hardware.usb.gadget-V1.0-java", + "android.hardware.usb.gadget-V1.1-java", ], } diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 9f3b07b951be..6407ec76958e 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -161,6 +161,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser private static final int MSG_GET_CURRENT_USB_FUNCTIONS = 16; private static final int MSG_FUNCTION_SWITCH_TIMEOUT = 17; private static final int MSG_GADGET_HAL_REGISTERED = 18; + private static final int MSG_RESET_USB_GADGET = 19; private static final int AUDIO_MODE_SOURCE = 1; @@ -1846,6 +1847,23 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser } } break; + case MSG_RESET_USB_GADGET: + synchronized (mGadgetProxyLock) { + if (mGadgetProxy == null) { + Slog.e(TAG, "reset Usb Gadget mGadgetProxy is null"); + break; + } + + try { + android.hardware.usb.gadget.V1_1.IUsbGadget gadgetProxy = + android.hardware.usb.gadget.V1_1.IUsbGadget + .castFrom(mGadgetProxy); + gadgetProxy.reset(); + } catch (RemoteException e) { + Slog.e(TAG, "reset Usb Gadget failed", e); + } + } + break; default: super.handleMessage(msg); } @@ -2054,6 +2072,17 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser mHandler.sendMessage(MSG_SET_SCREEN_UNLOCKED_FUNCTIONS, functions); } + /** + * Resets the USB Gadget. + */ + public void resetUsbGadget() { + if (DEBUG) { + Slog.d(TAG, "reset Usb Gadget"); + } + + mHandler.sendMessage(MSG_RESET_USB_GADGET, null); + } + private void onAdbEnabled(boolean enabled) { mHandler.sendMessage(MSG_ENABLE_ADB, enabled); } diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index b1bd04ecb13f..61f2c50ccecf 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -66,6 +66,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.concurrent.CompletableFuture; /** @@ -345,7 +346,7 @@ public class UsbService extends IUsbManager.Stub { @Override public void setDevicePackage(UsbDevice device, String packageName, int userId) { - device = Preconditions.checkNotNull(device); + Objects.requireNonNull(device); mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); @@ -361,7 +362,7 @@ public class UsbService extends IUsbManager.Stub { @Override public void setAccessoryPackage(UsbAccessory accessory, String packageName, int userId) { - accessory = Preconditions.checkNotNull(accessory); + Objects.requireNonNull(accessory); mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); @@ -379,9 +380,9 @@ public class UsbService extends IUsbManager.Stub { @Override public void addDevicePackagesToPreferenceDenied(UsbDevice device, String[] packageNames, UserHandle user) { - device = Preconditions.checkNotNull(device); + Objects.requireNonNull(device); packageNames = Preconditions.checkArrayElementsNotNull(packageNames, "packageNames"); - user = Preconditions.checkNotNull(user); + Objects.requireNonNull(user); mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); @@ -397,9 +398,9 @@ public class UsbService extends IUsbManager.Stub { @Override public void addAccessoryPackagesToPreferenceDenied(UsbAccessory accessory, String[] packageNames, UserHandle user) { - accessory = Preconditions.checkNotNull(accessory); + Objects.requireNonNull(accessory); packageNames = Preconditions.checkArrayElementsNotNull(packageNames, "packageNames"); - user = Preconditions.checkNotNull(user); + Objects.requireNonNull(user); mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); @@ -415,9 +416,9 @@ public class UsbService extends IUsbManager.Stub { @Override public void removeDevicePackagesFromPreferenceDenied(UsbDevice device, String[] packageNames, UserHandle user) { - device = Preconditions.checkNotNull(device); + Objects.requireNonNull(device); packageNames = Preconditions.checkArrayElementsNotNull(packageNames, "packageNames"); - user = Preconditions.checkNotNull(user); + Objects.requireNonNull(user); mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); @@ -433,9 +434,9 @@ public class UsbService extends IUsbManager.Stub { @Override public void removeAccessoryPackagesFromPreferenceDenied(UsbAccessory accessory, String[] packageNames, UserHandle user) { - accessory = Preconditions.checkNotNull(accessory); + Objects.requireNonNull(accessory); packageNames = Preconditions.checkArrayElementsNotNull(packageNames, "packageNames"); - user = Preconditions.checkNotNull(user); + Objects.requireNonNull(user); mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); @@ -451,8 +452,8 @@ public class UsbService extends IUsbManager.Stub { @Override public void setDevicePersistentPermission(UsbDevice device, int uid, UserHandle user, boolean shouldBeGranted) { - device = Preconditions.checkNotNull(device); - user = Preconditions.checkNotNull(user); + Objects.requireNonNull(device); + Objects.requireNonNull(user); mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); @@ -468,8 +469,8 @@ public class UsbService extends IUsbManager.Stub { @Override public void setAccessoryPersistentPermission(UsbAccessory accessory, int uid, UserHandle user, boolean shouldBeGranted) { - accessory = Preconditions.checkNotNull(accessory); - user = Preconditions.checkNotNull(user); + Objects.requireNonNull(accessory); + Objects.requireNonNull(user); mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); @@ -637,6 +638,19 @@ public class UsbService extends IUsbManager.Stub { } @Override + public void resetUsbGadget() { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + Preconditions.checkNotNull(mDeviceManager, "DeviceManager must not be null"); + + final long ident = Binder.clearCallingIdentity(); + try { + mDeviceManager.resetUsbGadget(); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override public List<ParcelableUsbPort> getPorts() { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); @@ -663,7 +677,7 @@ public class UsbService extends IUsbManager.Stub { @Override public UsbPortStatus getPortStatus(String portId) { - Preconditions.checkNotNull(portId, "portId must not be null"); + Objects.requireNonNull(portId, "portId must not be null"); mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); final long ident = Binder.clearCallingIdentity(); @@ -676,7 +690,7 @@ public class UsbService extends IUsbManager.Stub { @Override public void setPortRoles(String portId, int powerRole, int dataRole) { - Preconditions.checkNotNull(portId, "portId must not be null"); + Objects.requireNonNull(portId, "portId must not be null"); UsbPort.checkRoles(powerRole, dataRole); mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); @@ -692,7 +706,7 @@ public class UsbService extends IUsbManager.Stub { @Override public void enableContaminantDetection(String portId, boolean enable) { - Preconditions.checkNotNull(portId, "portId must not be null"); + Objects.requireNonNull(portId, "portId must not be null"); mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); final long ident = Binder.clearCallingIdentity(); 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/Call.java b/telecomm/java/android/telecom/Call.java index 826a89eb38bb..acf51f3856d3 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -547,8 +547,14 @@ public final class Call { */ public static final int PROPERTY_VOIP_AUDIO_MODE = 0x00001000; + /** + * Indicates that the call is an adhoc conference call. This property can be set for both + * incoming and outgoing calls. + */ + public static final int PROPERTY_IS_ADHOC_CONFERENCE = 0x00002000; + //****************************************************************************************** - // Next PROPERTY value: 0x00002000 + // Next PROPERTY value: 0x00004000 //****************************************************************************************** private final String mTelecomCallId; @@ -726,6 +732,9 @@ public final class Call { if (hasProperty(properties, PROPERTY_VOIP_AUDIO_MODE)) { builder.append(" PROPERTY_VOIP_AUDIO_MODE"); } + if (hasProperty(properties, PROPERTY_IS_ADHOC_CONFERENCE)) { + builder.append(" PROPERTY_IS_ADHOC_CONFERENCE"); + } builder.append("]"); return builder.toString(); } diff --git a/telecomm/java/android/telecom/CallerInfoAsyncQuery.java b/telecomm/java/android/telecom/CallerInfoAsyncQuery.java index 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/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java index 456290cd772a..6b0845f5d12b 100644 --- a/telecomm/java/android/telecom/Conference.java +++ b/telecomm/java/android/telecom/Conference.java @@ -69,6 +69,7 @@ public abstract class Conference extends Conferenceable { public void onConnectionEvent(Conference c, String event, Bundle extras) {} public void onCallerDisplayNameChanged( Conference c, String callerDisplayName, int presentation) {} + public void onRingbackRequested(Conference c, boolean ringback) {} } private final Set<Listener> mListeners = new CopyOnWriteArraySet<>(); @@ -97,6 +98,7 @@ public abstract class Conference extends Conferenceable { private int mAddressPresentation; private String mCallerDisplayName; private int mCallerDisplayNamePresentation; + private boolean mRingbackRequested = false; private final Connection.Listener mConnectionDeathListener = new Connection.Listener() { @Override @@ -170,6 +172,14 @@ public abstract class Conference extends Conferenceable { } /** + * Returns whether this conference is requesting that the system play a ringback tone + * on its behalf. + */ + public final boolean isRingbackRequested() { + return mRingbackRequested; + } + + /** * Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class * {@link Connection} for valid values. * @@ -308,6 +318,35 @@ public abstract class Conference extends Conferenceable { public void onConnectionAdded(Connection connection) {} /** + * Notifies this Conference, which is in {@code STATE_RINGING}, of + * a request to accept. + * For managed {@link ConnectionService}s, this will be called when the user answers a call via + * the default dialer's {@link InCallService}. + * + * @param videoState The video state in which to answer the connection. + */ + public void onAnswer(int videoState) {} + + /** + * Notifies this Conference, which is in {@code STATE_RINGING}, of + * a request to accept. + * For managed {@link ConnectionService}s, this will be called when the user answers a call via + * the default dialer's {@link InCallService}. + * @hide + */ + public final void onAnswer() { + onAnswer(VideoProfile.STATE_AUDIO_ONLY); + } + + /** + * Notifies this Conference, which is in {@code STATE_RINGING}, of + * a request to reject. + * For managed {@link ConnectionService}s, this will be called when the user rejects a call via + * the default dialer's {@link InCallService}. + */ + public void onReject() {} + + /** * Sets state to be on hold. */ public final void setOnHold() { @@ -322,9 +361,17 @@ public abstract class Conference extends Conferenceable { } /** + * Sets state to be ringing. + */ + public final void setRinging() { + setState(Connection.STATE_RINGING); + } + + /** * Sets state to be active. */ public final void setActive() { + setRingbackRequested(false); setState(Connection.STATE_ACTIVE); } @@ -436,6 +483,21 @@ public abstract class Conference extends Conferenceable { } /** + * Requests that the framework play a ringback tone. This is to be invoked by implementations + * that do not play a ringback tone themselves in the conference's audio stream. + * + * @param ringback Whether the ringback tone is to be played. + */ + public final void setRingbackRequested(boolean ringback) { + if (mRingbackRequested != ringback) { + mRingbackRequested = ringback; + for (Listener l : mListeners) { + l.onRingbackRequested(this, ringback); + } + } + } + + /** * Set the video state for the conference. * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY}, * {@link VideoProfile#STATE_BIDIRECTIONAL}, @@ -640,14 +702,6 @@ public abstract class Conference extends Conferenceable { } private void setState(int newState) { - if (newState != Connection.STATE_ACTIVE && - newState != Connection.STATE_HOLDING && - newState != Connection.STATE_DISCONNECTED) { - Log.w(this, "Unsupported state transition for Conference call.", - Connection.stateToString(newState)); - return; - } - if (mState != newState) { int oldState = mState; mState = newState; @@ -657,6 +711,37 @@ public abstract class Conference extends Conferenceable { } } + private static class FailureSignalingConference extends Conference { + private boolean mImmutable = false; + public FailureSignalingConference(DisconnectCause disconnectCause, + PhoneAccountHandle phoneAccount) { + super(phoneAccount); + setDisconnected(disconnectCause); + mImmutable = true; + } + public void checkImmutable() { + if (mImmutable) { + throw new UnsupportedOperationException("Conference is immutable"); + } + } + } + + /** + * Return a {@code Conference} which represents a failed conference attempt. The returned + * {@code Conference} will have a {@link android.telecom.DisconnectCause} and as specified, + * and a {@link #getState()} of {@code STATE_DISCONNECTED}. + * <p> + * The returned {@code Conference} can be assumed to {@link #destroy()} itself when appropriate, + * so users of this method need not maintain a reference to its return value to destroy it. + * + * @param disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}). + * @return A {@code Conference} which indicates failure. + */ + public @NonNull static Conference createFailedConference( + @NonNull DisconnectCause disconnectCause, @NonNull PhoneAccountHandle phoneAccount) { + return new FailureSignalingConference(disconnectCause, phoneAccount); + } + private final void clearConferenceableList() { for (Connection c : mConferenceableConnections) { c.removeConnectionListener(mConnectionDeathListener); @@ -667,11 +752,13 @@ public abstract class Conference extends Conferenceable { @Override public String toString() { return String.format(Locale.US, - "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s, ThisObject %s]", + "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s," + + "isRingbackRequested: %s, ThisObject %s]", Connection.stateToString(mState), Call.Details.capabilitiesToString(mConnectionCapabilities), getVideoState(), getVideoProvider(), + isRingbackRequested() ? "Y" : "N", super.toString()); } diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index f205ec64f49b..c934625f588b 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -497,8 +497,17 @@ public abstract class Connection extends Conferenceable { @TestApi public static final int PROPERTY_REMOTELY_HOSTED = 1 << 11; + /** + * Set by the framework to indicate that it is an adhoc conference call. + * <p> + * This is used for Outgoing and incoming conference calls. + * + */ + public static final int PROPERTY_IS_ADHOC_CONFERENCE = 1 << 12; + + //********************************************************************************************** - // Next PROPERTY value: 1<<12 + // Next PROPERTY value: 1<<13 //********************************************************************************************** /** @@ -1018,6 +1027,10 @@ public abstract class Connection extends Conferenceable { builder.append(isLong ? " PROPERTY_REMOTELY_HOSTED" : " remote_hst"); } + if ((properties & PROPERTY_IS_ADHOC_CONFERENCE) == PROPERTY_IS_ADHOC_CONFERENCE) { + builder.append(isLong ? " PROPERTY_IS_ADHOC_CONFERENCE" : " adhoc_conf"); + } + builder.append("]"); return builder.toString(); } diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java index 221f8f129744..6d7ceca0a2cd 100644 --- a/telecomm/java/android/telecom/ConnectionRequest.java +++ b/telecomm/java/android/telecom/ConnectionRequest.java @@ -26,6 +26,9 @@ import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; +import java.util.ArrayList; +import java.util.List; + /** * Simple data container encapsulating a request to some entity to * create a new {@link Connection}. @@ -46,6 +49,8 @@ public final class ConnectionRequest implements Parcelable { private boolean mShouldShowIncomingCallUi = false; private ParcelFileDescriptor mRttPipeToInCall; private ParcelFileDescriptor mRttPipeFromInCall; + private List<Uri> mParticipants; + private boolean mIsAdhocConference = false; public Builder() { } @@ -59,6 +64,15 @@ public final class ConnectionRequest implements Parcelable { } /** + * Sets the participants for the resulting {@link ConnectionRequest} + * @param participants The participants to which the {@link Connection} is to connect. + */ + public @NonNull Builder setParticipants(@Nullable List<Uri> participants) { + this.mParticipants = participants; + return this; + } + + /** * Sets the address for the resulting {@link ConnectionRequest} * @param address The address(e.g., phone number) to which the {@link Connection} is to * connect. @@ -108,6 +122,16 @@ public final class ConnectionRequest implements Parcelable { } /** + * Sets isAdhocConference for the resulting {@link ConnectionRequest} + * @param isAdhocConference {@code true} if it is a adhoc conference call + * {@code false}, if not a adhoc conference call + */ + public @NonNull Builder setIsAdhocConferenceCall(boolean isAdhocConference) { + this.mIsAdhocConference = isAdhocConference; + return this; + } + + /** * Sets the RTT pipe for transferring text into the {@link ConnectionService} for the * resulting {@link ConnectionRequest} * @param rttPipeFromInCall The data pipe to read from. @@ -141,7 +165,9 @@ public final class ConnectionRequest implements Parcelable { mTelecomCallId, mShouldShowIncomingCallUi, mRttPipeFromInCall, - mRttPipeToInCall); + mRttPipeToInCall, + mParticipants, + mIsAdhocConference); } } @@ -155,6 +181,8 @@ public final class ConnectionRequest implements Parcelable { private final ParcelFileDescriptor mRttPipeFromInCall; // Cached return value of getRttTextStream -- we don't want to wrap it more than once. private Connection.RttTextStream mRttTextStream; + private List<Uri> mParticipants; + private final boolean mIsAdhocConference; /** * @param accountHandle The accountHandle which should be used to place the call. @@ -214,6 +242,21 @@ public final class ConnectionRequest implements Parcelable { boolean shouldShowIncomingCallUi, ParcelFileDescriptor rttPipeFromInCall, ParcelFileDescriptor rttPipeToInCall) { + this(accountHandle, handle, extras, videoState, telecomCallId, + shouldShowIncomingCallUi, rttPipeFromInCall, rttPipeToInCall, null, false); + } + + private ConnectionRequest( + PhoneAccountHandle accountHandle, + Uri handle, + Bundle extras, + int videoState, + String telecomCallId, + boolean shouldShowIncomingCallUi, + ParcelFileDescriptor rttPipeFromInCall, + ParcelFileDescriptor rttPipeToInCall, + List<Uri> participants, + boolean isAdhocConference) { mAccountHandle = accountHandle; mAddress = handle; mExtras = extras; @@ -222,6 +265,8 @@ public final class ConnectionRequest implements Parcelable { mShouldShowIncomingCallUi = shouldShowIncomingCallUi; mRttPipeFromInCall = rttPipeFromInCall; mRttPipeToInCall = rttPipeToInCall; + mParticipants = participants; + mIsAdhocConference = isAdhocConference; } private ConnectionRequest(Parcel in) { @@ -233,6 +278,11 @@ public final class ConnectionRequest implements Parcelable { mShouldShowIncomingCallUi = in.readInt() == 1; mRttPipeFromInCall = in.readParcelable(getClass().getClassLoader()); mRttPipeToInCall = in.readParcelable(getClass().getClassLoader()); + + mParticipants = new ArrayList<Uri>(); + in.readList(mParticipants, getClass().getClassLoader()); + + mIsAdhocConference = in.readInt() == 1; } /** @@ -246,6 +296,11 @@ public final class ConnectionRequest implements Parcelable { public Uri getAddress() { return mAddress; } /** + * The participants to which the {@link Connection} is to connect. + */ + public @Nullable List<Uri> getParticipants() { return mParticipants; } + + /** * Application-specific extra data. Used for passing back information from an incoming * call {@code Intent}, and for any proprietary extensions arranged between a client * and servant {@code ConnectionService} which agree on a vocabulary for such data. @@ -290,6 +345,13 @@ public final class ConnectionRequest implements Parcelable { } /** + * @return {@code true} if the call is a adhoc conference call else @return {@code false} + */ + public boolean isAdhocConferenceCall() { + return mIsAdhocConference; + } + + /** * Gets the {@link ParcelFileDescriptor} that is used to send RTT text from the connection * service to the in-call UI. In order to obtain an * {@link java.io.InputStream} from this {@link ParcelFileDescriptor}, use @@ -345,11 +407,12 @@ public final class ConnectionRequest implements Parcelable { @Override public String toString() { - return String.format("ConnectionRequest %s %s", + return String.format("ConnectionRequest %s %s isAdhocConf: %s", mAddress == null ? Uri.EMPTY : Connection.toLogSafePhoneNumber(mAddress.toString()), - bundleToString(mExtras)); + bundleToString(mExtras), + isAdhocConferenceCall() ? "Y" : "N"); } private static String bundleToString(Bundle extras){ @@ -406,5 +469,7 @@ public final class ConnectionRequest implements Parcelable { destination.writeInt(mShouldShowIncomingCallUi ? 1 : 0); destination.writeParcelable(mRttPipeFromInCall, 0); destination.writeParcelable(mRttPipeToInCall, 0); + destination.writeList(mParticipants); + destination.writeInt(mIsAdhocConference ? 1 : 0); } } diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 3a0494e17db9..440f044fdcf7 100644 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -154,6 +154,9 @@ public abstract class ConnectionService extends Service { private static final String SESSION_CONNECTION_SERVICE_FOCUS_LOST = "CS.cSFL"; private static final String SESSION_CONNECTION_SERVICE_FOCUS_GAINED = "CS.cSFG"; private static final String SESSION_HANDOVER_FAILED = "CS.haF"; + private static final String SESSION_CREATE_CONF = "CS.crConf"; + private static final String SESSION_CREATE_CONF_COMPLETE = "CS.crConfC"; + private static final String SESSION_CREATE_CONF_FAILED = "CS.crConfF"; private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1; private static final int MSG_CREATE_CONNECTION = 2; @@ -188,6 +191,9 @@ public abstract class ConnectionService extends Service { private static final int MSG_HANDOVER_FAILED = 32; private static final int MSG_HANDOVER_COMPLETE = 33; private static final int MSG_DEFLECT = 34; + private static final int MSG_CREATE_CONFERENCE = 35; + private static final int MSG_CREATE_CONFERENCE_COMPLETE = 36; + private static final int MSG_CREATE_CONFERENCE_FAILED = 37; private static Connection sNullConnection; @@ -291,6 +297,63 @@ public abstract class ConnectionService extends Service { } @Override + public void createConference( + PhoneAccountHandle connectionManagerPhoneAccount, + String id, + ConnectionRequest request, + boolean isIncoming, + boolean isUnknown, + Session.Info sessionInfo) { + Log.startSession(sessionInfo, SESSION_CREATE_CONF); + try { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = connectionManagerPhoneAccount; + args.arg2 = id; + args.arg3 = request; + args.arg4 = Log.createSubsession(); + args.argi1 = isIncoming ? 1 : 0; + args.argi2 = isUnknown ? 1 : 0; + mHandler.obtainMessage(MSG_CREATE_CONFERENCE, args).sendToTarget(); + } finally { + Log.endSession(); + } + } + + @Override + public void createConferenceComplete(String id, Session.Info sessionInfo) { + Log.startSession(sessionInfo, SESSION_CREATE_CONF_COMPLETE); + try { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = id; + args.arg2 = Log.createSubsession(); + mHandler.obtainMessage(MSG_CREATE_CONFERENCE_COMPLETE, args).sendToTarget(); + } finally { + Log.endSession(); + } + } + + @Override + public void createConferenceFailed( + PhoneAccountHandle connectionManagerPhoneAccount, + String callId, + ConnectionRequest request, + boolean isIncoming, + Session.Info sessionInfo) { + Log.startSession(sessionInfo, SESSION_CREATE_CONF_FAILED); + try { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.arg2 = request; + args.arg3 = Log.createSubsession(); + args.arg4 = connectionManagerPhoneAccount; + args.argi1 = isIncoming ? 1 : 0; + mHandler.obtainMessage(MSG_CREATE_CONFERENCE_FAILED, args).sendToTarget(); + } finally { + Log.endSession(); + } + } + + @Override public void handoverFailed(String callId, ConnectionRequest request, int reason, Session.Info sessionInfo) { Log.startSession(sessionInfo, SESSION_HANDOVER_FAILED); @@ -802,6 +865,106 @@ public abstract class ConnectionService extends Service { } break; } + case MSG_CREATE_CONFERENCE: { + SomeArgs args = (SomeArgs) msg.obj; + Log.continueSession((Session) args.arg4, SESSION_HANDLER + SESSION_CREATE_CONN); + try { + final PhoneAccountHandle connectionManagerPhoneAccount = + (PhoneAccountHandle) args.arg1; + final String id = (String) args.arg2; + final ConnectionRequest request = (ConnectionRequest) args.arg3; + final boolean isIncoming = args.argi1 == 1; + final boolean isUnknown = args.argi2 == 1; + if (!mAreAccountsInitialized) { + Log.d(this, "Enqueueing pre-initconference request %s", id); + mPreInitializationConnectionRequests.add( + new android.telecom.Logging.Runnable( + SESSION_HANDLER + SESSION_CREATE_CONF + ".pIConfR", + null /*lock*/) { + @Override + public void loggedRun() { + createConference(connectionManagerPhoneAccount, + id, + request, + isIncoming, + isUnknown); + } + }.prepare()); + } else { + createConference(connectionManagerPhoneAccount, + id, + request, + isIncoming, + isUnknown); + } + } finally { + args.recycle(); + Log.endSession(); + } + break; + } + case MSG_CREATE_CONFERENCE_COMPLETE: { + SomeArgs args = (SomeArgs) msg.obj; + Log.continueSession((Session) args.arg2, + SESSION_HANDLER + SESSION_CREATE_CONN_COMPLETE); + try { + final String id = (String) args.arg1; + if (!mAreAccountsInitialized) { + Log.d(this, "Enqueueing pre-init conference request %s", id); + mPreInitializationConnectionRequests.add( + new android.telecom.Logging.Runnable( + SESSION_HANDLER + SESSION_CREATE_CONF_COMPLETE + + ".pIConfR", + null /*lock*/) { + @Override + public void loggedRun() { + notifyCreateConferenceComplete(id); + } + }.prepare()); + } else { + notifyCreateConferenceComplete(id); + } + } finally { + args.recycle(); + Log.endSession(); + } + break; + } + case MSG_CREATE_CONFERENCE_FAILED: { + SomeArgs args = (SomeArgs) msg.obj; + Log.continueSession((Session) args.arg3, SESSION_HANDLER + + SESSION_CREATE_CONN_FAILED); + try { + final String id = (String) args.arg1; + final ConnectionRequest request = (ConnectionRequest) args.arg2; + final boolean isIncoming = args.argi1 == 1; + final PhoneAccountHandle connectionMgrPhoneAccount = + (PhoneAccountHandle) args.arg4; + if (!mAreAccountsInitialized) { + Log.d(this, "Enqueueing pre-init conference request %s", id); + mPreInitializationConnectionRequests.add( + new android.telecom.Logging.Runnable( + SESSION_HANDLER + SESSION_CREATE_CONF_FAILED + + ".pIConfR", + null /*lock*/) { + @Override + public void loggedRun() { + createConferenceFailed(connectionMgrPhoneAccount, id, + request, isIncoming); + } + }.prepare()); + } else { + Log.i(this, "createConferenceFailed %s", id); + createConferenceFailed(connectionMgrPhoneAccount, id, request, + isIncoming); + } + } finally { + args.recycle(); + Log.endSession(); + } + break; + } + case MSG_HANDOVER_FAILED: { SomeArgs args = (SomeArgs) msg.obj; Log.continueSession((Session) args.arg3, SESSION_HANDLER + @@ -1162,6 +1325,12 @@ public abstract class ConnectionService extends Service { public void onStateChanged(Conference conference, int oldState, int newState) { String id = mIdByConference.get(conference); switch (newState) { + case Connection.STATE_RINGING: + mAdapter.setRinging(id); + break; + case Connection.STATE_DIALING: + mAdapter.setDialing(id); + break; case Connection.STATE_ACTIVE: mAdapter.setActive(id); break; @@ -1292,6 +1461,13 @@ public abstract class ConnectionService extends Service { mAdapter.onConnectionEvent(id, event, extras); } } + + @Override + public void onRingbackRequested(Conference c, boolean ringback) { + String id = mIdByConference.get(c); + Log.d(this, "Adapter conference onRingback %b", ringback); + mAdapter.setRingbackRequested(id, ringback); + } }; private final Connection.Listener mConnectionListener = new Connection.Listener() { @@ -1534,6 +1710,70 @@ public abstract class ConnectionService extends Service { return super.onUnbind(intent); } + + /** + * This can be used by telecom to either create a new outgoing conference call or attach + * to an existing incoming conference call. In either case, telecom will cycle through a + * set of services and call createConference until a connection service cancels the process + * or completes it successfully. + */ + private void createConference( + final PhoneAccountHandle callManagerAccount, + final String callId, + final ConnectionRequest request, + boolean isIncoming, + boolean isUnknown) { + + Conference conference = null; + conference = isIncoming ? onCreateIncomingConference(callManagerAccount, request) + : onCreateOutgoingConference(callManagerAccount, request); + + Log.d(this, "createConference, conference: %s", conference); + if (conference == null) { + Log.i(this, "createConference, implementation returned null conference."); + conference = Conference.createFailedConference( + new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONFERENCE"), + request.getAccountHandle()); + } + if (conference.getExtras() != null) { + conference.getExtras().putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId); + } + mConferenceById.put(callId, conference); + mIdByConference.put(conference, callId); + conference.addListener(mConferenceListener); + ParcelableConference parcelableConference = new ParcelableConference( + request.getAccountHandle(), + conference.getState(), + conference.getConnectionCapabilities(), + conference.getConnectionProperties(), + Collections.<String>emptyList(), //connectionIds + conference.getVideoProvider() == null ? + null : conference.getVideoProvider().getInterface(), + conference.getVideoState(), + conference.getConnectTimeMillis(), + conference.getConnectionStartElapsedRealTime(), + conference.getStatusHints(), + conference.getExtras(), + conference.getAddress(), + conference.getAddressPresentation(), + conference.getCallerDisplayName(), + conference.getCallerDisplayNamePresentation(), + conference.getDisconnectCause(), + conference.isRingbackRequested()); + if (conference.getState() != Connection.STATE_DISCONNECTED) { + conference.setTelecomCallId(callId); + mAdapter.setVideoProvider(callId, conference.getVideoProvider()); + mAdapter.setVideoState(callId, conference.getVideoState()); + onConferenceAdded(conference); + } + + Log.d(this, "createConference, calling handleCreateConferenceSuccessful %s", callId); + mAdapter.handleCreateConferenceComplete( + callId, + request, + parcelableConference); + } + /** * This can be used by telecom to either create a new outgoing call or attach to an existing * incoming call. In either case, telecom will cycle through a set of services and call @@ -1645,6 +1885,18 @@ public abstract class ConnectionService extends Service { } } + private void createConferenceFailed(final PhoneAccountHandle callManagerAccount, + final String callId, final ConnectionRequest request, + boolean isIncoming) { + + Log.i(this, "createConferenceFailed %s", callId); + if (isIncoming) { + onCreateIncomingConferenceFailed(callManagerAccount, request); + } else { + onCreateOutgoingConferenceFailed(callManagerAccount, request); + } + } + private void handoverFailed(final String callId, final ConnectionRequest request, int reason) { @@ -1669,6 +1921,24 @@ public abstract class ConnectionService extends Service { "notifyCreateConnectionComplete")); } + /** + * Called by Telecom when the creation of a new Conference has completed and it is now added + * to Telecom. + * @param callId The ID of the connection. + */ + private void notifyCreateConferenceComplete(final String callId) { + Log.i(this, "notifyCreateConferenceComplete %s", callId); + if (callId == null) { + // This could happen if the conference fails quickly and is removed from the + // ConnectionService before Telecom sends the create conference complete callback. + Log.w(this, "notifyCreateConferenceComplete: callId is null."); + return; + } + onCreateConferenceComplete(findConferenceForAction(callId, + "notifyCreateConferenceComplete")); + } + + private void abort(String callId) { Log.d(this, "abort %s", callId); findConnectionForAction(callId, "abort").onAbort(); @@ -1676,12 +1946,20 @@ public abstract class ConnectionService extends Service { private void answerVideo(String callId, int videoState) { Log.d(this, "answerVideo %s", callId); - findConnectionForAction(callId, "answer").onAnswer(videoState); + if (mConnectionById.containsKey(callId)) { + findConnectionForAction(callId, "answer").onAnswer(videoState); + } else { + findConferenceForAction(callId, "answer").onAnswer(videoState); + } } private void answer(String callId) { Log.d(this, "answer %s", callId); - findConnectionForAction(callId, "answer").onAnswer(); + if (mConnectionById.containsKey(callId)) { + findConnectionForAction(callId, "answer").onAnswer(); + } else { + findConferenceForAction(callId, "answer").onAnswer(); + } } private void deflect(String callId, Uri address) { @@ -1691,7 +1969,11 @@ public abstract class ConnectionService extends Service { private void reject(String callId) { Log.d(this, "reject %s", callId); - findConnectionForAction(callId, "reject").onReject(); + if (mConnectionById.containsKey(callId)) { + findConnectionForAction(callId, "reject").onReject(); + } else { + findConferenceForAction(callId, "reject").onReject(); + } } private void reject(String callId, String rejectWithMessage) { @@ -2198,6 +2480,21 @@ public abstract class ConnectionService extends Service { ConnectionRequest request) { return null; } + /** + * Create a {@code Connection} given an incoming request. This is used to attach to existing + * incoming conference call. + * + * @param connectionManagerPhoneAccount See description at + * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. + * @param request Details about the incoming call. + * @return The {@code Connection} object to satisfy this call, or {@code null} to + * not handle the call. + */ + public @Nullable Conference onCreateIncomingConference( + @Nullable PhoneAccountHandle connectionManagerPhoneAccount, + @Nullable ConnectionRequest request) { + return null; + } /** * Called after the {@link Connection} returned by @@ -2212,6 +2509,19 @@ public abstract class ConnectionService extends Service { } /** + * Called after the {@link Conference} returned by + * {@link #onCreateIncomingConference(PhoneAccountHandle, ConnectionRequest)} + * or {@link #onCreateOutgoingConference(PhoneAccountHandle, ConnectionRequest)} has been + * added to the {@link ConnectionService} and sent to Telecom. + * + * @param conference the {@link Conference}. + * @hide + */ + public void onCreateConferenceComplete(Conference conference) { + } + + + /** * Called by Telecom to inform the {@link ConnectionService} that its request to create a new * incoming {@link Connection} was denied. * <p> @@ -2250,6 +2560,47 @@ public abstract class ConnectionService extends Service { } /** + * Called by Telecom to inform the {@link ConnectionService} that its request to create a new + * incoming {@link Conference} was denied. + * <p> + * Used when a self-managed {@link ConnectionService} attempts to create a new incoming + * {@link Conference}, but Telecom has determined that the call cannot be allowed at this time. + * The {@link ConnectionService} is responsible for silently rejecting the new incoming + * {@link Conference}. + * <p> + * See {@link TelecomManager#isIncomingCallPermitted(PhoneAccountHandle)} for more information. + * + * @param connectionManagerPhoneAccount See description at + * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. + * @param request The incoming connection request. + */ + public void onCreateIncomingConferenceFailed( + @Nullable PhoneAccountHandle connectionManagerPhoneAccount, + @Nullable ConnectionRequest request) { + } + + /** + * Called by Telecom to inform the {@link ConnectionService} that its request to create a new + * outgoing {@link Conference} was denied. + * <p> + * Used when a self-managed {@link ConnectionService} attempts to create a new outgoing + * {@link Conference}, but Telecom has determined that the call cannot be placed at this time. + * The {@link ConnectionService} is responisible for informing the user that the + * {@link Conference} cannot be made at this time. + * <p> + * See {@link TelecomManager#isOutgoingCallPermitted(PhoneAccountHandle)} for more information. + * + * @param connectionManagerPhoneAccount See description at + * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. + * @param request The outgoing connection request. + */ + public void onCreateOutgoingConferenceFailed( + @Nullable PhoneAccountHandle connectionManagerPhoneAccount, + @Nullable ConnectionRequest request) { + } + + + /** * Trigger recalculate functinality for conference calls. This is used when a Telephony * Connection is part of a conference controller but is not yet added to Connection * Service and hence cannot be added to the conference call. @@ -2289,6 +2640,36 @@ public abstract class ConnectionService extends Service { } /** + * Create a {@code Conference} given an outgoing request. This is used to initiate new + * outgoing conference call. + * + * @param connectionManagerPhoneAccount The connection manager account to use for managing + * this call. + * <p> + * If this parameter is not {@code null}, it means that this {@code ConnectionService} + * has registered one or more {@code PhoneAccount}s having + * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. This parameter will contain + * one of these {@code PhoneAccount}s, while the {@code request} will contain another + * (usually but not always distinct) {@code PhoneAccount} to be used for actually + * making the connection. + * <p> + * If this parameter is {@code null}, it means that this {@code ConnectionService} is + * being asked to make a direct connection. The + * {@link ConnectionRequest#getAccountHandle()} of parameter {@code request} will be + * a {@code PhoneAccount} registered by this {@code ConnectionService} to use for + * making the connection. + * @param request Details about the outgoing call. + * @return The {@code Conference} object to satisfy this call, or the result of an invocation + * of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call. + */ + public @Nullable Conference onCreateOutgoingConference( + @Nullable PhoneAccountHandle connectionManagerPhoneAccount, + @Nullable ConnectionRequest request) { + return null; + } + + + /** * Called by Telecom to request that a {@link ConnectionService} creates an instance of an * outgoing handover {@link Connection}. * <p> diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java index 04e930ccd954..8f273233044e 100644 --- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java +++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java @@ -100,6 +100,19 @@ final class ConnectionServiceAdapter implements DeathRecipient { } } + void handleCreateConferenceComplete( + String id, + ConnectionRequest request, + ParcelableConference conference) { + for (IConnectionServiceAdapter adapter : mAdapters) { + try { + adapter.handleCreateConferenceComplete(id, request, conference, + Log.getExternalSession()); + } catch (RemoteException e) { + } + } + } + /** * Sets a call's state to active (e.g., an ongoing call where two parties can actively * communicate). diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java index 60b2172fdeca..79ad51b92b81 100644 --- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java +++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java @@ -75,6 +75,7 @@ final class ConnectionServiceAdapterServant { private static final int MSG_SET_PHONE_ACCOUNT_CHANGED = 34; private static final int MSG_CONNECTION_SERVICE_FOCUS_RELEASED = 35; private static final int MSG_SET_CONFERENCE_STATE = 36; + private static final int MSG_HANDLE_CREATE_CONFERENCE_COMPLETE = 37; private final IConnectionServiceAdapter mDelegate; @@ -103,6 +104,19 @@ final class ConnectionServiceAdapterServant { } break; } + case MSG_HANDLE_CREATE_CONFERENCE_COMPLETE: { + SomeArgs args = (SomeArgs) msg.obj; + try { + mDelegate.handleCreateConferenceComplete( + (String) args.arg1, + (ConnectionRequest) args.arg2, + (ParcelableConference) args.arg3, + null /*Session.Info*/); + } finally { + args.recycle(); + } + break; + } case MSG_SET_ACTIVE: mDelegate.setActive((String) msg.obj, null /*Session.Info*/); break; @@ -366,6 +380,20 @@ final class ConnectionServiceAdapterServant { } @Override + public void handleCreateConferenceComplete( + String id, + ConnectionRequest request, + ParcelableConference conference, + Session.Info sessionInfo) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = id; + args.arg2 = request; + args.arg3 = conference; + mHandler.obtainMessage(MSG_HANDLE_CREATE_CONFERENCE_COMPLETE, args).sendToTarget(); + } + + + @Override public void setActive(String connectionId, Session.Info sessionInfo) { mHandler.obtainMessage(MSG_SET_ACTIVE, connectionId).sendToTarget(); } diff --git a/telecomm/java/android/telecom/ParcelableConference.java b/telecomm/java/android/telecom/ParcelableConference.java index ede05943772e..90b69a338c7e 100644 --- a/telecomm/java/android/telecom/ParcelableConference.java +++ b/telecomm/java/android/telecom/ParcelableConference.java @@ -47,6 +47,34 @@ public final class ParcelableConference implements Parcelable { private final int mAddressPresentation; private final String mCallerDisplayName; private final int mCallerDisplayNamePresentation; + private DisconnectCause mDisconnectCause; + private boolean mRingbackRequested; + + public ParcelableConference( + PhoneAccountHandle phoneAccount, + int state, + int connectionCapabilities, + int connectionProperties, + List<String> connectionIds, + IVideoProvider videoProvider, + int videoState, + long connectTimeMillis, + long connectElapsedTimeMillis, + StatusHints statusHints, + Bundle extras, + Uri address, + int addressPresentation, + String callerDisplayName, + int callerDisplayNamePresentation, + DisconnectCause disconnectCause, + boolean ringbackRequested) { + this(phoneAccount, state, connectionCapabilities, connectionProperties, connectionIds, + videoProvider, videoState, connectTimeMillis, connectElapsedTimeMillis, + statusHints, extras, address, addressPresentation, callerDisplayName, + callerDisplayNamePresentation); + mDisconnectCause = disconnectCause; + mRingbackRequested = ringbackRequested; + } public ParcelableConference( PhoneAccountHandle phoneAccount, @@ -79,6 +107,8 @@ public final class ParcelableConference implements Parcelable { mAddressPresentation = addressPresentation; mCallerDisplayName = callerDisplayName; mCallerDisplayNamePresentation = callerDisplayNamePresentation; + mDisconnectCause = null; + mRingbackRequested = false; } @Override @@ -100,6 +130,10 @@ public final class ParcelableConference implements Parcelable { .append(mVideoState) .append(", VideoProvider: ") .append(mVideoProvider) + .append(", isRingbackRequested: ") + .append(mRingbackRequested) + .append(", disconnectCause: ") + .append(mDisconnectCause) .toString(); } @@ -151,6 +185,13 @@ public final class ParcelableConference implements Parcelable { return mAddress; } + public final DisconnectCause getDisconnectCause() { + return mDisconnectCause; + } + + public boolean isRingbackRequested() { + return mRingbackRequested; + } public int getHandlePresentation() { return mAddressPresentation; } @@ -177,11 +218,14 @@ public final class ParcelableConference implements Parcelable { int addressPresentation = source.readInt(); String callerDisplayName = source.readString(); int callerDisplayNamePresentation = source.readInt(); + DisconnectCause disconnectCause = source.readParcelable(classLoader); + boolean isRingbackRequested = source.readInt() == 1; return new ParcelableConference(phoneAccount, state, capabilities, properties, connectionIds, videoCallProvider, videoState, connectTimeMillis, connectElapsedTimeMillis, statusHints, extras, address, addressPresentation, - callerDisplayName, callerDisplayNamePresentation); + callerDisplayName, callerDisplayNamePresentation, disconnectCause, + isRingbackRequested); } @Override @@ -215,5 +259,7 @@ public final class ParcelableConference implements Parcelable { destination.writeInt(mAddressPresentation); destination.writeString(mCallerDisplayName); destination.writeInt(mCallerDisplayNamePresentation); + destination.writeParcelable(mDisconnectCause, 0); + destination.writeInt(mRingbackRequested ? 1 : 0); } } diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java index bb858cb0761b..abb210f13376 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -331,7 +331,17 @@ public final class PhoneAccount implements Parcelable { */ public static final int CAPABILITY_EMERGENCY_PREFERRED = 0x2000; - /* NEXT CAPABILITY: 0x4000 */ + /** + * An adhoc conference call is established by providing a list of addresses to + * {@code TelecomManager#startConference(List<Uri>, int videoState)} where the + * {@link ConnectionService} is responsible for connecting all indicated participants + * to a conference simultaneously. + * This is in contrast to conferences formed by merging calls together (e.g. using + * {@link android.telecom.Call#mergeConference()}). + */ + public static final int CAPABILITY_ADHOC_CONFERENCE_CALLING = 0x4000; + + /* NEXT CAPABILITY: 0x8000 */ /** * URI scheme for telephone number URIs. @@ -1054,6 +1064,9 @@ public final class PhoneAccount implements Parcelable { if (hasCapabilities(CAPABILITY_RTT)) { sb.append("Rtt"); } + if (hasCapabilities(CAPABILITY_ADHOC_CONFERENCE_CALLING)) { + sb.append("AdhocConf"); + } return sb.toString(); } diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java index 1e73bd61d68e..76640e036eeb 100644 --- a/telecomm/java/android/telecom/RemoteConnectionService.java +++ b/telecomm/java/android/telecom/RemoteConnectionService.java @@ -101,6 +101,14 @@ final class RemoteConnectionService { } @Override + public void handleCreateConferenceComplete( + String id, + ConnectionRequest request, + ParcelableConference parcel, + Session.Info info) { + } + + @Override public void setActive(String callId, Session.Info sessionInfo) { if (mConnectionById.containsKey(callId)) { findConnectionForAction(callId, "setActive") diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index c3fb51076bba..49b74c6061f4 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -1810,6 +1810,45 @@ public class TelecomManager { } /** + * Registers a new incoming conference. A {@link ConnectionService} should invoke this method + * when it has an incoming conference. For managed {@link ConnectionService}s, the specified + * {@link PhoneAccountHandle} must have been registered with {@link #registerPhoneAccount} and + * the user must have enabled the corresponding {@link PhoneAccount}. This can be checked using + * {@link #getPhoneAccount}. Self-managed {@link ConnectionService}s must have + * {@link android.Manifest.permission#MANAGE_OWN_CALLS} to add a new incoming call. + * <p> + * The incoming conference you are adding is assumed to have a video state of + * {@link VideoProfile#STATE_AUDIO_ONLY}, unless the extra value + * {@link #EXTRA_INCOMING_VIDEO_STATE} is specified. + * <p> + * Once invoked, this method will cause the system to bind to the {@link ConnectionService} + * associated with the {@link PhoneAccountHandle} and request additional information about the + * call (See {@link ConnectionService#onCreateIncomingConference}) before starting the incoming + * call UI. + * <p> + * For a managed {@link ConnectionService}, a {@link SecurityException} will be thrown if either + * the {@link PhoneAccountHandle} does not correspond to a registered {@link PhoneAccount} or + * the associated {@link PhoneAccount} is not currently enabled by the user. + * + * @param phoneAccount A {@link PhoneAccountHandle} registered with + * {@link #registerPhoneAccount}. + * @param extras A bundle that will be passed through to + * {@link ConnectionService#onCreateIncomingConference}. + */ + + public void addNewIncomingConference(@NonNull PhoneAccountHandle phoneAccount, + @NonNull Bundle extras) { + try { + if (isServiceConnected()) { + getTelecomService().addNewIncomingConference( + phoneAccount, extras == null ? new Bundle() : extras); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException adding a new incoming conference: " + phoneAccount, e); + } + } + + /** * Registers a new unknown call with Telecom. This can only be called by the system Telephony * service. This is invoked when Telephony detects a new unknown connection that was neither * a new incoming call, nor an user-initiated outgoing call. @@ -2014,6 +2053,42 @@ public class TelecomManager { } } + + /** + * Place a new conference call with the provided participants using the system telecom service + * This method doesn't support placing of emergency calls. + * + * An adhoc conference call is established by providing a list of addresses to + * {@code TelecomManager#startConference(List<Uri>, int videoState)} where the + * {@link ConnectionService} is responsible for connecting all indicated participants + * to a conference simultaneously. + * This is in contrast to conferences formed by merging calls together (e.g. using + * {@link android.telecom.Call#mergeConference()}). + * + * The following keys are supported in the supplied extras. + * <ul> + * <li>{@link #EXTRA_PHONE_ACCOUNT_HANDLE}</li> + * <li>{@link #EXTRA_START_CALL_WITH_SPEAKERPHONE}</li> + * <li>{@link #EXTRA_START_CALL_WITH_VIDEO_STATE}</li> + * </ul> + * + * @param participants List of participants to start conference with + * @param extras Bundle of extras to use with the call + */ + @RequiresPermission(android.Manifest.permission.CALL_PHONE) + public void startConference(@NonNull List<Uri> participants, + @NonNull Bundle extras) { + ITelecomService service = getTelecomService(); + if (service != null) { + try { + service.startConference(participants, extras, + mContext.getOpPackageName()); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelecomService#placeCall", e); + } + } + } + /** * Enables and disables specified phone account. * diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl index e35093c9656a..96f2483f32f9 100644 --- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl +++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl @@ -53,6 +53,20 @@ oneway interface IConnectionService { void createConnectionFailed(in PhoneAccountHandle connectionManagerPhoneAccount, String callId, in ConnectionRequest request, boolean isIncoming, in Session.Info sessionInfo); + void createConference( + in PhoneAccountHandle connectionManagerPhoneAccount, + String callId, + in ConnectionRequest request, + boolean isIncoming, + boolean isUnknown, + in Session.Info sessionInfo); + + void createConferenceComplete(String callId, in Session.Info sessionInfo); + + void createConferenceFailed(in PhoneAccountHandle connectionManagerPhoneAccount, String callId, + in ConnectionRequest request, boolean isIncoming, in Session.Info sessionInfo); + + void abort(String callId, in Session.Info sessionInfo); void answerVideo(String callId, int videoState, in Session.Info sessionInfo); diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl index 9cf098c75177..4f63e08abce6 100644 --- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl +++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl @@ -44,6 +44,12 @@ oneway interface IConnectionServiceAdapter { in ParcelableConnection connection, in Session.Info sessionInfo); + void handleCreateConferenceComplete( + String callId, + in ConnectionRequest request, + in ParcelableConference connection, + in Session.Info sessionInfo); + void setActive(String callId, in Session.Info sessionInfo); void setRinging(String callId, in Session.Info sessionInfo); diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index c54da6b4d527..285cf43cd3a1 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -246,11 +246,22 @@ interface ITelecomService { void addNewIncomingCall(in PhoneAccountHandle phoneAccount, in Bundle extras); /** + * @see TelecomServiceImpl#addNewIncomingConference + */ + void addNewIncomingConference(in PhoneAccountHandle phoneAccount, in Bundle extras); + + /** * @see TelecomServiceImpl#addNewUnknownCall */ void addNewUnknownCall(in PhoneAccountHandle phoneAccount, in Bundle extras); /** + * @see TelecomServiceImpl#startConference + */ + void startConference(in List<Uri> participants, in Bundle extras, + String callingPackage); + + /** * @see TelecomServiceImpl#placeCall */ void placeCall(in Uri handle, in Bundle extras, String callingPackage, String callingFeatureId); diff --git a/telephony/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 2dd1dc11c2a9..31fe4d7683d6 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/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java index d325cd84855c..610eef80c571 100644 --- a/telephony/java/android/telephony/AccessNetworkConstants.java +++ b/telephony/java/android/telephony/AccessNetworkConstants.java @@ -67,6 +67,22 @@ public final class AccessNetworkConstants { } } + /** + * Access network type + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"RADIO_ACCESS_NETWORK_TYPE_"}, + value = { + AccessNetworkType.UNKNOWN, + AccessNetworkType.GERAN, + AccessNetworkType.UTRAN, + AccessNetworkType.EUTRAN, + AccessNetworkType.CDMA2000, + AccessNetworkType.IWLAN, + AccessNetworkType.NGRAN}) + public @interface RadioAccessNetworkType {} + public static final class AccessNetworkType { public static final int UNKNOWN = 0; public static final int GERAN = 1; @@ -115,11 +131,11 @@ public final class AccessNetworkConstants { public static final int BAND_ER900 = 14; /** @hide */ - private GeranBand() {}; + private GeranBand() {} } /** - * Frenquency bands for UTRAN. + * Frequency bands for UTRAN. * http://www.etsi.org/deliver/etsi_ts/125100_125199/125104/13.03.00_60/ts_125104v130p.pdf */ public static final class UtranBand { @@ -146,12 +162,19 @@ public final class AccessNetworkConstants { public static final int BAND_25 = 25; public static final int BAND_26 = 26; + /** Frequency bands for TD-SCDMA. Defined in 3GPP TS 25.102, Table 5.2. */ + public static final int BAND_A = 101; + public static final int BAND_B = 102; + public static final int BAND_C = 103; + public static final int BAND_D = 104; + public static final int BAND_E = 105; + public static final int BAND_F = 106; /** @hide */ - private UtranBand() {}; + private UtranBand() {} } /** - * Frenquency bands for EUTRAN. + * Frequency bands for EUTRAN. * http://www.etsi.org/deliver/etsi_ts/136100_136199/136101/14.03.00_60/ts_136101v140p.pdf */ public static final class EutranBand { @@ -209,7 +232,7 @@ public final class AccessNetworkConstants { } /** - * Frenquency bands for CDMA2000. + * Frequency bands for CDMA2000. * http://www.3gpp2.org/Public_html/Specs/C.S0057-E_v1.0_Bandclass_Specification.pdf * @hide * @@ -240,7 +263,7 @@ public final class AccessNetworkConstants { public static final int BAND_21 = 22; /** @hide */ - private CdmaBands() {}; + private CdmaBands() {} } /** @@ -294,8 +317,161 @@ public final class AccessNetworkConstants { public static final int BAND_260 = 260; public static final int BAND_261 = 261; + /** + * NR Bands + * + * @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"BAND_"}, + value = {BAND_1, + BAND_2, + BAND_3, + BAND_5, + BAND_7, + BAND_8, + BAND_12, + BAND_14, + BAND_18, + BAND_20, + BAND_25, + BAND_28, + BAND_29, + BAND_30, + BAND_34, + BAND_38, + BAND_39, + BAND_40, + BAND_41, + BAND_48, + BAND_50, + BAND_51, + BAND_65, + BAND_66, + BAND_70, + BAND_71, + BAND_74, + BAND_75, + BAND_76, + BAND_77, + BAND_78, + BAND_79, + BAND_80, + BAND_81, + BAND_82, + BAND_83, + BAND_84, + BAND_86, + BAND_90, + BAND_257, + BAND_258, + BAND_260, + BAND_261}) + public @interface NgranBand {} + + /** + * Unknown NR frequency. + * + * @hide + */ + @SystemApi + @TestApi + public static final int FREQUENCY_RANGE_GROUP_UNKNOWN = 0; + + /** + * NR frequency group 1 defined in 3GPP TS 38.101-1 table 5.2-1 + * + * @hide + */ + @SystemApi + @TestApi + public static final int FREQUENCY_RANGE_GROUP_1 = 1; + + /** + * NR frequency group 2 defined in 3GPP TS 38.101-2 table 5.2-1 + * + * @hide + */ + @SystemApi + @TestApi + public static final int FREQUENCY_RANGE_GROUP_2 = 2; + + /** + * Radio frequency range group + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"FREQUENCY_RANGE_GROUP_"}, + value = { + FREQUENCY_RANGE_GROUP_UNKNOWN, + FREQUENCY_RANGE_GROUP_1, + FREQUENCY_RANGE_GROUP_2}) + public @interface FrequencyRangeGroup {} + + /** + * Get frequency range group + * + * @param band NR band + * @return The frequency range group + * + * @hide + */ + @SystemApi + @TestApi + public static @FrequencyRangeGroup int getFrequencyRangeGroup(@NgranBand int band) { + switch (band) { + case BAND_1: + case BAND_2: + case BAND_3: + case BAND_5: + case BAND_7: + case BAND_8: + case BAND_12: + case BAND_14: + case BAND_18: + case BAND_20: + case BAND_25: + case BAND_28: + case BAND_29: + case BAND_30: + case BAND_34: + case BAND_38: + case BAND_39: + case BAND_40: + case BAND_41: + case BAND_48: + case BAND_50: + case BAND_51: + case BAND_65: + case BAND_66: + case BAND_70: + case BAND_71: + case BAND_74: + case BAND_75: + case BAND_76: + case BAND_77: + case BAND_78: + case BAND_79: + case BAND_80: + case BAND_81: + case BAND_82: + case BAND_83: + case BAND_84: + case BAND_86: + case BAND_90: + return FREQUENCY_RANGE_GROUP_1; + case BAND_257: + case BAND_258: + case BAND_260: + case BAND_261: + return FREQUENCY_RANGE_GROUP_2; + default: + return FREQUENCY_RANGE_GROUP_UNKNOWN; + } + }; + /** @hide */ - private NgranBands() {}; + private NgranBands() {} } /** @hide */ diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index ff31d3e95997..b30f5868cc63 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -17,6 +17,7 @@ package android.telephony; import android.Manifest; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -27,6 +28,7 @@ import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; +import android.net.ipsec.ike.SaProposal; import android.os.PersistableBundle; import android.os.RemoteException; import android.service.carrier.CarrierService; @@ -1078,6 +1080,14 @@ public class CarrierConfigManager { public static final String KEY_IGNORE_RTT_MODE_SETTING_BOOL = "ignore_rtt_mode_setting_bool"; + + /** + * Determines whether adhoc conference calls are supported by a carrier. When {@code true}, + * adhoc conference calling is supported, {@code false otherwise}. + */ + public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL = + "support_adhoc_conference_calls_bool"; + /** * Determines whether conference calls are supported by a carrier. When {@code true}, * conference calling is supported, {@code false otherwise}. @@ -1444,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 */ @@ -2965,7 +3019,6 @@ public class CarrierConfigManager { /** * Controls hysteresis time in milli seconds for which OpportunisticNetworkService * will wait before switching data from opportunistic network to primary network. - * @hide */ public static final String KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG = "opportunistic_network_data_switch_exit_hysteresis_time_long"; @@ -2973,14 +3026,12 @@ public class CarrierConfigManager { /** * Controls whether to do ping test before switching data to opportunistic network. * This carrier config is used to disable this feature. - * @hide */ public static final String KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL = "ping_test_before_data_switch_bool"; /** * Controls time in milliseconds until DcTracker reevaluates 5G connection state. - * @hide */ public static final String KEY_5G_WATCHDOG_TIME_MS_LONG = "5g_watchdog_time_long"; @@ -2989,7 +3040,6 @@ public class CarrierConfigManager { * if primary is out of service. This control only affects system or 1st party app * initiated data switch, but will not override data switch initiated by privileged carrier apps * This carrier config is used to disable this feature. - * @hide */ public static final String KEY_SWITCH_DATA_TO_PRIMARY_IF_PRIMARY_IS_OOS_BOOL = "switch_data_to_primary_if_primary_is_oos_bool"; @@ -3001,7 +3051,6 @@ public class CarrierConfigManager { * #KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT within * #KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG of switching to opportunistic network, * it will be determined as ping pong situation by system app or 1st party app. - * @hide */ public static final String KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG = "opportunistic_network_ping_pong_time_long"; @@ -3015,7 +3064,6 @@ public class CarrierConfigManager { * #KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG. * If ping pong situation continuous #KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG * will be added to previously determined hysteresis time. - * @hide */ public static final String KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG = "opportunistic_network_backoff_time_long"; @@ -3027,7 +3075,6 @@ public class CarrierConfigManager { * continuous ping pong situation or not as described in * #KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG and * #KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG. - * @hide */ public static final String KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG = "opportunistic_network_max_backoff_time_long"; @@ -3074,7 +3121,6 @@ public class CarrierConfigManager { * validation result, this value defines customized value of how long we wait for validation * success before we fail and revoke the switch. * Time out is in milliseconds. - * @hide */ public static final String KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG = "data_switch_validation_timeout_long"; @@ -3417,6 +3463,377 @@ public class CarrierConfigManager { public static final String KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL = "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 + * Protocol Version 2 (IKEv2)</a> + */ + public static final class Iwlan { + /** Prefix of all Epdg.KEY_* constants. */ + public static final String KEY_PREFIX = "iwlan."; + + /** + * Time in seconds after which the child security association session is terminated if + * rekey procedure is not successful. If not set or set to <= 0, the default value is + * 3600 seconds. + */ + public static final String KEY_CHILD_SA_REKEY_HARD_TIMER_SEC_INT = + KEY_PREFIX + "child_sa_rekey_hard_timer_sec_int"; + + /** + * Time in seconds after which the child session rekey procedure is started. If not set or + * set to <= 0, default value is 3000 seconds. + */ + public static final String KEY_CHILD_SA_REKEY_SOFT_TIMER_SEC_INT = + KEY_PREFIX + "child_sa_rekey_soft_timer_sec_int"; + + /** Supported DH groups for IKE negotiation. + * Possible values are {@link #DH_GROUP_NONE}, {@link #DH_GROUP_1024_BIT_MODP}, + * {@link #DH_GROUP_2048_BIT_MODP} + */ + public static final String KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY = + KEY_PREFIX + "diffie_hellman_groups_int_array"; + + /** + * Time in seconds after which a dead peer detection (DPD) request is sent. + * If not set or set to <= 0, default value is 120 seconds. + */ + public static final String KEY_DPD_TIMER_SEC_INT = KEY_PREFIX + "dpd_timer_sec_int"; + + /** + * Method used to authenticate epdg server. + * Possible values are {@link #AUTHENTICATION_METHOD_EAP_ONLY}, + * {@link #AUTHENTICATION_METHOD_CERT} + */ + public static final String KEY_EPDG_AUTHENTICATION_METHOD_INT = + KEY_PREFIX + "epdg_authentication_method_int"; + + /** + * A priority list of ePDG addresses to be used. + * Possible values are {@link #EPDG_ADDRESS_STATIC}, {@link #EPDG_ADDRESS_PLMN}, + * {@link #EPDG_ADDRESS_PCO} + */ + public static final String KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY = + KEY_PREFIX + "epdg_address_priority_int_array"; + + /** Epdg static IP address or FQDN */ + public static final String KEY_EPDG_STATIC_ADDRESS_STRING = + KEY_PREFIX + "epdg_static_address_string"; + + /** Epdg static IP address or FQDN for roaming */ + public static final String KEY_EPDG_STATIC_ADDRESS_ROAMING_STRING = + KEY_PREFIX + "epdg_static_address_roaming_string"; + + /** + * List of supported key sizes for AES Cipher Block Chaining (CBC) encryption mode of child + * session. + * Possible values are {@link #KEY_LEN_UNUSED}, {@link #KEY_LEN_AES_128}, + * {@link #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256} + */ + public static final String KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY = + KEY_PREFIX + "child_session_aes_cbc_key_size_int_array"; + + /** + * List of supported key sizes for AES counter (CTR) encryption mode of child session. + * Possible values are {@link #KEY_LEN_UNUSED}, {@link #KEY_LEN_AES_128}, + * {@link #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256} + */ + public static final String KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = + KEY_PREFIX + "child_encryption_aes_ctr_key_size_int_array"; + + /** + * List of supported encryption algorithms for child session. + * Possible values are {@link #ENCRYPTION_ALGORITHM_3DES}, + * {@link #ENCRYPTION_ALGORITHM_AES_CBC}, {@link #ENCRYPTION_ALGORITHM_AES_GCM_8}, + * {@link #ENCRYPTION_ALGORITHM_AES_GCM_12}, {@link #ENCRYPTION_ALGORITHM_AES_GCM_16} + */ + public static final String KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = + KEY_PREFIX + "supported_child_session_encryption_algorithms_int_array"; + + /** Controls if IKE message fragmentation is enabled. */ + public static final String KEY_IKE_FRAGMENTATION_ENABLED_BOOL = + KEY_PREFIX + "ike_fragmentation_enabled_bool"; + + /** + * Time in seconds after which the IKE session is terminated if rekey procedure is not + * successful. If not set or set to <= 0, default value is 3600 seconds. + */ + public static final String KEY_IKE_REKEY_HARD_TIMER_SEC_INT = + KEY_PREFIX + "ike_rekey_hard_timer_in_sec"; + + /** + * Time in seconds after which the IKE session rekey procedure is started. If not set or + * set to <= 0, default value is 3000 seconds. + */ + public static final String KEY_IKE_REKEY_SOFT_TIMER_SEC_INT = + KEY_PREFIX + "ike_rekey_soft_timer_sec_int"; + + /** + * List of supported key sizes for AES Cipher Block Chaining (CBC) encryption mode of IKE + * session. + * Possible values - {@link #KEY_LEN_UNUSED}, {@link #KEY_LEN_AES_128}, + * {@link #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256} + */ + public static final String KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY = + KEY_PREFIX + "ike_session_encryption_aes_cbc_key_size_int_array"; + + /** + * List of supported key sizes for AES counter (CTR) encryption mode of IKE session. + * Possible values - {@link #KEY_LEN_UNUSED}, {@link #KEY_LEN_AES_128}, + * {@link #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256} + */ + public static final String KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = + KEY_PREFIX + "ike_session_aes_ctr_key_size_int_array"; + + /** + * List of supported encryption algorithms for IKE session. + * Possible values are {@link #ENCRYPTION_ALGORITHM_3DES}, + * {@link #ENCRYPTION_ALGORITHM_AES_CBC}, {@link #ENCRYPTION_ALGORITHM_AES_GCM_8}, + * {@link #ENCRYPTION_ALGORITHM_AES_GCM_12}, {@link #ENCRYPTION_ALGORITHM_AES_GCM_16} + */ + public static final String KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = + KEY_PREFIX + "supported_ike_session_encryption_algorithms_int_array"; + + /** + * List of supported integrity algorithms for IKE session + * Possible values are {@link #INTEGRITY_ALGORITHM_NONE}, + * {@link #INTEGRITY_ALGORITHM_HMAC_SHA1_96}, {@link #INTEGRITY_ALGORITHM_AES_XCBC_96}, + * {@link #INTEGRITY_ALGORITHM_HMAC_SHA2_256_128}, + * {@link #INTEGRITY_ALGORITHM_HMAC_SHA2_384_192}, + * {@link #INTEGRITY_ALGORITHM_HMAC_SHA2_512_256} + */ + public static final String KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY = + KEY_PREFIX + "supported_integrity_algorithms_int_array"; + + /** Maximum number of retries for tunnel establishment. */ + public static final String KEY_MAX_RETRIES_INT = KEY_PREFIX + "max_retries_int"; + + /** Controls if nat traversal should be enabled. */ + public static final String KEY_NATT_ENABLED_BOOL = KEY_PREFIX + "natt_enabled_bool"; + + /** + * Time in seconds after which a NATT keep alive message is sent. If not set or set to <= 0, + * default value is 20 seconds. + */ + public static final String KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT = + KEY_PREFIX + "natt_keep_alive_timer_sec_int"; + + /** List of comma separated MCC/MNCs used to create ePDG FQDN as per 3GPP TS 23.003 */ + public static final String KEY_MCC_MNCS_STRING_ARRAY = KEY_PREFIX + "mcc_mncs_string_array"; + + /** + * List of supported pseudo random function algorithms for IKE session + * Possible values are {@link #PSEUDORANDOM_FUNCTION_HMAC_SHA1}, + * {@link #PSEUDORANDOM_FUNCTION_AES128_XCBC} + */ + public static final String KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY = KEY_PREFIX + + "supported_prf_algorithms_int_array"; + + /** + * Time in seconds after which IKE message is retransmitted. If not set or set to <= 0, + * default value is 2 seconds. + */ + public static final String KEY_RETRANSMIT_TIMER_SEC_INT = + KEY_PREFIX + "retransmit_timer_sec_int"; + + /** @hide */ + @IntDef({ + AUTHENTICATION_METHOD_EAP_ONLY, + AUTHENTICATION_METHOD_CERT + }) + public @interface AuthenticationMethodType {} + + /** + * Certificate sent from the server is ignored. Only Extensible Authentication Protocol + * (EAP) is used to authenticate the server. + * EAP_ONLY_AUTH payload is added to IKE_AUTH request if supported. + * @see <a href="https://tools.ietf.org/html/rfc5998">RFC 5998</a> + */ + public static final int AUTHENTICATION_METHOD_EAP_ONLY = 0; + /** Server is authenticated using its certificate. */ + public static final int AUTHENTICATION_METHOD_CERT = 1; + + /** @hide */ + @IntDef({ + EPDG_ADDRESS_STATIC, + EPDG_ADDRESS_PLMN, + EPDG_ADDRESS_PCO + }) + public @interface EpdgAddressType {} + + /** Use static epdg address. */ + public static final int EPDG_ADDRESS_STATIC = 0; + /** Construct the epdg address using plmn. */ + public static final int EPDG_ADDRESS_PLMN = 1; + /** + * Use the epdg address received in protocol configuration options (PCO) from the + * network. + */ + public static final int EPDG_ADDRESS_PCO = 2; + + /** @hide */ + @IntDef({ + KEY_LEN_UNUSED, + KEY_LEN_AES_128, + KEY_LEN_AES_192, + KEY_LEN_AES_256 + }) + public @interface EncrpytionKeyLengthType {} + + public static final int KEY_LEN_UNUSED = SaProposal.KEY_LEN_UNUSED; + /** AES Encryption/Ciphering Algorithm key length 128 bits. */ + public static final int KEY_LEN_AES_128 = SaProposal.KEY_LEN_AES_128; + /** AES Encryption/Ciphering Algorithm key length 192 bits. */ + public static final int KEY_LEN_AES_192 = SaProposal.KEY_LEN_AES_192; + /** AES Encryption/Ciphering Algorithm key length 256 bits. */ + public static final int KEY_LEN_AES_256 = SaProposal.KEY_LEN_AES_256; + + /** @hide */ + @IntDef({ + DH_GROUP_NONE, + DH_GROUP_1024_BIT_MODP, + DH_GROUP_2048_BIT_MODP + }) + public @interface DhGroup {} + + /** None Diffie-Hellman Group. */ + public static final int DH_GROUP_NONE = SaProposal.DH_GROUP_NONE; + /** 1024-bit MODP Diffie-Hellman Group. */ + public static final int DH_GROUP_1024_BIT_MODP = SaProposal.DH_GROUP_1024_BIT_MODP; + /** 2048-bit MODP Diffie-Hellman Group. */ + public static final int DH_GROUP_2048_BIT_MODP = SaProposal.DH_GROUP_2048_BIT_MODP; + + /** @hide */ + @IntDef({ + ENCRYPTION_ALGORITHM_3DES, + ENCRYPTION_ALGORITHM_AES_CBC, + ENCRYPTION_ALGORITHM_AES_GCM_8, + ENCRYPTION_ALGORITHM_AES_GCM_12, + ENCRYPTION_ALGORITHM_AES_GCM_16 + }) + public @interface EncryptionAlgorithm {} + + /** 3DES Encryption/Ciphering Algorithm. */ + public static final int ENCRYPTION_ALGORITHM_3DES = SaProposal.ENCRYPTION_ALGORITHM_3DES; + /** AES-CBC Encryption/Ciphering Algorithm. */ + public static final int ENCRYPTION_ALGORITHM_AES_CBC = + SaProposal.ENCRYPTION_ALGORITHM_AES_CBC; + + /** + * AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm with 8-octet ICV + * (truncation). + */ + public static final int ENCRYPTION_ALGORITHM_AES_GCM_8 = + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8; + /** + * AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm with 12-octet ICV + * (truncation). + */ + public static final int ENCRYPTION_ALGORITHM_AES_GCM_12 = + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12; + /** + * AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm with 16-octet ICV + * (truncation). + */ + public static final int ENCRYPTION_ALGORITHM_AES_GCM_16 = + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16; + + /** @hide */ + @IntDef({ + INTEGRITY_ALGORITHM_NONE, + INTEGRITY_ALGORITHM_HMAC_SHA1_96, + INTEGRITY_ALGORITHM_AES_XCBC_96, + INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, + INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, + INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 + }) + public @interface IntegrityAlgorithm {} + + /** None Authentication/Integrity Algorithm. */ + public static final int INTEGRITY_ALGORITHM_NONE = SaProposal.INTEGRITY_ALGORITHM_NONE; + /** HMAC-SHA1 Authentication/Integrity Algorithm. */ + public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 = + SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96; + /** AES-XCBC-96 Authentication/Integrity Algorithm. */ + public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 = + SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96; + /** HMAC-SHA256 Authentication/Integrity Algorithm with 128-bit truncation. */ + public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 = + SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128; + /** HMAC-SHA384 Authentication/Integrity Algorithm with 192-bit truncation. */ + public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 = + SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192; + /** HMAC-SHA512 Authentication/Integrity Algorithm with 256-bit truncation. */ + public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = + SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256; + + /** @hide */ + @IntDef({ + PSEUDORANDOM_FUNCTION_HMAC_SHA1, + PSEUDORANDOM_FUNCTION_AES128_XCBC + }) + public @interface PseudorandomFunction {} + + /** HMAC-SHA1 Pseudorandom Function. */ + public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = + SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1; + /** AES128-XCBC Pseudorandom Function. */ + public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC = + SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC; + + private Iwlan() {} + + private static PersistableBundle getDefaults() { + PersistableBundle defaults = new PersistableBundle(); + defaults.putInt(KEY_IKE_REKEY_SOFT_TIMER_SEC_INT, 3000); + defaults.putInt(KEY_IKE_REKEY_HARD_TIMER_SEC_INT, 3600); + defaults.putInt(KEY_CHILD_SA_REKEY_SOFT_TIMER_SEC_INT, 3000); + defaults.putInt(KEY_CHILD_SA_REKEY_HARD_TIMER_SEC_INT, 3600); + defaults.putInt(KEY_RETRANSMIT_TIMER_SEC_INT, 2); + defaults.putInt(KEY_DPD_TIMER_SEC_INT, 120); + defaults.putInt(KEY_MAX_RETRIES_INT, 3); + defaults.putIntArray(KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY, + new int[]{DH_GROUP_1024_BIT_MODP, DH_GROUP_2048_BIT_MODP}); + defaults.putIntArray(KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY, + new int[]{ENCRYPTION_ALGORITHM_3DES, ENCRYPTION_ALGORITHM_AES_CBC}); + defaults.putIntArray(KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY, + new int[]{ENCRYPTION_ALGORITHM_3DES, ENCRYPTION_ALGORITHM_AES_CBC}); + defaults.putIntArray(KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY, + new int[]{INTEGRITY_ALGORITHM_AES_XCBC_96, INTEGRITY_ALGORITHM_HMAC_SHA1_96, + INTEGRITY_ALGORITHM_HMAC_SHA2_256_128}); + defaults.putIntArray(KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY, + new int[]{PSEUDORANDOM_FUNCTION_HMAC_SHA1, PSEUDORANDOM_FUNCTION_AES128_XCBC}); + defaults.putBoolean(KEY_NATT_ENABLED_BOOL, true); + defaults.putInt(KEY_EPDG_AUTHENTICATION_METHOD_INT, AUTHENTICATION_METHOD_CERT); + defaults.putString(KEY_EPDG_STATIC_ADDRESS_STRING, ""); + defaults.putString(KEY_EPDG_STATIC_ADDRESS_ROAMING_STRING, ""); + defaults.putInt(KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT, 20); + defaults.putIntArray(KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY, + new int[]{KEY_LEN_AES_128, KEY_LEN_AES_256}); + defaults.putIntArray(KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY, + new int[]{KEY_LEN_AES_128}); + defaults.putIntArray(KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY, + new int[]{KEY_LEN_AES_128, KEY_LEN_AES_256}); + defaults.putIntArray(KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY, + new int[]{KEY_LEN_AES_128}); + defaults.putBoolean(KEY_IKE_FRAGMENTATION_ENABLED_BOOL, false); + defaults.putIntArray(KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, new int[]{EPDG_ADDRESS_PLMN, + EPDG_ADDRESS_STATIC}); + defaults.putStringArray(KEY_MCC_MNCS_STRING_ARRAY, new String[]{}); + + return defaults; + } + } + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -3538,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[]{ @@ -3583,6 +4002,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL, false); sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, false); sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0); + sDefaults.putBoolean(KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL, false); sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true); @@ -3908,6 +4328,8 @@ 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/CdmaEriInformation.java b/telephony/java/android/telephony/CdmaEriInformation.java new file mode 100644 index 000000000000..1cd9d3048526 --- /dev/null +++ b/telephony/java/android/telephony/CdmaEriInformation.java @@ -0,0 +1,170 @@ +/** + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * CDMA ERI (Enhanced Roaming Indicator) information. + * + * This contains the following ERI information + * + * 1. ERI (Enhanced Roaming Indicator) icon index. The number is assigned by + * 3GPP2 C.R1001-H v1.0 Table 8.1-1. Additionally carriers define their own + * ERI icon index. + * 2. CDMA ERI icon mode. This represents how the icon should be displayed. + * Its one of the following CDMA ERI icon mode + * {@link android.telephony.CdmaEriInformation#ERI_ICON_MODE_NORMAL} + * {@link android.telephony.CdmaEriInformation#ERI_ICON_MODE_FLASH} + * + * @hide + */ +@SystemApi +public final class CdmaEriInformation implements Parcelable { + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"ERI_"}, value = { + ERI_ON, + ERI_OFF, + ERI_FLASH + }) + public @interface EriIconIndex {} + + /** + * ERI (Enhanced Roaming Indicator) is ON i.e value 0 defined by + * 3GPP2 C.R1001-H v1.0 Table 8.1-1. + */ + public static final int ERI_ON = 0; + + /** + * ERI (Enhanced Roaming Indicator) is OFF i.e value 1 defined by + * 3GPP2 C.R1001-H v1.0 Table 8.1-1. + */ + public static final int ERI_OFF = 1; + + /** + * ERI (Enhanced Roaming Indicator) is FLASH i.e value 2 defined by + * 3GPP2 C.R1001-H v1.0 Table 8.1-1. + */ + public static final int ERI_FLASH = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"ERI_ICON_MODE_"}, value = { + ERI_ICON_MODE_NORMAL, + ERI_ICON_MODE_FLASH + }) + public @interface EriIconMode {} + + /** + * ERI (Enhanced Roaming Indicator) icon mode is normal. This constant represents that + * the ERI icon should be displayed normally. + * + * Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1 + */ + public static final int ERI_ICON_MODE_NORMAL = 0; + + /** + * ERI (Enhanced Roaming Indicator) icon mode flash. This constant represents that + * the ERI icon should be flashing. + * + * Note: ERI is defined 3GPP2 C.R1001-H Table 8.1-1 + */ + public static final int ERI_ICON_MODE_FLASH = 1; + + private @EriIconIndex int mIconIndex; + private @EriIconMode int mIconMode; + + /** + * Creates CdmaEriInformation from iconIndex and iconMode + * + * @hide + */ + public CdmaEriInformation(@EriIconIndex int iconIndex, @EriIconMode int iconMode) { + mIconIndex = iconIndex; + mIconMode = iconMode; + } + + /** Gets the ERI icon index */ + public @EriIconIndex int getEriIconIndex() { + return mIconIndex; + } + + /** + * Sets the ERI icon index + * + * @hide + */ + public void setEriIconIndex(@EriIconIndex int iconIndex) { + mIconIndex = iconIndex; + } + + /** Gets the ERI icon mode */ + public @EriIconMode int getEriIconMode() { + return mIconMode; + } + + /** + * Sets the ERI icon mode + * + * @hide + */ + public void setEriIconMode(@EriIconMode int iconMode) { + mIconMode = iconMode; + } + /** Implement the Parcelable interface */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mIconIndex); + dest.writeInt(mIconMode); + } + + /** Implement the Parcelable interface */ + @Override + public int describeContents() { + return 0; + } + + /** + * Construct a CdmaEriInformation object from the given parcel + */ + private CdmaEriInformation(Parcel in) { + mIconIndex = in.readInt(); + mIconMode = in.readInt(); + } + + /** Implement the Parcelable interface */ + public static final @android.annotation.NonNull Parcelable.Creator<CdmaEriInformation> CREATOR = + new Parcelable.Creator<CdmaEriInformation>() { + @Override + public CdmaEriInformation createFromParcel(Parcel in) { + return new CdmaEriInformation(in); + } + + @Override + public CdmaEriInformation[] newArray(int size) { + return new CdmaEriInformation[size]; + } + }; +} diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java index 49f425acead6..dc73cbf735b0 100644 --- a/telephony/java/android/telephony/CellIdentityGsm.java +++ b/telephony/java/android/telephony/CellIdentityGsm.java @@ -23,6 +23,8 @@ import android.os.Parcel; import android.telephony.gsm.GsmCellLocation; import android.text.TextUtils; +import java.util.Collections; +import java.util.List; import java.util.Objects; /** @@ -46,6 +48,9 @@ public final class CellIdentityGsm extends CellIdentity { // 6-bit Base Station Identity Code private final int mBsic; + // a list of additional PLMN-IDs reported for this cell + private final List<String> mAdditionalPlmns; + /** * @hide */ @@ -56,6 +61,7 @@ public final class CellIdentityGsm extends CellIdentity { mCid = CellInfo.UNAVAILABLE; mArfcn = CellInfo.UNAVAILABLE; mBsic = CellInfo.UNAVAILABLE; + mAdditionalPlmns = Collections.emptyList(); } /** @@ -68,35 +74,48 @@ public final class CellIdentityGsm extends CellIdentity { * @param mncStr 2 or 3-digit Mobile Network Code in string format * @param alphal long alpha Operator Name String or Enhanced Operator Name String * @param alphas short alpha Operator Name String or Enhanced Operator Name String + * @param additionalPlmns a list of additional PLMN IDs broadcast by the cell * * @hide */ public CellIdentityGsm(int lac, int cid, int arfcn, int bsic, String mccStr, - String mncStr, String alphal, String alphas) { + String mncStr, String alphal, String alphas, + List<String> additionalPlmns) { super(TAG, CellInfo.TYPE_GSM, mccStr, mncStr, alphal, alphas); mLac = inRangeOrUnavailable(lac, 0, MAX_LAC); mCid = inRangeOrUnavailable(cid, 0, MAX_CID); mArfcn = inRangeOrUnavailable(arfcn, 0, MAX_ARFCN); mBsic = inRangeOrUnavailable(bsic, 0, MAX_BSIC); + mAdditionalPlmns = additionalPlmns; } /** @hide */ public CellIdentityGsm(android.hardware.radio.V1_0.CellIdentityGsm cid) { this(cid.lac, cid.cid, cid.arfcn, cid.bsic == (byte) 0xFF ? CellInfo.UNAVAILABLE : cid.bsic, - cid.mcc, cid.mnc, "", ""); + cid.mcc, cid.mnc, "", "", Collections.emptyList()); } /** @hide */ public CellIdentityGsm(android.hardware.radio.V1_2.CellIdentityGsm cid) { this(cid.base.lac, cid.base.cid, cid.base.arfcn, cid.base.bsic == (byte) 0xFF ? CellInfo.UNAVAILABLE : cid.base.bsic, cid.base.mcc, - cid.base.mnc, cid.operatorNames.alphaLong, cid.operatorNames.alphaShort); + cid.base.mnc, cid.operatorNames.alphaLong, cid.operatorNames.alphaShort, + Collections.emptyList()); + } + + /** @hide */ + public CellIdentityGsm(android.hardware.radio.V1_5.CellIdentityGsm cid) { + this(cid.base.base.lac, cid.base.base.cid, cid.base.base.arfcn, + cid.base.base.bsic == (byte) 0xFF ? CellInfo.UNAVAILABLE + : cid.base.base.bsic, cid.base.base.mcc, + cid.base.base.mnc, cid.base.operatorNames.alphaLong, + cid.base.operatorNames.alphaShort, cid.additionalPlmns); } private CellIdentityGsm(CellIdentityGsm cid) { this(cid.mLac, cid.mCid, cid.mArfcn, cid.mBsic, cid.mMccStr, - cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort); + cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort, cid.mAdditionalPlmns); } CellIdentityGsm copy() { @@ -107,7 +126,7 @@ public final class CellIdentityGsm extends CellIdentity { @Override public @NonNull CellIdentityGsm sanitizeLocationInfo() { return new CellIdentityGsm(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, - CellInfo.UNAVAILABLE, mMccStr, mMncStr, mAlphaLong, mAlphaShort); + CellInfo.UNAVAILABLE, mMccStr, mMncStr, mAlphaLong, mAlphaShort, mAdditionalPlmns); } /** @@ -193,6 +212,14 @@ public final class CellIdentityGsm extends CellIdentity { } /** + * @return a list of additional PLMN IDs supported by this cell. + */ + @NonNull + public List<String> getAdditionalPlmns() { + return mAdditionalPlmns; + } + + /** * @deprecated Primary Scrambling Code is not applicable to GSM. * @return {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} - undefined for GSM */ @@ -215,7 +242,7 @@ public final class CellIdentityGsm extends CellIdentity { @Override public int hashCode() { - return Objects.hash(mLac, mCid, super.hashCode()); + return Objects.hash(mLac, mCid, mAdditionalPlmns.hashCode(), super.hashCode()); } @Override @@ -235,6 +262,7 @@ public final class CellIdentityGsm extends CellIdentity { && mBsic == o.mBsic && TextUtils.equals(mMccStr, o.mMccStr) && TextUtils.equals(mMncStr, o.mMncStr) + && mAdditionalPlmns.equals(o.mAdditionalPlmns) && super.equals(other); } @@ -249,6 +277,7 @@ public final class CellIdentityGsm extends CellIdentity { .append(" mMnc=").append(mMncStr) .append(" mAlphaLong=").append(mAlphaLong) .append(" mAlphaShort=").append(mAlphaShort) + .append(" mAdditionalPlmns=").append(mAdditionalPlmns) .append("}").toString(); } @@ -261,6 +290,7 @@ public final class CellIdentityGsm extends CellIdentity { dest.writeInt(mCid); dest.writeInt(mArfcn); dest.writeInt(mBsic); + dest.writeList(mAdditionalPlmns); } /** Construct from Parcel, type has already been processed */ @@ -270,6 +300,7 @@ public final class CellIdentityGsm extends CellIdentity { mCid = in.readInt(); mArfcn = in.readInt(); mBsic = in.readInt(); + mAdditionalPlmns = in.readArrayList(null); if (DBG) log(toString()); } diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java index bc4655069dba..cf8fe6a3c345 100644 --- a/telephony/java/android/telephony/CellIdentityLte.java +++ b/telephony/java/android/telephony/CellIdentityLte.java @@ -24,6 +24,8 @@ import android.os.Parcel; import android.telephony.gsm.GsmCellLocation; import android.text.TextUtils; +import java.util.Collections; +import java.util.List; import java.util.Objects; /** @@ -50,6 +52,11 @@ public final class CellIdentityLte extends CellIdentity { // cell bandwidth, in kHz private final int mBandwidth; + // a list of additional PLMN-IDs reported for this cell + private final List<String> mAdditionalPlmns; + + private ClosedSubscriberGroupInfo mCsgInfo; + /** * @hide */ @@ -61,6 +68,8 @@ public final class CellIdentityLte extends CellIdentity { mTac = CellInfo.UNAVAILABLE; mEarfcn = CellInfo.UNAVAILABLE; mBandwidth = CellInfo.UNAVAILABLE; + mAdditionalPlmns = Collections.emptyList(); + mCsgInfo = null; } /** @@ -76,7 +85,7 @@ public final class CellIdentityLte extends CellIdentity { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac) { this(ci, pci, tac, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, String.valueOf(mcc), - String.valueOf(mnc), null, null); + String.valueOf(mnc), null, null, Collections.emptyList(), null); } /** @@ -90,34 +99,49 @@ public final class CellIdentityLte extends CellIdentity { * @param mncStr 2 or 3-digit Mobile Network Code in string format * @param alphal long alpha Operator Name String or Enhanced Operator Name String * @param alphas short alpha Operator Name String or Enhanced Operator Name String + * @param additionalPlmns a list of additional PLMN IDs broadcast by the cell + * @param csgInfo info about the closed subscriber group broadcast by the cell * * @hide */ public CellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth, String mccStr, - String mncStr, String alphal, String alphas) { + String mncStr, String alphal, String alphas, List<String> additionalPlmns, + ClosedSubscriberGroupInfo csgInfo) { super(TAG, CellInfo.TYPE_LTE, mccStr, mncStr, alphal, alphas); mCi = inRangeOrUnavailable(ci, 0, MAX_CI); mPci = inRangeOrUnavailable(pci, 0, MAX_PCI); mTac = inRangeOrUnavailable(tac, 0, MAX_TAC); mEarfcn = inRangeOrUnavailable(earfcn, 0, MAX_EARFCN); mBandwidth = inRangeOrUnavailable(bandwidth, 0, MAX_BANDWIDTH); + mAdditionalPlmns = additionalPlmns; + mCsgInfo = csgInfo; } /** @hide */ public CellIdentityLte(android.hardware.radio.V1_0.CellIdentityLte cid) { - this(cid.ci, cid.pci, cid.tac, cid.earfcn, CellInfo.UNAVAILABLE, cid.mcc, cid.mnc, "", ""); + this(cid.ci, cid.pci, cid.tac, cid.earfcn, + CellInfo.UNAVAILABLE, cid.mcc, cid.mnc, "", "", Collections.emptyList(), null); } /** @hide */ public CellIdentityLte(android.hardware.radio.V1_2.CellIdentityLte cid) { this(cid.base.ci, cid.base.pci, cid.base.tac, cid.base.earfcn, cid.bandwidth, cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong, - cid.operatorNames.alphaShort); + cid.operatorNames.alphaShort, Collections.emptyList(), null); + } + + /** @hide */ + public CellIdentityLte(android.hardware.radio.V1_5.CellIdentityLte cid) { + this(cid.base.base.ci, cid.base.base.pci, cid.base.base.tac, cid.base.base.earfcn, + cid.base.bandwidth, cid.base.base.mcc, cid.base.base.mnc, + cid.base.operatorNames.alphaLong, cid.base.operatorNames.alphaShort, + cid.additionalPlmns, cid.optionalCsgInfo.csgInfo() != null + ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo()) : null); } private CellIdentityLte(CellIdentityLte cid) { this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mBandwidth, cid.mMccStr, - cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort); + cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort, cid.mAdditionalPlmns, cid.mCsgInfo); } /** @hide */ @@ -125,7 +149,7 @@ public final class CellIdentityLte extends CellIdentity { public @NonNull CellIdentityLte sanitizeLocationInfo() { return new CellIdentityLte(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, - mMccStr, mMncStr, mAlphaLong, mAlphaShort); + mMccStr, mMncStr, mAlphaLong, mAlphaShort, mAdditionalPlmns, null); } CellIdentityLte copy() { @@ -185,6 +209,19 @@ 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. */ @@ -223,6 +260,22 @@ public final class CellIdentityLte extends CellIdentity { } /** + * @return a list of additional PLMN IDs supported by this cell. + */ + @NonNull + public List<String> getAdditionalPlmns() { + return mAdditionalPlmns; + } + + /** + * @return closed subscriber group information about the cell if available, otherwise null. + */ + @Nullable + public ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo() { + return mCsgInfo; + } + + /** * A hack to allow tunneling of LTE information via GsmCellLocation * so that older Network Location Providers can return some information * on LTE only networks, see bug 9228974. @@ -247,7 +300,8 @@ public final class CellIdentityLte extends CellIdentity { @Override public int hashCode() { - return Objects.hash(mCi, mPci, mTac, super.hashCode()); + return Objects.hash(mCi, mPci, mTac, + mAdditionalPlmns.hashCode(), mCsgInfo, super.hashCode()); } @Override @@ -268,6 +322,8 @@ public final class CellIdentityLte extends CellIdentity { && mBandwidth == o.mBandwidth && TextUtils.equals(mMccStr, o.mMccStr) && TextUtils.equals(mMncStr, o.mMncStr) + && mAdditionalPlmns.equals(o.mAdditionalPlmns) + && Objects.equals(mCsgInfo, o.mCsgInfo) && super.equals(other); } @@ -283,6 +339,8 @@ public final class CellIdentityLte extends CellIdentity { .append(" mMnc=").append(mMncStr) .append(" mAlphaLong=").append(mAlphaLong) .append(" mAlphaShort=").append(mAlphaShort) + .append(" mAdditionalPlmns=").append(mAdditionalPlmns) + .append(" mCsgInfo=").append(mCsgInfo) .append("}").toString(); } @@ -296,6 +354,8 @@ public final class CellIdentityLte extends CellIdentity { dest.writeInt(mTac); dest.writeInt(mEarfcn); dest.writeInt(mBandwidth); + dest.writeList(mAdditionalPlmns); + dest.writeParcelable(mCsgInfo, flags); } /** Construct from Parcel, type has already been processed */ @@ -306,7 +366,8 @@ public final class CellIdentityLte extends CellIdentity { mTac = in.readInt(); mEarfcn = in.readInt(); mBandwidth = in.readInt(); - + mAdditionalPlmns = in.readArrayList(null); + mCsgInfo = in.readParcelable(null); if (DBG) log(toString()); } diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java index f08a5807c2b6..d4f181fc735a 100644 --- a/telephony/java/android/telephony/CellIdentityNr.java +++ b/telephony/java/android/telephony/CellIdentityNr.java @@ -20,8 +20,12 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; +import android.telephony.AccessNetworkConstants.NgranBands.NgranBand; import android.telephony.gsm.GsmCellLocation; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Objects; /** @@ -39,40 +43,58 @@ public final class CellIdentityNr extends CellIdentity { private final int mPci; private final int mTac; private final long mNci; + private final List<Integer> mBands; + + // a list of additional PLMN-IDs reported for this cell + private final List<String> mAdditionalPlmns; /** * * @param pci Physical Cell Id in range [0, 1007]. * @param tac 16-bit Tracking Area Code. * @param nrArfcn NR Absolute Radio Frequency Channel Number, in range [0, 3279165]. + * @param bands Bands used by the cell. Band number defined in 3GPP TS 38.101-1 and TS 38.101-2. * @param mccStr 3-digit Mobile Country Code in string format. * @param mncStr 2 or 3-digit Mobile Network Code in string format. * @param nci The 36-bit NR Cell Identity in range [0, 68719476735]. * @param alphal long alpha Operator Name String or Enhanced Operator Name String. * @param alphas short alpha Operator Name String or Enhanced Operator Name String. + * @param additionalPlmns a list of additional PLMN IDs broadcast by the cell * * @hide */ - public CellIdentityNr(int pci, int tac, int nrArfcn, String mccStr, String mncStr, - long nci, String alphal, String alphas) { + public CellIdentityNr(int pci, int tac, int nrArfcn, @NgranBand List<Integer> bands, + String mccStr, String mncStr, long nci, String alphal, String alphas, + List<String> additionalPlmns) { super(TAG, CellInfo.TYPE_NR, mccStr, mncStr, alphal, alphas); mPci = inRangeOrUnavailable(pci, 0, MAX_PCI); mTac = inRangeOrUnavailable(tac, 0, MAX_TAC); mNrArfcn = inRangeOrUnavailable(nrArfcn, 0, MAX_NRARFCN); + mBands = new ArrayList<>(bands); mNci = inRangeOrUnavailable(nci, 0, MAX_NCI); + mAdditionalPlmns = new ArrayList<>(additionalPlmns); } /** @hide */ public CellIdentityNr(android.hardware.radio.V1_4.CellIdentityNr cid) { - this(cid.pci, cid.tac, cid.nrarfcn, cid.mcc, cid.mnc, cid.nci, cid.operatorNames.alphaLong, - cid.operatorNames.alphaShort); + this(cid.pci, cid.tac, cid.nrarfcn, Collections.emptyList(), cid.mcc, cid.mnc, cid.nci, + cid.operatorNames.alphaLong, cid.operatorNames.alphaShort, + Collections.emptyList()); + } + + /** @hide */ + public CellIdentityNr(android.hardware.radio.V1_5.CellIdentityNr cid) { + this(cid.base.pci, cid.base.tac, cid.base.nrarfcn, cid.bands, cid.base.mcc, cid.base.mnc, + cid.base.nci, cid.base.operatorNames.alphaLong, + cid.base.operatorNames.alphaShort, cid.additionalPlmns); } /** @hide */ @Override public @NonNull CellIdentityNr sanitizeLocationInfo() { - return new CellIdentityNr(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, - mMccStr, mMncStr, CellInfo.UNAVAILABLE, mAlphaLong, mAlphaShort); + return new CellIdentityNr(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, mNrArfcn, + mBands, mMccStr, mMncStr, CellInfo.UNAVAILABLE, mAlphaLong, mAlphaShort, + mAdditionalPlmns); } /** @@ -87,7 +109,8 @@ public final class CellIdentityNr extends CellIdentity { @Override public int hashCode() { - return Objects.hash(super.hashCode(), mPci, mTac, mNrArfcn, mNci); + return Objects.hash(super.hashCode(), mPci, mTac, + mNrArfcn, mBands.hashCode(), mNci, mAdditionalPlmns.hashCode()); } @Override @@ -98,7 +121,8 @@ public final class CellIdentityNr extends CellIdentity { CellIdentityNr o = (CellIdentityNr) other; return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn - && mNci == o.mNci; + && mBands.equals(o.mBands) && mNci == o.mNci + && mAdditionalPlmns.equals(o.mAdditionalPlmns); } /** @@ -125,6 +149,20 @@ public final class CellIdentityNr extends CellIdentity { } /** + * Get bands of the cell + * + * Reference: TS 38.101-1 table 5.2-1 + * Reference: TS 38.101-2 table 5.2-1 + * + * @return List of band number or empty list if not available. + */ + @NgranBand + @NonNull + public List<Integer> getBands() { + return Collections.unmodifiableList(mBands); + } + + /** * Get the physical cell id. * @return Integer value in range [0, 1007] or {@link CellInfo#UNAVAILABLE} if unknown. */ @@ -158,17 +196,33 @@ public final class CellIdentityNr extends CellIdentity { return mMncStr; } + /** @hide */ + @Override + public int getChannelNumber() { + return mNrArfcn; + } + + /** + * @return a list of additional PLMN IDs supported by this cell. + */ + @NonNull + public List<String> getAdditionalPlmns() { + return Collections.unmodifiableList(mAdditionalPlmns); + } + @Override public String toString() { return new StringBuilder(TAG + ":{") .append(" mPci = ").append(mPci) .append(" mTac = ").append(mTac) .append(" mNrArfcn = ").append(mNrArfcn) + .append(" mBands = ").append(mBands) .append(" mMcc = ").append(mMccStr) .append(" mMnc = ").append(mMncStr) .append(" mNci = ").append(mNci) .append(" mAlphaLong = ").append(mAlphaLong) .append(" mAlphaShort = ").append(mAlphaShort) + .append(" mAdditionalPlmns = ").append(mAdditionalPlmns) .append(" }") .toString(); } @@ -179,7 +233,9 @@ public final class CellIdentityNr extends CellIdentity { dest.writeInt(mPci); dest.writeInt(mTac); dest.writeInt(mNrArfcn); + dest.writeList(mBands); dest.writeLong(mNci); + dest.writeList(mAdditionalPlmns); } /** Construct from Parcel, type has already been processed */ @@ -188,7 +244,9 @@ public final class CellIdentityNr extends CellIdentity { mPci = in.readInt(); mTac = in.readInt(); mNrArfcn = in.readInt(); + mBands = in.readArrayList(null); mNci = in.readLong(); + mAdditionalPlmns = in.readArrayList(null); } /** Implement the Parcelable interface */ diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java index 4bb3a95a9f1d..2ff351c17e9a 100644 --- a/telephony/java/android/telephony/CellIdentityTdscdma.java +++ b/telephony/java/android/telephony/CellIdentityTdscdma.java @@ -21,6 +21,8 @@ import android.annotation.Nullable; import android.os.Parcel; import android.telephony.gsm.GsmCellLocation; +import java.util.Collections; +import java.util.List; import java.util.Objects; /** @@ -46,6 +48,11 @@ public final class CellIdentityTdscdma extends CellIdentity { // 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3 private final int mUarfcn; + // a list of additional PLMN-IDs reported for this cell + private final List<String> mAdditionalPlmns; + + private ClosedSubscriberGroupInfo mCsgInfo; + /** * @hide */ @@ -55,6 +62,8 @@ public final class CellIdentityTdscdma extends CellIdentity { mCid = CellInfo.UNAVAILABLE; mCpid = CellInfo.UNAVAILABLE; mUarfcn = CellInfo.UNAVAILABLE; + mAdditionalPlmns = Collections.emptyList(); + mCsgInfo = null; } /** @@ -68,39 +77,57 @@ public final class CellIdentityTdscdma extends CellIdentity { * @param uarfcn 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3 * @param alphal long alpha Operator Name String or Enhanced Operator Name String * @param alphas short alpha Operator Name String or Enhanced Operator Name String + * @param additionalPlmns a list of additional PLMN IDs broadcast by the cell + * @param csgInfo info about the closed subscriber group broadcast by the cell * * @hide */ public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid, int uarfcn, - String alphal, String alphas) { + String alphal, String alphas, @NonNull List<String> additionalPlmns, + ClosedSubscriberGroupInfo csgInfo) { super(TAG, CellInfo.TYPE_TDSCDMA, mcc, mnc, alphal, alphas); mLac = inRangeOrUnavailable(lac, 0, MAX_LAC); mCid = inRangeOrUnavailable(cid, 0, MAX_CID); mCpid = inRangeOrUnavailable(cpid, 0, MAX_CPID); mUarfcn = inRangeOrUnavailable(uarfcn, 0, MAX_UARFCN); + mAdditionalPlmns = additionalPlmns; + mCsgInfo = csgInfo; } private CellIdentityTdscdma(CellIdentityTdscdma cid) { this(cid.mMccStr, cid.mMncStr, cid.mLac, cid.mCid, - cid.mCpid, cid.mUarfcn, cid.mAlphaLong, cid.mAlphaShort); + cid.mCpid, cid.mUarfcn, cid.mAlphaLong, + cid.mAlphaShort, cid.mAdditionalPlmns, cid.mCsgInfo); } /** @hide */ public CellIdentityTdscdma(android.hardware.radio.V1_0.CellIdentityTdscdma cid) { - this(cid.mcc, cid.mnc, cid.lac, cid.cid, cid.cpid, CellInfo.UNAVAILABLE, "", ""); + this(cid.mcc, cid.mnc, cid.lac, cid.cid, cid.cpid, CellInfo.UNAVAILABLE, "", "", + Collections.emptyList(), null); } /** @hide */ public CellIdentityTdscdma(android.hardware.radio.V1_2.CellIdentityTdscdma cid) { this(cid.base.mcc, cid.base.mnc, cid.base.lac, cid.base.cid, cid.base.cpid, - cid.uarfcn, cid.operatorNames.alphaLong, cid.operatorNames.alphaShort); + cid.uarfcn, cid.operatorNames.alphaLong, cid.operatorNames.alphaShort, + Collections.emptyList(), null); + } + + /** @hide */ + public CellIdentityTdscdma(android.hardware.radio.V1_5.CellIdentityTdscdma cid) { + this(cid.base.base.mcc, cid.base.base.mnc, cid.base.base.lac, cid.base.base.cid, + cid.base.base.cpid, cid.base.uarfcn, cid.base.operatorNames.alphaLong, + cid.base.operatorNames.alphaShort, + cid.additionalPlmns, cid.optionalCsgInfo.csgInfo() != null + ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo()) : null); } /** @hide */ @Override public @NonNull CellIdentityTdscdma sanitizeLocationInfo() { return new CellIdentityTdscdma(mMccStr, mMncStr, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, - CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, mAlphaLong, mAlphaShort); + CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, mAlphaLong, mAlphaShort, + mAdditionalPlmns, null); } CellIdentityTdscdma copy() { @@ -171,6 +198,22 @@ public final class CellIdentityTdscdma extends CellIdentity { return mUarfcn; } + /** + * @return a list of additional PLMN IDs supported by this cell. + */ + @NonNull + public List<String> getAdditionalPlmns() { + return mAdditionalPlmns; + } + + /** + * @return closed subscriber group information about the cell if available, otherwise null. + */ + @Nullable + public ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo() { + return mCsgInfo; + } + /** @hide */ @NonNull @Override @@ -198,12 +241,15 @@ public final class CellIdentityTdscdma extends CellIdentity { && mCid == o.mCid && mCpid == o.mCpid && mUarfcn == o.mUarfcn + && mAdditionalPlmns.equals(o.mAdditionalPlmns) + && Objects.equals(mCsgInfo, o.mCsgInfo) && super.equals(other); } @Override public int hashCode() { - return Objects.hash(mLac, mCid, mCpid, mUarfcn, super.hashCode()); + return Objects.hash(mLac, mCid, mCpid, mUarfcn, + mAdditionalPlmns.hashCode(), mCsgInfo, super.hashCode()); } @Override @@ -217,6 +263,8 @@ public final class CellIdentityTdscdma extends CellIdentity { .append(" mCid=").append(mCid) .append(" mCpid=").append(mCpid) .append(" mUarfcn=").append(mUarfcn) + .append(" mAdditionalPlmns=").append(mAdditionalPlmns) + .append(" mCsgInfo=").append(mCsgInfo) .append("}").toString(); } @@ -235,6 +283,8 @@ public final class CellIdentityTdscdma extends CellIdentity { dest.writeInt(mCid); dest.writeInt(mCpid); dest.writeInt(mUarfcn); + dest.writeList(mAdditionalPlmns); + dest.writeParcelable(mCsgInfo, flags); } /** Construct from Parcel, type has already been processed */ @@ -244,6 +294,8 @@ public final class CellIdentityTdscdma extends CellIdentity { mCid = in.readInt(); mCpid = in.readInt(); mUarfcn = in.readInt(); + mAdditionalPlmns = in.readArrayList(null); + mCsgInfo = in.readParcelable(null); if (DBG) log(toString()); } diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java index 4ecd134c4ba9..9be42a17677b 100644 --- a/telephony/java/android/telephony/CellIdentityWcdma.java +++ b/telephony/java/android/telephony/CellIdentityWcdma.java @@ -23,6 +23,8 @@ import android.os.Parcel; import android.telephony.gsm.GsmCellLocation; import android.text.TextUtils; +import java.util.Collections; +import java.util.List; import java.util.Objects; /** @@ -47,6 +49,12 @@ public final class CellIdentityWcdma extends CellIdentity { @UnsupportedAppUsage private final int mUarfcn; + // a list of additional PLMN-IDs reported for this cell + private final List<String> mAdditionalPlmns; + + @Nullable + private final ClosedSubscriberGroupInfo mCsgInfo; + /** * @hide */ @@ -56,6 +64,8 @@ public final class CellIdentityWcdma extends CellIdentity { mCid = CellInfo.UNAVAILABLE; mPsc = CellInfo.UNAVAILABLE; mUarfcn = CellInfo.UNAVAILABLE; + mAdditionalPlmns = Collections.emptyList(); + mCsgInfo = null; } /** @@ -68,33 +78,49 @@ public final class CellIdentityWcdma extends CellIdentity { * @param mncStr 2 or 3-digit Mobile Network Code in string format * @param alphal long alpha Operator Name String or Enhanced Operator Name String * @param alphas short alpha Operator Name String or Enhanced Operator Name String + * @param additionalPlmns a list of additional PLMN IDs broadcast by the cell + * @param csgInfo info about the closed subscriber group broadcast by the cell * * @hide */ public CellIdentityWcdma (int lac, int cid, int psc, int uarfcn, - String mccStr, String mncStr, String alphal, String alphas) { + String mccStr, String mncStr, String alphal, String alphas, + @NonNull List<String> additionalPlmns, + @Nullable ClosedSubscriberGroupInfo csgInfo) { super(TAG, CellInfo.TYPE_WCDMA, mccStr, mncStr, alphal, alphas); mLac = inRangeOrUnavailable(lac, 0, MAX_LAC); mCid = inRangeOrUnavailable(cid, 0, MAX_CID); mPsc = inRangeOrUnavailable(psc, 0, MAX_PSC); mUarfcn = inRangeOrUnavailable(uarfcn, 0, MAX_UARFCN); + mAdditionalPlmns = additionalPlmns; + mCsgInfo = csgInfo; } /** @hide */ public CellIdentityWcdma(android.hardware.radio.V1_0.CellIdentityWcdma cid) { - this(cid.lac, cid.cid, cid.psc, cid.uarfcn, cid.mcc, cid.mnc, "", ""); + this(cid.lac, cid.cid, cid.psc, cid.uarfcn, cid.mcc, cid.mnc, "", "", + Collections.emptyList(), null); } /** @hide */ public CellIdentityWcdma(android.hardware.radio.V1_2.CellIdentityWcdma cid) { this(cid.base.lac, cid.base.cid, cid.base.psc, cid.base.uarfcn, cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong, - cid.operatorNames.alphaShort); + cid.operatorNames.alphaShort, Collections.emptyList(), null); + } + + /** @hide */ + public CellIdentityWcdma(android.hardware.radio.V1_5.CellIdentityWcdma cid) { + this(cid.base.base.lac, cid.base.base.cid, cid.base.base.psc, cid.base.base.uarfcn, + cid.base.base.mcc, cid.base.base.mnc, cid.base.operatorNames.alphaLong, + cid.base.operatorNames.alphaShort, cid.additionalPlmns, + cid.optionalCsgInfo.csgInfo() != null + ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo()) : null); } private CellIdentityWcdma(CellIdentityWcdma cid) { this(cid.mLac, cid.mCid, cid.mPsc, cid.mUarfcn, cid.mMccStr, - cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort); + cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort, cid.mAdditionalPlmns, cid.mCsgInfo); } /** @hide */ @@ -102,7 +128,7 @@ public final class CellIdentityWcdma extends CellIdentity { public @NonNull CellIdentityWcdma sanitizeLocationInfo() { return new CellIdentityWcdma(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, mMccStr, mMncStr, - mAlphaLong, mAlphaShort); + mAlphaLong, mAlphaShort, mAdditionalPlmns, null); } CellIdentityWcdma copy() { @@ -180,7 +206,7 @@ public final class CellIdentityWcdma extends CellIdentity { @Override public int hashCode() { - return Objects.hash(mLac, mCid, mPsc, super.hashCode()); + return Objects.hash(mLac, mCid, mPsc, mAdditionalPlmns.hashCode(), super.hashCode()); } /** @@ -197,6 +223,22 @@ public final class CellIdentityWcdma extends CellIdentity { return mUarfcn; } + /** + * @return a list of additional PLMN IDs supported by this cell. + */ + @NonNull + public List<String> getAdditionalPlmns() { + return mAdditionalPlmns; + } + + /** + * @return closed subscriber group information about the cell if available, otherwise null. + */ + @Nullable + public ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo() { + return mCsgInfo; + } + /** @hide */ @NonNull @Override @@ -228,6 +270,8 @@ public final class CellIdentityWcdma extends CellIdentity { && mUarfcn == o.mUarfcn && TextUtils.equals(mMccStr, o.mMccStr) && TextUtils.equals(mMncStr, o.mMncStr) + && mAdditionalPlmns.equals(o.mAdditionalPlmns) + && Objects.equals(mCsgInfo, o.mCsgInfo) && super.equals(other); } @@ -242,6 +286,8 @@ public final class CellIdentityWcdma extends CellIdentity { .append(" mMnc=").append(mMncStr) .append(" mAlphaLong=").append(mAlphaLong) .append(" mAlphaShort=").append(mAlphaShort) + .append(" mAdditionalPlmns=").append(mAdditionalPlmns) + .append(" mCsgInfo=").append(mCsgInfo) .append("}").toString(); } @@ -254,6 +300,8 @@ public final class CellIdentityWcdma extends CellIdentity { dest.writeInt(mCid); dest.writeInt(mPsc); dest.writeInt(mUarfcn); + dest.writeList(mAdditionalPlmns); + dest.writeParcelable(mCsgInfo, flags); } /** Construct from Parcel, type has already been processed */ @@ -263,6 +311,8 @@ public final class CellIdentityWcdma extends CellIdentity { mCid = in.readInt(); mPsc = in.readInt(); mUarfcn = in.readInt(); + mAdditionalPlmns = in.readArrayList(null); + mCsgInfo = in.readParcelable(null); if (DBG) log(toString()); } diff --git a/telephony/java/android/telephony/ClosedSubscriberGroupInfo.aidl b/telephony/java/android/telephony/ClosedSubscriberGroupInfo.aidl new file mode 100644 index 000000000000..cbe76381a608 --- /dev/null +++ b/telephony/java/android/telephony/ClosedSubscriberGroupInfo.aidl @@ -0,0 +1,20 @@ +/* +** +** Copyright 2020, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.telephony; + +parcelable ClosedSubscriberGroupInfo; diff --git a/telephony/java/android/telephony/ClosedSubscriberGroupInfo.java b/telephony/java/android/telephony/ClosedSubscriberGroupInfo.java new file mode 100644 index 000000000000..e7dfe634590d --- /dev/null +++ b/telephony/java/android/telephony/ClosedSubscriberGroupInfo.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Information to represent a closed subscriber group. + */ +public final class ClosedSubscriberGroupInfo implements Parcelable { + private static final String TAG = "ClosedSubscriberGroupInfo"; + + private final boolean mCsgIndicator; + + private final String mHomeNodebName; + + private final int mCsgIdentity; + + /** @hide */ + public ClosedSubscriberGroupInfo(boolean csgIndicator, @Nullable String homeNodebName, + int csgIdentity) { + mCsgIndicator = csgIndicator; + mHomeNodebName = (homeNodebName == null) ? "" : homeNodebName; + mCsgIdentity = csgIdentity; + } + + /** @hide */ + public ClosedSubscriberGroupInfo( + @NonNull android.hardware.radio.V1_5.ClosedSubscriberGroupInfo csgInfo) { + this(csgInfo.csgIndication, csgInfo.homeNodebName, csgInfo.csgIdentity); + } + + /** + * Indicates whether the cell is restricted to only CSG members. + * + * A cell not broadcasting the CSG Indication but reporting CSG information is considered a + * Hybrid Cell. + * Refer to the "csg-Indication" field in 3GPP TS 36.331 section 6.2.2 + * SystemInformationBlockType1. + * Also refer to "CSG Indicator" in 3GPP TS 25.331 section 10.2.48.8.1 and TS 25.304. + * + * @return true if the cell is restricted to group members only. + */ + public boolean getCsgIndicator() { + return mCsgIndicator; + } + + /** + * Returns human-readable name of the closed subscriber group operating this cell (Node-B). + * + * Refer to "hnb-Name" in TS 36.331 section 6.2.2 SystemInformationBlockType9. + * Also refer to "HNB Name" in 3GPP TS25.331 section 10.2.48.8.23 and TS 23.003 section 4.8. + * + * @return the home Node-B name if available. + */ + public @NonNull String getHomeNodebName() { + return mHomeNodebName; + } + + /** + * The identity of the closed subscriber group that the cell belongs to. + * + * Refer to "CSG-Identity" in TS 36.336 section 6.3.4. + * Also refer to "CSG Identity" in 3GPP TS 25.331 section 10.3.2.8 and TS 23.003 section 4.7. + * + * @return the unique 27-bit CSG Identity. + */ + @IntRange(from = 0, to = 0x7FFFFFF) + public int getCsgIdentity() { + return mCsgIdentity; + } + + @Override + public int hashCode() { + return Objects.hash(mCsgIndicator, mHomeNodebName, mCsgIdentity); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof ClosedSubscriberGroupInfo)) { + return false; + } + + ClosedSubscriberGroupInfo o = (ClosedSubscriberGroupInfo) other; + return mCsgIndicator == o.mCsgIndicator && mHomeNodebName == o.mHomeNodebName + && mCsgIdentity == o.mCsgIdentity; + } + + @Override + public String toString() { + return new StringBuilder(TAG + ":{") + .append(" mCsgIndicator = ").append(mCsgIndicator) + .append(" mHomeNodebName = ").append(mHomeNodebName) + .append(" mCsgIdentity = ").append(mCsgIdentity) + .toString(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int type) { + dest.writeBoolean(mCsgIndicator); + dest.writeString(mHomeNodebName); + dest.writeInt(mCsgIdentity); + } + + /** Construct from Parcel, type has already been processed */ + private ClosedSubscriberGroupInfo(Parcel in) { + this(in.readBoolean(), in.readString(), in.readInt()); + } + + /** + * Implement the Parcelable interface + */ + @Override + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface */ + public static final @android.annotation.NonNull Creator<ClosedSubscriberGroupInfo> CREATOR = + new Creator<ClosedSubscriberGroupInfo>() { + @Override + public ClosedSubscriberGroupInfo createFromParcel(Parcel in) { + return createFromParcelBody(in); + } + + @Override + public ClosedSubscriberGroupInfo[] newArray(int size) { + return new ClosedSubscriberGroupInfo[size]; + } + }; + + /** @hide */ + protected static ClosedSubscriberGroupInfo createFromParcelBody(Parcel in) { + return new ClosedSubscriberGroupInfo(in); + } +} diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java index c706d288b7f2..9b4292f42172 100644 --- a/telephony/java/android/telephony/ImsManager.java +++ b/telephony/java/android/telephony/ImsManager.java @@ -54,6 +54,43 @@ public class ImsManager { "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION"; /** + * An intent action indicating that IMS registration for WiFi calling has resulted in an error. + * Contains error information that should be displayed to the user. + * <p> + * This intent will contain the following extra key/value pairs: + * {@link #EXTRA_WFC_REGISTRATION_FAILURE_TITLE} + * and {@link #EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE}, which contain carrier specific + * error information that should be displayed to the user. + * <p> + * Usage: This intent is sent as an ordered broadcast. If the settings application is going + * to show the error information specified to the user, it should respond to + * {@link android.content.BroadcastReceiver#setResultCode(int)} with + * {@link android.app.Activity#RESULT_CANCELED}, which will signal to the framework that the + * event was handled. If the framework does not receive a response to the ordered broadcast, + * it will then show a notification to the user indicating that there was a registration + * failure. + */ + @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_WFC_IMS_REGISTRATION_ERROR = + "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR"; + + /** + * An extra key corresponding to a String value which contains the carrier specific title to be + * displayed as part of the message shown to the user when there is an error registering for + * WiFi calling. + */ + public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE = + "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE"; + + /** + * An extra key corresponding to a String value which contains the carrier specific message to + * be displayed as part of the message shown to the user when there is an error registering for + * WiFi calling. + */ + public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = + "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE"; + + /** * Use {@link Context#getSystemService(String)} to get an instance of this class. * @hide */ diff --git a/telephony/java/android/telephony/ModemInfo.java b/telephony/java/android/telephony/ModemInfo.java deleted file mode 100644 index c0833af954d8..000000000000 --- a/telephony/java/android/telephony/ModemInfo.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.telephony; - -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.Objects; - -/** - * Information of a single logical modem indicating - * its id, supported rats and whether it supports voice or data, etc. - * @hide - */ -public class ModemInfo implements Parcelable { - public final int modemId; - public final int rat; /* bitset */ - public final boolean isVoiceSupported; - public final boolean isDataSupported; - - // TODO b/121394331: Clean up this class after V1_1.PhoneCapability cleanup. - public ModemInfo(int modemId) { - this(modemId, 0, true, true); - } - - public ModemInfo(int modemId, int rat, boolean isVoiceSupported, boolean isDataSupported) { - this.modemId = modemId; - this.rat = rat; - this.isVoiceSupported = isVoiceSupported; - this.isDataSupported = isDataSupported; - } - - public ModemInfo(Parcel in) { - modemId = in.readInt(); - rat = in.readInt(); - isVoiceSupported = in.readBoolean(); - isDataSupported = in.readBoolean(); - } - - @Override - public String toString() { - return "modemId=" + modemId + " rat=" + rat + " isVoiceSupported:" + isVoiceSupported - + " isDataSupported:" + isDataSupported; - } - - @Override - public int hashCode() { - return Objects.hash(modemId, rat, isVoiceSupported, isDataSupported); - } - - @Override - public boolean equals(Object o) { - if (o == null || !(o instanceof ModemInfo) || hashCode() != o.hashCode()) { - return false; - } - - if (this == o) { - return true; - } - - ModemInfo s = (ModemInfo) o; - - return (modemId == s.modemId - && rat == s.rat - && isVoiceSupported == s.isVoiceSupported - && isDataSupported == s.isDataSupported); - } - - /** - * {@link Parcelable#describeContents} - */ - public @ContentsFlags int describeContents() { - return 0; - } - - /** - * {@link Parcelable#writeToParcel} - */ - public void writeToParcel(Parcel dest, @WriteFlags int flags) { - dest.writeInt(modemId); - dest.writeInt(rat); - dest.writeBoolean(isVoiceSupported); - dest.writeBoolean(isDataSupported); - } - - public static final @android.annotation.NonNull Parcelable.Creator<ModemInfo> CREATOR = new Parcelable.Creator() { - public ModemInfo createFromParcel(Parcel in) { - return new ModemInfo(in); - } - - public ModemInfo[] newArray(int size) { - return new ModemInfo[size]; - } - }; -} diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java index 8a75831a75b8..90244b3df350 100644 --- a/telephony/java/android/telephony/PhoneCapability.java +++ b/telephony/java/android/telephony/PhoneCapability.java @@ -16,13 +16,20 @@ package android.telephony; +import android.annotation.LongDef; import android.annotation.NonNull; -import android.annotation.SystemApi; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; +import android.telephony.AccessNetworkConstants.AccessNetworkType; +import android.telephony.AccessNetworkConstants.RadioAccessNetworkType; +import android.telephony.TelephonyManager.NetworkTypeBitMask; +import com.android.internal.util.CollectionUtils; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -31,68 +38,365 @@ import java.util.Objects; * are shared between those modems defined by list of modem IDs. */ public final class PhoneCapability implements Parcelable { - // Hardcoded default DSDS capability. + /** Modem feature indicating 3GPP2 capability. */ + public static final long MODEM_FEATURE_3GPP2_REG = 1 << 0; + /** Modem feature indicating 3GPP capability. */ + public static final long MODEM_FEATURE_3GPP_REG = 1 << 1; + /** Modem feature indicating CDMA 2000 with EHRPD capability. */ + public static final long MODEM_FEATURE_CDMA2000_EHRPD_REG = 1 << 2; + /** Modem feature indicating GSM capability. */ + public static final long MODEM_FEATURE_GERAN_REG = 1 << 3; + /** Modem feature indicating UMTS capability. */ + public static final long MODEM_FEATURE_UTRAN_REG = 1 << 4; + /** Modem feature indicating LTE capability. */ + public static final long MODEM_FEATURE_EUTRAN_REG = 1 << 5; + /** Modem feature indicating 5G capability.*/ + public static final long MODEM_FEATURE_NGRAN_REG = 1 << 6; + /** Modem feature indicating EN-DC capability. */ + public static final long MODEM_FEATURE_EUTRA_NR_DUAL_CONNECTIVITY_REG = 1 << 7; + /** Modem feature indicating VoLTE capability (IMS registered). */ + public static final long MODEM_FEATURE_PS_VOICE_REG = 1 << 8; + /** Modem feature indicating CS voice call capability. */ + public static final long MODEM_FEATURE_CS_VOICE_SESSION = 1 << 9; + /** Modem feature indicating Internet connection capability. */ + public static final long MODEM_FEATURE_INTERACTIVE_DATA_SESSION = 1 << 10; + /** + * Modem feature indicating dedicated bearer capability. + * For services that require a high level QoS (eg. VoLTE), the network can create + * a dedicated bearer with the required QoS on top of an established default bearer. + * This will provide a dedicated tunnel for one or more specific traffic types. + */ + public static final long MODEM_FEATURE_DEDICATED_BEARER = 1 << 11; + /** Modem feature indicating network scan capability. */ + public static final long MODEM_FEATURE_NETWORK_SCAN = 1 << 12; + /** Modem feature indicating corresponding SIM has CDMA capability. */ + public static final long MODEM_FEATURE_CSIM = 1 << 13; + /** @hide */ + @LongDef(flag = true, prefix = {"MODEM_FEATURE_" }, value = { + MODEM_FEATURE_3GPP2_REG, + MODEM_FEATURE_3GPP_REG, + MODEM_FEATURE_CDMA2000_EHRPD_REG, + MODEM_FEATURE_GERAN_REG, + MODEM_FEATURE_UTRAN_REG, + MODEM_FEATURE_EUTRAN_REG, + MODEM_FEATURE_NGRAN_REG, + MODEM_FEATURE_EUTRA_NR_DUAL_CONNECTIVITY_REG, + MODEM_FEATURE_PS_VOICE_REG, + MODEM_FEATURE_CS_VOICE_SESSION, + MODEM_FEATURE_INTERACTIVE_DATA_SESSION, + MODEM_FEATURE_DEDICATED_BEARER, + MODEM_FEATURE_NETWORK_SCAN, + MODEM_FEATURE_CSIM, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ModemFeature { + } + + /** + * Hardcoded default DSDS capability. + * @hide + */ public static final PhoneCapability DEFAULT_DSDS_CAPABILITY; - // Hardcoded default Single SIM single standby capability. - /** @hide */ + /** + * Hardcoded default Single SIM single standby capability. + * @hide + */ public static final PhoneCapability DEFAULT_SSSS_CAPABILITY; static { - ModemInfo modemInfo1 = new ModemInfo(0, 0, true, true); - ModemInfo modemInfo2 = new ModemInfo(1, 0, true, true); + List<List<Long>> capabilities = new ArrayList<>(); + List<Long> modem1 = new ArrayList<>(); + List<Long> modem2 = new ArrayList<>(); + modem1.add(MODEM_FEATURE_GERAN_REG | MODEM_FEATURE_UTRAN_REG | MODEM_FEATURE_EUTRAN_REG + | MODEM_FEATURE_PS_VOICE_REG | MODEM_FEATURE_CS_VOICE_SESSION + | MODEM_FEATURE_INTERACTIVE_DATA_SESSION | MODEM_FEATURE_DEDICATED_BEARER); + modem2.add(MODEM_FEATURE_GERAN_REG | MODEM_FEATURE_UTRAN_REG | MODEM_FEATURE_EUTRAN_REG + | MODEM_FEATURE_PS_VOICE_REG | MODEM_FEATURE_INTERACTIVE_DATA_SESSION + | MODEM_FEATURE_DEDICATED_BEARER); + capabilities.add(modem1); + capabilities.add(modem2); + List<String> uuids = new ArrayList<>(); + uuids.add("com.xxxx.lm0"); + uuids.add("com.xxxx.lm1"); + long rats = TelephonyManager.NETWORK_TYPE_BITMASK_GSM + | TelephonyManager.NETWORK_TYPE_BITMASK_GPRS + | TelephonyManager.NETWORK_TYPE_BITMASK_EDGE + | TelephonyManager.NETWORK_TYPE_BITMASK_UMTS + | TelephonyManager.NETWORK_TYPE_BITMASK_LTE; + DEFAULT_DSDS_CAPABILITY = new PhoneCapability(0, 0, 0, 0, 0, rats, null, null, null, null, + uuids, null, capabilities); + + capabilities = new ArrayList<>(); + capabilities.add(modem1); + uuids = new ArrayList<>(); + uuids.add("com.xxxx.lm0"); + DEFAULT_SSSS_CAPABILITY = new PhoneCapability(0, 0, 0, 0, 0, rats, null, null, null, null, + uuids, null, capabilities); + } - List<ModemInfo> logicalModemList = new ArrayList<>(); - logicalModemList.add(modemInfo1); - logicalModemList.add(modemInfo2); - DEFAULT_DSDS_CAPABILITY = new PhoneCapability(1, 1, 0, logicalModemList, false); + private final int mUtranUeCategoryDl; + private final int mUtranUeCategoryUl; + private final int mEutranUeCategoryDl; + private final int mEutranUeCategoryUl; + private final long mPsDataConnectionLingerTimeMillis; + private final @NetworkTypeBitMask long mSupportedRats; + private final List<Integer> mGeranBands; + private final List<Integer> mUtranBands; + private final List<Integer> mEutranBands; + private final List<Integer> mNgranBands; + private final List<String> mLogicalModemUuids; + private final List<SimSlotCapability> mSimSlotCapabilities; + private final @ModemFeature List<List<Long>> mConcurrentFeaturesSupport; - logicalModemList = new ArrayList<>(); - logicalModemList.add(modemInfo1); - DEFAULT_SSSS_CAPABILITY = new PhoneCapability(1, 1, 0, logicalModemList, false); + /** + * Default constructor to create a PhoneCapability object. + * @param utranUeCategoryDl 3GPP UE category for UTRAN downlink. + * @param utranUeCategoryUl 3GPP UE category for UTRAN uplink. + * @param eutranUeCategoryDl 3GPP UE category for EUTRAN downlink. + * @param eutranUeCategoryUl 3GPP UE category for EUTRAN uplink. + * @param psDataConnectionLingerTimeMillis length of the grace period to allow a smooth + * "handover" between data connections. + * @param supportedRats all radio access technologies this phone is capable of supporting. + * @param geranBands list of supported {@link AccessNetworkConstants.GeranBand}. + * @param utranBands list of supported {@link AccessNetworkConstants.UtranBand}. + * @param eutranBands list of supported {@link AccessNetworkConstants.EutranBand}. + * @param ngranBands list of supported {@link AccessNetworkConstants.NgranBands}. + * @param logicalModemUuids list of logical modem UUIDs, typically of the form + * "com.xxxx.lmX", where X is the logical modem ID. + * @param simSlotCapabilities list of {@link SimSlotCapability} for the device + * @param concurrentFeaturesSupport list of list of concurrently supportable modem feature sets. + * @hide + */ + public PhoneCapability(int utranUeCategoryDl, int utranUeCategoryUl, int eutranUeCategoryDl, + int eutranUeCategoryUl, long psDataConnectionLingerTimeMillis, + @NetworkTypeBitMask long supportedRats, @Nullable List<Integer> geranBands, + @Nullable List<Integer> utranBands, @Nullable List<Integer> eutranBands, + @Nullable List<Integer> ngranBands, @Nullable List<String> logicalModemUuids, + @Nullable List<SimSlotCapability> simSlotCapabilities, + @Nullable @ModemFeature List<List<Long>> concurrentFeaturesSupport) { + this.mUtranUeCategoryDl = utranUeCategoryDl; + this.mUtranUeCategoryUl = utranUeCategoryUl; + this.mEutranUeCategoryDl = eutranUeCategoryDl; + this.mEutranUeCategoryUl = eutranUeCategoryUl; + this.mPsDataConnectionLingerTimeMillis = psDataConnectionLingerTimeMillis; + this.mSupportedRats = supportedRats; + this.mGeranBands = CollectionUtils.emptyIfNull(geranBands); + this.mUtranBands = CollectionUtils.emptyIfNull(utranBands); + this.mEutranBands = CollectionUtils.emptyIfNull(eutranBands); + this.mNgranBands = CollectionUtils.emptyIfNull(ngranBands); + this.mLogicalModemUuids = CollectionUtils.emptyIfNull(logicalModemUuids); + this.mSimSlotCapabilities = CollectionUtils.emptyIfNull(simSlotCapabilities); + this.mConcurrentFeaturesSupport = CollectionUtils.emptyIfNull(concurrentFeaturesSupport); } - /** @hide */ - public final int maxActiveVoiceCalls; - /** @hide */ - public final int maxActiveData; - /** @hide */ - public final int max5G; - /** @hide */ - public final boolean validationBeforeSwitchSupported; - /** @hide */ - public final List<ModemInfo> logicalModemList; - /** @hide */ - public PhoneCapability(int maxActiveVoiceCalls, int maxActiveData, int max5G, - List<ModemInfo> logicalModemList, boolean validationBeforeSwitchSupported) { - this.maxActiveVoiceCalls = maxActiveVoiceCalls; - this.maxActiveData = maxActiveData; - this.max5G = max5G; - // Make sure it's not null. - this.logicalModemList = logicalModemList == null ? new ArrayList<>() : logicalModemList; - this.validationBeforeSwitchSupported = validationBeforeSwitchSupported; + private PhoneCapability(Parcel in) { + mUtranUeCategoryDl = in.readInt(); + mUtranUeCategoryUl = in.readInt(); + mEutranUeCategoryDl = in.readInt(); + mEutranUeCategoryUl = in.readInt(); + mPsDataConnectionLingerTimeMillis = in.readLong(); + mSupportedRats = in.readLong(); + mGeranBands = new ArrayList<>(); + in.readList(mGeranBands, Integer.class.getClassLoader()); + mUtranBands = new ArrayList<>(); + in.readList(mUtranBands, Integer.class.getClassLoader()); + mEutranBands = new ArrayList<>(); + in.readList(mEutranBands, Integer.class.getClassLoader()); + mNgranBands = new ArrayList<>(); + in.readList(mNgranBands, Integer.class.getClassLoader()); + mLogicalModemUuids = in.createStringArrayList(); + mSimSlotCapabilities = in.createTypedArrayList(SimSlotCapability.CREATOR); + int length = in.readInt(); + mConcurrentFeaturesSupport = new ArrayList<>(); + for (int i = 0; i < length; i++) { + ArrayList<Long> feature = new ArrayList<>(); + in.readList(feature, Long.class.getClassLoader()); + mConcurrentFeaturesSupport.add(feature); + } } - @Override - public String toString() { - return "maxActiveVoiceCalls=" + maxActiveVoiceCalls + " maxActiveData=" + maxActiveData - + " max5G=" + max5G + "logicalModemList:" - + Arrays.toString(logicalModemList.toArray()); + /** + * 3GPP UE category for a given Radio Access Network and direction. + * + * References are: + * TS 25.306 Table 4.1a EUTRAN downlink + * TS 25.306 Table 5.1a-2 EUTRAN uplink + * TS 25.306 Table 5.1a UTRAN downlink + * TS 25.306 Table 5.1g UTRAN uplink + * + * @param uplink true for uplink direction and false for downlink direction. + * @param accessNetworkType accessNetworkType, defined in {@link AccessNetworkType}. + * @return the UE category, or -1 if it is not supported. + */ + public int getUeCategory(boolean uplink, @RadioAccessNetworkType int accessNetworkType) { + if (uplink) { + switch (accessNetworkType) { + case AccessNetworkType.UTRAN: return mUtranUeCategoryUl; + case AccessNetworkType.EUTRAN: return mEutranUeCategoryUl; + default: return -1; + } + } else { + switch (accessNetworkType) { + case AccessNetworkType.UTRAN: return mUtranUeCategoryDl; + case AccessNetworkType.EUTRAN: return mEutranUeCategoryDl; + default: return -1; + } + } } - private PhoneCapability(Parcel in) { - maxActiveVoiceCalls = in.readInt(); - maxActiveData = in.readInt(); - max5G = in.readInt(); - validationBeforeSwitchSupported = in.readBoolean(); - logicalModemList = new ArrayList<>(); - in.readList(logicalModemList, ModemInfo.class.getClassLoader()); + /** + * In cellular devices that support a greater number of logical modems than + * Internet connections, some devices support a grace period to allow a smooth "handover" + * between those connections. If that feature is supported, then this API will provide + * the length of that grace period in milliseconds. If it is not supported, the default value + * for the grace period is 0. + * @return handover linger time in milliseconds, or 0 if it is not supported. + */ + public long getPsDataConnectionLingerTimeMillis() { + return mPsDataConnectionLingerTimeMillis; + } + + /** + * The radio access technologies this device is capable of supporting. + * @return a bitfield of all supported network types, defined in {@link TelephonyManager} + */ + public @NetworkTypeBitMask long getSupportedRats() { + return mSupportedRats; + } + + /** + * List of supported cellular bands for the given accessNetworkType. + * @param accessNetworkType accessNetworkType, defined in {@link AccessNetworkType}. + * @return a list of bands, or an empty list if the access network type is unsupported. + */ + public @NonNull List<Integer> getBands(@RadioAccessNetworkType int accessNetworkType) { + switch (accessNetworkType) { + case AccessNetworkType.GERAN: return mGeranBands; + case AccessNetworkType.UTRAN: return mUtranBands; + case AccessNetworkType.EUTRAN: return mEutranBands; + case AccessNetworkType.NGRAN: return mNgranBands; + default: return new ArrayList<>(); + } + } + + /** + * List of logical modem UUIDs, each typically "com.xxxx.lmX", where X is the logical modem ID. + * @return a list of modem UUIDs, one for every logical modem the device has. + */ + public @NonNull List<String> getLogicalModemUuids() { + return mLogicalModemUuids; + } + + /** + * List of {@link SimSlotCapability} for the device. The order of SIMs corresponds to the + * order of modems in {@link #getLogicalModemUuids}. + * @return a list of SIM slot capabilities, one for every SIM slot the device has. + */ + public @NonNull List<SimSlotCapability> getSimSlotCapabilities() { + return mSimSlotCapabilities; + } + + /** + * A List of Lists of concurrently supportable modem feature sets. + * + * Each entry in the top-level list is an independent configuration across all modems + * that describes the capabilities of the device as a whole. + * + * Each entry in the second-level list is a bitfield of ModemFeatures that describes + * the capabilities for a single modem. In the second-level list, the order of the modems + * corresponds to order of the UUIDs in {@link #getLogicalModemUuids}. + * + * For symmetric capabilities that can only be active on one modem at a time, there will be + * multiple configurations (equal to the number of modems) that shows it active on each modem. + * For asymmetric capabilities that are only available on one of the modems, all configurations + * will have that capability on just that one modem. + * + * The example below shows the concurrentFeaturesSupport for a 3-modem device with + * theoretical capabilities SYMMETRIC (available on all modems, but only one at a time) and + * ASYMMETRIC (only available on the first modem): + * { + * Configuration 1: ASYMMETRIC and SYMMETRIC on modem 1, modem 2 empty, modem 3 empty + * {(ASYMMETRIC | SYMMETRIC), (), ()}, + * + * Configuration 2: ASYMMETRIC on modem 1, SYMMETRIC on modem 2, modem 3 empty + * {(ASYMMETRIC), (SYMMETRIC), ()}, + * + * Configuration 3: ASYMMETRIC on modem 1, modem 2 empty, SYMMETRIC on modem 3 + * {(ASYMMETRIC), (), (SYMMETRIC)} + * } + * + * @return List of all concurrently supportable modem features. + */ + public @NonNull @ModemFeature List<List<Long>> getConcurrentFeaturesSupport() { + return mConcurrentFeaturesSupport; + } + + /** + * How many modems can simultaneously have PS attached. + * @return maximum number of active PS voice connections. + */ + public int getMaxActivePsVoice() { + return countFeature(MODEM_FEATURE_PS_VOICE_REG); + } + + /** + * How many modems can simultaneously support active data connections. + * For DSDS, this will be 1, and for DSDA this will be 2. + * @return maximum number of active Internet data sessions. + */ + public int getMaxActiveInternetData() { + return countFeature(MODEM_FEATURE_INTERACTIVE_DATA_SESSION); + } + + /** + * How many modems can simultaneously have dedicated bearer capability. + * @return maximum number of active dedicated bearers. + */ + public int getMaxActiveDedicatedBearers() { + return countFeature(MODEM_FEATURE_DEDICATED_BEARER); + } + + /** + * Whether the CBRS band 48 is supported or not. + * @return true if any RadioAccessNetwork supports CBRS and false if none do. + * @hide + */ + public boolean isCbrsSupported() { + return mEutranBands.contains(AccessNetworkConstants.EutranBand.BAND_48) + || mNgranBands.contains(AccessNetworkConstants.NgranBands.BAND_48); + } + + private int countFeature(@ModemFeature long feature) { + int count = 0; + for (long featureSet : mConcurrentFeaturesSupport.get(0)) { + if ((featureSet & feature) != 0) { + count++; + } + } + return count; + } + + @Override + public String toString() { + return "utranUeCategoryDl=" + mUtranUeCategoryDl + + " utranUeCategoryUl=" + mUtranUeCategoryUl + + " eutranUeCategoryDl=" + mEutranUeCategoryDl + + " eutranUeCategoryUl=" + mEutranUeCategoryUl + + " psDataConnectionLingerTimeMillis=" + mPsDataConnectionLingerTimeMillis + + " supportedRats=" + mSupportedRats + " geranBands=" + mGeranBands + + " utranBands=" + mUtranBands + " eutranBands=" + mEutranBands + + " ngranBands=" + mNgranBands + " logicalModemUuids=" + mLogicalModemUuids + + " simSlotCapabilities=" + mSimSlotCapabilities + + " concurrentFeaturesSupport=" + mConcurrentFeaturesSupport; } @Override public int hashCode() { - return Objects.hash(maxActiveVoiceCalls, maxActiveData, max5G, logicalModemList, - validationBeforeSwitchSupported); + return Objects.hash(mUtranUeCategoryDl, mUtranUeCategoryUl, mEutranUeCategoryDl, + mEutranUeCategoryUl, mPsDataConnectionLingerTimeMillis, mSupportedRats, mGeranBands, + mUtranBands, mEutranBands, mNgranBands, mLogicalModemUuids, mSimSlotCapabilities, + mConcurrentFeaturesSupport); } @Override @@ -107,11 +411,19 @@ public final class PhoneCapability implements Parcelable { PhoneCapability s = (PhoneCapability) o; - return (maxActiveVoiceCalls == s.maxActiveVoiceCalls - && maxActiveData == s.maxActiveData - && max5G == s.max5G - && validationBeforeSwitchSupported == s.validationBeforeSwitchSupported - && logicalModemList.equals(s.logicalModemList)); + return (mUtranUeCategoryDl == s.mUtranUeCategoryDl + && mUtranUeCategoryUl == s.mUtranUeCategoryUl + && mEutranUeCategoryDl == s.mEutranUeCategoryDl + && mEutranUeCategoryUl == s.mEutranUeCategoryUl + && mPsDataConnectionLingerTimeMillis == s.mPsDataConnectionLingerTimeMillis + && mSupportedRats == s.mSupportedRats + && mGeranBands.equals(s.mGeranBands) + && mUtranBands.equals(s.mUtranBands) + && mEutranBands.equals(s.mEutranBands) + && mNgranBands.equals(s.mNgranBands) + && mLogicalModemUuids.equals(s.mLogicalModemUuids) + && mSimSlotCapabilities.equals(s.mSimSlotCapabilities) + && mConcurrentFeaturesSupport.equals(s.mConcurrentFeaturesSupport)); } /** @@ -125,20 +437,32 @@ public final class PhoneCapability implements Parcelable { * {@link Parcelable#writeToParcel} */ public void writeToParcel(@NonNull Parcel dest, @Parcelable.WriteFlags int flags) { - dest.writeInt(maxActiveVoiceCalls); - dest.writeInt(maxActiveData); - dest.writeInt(max5G); - dest.writeBoolean(validationBeforeSwitchSupported); - dest.writeList(logicalModemList); + dest.writeInt(mUtranUeCategoryDl); + dest.writeInt(mUtranUeCategoryUl); + dest.writeInt(mEutranUeCategoryDl); + dest.writeInt(mEutranUeCategoryUl); + dest.writeLong(mPsDataConnectionLingerTimeMillis); + dest.writeLong(mSupportedRats); + dest.writeList(mGeranBands); + dest.writeList(mUtranBands); + dest.writeList(mEutranBands); + dest.writeList(mNgranBands); + dest.writeStringList(mLogicalModemUuids); + dest.writeTypedList(mSimSlotCapabilities); + dest.writeInt(mConcurrentFeaturesSupport.size()); + for (List<Long> feature : mConcurrentFeaturesSupport) { + dest.writeList(feature); + } } - public static final @android.annotation.NonNull Parcelable.Creator<PhoneCapability> CREATOR = new Parcelable.Creator() { - public PhoneCapability createFromParcel(Parcel in) { - return new PhoneCapability(in); - } + public static final @NonNull Parcelable.Creator<PhoneCapability> CREATOR = + new Parcelable.Creator() { + public PhoneCapability createFromParcel(Parcel in) { + return new PhoneCapability(in); + } - public PhoneCapability[] newArray(int size) { - return new PhoneCapability[size]; - } - }; + public PhoneCapability[] newArray(int size) { + return new PhoneCapability[size]; + } + }; } diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index 2f9e6ac0f9ff..0074772a221d 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -28,7 +28,6 @@ import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.database.Cursor; -import android.location.CountryDetector; import android.net.Uri; import android.os.PersistableBundle; import android.provider.Contacts; @@ -2032,6 +2031,7 @@ public class PhoneNumberUtils { private static boolean isEmergencyNumberInternal(int subId, String number, String defaultCountryIso, boolean useExactMatch) { + // TODO: clean up all the callers that pass in a defaultCountryIso, since it's ignored now. try { if (useExactMatch) { return TelephonyManager.getDefault().isEmergencyNumber(number); @@ -2193,18 +2193,7 @@ public class PhoneNumberUtils { private static boolean isLocalEmergencyNumberInternal(int subId, String number, Context context, boolean useExactMatch) { - String countryIso; - CountryDetector detector = (CountryDetector) context.getSystemService( - Context.COUNTRY_DETECTOR); - if (detector != null && detector.detectCountry() != null) { - countryIso = detector.detectCountry().getCountryIso(); - } else { - Locale locale = context.getResources().getConfiguration().locale; - countryIso = locale.getCountry(); - Rlog.w(LOG_TAG, "No CountryDetector; falling back to countryIso based on locale: " - + countryIso); - } - return isEmergencyNumberInternal(subId, number, countryIso, useExactMatch); + return isEmergencyNumberInternal(subId, number, null /* unused */, useExactMatch); } /** diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java index 0cfb8c56320a..094b8b0d1631 100644 --- a/telephony/java/android/telephony/PreciseDataConnectionState.java +++ b/telephony/java/android/telephony/PreciseDataConnectionState.java @@ -135,7 +135,7 @@ public final class PreciseDataConnectionState implements Parcelable { } /** - * To check the SDK version for {@link PreciseDataConnectionState#getDataConnectionState}. + * To check the SDK version for {@code PreciseDataConnectionState#getDataConnectionState}. */ @ChangeId @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) diff --git a/telephony/java/android/telephony/SimSlotCapability.java b/telephony/java/android/telephony/SimSlotCapability.java new file mode 100644 index 000000000000..3d38d0429908 --- /dev/null +++ b/telephony/java/android/telephony/SimSlotCapability.java @@ -0,0 +1,130 @@ +/* + * 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.telephony; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * Capabilities for a SIM Slot. + */ +public final class SimSlotCapability implements Parcelable { + /** Slot type for UICC (removable SIM). */ + public static final int SLOT_TYPE_UICC = 1; + /** Slot type for iUICC/iSIM (integrated SIM). */ + public static final int SLOT_TYPE_IUICC = 2; + /** Slot type for eUICC/eSIM (embedded SIM). */ + public static final int SLOT_TYPE_EUICC = 3; + /** Slot type for soft SIM (no physical SIM). */ + public static final int SLOT_TYPE_SOFT_SIM = 4; + + /** @hide */ + @IntDef(prefix = {"SLOT_TYPE_" }, value = { + SLOT_TYPE_UICC, + SLOT_TYPE_IUICC, + SLOT_TYPE_EUICC, + SLOT_TYPE_SOFT_SIM, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SlotType { + } + + private final int mPhysicalSlotIndex; + private final int mSlotType; + + /** @hide */ + public SimSlotCapability(int physicalSlotId, int slotType) { + this.mPhysicalSlotIndex = physicalSlotId; + this.mSlotType = slotType; + } + + private SimSlotCapability(Parcel in) { + mPhysicalSlotIndex = in.readInt(); + mSlotType = in.readInt(); + } + + /** + * @return physical SIM slot index + */ + public int getPhysicalSlotIndex() { + return mPhysicalSlotIndex; + } + + /** + * @return type of SIM {@link SlotType} + */ + public @SlotType int getSlotType() { + return mSlotType; + } + + @Override + public String toString() { + return "mPhysicalSlotIndex=" + mPhysicalSlotIndex + " slotType=" + mSlotType; + } + + @Override + public int hashCode() { + return Objects.hash(mPhysicalSlotIndex, mSlotType); + } + + @Override + public boolean equals(Object o) { + if (o == null || !(o instanceof SimSlotCapability) || hashCode() != o.hashCode()) { + return false; + } + + if (this == o) { + return true; + } + + SimSlotCapability s = (SimSlotCapability) o; + + return (mPhysicalSlotIndex == s.mPhysicalSlotIndex && mSlotType == s.mSlotType); + } + + /** + * {@link Parcelable#describeContents} + */ + public @ContentsFlags int describeContents() { + return 0; + } + + /** + * {@link Parcelable#writeToParcel} + */ + public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags) { + dest.writeInt(mPhysicalSlotIndex); + dest.writeInt(mSlotType); + } + + public static final @NonNull Parcelable.Creator<SimSlotCapability> CREATOR = + new Parcelable.Creator() { + public SimSlotCapability createFromParcel(Parcel in) { + return new SimSlotCapability(in); + } + + public SimSlotCapability[] newArray(int size) { + return new SimSlotCapability[size]; + } + }; +} diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index 2f95a501ce2f..5cd7cf8fae8a 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -1658,7 +1658,7 @@ public final class SmsManager { } /** - * Copy a raw SMS PDU to the ICC. + * Copies a raw SMS PDU to the ICC. * ICC (Integrated Circuit Card) is the card of the device. * For example, this can be the SIM or USIM for GSM. * @@ -1672,21 +1672,26 @@ public final class SmsManager { * operation is performed on the correct subscription. * </p> * - * @param smsc the SMSC for this message, or NULL for the default SMSC - * @param pdu the raw PDU to store - * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD, - * STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT) - * @return true for success + * @param smsc the SMSC for this messag or null for the default SMSC. + * @param pdu the raw PDU to store. + * @param status message status. One of these status: + * <code>STATUS_ON_ICC_READ</code> + * <code>STATUS_ON_ICC_UNREAD</code> + * <code>STATUS_ON_ICC_SENT</code> + * <code>STATUS_ON_ICC_UNSENT</code> + * @return true for success. Otherwise false. * - * @throws IllegalArgumentException if pdu is NULL - * {@hide} + * @throws IllegalArgumentException if pdu is null. + * @hide */ - @UnsupportedAppUsage - public boolean copyMessageToIcc(byte[] smsc, byte[] pdu,int status) { + @SystemApi + @RequiresPermission(Manifest.permission.ACCESS_MESSAGES_ON_ICC) + public boolean copyMessageToIcc( + @Nullable byte[] smsc, @NonNull byte[] pdu, @StatusOnIcc int status) { boolean success = false; - if (null == pdu) { - throw new IllegalArgumentException("pdu is NULL"); + if (pdu == null) { + throw new IllegalArgumentException("pdu is null"); } try { ISms iSms = getISmsService(); @@ -1703,7 +1708,7 @@ public final class SmsManager { } /** - * Delete the specified message from the ICC. + * Deletes the specified message from the ICC. * ICC (Integrated Circuit Card) is the card of the device. * For example, this can be the SIM or USIM for GSM. * @@ -1787,7 +1792,7 @@ public final class SmsManager { } /** - * Retrieves all messages currently stored on ICC. + * Retrieves all messages currently stored on the ICC. * ICC (Integrated Circuit Card) is the card of the device. * For example, this can be the SIM or USIM for GSM. * @@ -1953,8 +1958,7 @@ public final class SmsManager { } /** - * Create a list of <code>SmsMessage</code>s from a list of RawSmsData - * records returned by <code>getAllMessagesFromIcc()</code> + * Creates a list of <code>SmsMessage</code>s from a list of SmsRawData records. * * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier * applications or the Telephony framework and will never trigger an SMS disambiguation @@ -1966,8 +1970,7 @@ public final class SmsManager { * operation is performed on the correct subscription. * </p> * - * @param records SMS EF records, returned by - * <code>getAllMessagesFromIcc</code> + * @param records SMS EF records. * @return <code>ArrayList</code> of <code>SmsMessage</code> objects. */ private ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) { @@ -1978,7 +1981,7 @@ public final class SmsManager { SmsRawData data = records.get(i); // List contains all records, including "free" records (null) if (data != null) { - SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes(), + SmsMessage sms = SmsMessage.createFromEfRecord(i + 1, data.getBytes(), getSubscriptionId()); if (sms != null) { messages.add(sms); @@ -2122,6 +2125,17 @@ public final class SmsManager { return ret; } + /** @hide */ + @IntDef(prefix = { "STATUS_ON_ICC_" }, value = { + STATUS_ON_ICC_FREE, + STATUS_ON_ICC_READ, + STATUS_ON_ICC_UNREAD, + STATUS_ON_ICC_SENT, + STATUS_ON_ICC_UNSENT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface StatusOnIcc {} + // see SmsMessage.getStatusOnIcc /** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index eefbd44ad6de..7a30f143a3a4 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -278,41 +278,24 @@ public class SmsMessage { } /** - * Create an SmsMessage from an SMS EF record. + * Creates an SmsMessage from an SMS EF record. * - * @param index Index of SMS record. This should be index in ArrayList - * returned by SmsManager.getAllMessagesFromSim + 1. + * @param index Index of SMS EF record. * @param data Record data. * @return An SmsMessage representing the record. * * @hide */ public static SmsMessage createFromEfRecord(int index, byte[] data) { - SmsMessageBase wrappedMessage; - - if (isCdmaVoice()) { - wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord( - index, data); - } else { - wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord( - index, data); - } - - if (wrappedMessage != null) { - return new SmsMessage(wrappedMessage); - } else { - Rlog.e(LOG_TAG, "createFromEfRecord(): wrappedMessage is null"); - return null; - } + return createFromEfRecord(index, data, SmsManager.getDefaultSmsSubscriptionId()); } /** - * Create an SmsMessage from an SMS EF record. + * Creates an SmsMessage from an SMS EF record. * - * @param index Index of SMS record. This should be index in ArrayList - * returned by SmsManager.getAllMessagesFromSim + 1. + * @param index Index of SMS EF record. * @param data Record data. - * @param subId Subscription Id of the SMS + * @param subId Subscription Id associated with the record. * @return An SmsMessage representing the record. * * @hide @@ -602,13 +585,15 @@ public class SmsMessage { */ /** - * Get an SMS-SUBMIT PDU for a destination address and a message. + * Gets an SMS-SUBMIT PDU for a destination address and a message. * This method will not attempt to use any GSM national language 7 bit encodings. * - * @param scAddress Service Centre address. Null means use default. - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param scAddress Service Centre address. Null means use default. + * @param destinationAddress the address of the destination for the message. + * @param message string representation of the message payload. + * @param statusReportRequested indicates whether a report is requested for this message. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. */ public static SubmitPdu getSubmitPdu(String scAddress, String destinationAddress, String message, boolean statusReportRequested) { @@ -621,17 +606,16 @@ public class SmsMessage { } /** - * Get an SMS-SUBMIT PDU for a destination address and a message. + * Gets an SMS-SUBMIT PDU for a destination address and a message. * This method will not attempt to use any GSM national language 7 bit encodings. * - * @param scAddress Service Centre address. Null means use default. + * @param scAddress Service Centre address. Null means use default. * @param destinationAddress the address of the destination for the message. - * @param message String representation of the message payload. - * @param statusReportRequested Indicates whether a report is requested for this message. - * @param subId Subscription of the message - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param message string representation of the message payload. + * @param statusReportRequested indicates whether a report is requested for this message. + * @param subId subscription of the message. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. * @hide */ public static SubmitPdu getSubmitPdu(String scAddress, @@ -649,17 +633,16 @@ public class SmsMessage { } /** - * Get an SMS-SUBMIT PDU for a data message to a destination address & port. + * Gets an SMS-SUBMIT PDU for a data message to a destination address & port. * This method will not attempt to use any GSM national language 7 bit encodings. * - * @param scAddress Service Centre address. null == use default - * @param destinationAddress the address of the destination for the message - * @param destinationPort the port to deliver the message to at the - * destination - * @param data the data for the message - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param scAddress Service Centre address. Null means use default. + * @param destinationAddress the address of the destination for the message. + * @param destinationPort the port to deliver the message to at the destination. + * @param data the data for the message. + * @param statusReportRequested indicates whether a report is requested for this message. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. */ public static SubmitPdu getSubmitPdu(String scAddress, String destinationAddress, short destinationPort, byte[] data, @@ -677,6 +660,55 @@ public class SmsMessage { return new SubmitPdu(spb); } + // TODO: SubmitPdu class is used for SMS-DELIVER also now. Refactor for SubmitPdu and new + // DeliverPdu accordingly. + + /** + * Gets an SMS PDU to store in the ICC. + * + * @param subId subscription of the message. + * @param status message status. One of these status: + * <code>SmsManager.STATUS_ON_ICC_READ</code> + * <code>SmsManager.STATUS_ON_ICC_UNREAD</code> + * <code>SmsManager.STATUS_ON_ICC_SENT</code> + * <code>SmsManager.STATUS_ON_ICC_UNSENT</code> + * @param scAddress Service Centre address. Null means use default. + * @param address destination or originating address. + * @param message string representation of the message payload. + * @param date the time stamp the message was received. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. + * @hide + */ + @SystemApi + @Nullable + public static SubmitPdu getSmsPdu(int subId, @SmsManager.StatusOnIcc int status, + @Nullable String scAddress, @NonNull String address, @NonNull String message, + long date) { + SubmitPduBase spb; + if (isCdmaVoice(subId)) { // 3GPP2 format + if (status == SmsManager.STATUS_ON_ICC_READ + || status == SmsManager.STATUS_ON_ICC_UNREAD) { // Deliver PDU + spb = com.android.internal.telephony.cdma.SmsMessage.getDeliverPdu(address, + message, date); + } else { // Submit PDU + spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress, + address, message, false /* statusReportRequested */, null /* smsHeader */); + } + } else { // 3GPP format + if (status == SmsManager.STATUS_ON_ICC_READ + || status == SmsManager.STATUS_ON_ICC_UNREAD) { // Deliver PDU + spb = com.android.internal.telephony.gsm.SmsMessage.getDeliverPdu(scAddress, + address, message, date); + } else { // Submit PDU + spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress, + address, message, false /* statusReportRequested */, null /* header */); + } + } + + return spb != null ? new SubmitPdu(spb) : null; + } + /** * Get an SMS-SUBMIT PDU's encoded message. * This is used by Bluetooth MAP profile to handle long non UTF-8 SMS messages. diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 8de5b8541f56..63a85fa2845c 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -16,8 +16,8 @@ package android.telephony; -import static android.net.NetworkPolicyManager.OVERRIDE_CONGESTED; -import static android.net.NetworkPolicyManager.OVERRIDE_UNMETERED; +import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_CONGESTED; +import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED; import android.Manifest; import android.annotation.CallbackExecutor; @@ -45,6 +45,7 @@ import android.content.res.Resources; import android.database.ContentObserver; import android.net.INetworkPolicyManager; import android.net.NetworkCapabilities; +import android.net.NetworkPolicyManager; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -957,6 +958,11 @@ public class SubscriptionManager { mContext = context; } + private NetworkPolicyManager getNetworkPolicyManager() { + return (NetworkPolicyManager) mContext + .getSystemService(Context.NETWORK_POLICY_SERVICE); + } + /** * @deprecated developers should always obtain references directly from * {@link Context#getSystemService(Class)}. @@ -967,7 +973,7 @@ public class SubscriptionManager { .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); } - private final INetworkPolicyManager getNetworkPolicy() { + private INetworkPolicyManager getINetworkPolicyManager() { if (mNetworkPolicy == null) { mNetworkPolicy = INetworkPolicyManager.Stub.asInterface( TelephonyFrameworkInitializer @@ -2587,14 +2593,10 @@ public class SubscriptionManager { * outlined above. */ public @NonNull List<SubscriptionPlan> getSubscriptionPlans(int subId) { - try { - SubscriptionPlan[] subscriptionPlans = - getNetworkPolicy().getSubscriptionPlans(subId, mContext.getOpPackageName()); - return subscriptionPlans == null - ? Collections.emptyList() : Arrays.asList(subscriptionPlans); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + SubscriptionPlan[] subscriptionPlans = + getNetworkPolicyManager().getSubscriptionPlans(subId, mContext.getOpPackageName()); + return subscriptionPlans == null + ? Collections.emptyList() : Arrays.asList(subscriptionPlans); } /** @@ -2620,18 +2622,14 @@ public class SubscriptionManager { * defined in {@link SubscriptionPlan}. */ public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) { - try { - getNetworkPolicy().setSubscriptionPlans(subId, - plans.toArray(new SubscriptionPlan[plans.size()]), mContext.getOpPackageName()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + getNetworkPolicyManager().setSubscriptionPlans(subId, + plans.toArray(new SubscriptionPlan[plans.size()]), mContext.getOpPackageName()); } /** @hide */ private String getSubscriptionPlansOwner(int subId) { try { - return getNetworkPolicy().getSubscriptionPlansOwner(subId); + return getINetworkPolicyManager().getSubscriptionPlansOwner(subId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2662,13 +2660,10 @@ public class SubscriptionManager { */ public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered, @DurationMillisLong long timeoutMillis) { - try { - final int overrideValue = overrideUnmetered ? OVERRIDE_UNMETERED : 0; - getNetworkPolicy().setSubscriptionOverride(subId, OVERRIDE_UNMETERED, overrideValue, - timeoutMillis, mContext.getOpPackageName()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + + final int overrideValue = overrideUnmetered ? SUBSCRIPTION_OVERRIDE_UNMETERED : 0; + getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_UNMETERED, + overrideValue, timeoutMillis, mContext.getOpPackageName()); } /** @@ -2697,13 +2692,9 @@ public class SubscriptionManager { */ public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested, @DurationMillisLong long timeoutMillis) { - try { - final int overrideValue = overrideCongested ? OVERRIDE_CONGESTED : 0; - getNetworkPolicy().setSubscriptionOverride(subId, OVERRIDE_CONGESTED, overrideValue, - timeoutMillis, mContext.getOpPackageName()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + final int overrideValue = overrideCongested ? SUBSCRIPTION_OVERRIDE_CONGESTED : 0; + getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_CONGESTED, + overrideValue, timeoutMillis, mContext.getOpPackageName()); } /** diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 92fafdca8115..903be6f68282 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -50,7 +50,6 @@ import android.net.ConnectivityManager; import android.net.NetworkStats; import android.net.Uri; import android.os.AsyncTask; -import android.os.BatteryStats; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -94,6 +93,7 @@ import android.util.Pair; import com.android.ims.internal.IImsServiceFeatureCallback; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.CellNetworkScanResult; +import com.android.internal.telephony.IBooleanConsumer; import com.android.internal.telephony.INumberVerificationCallback; import com.android.internal.telephony.IOns; import com.android.internal.telephony.IPhoneSubInfo; @@ -160,8 +160,8 @@ public class TelephonyManager { * into the ResultReceiver Bundle. * @hide */ - public static final String MODEM_ACTIVITY_RESULT_KEY = - BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY; + @SystemApi + public static final String MODEM_ACTIVITY_RESULT_KEY = "controller_activity"; /** * The process name of the Phone app as well as many other apps that use this process name, such @@ -193,11 +193,8 @@ public class TelephonyManager { NETWORK_SELECTION_MODE_MANUAL}) public @interface NetworkSelectionMode {} - /** @hide */ public static final int NETWORK_SELECTION_MODE_UNKNOWN = 0; - /** @hide */ public static final int NETWORK_SELECTION_MODE_AUTO = 1; - /** @hide */ public static final int NETWORK_SELECTION_MODE_MANUAL = 2; /** The otaspMode passed to PhoneStateListener#onOtaspChanged */ @@ -1247,6 +1244,80 @@ public class TelephonyManager { public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID"; /** + * Broadcast Action: The Service Provider string(s) have been updated. Activities or + * services that use these strings should update their display. + * + * <p>The intent will have the following extra values: + * <dl> + * <dt>{@link #EXTRA_SHOW_PLMN}</dt> + * <dd>Boolean that indicates whether the PLMN should be shown.</dd> + * <dt>{@link #EXTRA_PLMN}</dt> + * <dd>The operator name of the registered network, as a string.</dd> + * <dt>{@link #EXTRA_SHOW_SPN}</dt> + * <dd>Boolean that indicates whether the SPN should be shown.</dd> + * <dt>{@link #EXTRA_SPN}</dt> + * <dd>The service provider name, as a string.</dd> + * <dt>{@link #EXTRA_DATA_SPN}</dt> + * <dd>The service provider name for data service, as a string.</dd> + * </dl> + * + * Note that {@link #EXTRA_SHOW_PLMN} may indicate that {@link #EXTRA_PLMN} should be displayed, + * even though the value for {@link #EXTRA_PLMN} is null. This can happen, for example, if the + * phone has not registered to a network yet. In this case the receiver may substitute an + * appropriate placeholder string (eg, "No service"). + * + * It is recommended to display {@link #EXTRA_PLMN} before / above {@link #EXTRA_SPN} if + * both are displayed. + * + * <p>Note: this is a protected intent that can only be sent by the system. + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SERVICE_PROVIDERS_UPDATED = + "android.telephony.action.SERVICE_PROVIDERS_UPDATED"; + + /** + * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate + * whether the PLMN should be shown. + * @hide + */ + @SystemApi + public static final String EXTRA_SHOW_PLMN = "android.telephony.extra.SHOW_PLMN"; + + /** + * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate + * the operator name of the registered network. + * @hide + */ + @SystemApi + public static final String EXTRA_PLMN = "android.telephony.extra.PLMN"; + + /** + * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate + * whether the PLMN should be shown. + * @hide + */ + @SystemApi + public static final String EXTRA_SHOW_SPN = "android.telephony.extra.SHOW_SPN"; + + /** + * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate + * the service provider name. + * @hide + */ + @SystemApi + public static final String EXTRA_SPN = "android.telephony.extra.SPN"; + + /** + * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate + * the service provider name for data service. + * @hide + */ + @SystemApi + public static final String EXTRA_DATA_SPN = "android.telephony.extra.DATA_SPN"; + + /** * Broadcast intent action indicating that when data stall recovery is attempted by Telephony, * intended for report every data stall recovery step attempted. * @@ -1364,6 +1435,7 @@ public class TelephonyManager { * * @hide */ + @SystemApi public static final String EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE = "android.telephony.extra.DEFAULT_SUBSCRIPTION_SELECT_TYPE"; @@ -1383,6 +1455,7 @@ public class TelephonyManager { * to indicate there's no need to re-select any default subscription. * @hide */ + @SystemApi public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE = 0; /** @@ -1390,6 +1463,7 @@ public class TelephonyManager { * to indicate there's a need to select default data subscription. * @hide */ + @SystemApi public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA = 1; /** @@ -1397,6 +1471,7 @@ public class TelephonyManager { * to indicate there's a need to select default voice call subscription. * @hide */ + @SystemApi public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_VOICE = 2; /** @@ -1404,6 +1479,7 @@ public class TelephonyManager { * to indicate there's a need to select default sms subscription. * @hide */ + @SystemApi public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_SMS = 3; /** @@ -1413,6 +1489,7 @@ public class TelephonyManager { * which subscription should be the default subscription. * @hide */ + @SystemApi public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL = 4; /** @@ -1422,6 +1499,7 @@ public class TelephonyManager { * * @hide */ + @SystemApi public static final String EXTRA_SIM_COMBINATION_WARNING_TYPE = "android.telephony.extra.SIM_COMBINATION_WARNING_TYPE"; @@ -1438,6 +1516,7 @@ public class TelephonyManager { * to indicate there's no SIM combination warning. * @hide */ + @SystemApi public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE = 0; /** @@ -1445,6 +1524,7 @@ public class TelephonyManager { * to indicate two active SIMs are both CDMA hence there might be functional limitation. * @hide */ + @SystemApi public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA = 1; /** @@ -1455,6 +1535,7 @@ public class TelephonyManager { * * @hide */ + @SystemApi public static final String EXTRA_SIM_COMBINATION_NAMES = "android.telephony.extra.SIM_COMBINATION_NAMES"; @@ -1776,6 +1857,24 @@ public class TelephonyManager { // /** + * Returns the {@link PhoneCapability} for the device or null if it is not available. + * <p> + * Requires Permission: READ_PHONE_STATE or that the calling app has + * carrier privileges (see {@link #hasCarrierPrivileges}). + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @Nullable + public PhoneCapability getPhoneCapability() { + try { + ITelephony telephony = getITelephony(); + return telephony == null ? null : + telephony.getPhoneCapability(getSubId(), getOpPackageName(), getFeatureId()); + } catch (RemoteException ex) { + return null; + } + } + + /** * Returns the software version number for the device, for example, * the IMEI/SV for GSM phones. Return null if the software version is * not available. @@ -2682,7 +2781,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); } /** @@ -5613,19 +5712,25 @@ public class TelephonyManager { } /** - * Returns the CDMA ERI icon index to display + * Get the CDMA ERI (Enhanced Roaming Indicator) information + * + * Returns {@link android.telephony#CdmaEriInformation} + * * @hide */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public int getCdmaEriIconIndex() { - return getCdmaEriIconIndex(getSubId()); + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SystemApi + @NonNull + public CdmaEriInformation getCdmaEriInformation() { + return new CdmaEriInformation( + getCdmaEriIconMode(getSubId()), getCdmaEriIconIndex(getSubId())); } /** * Returns the CDMA ERI icon index to display for a subscription * @hide */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @UnsupportedAppUsage public int getCdmaEriIconIndex(int subId) { try { @@ -5643,25 +5748,13 @@ public class TelephonyManager { } /** - * Returns the CDMA ERI icon mode, - * 0 - ON - * 1 - FLASHING - * - * @hide - */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public int getCdmaEriIconMode() { - return getCdmaEriIconMode(getSubId()); - } - - /** * Returns the CDMA ERI icon mode for a subscription. * 0 - ON * 1 - FLASHING * * @hide */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @UnsupportedAppUsage public int getCdmaEriIconMode(int subId) { try { @@ -7919,6 +8012,36 @@ public class TelephonyManager { * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling * app has carrier privileges (see {@link #hasCarrierPrivileges}). * + * @param operatorNumeric the PLMN ID of the network to select. + * @param ran the initial suggested radio access network type. + * If registration fails, the RAN is not available after, the RAN is not within the + * network types specified by {@link #setPreferredNetworkTypeBitmask}, or the value is + * {@link AccessNetworkConstants.AccessNetworkType#UNKNOWN}, modem will select + * the next best RAN for network registration. + * @param persistSelection whether the selection will persist until reboot. + * If true, only allows attaching to the selected PLMN until reboot; otherwise, + * attach to the chosen PLMN and resume normal network selection next time. + * @return {@code true} on success; {@code false} on any failure. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @SystemApi + public boolean setNetworkSelectionModeManual(@NonNull String operatorNumeric, + @AccessNetworkConstants.RadioAccessNetworkType int ran, boolean persistSelection) { + return setNetworkSelectionModeManual(new OperatorInfo("" /* operatorAlphaLong */, + "" /* operatorAlphaShort */, operatorNumeric, ran), persistSelection); + } + + /** + * Ask the radio to connect to the input network and change selection mode to manual. + * + * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link #hasCarrierPrivileges}). + * * @param operatorInfo included the PLMN id, long name, short name of the operator to attach to. * @param persistSelection whether the selection will persist until reboot. If true, only allows * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume @@ -7947,14 +8070,18 @@ public class TelephonyManager { * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} - - * @return the network selection mode. + * <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}). * - * @hide + * @return the network selection mode. */ - @NetworkSelectionMode - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - public int getNetworkSelectionMode() { + @SuppressAutoDoc // No support for carrier privileges (b/72967236). + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PRECISE_PHONE_STATE + }) + public @NetworkSelectionMode int getNetworkSelectionMode() { int mode = NETWORK_SELECTION_MODE_UNKNOWN; try { ITelephony telephony = getITelephony(); @@ -7968,6 +8095,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. @@ -10182,7 +10333,8 @@ public class TelephonyManager { * Requests the modem activity info. The recipient will place the result * in `result`. * @param result The object on which the recipient will send the resulting - * {@link android.telephony.ModemActivityInfo} object. + * {@link android.telephony.ModemActivityInfo} object with key of + * {@link #MODEM_ACTIVITY_RESULT_KEY}. * @hide */ @SystemApi @@ -11019,15 +11171,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(); @@ -12521,6 +12676,51 @@ public class TelephonyManager { } /** + * Specify which bands modem's background scan must act on. + * If {@code specifiers} is non-empty, the scan will be restricted to the bands specified. + * Otherwise, it scans all bands. + * + * For example, CBRS is only on LTE band 48. By specifying this band, + * modem saves more power. + * + * @param specifiers which bands to scan. + * @param executor The executor to execute the callback on + * @param callback The callback that gets invoked when the radio responds to the request. Called + * with {@code true} if the request succeeded, {@code false} otherwise. + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void setSystemSelectionChannels(@NonNull List<RadioAccessSpecifier> specifiers, + @Nullable @CallbackExecutor Executor executor, + @Nullable Consumer<Boolean> callback) { + Objects.requireNonNull(specifiers, "Specifiers must not be null."); + if (callback != null) { + Objects.requireNonNull(executor, "Executor must not be null when" + + " the callback is nonnull"); + } + + IBooleanConsumer aidlConsumer = callback == null ? null : new IBooleanConsumer.Stub() { + @Override + public void accept(boolean result) { + executor.execute(() -> callback.accept(result)); + } + }; + + try { + ITelephony service = getITelephony(); + if (service != null) { + service.setSystemSelectionChannels(specifiers, getSubId(), aidlConsumer); + } + } catch (RemoteException ex) { + if (!isSystemProcess()) { + ex.rethrowAsRuntimeException(); + } + } + } + + /** * Verifies whether the input MCC/MNC and MVNO correspond to the current carrier. * * @param mccmnc the carrier's mccmnc that you want to match @@ -12741,4 +12941,22 @@ public class TelephonyManager { } return 0; } + + /** + * Called when userActivity is signalled in the power manager. + * This should only be called from system Uid. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void notifyUserActivity() { + try { + ITelephony service = getITelephony(); + if (service != null) { + service.userActivity(); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index 49625bbecf4f..a116c07e2646 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -78,6 +78,9 @@ public final class DataCallResponse implements Parcelable { private final List<InetAddress> mGatewayAddresses; private final List<InetAddress> mPcscfAddresses; private final int mMtu; + private final int mMtuV4; + private final int mMtuV6; + private final int mVersion; /** * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error. @@ -97,9 +100,8 @@ public final class DataCallResponse implements Parcelable { * "192.0.1.11 2001:db8::1". When null, the addresses represent point to point connections. * @param pcscfAddresses A list of Proxy Call State Control Function address via PCO (Protocol * Configuration Option) for IMS client. - * @param mtu MTU (maximum transmission unit) in bytes received from network. Zero or negative - * values means network has either not sent a value or sent an invalid value. - * either not sent a value or sent an invalid value. + * @param mtu MTU (maximum transmission unit) in bytes received from network. + * Zero or negative values means network has either not sent a value or sent an invalid value. * * @removed Use the {@link Builder()} instead. */ @@ -125,6 +127,34 @@ public final class DataCallResponse implements Parcelable { mPcscfAddresses = (pcscfAddresses == null) ? new ArrayList<>() : new ArrayList<>(pcscfAddresses); mMtu = mtu; + mMtuV4 = mMtuV6 = 0; + mVersion = 0; + } + + /** @hide */ + private DataCallResponse(@DataFailureCause int cause, int suggestedRetryTime, int id, + @LinkStatus int linkStatus, @ProtocolType int protocolType, + @Nullable String interfaceName, @Nullable List<LinkAddress> addresses, + @Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses, + @Nullable List<InetAddress> pcscfAddresses, int mtuV4, int mtuV6, int version) { + mCause = cause; + mSuggestedRetryTime = suggestedRetryTime; + mId = id; + mLinkStatus = linkStatus; + mProtocolType = protocolType; + mInterfaceName = (interfaceName == null) ? "" : interfaceName; + mAddresses = (addresses == null) + ? new ArrayList<>() : new ArrayList<>(addresses); + mDnsAddresses = (dnsAddresses == null) + ? new ArrayList<>() : new ArrayList<>(dnsAddresses); + mGatewayAddresses = (gatewayAddresses == null) + ? new ArrayList<>() : new ArrayList<>(gatewayAddresses); + mPcscfAddresses = (pcscfAddresses == null) + ? new ArrayList<>() : new ArrayList<>(pcscfAddresses); + mMtu = 0; + mMtuV4 = mtuV4; + mMtuV6 = mtuV6; + mVersion = version; } /** @hide */ @@ -145,6 +175,9 @@ public final class DataCallResponse implements Parcelable { mPcscfAddresses = new ArrayList<>(); source.readList(mPcscfAddresses, InetAddress.class.getClassLoader()); mMtu = source.readInt(); + mMtuV4 = source.readInt(); + mMtuV6 = source.readInt(); + mVersion = source.readInt(); } /** @@ -210,8 +243,29 @@ public final class DataCallResponse implements Parcelable { /** * @return MTU (maximum transmission unit) in bytes received from network. Zero or negative * values means network has either not sent a value or sent an invalid value. + * @deprecated For IRadio 1.5 and up, use {@link #getMtuV4} or {@link #getMtuV6} instead. + */ + @Deprecated + public int getMtu() { + return mVersion < 5 ? mMtu : 0; + } + + /** + * This replaces the deprecated method getMtu. + * @return MTU (maximum transmission unit) in bytes received from network, for IPv4. + * Zero or negative values means network has either not sent a value or sent an invalid value. */ - public int getMtu() { return mMtu; } + public int getMtuV4() { + return mVersion < 5 ? 0 : mMtuV4; + } + + /** + * @return MTU (maximum transmission unit) in bytes received from network, for IPv6. + * Zero or negative values means network has either not sent a value or sent an invalid value. + */ + public int getMtuV6() { + return mVersion < 5 ? 0 : mMtuV6; + } @NonNull @Override @@ -229,6 +283,9 @@ public final class DataCallResponse implements Parcelable { .append(" gateways=").append(mGatewayAddresses) .append(" pcscf=").append(mPcscfAddresses) .append(" mtu=").append(mMtu) + .append(" mtuV4=").append(mMtuV4) + .append(" mtuV6=").append(mMtuV6) + .append(" version=").append(mVersion) .append("}"); return sb.toString(); } @@ -256,14 +313,17 @@ public final class DataCallResponse implements Parcelable { && mGatewayAddresses.containsAll(other.mGatewayAddresses) && mPcscfAddresses.size() == other.mPcscfAddresses.size() && mPcscfAddresses.containsAll(other.mPcscfAddresses) - && mMtu == other.mMtu; + && mMtu == other.mMtu + && mMtuV4 == other.mMtuV4 + && mMtuV6 == other.mMtuV6 + && mVersion == other.mVersion; } @Override public int hashCode() { return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses, - mMtu); + mMtu, mMtuV4, mMtuV6, mVersion); } @Override @@ -284,6 +344,9 @@ public final class DataCallResponse implements Parcelable { dest.writeList(mGatewayAddresses); dest.writeList(mPcscfAddresses); dest.writeInt(mMtu); + dest.writeInt(mMtuV4); + dest.writeInt(mMtuV6); + dest.writeInt(mVersion); } public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR = @@ -336,6 +399,12 @@ public final class DataCallResponse implements Parcelable { private int mMtu; + private int mMtuV4; + + private int mMtuV6; + + private int mVersion; + /** * Default constructor for Builder. */ @@ -460,6 +529,7 @@ public final class DataCallResponse implements Parcelable { * negative values means network has either not sent a value or sent an invalid value. * * @return The same instance of the builder. + * @deprecated For IRadio 1.5 and up, use {@link #setMtuV4} or {@link #setMtuV6} instead. */ public @NonNull Builder setMtu(int mtu) { mMtu = mtu; @@ -467,14 +537,55 @@ public final class DataCallResponse implements Parcelable { } /** + * Set maximum transmission unit of the data connection, for IPv4. + * + * @param mtu MTU (maximum transmission unit) in bytes received from network. Zero or + * negative values means network has either not sent a value or sent an invalid value. + * + * @return The same instance of the builder. + */ + public @NonNull Builder setMtuV4(int mtu) { + mMtuV4 = mtu; + return this; + } + + /** + * Set maximum transmission unit of the data connection, for IPv6. + * + * @param mtu MTU (maximum transmission unit) in bytes received from network. Zero or + * negative values means network has either not sent a value or sent an invalid value. + * + * @return The same instance of the builder. + */ + public @NonNull Builder setMtuV6(int mtu) { + mMtuV6 = mtu; + return this; + } + + /** + * Set the IRadio version for this DataCallResponse + * @hide + */ + public @NonNull Builder setVersion(int version) { + mVersion = version; + return this; + } + + /** * Build the DataCallResponse. * * @return the DataCallResponse object. */ public @NonNull DataCallResponse build() { - return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus, - mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, - mPcscfAddresses, mMtu); + if (mVersion >= 5) { + return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus, + mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, + mPcscfAddresses, mMtuV4, mMtuV6, mVersion); + } else { + return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus, + mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, + mPcscfAddresses, mMtu); + } } } } diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java index 96a5a8151065..f2a124901bc4 100644 --- a/telephony/java/android/telephony/data/DataProfile.java +++ b/telephony/java/android/telephony/data/DataProfile.java @@ -96,7 +96,9 @@ public final class DataProfile implements Parcelable { @NetworkTypeBitMask private final int mBearerBitmask; - private final int mMtu; + private final int mMtuV4; + + private final int mMtuV6; private final boolean mPersistent; @@ -104,12 +106,11 @@ public final class DataProfile implements Parcelable { /** @hide */ private DataProfile(int profileId, String apn, @ProtocolType int protocolType, int authType, - String userName, String password, int type, int maxConnectionsTime, - int maxConnections, int waitTime, boolean enabled, - @ApnType int supportedApnTypesBitmask, - @ProtocolType int roamingProtocolType, - @NetworkTypeBitMask int bearerBitmask, int mtu, boolean persistent, - boolean preferred) { + String userName, String password, int type, int maxConnectionsTime, + int maxConnections, int waitTime, boolean enabled, + @ApnType int supportedApnTypesBitmask, @ProtocolType int roamingProtocolType, + @NetworkTypeBitMask int bearerBitmask, int mtuV4, int mtuV6, boolean persistent, + boolean preferred) { this.mProfileId = profileId; this.mApn = apn; this.mProtocolType = protocolType; @@ -128,7 +129,8 @@ public final class DataProfile implements Parcelable { this.mSupportedApnTypesBitmask = supportedApnTypesBitmask; this.mRoamingProtocolType = roamingProtocolType; this.mBearerBitmask = bearerBitmask; - this.mMtu = mtu; + this.mMtuV4 = mtuV4; + this.mMtuV6 = mtuV6; this.mPersistent = persistent; this.mPreferred = preferred; } @@ -148,7 +150,8 @@ public final class DataProfile implements Parcelable { mSupportedApnTypesBitmask = source.readInt(); mRoamingProtocolType = source.readInt(); mBearerBitmask = source.readInt(); - mMtu = source.readInt(); + mMtuV4 = source.readInt(); + mMtuV6 = source.readInt(); mPersistent = source.readBoolean(); mPreferred = source.readBoolean(); } @@ -237,8 +240,21 @@ public final class DataProfile implements Parcelable { /** * @return The maximum transmission unit (MTU) size in bytes. + * @deprecated use {@link #getMtuV4} or {@link #getMtuV6} instead. + */ + @Deprecated + public int getMtu() { return mMtuV4; } + + /** + * This replaces the deprecated method getMtu. + * @return The maximum transmission unit (MTU) size in bytes, for IPv4. + */ + public int getMtuV4() { return mMtuV4; } + + /** + * @return The maximum transmission unit (MTU) size in bytes, for IPv6. */ - public int getMtu() { return mMtu; } + public int getMtuV6() { return mMtuV6; } /** * @return {@code true} if modem must persist this data profile. @@ -265,8 +281,8 @@ public final class DataProfile implements Parcelable { (mApn + "/" + mUserName + "/" + mPassword)) + "/" + mType + "/" + mMaxConnectionsTime + "/" + mMaxConnections + "/" + mWaitTime + "/" + mEnabled + "/" + mSupportedApnTypesBitmask + "/" - + mRoamingProtocolType + "/" + mBearerBitmask + "/" + mMtu + "/" + mPersistent + "/" - + mPreferred; + + mRoamingProtocolType + "/" + mBearerBitmask + "/" + mMtuV4 + "/" + mMtuV6 + "/" + + mPersistent + "/" + mPreferred; } @Override @@ -285,7 +301,8 @@ public final class DataProfile implements Parcelable { dest.writeInt(mSupportedApnTypesBitmask); dest.writeInt(mRoamingProtocolType); dest.writeInt(mBearerBitmask); - dest.writeInt(mMtu); + dest.writeInt(mMtuV4); + dest.writeInt(mMtuV6); dest.writeBoolean(mPersistent); dest.writeBoolean(mPreferred); } @@ -319,7 +336,8 @@ public final class DataProfile implements Parcelable { && mSupportedApnTypesBitmask == that.mSupportedApnTypesBitmask && mRoamingProtocolType == that.mRoamingProtocolType && mBearerBitmask == that.mBearerBitmask - && mMtu == that.mMtu + && mMtuV4 == that.mMtuV4 + && mMtuV6 == that.mMtuV6 && mPersistent == that.mPersistent && mPreferred == that.mPreferred && Objects.equals(mApn, that.mApn) @@ -331,8 +349,8 @@ public final class DataProfile implements Parcelable { public int hashCode() { return Objects.hash(mProfileId, mApn, mProtocolType, mAuthType, mUserName, mPassword, mType, mMaxConnectionsTime, mMaxConnections, mWaitTime, mEnabled, - mSupportedApnTypesBitmask, mRoamingProtocolType, mBearerBitmask, mMtu, mPersistent, - mPreferred); + mSupportedApnTypesBitmask, mRoamingProtocolType, mBearerBitmask, mMtuV4, mMtuV6, + mPersistent, mPreferred); } /** @@ -384,7 +402,9 @@ public final class DataProfile implements Parcelable { @NetworkTypeBitMask private int mBearerBitmask; - private int mMtu; + private int mMtuV4; + + private int mMtuV6; private boolean mPersistent; @@ -567,9 +587,33 @@ public final class DataProfile implements Parcelable { * * @param mtu The maximum transmission unit (MTU) size in bytes. * @return The same instance of the builder. + * @deprecated use {@link #setMtuV4} or {@link #setMtuV6} instead. */ public @NonNull Builder setMtu(int mtu) { - mMtu = mtu; + mMtuV4 = mMtuV6 = mtu; + return this; + } + + /** + * Set the maximum transmission unit (MTU) size in bytes, for IPv4. + * This replaces the deprecated method setMtu. + * + * @param mtu The maximum transmission unit (MTU) size in bytes. + * @return The same instance of the builder. + */ + public @NonNull Builder setMtuV4(int mtu) { + mMtuV4 = mtu; + return this; + } + + /** + * Set the maximum transmission unit (MTU) size in bytes, for IPv6. + * + * @param mtu The maximum transmission unit (MTU) size in bytes. + * @return The same instance of the builder. + */ + public @NonNull Builder setMtuV6(int mtu) { + mMtuV6 = mtu; return this; } @@ -606,7 +650,7 @@ public final class DataProfile implements Parcelable { public @NonNull DataProfile build() { return new DataProfile(mProfileId, mApn, mProtocolType, mAuthType, mUserName, mPassword, mType, mMaxConnectionsTime, mMaxConnections, mWaitTime, mEnabled, - mSupportedApnTypesBitmask, mRoamingProtocolType, mBearerBitmask, mMtu, + mSupportedApnTypesBitmask, mRoamingProtocolType, mBearerBitmask, mMtuV4, mMtuV6, mPersistent, mPreferred); } } diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java index d5a48df149f1..27a70228a433 100644 --- a/telephony/java/android/telephony/euicc/EuiccManager.java +++ b/telephony/java/android/telephony/euicc/EuiccManager.java @@ -246,13 +246,56 @@ 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 remaing 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} */ 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}. + */ + 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}. + */ + 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}. + */ + 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}. + */ + 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 +534,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_EUICC_GSMA_INSTALL_ERROR, + 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. i.e + * 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_EUICC_GSMA_INSTALL_ERROR = 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; 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/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index 8c11df41cf64..037084608d26 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -101,6 +101,101 @@ public class ProvisioningManager { // Inheriting values from ImsConfig for backwards compatibility. /** + * AMR CODEC Mode Value set, 0-7 in comma separated sequence. + * <p> + * This corresponds to the {@code mode-set} parameter for the AMR codec. + * See 3GPP TS 26.101 Table 1A for more information. + * <p> + * <UL> + * <LI>0 - AMR 4.75 kbit/s</LI> + * <LI>1 - AMR 5.15 kbit/s</LI> + * <LI>2 - AMR 5.90 kbit/s</LI> + * <LI>3 - AMR 6.70 kbit/s (PDC-EFR)</LI> + * <LI>4 - AMR 7.40 kbit/s (TDMA-EFR)</LI> + * <LI>5 - AMR 7.95 kbit/s</LI> + * <LI>6 - AMR 10.2 kbit/s</LI> + * <LI>7 - AMR 12.2 kbit/s (GSM-EFR)</LI> + * </UL> + * <p> + * Value is in String format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0; + + /** + * Wide Band AMR CODEC Mode Value set,0-7 in comma separated sequence. + * <p> + * This corresponds to the {@code mode-set} parameter for the AMR wideband codec. + * See 3GPP TS 26.101 Table 1A for more information. + * <p> + * <UL> + * <LI>0 - AMR 4.75 kbit/s</LI> + * <LI>1 - AMR 5.15 kbit/s</LI> + * <LI>2 - AMR 5.90 kbit/s</LI> + * <LI>3 - AMR 6.70 kbit/s (PDC-EFR)</LI> + * <LI>4 - AMR 7.40 kbit/s (TDMA-EFR)</LI> + * <LI>5 - AMR 7.95 kbit/s</LI> + * <LI>6 - AMR 10.2 kbit/s</LI> + * <LI>7 - AMR 12.2 kbit/s (GSM-EFR)</LI> + * </UL> + * <p> + * Value is in String format. + * @see #setProvisioningStringValue(int, String) + * @see #getProvisioningStringValue(int) + */ + public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1; + + /** + * SIP Session Timer value (seconds). + * <p> + * See RFC4028 for more information. + * <p> + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_SIP_SESSION_TIMER_SEC = 2; + + /** + * Minimum SIP Session Expiration Timer in (seconds). + * <p> + * See RFC4028 for more information. + * <p> + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3; + + /** + * SIP_INVITE cancellation time out value (in milliseconds). Integer format. + * <p> + * See RFC4028 for more information. + * <p> + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4; + + /** + * Delay time when an iRAT transitions from eHRPD/HRPD/1xRTT to LTE. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5; + + /** + * Silent redial status of Enabled (True), or Disabled (False). + * Value is in boolean format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_ENABLE_SILENT_REDIAL = 6; + + /** * An integer key representing the SIP T1 timer value in milliseconds for the associated * subscription. * <p> @@ -116,6 +211,28 @@ public class ProvisioningManager { public static final int KEY_T1_TIMER_VALUE_MS = 7; /** + * SIP T2 timer value in milliseconds. See RFC 3261 for information. + * <p> + * The T2 timer is the maximum retransmit interval for non-INVITE requests and INVITE responses. + * <p> + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_T2_TIMER_VALUE_MS = 8; + + /** + * SIP TF timer value in milliseconds. See RFC 3261 for information. + * <p> + * The TF timer is the non-INVITE transaction timeout timer. + * <p> + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_TF_TIMER_VALUE_MS = 9; + + /** * An integer key representing the voice over LTE (VoLTE) provisioning status for the * associated subscription. Determines whether the user can register for voice services over * LTE. @@ -140,6 +257,43 @@ public class ProvisioningManager { public static final int KEY_VT_PROVISIONING_STATUS = 11; /** + * Domain Name for the device to populate the request URI for REGISTRATION. + * Value is in String format. + * @see #setProvisioningStringValue(int, String) + * @see #getProvisioningStringValue(int) + */ + public static final int KEY_REGISTRATION_DOMAIN_NAME = 12; + + /** + * Device Outgoing SMS based on either 3GPP or 3GPP2 standards. + * Value is in Integer format. + * Valid values are {@link #SMS_FORMAT_3GPP} and {@link #SMS_FORMAT_3GPP2}. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_SMS_FORMAT = 13; + + /** + * Value used with {@link #KEY_SMS_FORMAT} to indicate 3GPP2 SMS format is used. + * See {@link android.telephony.SmsMessage#FORMAT_3GPP2} for more information. + */ + public static final int SMS_FORMAT_3GPP2 = 0; + + /** + * Value used with {@link #KEY_SMS_FORMAT} to indicate 3GPP SMS format is used. + * See {@link android.telephony.SmsMessage#FORMAT_3GPP} for more information. + */ + public static final int SMS_FORMAT_3GPP = 1; + + /** + * Turns SMS over IMS ON/OFF on the device. + * Value is in Integer format. ON (1), OFF(0). + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_SMS_OVER_IP_ENABLED = 14; + + /** * An integer key associated with the carrier configured SIP PUBLISH timer, which dictates the * expiration time in seconds for published online availability in RCS presence. * <p> @@ -222,7 +376,7 @@ public class ProvisioningManager { public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22; /** - * An integer associated with the expiration timer used duriing the SIP subscription of a + * An integer associated with the expiration timer used during the SIP subscription of a * Request Contained List (RCL), which is used to retrieve the RCS capabilities of the contact * book. * <p> @@ -233,6 +387,14 @@ public class ProvisioningManager { public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23; /** + * Applies compression to LIST Subscription. + * Value is in Integer format. Enable (1), Disable(0). + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24; + + /** * An integer key representing the RCS enhanced address book (EAB) provisioning status for the * associated subscription. Determines whether or not SIP OPTIONS or presence will be used to * retrieve RCS capabilities for the user's contacts. @@ -270,6 +432,349 @@ public class ProvisioningManager { public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; /** + * Enable voice over wifi. Enabled (1), or Disabled (0). + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28; + + /** + * Mobile data enabled. + * Value is in Integer format. On (1), OFF(0). + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_MOBILE_DATA_ENABLED = 29; + + /** + * VoLTE user opted in status. + * Value is in Integer format. Opted-in (1) Opted-out (0). + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30; + + /** + * Proxy for Call Session Control Function(P-CSCF) address for Local-BreakOut(LBO). + * Value is in String format. + */ + public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31; + + /** + * Keep Alive Enabled for SIP. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32; + + /** + * Registration retry Base Time value in seconds. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33; + + /** + * Registration retry Max Time value in seconds. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34; + + /** + * Smallest RTP port for speech codec. + * Value is in integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + + public static final int KEY_RTP_SPEECH_START_PORT = 35; + + /** + * Largest RTP port for speech code. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_RTP_SPEECH_END_PORT = 36; + + /** + * SIP Timer A's value in milliseconds. Timer A is the INVITE request retransmit interval (in + * milliseconds), for UDP only. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37; + + /** + * SIP Timer B's value in milliseconds. Timer B is the wait time for INVITE message to be, + * in milliseconds. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38; + + /** + * SIP Timer D's value in milliseconds. Timer D is the wait time for response retransmits of + * the invite client transactions, in milliseconds. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39; + + /** + * SIP Timer E's value in milliseconds. Timer E is the value Non-INVITE request retransmit + * interval (in milliseconds), for UDP only. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40; + + /** + * SIP Timer F's value in milliseconds. Timer F is the Non-INVITE transaction timeout timer, + * in milliseconds. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41; + + /** + * SIP Timer G's value in milliseconds. Timer G is the value of INVITE response + * retransmit interval. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42; + + /** + * SIP Timer H's value in milliseconds. Timer H is the value of wait time for + * ACK receipt. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43; + + /** + * SIP Timer I's value in milliseconds. Timer I is the value of wait time for + * ACK retransmits. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44; + + /** + * SIP Timer J's value in milliseconds. Timer J is the value of wait time for + * non-invite request retransmission. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45; + + /** + * SIP Timer K's value in milliseconds. Timer K is the value of wait time for + * non-invite response retransmits. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46; + + /** + * AMR WB octet aligned dynamic payload type. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47; + + /** + * AMR WB bandwidth efficient payload type. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48; + + /** + * AMR octet aligned dynamic payload type. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49; + + /** + * AMR bandwidth efficient payload type. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50; + + /** + * DTMF WB payload type. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51; + + /** + * DTMF NB payload type. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52; + + /** + * AMR Default encoding mode. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53; + + /** + * SMS Public Service Identity. + * Value is in String format. + */ + public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54; + + /** + * Video Quality - VideoQualityFeatureValuesConstants. + * Valid values are: {@link #VIDEO_QUALITY_HIGH} and {@link #VIDEO_QUALITY_LOW}. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_VIDEO_QUALITY = 55; + + /** + * Used with {@link #KEY_VIDEO_QUALITY} to indicate low video quality. + */ + public static final int VIDEO_QUALITY_LOW = 0; + + /** + * Used with {@link #KEY_VIDEO_QUALITY} to indicate high video quality. + */ + public static final int VIDEO_QUALITY_HIGH = 1; + + /** + * LTE to WIFI handover threshold. + * Handover from LTE to WiFi if LTE < THLTE1 and WiFi >= {@link #KEY_WIFI_THRESHOLD_A}. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_LTE_THRESHOLD_1 = 56; + + /** + * WIFI to LTE handover threshold. + * Handover from WiFi to LTE if LTE >= {@link #KEY_LTE_THRESHOLD_3} or (WiFi < {@link + * #KEY_WIFI_THRESHOLD_B} and LTE >= {@link #KEY_LTE_THRESHOLD_2}). + * Value is in Integer format. + * + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_LTE_THRESHOLD_2 = 57; + + /** + * LTE to WIFI handover threshold. + * Handover from WiFi to LTE if LTE >= {@link #KEY_LTE_THRESHOLD_3} or (WiFi < {@link + * #KEY_WIFI_THRESHOLD_B} and LTE >= {@link #KEY_LTE_THRESHOLD_2}). + * Value is in Integer format. + * + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_LTE_THRESHOLD_3 = 58; + + /** + * 1x to WIFI handover threshold. + * Handover from 1x to WiFi if 1x < {@link #KEY_1X_THRESHOLD}. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_1X_THRESHOLD = 59; + + /** + * LTE to WIFI threshold A. + * Handover from LTE to WiFi if LTE < {@link #KEY_LTE_THRESHOLD_1} and WiFi >= {@link + * #KEY_WIFI_THRESHOLD_A}. + * Value is in Integer format. + * + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_WIFI_THRESHOLD_A = 60; + + /** + * WiFi to LTRE handover threshold B. + * Handover from WiFi to LTE if LTE >= {@link #KEY_LTE_THRESHOLD_3} or (WiFi < + * {@link #KEY_WIFI_THRESHOLD_B} and LTE >= {@link #KEY_LTE_THRESHOLD_2}). + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_WIFI_THRESHOLD_B = 61; + + /** + * LTE ePDG timer (in seconds). + * Device shall not handover back to LTE until the T_ePDG_LTE timer expires. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_LTE_EPDG_TIMER_SEC = 62; + + /** + * WiFi ePDG timer (in seconds). + * Device shall not handover back to WiFi until the T_ePDG_WiFi timer expires. + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_WIFI_EPDG_TIMER_SEC = 63; + + /** + * 1x ePDG timer (in seconds). + * Device shall not re-register on 1x until the T_ePDG_1x timer expires. + */ + public static final int KEY_1X_EPDG_TIMER_SEC = 64; + + /** + * MultiEndpoint status: Enabled (1), or Disabled (0). + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_MULTIENDPOINT_ENABLED = 65; + + /** + * RTT status: Enabled (1), or Disabled (0). + * Value is in Integer format. + * @see #setProvisioningIntValue(int, int) + * @see #getProvisioningIntValue(int) + */ + public static final int KEY_RTT_ENABLED = 66; + + /** * Callback for IMS provisioning changes. */ public static class Callback { 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/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java index 0d86e2b7c2b1..0f6ce13a6ce4 100644 --- a/telephony/java/com/android/ims/ImsConfig.java +++ b/telephony/java/com/android/ims/ImsConfig.java @@ -135,374 +135,596 @@ public class ImsConfig { /** * AMR CODEC Mode Value set, 0-7 in comma separated sequence. * Value is in String format. + * @deprecated use {@link ProvisioningManager#KEY_AMR_CODEC_MODE_SET_VALUES} instead. */ - public static final int VOCODER_AMRMODESET = CONFIG_START; + @Deprecated + public static final int VOCODER_AMRMODESET = + ProvisioningManager.KEY_AMR_CODEC_MODE_SET_VALUES; /** * Wide Band AMR CODEC Mode Value set,0-7 in comma separated sequence. * Value is in String format. + * @deprecated use {@link ProvisioningManager#KEY_AMR_WB_CODEC_MODE_SET_VALUES} instead. */ - public static final int VOCODER_AMRWBMODESET = 1; + @Deprecated + public static final int VOCODER_AMRWBMODESET = + ProvisioningManager.KEY_AMR_WB_CODEC_MODE_SET_VALUES; /** * SIP Session Timer value (seconds). * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_SIP_SESSION_TIMER_SEC} instead. */ - public static final int SIP_SESSION_TIMER = 2; + @Deprecated + public static final int SIP_SESSION_TIMER = ProvisioningManager.KEY_SIP_SESSION_TIMER_SEC; /** * Minimum SIP Session Expiration Timer in (seconds). * Value is in Integer format. + * @deprecated use + * {@link ProvisioningManager#KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC} instead. */ - public static final int MIN_SE = 3; + @Deprecated + public static final int MIN_SE = + ProvisioningManager.KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC; /** * SIP_INVITE cancellation time out value (in milliseconds). Integer format. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_SIP_INVITE_CANCELLATION_TIMER_MS} instead. */ - public static final int CANCELLATION_TIMER = 4; + @Deprecated + public static final int CANCELLATION_TIMER = + ProvisioningManager.KEY_SIP_INVITE_CANCELLATION_TIMER_MS; /** * Delay time when an iRAT transition from eHRPD/HRPD/1xRTT to LTE. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_TRANSITION_TO_LTE_DELAY_MS} instead. */ - public static final int TDELAY = 5; + @Deprecated + public static final int TDELAY = ProvisioningManager.KEY_TRANSITION_TO_LTE_DELAY_MS; /** * Silent redial status of Enabled (True), or Disabled (False). * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_ENABLE_SILENT_REDIAL} instead. */ - public static final int SILENT_REDIAL_ENABLE = 6; + @Deprecated + public static final int SILENT_REDIAL_ENABLE = ProvisioningManager.KEY_ENABLE_SILENT_REDIAL; /** * SIP T1 timer value in milliseconds. See RFC 3261 for define. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_T1_TIMER_VALUE_MS} instead. */ + @Deprecated public static final int SIP_T1_TIMER = ProvisioningManager.KEY_T1_TIMER_VALUE_MS; /** * SIP T2 timer value in milliseconds. See RFC 3261 for define. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_T2_TIMER_VALUE_MS} instead. */ - public static final int SIP_T2_TIMER = 8; + @Deprecated + public static final int SIP_T2_TIMER = ProvisioningManager.KEY_T2_TIMER_VALUE_MS; - /** + /** * SIP TF timer value in milliseconds. See RFC 3261 for define. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_TF_TIMER_VALUE_MS} instead. */ - public static final int SIP_TF_TIMER = 9; + @Deprecated + public static final int SIP_TF_TIMER = ProvisioningManager.KEY_TF_TIMER_VALUE_MS; /** * VoLTE status for VLT/s status of Enabled (1), or Disabled (0). * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_VOLTE_PROVISIONING_STATUS} instead. */ + @Deprecated public static final int VLT_SETTING_ENABLED = ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS; /** * VoLTE status for LVC/s status of Enabled (1), or Disabled (0). * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_VT_PROVISIONING_STATUS} instead. */ + @Deprecated public static final int LVC_SETTING_ENABLED = ProvisioningManager.KEY_VT_PROVISIONING_STATUS; + /** * Domain Name for the device to populate the request URI for REGISTRATION. * Value is in String format. + * @deprecated use {@link ProvisioningManager#KEY_REGISTRATION_DOMAIN_NAME}. */ - public static final int DOMAIN_NAME = 12; + @Deprecated + public static final int DOMAIN_NAME = ProvisioningManager.KEY_REGISTRATION_DOMAIN_NAME; + /** * Device Outgoing SMS based on either 3GPP or 3GPP2 standards. * Value is in Integer format. 3GPP2(0), 3GPP(1) - */ - public static final int SMS_FORMAT = 13; + * @deprecated use {@link ProvisioningManager#KEY_SMS_FORMAT}. + */ + @Deprecated + public static final int SMS_FORMAT = ProvisioningManager.KEY_SMS_FORMAT; + /** * Turns IMS ON/OFF on the device. * Value is in Integer format. ON (1), OFF(0). - */ - public static final int SMS_OVER_IP = 14; + * @deprecated use {@link ProvisioningManager#KEY_SMS_OVER_IP_ENABLED}. + */ + @Deprecated + public static final int SMS_OVER_IP = ProvisioningManager.KEY_SMS_OVER_IP_ENABLED; + /** * Requested expiration for Published Online availability. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_RCS_PUBLISH_TIMER_SEC}. */ + @Deprecated public static final int PUBLISH_TIMER = ProvisioningManager.KEY_RCS_PUBLISH_TIMER_SEC; + /** * Requested expiration for Published Offline availability. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC}. */ + @Deprecated public static final int PUBLISH_TIMER_EXTENDED = ProvisioningManager.KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC; + /** * * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_RCS_CAPABILITY_DISCOVERY_ENABLED}. */ + @Deprecated public static final int CAPABILITY_DISCOVERY_ENABLED = ProvisioningManager.KEY_RCS_CAPABILITY_DISCOVERY_ENABLED; + /** - * Period of time the capability information of the contact is cached on handset. + * Period of time the capability information of the contact is cached on handset. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC}. */ + @Deprecated public static final int CAPABILITIES_CACHE_EXPIRATION = ProvisioningManager.KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC; + /** * Peiod of time the availability information of a contact is cached on device. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC}. */ + @Deprecated public static final int AVAILABILITY_CACHE_EXPIRATION = ProvisioningManager.KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC; + /** * Interval between successive capabilities polling. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC}. */ + @Deprecated public static final int CAPABILITIES_POLL_INTERVAL = ProvisioningManager.KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC; + /** * Minimum time between two published messages from the device. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS}. */ + @Deprecated public static final int SOURCE_THROTTLE_PUBLISH = ProvisioningManager.KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS; + /** * The Maximum number of MDNs contained in one Request Contained List. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_RCS_MAX_NUM_ENTRIES_IN_RCL}. */ + @Deprecated public static final int MAX_NUMENTRIES_IN_RCL = ProvisioningManager.KEY_RCS_MAX_NUM_ENTRIES_IN_RCL; + /** * Expiration timer for subscription of a Request Contained List, used in capability * polling. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC}. */ + @Deprecated public static final int CAPAB_POLL_LIST_SUB_EXP = ProvisioningManager.KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC; + /** * Applies compression to LIST Subscription. * Value is in Integer format. Enable (1), Disable(0). + * @deprecated use {@link ProvisioningManager#KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION}. */ - public static final int GZIP_FLAG = 24; + @Deprecated + public static final int GZIP_FLAG = ProvisioningManager.KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION; + /** * VOLTE Status for EAB/s status of Enabled (1), or Disabled (0). * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_EAB_PROVISIONING_STATUS}. */ + @Deprecated public static final int EAB_SETTING_ENABLED = ProvisioningManager.KEY_EAB_PROVISIONING_STATUS; + /** * Wi-Fi calling roaming status. * Value is in Integer format. ON (1), OFF(0). + * @deprecated use {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE} + * instead. */ + @Deprecated public static final int VOICE_OVER_WIFI_ROAMING = ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE; + /** - * Wi-Fi calling modem - WfcModeFeatureValueConstants. + * Wi-Fi calling mode - WfcModeFeatureValueConstants. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_MODE_OVERRIDE} + * instead. */ + @Deprecated public static final int VOICE_OVER_WIFI_MODE = ProvisioningManager.KEY_VOICE_OVER_WIFI_MODE_OVERRIDE; + /** * VOLTE Status for voice over wifi status of Enabled (1), or Disabled (0). * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE}. */ - public static final int VOICE_OVER_WIFI_SETTING_ENABLED = 28; + @Deprecated + public static final int VOICE_OVER_WIFI_SETTING_ENABLED = + ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE; + + /** * Mobile data enabled. * Value is in Integer format. On (1), OFF(0). + * @deprecated use {@link ProvisioningManager#KEY_MOBILE_DATA_ENABLED}. */ - public static final int MOBILE_DATA_ENABLED = 29; + @Deprecated + public static final int MOBILE_DATA_ENABLED = ProvisioningManager.KEY_MOBILE_DATA_ENABLED; + /** * VoLTE user opted in status. * Value is in Integer format. Opted-in (1) Opted-out (0). + * @deprecated use {@link ProvisioningManager#KEY_VOLTE_USER_OPT_IN_STATUS}. */ - public static final int VOLTE_USER_OPT_IN_STATUS = 30; + @Deprecated + public static final int VOLTE_USER_OPT_IN_STATUS = + ProvisioningManager.KEY_VOLTE_USER_OPT_IN_STATUS; + /** * Proxy for Call Session Control Function(P-CSCF) address for Local-BreakOut(LBO). * Value is in String format. + * @deprecated use {@link ProvisioningManager#KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS}. */ - public static final int LBO_PCSCF_ADDRESS = 31; + @Deprecated + public static final int LBO_PCSCF_ADDRESS = + ProvisioningManager.KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS; + /** * Keep Alive Enabled for SIP. * Value is in Integer format. On(1), OFF(0). + * @deprecated use {@link ProvisioningManager#KEY_SIP_KEEP_ALIVE_ENABLED}. */ - public static final int KEEP_ALIVE_ENABLED = 32; + @Deprecated + public static final int KEEP_ALIVE_ENABLED = ProvisioningManager.KEY_SIP_KEEP_ALIVE_ENABLED; + /** * Registration retry Base Time value in seconds. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_REGISTRATION_RETRY_BASE_TIME_SEC}. */ - public static final int REGISTRATION_RETRY_BASE_TIME_SEC = 33; + @Deprecated + public static final int REGISTRATION_RETRY_BASE_TIME_SEC = + ProvisioningManager.KEY_REGISTRATION_RETRY_BASE_TIME_SEC; + /** * Registration retry Max Time value in seconds. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_REGISTRATION_RETRY_MAX_TIME_SEC}. */ - public static final int REGISTRATION_RETRY_MAX_TIME_SEC = 34; + @Deprecated + public static final int REGISTRATION_RETRY_MAX_TIME_SEC = + ProvisioningManager.KEY_REGISTRATION_RETRY_MAX_TIME_SEC; + /** * Smallest RTP port for speech codec. * Value is in integer format. + * @deprecated use {@link ProvisioningManager#KEY_RTP_SPEECH_START_PORT}. */ - public static final int SPEECH_START_PORT = 35; + @Deprecated + public static final int SPEECH_START_PORT = ProvisioningManager.KEY_RTP_SPEECH_START_PORT; + /** * Largest RTP port for speech code. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_RTP_SPEECH_END_PORT}. */ - public static final int SPEECH_END_PORT = 36; + @Deprecated + public static final int SPEECH_END_PORT = ProvisioningManager.KEY_RTP_SPEECH_END_PORT; + /** * SIP Timer A's value in milliseconds. Timer A is the INVITE request * retransmit interval, for UDP only. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS}. */ - public static final int SIP_INVITE_REQ_RETX_INTERVAL_MSEC = 37; + @Deprecated + public static final int SIP_INVITE_REQ_RETX_INTERVAL_MSEC = + ProvisioningManager.KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS; + /** * SIP Timer B's value in milliseconds. Timer B is the wait time for * INVITE message to be acknowledged. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_SIP_INVITE_ACK_WAIT_TIME_MS}. */ - public static final int SIP_INVITE_RSP_WAIT_TIME_MSEC = 38; + @Deprecated + public static final int SIP_INVITE_RSP_WAIT_TIME_MSEC = + ProvisioningManager.KEY_SIP_INVITE_ACK_WAIT_TIME_MS; + /** * SIP Timer D's value in milliseconds. Timer D is the wait time for * response retransmits of the invite client transactions. * Value is in Integer format. + * @deprecated use + * {@link ProvisioningManager#KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS}. */ - public static final int SIP_INVITE_RSP_RETX_WAIT_TIME_MSEC = 39; + @Deprecated + public static final int SIP_INVITE_RSP_RETX_WAIT_TIME_MSEC = + ProvisioningManager.KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS; + /** * SIP Timer E's value in milliseconds. Timer E is the value Non-INVITE * request retransmit interval, for UDP only. * Value is in Integer format. + * @deprecated use + * {@link ProvisioningManager#KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS}. */ - public static final int SIP_NON_INVITE_REQ_RETX_INTERVAL_MSEC = 40; + @Deprecated + public static final int SIP_NON_INVITE_REQ_RETX_INTERVAL_MSEC = + ProvisioningManager.KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS; + /** * SIP Timer F's value in milliseconds. Timer F is the Non-INVITE transaction * timeout timer. * Value is in Integer format. + * @deprecated use + * {@link ProvisioningManager#KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS}. */ - public static final int SIP_NON_INVITE_TXN_TIMEOUT_TIMER_MSEC = 41; + @Deprecated + public static final int SIP_NON_INVITE_TXN_TIMEOUT_TIMER_MSEC = + ProvisioningManager.KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS; + /** * SIP Timer G's value in milliseconds. Timer G is the value of INVITE response * retransmit interval. * Value is in Integer format. + * @deprecated use + * {@link ProvisioningManager#KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS}. */ - public static final int SIP_INVITE_RSP_RETX_INTERVAL_MSEC = 42; + @Deprecated + public static final int SIP_INVITE_RSP_RETX_INTERVAL_MSEC = + ProvisioningManager.KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS; + /** * SIP Timer H's value in milliseconds. Timer H is the value of wait time for * ACK receipt. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS}. */ - public static final int SIP_ACK_RECEIPT_WAIT_TIME_MSEC = 43; + @Deprecated + public static final int SIP_ACK_RECEIPT_WAIT_TIME_MSEC = + ProvisioningManager.KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS; + /** * SIP Timer I's value in milliseconds. Timer I is the value of wait time for * ACK retransmits. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS}. */ - public static final int SIP_ACK_RETX_WAIT_TIME_MSEC = 44; + @Deprecated + public static final int SIP_ACK_RETX_WAIT_TIME_MSEC = + ProvisioningManager.KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS; + /** * SIP Timer J's value in milliseconds. Timer J is the value of wait time for * non-invite request retransmission. * Value is in Integer format. + * @deprecated use + * {@link ProvisioningManager#KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS}. */ - public static final int SIP_NON_INVITE_REQ_RETX_WAIT_TIME_MSEC = 45; + @Deprecated + public static final int SIP_NON_INVITE_REQ_RETX_WAIT_TIME_MSEC = + ProvisioningManager.KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS; + /** * SIP Timer K's value in milliseconds. Timer K is the value of wait time for * non-invite response retransmits. * Value is in Integer format. + * @deprecated use + * {@link ProvisioningManager#KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS}. */ - public static final int SIP_NON_INVITE_RSP_RETX_WAIT_TIME_MSEC = 46; + @Deprecated + public static final int SIP_NON_INVITE_RSP_RETX_WAIT_TIME_MSEC = + ProvisioningManager.KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS; + /** * AMR WB octet aligned dynamic payload type. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE}. */ - public static final int AMR_WB_OCTET_ALIGNED_PT = 47; + @Deprecated + public static final int AMR_WB_OCTET_ALIGNED_PT = + ProvisioningManager.KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE; + /** * AMR WB bandwidth efficient payload type. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE}. */ - public static final int AMR_WB_BANDWIDTH_EFFICIENT_PT = 48; + @Deprecated + public static final int AMR_WB_BANDWIDTH_EFFICIENT_PT = + ProvisioningManager.KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE; + /** * AMR octet aligned dynamic payload type. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE}. */ - public static final int AMR_OCTET_ALIGNED_PT = 49; + @Deprecated + public static final int AMR_OCTET_ALIGNED_PT = + ProvisioningManager.KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE; + /** * AMR bandwidth efficient payload type. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE}. */ - public static final int AMR_BANDWIDTH_EFFICIENT_PT = 50; + @Deprecated + public static final int AMR_BANDWIDTH_EFFICIENT_PT = + ProvisioningManager.KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE; + /** * DTMF WB payload type. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_DTMF_WB_PAYLOAD_TYPE}. */ - public static final int DTMF_WB_PT = 51; + @Deprecated + public static final int DTMF_WB_PT = ProvisioningManager.KEY_DTMF_WB_PAYLOAD_TYPE; + /** * DTMF NB payload type. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_DTMF_NB_PAYLOAD_TYPE}. */ - public static final int DTMF_NB_PT = 52; + @Deprecated + public static final int DTMF_NB_PT = ProvisioningManager.KEY_DTMF_NB_PAYLOAD_TYPE; + /** * AMR Default encoding mode. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_AMR_DEFAULT_ENCODING_MODE}. */ - public static final int AMR_DEFAULT_MODE = 53; + @Deprecated + public static final int AMR_DEFAULT_MODE = + ProvisioningManager.KEY_AMR_DEFAULT_ENCODING_MODE; + /** * SMS Public Service Identity. * Value is in String format. + * @deprecated use {@link ProvisioningManager#KEY_SMS_PUBLIC_SERVICE_IDENTITY}. */ - public static final int SMS_PSI = 54; + @Deprecated + public static final int SMS_PSI = ProvisioningManager.KEY_SMS_PUBLIC_SERVICE_IDENTITY; + /** * Video Quality - VideoQualityFeatureValuesConstants. * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_VIDEO_QUALITY}. */ - public static final int VIDEO_QUALITY = 55; + @Deprecated + public static final int VIDEO_QUALITY = ProvisioningManager.KEY_VIDEO_QUALITY; + /** * LTE threshold. * Handover from LTE to WiFi if LTE < THLTE1 and WiFi >= VOWT_A. + * @deprecated use {@link ProvisioningManager#KEY_LTE_THRESHOLD_1}. */ - public static final int TH_LTE1 = 56; + @Deprecated + public static final int TH_LTE1 = ProvisioningManager.KEY_LTE_THRESHOLD_1; + /** * LTE threshold. * Handover from WiFi to LTE if LTE >= THLTE3 or (WiFi < VOWT_B and LTE >= THLTE2). + * @deprecated use {@link ProvisioningManager#KEY_LTE_THRESHOLD_2}. */ - public static final int TH_LTE2 = 57; + @Deprecated + public static final int TH_LTE2 = ProvisioningManager.KEY_LTE_THRESHOLD_2; + /** * LTE threshold. * Handover from WiFi to LTE if LTE >= THLTE3 or (WiFi < VOWT_B and LTE >= THLTE2). + * @deprecated use {@link ProvisioningManager#KEY_LTE_THRESHOLD_3}. */ - public static final int TH_LTE3 = 58; + @Deprecated + public static final int TH_LTE3 = ProvisioningManager.KEY_LTE_THRESHOLD_3; + /** * 1x threshold. * Handover from 1x to WiFi if 1x < TH1x + * @deprecated use {@link ProvisioningManager#KEY_1X_THRESHOLD}. */ - public static final int TH_1x = 59; + @Deprecated + public static final int TH_1x = ProvisioningManager.KEY_1X_THRESHOLD; + /** * WiFi threshold. * Handover from LTE to WiFi if LTE < THLTE1 and WiFi >= VOWT_A. + * @deprecated use {@link ProvisioningManager#KEY_WIFI_THRESHOLD_A}. */ - public static final int VOWT_A = 60; + @Deprecated + public static final int VOWT_A = ProvisioningManager.KEY_WIFI_THRESHOLD_A; + /** * WiFi threshold. * Handover from WiFi to LTE if LTE >= THLTE3 or (WiFi < VOWT_B and LTE >= THLTE2). + * @deprecated use {@link ProvisioningManager#KEY_WIFI_THRESHOLD_B}. */ - public static final int VOWT_B = 61; + @Deprecated + public static final int VOWT_B = ProvisioningManager.KEY_WIFI_THRESHOLD_B; + /** * LTE ePDG timer. * Device shall not handover back to LTE until the T_ePDG_LTE timer expires. + * @deprecated use {@link ProvisioningManager#KEY_LTE_EPDG_TIMER_SEC}. */ - public static final int T_EPDG_LTE = 62; + @Deprecated + public static final int T_EPDG_LTE = ProvisioningManager.KEY_LTE_EPDG_TIMER_SEC; + /** * WiFi ePDG timer. * Device shall not handover back to WiFi until the T_ePDG_WiFi timer expires. + * @deprecated use {@link ProvisioningManager#KEY_WIFI_EPDG_TIMER_SEC}. */ - public static final int T_EPDG_WIFI = 63; + @Deprecated + public static final int T_EPDG_WIFI = ProvisioningManager.KEY_WIFI_EPDG_TIMER_SEC; + /** * 1x ePDG timer. * Device shall not re-register on 1x until the T_ePDG_1x timer expires. + * @deprecated use {@link ProvisioningManager#KEY_1X_EPDG_TIMER_SEC}. */ - public static final int T_EPDG_1X = 64; + @Deprecated + public static final int T_EPDG_1X = ProvisioningManager.KEY_1X_EPDG_TIMER_SEC; + /** * MultiEndpoint status: Enabled (1), or Disabled (0). * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_MULTIENDPOINT_ENABLED}. */ - public static final int VICE_SETTING_ENABLED = 65; + @Deprecated + public static final int VICE_SETTING_ENABLED = ProvisioningManager.KEY_MULTIENDPOINT_ENABLED; /** * RTT status: Enabled (1), or Disabled (0). * Value is in Integer format. + * @deprecated use {@link ProvisioningManager#KEY_RTT_ENABLED}. */ - public static final int RTT_SETTING_ENABLED = 66; + @Deprecated + public static final int RTT_SETTING_ENABLED = ProvisioningManager.KEY_RTT_ENABLED; // Expand the operator config items as needed here, need to change // PROVISIONED_CONFIG_END after that. 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/IBooleanConsumer.aidl b/telephony/java/com/android/internal/telephony/IBooleanConsumer.aidl new file mode 100644 index 000000000000..eb5bedabf15f --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IBooleanConsumer.aidl @@ -0,0 +1,23 @@ +/* + * 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.telephony; + +// Copies consumer pattern for an operation that requires a boolean result from another process to +// finish. +oneway interface IBooleanConsumer { + void accept(boolean result); +}
\ No newline at end of file diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index e88c7736f094..6aa5a52d55d5 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -38,8 +38,10 @@ import android.telephony.ICellInfoCallback; import android.telephony.ModemActivityInfo; import android.telephony.NeighboringCellInfo; import android.telephony.NetworkScanRequest; +import android.telephony.PhoneCapability; import android.telephony.PhoneNumberRange; import android.telephony.RadioAccessFamily; +import android.telephony.RadioAccessSpecifier; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.TelephonyHistogram; @@ -54,6 +56,7 @@ import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.aidl.IImsRegistrationCallback; import com.android.ims.internal.IImsServiceFeatureCallback; import com.android.internal.telephony.CellNetworkScanResult; +import com.android.internal.telephony.IBooleanConsumer; import com.android.internal.telephony.IIntegerConsumer; import com.android.internal.telephony.INumberVerificationCallback; import com.android.internal.telephony.OperatorInfo; @@ -1825,12 +1828,17 @@ interface ITelephony { /** * Return the network selection mode on the subscription with id {@code subId}. */ - int getNetworkSelectionMode(int subId); + int getNetworkSelectionMode(int subId); - /** + /** + * Return the PhoneCapability for the device. + */ + PhoneCapability getPhoneCapability(int subId, String callingPackage, String callingFeatureId); + + /** * Return true if the device is in emergency sms mode, false otherwise. */ - boolean isInEmergencySmsMode(); + boolean isInEmergencySmsMode(); /** * Return the modem radio power state for slot index. @@ -2126,6 +2134,9 @@ interface ITelephony { boolean isApnMetered(int apnType, int subId); + oneway void setSystemSelectionChannels(in List<RadioAccessSpecifier> specifiers, + int subId, IBooleanConsumer resultCallback); + boolean isMvnoMatched(int subId, int mvnoType, String mvnoMatchData); /** @@ -2175,4 +2186,24 @@ interface ITelephony { int setIccLockEnabled(int subId, boolean enabled, String password); int changeIccLockPassword(int subId, String oldPassword, String newPassword); + + /** + * Request for receiving user activity notification + */ + oneway void requestUserActivityNotification(); + + /** + * Called when userActivity is signalled in the power manager. + * This is safe to call from any thread, with any window manager locks held or not. + */ + oneway void userActivity(); + + /** + * Get the user manual network selection. + * Return empty string if in automatic selection. + * + * @param subId the id of the subscription + * @return operatorinfo on success + */ + String getManualNetworkSelectionPlmn(int subId); } diff --git a/telephony/java/com/android/internal/telephony/OperatorInfo.java b/telephony/java/com/android/internal/telephony/OperatorInfo.java index 64d786391021..2ca459811e04 100644 --- a/telephony/java/com/android/internal/telephony/OperatorInfo.java +++ b/telephony/java/com/android/internal/telephony/OperatorInfo.java @@ -20,6 +20,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import android.telephony.AccessNetworkConstants.AccessNetworkType; /** * @hide @@ -43,6 +44,7 @@ public class OperatorInfo implements Parcelable { @UnsupportedAppUsage private State mState = State.UNKNOWN; + private int mRan = AccessNetworkType.UNKNOWN; @UnsupportedAppUsage @@ -69,6 +71,10 @@ public class OperatorInfo implements Parcelable { return mState; } + public int getRan() { + return mRan; + } + @UnsupportedAppUsage OperatorInfo(String operatorAlphaLong, String operatorAlphaShort, @@ -82,6 +88,14 @@ public class OperatorInfo implements Parcelable { mState = state; } + OperatorInfo(String operatorAlphaLong, + String operatorAlphaShort, + String operatorNumeric, + State state, + int ran) { + this (operatorAlphaLong, operatorAlphaShort, operatorNumeric, state); + mRan = ran; + } @UnsupportedAppUsage public OperatorInfo(String operatorAlphaLong, @@ -92,6 +106,14 @@ public class OperatorInfo implements Parcelable { operatorNumeric, rilStateToState(stateString)); } + public OperatorInfo(String operatorAlphaLong, + String operatorAlphaShort, + String operatorNumeric, + int ran) { + this (operatorAlphaLong, operatorAlphaShort, operatorNumeric); + mRan = ran; + } + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public OperatorInfo(String operatorAlphaLong, String operatorAlphaShort, @@ -124,7 +146,8 @@ public class OperatorInfo implements Parcelable { return "OperatorInfo " + mOperatorAlphaLong + "/" + mOperatorAlphaShort + "/" + mOperatorNumeric - + "/" + mState; + + "/" + mState + + "/" + mRan; } /** @@ -150,6 +173,7 @@ public class OperatorInfo implements Parcelable { dest.writeString(mOperatorAlphaShort); dest.writeString(mOperatorNumeric); dest.writeSerializable(mState); + dest.writeInt(mRan); } /** @@ -158,20 +182,21 @@ public class OperatorInfo implements Parcelable { */ @UnsupportedAppUsage public static final Creator<OperatorInfo> CREATOR = - new Creator<OperatorInfo>() { - @Override - public OperatorInfo createFromParcel(Parcel in) { - OperatorInfo opInfo = new OperatorInfo( - in.readString(), /*operatorAlphaLong*/ - in.readString(), /*operatorAlphaShort*/ - in.readString(), /*operatorNumeric*/ - (State) in.readSerializable()); /*state*/ - return opInfo; - } - - @Override - public OperatorInfo[] newArray(int size) { - return new OperatorInfo[size]; - } - }; + new Creator<OperatorInfo>() { + @Override + public OperatorInfo createFromParcel(Parcel in) { + OperatorInfo opInfo = new OperatorInfo( + in.readString(), /*operatorAlphaLong*/ + in.readString(), /*operatorAlphaShort*/ + in.readString(), /*operatorNumeric*/ + (State) in.readSerializable(), /*state*/ + in.readInt()); /*ran*/ + return opInfo; + } + + @Override + public OperatorInfo[] newArray(int size) { + return new OperatorInfo[size]; + } + }; } diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index 9ee26c28f906..0db86d6054b3 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -493,6 +493,7 @@ public interface RILConstants { int RIL_REQUEST_SWITCH_DUAL_SIM_CONFIG = 207; int RIL_REQUEST_ENABLE_UICC_APPLICATIONS = 208; int RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT = 209; + int RIL_REQUEST_SET_SYSTEM_SELECTION_CHANNELS = 210; /* Responses begin */ int RIL_RESPONSE_ACKNOWLEDGEMENT = 800; diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java index a15f73cf348d..48f785091764 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -180,8 +180,6 @@ public class TelephonyIntents { public static final String ACTION_SIM_STATE_CHANGED = Intent.ACTION_SIM_STATE_CHANGED; - public static final String EXTRA_REBROADCAST_ON_UNLOCK= "rebroadcastOnUnlock"; - /** * <p>Broadcast Action: It indicates the Emergency callback mode blocks datacall/sms * <p class="note">. @@ -214,37 +212,6 @@ public 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/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java index 832502cae37d..d0c8024c56fe 100644 --- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java @@ -148,10 +148,9 @@ public class SmsMessage extends SmsMessageBase { } /** - * Create an SmsMessage from an SMS EF record. + * Creates an SmsMessage from an SMS EF record. * - * @param index Index of SMS record. This should be index in ArrayList - * returned by RuimSmsInterfaceManager.getAllMessagesFromIcc + 1. + * @param index Index of SMS EF record. * @param data Record data. * @return An SmsMessage representing the record. * @@ -202,26 +201,16 @@ public class SmsMessage extends SmsMessageBase { } /** - * TODO(cleanup): why do getSubmitPdu methods take an scAddr input - * and do nothing with it? GSM allows us to specify a SC (eg, - * when responding to an SMS that explicitly requests the response - * is sent to a specific SC), or pass null to use the default - * value. Is there no similar notion in CDMA? Or do we just not - * have it hooked up? - */ - - /** - * Get an SMS-SUBMIT PDU for a destination address and a message + * Gets an SMS-SUBMIT PDU for a destination address and a message. * - * @param scAddr Service Centre address. Null means use default. - * @param destAddr Address of the recipient. - * @param message String representation of the message payload. - * @param statusReportRequested Indicates whether a report is requested for this message. - * @param smsHeader Array containing the data for the User Data Header, preceded - * by the Element Identifiers. - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param scAddr Service Centre address. No use for this message. + * @param destAddr the address of the destination for the message. + * @param message string representation of the message payload. + * @param statusReportRequested indicates whether a report is requested for this message. + * @param smsHeader array containing the data for the User Data Header, preceded by the Element + * Identifiers. + * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns + * null on encode error. * @hide */ @UnsupportedAppUsage @@ -231,18 +220,17 @@ public class SmsMessage extends SmsMessageBase { } /** - * Get an SMS-SUBMIT PDU for a destination address and a message + * Gets an SMS-SUBMIT PDU for a destination address and a message. * - * @param scAddr Service Centre address. Null means use default. - * @param destAddr Address of the recipient. - * @param message String representation of the message payload. - * @param statusReportRequested Indicates whether a report is requested for this message. - * @param smsHeader Array containing the data for the User Data Header, preceded - * by the Element Identifiers. - * @param priority Priority level of the message - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param scAddr Service Centre address. No use for this message. + * @param destAddr the address of the destination for the message. + * @param message string representation of the message payload. + * @param statusReportRequested indicates whether a report is requested for this message. + * @param smsHeader array containing the data for the User Data Header, preceded by the Element + * Identifiers. + * @param priority priority level of the message. + * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns + * null on encode error. * @hide */ @UnsupportedAppUsage @@ -265,16 +253,15 @@ public class SmsMessage extends SmsMessageBase { } /** - * Get an SMS-SUBMIT PDU for a data message to a destination address and port. + * Gets an SMS-SUBMIT PDU for a data message to a destination address & port. * - * @param scAddr Service Centre address. null == use default - * @param destAddr the address of the destination for the message - * @param destPort the port to deliver the message to at the - * destination - * @param data the data for the message - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param scAddr Service Centre address. No use for this message. + * @param destAddr the address of the destination for the message. + * @param destPort the port to deliver the message to at the destination. + * @param data the data for the message. + * @param statusReportRequested indicates whether a report is requested for this message. + * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns + * null on encode error. */ @UnsupportedAppUsage public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, int destPort, @@ -305,14 +292,13 @@ public class SmsMessage extends SmsMessageBase { } /** - * Get an SMS-SUBMIT PDU for a data message to a destination address & port + * Gets an SMS-SUBMIT PDU for a data message to a destination address & port. * - * @param destAddr the address of the destination for the message - * @param userData the data for the message - * @param statusReportRequested Indicates whether a report is requested for this message. - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param destAddr the address of the destination for the message. + * @param userData the data for the message. + * @param statusReportRequested indicates whether a report is requested for this message. + * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns + * null on encode error. */ @UnsupportedAppUsage public static SubmitPdu getSubmitPdu(String destAddr, UserData userData, @@ -321,15 +307,14 @@ public class SmsMessage extends SmsMessageBase { } /** - * Get an SMS-SUBMIT PDU for a data message to a destination address & port + * Gets an SMS-SUBMIT PDU for a data message to a destination address & port. * - * @param destAddr the address of the destination for the message - * @param userData the data for the message - * @param statusReportRequested Indicates whether a report is requested for this message. - * @param priority Priority level of the message - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param destAddr the address of the destination for the message. + * @param userData the data for the message. + * @param statusReportRequested indicates whether a report is requested for this message. + * @param priority Priority level of the message. + * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns + * null on encode error. */ @UnsupportedAppUsage public static SubmitPdu getSubmitPdu(String destAddr, UserData userData, @@ -1059,6 +1044,72 @@ public class SmsMessage extends SmsMessageBase { } /** + * Gets an SMS-DELIVER PDU for a originating address and a message. + * + * @param origAddr the address of the originating for the message. + * @param message string representation of the message payload. + * @param date the time stamp the message was received. + * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns + * null on encode error. + * @hide + */ + public static SubmitPdu getDeliverPdu(String origAddr, String message, long date) { + if (origAddr == null || message == null) { + return null; + } + + CdmaSmsAddress addr = CdmaSmsAddress.parse(origAddr); + if (addr == null) return null; + + BearerData bearerData = new BearerData(); + bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER; + + bearerData.messageId = 0; + + bearerData.deliveryAckReq = false; + bearerData.userAckReq = false; + bearerData.readAckReq = false; + bearerData.reportReq = false; + + bearerData.userData = new UserData(); + bearerData.userData.payloadStr = message; + + bearerData.msgCenterTimeStamp = BearerData.TimeStamp.fromMillis(date); + + byte[] encodedBearerData = BearerData.encode(bearerData); + if (encodedBearerData == null) return null; + + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(100); + DataOutputStream dos = new DataOutputStream(baos); + dos.writeInt(SmsEnvelope.TELESERVICE_WMT); + dos.writeInt(0); // servicePresent + dos.writeInt(0); // serviceCategory + dos.write(addr.digitMode); + dos.write(addr.numberMode); + dos.write(addr.ton); // number_type + dos.write(addr.numberPlan); + dos.write(addr.numberOfDigits); + dos.write(addr.origBytes, 0, addr.origBytes.length); // digits + // Subaddress is not supported. + dos.write(0); // subaddressType + dos.write(0); // subaddr_odd + dos.write(0); // subaddr_nbr_of_digits + dos.write(encodedBearerData.length); + dos.write(encodedBearerData, 0, encodedBearerData.length); + dos.close(); + + SubmitPdu pdu = new SubmitPdu(); + pdu.encodedMessage = baos.toByteArray(); + pdu.encodedScAddress = null; + return pdu; + } catch (IOException ex) { + Rlog.e(LOG_TAG, "creating Deliver PDU failed: " + ex); + } + return null; + } + + /** * Creates byte array (pseudo pdu) from SMS object. * Note: Do not call this method more than once per object! * @hide diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java index cbf0f5c297e1..6ad6dd119f50 100644 --- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java +++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java @@ -32,6 +32,7 @@ import com.android.internal.telephony.uicc.IccUtils; import com.android.internal.util.BitwiseInputStream; import com.android.internal.util.BitwiseOutputStream; +import java.io.ByteArrayOutputStream; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; @@ -284,6 +285,33 @@ public final class BearerData { return ts; } + public static TimeStamp fromMillis(long timeInMillis) { + TimeStamp ts = new TimeStamp(); + LocalDateTime localDateTime = + Instant.ofEpochMilli(timeInMillis).atZone(ts.mZoneId).toLocalDateTime(); + int year = localDateTime.getYear(); + if (year < 1996 || year > 2095) return null; + ts.year = year; + ts.month = localDateTime.getMonthValue(); + ts.monthDay = localDateTime.getDayOfMonth(); + ts.hour = localDateTime.getHour(); + ts.minute = localDateTime.getMinute(); + ts.second = localDateTime.getSecond(); + return ts; + } + + public byte[] toByteArray() { + int year = this.year % 100; // 00 - 99 + ByteArrayOutputStream outStream = new ByteArrayOutputStream(6); + outStream.write((((year / 10) & 0x0F) << 4) | ((year % 10) & 0x0F)); + outStream.write((((month / 10) << 4) & 0xF0) | ((month % 10) & 0x0F)); + outStream.write((((monthDay / 10) << 4) & 0xF0) | ((monthDay % 10) & 0x0F)); + outStream.write((((hour / 10) << 4) & 0xF0) | ((hour % 10) & 0x0F)); + outStream.write((((minute / 10) << 4) & 0xF0) | ((minute % 10) & 0x0F)); + outStream.write((((second / 10) << 4) & 0xF0) | ((second % 10) & 0x0F)); + return outStream.toByteArray(); + } + public long toMillis() { LocalDateTime localDateTime = LocalDateTime.of(year, month + 1, monthDay, hour, minute, second); @@ -957,6 +985,12 @@ public final class BearerData { } } + private static void encodeMsgCenterTimeStamp(BearerData bData, BitwiseOutputStream outStream) + throws BitwiseOutputStream.AccessException { + outStream.write(8, 6); + outStream.writeByteArray(8 * 6, bData.msgCenterTimeStamp.toByteArray()); + }; + /** * Create serialized representation for BearerData object. * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details) @@ -1021,6 +1055,10 @@ public final class BearerData { outStream.write(8, SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS); encodeScpResults(bData, outStream); } + if (bData.msgCenterTimeStamp != null) { + outStream.write(8, SUBPARAM_MESSAGE_CENTER_TIME_STAMP); + encodeMsgCenterTimeStamp(bData, outStream); + } return outStream.toByteArray(); } catch (BitwiseOutputStream.AccessException ex) { Rlog.e(LOG_TAG, "BearerData encode failed: " + ex); diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java index 417aafd765ea..c91ea696ec29 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java @@ -42,8 +42,11 @@ import com.android.internal.telephony.uicc.IccUtils; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.text.ParseException; +import java.time.Instant; import java.time.LocalDateTime; +import java.time.ZoneId; import java.time.ZoneOffset; +import java.time.ZonedDateTime; /** * A Short Message Service message. @@ -167,10 +170,9 @@ public class SmsMessage extends SmsMessageBase { } /** - * Create an SmsMessage from an SMS EF record. + * Creates an SmsMessage from an SMS EF record. * - * @param index Index of SMS record. This should be index in ArrayList - * returned by SmsManager.getAllMessagesFromSim + 1. + * @param index Index of SMS EF record. * @param data Record data. * @return An SmsMessage representing the record. * @@ -259,12 +261,15 @@ public class SmsMessage extends SmsMessageBase { } /** - * Get an SMS-SUBMIT PDU for a destination address and a message + * Gets an SMS-SUBMIT PDU for a destination address and a message. * - * @param scAddress Service Centre address. Null means use default. - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param scAddress Service Centre address. Null means use default. + * @param destinationAddress the address of the destination for the message. + * @param message string representation of the message payload. + * @param statusReportRequested indicates whether a report is reuested for this message. + * @param header a byte array containing the data for the User Data Header. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. * @hide */ @UnsupportedAppUsage @@ -277,17 +282,19 @@ public class SmsMessage extends SmsMessageBase { /** - * Get an SMS-SUBMIT PDU for a destination address and a message using the - * specified encoding. + * Gets an SMS-SUBMIT PDU for a destination address and a message using the specified encoding. * - * @param scAddress Service Centre address. Null means use default. - * @param encoding Encoding defined by constants in - * com.android.internal.telephony.SmsConstants.ENCODING_* + * @param scAddress Service Centre address. Null means use default. + * @param destinationAddress the address of the destination for the message. + * @param message string representation of the message payload. + * @param statusReportRequested indicates whether a report is reuested for this message. + * @param header a byte array containing the data for the User Data Header. + * @param encoding encoding defined by constants in + * com.android.internal.telephony.SmsConstants.ENCODING_* * @param languageTable * @param languageShiftTable - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. * @hide */ @UnsupportedAppUsage @@ -300,18 +307,20 @@ public class SmsMessage extends SmsMessageBase { } /** - * Get an SMS-SUBMIT PDU for a destination address and a message using the - * specified encoding. + * Gets an SMS-SUBMIT PDU for a destination address and a message using the specified encoding. * - * @param scAddress Service Centre address. Null means use default. - * @param encoding Encoding defined by constants in - * com.android.internal.telephony.SmsConstants.ENCODING_* + * @param scAddress Service Centre address. Null means use default. + * @param destinationAddress the address of the destination for the message. + * @param message string representation of the message payload. + * @param statusReportRequested indicates whether a report is reuested for this message. + * @param header a byte array containing the data for the User Data Header. + * @param encoding encoding defined by constants in + * com.android.internal.telephony.SmsConstants.ENCODING_* * @param languageTable * @param languageShiftTable * @param validityPeriod Validity Period of the message in Minutes. - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. * @hide */ @UnsupportedAppUsage @@ -483,12 +492,14 @@ public class SmsMessage extends SmsMessageBase { } /** - * Get an SMS-SUBMIT PDU for a destination address and a message + * Gets an SMS-SUBMIT PDU for a destination address and a message. * - * @param scAddress Service Centre address. Null means use default. - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param scAddress Service Centre address. Null means use default. + * @param destinationAddress the address of the destination for the message. + * @param message string representation of the message payload. + * @param statusReportRequested indicates whether a report is reuested for this message. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. */ @UnsupportedAppUsage public static SubmitPdu getSubmitPdu(String scAddress, @@ -499,15 +510,15 @@ public class SmsMessage extends SmsMessageBase { } /** - * Get an SMS-SUBMIT PDU for a destination address and a message + * Gets an SMS-SUBMIT PDU for a destination address and a message. * - * @param scAddress Service Centre address. Null means use default. - * @param destinationAddress the address of the destination for the message - * @param statusReportRequested staus report of the message Requested + * @param scAddress Service Centre address. Null means use default. + * @param destinationAddress the address of the destination for the message. + * @param message string representation of the message payload. + * @param statusReportRequested indicates whether a report is reuested for this message. * @param validityPeriod Validity Period of the message in Minutes. - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. */ @UnsupportedAppUsage public static SubmitPdu getSubmitPdu(String scAddress, @@ -518,16 +529,15 @@ public class SmsMessage extends SmsMessageBase { } /** - * Get an SMS-SUBMIT PDU for a data message to a destination address & port + * Gets an SMS-SUBMIT PDU for a data message to a destination address & port. * - * @param scAddress Service Centre address. null == use default - * @param destinationAddress the address of the destination for the message - * @param destinationPort the port to deliver the message to at the - * destination - * @param data the data for the message - * @return a <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. + * @param scAddress Service Centre address. Null means use default. + * @param destinationAddress the address of the destination for the message. + * @param destinationPort the port to deliver the message to at the destination. + * @param data the data for the message. + * @param statusReportRequested indicates whether a report is reuested for this message. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. */ public static SubmitPdu getSubmitPdu(String scAddress, String destinationAddress, int destinationPort, byte[] data, @@ -551,8 +561,7 @@ public class SmsMessage extends SmsMessageBase { SubmitPdu ret = new SubmitPdu(); ByteArrayOutputStream bo = getSubmitPduHead( - scAddress, destinationAddress, (byte) 0x41, // MTI = SMS-SUBMIT, - // TP-UDHI = true + scAddress, destinationAddress, (byte) 0x41, /* TP-MTI=SMS-SUBMIT, TP-UDHI=true */ statusReportRequested, ret); // Skip encoding pdu if error occurs when create pdu head and the error will be handled // properly later on encodedMessage sanity check. @@ -579,16 +588,18 @@ public class SmsMessage extends SmsMessageBase { } /** - * Create the beginning of a SUBMIT PDU. This is the part of the - * SUBMIT PDU that is common to the two versions of {@link #getSubmitPdu}, - * one of which takes a byte array and the other of which takes a + * Creates the beginning of a SUBMIT PDU. + * + * This is the part of the SUBMIT PDU that is common to the two versions of + * {@link #getSubmitPdu}, one of which takes a byte array and the other of which takes a * <code>String</code>. * - * @param scAddress Service Centre address. null == use default - * @param destinationAddress the address of the destination for the message + * @param scAddress Service Centre address. Null means use default. + * @param destinationAddress the address of the destination for the message. * @param mtiByte - * @param ret <code>SubmitPdu</code> containing the encoded SC - * address, if applicable, and the encoded message. Returns null on encode error. + * @param statusReportRequested indicates whether a report is reuested for this message. + * @param ret <code>SubmitPdu</code>. + * @return a byte array of the beginning of a SUBMIT PDU. Null for invalid destinationAddress. */ @UnsupportedAppUsage private static ByteArrayOutputStream getSubmitPduHead( @@ -636,6 +647,161 @@ public class SmsMessage extends SmsMessageBase { return bo; } + /** + * Gets an SMS-DELIVER PDU for an originating address and a message. + * + * @param scAddress Service Centre address. Null means use default. + * @param originatingAddress the address of the originating for the message. + * @param message string representation of the message payload. + * @param date the time stamp the message was received. + * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the + * encoded message. Returns null on encode error. + * @hide + */ + public static SubmitPdu getDeliverPdu( + String scAddress, String originatingAddress, String message, long date) { + if (originatingAddress == null || message == null) { + return null; + } + + // Find the best encoding to use + TextEncodingDetails ted = calculateLength(message, false); + int encoding = ted.codeUnitSize; + int languageTable = ted.languageTable; + int languageShiftTable = ted.languageShiftTable; + byte[] header = null; + + if (encoding == ENCODING_7BIT && (languageTable != 0 || languageShiftTable != 0)) { + SmsHeader smsHeader = new SmsHeader(); + smsHeader.languageTable = languageTable; + smsHeader.languageShiftTable = languageShiftTable; + header = SmsHeader.toByteArray(smsHeader); + } + + SubmitPdu ret = new SubmitPdu(); + + ByteArrayOutputStream bo = new ByteArrayOutputStream(MAX_USER_DATA_BYTES + 40); + + // SMSC address with length octet, or 0 + if (scAddress == null) { + ret.encodedScAddress = null; + } else { + ret.encodedScAddress = + PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength(scAddress); + } + + // TP-Message-Type-Indicator + bo.write(0); // SMS-DELIVER + + byte[] oaBytes; + + oaBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD(originatingAddress); + + // Return null for invalid originating address + if (oaBytes == null) return null; + + // Originating address length in BCD digits, ignoring TON byte and pad + // TODO Should be better. + bo.write((oaBytes.length - 1) * 2 - ((oaBytes[oaBytes.length - 1] & 0xf0) == 0xf0 ? 1 : 0)); + + // Originating Address + bo.write(oaBytes, 0, oaBytes.length); + + // TP-Protocol-Identifier + bo.write(0); + + // User Data (and length) + byte[] userData; + try { + if (encoding == ENCODING_7BIT) { + userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header, + languageTable, languageShiftTable); + } else { // Assume UCS-2 + try { + userData = encodeUCS2(message, header); + } catch (UnsupportedEncodingException uex) { + Rlog.e(LOG_TAG, "Implausible UnsupportedEncodingException ", uex); + return null; + } + } + } catch (EncodeException ex) { + if (ex.getError() == EncodeException.ERROR_EXCEED_SIZE) { + Rlog.e(LOG_TAG, "Exceed size limitation EncodeException", ex); + return null; + } else { + // Encoding to the 7-bit alphabet failed. Let's see if we can send it as a UCS-2 + // encoded message + try { + userData = encodeUCS2(message, header); + encoding = ENCODING_16BIT; + } catch (EncodeException ex1) { + Rlog.e(LOG_TAG, "Exceed size limitation EncodeException", ex1); + return null; + } catch (UnsupportedEncodingException uex) { + Rlog.e(LOG_TAG, "Implausible UnsupportedEncodingException ", uex); + return null; + } + } + } + + if (encoding == ENCODING_7BIT) { + if ((0xff & userData[0]) > MAX_USER_DATA_SEPTETS) { + // Message too long + Rlog.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " septets)"); + return null; + } + // TP-Data-Coding-Scheme + // Default encoding, uncompressed + bo.write(0x00); + } else { // Assume UCS-2 + if ((0xff & userData[0]) > MAX_USER_DATA_BYTES) { + // Message too long + Rlog.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " bytes)"); + return null; + } + // TP-Data-Coding-Scheme + // UCS-2 encoding, uncompressed + bo.write(0x08); + } + + // TP-Service-Centre-Time-Stamp + byte[] scts = new byte[7]; + + ZonedDateTime zoneDateTime = Instant.ofEpochMilli(date).atZone(ZoneId.systemDefault()); + LocalDateTime localDateTime = zoneDateTime.toLocalDateTime(); + + // It indicates the difference, expressed in quarters of an hour, between the local time and + // GMT. + int timezoneOffset = zoneDateTime.getOffset().getTotalSeconds() / 60 / 15; + boolean negativeOffset = timezoneOffset < 0; + if (negativeOffset) { + timezoneOffset = -timezoneOffset; + } + int year = localDateTime.getYear(); + int month = localDateTime.getMonthValue(); + int day = localDateTime.getDayOfMonth(); + int hour = localDateTime.getHour(); + int minute = localDateTime.getMinute(); + int second = localDateTime.getSecond(); + + year = year > 2000 ? year - 2000 : year - 1900; + scts[0] = (byte) ((((year % 10) & 0x0F) << 4) | ((year / 10) & 0x0F)); + scts[1] = (byte) ((((month % 10) & 0x0F) << 4) | ((month / 10) & 0x0F)); + scts[2] = (byte) ((((day % 10) & 0x0F) << 4) | ((day / 10) & 0x0F)); + scts[3] = (byte) ((((hour % 10) & 0x0F) << 4) | ((hour / 10) & 0x0F)); + scts[4] = (byte) ((((minute % 10) & 0x0F) << 4) | ((minute / 10) & 0x0F)); + scts[5] = (byte) ((((second % 10) & 0x0F) << 4) | ((second / 10) & 0x0F)); + scts[6] = (byte) ((((timezoneOffset % 10) & 0x0F) << 4) | ((timezoneOffset / 10) & 0x0F)); + if (negativeOffset) { + scts[0] |= 0x08; // Negative offset + } + bo.write(scts, 0, scts.length); + + bo.write(userData, 0, userData.length); + ret.encodedMessage = bo.toByteArray(); + return ret; + } + private static class PduParser { @UnsupportedAppUsage byte mPdu[]; diff --git a/test-mock/api/test-current.txt b/test-mock/api/test-current.txt index 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/Internal/src/android/service/wallpaper/WallpaperServiceTest.java b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java index 592aa3ac4a6b..153ca79e346b 100644 --- a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java +++ b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java @@ -58,4 +58,31 @@ public class WallpaperServiceTest { ambientModeChangedCount[0], 2); } + @Test + public void testDeliversZoomChanged() { + int[] zoomChangedCount = {0}; + WallpaperService service = new WallpaperService() { + @Override + public Engine onCreateEngine() { + return new Engine() { + @Override + public void onZoomChanged(float zoom) { + super.onZoomChanged(zoom); + zoomChangedCount[0]++; + } + }; + } + }; + WallpaperService.Engine engine = service.onCreateEngine(); + engine.setCreated(true); + + engine.setZoom(.5f); + assertEquals("engine scale was not updated", .5f, engine.getZoom(), .001f); + assertEquals("onZoomChanged should have been called", 1, zoomChangedCount[0]); + + engine.setZoom(0); + assertEquals("engine scale was not updated", 0, engine.getZoom(), .001f); + assertEquals("onAmbientModeChanged should have been called", 2, zoomChangedCount[0]); + } + } diff --git a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java index 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/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java index 6005cc375d5c..f25fd4daf829 100644 --- a/tests/net/common/java/android/net/LinkPropertiesTest.java +++ b/tests/net/common/java/android/net/LinkPropertiesTest.java @@ -75,6 +75,9 @@ public class LinkPropertiesTest { private static final LinkAddress LINKADDRV4 = new LinkAddress(ADDRV4, 32); private static final LinkAddress LINKADDRV6 = new LinkAddress(ADDRV6, 128); private static final LinkAddress LINKADDRV6LINKLOCAL = new LinkAddress("fe80::1/64"); + private static final Uri CAPPORT_API_URL = Uri.parse("https://test.example.com/capportapi"); + private static final CaptivePortalData CAPPORT_DATA = new CaptivePortalData.Builder() + .setVenueInfoUrl(Uri.parse("https://test.example.com/venue")).build(); private static InetAddress address(String addrString) { return InetAddresses.parseNumericAddress(addrString); @@ -101,6 +104,8 @@ public class LinkPropertiesTest { assertFalse(lp.isIpv6Provisioned()); assertFalse(lp.isPrivateDnsActive()); assertFalse(lp.isWakeOnLanSupported()); + assertNull(lp.getCaptivePortalApiUrl()); + assertNull(lp.getCaptivePortalData()); } private LinkProperties makeTestObject() { @@ -124,6 +129,8 @@ public class LinkPropertiesTest { lp.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96")); lp.setDhcpServerAddress(DHCPSERVER); lp.setWakeOnLanSupported(true); + lp.setCaptivePortalApiUrl(CAPPORT_API_URL); + lp.setCaptivePortalData(CAPPORT_DATA); return lp; } @@ -165,6 +172,12 @@ public class LinkPropertiesTest { assertTrue(source.isIdenticalWakeOnLan(target)); assertTrue(target.isIdenticalWakeOnLan(source)); + assertTrue(source.isIdenticalCaptivePortalApiUrl(target)); + assertTrue(target.isIdenticalCaptivePortalApiUrl(source)); + + assertTrue(source.isIdenticalCaptivePortalData(target)); + assertTrue(target.isIdenticalCaptivePortalData(source)); + // Check result of equals(). assertTrue(source.equals(target)); assertTrue(target.equals(source)); @@ -963,6 +976,8 @@ public class LinkPropertiesTest { source.setNat64Prefix(new IpPrefix("2001:db8:1:2:64:64::/96")); source.setWakeOnLanSupported(true); + source.setCaptivePortalApiUrl(CAPPORT_API_URL); + source.setCaptivePortalData(CAPPORT_DATA); source.setDhcpServerAddress((Inet4Address) GATEWAY1); @@ -970,7 +985,13 @@ public class LinkPropertiesTest { stacked.setInterfaceName("test-stacked"); source.addStackedLink(stacked); - assertParcelSane(source, 16 /* fieldCount */); + assertParcelSane(source.makeSensitiveFieldsParcelingCopy(), 18 /* fieldCount */); + + // Verify that without using a sensitiveFieldsParcelingCopy, sensitive fields are cleared. + final LinkProperties sanitized = new LinkProperties(source); + sanitized.setCaptivePortalApiUrl(null); + sanitized.setCaptivePortalData(null); + assertEquals(sanitized, parcelingRoundTrip(source)); } @Test @@ -1113,4 +1134,22 @@ public class LinkPropertiesTest { lp.clear(); assertFalse(lp.isWakeOnLanSupported()); } + + @Test + public void testCaptivePortalApiUrl() { + final LinkProperties lp = makeTestObject(); + assertEquals(CAPPORT_API_URL, lp.getCaptivePortalApiUrl()); + + lp.clear(); + assertNull(lp.getCaptivePortalApiUrl()); + } + + @Test + public void testCaptivePortalData() { + final LinkProperties lp = makeTestObject(); + assertEquals(CAPPORT_DATA, lp.getCaptivePortalData()); + + lp.clear(); + assertNull(lp.getCaptivePortalData()); + } } diff --git a/tests/net/common/java/android/net/RouteInfoTest.java b/tests/net/common/java/android/net/RouteInfoTest.java index 5ce84363082f..fe51b3af4d72 100644 --- a/tests/net/common/java/android/net/RouteInfoTest.java +++ b/tests/net/common/java/android/net/RouteInfoTest.java @@ -258,6 +258,16 @@ public class RouteInfoTest extends TestCase { assertParcelingIsLossless(r); r = new RouteInfo(Prefix("192.0.2.0/24"), null, "wlan0"); - assertParcelSane(r, 6); + assertParcelSane(r, 7); + } + + public void testMtu() { + RouteInfo r; + r = new RouteInfo(Prefix("0.0.0.0/0"), Address("0.0.0.0"), "wlan0", + RouteInfo.RTN_UNICAST, 1500); + assertEquals(1500, r.getMtu()); + + r = new RouteInfo(Prefix("0.0.0.0/0"), Address("0.0.0.0"), "wlan0"); + assertEquals(0, r.getMtu()); } } diff --git a/tests/net/java/android/net/CaptivePortalDataTest.kt b/tests/net/java/android/net/CaptivePortalDataTest.kt new file mode 100644 index 000000000000..00714382684f --- /dev/null +++ b/tests/net/java/android/net/CaptivePortalDataTest.kt @@ -0,0 +1,72 @@ +/* + * 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.net + +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.testutils.assertParcelSane +import com.android.testutils.assertParcelingIsLossless +import org.junit.Test +import org.junit.runner.RunWith +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals + +@SmallTest +@RunWith(AndroidJUnit4::class) +class CaptivePortalDataTest { + private val data = CaptivePortalData.Builder() + .setRefreshTime(123L) + .setUserPortalUrl(Uri.parse("https://portal.example.com/test")) + .setVenueInfoUrl(Uri.parse("https://venue.example.com/test")) + .setSessionExtendable(true) + .setBytesRemaining(456L) + .setExpiryTime(789L) + .setCaptive(true) + .build() + + private fun makeBuilder() = CaptivePortalData.Builder(data) + + @Test + fun testParcelUnparcel() { + assertParcelSane(data, fieldCount = 7) + + assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build()) + assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build()) + } + + @Test + fun testEquals() { + assertEquals(data, makeBuilder().build()) + + assertNotEqualsAfterChange { it.setRefreshTime(456L) } + assertNotEqualsAfterChange { it.setUserPortalUrl(Uri.parse("https://example.com/")) } + assertNotEqualsAfterChange { it.setUserPortalUrl(null) } + assertNotEqualsAfterChange { it.setVenueInfoUrl(Uri.parse("https://example.com/")) } + assertNotEqualsAfterChange { it.setVenueInfoUrl(null) } + assertNotEqualsAfterChange { it.setSessionExtendable(false) } + assertNotEqualsAfterChange { it.setBytesRemaining(789L) } + assertNotEqualsAfterChange { it.setExpiryTime(12L) } + assertNotEqualsAfterChange { it.setCaptive(false) } + } + + private fun CaptivePortalData.mutate(mutator: (CaptivePortalData.Builder) -> Unit) = + CaptivePortalData.Builder(this).apply { mutator(this) }.build() + + private fun assertNotEqualsAfterChange(mutator: (CaptivePortalData.Builder) -> Unit) { + assertNotEquals(data, data.mutate(mutator)) + } +}
\ No newline at end of file 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/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 4e2933472706..09cc69e83f41 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -21,6 +21,8 @@ import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.content.pm.PackageManager.MATCH_ANY_USER; +import static android.content.pm.PackageManager.PERMISSION_DENIED; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_SUPL; @@ -114,6 +116,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import android.Manifest; import android.annotation.NonNull; import android.app.AlarmManager; import android.app.NotificationManager; @@ -129,6 +132,7 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.content.res.Resources; +import android.net.CaptivePortalData; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.ConnectivityManager.PacketKeepalive; @@ -165,6 +169,7 @@ import android.net.ResolverParamsParcel; import android.net.RouteInfo; import android.net.SocketKeepalive; import android.net.UidRange; +import android.net.Uri; import android.net.metrics.IpConnectivityLog; import android.net.shared.NetworkMonitorUtils; import android.net.shared.PrivateDnsConfig; @@ -243,8 +248,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; @@ -347,6 +354,8 @@ public class ConnectivityServiceTest { @Spy private Resources mResources; private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>(); + // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant + private final HashMap<String, Integer> mMockedPermissions = new HashMap<>(); MockContext(Context base, ContentProvider settingsProvider) { super(base); @@ -417,13 +426,39 @@ public class ConnectivityServiceTest { } @Override + public int checkPermission(String permission, int pid, int uid) { + final Integer granted = mMockedPermissions.get(permission); + if (granted == null) { + // All non-mocked permissions should be held by the test or unnecessary: check as + // normal to make sure the code does not rely on unexpected permissions. + return super.checkPermission(permission, pid, uid); + } + return granted; + } + + @Override public void enforceCallingOrSelfPermission(String permission, String message) { - // The mainline permission can only be held if signed with the network stack certificate - // Skip testing for this permission. - if (NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK.equals(permission)) return; - // All other permissions should be held by the test or unnecessary: check as normal to - // make sure the code does not rely on unexpected permissions. - super.enforceCallingOrSelfPermission(permission, message); + final Integer granted = mMockedPermissions.get(permission); + if (granted == null) { + super.enforceCallingOrSelfPermission(permission, message); + return; + } + + if (!granted.equals(PERMISSION_GRANTED)) { + throw new SecurityException("[Test] permission denied: " + permission); + } + } + + /** + * Mock checks for the specified permission, and have them behave as per {@code granted}. + * + * <p>Passing null reverts to default behavior, which does a real permission check on the + * test package. + * @param granted One of {@link PackageManager#PERMISSION_GRANTED} or + * {@link PackageManager#PERMISSION_DENIED}. + */ + public void setPermission(String permission, Integer granted) { + mMockedPermissions.put(permission, granted); } @Override @@ -1750,6 +1785,66 @@ public class ConnectivityServiceTest { assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); } + private void doNetworkCallbacksSanitizationTest(boolean sanitized) throws Exception { + final TestNetworkCallback callback = new TestNetworkCallback(); + final TestNetworkCallback defaultCallback = new TestNetworkCallback(); + final NetworkRequest wifiRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI).build(); + mCm.registerNetworkCallback(wifiRequest, callback); + mCm.registerDefaultNetworkCallback(defaultCallback); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + + final LinkProperties newLp = new LinkProperties(); + final Uri capportUrl = Uri.parse("https://capport.example.com/api"); + final CaptivePortalData capportData = new CaptivePortalData.Builder() + .setCaptive(true).build(); + newLp.setCaptivePortalApiUrl(capportUrl); + newLp.setCaptivePortalData(capportData); + mWiFiNetworkAgent.sendLinkProperties(newLp); + + final Uri expectedCapportUrl = sanitized ? null : capportUrl; + final CaptivePortalData expectedCapportData = sanitized ? null : capportData; + callback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp -> + Objects.equals(expectedCapportUrl, lp.getCaptivePortalApiUrl()) + && Objects.equals(expectedCapportData, lp.getCaptivePortalData())); + defaultCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp -> + Objects.equals(expectedCapportUrl, lp.getCaptivePortalApiUrl()) + && Objects.equals(expectedCapportData, lp.getCaptivePortalData())); + + final LinkProperties lp = mCm.getLinkProperties(mWiFiNetworkAgent.getNetwork()); + assertEquals(expectedCapportUrl, lp.getCaptivePortalApiUrl()); + assertEquals(expectedCapportData, lp.getCaptivePortalData()); + } + + @Test + public void networkCallbacksSanitizationTest_Sanitize() throws Exception { + mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + PERMISSION_DENIED); + mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, + PERMISSION_DENIED); + doNetworkCallbacksSanitizationTest(true /* sanitized */); + } + + @Test + public void networkCallbacksSanitizationTest_NoSanitize_NetworkStack() throws Exception { + mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + PERMISSION_GRANTED); + mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, PERMISSION_DENIED); + doNetworkCallbacksSanitizationTest(false /* sanitized */); + } + + @Test + public void networkCallbacksSanitizationTest_NoSanitize_Settings() throws Exception { + mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + PERMISSION_DENIED); + mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED); + doNetworkCallbacksSanitizationTest(false /* sanitized */); + } + @Test public void testMultipleLingering() throws Exception { // This test would be flaky with the default 120ms timer: that is short enough that @@ -2628,6 +2723,8 @@ public class ConnectivityServiceTest { final String testKey = "testkey"; final String testValue = "testvalue"; testBundle.putString(testKey, testValue); + mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + PERMISSION_GRANTED); mCm.startCaptivePortalApp(wifiNetwork, testBundle); final Intent signInIntent = mServiceContext.expectStartActivityIntent(TIMEOUT_MS); assertEquals(ACTION_CAPTIVE_PORTAL_SIGN_IN, signInIntent.getAction()); diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java index 8eac3ea13a23..fe0224a27c80 100644 --- a/tests/testables/src/android/testing/TestableLooper.java +++ b/tests/testables/src/android/testing/TestableLooper.java @@ -234,7 +234,9 @@ public class TestableLooper { try { mLooper = setAsMain ? Looper.getMainLooper() : createLooper(); mTestableLooper = new TestableLooper(mLooper, false); - mTestableLooper.getLooper().getThread().setName(test.getClass().getName()); + if (!setAsMain) { + mTestableLooper.getLooper().getThread().setName(test.getClass().getName()); + } } catch (Exception e) { throw new RuntimeException(e); } diff --git a/wifi/Android.bp b/wifi/Android.bp index 8fc8af980f9e..dae04c6c3a25 100644 --- a/wifi/Android.bp +++ b/wifi/Android.bp @@ -91,6 +91,8 @@ java_library { srcs: [ ":framework-wifi-updatable-sources", ], + // java_api_finder must accompany `srcs` + plugins: ["java_api_finder"], installable: false, visibility: [ "//frameworks/opt/net/wifi/service", @@ -128,7 +130,6 @@ java_library { "com.android.wifi", "test_com.android.wifi", ], - plugins: ["java_api_finder"], } droidstubs { diff --git a/wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl b/wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl index 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 0b5969a8999d..558de7c82d92 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,6 +89,8 @@ interface IWifiManager boolean disableNetwork(int netId, String packageName); + void allowAutojoinGlobal(boolean choice); + void allowAutojoin(int netId, boolean choice); void allowAutojoinPasspoint(String fqdn, boolean enableAutoJoin); @@ -96,7 +99,7 @@ interface IWifiManager boolean startScan(String packageName, String featureId); - List<android.net.wifi.ScanResult> getScanResults(String callingPackage, String callingFeatureId); + List<ScanResult> getScanResults(String callingPackage, String callingFeatureId); boolean disconnect(String packageName); @@ -179,8 +182,6 @@ interface IWifiManager int getVerboseLoggingLevel(); - void enableWifiConnectivityManager(boolean enabled); - void disableEphemeralNetwork(String SSID, String packageName); void factoryReset(String packageName); @@ -255,9 +256,14 @@ 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); void clearWifiConnectedNetworkScorer(); + + /** + * Return the Map of {@link WifiNetworkSuggestion} and the list of <ScanResult> + */ + Map getMatchingScanResults(in List<WifiNetworkSuggestion> networkSuggestions, in List<ScanResult> scanResults, String callingPackage, String callingFeatureId); } diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index e2befeb29004..114e0fa21296 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -1662,7 +1662,9 @@ public class WifiConfiguration implements Parcelable { */ @NonNull public NetworkSelectionStatus build() { - return mNetworkSelectionStatus; + NetworkSelectionStatus status = new NetworkSelectionStatus(); + status.copy(mNetworkSelectionStatus); + return status; } } diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index 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/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java index f2a875b43538..419bcb107013 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -435,7 +435,7 @@ public class WifiInfo implements Parcelable { */ @NonNull public WifiInfo build() { - return mWifiInfo; + return new WifiInfo(mWifiInfo); } } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 208ce9c37b23..0e8c6ed8774e 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; @@ -2353,7 +2354,7 @@ public class WifiManager { return (getSupportedFeatures() & feature) == feature; } - /** + /** * @return true if this adapter supports Passpoint * @hide */ @@ -2685,6 +2686,34 @@ public class WifiManager { } /** + * Return the filtered ScanResults which may be authenticated by the suggested network + * configurations. + * @param networkSuggestions The list of {@link WifiNetworkSuggestion} + * @param scanResults The scan results to be filtered, this is optional, if it is null or + * empty, wifi system would use the recent scan results in the system. + * @return The map of {@link WifiNetworkSuggestion} and the list of {@link ScanResult} which + * may be authenticated by the corresponding network configuration. + * @hide + */ + @SystemApi + @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE}) + @NonNull + public Map<WifiNetworkSuggestion, List<ScanResult>> getMatchingScanResults( + @NonNull List<WifiNetworkSuggestion> networkSuggestions, + @Nullable List<ScanResult> scanResults) { + if (networkSuggestions == null) { + throw new IllegalArgumentException("networkSuggestions must not be null."); + } + try { + return mService.getMatchingScanResults( + networkSuggestions, scanResults, + mContext.getOpPackageName(), mContext.getFeatureId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Check if scanning is always available. * * If this return {@code true}, apps can issue {@link #startScan} and fetch scan results @@ -2877,6 +2906,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); @@ -2889,6 +2919,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); } @@ -4271,6 +4302,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. @@ -4897,18 +4945,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[])}. 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/android/net/wifi/rtt/ResponderLocation.java b/wifi/java/android/net/wifi/rtt/ResponderLocation.java index 970a75d7c418..218b2dcae71d 100644 --- a/wifi/java/android/net/wifi/rtt/ResponderLocation.java +++ b/wifi/java/android/net/wifi/rtt/ResponderLocation.java @@ -21,6 +21,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.location.Address; import android.location.Location; @@ -1367,7 +1368,8 @@ public final class ResponderLocation implements Parcelable { * */ @Nullable - public SparseArray toCivicLocationSparseArray() { + @SuppressLint("ChangedType") + public SparseArray<String> toCivicLocationSparseArray() { if (mCivicLocation != null && mCivicLocation.isValid()) { return mCivicLocation.toSparseArray(); } else { diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java index 56fa6e23a852..080c6c772f63 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(); } @@ -404,7 +409,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(); } @@ -617,4 +623,12 @@ public class BaseWifiService extends IWifiManager.Stub { public void clearWifiConnectedNetworkScorer() { throw new UnsupportedOperationException(); } + + @Override + public Map<WifiNetworkSuggestion, List<ScanResult>> getMatchingScanResults( + List<WifiNetworkSuggestion> networkSuggestions, + List<ScanResult> scanResults, + String callingPackage, String callingFeatureId) { + throw new UnsupportedOperationException(); + } } diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java index 0ef75aa3eb5a..8023160a811e 100644 --- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; import android.net.MacAddress; @@ -386,4 +387,43 @@ public class WifiConfigurationTest { .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256)); assertTrue(config.requirePMF); } + + /** + * Test that the NetworkSelectionStatus Builder returns the same values that was set, and that + * calling build multiple times returns different instances. + */ + @Test + public void testNetworkSelectionStatusBuilder() throws Exception { + NetworkSelectionStatus.Builder builder = new NetworkSelectionStatus.Builder() + .setNetworkSelectionDisableReason( + NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION) + .setNetworkSelectionStatus( + NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED); + + NetworkSelectionStatus status1 = builder.build(); + + assertEquals(NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION, + status1.getNetworkSelectionDisableReason()); + assertEquals(NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED, + status1.getNetworkSelectionStatus()); + + NetworkSelectionStatus status2 = builder + .setNetworkSelectionDisableReason(NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD) + .build(); + + // different instances + assertNotSame(status1, status2); + + // assert that status1 didn't change + assertEquals(NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION, + status1.getNetworkSelectionDisableReason()); + assertEquals(NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED, + status1.getNetworkSelectionStatus()); + + // assert that status2 changed + assertEquals(NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD, + status2.getNetworkSelectionDisableReason()); + assertEquals(NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED, + status2.getNetworkSelectionStatus()); + } } diff --git a/wifi/tests/src/android/net/wifi/WifiInfoTest.java b/wifi/tests/src/android/net/wifi/WifiInfoTest.java index af85ce05f23b..04759ac21bba 100644 --- a/wifi/tests/src/android/net/wifi/WifiInfoTest.java +++ b/wifi/tests/src/android/net/wifi/WifiInfoTest.java @@ -18,6 +18,7 @@ package android.net.wifi; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; import android.os.Parcel; @@ -26,6 +27,8 @@ import androidx.test.filters.SmallTest; import org.junit.Test; +import java.nio.charset.StandardCharsets; + /** * Unit tests for {@link android.net.wifi.WifiInfo}. */ @@ -41,6 +44,11 @@ public class WifiInfoTest { private static final int TEST_WIFI_STANDARD = ScanResult.WIFI_STANDARD_11AC; private static final int TEST_MAX_SUPPORTED_TX_LINK_SPEED_MBPS = 866; private static final int TEST_MAX_SUPPORTED_RX_LINK_SPEED_MBPS = 1200; + private static final String TEST_SSID = "Test123"; + private static final String TEST_BSSID = "12:12:12:12:12:12"; + private static final int TEST_RSSI = -60; + private static final int TEST_NETWORK_ID = 5; + private static final int TEST_NETWORK_ID2 = 6; /** * Verify parcel write/read with WifiInfo. @@ -101,4 +109,43 @@ public class WifiInfoTest { assertEquals(null, wifiInfo.getBSSID()); assertEquals(-1, wifiInfo.getNetworkId()); } + + /** + * Test that the WifiInfo Builder returns the same values that was set, and that + * calling build multiple times returns different instances. + */ + @Test + public void testWifiInfoBuilder() throws Exception { + WifiInfo.Builder builder = new WifiInfo.Builder() + .setSsid(TEST_SSID.getBytes(StandardCharsets.UTF_8)) + .setBssid(TEST_BSSID) + .setRssi(TEST_RSSI) + .setNetworkId(TEST_NETWORK_ID); + + WifiInfo info1 = builder.build(); + + assertEquals("\"" + TEST_SSID + "\"", info1.getSSID()); + assertEquals(TEST_BSSID, info1.getBSSID()); + assertEquals(TEST_RSSI, info1.getRssi()); + assertEquals(TEST_NETWORK_ID, info1.getNetworkId()); + + WifiInfo info2 = builder + .setNetworkId(TEST_NETWORK_ID2) + .build(); + + // different instances + assertNotSame(info1, info2); + + // assert that info1 didn't change + assertEquals("\"" + TEST_SSID + "\"", info1.getSSID()); + assertEquals(TEST_BSSID, info1.getBSSID()); + assertEquals(TEST_RSSI, info1.getRssi()); + assertEquals(TEST_NETWORK_ID, info1.getNetworkId()); + + // assert that info2 changed + assertEquals("\"" + TEST_SSID + "\"", info2.getSSID()); + assertEquals(TEST_BSSID, info2.getBSSID()); + assertEquals(TEST_RSSI, info2.getRssi()); + assertEquals(TEST_NETWORK_ID2, info2.getNetworkId()); + } } diff --git a/wifi/tests/src/android/net/wifi/rtt/ResponderLocationTest.java b/wifi/tests/src/android/net/wifi/rtt/ResponderLocationTest.java index b02eebbe9a01..271339cecf1e 100644 --- a/wifi/tests/src/android/net/wifi/rtt/ResponderLocationTest.java +++ b/wifi/tests/src/android/net/wifi/rtt/ResponderLocationTest.java @@ -20,6 +20,7 @@ import android.location.Address; import android.location.Location; import android.net.MacAddress; import android.os.Parcel; +import android.util.SparseArray; import android.webkit.MimeTypeMap; import static junit.framework.Assert.assertEquals; @@ -505,6 +506,30 @@ public class ResponderLocationTest { } /** + * Test that a Civic Location sparseArray can be extracted from a valid lcr buffer. + */ + @Test + public void testLcrTestCivicLocationSparseArray() { + byte[] testLciBuffer = concatenateArrays(sTestLciIeHeader, sTestLciSE); + byte[] testLcrBuffer = + concatenateArrays(sTestLcrBufferHeader, sTestCivicLocationSEWithAddress); + ResponderLocation responderLocation = new ResponderLocation(testLciBuffer, testLcrBuffer); + + boolean valid = responderLocation.isValid(); + SparseArray<String> civicLocationSparseArray = responderLocation + .toCivicLocationSparseArray(); + + assertTrue(valid); + assertEquals("15", civicLocationSparseArray.get(CivicLocationKeys.HNO)); + assertEquals("Alto", + civicLocationSparseArray.get(CivicLocationKeys.PRIMARY_ROAD_NAME)); + assertEquals("Road", + civicLocationSparseArray.get(CivicLocationKeys.STREET_NAME_POST_MODIFIER)); + assertEquals("Mtn View", civicLocationSparseArray.get(CivicLocationKeys.CITY)); + assertEquals("94043", civicLocationSparseArray.get(CivicLocationKeys.POSTAL_CODE)); + } + + /** * Test that a URL can be extracted from a valid lcr buffer with a map image subelement. */ @Test |