Merge "Fix sizes of audio recording indicator"
diff --git a/Android.bp b/Android.bp
index eb9928c..0e3d374 100644
--- a/Android.bp
+++ b/Android.bp
@@ -221,7 +221,6 @@
":framework-sax-sources",
":framework-telecomm-sources",
":framework-telephony-common-sources",
- ":framework-telephony-sources",
":framework-wifi-annotations",
":framework-wifi-non-updatable-sources",
":PacProcessor-aidl-sources",
@@ -256,6 +255,9 @@
// etc.
":framework-javastream-protos",
":framework-statslog-gen",
+
+ // telephony annotations
+ ":framework-telephony-annotations",
],
}
@@ -268,7 +270,9 @@
":framework-tethering-srcs",
":updatable-media-srcs",
":framework-mediaprovider-sources",
+ ":framework-permission-sources",
":framework-wifi-updatable-sources",
+ ":framework-telephony-sources",
":ike-srcs",
]
}
@@ -302,7 +306,6 @@
"rs/java",
"sax/java",
"telecomm/java",
- "telephony/java",
"wifi/java",
],
},
@@ -382,6 +385,7 @@
"updatable_media_stubs",
"framework_mediaprovider_stubs",
"framework-tethering",
+ "framework-telephony-stubs",
],
jarjar_rules: ":framework-jarjar-rules",
@@ -438,8 +442,9 @@
srcs: [":framework-non-updatable-sources"],
libs: [
"framework-appsearch-stubs",
- // TODO(b/146167933): Use framework-statsd-stubs
- "framework-statsd",
+ "framework-sdkextensions-stubs-systemapi",
+ "framework-statsd", // TODO(b/146167933): Use framework-statsd-stubs
+ "framework-permission-stubs",
"framework-wifi-stubs",
"ike-stubs",
],
@@ -465,7 +470,9 @@
"//frameworks/base/apex/appsearch/framework",
"//frameworks/base/apex/blobstore/framework",
"//frameworks/base/apex/jobscheduler/framework",
+ "//frameworks/base/apex/permission/framework",
"//frameworks/base/apex/statsd/service",
+ "//frameworks/base/telephony",
"//frameworks/base/wifi",
"//frameworks/opt/net/wifi/service",
],
@@ -489,6 +496,7 @@
"updatable_media_stubs",
"framework_mediaprovider_stubs",
"framework-appsearch", // TODO(b/146218515): should be framework-appsearch-stubs
+ "framework-permission-stubs",
"framework-sdkextensions-stubs-systemapi",
// TODO(b/146167933): Use framework-statsd-stubs instead.
"framework-statsd",
@@ -497,6 +505,9 @@
"ike-stubs",
// TODO(b/147200698): should be the stub of framework-tethering
"framework-tethering",
+ // TODO (b/147688669) should be framework-telephony-stubs
+ "framework-telephony",
+ // TODO(jiyong): add stubs for APEXes here
],
sdk_version: "core_platform",
apex_available: ["//apex_available:platform"],
@@ -1178,6 +1189,7 @@
"core/java/com/android/internal/util/Protocol.java",
"core/java/com/android/internal/util/Preconditions.java",
"telephony/java/android/telephony/Annotation.java",
+ ":net-utils-framework-wifi-common-srcs",
],
libs: [
"framework-annotations-lib",
@@ -1222,3 +1234,82 @@
"StubLibraries.bp",
"ApiDocs.bp",
]
+
+// TODO(b/147699819): move to frameworks/base/telephony/ folder
+droidstubs {
+ name: "framework-telephony-stubs-srcs",
+ srcs: [
+ ":framework-telephony-sources",
+ ":framework_native_aidl",
+ ":framework-javastream-protos",
+ ],
+ aidl: {
+ local_include_dirs: [
+ "core/java",
+ "telecomm/java"
+ ],
+ },
+ libs: [
+ "framework-annotations-lib",
+ "android.hardware.radio-V1.5-java",
+ ],
+ defaults: ["framework-module-stubs-defaults-systemapi"],
+ filter_packages: ["android.telephony"],
+ sdk_version: "system_current",
+}
+
+java_library {
+ name: "framework-telephony-stubs",
+ srcs: [":framework-telephony-stubs-srcs"],
+ // TODO(b/147699819): move public aidls to a separate folder and potentially remove
+ // below aidl exports.
+ aidl: {
+ export_include_dirs: ["telephony/java"],
+ },
+ sdk_version: "system_current",
+}
+
+java_library {
+ name: "framework-telephony",
+ srcs: [
+ ":framework-telephony-sources",
+ ],
+ // TODO: change to framework-system-stub to build against system APIs.
+ libs: [
+ "framework-minus-apex",
+ "unsupportedappusage",
+ ],
+ static_libs: [
+ "libphonenumber-platform",
+ "app-compat-annotations",
+ ],
+ sdk_version: "core_platform",
+ aidl: {
+ export_include_dirs: ["telephony/java"],
+ include_dirs: [
+ "frameworks/native/aidl/binder",
+ "frameworks/native/aidl/gui",
+ ]
+ },
+ jarjar_rules: ":telephony-framework-jarjar-rules",
+ dxflags: [
+ "--core-library",
+ "--multi-dex",
+ ],
+ // This is to break the dependency from boot jars.
+ dex_preopt: {
+ enabled: false,
+ },
+ installable: true,
+}
+
+filegroup {
+ // TODO (b/147690217): move to frameworks/base/telephony/common.
+ name: "framework-telephony-annotations",
+ srcs: ["telephony/java/android/telephony/Annotation.java"],
+}
+
+filegroup {
+ name: "telephony-framework-jarjar-rules",
+ srcs: ["telephony/framework-telephony-jarjar-rules.txt"],
+}
diff --git a/StubLibraries.bp b/StubLibraries.bp
index baa3c61..cdc0d32 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -51,6 +51,10 @@
":core_public_api_files",
":ike-api-srcs",
],
+ // TODO(b/147699819): remove below aidl includes.
+ aidl: {
+ local_include_dirs: ["telephony/java"],
+ },
libs: ["framework-internal-utils"],
installable: false,
annotations_enabled: true,
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index 83195dc..0aa685d 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -25,9 +25,12 @@
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.protobuf.InvalidProtocolBufferException;
import java.util.List;
import java.util.concurrent.Executor;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
@@ -127,4 +130,94 @@
// TODO(b/147614371) Fix error report for multiple documents.
future.whenCompleteAsync((noop, err) -> callback.accept(err), executor);
}
+
+ /**
+ * This method searches for documents based on a given query string. It also accepts
+ * specifications regarding how to search and format the results.
+ *
+ *<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
+ * ‘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
+ * ‘cat’”).
+ * Example: dog OR puppy
+ * <li>Exclusion
+ * 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.
+ * “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.
+ * “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
+ * fields, such as schema_type. Clients should be able to limit their query to documents of
+ * a certain schema_type (e.g. “match documents that are of the ‘Email’ schema_type”).
+ * Example: { schema_type_filters: “Email”, “Video”,query: “dog” } will match documents
+ * that contain the query term ‘dog’ and are of either the ‘Email’ schema type or the
+ * ‘Video’ schema type.
+ * </ul>
+ *
+ * <p> It is strongly recommended to use Jetpack APIs.
+ *
+ * @param queryExpression Query String to search.
+ * @param searchSpec Spec for setting filters, raw query etc.
+ * @param executor Executor on which to invoke the callback.
+ * @param callback Callback to receive errors resulting from the query operation. If the
+ * operation succeeds, the callback will be invoked with {@code null}.
+ * @hide
+ */
+ @NonNull
+ public void query(
+ @NonNull String queryExpression,
+ @NonNull SearchSpec searchSpec,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull BiConsumer<? super SearchResults, ? super Throwable> callback) {
+ AndroidFuture<byte[]> future = new AndroidFuture<>();
+ future.whenCompleteAsync((searchResultBytes, err) -> {
+ if (err != null) {
+ callback.accept(null, err);
+ return;
+ }
+
+ if (searchResultBytes != null) {
+ SearchResultProto searchResultProto;
+ try {
+ searchResultProto = SearchResultProto.parseFrom(searchResultBytes);
+ } catch (InvalidProtocolBufferException e) {
+ callback.accept(null, e);
+ return;
+ }
+ if (searchResultProto.hasError()) {
+ // TODO(sidchhabra): Add better exception handling.
+ callback.accept(
+ null,
+ new RuntimeException(searchResultProto.getError().getErrorMessage()));
+ return;
+ }
+ SearchResults searchResults = new SearchResults(searchResultProto);
+ callback.accept(searchResults, null);
+ return;
+ }
+
+ // Nothing was supplied in the future at all
+ callback.accept(
+ null, new IllegalStateException("Unknown failure occurred while querying"));
+ }, executor);
+
+ try {
+ mService.query(queryExpression, searchSpec.getProto().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 fc83d8c..22250f4 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright 2019, The Android Open Source Project
+ * Copyright 2020, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,4 +29,13 @@
*/
void setSchema(in byte[] schemaProto, in AndroidFuture callback);
void put(in byte[] documentBytes, in AndroidFuture callback);
+ /**
+ * Searches a document based on a given query string.
+ *
+ * @param queryExpression Query String to search.
+ * @param searchSpec Serialized SearchSpecProto.
+ * @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);
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IllegalSearchSpecException.java b/apex/appsearch/framework/java/android/app/appsearch/IllegalSearchSpecException.java
new file mode 100644
index 0000000..0d029f0
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/IllegalSearchSpecException.java
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+/**
+ * Indicates that a {@link android.app.appsearch.SearchResults} has logical inconsistencies such
+ * as unpopulated mandatory fields or illegal combinations of parameters.
+ *
+ * @hide
+ */
+public class IllegalSearchSpecException extends IllegalArgumentException {
+ /**
+ * Constructs a new {@link IllegalSearchSpecException}.
+ *
+ * @param message A developer-readable description of the issue with the bundle.
+ */
+ public IllegalSearchSpecException(@NonNull String message) {
+ super(message);
+ }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
new file mode 100644
index 0000000..ec4258d
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
@@ -0,0 +1,89 @@
+/*
+ * 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 com.google.android.icing.proto.SearchResultProto;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * 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.
+ * @hide
+ */
+public final class SearchResults {
+
+ private final SearchResultProto mSearchResultProto;
+
+ /** @hide */
+ public SearchResults(SearchResultProto searchResultProto) {
+ mSearchResultProto = searchResultProto;
+ }
+
+ /**
+ * This class represents the result obtained from the query. It will contain the document which
+ * which matched the specified query string and specifications.
+ * @hide
+ */
+ public static final class Result {
+ private final SearchResultProto.ResultProto mResultProto;
+
+ private Result(SearchResultProto.ResultProto resultProto) {
+ mResultProto = resultProto;
+ }
+
+ /**
+ * Contains the matching {@link AppSearch.Document}.
+ * @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())
+ .setCreationTimestampSecs(mResultProto.getDocument().getCreationTimestampSecs())
+ .setScore(mResultProto.getDocument().getScore())
+ .build();
+ }
+
+ // TODO(sidchhabra): Add Getter for ResultReader for Snippet.
+ }
+
+ @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
new file mode 100644
index 0000000..5df7108
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java
@@ -0,0 +1,127 @@
+/*
+ * 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.IntDef;
+import android.annotation.NonNull;
+
+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 SearchSpec(SearchSpecProto searchSpecProto) {
+ mSearchSpecProto = searchSpecProto;
+ }
+
+ /** Creates a new {@link SearchSpec.Builder}. */
+ @NonNull
+ public static SearchSpec.Builder newBuilder() {
+ return new SearchSpec.Builder();
+ }
+
+ /** @hide */
+ @NonNull
+ SearchSpecProto getProto() {
+ return mSearchSpecProto;
+ }
+
+ /** 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}
+ @IntDef(prefix = {"TERM_MATCH_TYPE_"}, value = {
+ TERM_MATCH_TYPE_EXACT_ONLY,
+ TERM_MATCH_TYPE_PREFIX
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TermMatchTypeCode {}
+
+ public static final int TERM_MATCH_TYPE_EXACT_ONLY = 1;
+ public static final int TERM_MATCH_TYPE_PREFIX = 2;
+
+ /** Builder for {@link SearchSpec objects}. */
+ public static final class Builder {
+
+ private final SearchSpecProto.Builder mBuilder = SearchSpecProto.newBuilder();
+
+ 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);
+ }
+ mBuilder.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.
+ */
+ @NonNull
+ public Builder setSchemaTypes(@NonNull String... schemaTypes) {
+ for (String schemaType : schemaTypes) {
+ mBuilder.addSchemaTypeFilters(schemaType);
+ }
+ return this;
+ }
+
+ /**
+ * Constructs a new {@link SearchSpec} from the contents of this builder.
+ *
+ * <p>After calling this method, the builder must no longer be used.
+ */
+ @NonNull
+ public SearchSpec build() {
+ if (mBuilder.getTermMatchType() == TermMatchType.Code.UNKNOWN) {
+ throw new IllegalSearchSpecException("Missing termMatchType field.");
+ }
+ return new SearchSpec(mBuilder.build());
+ }
+ }
+
+}
diff --git a/apex/appsearch/service/Android.bp b/apex/appsearch/service/Android.bp
index 7327231..04f385e 100644
--- a/apex/appsearch/service/Android.bp
+++ b/apex/appsearch/service/Android.bp
@@ -20,6 +20,5 @@
"framework-appsearch",
"services.core",
],
- static_libs: ["icing-java-proto-lite"],
apex_available: ["com.android.appsearch"],
}
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 042f051..f63abd9 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -15,13 +15,23 @@
*/
package com.android.server.appsearch;
+import android.annotation.NonNull;
import android.app.appsearch.IAppSearchManager;
import android.content.Context;
+import android.os.Binder;
+import android.os.UserHandle;
import com.android.internal.infra.AndroidFuture;
+import com.android.internal.util.Preconditions;
import com.android.server.SystemService;
+import com.android.server.appsearch.impl.AppSearchImpl;
+import com.android.server.appsearch.impl.FakeIcing;
+import com.android.server.appsearch.impl.ImplInstanceManager;
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.protobuf.InvalidProtocolBufferException;
/**
* TODO(b/142567528): add comments when implement this class
@@ -30,8 +40,11 @@
public AppSearchManagerService(Context context) {
super(context);
+ mFakeIcing = new FakeIcing();
}
+ private final FakeIcing mFakeIcing;
+
@Override
public void onStart() {
publishBinderService(Context.APP_SEARCH_SERVICE, new Stub());
@@ -40,12 +53,20 @@
private class Stub extends IAppSearchManager.Stub {
@Override
public void setSchema(byte[] schemaBytes, AndroidFuture callback) {
+ Preconditions.checkNotNull(schemaBytes);
+ Preconditions.checkNotNull(callback);
+ int callingUid = Binder.getCallingUidOrThrow();
+ int callingUserId = UserHandle.getUserId(callingUid);
+ long callingIdentity = Binder.clearCallingIdentity();
try {
SchemaProto schema = SchemaProto.parseFrom(schemaBytes);
- throw new UnsupportedOperationException("setSchema not yet implemented: " + schema);
-
+ AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
+ impl.setSchema(callingUid, schema);
+ callback.complete(null);
} catch (Throwable t) {
callback.completeExceptionally(t);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
}
}
@@ -57,5 +78,22 @@
callback.completeExceptionally(t);
}
}
+ // 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);
+ Preconditions.checkNotNull(searchSpec);
+ SearchSpecProto searchSpecProto = null;
+ try {
+ searchSpecProto = SearchSpecProto.parseFrom(searchSpec);
+ } catch (InvalidProtocolBufferException e) {
+ throw new RuntimeException(e);
+ }
+ SearchResultProto searchResults =
+ mFakeIcing.query(queryExpression);
+ callback.complete(searchResults.toByteArray());
+ }
}
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/impl/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/impl/AppSearchImpl.java
new file mode 100644
index 0000000..7c97b0b
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/impl/AppSearchImpl.java
@@ -0,0 +1,110 @@
+/*
+ * 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.appsearch.impl;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.Context;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import com.google.android.icing.proto.PropertyConfigProto;
+import com.google.android.icing.proto.SchemaProto;
+import com.google.android.icing.proto.SchemaTypeConfigProto;
+
+/**
+ * Manages interaction with {@link FakeIcing} and other components to implement AppSearch
+ * functionality.
+ */
+public final class AppSearchImpl {
+ private final Context mContext;
+ private final @UserIdInt int mUserId;
+ private final FakeIcing mFakeIcing = new FakeIcing();
+
+ AppSearchImpl(@NonNull Context context, @UserIdInt int userId) {
+ mContext = context;
+ mUserId = userId;
+ }
+
+ /**
+ * Updates the AppSearch schema for this app.
+ *
+ * @param callingUid The uid of the app calling AppSearch.
+ * @param origSchema The schema to set for this app.
+ */
+ public void setSchema(int callingUid, @NonNull SchemaProto origSchema) {
+ // Rewrite schema type names to include the calling app's package and uid.
+ String typePrefix = getTypePrefix(callingUid);
+ SchemaProto.Builder schemaBuilder = origSchema.toBuilder();
+ rewriteSchemaTypes(typePrefix, schemaBuilder);
+
+ // TODO(b/145635424): Save in schema type map
+ // TODO(b/145635424): Apply the schema to Icing and report results
+ }
+
+ /**
+ * Rewrites all types mentioned in the given {@code schemaBuilder} to prepend
+ * {@code typePrefix}.
+ *
+ * @param typePrefix The prefix to add
+ * @param schemaBuilder The schema to mutate
+ */
+ @VisibleForTesting
+ void rewriteSchemaTypes(
+ @NonNull String typePrefix, @NonNull SchemaProto.Builder schemaBuilder) {
+ for (int typeIdx = 0; typeIdx < schemaBuilder.getTypesCount(); typeIdx++) {
+ SchemaTypeConfigProto.Builder typeConfigBuilder =
+ schemaBuilder.getTypes(typeIdx).toBuilder();
+
+ // Rewrite SchemaProto.types.schema_type
+ String newSchemaType = typePrefix + typeConfigBuilder.getSchemaType();
+ typeConfigBuilder.setSchemaType(newSchemaType);
+
+ // Rewrite SchemaProto.types.properties.schema_type
+ for (int propertyIdx = 0;
+ propertyIdx < typeConfigBuilder.getPropertiesCount();
+ propertyIdx++) {
+ PropertyConfigProto.Builder propertyConfigBuilder =
+ typeConfigBuilder.getProperties(propertyIdx).toBuilder();
+ if (!propertyConfigBuilder.getSchemaType().isEmpty()) {
+ String newPropertySchemaType =
+ typePrefix + propertyConfigBuilder.getSchemaType();
+ propertyConfigBuilder.setSchemaType(newPropertySchemaType);
+ typeConfigBuilder.setProperties(propertyIdx, propertyConfigBuilder);
+ }
+ }
+
+ schemaBuilder.setTypes(typeIdx, typeConfigBuilder);
+ }
+ }
+
+ /**
+ * Returns a type prefix in a format like {@code com.example.package@1000/} or
+ * {@code com.example.sharedname:5678@1000/}.
+ */
+ @NonNull
+ private String getTypePrefix(int callingUid) {
+ // For regular apps, this call will return the package name. If callingUid is an
+ // android:sharedUserId, this value may be another type of name and have a :uid suffix.
+ String callingUidName = mContext.getPackageManager().getNameForUid(callingUid);
+ if (callingUidName == null) {
+ // Not sure how this is possible --- maybe app was uninstalled?
+ throw new IllegalStateException("Failed to look up package name for uid " + callingUid);
+ }
+ return callingUidName + "@" + mUserId + "/";
+ }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/impl/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/impl/ImplInstanceManager.java
new file mode 100644
index 0000000..395e30e
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/impl/ImplInstanceManager.java
@@ -0,0 +1,56 @@
+/*
+ * 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.appsearch.impl;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.util.SparseArray;
+
+/**
+ * Manages the lifecycle of instances of {@link AppSearchImpl}.
+ *
+ * <p>These instances are managed per unique device-user.
+ */
+public final class ImplInstanceManager {
+ private static final SparseArray<AppSearchImpl> sInstances = new SparseArray<>();
+
+ /**
+ * Gets an instance of AppSearchImpl for the given user.
+ *
+ * <p>If no AppSearchImpl instance exists for this user, Icing will be initialized and one will
+ * be created.
+ *
+ * @param context The Android context
+ * @param userId The multi-user userId of the device user calling AppSearch
+ * @return An initialized {@link AppSearchImpl} for this user
+ */
+ @NonNull
+ public static AppSearchImpl getInstance(@NonNull Context context, @UserIdInt int userId) {
+ AppSearchImpl instance = sInstances.get(userId);
+ if (instance == null) {
+ synchronized (ImplInstanceManager.class) {
+ instance = sInstances.get(userId);
+ if (instance == null) {
+ instance = new AppSearchImpl(context, userId);
+ sInstances.put(userId, instance);
+ }
+ }
+ }
+ return instance;
+ }
+}
diff --git a/apex/permission/Android.bp b/apex/permission/Android.bp
index f232e62..d746ea6 100644
--- a/apex/permission/Android.bp
+++ b/apex/permission/Android.bp
@@ -22,6 +22,10 @@
name: "com.android.permission-defaults",
key: "com.android.permission.key",
certificate: ":com.android.permission.certificate",
+ java_libs: [
+ "framework-permission",
+ "service-permission",
+ ],
apps: ["PermissionController"],
}
diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp
new file mode 100644
index 0000000..8b03da3
--- /dev/null
+++ b/apex/permission/framework/Android.bp
@@ -0,0 +1,60 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+filegroup {
+ name: "framework-permission-sources",
+ srcs: [
+ "java/**/*.java",
+ "java/**/*.aidl",
+ ],
+ path: "java",
+}
+
+java_library {
+ name: "framework-permission",
+ srcs: [
+ ":framework-permission-sources",
+ ],
+ sdk_version: "system_current",
+ apex_available: [
+ "com.android.permission",
+ "test_com.android.permission",
+ ],
+ hostdex: true,
+ installable: true,
+ visibility: [
+ "//frameworks/base/apex/permission:__subpackages__",
+ ],
+}
+
+droidstubs {
+ name: "framework-permission-stubs-sources",
+ srcs: [
+ ":framework-annotations",
+ ":framework-permission-sources",
+ ],
+ sdk_version: "system_current",
+ defaults: [
+ "framework-module-stubs-defaults-systemapi",
+ ],
+}
+
+java_library {
+ name: "framework-permission-stubs",
+ srcs: [
+ ":framework-permission-stubs-sources",
+ ],
+ sdk_version: "system_current",
+ installable: false,
+}
diff --git a/apex/permission/framework/java/android/permission/PermissionState.java b/apex/permission/framework/java/android/permission/PermissionState.java
new file mode 100644
index 0000000..e810db8
--- /dev/null
+++ b/apex/permission/framework/java/android/permission/PermissionState.java
@@ -0,0 +1,22 @@
+/*
+ * 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.permission;
+
+/**
+ * @hide
+ */
+public class PermissionState {}
diff --git a/apex/permission/service/Android.bp b/apex/permission/service/Android.bp
new file mode 100644
index 0000000..972b362
--- /dev/null
+++ b/apex/permission/service/Android.bp
@@ -0,0 +1,29 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_library {
+ name: "service-permission",
+ srcs: [
+ "java/**/*.java",
+ ],
+ sdk_version: "system_current",
+ libs: [
+ "framework-permission",
+ ],
+ apex_available: [
+ "com.android.permission",
+ "test_com.android.permission",
+ ],
+ installable: true,
+}
diff --git a/apex/permission/service/java/com/android/server/permission/RuntimePermissionPersistence.java b/apex/permission/service/java/com/android/server/permission/RuntimePermissionPersistence.java
new file mode 100644
index 0000000..a534e22
--- /dev/null
+++ b/apex/permission/service/java/com/android/server/permission/RuntimePermissionPersistence.java
@@ -0,0 +1,22 @@
+/*
+ * 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.permission;
+
+/**
+ * Persistence for runtime permissions.
+ */
+public class RuntimePermissionPersistence {}
diff --git a/apex/statsd/aidl/android/os/IStatsd.aidl b/apex/statsd/aidl/android/os/IStatsd.aidl
index c409f51..0ecf2f0 100644
--- a/apex/statsd/aidl/android/os/IStatsd.aidl
+++ b/apex/statsd/aidl/android/os/IStatsd.aidl
@@ -212,12 +212,17 @@
*
* Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS
*/
- oneway void unregisterPullerCallback(int atomTag, String packageName);
+ oneway void unregisterPullerCallback(int atomTag, String packageName);
- /**
- * Unregisters any pullAtomCallback for the given uid/atom.
- */
- oneway void unregisterPullAtomCallback(int uid, int atomTag);
+ /**
+ * Unregisters any pullAtomCallback for the given uid/atom.
+ */
+ oneway void unregisterPullAtomCallback(int uid, int atomTag);
+
+ /**
+ * Unregisters any pullAtomCallback for the given atom.
+ */
+ oneway void unregisterNativePullAtomCallback(int atomTag);
/**
* The install requires staging.
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 6444b4e..17573bb 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -24,11 +24,11 @@
import static android.os.storage.VolumeInfo.TYPE_PUBLIC;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
-import static com.android.server.stats.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
-import static com.android.server.stats.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
-import static com.android.server.stats.ProcfsMemoryUtil.forEachPid;
-import static com.android.server.stats.ProcfsMemoryUtil.readCmdlineFromProcfs;
-import static com.android.server.stats.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
+import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
+import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
+import static com.android.server.stats.pull.ProcfsMemoryUtil.forEachPid;
+import static com.android.server.stats.pull.ProcfsMemoryUtil.readCmdlineFromProcfs;
+import static com.android.server.stats.pull.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -135,8 +135,8 @@
import com.android.server.am.MemoryStatUtil.MemoryStat;
import com.android.server.notification.NotificationManagerService;
import com.android.server.role.RoleManagerInternal;
-import com.android.server.stats.IonMemoryUtil.IonAllocations;
-import com.android.server.stats.ProcfsMemoryUtil.MemorySnapshot;
+import com.android.server.stats.pull.IonMemoryUtil.IonAllocations;
+import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot;
import com.android.server.storage.DiskStatsFileLogger;
import com.android.server.storage.DiskStatsLoggingService;
@@ -714,111 +714,6 @@
}
}
- /**
- * Helper method to extract the Parcelable controller info from a
- * SynchronousResultReceiver.
- */
- private static <T extends Parcelable> T awaitControllerInfo(
- @Nullable SynchronousResultReceiver receiver) {
- if (receiver == null) {
- return null;
- }
-
- try {
- final SynchronousResultReceiver.Result result =
- receiver.awaitResult(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS);
- if (result.bundle != null) {
- // This is the final destination for the Bundle.
- result.bundle.setDefusable(true);
-
- final T data = result.bundle.getParcelable(
- RESULT_RECEIVER_CONTROLLER_KEY);
- if (data != null) {
- return data;
- }
- }
- Slog.e(TAG, "no controller energy info supplied for " + receiver.getName());
- } catch (TimeoutException e) {
- Slog.w(TAG, "timeout reading " + receiver.getName() + " stats");
- }
- return null;
- }
-
- private void pullWifiActivityInfo(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- WifiManager wifiManager;
- synchronized (this) {
- if (mWifiManager == null) {
- mWifiManager = mContext.getSystemService(WifiManager.class);
- }
- wifiManager = mWifiManager;
- }
- if (wifiManager == null) {
- return;
- }
- long token = Binder.clearCallingIdentity();
- try {
- SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
- wifiManager.getWifiActivityEnergyInfoAsync(
- new Executor() {
- @Override
- public void execute(Runnable runnable) {
- // run the listener on the binder thread, if it was run on the main
- // thread it would deadlock since we would be waiting on ourselves
- runnable.run();
- }
- },
- info -> {
- Bundle bundle = new Bundle();
- bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info);
- wifiReceiver.send(0, bundle);
- }
- );
- final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
- if (wifiInfo == null) {
- return;
- }
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeLong(wifiInfo.getTimeSinceBootMillis());
- e.writeInt(wifiInfo.getStackState());
- e.writeLong(wifiInfo.getControllerTxDurationMillis());
- e.writeLong(wifiInfo.getControllerRxDurationMillis());
- e.writeLong(wifiInfo.getControllerIdleDurationMillis());
- e.writeLong(wifiInfo.getControllerEnergyUsedMicroJoules());
- pulledData.add(e);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- private void pullModemActivityInfo(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- synchronized (this) {
- if (mTelephony == null) {
- mTelephony = mContext.getSystemService(TelephonyManager.class);
- }
- }
- if (mTelephony != null) {
- SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
- mTelephony.requestModemActivityInfo(modemReceiver);
- final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeLong(modemInfo.getTimestamp());
- e.writeLong(modemInfo.getSleepTimeMillis());
- e.writeLong(modemInfo.getIdleTimeMillis());
- e.writeLong(modemInfo.getTransmitPowerInfo().get(0).getTimeInMillis());
- e.writeLong(modemInfo.getTransmitPowerInfo().get(1).getTimeInMillis());
- e.writeLong(modemInfo.getTransmitPowerInfo().get(2).getTimeInMillis());
- e.writeLong(modemInfo.getTransmitPowerInfo().get(3).getTimeInMillis());
- e.writeLong(modemInfo.getTransmitPowerInfo().get(4).getTimeInMillis());
- e.writeLong(modemInfo.getReceiveTimeMillis());
- pulledData.add(e);
- }
- }
-
private void pullSystemElapsedRealtime(
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
@@ -827,207 +722,6 @@
pulledData.add(e);
}
- private void pullProcessMemoryState(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- List<ProcessMemoryState> processMemoryStates =
- LocalServices.getService(
- ActivityManagerInternal.class).getMemoryStateForProcesses();
- for (ProcessMemoryState processMemoryState : processMemoryStates) {
- final MemoryStat memoryStat = readMemoryStatFromFilesystem(processMemoryState.uid,
- processMemoryState.pid);
- if (memoryStat == null) {
- continue;
- }
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(processMemoryState.uid);
- e.writeString(processMemoryState.processName);
- e.writeInt(processMemoryState.oomScore);
- e.writeLong(memoryStat.pgfault);
- e.writeLong(memoryStat.pgmajfault);
- e.writeLong(memoryStat.rssInBytes);
- e.writeLong(memoryStat.cacheInBytes);
- e.writeLong(memoryStat.swapInBytes);
- e.writeLong(-1); // unused
- e.writeLong(-1); // unused
- e.writeInt(-1); // unsed
- pulledData.add(e);
- }
- }
-
- private void pullProcessMemoryHighWaterMark(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- List<ProcessMemoryState> managedProcessList =
- LocalServices.getService(
- ActivityManagerInternal.class).getMemoryStateForProcesses();
- for (ProcessMemoryState managedProcess : managedProcessList) {
- final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid);
- if (snapshot == null) {
- continue;
- }
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(managedProcess.uid);
- e.writeString(managedProcess.processName);
- // RSS high-water mark in bytes.
- e.writeLong((long) snapshot.rssHighWaterMarkInKilobytes * 1024L);
- e.writeInt(snapshot.rssHighWaterMarkInKilobytes);
- pulledData.add(e);
- }
- forEachPid((pid, cmdLine) -> {
- if (!MEMORY_INTERESTING_NATIVE_PROCESSES.contains(cmdLine)) {
- return;
- }
- final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid);
- if (snapshot == null) {
- return;
- }
- // Sometimes we get here a process that is not included in the whitelist. It comes
- // from forking the zygote for an app. We can ignore that sample because this process
- // is collected by ProcessMemoryState.
- if (isAppUid(snapshot.uid)) {
- return;
- }
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(snapshot.uid);
- e.writeString(cmdLine);
- // RSS high-water mark in bytes.
- e.writeLong((long) snapshot.rssHighWaterMarkInKilobytes * 1024L);
- e.writeInt(snapshot.rssHighWaterMarkInKilobytes);
- pulledData.add(e);
- });
- // Invoke rss_hwm_reset binary to reset RSS HWM counters for all processes.
- SystemProperties.set("sys.rss_hwm_reset.on", "1");
- }
-
- private void pullProcessMemorySnapshot(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- List<ProcessMemoryState> managedProcessList =
- LocalServices.getService(
- ActivityManagerInternal.class).getMemoryStateForProcesses();
- for (ProcessMemoryState managedProcess : managedProcessList) {
- final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid);
- if (snapshot == null) {
- continue;
- }
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(managedProcess.uid);
- e.writeString(managedProcess.processName);
- e.writeInt(managedProcess.pid);
- e.writeInt(managedProcess.oomScore);
- e.writeInt(snapshot.rssInKilobytes);
- e.writeInt(snapshot.anonRssInKilobytes);
- e.writeInt(snapshot.swapInKilobytes);
- e.writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes);
- pulledData.add(e);
- }
- forEachPid((pid, cmdLine) -> {
- if (!MEMORY_INTERESTING_NATIVE_PROCESSES.contains(cmdLine)) {
- return;
- }
- final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid);
- if (snapshot == null) {
- return;
- }
- // Sometimes we get here a process that is not included in the whitelist. It comes
- // from forking the zygote for an app. We can ignore that sample because this process
- // is collected by ProcessMemoryState.
- if (isAppUid(snapshot.uid)) {
- return;
- }
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(snapshot.uid);
- e.writeString(cmdLine);
- e.writeInt(pid);
- e.writeInt(-1001); // Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1.
- e.writeInt(snapshot.rssInKilobytes);
- e.writeInt(snapshot.anonRssInKilobytes);
- e.writeInt(snapshot.swapInKilobytes);
- e.writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes);
- pulledData.add(e);
- });
- }
-
- private static boolean isAppUid(int uid) {
- return uid >= MIN_APP_UID;
- }
-
- private void pullSystemIonHeapSize(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- final long systemIonHeapSizeInBytes = readSystemIonHeapSizeFromDebugfs();
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeLong(systemIonHeapSizeInBytes);
- pulledData.add(e);
- }
-
- private void pullProcessSystemIonHeapSize(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- List<IonAllocations> result = readProcessSystemIonHeapSizesFromDebugfs();
- for (IonAllocations allocations : result) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(getUidForPid(allocations.pid));
- e.writeString(readCmdlineFromProcfs(allocations.pid));
- e.writeInt((int) (allocations.totalSizeInBytes / 1024));
- e.writeInt(allocations.count);
- e.writeInt((int) (allocations.maxSizeInBytes / 1024));
- pulledData.add(e);
- }
- }
-
- private void pullBinderCallsStats(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- BinderCallsStatsService.Internal binderStats =
- LocalServices.getService(BinderCallsStatsService.Internal.class);
- if (binderStats == null) {
- throw new IllegalStateException("binderStats is null");
- }
-
- List<ExportedCallStat> callStats = binderStats.getExportedCallStats();
- binderStats.reset();
- for (ExportedCallStat callStat : callStats) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(callStat.workSourceUid);
- e.writeString(callStat.className);
- e.writeString(callStat.methodName);
- e.writeLong(callStat.callCount);
- e.writeLong(callStat.exceptionCount);
- e.writeLong(callStat.latencyMicros);
- e.writeLong(callStat.maxLatencyMicros);
- e.writeLong(callStat.cpuTimeMicros);
- e.writeLong(callStat.maxCpuTimeMicros);
- e.writeLong(callStat.maxReplySizeBytes);
- e.writeLong(callStat.maxRequestSizeBytes);
- e.writeLong(callStat.recordedCallCount);
- e.writeInt(callStat.screenInteractive ? 1 : 0);
- e.writeInt(callStat.callingUid);
- pulledData.add(e);
- }
- }
-
- private void pullBinderCallsStatsExceptions(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- BinderCallsStatsService.Internal binderStats =
- LocalServices.getService(BinderCallsStatsService.Internal.class);
- if (binderStats == null) {
- throw new IllegalStateException("binderStats is null");
- }
-
- ArrayMap<String, Integer> exceptionStats = binderStats.getExportedExceptionStats();
- // TODO: decouple binder calls exceptions with the rest of the binder calls data so that we
- // can reset the exception stats.
- for (Entry<String, Integer> entry : exceptionStats.entrySet()) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeString(entry.getKey());
- e.writeInt(entry.getValue());
- pulledData.add(e);
- }
- }
-
private void pullLooperStats(int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
LooperStats looperStats = LocalServices.getService(LooperStats.class);
@@ -1404,78 +1098,6 @@
}
}
- private BatteryStatsHelper getBatteryStatsHelper() {
- if (mBatteryStatsHelper == null) {
- final long callingToken = Binder.clearCallingIdentity();
- try {
- // clearCallingIdentity required for BatteryStatsHelper.checkWifiOnly().
- mBatteryStatsHelper = new BatteryStatsHelper(mContext, false);
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
- mBatteryStatsHelper.create((Bundle) null);
- }
- long currentTime = SystemClock.elapsedRealtime();
- if (currentTime - mBatteryStatsHelperTimestampMs >= MAX_BATTERY_STATS_HELPER_FREQUENCY_MS) {
- // Load BatteryStats and do all the calculations.
- mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.USER_ALL);
- // Calculations are done so we don't need to save the raw BatteryStats data in RAM.
- mBatteryStatsHelper.clearStats();
- mBatteryStatsHelperTimestampMs = currentTime;
- }
- return mBatteryStatsHelper;
- }
-
- private long milliAmpHrsToNanoAmpSecs(double mAh) {
- final long MILLI_AMP_HR_TO_NANO_AMP_SECS = 1_000_000L * 3600L;
- return (long) (mAh * MILLI_AMP_HR_TO_NANO_AMP_SECS + 0.5);
- }
-
- private void pullDeviceCalculatedPowerUse(int tagId,
- long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
- BatteryStatsHelper bsHelper = getBatteryStatsHelper();
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeLong(milliAmpHrsToNanoAmpSecs(bsHelper.getComputedPower()));
- pulledData.add(e);
- }
-
- private void pullDeviceCalculatedPowerBlameUid(int tagId,
- long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
- final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
- if (sippers == null) {
- return;
- }
- for (BatterySipper bs : sippers) {
- if (bs.drainType != bs.drainType.APP) {
- continue;
- }
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(bs.uidObj.getUid());
- e.writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah));
- pulledData.add(e);
- }
- }
-
- private void pullDeviceCalculatedPowerBlameOther(int tagId,
- long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
- final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
- if (sippers == null) {
- return;
- }
- for (BatterySipper bs : sippers) {
- if (bs.drainType == bs.drainType.APP) {
- continue; // This is a separate atom; see pullDeviceCalculatedPowerBlameUid().
- }
- if (bs.drainType == bs.drainType.USER) {
- continue; // This is not supported. We purposefully calculate over USER_ALL.
- }
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(bs.drainType.ordinal());
- e.writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah));
- pulledData.add(e);
- }
- }
-
private void pullDiskIo(int tagId, long elapsedNanos, final long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
mStoragedUidIoStatsReader.readAbsolute((uid, fgCharsRead, fgCharsWrite, fgBytesRead,
@@ -1574,49 +1196,6 @@
}
}
- private void pullTemperature(int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- long callingToken = Binder.clearCallingIdentity();
- try {
- List<Temperature> temperatures = sThermalService.getCurrentTemperatures();
- for (Temperature temp : temperatures) {
- StatsLogEventWrapper e =
- new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(temp.getType());
- e.writeString(temp.getName());
- e.writeInt((int) (temp.getValue() * 10));
- e.writeInt(temp.getStatus());
- pulledData.add(e);
- }
- } catch (RemoteException e) {
- // Should not happen.
- Slog.e(TAG, "Disconnected from thermal service. Cannot pull temperatures.");
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
- }
-
- private void pullCoolingDevices(int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- long callingToken = Binder.clearCallingIdentity();
- try {
- List<CoolingDevice> devices = sThermalService.getCurrentCoolingDevices();
- for (CoolingDevice device : devices) {
- StatsLogEventWrapper e =
- new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(device.getType());
- e.writeString(device.getName());
- e.writeInt((int) (device.getValue()));
- pulledData.add(e);
- }
- } catch (RemoteException e) {
- // Should not happen.
- Slog.e(TAG, "Disconnected from thermal service. Cannot pull temperatures.");
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
- }
-
private void pullDebugElapsedClock(int tagId,
long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
final long elapsedMillis = SystemClock.elapsedRealtime();
@@ -1995,56 +1574,11 @@
long wallClockNanos = SystemClock.currentTimeMicro() * 1000L;
switch (tagId) {
- case StatsLog.WIFI_ACTIVITY_INFO: {
- pullWifiActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
- case StatsLog.MODEM_ACTIVITY_INFO: {
- pullModemActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
case StatsLog.SYSTEM_ELAPSED_REALTIME: {
pullSystemElapsedRealtime(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
- case StatsLog.PROCESS_MEMORY_STATE: {
- pullProcessMemoryState(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
- case StatsLog.PROCESS_MEMORY_HIGH_WATER_MARK: {
- pullProcessMemoryHighWaterMark(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
- case StatsLog.PROCESS_MEMORY_SNAPSHOT: {
- pullProcessMemorySnapshot(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
- case StatsLog.SYSTEM_ION_HEAP_SIZE: {
- pullSystemIonHeapSize(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
- case StatsLog.PROCESS_SYSTEM_ION_HEAP_SIZE: {
- pullProcessSystemIonHeapSize(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
- case StatsLog.BINDER_CALLS: {
- pullBinderCallsStats(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
- case StatsLog.BINDER_CALLS_EXCEPTIONS: {
- pullBinderCallsStatsExceptions(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
case StatsLog.LOOPER_STATS: {
pullLooperStats(tagId, elapsedNanos, wallClockNanos, ret);
break;
@@ -2107,31 +1641,6 @@
break;
}
- case StatsLog.DEVICE_CALCULATED_POWER_USE: {
- pullDeviceCalculatedPowerUse(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
- case StatsLog.DEVICE_CALCULATED_POWER_BLAME_UID: {
- pullDeviceCalculatedPowerBlameUid(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
- case StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER: {
- pullDeviceCalculatedPowerBlameOther(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
- case StatsLog.TEMPERATURE: {
- pullTemperature(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
- case StatsLog.COOLING_DEVICE: {
- pullCoolingDevices(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
-
case StatsLog.DEBUG_ELAPSED_CLOCK: {
pullDebugElapsedClock(tagId, elapsedNanos, wallClockNanos, ret);
break;
diff --git a/api/current.txt b/api/current.txt
index a3d8458..d9c305a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -17289,6 +17289,7 @@
field public static final int LENS_OPTICAL_STABILIZATION_MODE_ON = 1; // 0x1
field public static final int LENS_POSE_REFERENCE_GYROSCOPE = 1; // 0x1
field public static final int LENS_POSE_REFERENCE_PRIMARY_CAMERA = 0; // 0x0
+ field public static final int LENS_POSE_REFERENCE_UNDEFINED = 2; // 0x2
field public static final int LENS_STATE_MOVING = 1; // 0x1
field public static final int LENS_STATE_STATIONARY = 0; // 0x0
field public static final int LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE = 0; // 0x0
@@ -30654,6 +30655,7 @@
method public boolean isP2pSupported();
method public boolean isPreferredNetworkOffloadSupported();
method @Deprecated public boolean isScanAlwaysAvailable();
+ method public boolean isStaApConcurrencySupported();
method public boolean isTdlsSupported();
method public boolean isWapiSupported();
method public boolean isWifiEnabled();
@@ -30809,6 +30811,7 @@
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsAppInteractionRequired(boolean);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsEnhancedOpen(boolean);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsHiddenSsid(boolean);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsInitialAutoJoinEnabled(boolean);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsMetered(boolean);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsUserInteractionRequired(boolean);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPasspointConfig(@NonNull android.net.wifi.hotspot2.PasspointConfiguration);
@@ -39066,7 +39069,7 @@
field public static final String COLUMN_MIME_TYPE = "mime_type";
field public static final String COLUMN_SIZE = "_size";
field public static final String COLUMN_SUMMARY = "summary";
- field public static final int FLAG_DIR_BLOCKS_TREE = 32768; // 0x8000
+ field public static final int FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE = 32768; // 0x8000
field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10
field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 32; // 0x20
field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8
@@ -39747,8 +39750,8 @@
field public static final String EXTRA_INPUT_METHOD_ID = "input_method_id";
field public static final String EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME = "android.provider.extra.NOTIFICATION_LISTENER_COMPONENT_NAME";
field public static final String EXTRA_SUB_ID = "android.provider.extra.SUB_ID";
- field public static final String EXTRA_WIFI_CONFIGURATION_LIST = "android.provider.extra.WIFI_CONFIGURATION_LIST";
- field public static final String EXTRA_WIFI_CONFIGURATION_RESULT_LIST = "android.provider.extra.WIFI_CONFIGURATION_RESULT_LIST";
+ field public static final String EXTRA_WIFI_NETWORK_LIST = "android.provider.extra.WIFI_NETWORK_LIST";
+ field public static final String EXTRA_WIFI_NETWORK_RESULT_LIST = "android.provider.extra.WIFI_NETWORK_RESULT_LIST";
field public static final String INTENT_CATEGORY_USAGE_ACCESS_CONFIG = "android.intent.category.USAGE_ACCESS_CONFIG";
field public static final String METADATA_USAGE_ACCESS_REASON = "android.settings.metadata.USAGE_ACCESS_REASON";
}
@@ -55694,7 +55697,7 @@
method public int describeContents();
method @NonNull public android.os.Bundle getExtras();
method @NonNull public java.util.Collection<android.view.textclassifier.TextLinks.TextLink> getLinks();
- method @NonNull public String getText();
+ method @NonNull public CharSequence getText();
method public void writeToParcel(android.os.Parcel, int);
field public static final int APPLY_STRATEGY_IGNORE = 0; // 0x0
field public static final int APPLY_STRATEGY_REPLACE = 1; // 0x1
diff --git a/api/module-app-current.txt b/api/module-app-current.txt
index d802177..4307e67 100644
--- a/api/module-app-current.txt
+++ b/api/module-app-current.txt
@@ -1 +1,9 @@
// Signature format: 2.0
+package android.app {
+
+ public final class NotificationChannel implements android.os.Parcelable {
+ method public void setBlockableSystem(boolean);
+ }
+
+}
+
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index c8253a0..1cb1c20 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -124,3 +124,11 @@
}
+package android.util {
+
+ public final class Log {
+ method public static int logToRadioBuffer(int, @Nullable String, @Nullable String);
+ }
+
+}
+
diff --git a/api/system-current.txt b/api/system-current.txt
index d02bbfb..e532a3a 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -126,6 +126,7 @@
field @Deprecated public static final String MODIFY_NETWORK_ACCOUNTING = "android.permission.MODIFY_NETWORK_ACCOUNTING";
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 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";
@@ -242,8 +243,10 @@
public static final class R.attr {
field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
field public static final int isVrOnly = 16844152; // 0x1010578
+ field public static final int minExtensionVersion = 16844306; // 0x1010612
field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
+ field public static final int sdkVersion = 16844305; // 0x1010611
field public static final int supportsAmbientMode = 16844173; // 0x101058d
field public static final int userRestriction = 16844164; // 0x1010584
}
@@ -6243,6 +6246,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveSoftApBackupData();
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public boolean setSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
@@ -6592,6 +6596,7 @@
public final class PasspointConfiguration implements android.os.Parcelable {
method public boolean isAutoJoinEnabled();
+ method public boolean isMacRandomizationEnabled();
}
public abstract class ProvisioningCallback {
@@ -7713,7 +7718,10 @@
public final class PermissionManager {
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public int getRuntimePermissionsVersion();
method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions();
+ method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToEnabledImsServices(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToEnabledTelephonyDataServices(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToLuiApp(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void revokeDefaultPermissionsFromDisabledTelephonyDataServices(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void revokeDefaultPermissionsFromLuiApps(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public void setRuntimePermissionsVersion(@IntRange(from=0) int);
method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void startOneTimePermissionSession(@NonNull String, long, int, int);
@@ -8014,6 +8022,8 @@
field public static final int COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE = 5; // 0x5
field public static final int COLUMN_INDEX_XML_RES_RANK = 0; // 0x0
field public static final int COLUMN_INDEX_XML_RES_RESID = 1; // 0x1
+ field public static final String DYNAMIC_INDEXABLES_RAW = "dynamic_indexables_raw";
+ field public static final String DYNAMIC_INDEXABLES_RAW_PATH = "settings/dynamic_indexables_raw";
field public static final String INDEXABLES_RAW = "indexables_raw";
field public static final String[] INDEXABLES_RAW_COLUMNS;
field public static final String INDEXABLES_RAW_PATH = "settings/indexables_raw";
@@ -8071,6 +8081,7 @@
method public String getType(android.net.Uri);
method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
method public android.database.Cursor query(android.net.Uri, String[], String, String[], String);
+ method @Nullable public android.database.Cursor queryDynamicRawData(@Nullable String[]);
method public abstract android.database.Cursor queryNonIndexableKeys(String[]);
method public abstract android.database.Cursor queryRawData(String[]);
method @Nullable public android.database.Cursor querySliceUriPairs();
@@ -8164,6 +8175,10 @@
field public static final int VOLUME_HUSH_VIBRATE = 1; // 0x1
}
+ public static final class Settings.System extends android.provider.Settings.NameValueTable {
+ method @RequiresPermission(android.Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE) public static boolean putString(@NonNull android.content.ContentResolver, @NonNull String, @Nullable String, boolean);
+ }
+
public static interface Telephony.CarrierColumns extends android.provider.BaseColumns {
field @NonNull public static final android.net.Uri CONTENT_URI;
field public static final String EXPIRATION_TIME = "expiration_time";
diff --git a/api/test-current.txt b/api/test-current.txt
index 6498e9a..28119e3 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -73,6 +73,7 @@
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
method public static void resumeAppSwitches() throws android.os.RemoteException;
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int);
+ method @RequiresPermission("android.permission.MANAGE_USERS") public boolean switchUser(@NonNull android.os.UserHandle);
field public static final int PROCESS_CAPABILITY_ALL = 1; // 0x1
field public static final int PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1; // 0x1
field public static final int PROCESS_CAPABILITY_NONE = 0; // 0x0
@@ -756,6 +757,7 @@
method public int getUserId();
method public void setAutofillOptions(@Nullable android.content.AutofillOptions);
method public void setContentCaptureOptions(@Nullable android.content.ContentCaptureOptions);
+ method @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
field public static final String BLOB_STORE_SERVICE = "blob_store";
field public static final String BUGREPORT_SERVICE = "bugreport";
field public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
@@ -2512,10 +2514,12 @@
method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static float getFloat(@NonNull String, @NonNull String, float);
method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static int getInt(@NonNull String, @NonNull String, int);
method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static long getLong(@NonNull String, @NonNull String, long);
+ method @NonNull @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static android.provider.DeviceConfig.Properties getProperties(@NonNull String, @NonNull java.lang.String...);
method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static String getProperty(@NonNull String, @NonNull String);
method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static String getString(@NonNull String, @NonNull String, @Nullable String);
method public static void removeOnPropertiesChangedListener(@NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static void resetToDefaults(int, @Nullable String);
+ method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperties(@NonNull android.provider.DeviceConfig.Properties) throws android.provider.DeviceConfig.BadConfigException;
method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(@NonNull String, @NonNull String, @Nullable String, boolean);
field public static final String NAMESPACE_ANDROID = "android";
field public static final String NAMESPACE_AUTOFILL = "autofill";
@@ -2527,6 +2531,10 @@
field public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot";
}
+ public static class DeviceConfig.BadConfigException extends java.lang.Exception {
+ ctor public DeviceConfig.BadConfigException();
+ }
+
public static interface DeviceConfig.OnPropertiesChangedListener {
method public void onPropertiesChanged(@NonNull android.provider.DeviceConfig.Properties);
}
@@ -4672,6 +4680,18 @@
package android.view.inputmethod {
+ public final class InlineSuggestion implements android.os.Parcelable {
+ method @NonNull public static android.view.inputmethod.InlineSuggestion newInlineSuggestion(@NonNull android.view.inputmethod.InlineSuggestionInfo);
+ }
+
+ public final class InlineSuggestionInfo implements android.os.Parcelable {
+ method @NonNull public static android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(@NonNull android.view.inline.InlinePresentationSpec, @NonNull String, @Nullable String[]);
+ }
+
+ public final class InlineSuggestionsResponse implements android.os.Parcelable {
+ method @NonNull public static android.view.inputmethod.InlineSuggestionsResponse newInlineSuggestionsResponse(@NonNull java.util.List<android.view.inputmethod.InlineSuggestion>);
+ }
+
public final class InputMethodManager {
method public int getDisplayId();
method public boolean isInputMethodPickerShown();
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index 6d1f291..603f7a2 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -400,7 +400,7 @@
GetterSetterNames: android.location.GnssClock#setTimeUncertaintyNanos(double):
GetterSetterNames: android.location.GnssMeasurement#setBasebandCn0DbHz(double):
-
+
GetterSetterNames: android.location.GnssMeasurement#setCarrierFrequencyHz(float):
GetterSetterNames: android.location.GnssMeasurement#setCodeType(String):
@@ -466,7 +466,7 @@
KotlinOperator: android.os.WorkSource#get(int):
KotlinOperator: android.util.SparseArrayMap#get(int, String):
- Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+
ListenerInterface: android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener:
@@ -2059,6 +2059,18 @@
MissingNullability: android.view.KeyEvent#actionToString(int):
+MissingNullability: android.view.SurfaceControlViewHost#SurfaceControlViewHost(android.content.Context, android.view.Display, android.view.SurfaceControl) parameter #0:
+
+MissingNullability: android.view.SurfaceControlViewHost#SurfaceControlViewHost(android.content.Context, android.view.Display, android.view.SurfaceControl) parameter #1:
+
+MissingNullability: android.view.SurfaceControlViewHost#SurfaceControlViewHost(android.content.Context, android.view.Display, android.view.SurfaceControl) parameter #2:
+
+MissingNullability: android.view.SurfaceControlViewHost#addView(android.view.View, android.view.WindowManager.LayoutParams) parameter #0:
+
+MissingNullability: android.view.SurfaceControlViewHost#addView(android.view.View, android.view.WindowManager.LayoutParams) parameter #1:
+
+MissingNullability: android.view.SurfaceControlViewHost#relayout(android.view.WindowManager.LayoutParams) parameter #0:
+
MissingNullability: android.view.View#getTooltipView():
MissingNullability: android.view.View#isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable) parameter #0:
@@ -2079,18 +2091,6 @@
MissingNullability: android.view.WindowManager.LayoutParams#accessibilityTitle:
-MissingNullability: android.view.SurfaceControlViewHost#SurfaceControlViewHost(android.content.Context, android.view.Display, android.view.SurfaceControl) parameter #0:
-
-MissingNullability: android.view.SurfaceControlViewHost#SurfaceControlViewHost(android.content.Context, android.view.Display, android.view.SurfaceControl) parameter #1:
-
-MissingNullability: android.view.SurfaceControlViewHost#SurfaceControlViewHost(android.content.Context, android.view.Display, android.view.SurfaceControl) parameter #2:
-
-MissingNullability: android.view.SurfaceControlViewHost#addView(android.view.View, android.view.WindowManager.LayoutParams) parameter #0:
-
-MissingNullability: android.view.SurfaceControlViewHost#addView(android.view.View, android.view.WindowManager.LayoutParams) parameter #1:
-
-MissingNullability: android.view.SurfaceControlViewHost#relayout(android.view.WindowManager.LayoutParams) parameter #0:
-
MissingNullability: android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener#onAccessibilityServicesStateChanged(android.view.accessibility.AccessibilityManager) parameter #0:
MissingNullability: android.view.accessibility.AccessibilityNodeInfo#setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger) parameter #0:
@@ -2426,11 +2426,13 @@
ProtectedMember: android.view.ViewGroup#resetResolvedDrawables():
-PublicTypedef: android.os.HwParcel.Status: Don't expose @IntDef: @Status must be hidden.
-PublicTypedef: android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability: Don't expose @IntDef: @MmTelCapability must be hidden.
-
-PublicTypedef: android.telephony.ims.feature.MmTelFeature.ProcessCallResult: Don't expose @IntDef: @ProcessCallResult must be hidden.
+PublicTypedef: android.os.HwParcel.Status:
+
+PublicTypedef: android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability:
+
+PublicTypedef: android.telephony.ims.feature.MmTelFeature.ProcessCallResult:
+
RawAidl: android.telephony.mbms.vendor.MbmsDownloadServiceBase:
@@ -2513,6 +2515,8 @@
SamShouldBeLast: android.database.sqlite.SQLiteDirectCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]):
+SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(android.location.LocationRequest, android.location.LocationListener, android.os.Looper):
+ SAM-compatible parameters (such as parameter 2, "listener", in android.location.LocationManager.requestLocationUpdates) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(android.location.LocationRequest, java.util.concurrent.Executor, android.location.LocationListener):
SamShouldBeLast: android.os.BugreportManager#startBugreport(android.os.ParcelFileDescriptor, android.os.ParcelFileDescriptor, android.os.BugreportParams, java.util.concurrent.Executor, android.os.BugreportManager.BugreportCallback):
@@ -2593,6 +2597,8 @@
+UserHandle: android.app.ActivityManager#switchUser(android.os.UserHandle):
+ When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
UserHandle: android.app.admin.DevicePolicyManager#getOwnerInstalledCaCerts(android.os.UserHandle):
UserHandle: android.app.role.RoleManager#addOnRoleHoldersChangedListenerAsUser(java.util.concurrent.Executor, android.app.role.OnRoleHoldersChangedListener, android.os.UserHandle):
@@ -2607,8 +2613,12 @@
UserHandle: android.app.role.RoleManager#removeRoleHolderAsUser(String, String, int, android.os.UserHandle, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Boolean>):
-UserHandle: android.companion.CompanionDeviceManager#isDeviceAssociated(String, android.net.MacAddress, android.os.UserHandle):
+UserHandle: android.app.usage.StorageStatsManager#queryCratesForPackage(java.util.UUID, String, android.os.UserHandle):
When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
+UserHandle: android.app.usage.StorageStatsManager#queryCratesForUser(java.util.UUID, android.os.UserHandle):
+ When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
+UserHandle: android.companion.CompanionDeviceManager#isDeviceAssociated(String, android.net.MacAddress, android.os.UserHandle):
+
UserHandle: android.content.pm.PackageManager#getInstallReason(String, android.os.UserHandle):
UserHandle: android.content.pm.PackageManager#getPermissionFlags(String, String, android.os.UserHandle):
diff --git a/cmds/incident_helper/Android.bp b/cmds/incident_helper/Android.bp
index d7b6d69..64f4c66 100644
--- a/cmds/incident_helper/Android.bp
+++ b/cmds/incident_helper/Android.bp
@@ -1,3 +1,28 @@
+// 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.
+
+java_binary {
+ name: "incident-helper-cmd",
+ wrapper: "incident_helper_cmd",
+ srcs: [
+ "java/**/*.java",
+ ],
+ proto: {
+ plugin: "javastream",
+ },
+}
+
cc_defaults {
name: "incident_helper_defaults",
diff --git a/cmds/incident_helper/incident_helper_cmd b/cmds/incident_helper/incident_helper_cmd
new file mode 100644
index 0000000..d45f7df
--- /dev/null
+++ b/cmds/incident_helper/incident_helper_cmd
@@ -0,0 +1,6 @@
+#!/system/bin/sh
+# Script to start "incident_helper_cmd" on the device
+#
+base=/system
+export CLASSPATH=$base/framework/incident-helper-cmd.jar
+exec app_process $base/bin com.android.commands.incident.IncidentHelper "$@"
diff --git a/cmds/incident_helper/java/com/android/commands/incident/ExecutionException.java b/cmds/incident_helper/java/com/android/commands/incident/ExecutionException.java
new file mode 100644
index 0000000..d97b17e
--- /dev/null
+++ b/cmds/incident_helper/java/com/android/commands/incident/ExecutionException.java
@@ -0,0 +1,40 @@
+/*
+ * 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.commands.incident;
+
+/**
+ * Thrown when there is an error executing a section.
+ */
+public class ExecutionException extends Exception {
+ /**
+ * Constructs a ExecutionException.
+ *
+ * @param msg the message
+ */
+ public ExecutionException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructs a ExecutionException from another exception.
+ *
+ * @param e the exception
+ */
+ public ExecutionException(Exception e) {
+ super(e);
+ }
+}
diff --git a/cmds/incident_helper/java/com/android/commands/incident/IncidentHelper.java b/cmds/incident_helper/java/com/android/commands/incident/IncidentHelper.java
new file mode 100644
index 0000000..e5874e0
--- /dev/null
+++ b/cmds/incident_helper/java/com/android/commands/incident/IncidentHelper.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.commands.incident;
+
+import android.util.Log;
+
+import com.android.commands.incident.sections.PersistLogSection;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * Helper command runner for incidentd to run customized command to gather data for a non-standard
+ * section.
+ */
+public class IncidentHelper {
+ private static final String TAG = "IncidentHelper";
+ private static boolean sLog = false;
+ private final List<String> mArgs;
+ private ListIterator<String> mArgsIterator;
+
+ private IncidentHelper(String[] args) {
+ mArgs = Collections.unmodifiableList(Arrays.asList(args));
+ mArgsIterator = mArgs.listIterator();
+ }
+
+ private static void showUsage(PrintStream out) {
+ out.println("This command is not designed to be run manually.");
+ out.println("Usage:");
+ out.println(" run [sectionName]");
+ }
+
+ private void run(String[] args) throws ExecutionException {
+ Section section = null;
+ List<String> sectionArgs = new ArrayList<>();
+ while (mArgsIterator.hasNext()) {
+ String arg = mArgsIterator.next();
+ if ("-l".equals(arg)) {
+ sLog = true;
+ Log.i(TAG, "Args: [" + String.join(",", args) + "]");
+ } else if ("run".equals(arg)) {
+ section = getSection(nextArgRequired());
+ mArgsIterator.forEachRemaining(sectionArgs::add);
+ break;
+ } else {
+ log(Log.WARN, TAG, "Error: Unknown argument: " + arg);
+ return;
+ }
+ }
+ section.run(System.in, System.out, sectionArgs);
+ }
+
+ private static Section getSection(String name) throws IllegalArgumentException {
+ if ("persisted_logs".equals(name)) {
+ return new PersistLogSection();
+ }
+ throw new IllegalArgumentException("Section not found: " + name);
+ }
+
+ private String nextArgRequired() {
+ if (!mArgsIterator.hasNext()) {
+ throw new IllegalArgumentException(
+ "Arg required after \"" + mArgs.get(mArgsIterator.previousIndex()) + "\"");
+ }
+ return mArgsIterator.next();
+ }
+
+ /**
+ * Print the given message to stderr, also log it if asked to (set by -l cmd arg).
+ */
+ public static void log(int priority, String tag, String msg) {
+ System.err.println(tag + ": " + msg);
+ if (sLog) {
+ Log.println(priority, tag, msg);
+ }
+ }
+
+ /**
+ * Command-line entry point.
+ *
+ * @param args The command-line arguments
+ */
+ public static void main(String[] args) {
+ if (args.length == 0) {
+ showUsage(System.err);
+ System.exit(0);
+ }
+ IncidentHelper incidentHelper = new IncidentHelper(args);
+ try {
+ incidentHelper.run(args);
+ } catch (IllegalArgumentException e) {
+ showUsage(System.err);
+ System.err.println();
+ e.printStackTrace(System.err);
+ if (sLog) {
+ Log.e(TAG, "Error: ", e);
+ }
+ } catch (Exception e) {
+ e.printStackTrace(System.err);
+ if (sLog) {
+ Log.e(TAG, "Error: ", e);
+ }
+ System.exit(1);
+ }
+ }
+}
diff --git a/cmds/incident_helper/java/com/android/commands/incident/Section.java b/cmds/incident_helper/java/com/android/commands/incident/Section.java
new file mode 100644
index 0000000..1c8c657
--- /dev/null
+++ b/cmds/incident_helper/java/com/android/commands/incident/Section.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.commands.incident;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+
+/** Section interface used by {@link IncidentHelper}. */
+public interface Section {
+ /**
+ * Writes protobuf wire format to out, optionally reads data from in, with supplied args.
+ */
+ void run(InputStream in, OutputStream out, List<String> args) throws ExecutionException;
+}
diff --git a/cmds/incident_helper/java/com/android/commands/incident/sections/PersistLogSection.java b/cmds/incident_helper/java/com/android/commands/incident/sections/PersistLogSection.java
new file mode 100644
index 0000000..f9d2e79
--- /dev/null
+++ b/cmds/incident_helper/java/com/android/commands/incident/sections/PersistLogSection.java
@@ -0,0 +1,287 @@
+/*
+ * 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.commands.incident.sections;
+
+import android.util.Log;
+import android.util.PersistedLogProto;
+import android.util.TextLogEntry;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.commands.incident.ExecutionException;
+import com.android.commands.incident.IncidentHelper;
+import com.android.commands.incident.Section;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Comparator;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+/** PersistLogSection reads persisted logs and parses them into a PersistedLogProto. */
+public class PersistLogSection implements Section {
+ private static final String TAG = "IH_PersistLog";
+ private static final String LOG_DIR = "/data/misc/logd/";
+ // Persist log files are named logcat, logcat.001, logcat.002, logcat.003, ...
+ private static final Pattern LOG_FILE_RE = Pattern.compile("logcat(\\.\\d+)?");
+ private static final Pattern BUFFER_BEGIN_RE =
+ Pattern.compile("--------- (?:beginning of|switch to) (.*)");
+ private static final Map<String, Long> SECTION_NAME_TO_ID = new HashMap<>();
+ private static final Map<Character, Integer> LOG_PRIORITY_MAP = new HashMap<>();
+ private static final String DEFAULT_BUFFER = "main";
+
+ static {
+ SECTION_NAME_TO_ID.put("main", PersistedLogProto.MAIN_LOGS);
+ SECTION_NAME_TO_ID.put("radio", PersistedLogProto.RADIO_LOGS);
+ SECTION_NAME_TO_ID.put("events", PersistedLogProto.EVENTS_LOGS);
+ SECTION_NAME_TO_ID.put("system", PersistedLogProto.SYSTEM_LOGS);
+ SECTION_NAME_TO_ID.put("crash", PersistedLogProto.CRASH_LOGS);
+ SECTION_NAME_TO_ID.put("kernel", PersistedLogProto.KERNEL_LOGS);
+ }
+
+ static {
+ LOG_PRIORITY_MAP.put('V', TextLogEntry.LOG_VERBOSE);
+ LOG_PRIORITY_MAP.put('D', TextLogEntry.LOG_DEBUG);
+ LOG_PRIORITY_MAP.put('I', TextLogEntry.LOG_INFO);
+ LOG_PRIORITY_MAP.put('W', TextLogEntry.LOG_WARN);
+ LOG_PRIORITY_MAP.put('E', TextLogEntry.LOG_ERROR);
+ LOG_PRIORITY_MAP.put('F', TextLogEntry.LOG_FATAL);
+ LOG_PRIORITY_MAP.put('S', TextLogEntry.LOG_SILENT);
+ }
+
+ /**
+ * Caches dates at 00:00:00 to epoch second elapsed conversion. There are only a few different
+ * dates in persisted logs in one device, and constructing DateTime object is relatively
+ * expensive.
+ */
+ private Map<Integer, Long> mEpochTimeCache = new HashMap<>();
+ private ProtoOutputStream mProto;
+ private long mCurrFieldId;
+ private long mMaxBytes = Long.MAX_VALUE;
+
+ @Override
+ public void run(InputStream in, OutputStream out, List<String> args) throws ExecutionException {
+ parseArgs(args);
+ Path logDirPath = Paths.get(LOG_DIR);
+ if (!Files.exists(logDirPath)) {
+ IncidentHelper.log(Log.WARN, TAG, "Skip dump. " + logDirPath + " does not exist.");
+ return;
+ }
+ if (!Files.isReadable(logDirPath)) {
+ IncidentHelper.log(Log.WARN, TAG, "Skip dump. " + logDirPath + " is not readable.");
+ return;
+ }
+ mProto = new ProtoOutputStream(out);
+ setCurrentSection(DEFAULT_BUFFER);
+ final Matcher logFileRe = LOG_FILE_RE.matcher("");
+ // Need to process older log files first and write logs to proto in chronological order
+ // But we want to process only the latest ones if there is a size limit
+ try (Stream<File> stream = Files.list(logDirPath).map(Path::toFile)
+ .filter(f -> !f.isDirectory() && match(logFileRe, f.getName()) != null)
+ .sorted(Comparator.comparingLong(File::lastModified).reversed())) {
+ Iterator<File> iter = stream.iterator();
+ List<File> filesToProcess = new ArrayList<>();
+ long sumBytes = 0;
+ while (iter.hasNext()) {
+ File file = iter.next();
+ sumBytes += file.length();
+ if (sumBytes > mMaxBytes) {
+ break;
+ }
+ filesToProcess.add(file);
+ }
+ IncidentHelper.log(Log.INFO, TAG, "Limit # log files to " + filesToProcess.size());
+ filesToProcess.stream()
+ .sorted(Comparator.comparingLong(File::lastModified))
+ .forEachOrdered(this::processFile);
+ } catch (IOException e) {
+ throw new ExecutionException(e);
+ } finally {
+ mProto.flush();
+ }
+ IncidentHelper.log(Log.DEBUG, TAG, "Bytes written: " + mProto.getBytes().length);
+ }
+
+ private void parseArgs(List<String> args) {
+ Iterator<String> iter = args.iterator();
+ while (iter.hasNext()) {
+ String arg = iter.next();
+ if ("--limit".equals(arg) && iter.hasNext()) {
+ String sizeStr = iter.next().toLowerCase();
+ if (sizeStr.endsWith("mb")) {
+ mMaxBytes = Long.parseLong(sizeStr.replace("mb", "")) * 1024 * 1024;
+ } else if (sizeStr.endsWith("kb")) {
+ mMaxBytes = Long.parseLong(sizeStr.replace("kb", "")) * 1024;
+ } else {
+ mMaxBytes = Long.parseLong(sizeStr);
+ }
+ } else {
+ throw new IllegalArgumentException("Unknown argument: " + arg);
+ }
+ }
+ }
+
+ private void processFile(File file) {
+ final Matcher bufferBeginRe = BUFFER_BEGIN_RE.matcher("");
+ try (BufferedReader reader = Files.newBufferedReader(file.toPath(),
+ StandardCharsets.UTF_8)) {
+ String line;
+ Matcher m;
+ while ((line = reader.readLine()) != null) {
+ if ((m = match(bufferBeginRe, line)) != null) {
+ setCurrentSection(m.group(1));
+ continue;
+ }
+ parseLine(line);
+ }
+ } catch (IOException e) {
+ // Non-fatal error. We can skip and still process other files.
+ IncidentHelper.log(Log.WARN, TAG, "Error reading \"" + file + "\": " + e.getMessage());
+ }
+ IncidentHelper.log(Log.DEBUG, TAG, "Finished reading " + file);
+ }
+
+ private void setCurrentSection(String sectionName) {
+ Long sectionId = SECTION_NAME_TO_ID.get(sectionName);
+ if (sectionId == null) {
+ IncidentHelper.log(Log.WARN, TAG, "Section does not exist: " + sectionName);
+ sectionId = SECTION_NAME_TO_ID.get(DEFAULT_BUFFER);
+ }
+ mCurrFieldId = sectionId;
+ }
+
+ /**
+ * Parse a log line in the following format:
+ * 01-01 15:01:47.723501 2738 2895 I Exp_TAG: example log line
+ *
+ * It does not use RegExp for performance reasons. Using this RegExp "(\\d{2})-(\\d{2})\\s
+ * (\\d{2}):(\\d{2}):(\\d{2}).(\\d{6})\\s+(\\d+)\\s+(\\d+)\\s+(.)\\s+(.*?):\\s(.*)" is twice as
+ * slow as the current approach.
+ */
+ private void parseLine(String line) {
+ long token = mProto.start(mCurrFieldId);
+ try {
+ mProto.write(TextLogEntry.SEC, getEpochSec(line));
+ // Nanosec is 15th to 20th digits of "10-01 02:57:27.710652" times 1000
+ mProto.write(TextLogEntry.NANOSEC, parseInt(line, 15, 21) * 1000L);
+
+ int start = nextNonBlank(line, 21);
+ int end = line.indexOf(' ', start + 1);
+ mProto.write(TextLogEntry.PID, parseInt(line, start, end));
+
+ start = nextNonBlank(line, end);
+ end = line.indexOf(' ', start + 1);
+ mProto.write(TextLogEntry.TID, parseInt(line, start, end));
+
+ start = nextNonBlank(line, end);
+ char priority = line.charAt(start);
+ mProto.write(TextLogEntry.PRIORITY,
+ LOG_PRIORITY_MAP.getOrDefault(priority, TextLogEntry.LOG_DEFAULT));
+
+ start = nextNonBlank(line, start + 1);
+ end = line.indexOf(": ", start);
+ mProto.write(TextLogEntry.TAG, line.substring(start, end).trim());
+ mProto.write(TextLogEntry.LOG, line.substring(Math.min(end + 2, line.length())));
+ } catch (RuntimeException e) {
+ // Error reporting is likely piped to /dev/null. Inserting it into the proto to make
+ // it more useful.
+ mProto.write(TextLogEntry.SEC, System.currentTimeMillis() / 1000);
+ mProto.write(TextLogEntry.PRIORITY, TextLogEntry.LOG_ERROR);
+ mProto.write(TextLogEntry.TAG, TAG);
+ mProto.write(TextLogEntry.LOG,
+ "Error parsing \"" + line + "\"" + ": " + e.getMessage());
+ }
+ mProto.end(token);
+ }
+
+ // ============== Below are util methods to parse log lines ==============
+
+ private static int nextNonBlank(String line, int start) {
+ for (int i = start; i < line.length(); i++) {
+ if (line.charAt(i) != ' ') {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Gets the epoch second from the line string. Line starts with a fixed-length timestamp like
+ * "10-01 02:57:27.710652"
+ */
+ private long getEpochSec(String line) {
+ int month = getDigit(line, 0) * 10 + getDigit(line, 1);
+ int day = getDigit(line, 3) * 10 + getDigit(line, 4);
+
+ int mmdd = month * 100 + day;
+ long epochSecBase = mEpochTimeCache.computeIfAbsent(mmdd, (key) -> {
+ final GregorianCalendar calendar = new GregorianCalendar();
+ calendar.set(Calendar.MONTH, (month + 12 - 1) % 12);
+ calendar.set(Calendar.DAY_OF_MONTH, day);
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.MILLISECOND, 0);
+ // Date in log entries can never be in the future. If it happens, it means we are off
+ // by one year.
+ if (calendar.getTimeInMillis() > System.currentTimeMillis()) {
+ calendar.roll(Calendar.YEAR, /*amount=*/-1);
+ }
+ return calendar.getTimeInMillis() / 1000;
+ });
+
+ int hh = getDigit(line, 6) * 10 + getDigit(line, 7);
+ int mm = getDigit(line, 9) * 10 + getDigit(line, 10);
+ int ss = getDigit(line, 12) * 10 + getDigit(line, 13);
+ return epochSecBase + hh * 3600 + mm * 60 + ss;
+ }
+
+ private static int parseInt(String line, /*inclusive*/ int start, /*exclusive*/ int end) {
+ int num = 0;
+ for (int i = start; i < end; i++) {
+ num = num * 10 + getDigit(line, i);
+ }
+ return num;
+ }
+
+ private static int getDigit(String str, int pos) {
+ int digit = str.charAt(pos) - '0';
+ if (digit < 0 || digit > 9) {
+ throw new NumberFormatException("'" + str.charAt(pos) + "' is not a digit.");
+ }
+ return digit;
+ }
+
+ private static Matcher match(Matcher matcher, String text) {
+ matcher.reset(text);
+ return matcher.matches() ? matcher : null;
+ }
+}
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 1ca19c34..ada2f2d 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -1312,6 +1312,13 @@
return Status::ok();
}
+Status StatsService::unregisterNativePullAtomCallback(int32_t atomTag) {
+ VLOG("StatsService::unregisterNativePullAtomCallback called.");
+ int32_t uid = IPCThreadState::self()->getCallingUid();
+ mPullerManager->UnregisterPullAtomCallback(uid, atomTag);
+ return Status::ok();
+}
+
Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& trainNameIn,
const int64_t trainVersionCodeIn,
const int options,
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index c9a9072..7990e5e 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -203,6 +203,11 @@
virtual Status unregisterPullAtomCallback(int32_t uid, int32_t atomTag) override;
/**
+ * Binder call to unregister any existing callback for the given atom and calling uid.
+ */
+ virtual Status unregisterNativePullAtomCallback(int32_t atomTag) override;
+
+ /**
* Binder call to log BinaryPushStateChanged atom.
*/
virtual Status sendBinaryPushStateChangedAtom(
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index cbece78..4372e22 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -53,6 +53,7 @@
import "frameworks/base/core/proto/android/stats/mediaprovider/mediaprovider_enums.proto";
import "frameworks/base/core/proto/android/stats/storage/storage_enums.proto";
import "frameworks/base/core/proto/android/stats/style/style_enums.proto";
+import "frameworks/base/core/proto/android/stats/sysui/notification_enums.proto";
import "frameworks/base/core/proto/android/telecomm/enums.proto";
import "frameworks/base/core/proto/android/telephony/enums.proto";
import "frameworks/base/core/proto/android/view/enums.proto";
@@ -324,8 +325,6 @@
228 [(allow_from_any_uid) = true];
PerfettoUploaded perfetto_uploaded = 229 [(module) = "perfetto"];
VmsClientConnectionStateChanged vms_client_connection_state_changed = 230;
- GpsLocationStatusReported gps_location_status_reported = 231;
- GpsTimeToFirstFixReported gps_time_to_first_fix_reported = 232;
MediaProviderScanEvent media_provider_scan_event = 233 [(module) = "mediaprovider"];
MediaProviderDeletionEvent media_provider_deletion_event = 234 [(module) = "mediaprovider"];
MediaProviderPermissionEvent media_provider_permission_event =
@@ -339,10 +338,13 @@
BootTimeEventUtcTime boot_time_event_utc_time_reported = 241;
BootTimeEventErrorCode boot_time_event_error_code_reported = 242;
UserspaceRebootReported userspace_reboot_reported = 243;
+ NotificationReported notification_reported = 244;
+ NotificationPanelReported notification_panel_reported = 245;
+ NotificationChannelModified notification_panel_modified = 246;
}
// Pulled events will start at field 10000.
- // Next: 10070
+ // Next: 10071
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000;
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -399,7 +401,7 @@
ExternalStorageInfo external_storage_info = 10053;
GpuStatsGlobalInfo gpu_stats_global_info = 10054;
GpuStatsAppInfo gpu_stats_app_info = 10055;
- SystemIonHeapSize system_ion_heap_size = 10056;
+ SystemIonHeapSize system_ion_heap_size = 10056 [deprecated = true];
AppsOnExternalStorageInfo apps_on_external_storage_info = 10057;
FaceSettings face_settings = 10058;
CoolingDevice cooling_device = 10059;
@@ -413,6 +415,7 @@
DangerousPermissionStateSampled dangerous_permission_state_sampled = 10067;
GraphicsStats graphics_stats = 10068;
RuntimeAppOpsAccess runtime_app_ops_access = 10069;
+ IonHeapSize ion_heap_size = 10070;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -737,27 +740,6 @@
optional android.server.location.GpsSignalQualityEnum level = 1;
}
-/**
- * Gps location status report
- *
- * Logged from:
- * /frameworks/base/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
- */
-message GpsLocationStatusReported {
- // Boolean stating if location was acquired
- optional bool location_success = 1;
-}
-
-/**
- * Gps log time to first fix report
- *
- * Logged from:
- * /frameworks/base/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
- */
-message GpsTimeToFirstFixReported {
- // int32 reporting the time to first fix in milliseconds
- optional int32 time_to_first_fix_millis = 1;
-}
/**
* Logs when a sync manager sync state changes.
@@ -3309,6 +3291,10 @@
* this button" or "this dialog was displayed".
* Keep the UI event stream clean: don't use for system or background events.
* Log using the UiEventLogger wrapper - don't write with the StatsLog API directly.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/
+ * frameworks/base/packages/SystemUI/src/com/android/systemui/
*/
message UiEventReported {
// The event_id.
@@ -3320,6 +3306,122 @@
}
/**
+ * Reports a notification was created or updated.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/notification/
+ */
+message NotificationReported {
+ // The event_id (as for UiEventReported).
+ optional int32 event_id = 1;
+ // The notifying app's uid and package.
+ optional int32 uid = 2 [(is_uid) = true];
+ optional string package_name = 3;
+ // A small system-assigned identifier for the notification.
+ // Locally probably-unique, but expect collisions across users and/or days.
+ optional int32 instance_id = 4;
+ // The app-assigned notification ID and tag
+ optional int32 notification_id = 5;
+ optional string notification_tag = 6;
+ optional string channel_id = 7; // App-assigned channel ID
+
+ // Grouping information
+ optional string group_id = 8; // Group the notification currently belongs to
+ optional int32 group_instance_id = 9; // Instance_id of the group-summary notification
+ optional bool is_group_summary = 10; // Tags the group-summary notification
+
+ // Attributes
+ optional string category = 11; // App-assigned notification category (API-defined strings)
+ optional int32 style = 12; // App-assigned notification style
+ optional int32 num_people = 13; // Number of Person records attached to the notification
+
+ // Ordering, importance and interruptiveness
+
+ optional int32 position = 14; // Position in NotificationManager's list
+
+ optional android.stats.sysui.NotificationImportance importance = 15;
+ optional int32 alerting = 16; // Bitfield, 1=buzz 2=beep 4=blink
+
+ enum NotificationImportanceExplanation {
+ IMPORTANCE_EXPLANATION_UNKNOWN = 0;
+ IMPORTANCE_EXPLANATION_APP = 1; // App-specified channel importance.
+ IMPORTANCE_EXPLANATION_USER = 2; // User-specified channel importance.
+ IMPORTANCE_EXPLANATION_ASST = 3; // Notification Assistant override.
+ IMPORTANCE_EXPLANATION_SYSTEM = 4; // System override.
+ // Like _APP, but based on pre-channels priority signal.
+ IMPORTANCE_EXPLANATION_APP_PRE_CHANNELS = 5;
+ }
+
+ optional NotificationImportanceExplanation importance_source = 17;
+ optional android.stats.sysui.NotificationImportance importance_initial = 18;
+ optional NotificationImportanceExplanation importance_initial_source = 19;
+ optional android.stats.sysui.NotificationImportance importance_asst = 20;
+ optional int32 assistant_hash = 21;
+ optional float assistant_ranking_score = 22;
+}
+
+message Notification {
+ // The notifying app's uid and package.
+ optional int32 uid = 1 [(is_uid) = true];
+ optional string package_name = 2;
+ // A small system-assigned identifier for the notification.
+ optional int32 instance_id = 3;
+
+ // Grouping information.
+ optional int32 group_instance_id = 4;
+ optional bool is_group_summary = 5;
+
+ // The section of the shade that the notification is in.
+ // See NotificationSectionsManager.PriorityBucket.
+ enum NotificationSection {
+ SECTION_UNKNOWN = 0;
+ SECTION_PEOPLE = 1;
+ SECTION_ALERTING = 2;
+ SECTION_SILENT = 3;
+ }
+ optional NotificationSection section = 6;
+}
+
+message NotificationList {
+ repeated Notification notifications = 1; // An ordered sequence of notifications.
+}
+
+/**
+ * Reports a notification panel was displayed, e.g. from the lockscreen or status bar.
+ *
+ * Logged from:
+ * frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/
+ */
+message NotificationPanelReported {
+ // The event_id (as for UiEventReported).
+ optional int32 event_id = 1;
+ optional int32 num_notifications = 2;
+ // The notifications in the panel, in the order that they appear there.
+ optional NotificationList notifications = 3 [(log_mode) = MODE_BYTES];
+}
+
+/**
+ * Reports a notification channel, or channel group, was created, updated, or deleted.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/notification/
+ */
+message NotificationChannelModified {
+ // The event_id (as for UiEventReported).
+ optional int32 event_id = 1;
+ // The notifying app's uid and package.
+ optional int32 uid = 2 [(is_uid) = true];
+ optional string package_name = 3;
+ // App-assigned notification channel ID or channel-group ID
+ optional string channel_id = 4;
+ // Previous importance setting, if applicable
+ optional android.stats.sysui.NotificationImportance old_importance = 5;
+ // New importance setting
+ optional android.stats.sysui.NotificationImportance importance = 6;
+}
+
+
+/**
* Logs when a biometric acquire event occurs.
*
* Logged from:
@@ -3507,12 +3609,14 @@
INSTALL_FAILURE_DOWNLOAD = 23;
INSTALL_FAILURE_STATE_MISMATCH = 24;
INSTALL_FAILURE_COMMIT = 25;
+ REBOOT_TRIGGERED = 26;
}
optional State state = 6;
// Possible experiment ids for monitoring this push.
optional TrainExperimentIds experiment_ids = 7 [(log_mode) = MODE_BYTES];
// user id
optional int32 user_id = 8;
+ optional int32 reason = 9;
}
/* Test atom, is not logged anywhere */
@@ -6804,6 +6908,7 @@
INSTALL_FAILURE_DOWNLOAD = 23;
INSTALL_FAILURE_STATE_MISMATCH = 24;
INSTALL_FAILURE_COMMIT = 25;
+ REBOOT_TRIGGERED = 26;
}
optional Status status = 4;
}
@@ -6946,11 +7051,27 @@
* Pulled from StatsCompanionService.
*/
message SystemIonHeapSize {
+ // Deprecated due to limited support of ion stats in debugfs.
+ // Use `IonHeapSize` instead.
+ option deprecated = true;
+
// Size of the system ion heap in bytes.
+ // Read from debugfs.
optional int64 size_in_bytes = 1;
}
/*
+ * Logs the total size of the ion heap.
+ *
+ * Pulled from StatsCompanionService.
+ */
+message IonHeapSize {
+ // Total size of all ion heaps in kilobytes.
+ // Read from: /sys/kernel/ion/total_heaps_kb.
+ optional int32 total_size_kb = 1;
+}
+
+/*
* Logs the per-process size of the system ion heap.
*
* Pulled from StatsCompanionService.
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp
index 0e6b677..e5a83a2 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.cpp
+++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp
@@ -42,7 +42,7 @@
}
bool StatsCallbackPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
- VLOG("StatsCallbackPuller called for tag %d", mTagId)
+ VLOG("StatsCallbackPuller called for tag %d", mTagId);
if(mCallback == nullptr) {
ALOGW("No callback registered");
return false;
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index b5920cb..d5cda85 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -68,14 +68,6 @@
{{.atomTag = android::util::ON_DEVICE_POWER_MEASUREMENT},
{.puller = new PowerStatsPuller()}},
- // wifi_activity_energy_info
- {{.atomTag = android::util::WIFI_ACTIVITY_INFO},
- {.puller = new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
-
- // modem_activity_info
- {{.atomTag = android::util::MODEM_ACTIVITY_INFO},
- {.puller = new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
-
// system_elapsed_realtime
{{.atomTag = android::util::SYSTEM_ELAPSED_REALTIME},
{.coolDownNs = NS_PER_SEC,
@@ -103,45 +95,6 @@
{{.atomTag = android::util::BATTERY_CYCLE_COUNT},
{.puller = new ResourceHealthManagerPuller(android::util::BATTERY_CYCLE_COUNT)}},
- // process_memory_state
- {{.atomTag = android::util::PROCESS_MEMORY_STATE},
- {.additiveFields = {4, 5, 6, 7, 8},
- .puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
-
- // process_memory_high_water_mark
- {{.atomTag = android::util::PROCESS_MEMORY_HIGH_WATER_MARK},
- {.puller =
- new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
-
- // process_memory_snapshot
- {{.atomTag = android::util::PROCESS_MEMORY_SNAPSHOT},
- {.puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_SNAPSHOT)}},
-
- // system_ion_heap_size
- {{.atomTag = android::util::SYSTEM_ION_HEAP_SIZE},
- {.puller = new StatsCompanionServicePuller(android::util::SYSTEM_ION_HEAP_SIZE)}},
-
- // process_system_ion_heap_size
- {{.atomTag = android::util::PROCESS_SYSTEM_ION_HEAP_SIZE},
- {.puller = new StatsCompanionServicePuller(android::util::PROCESS_SYSTEM_ION_HEAP_SIZE)}},
-
- // temperature
- {{.atomTag = android::util::TEMPERATURE},
- {.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}},
-
- // cooling_device
- {{.atomTag = android::util::COOLING_DEVICE},
- {.puller = new StatsCompanionServicePuller(android::util::COOLING_DEVICE)}},
-
- // binder_calls
- {{.atomTag = android::util::BINDER_CALLS},
- {.additiveFields = {4, 5, 6, 8, 12},
- .puller = new StatsCompanionServicePuller(android::util::BINDER_CALLS)}},
-
- // binder_calls_exceptions
- {{.atomTag = android::util::BINDER_CALLS_EXCEPTIONS},
- {.puller = new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}},
-
// looper_stats
{{.atomTag = android::util::LOOPER_STATS},
{.additiveFields = {5, 6, 7, 8, 9},
@@ -193,20 +146,6 @@
{.additiveFields = {7, 9, 11, 13, 15, 17, 19, 21},
.puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}},
- // DeviceCalculatedPowerUse.
- {{.atomTag = android::util::DEVICE_CALCULATED_POWER_USE},
- {.puller = new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}},
-
- // DeviceCalculatedPowerBlameUid.
- {{.atomTag = android::util::DEVICE_CALCULATED_POWER_BLAME_UID},
- {.puller = new StatsCompanionServicePuller(
- android::util::DEVICE_CALCULATED_POWER_BLAME_UID)}},
-
- // DeviceCalculatedPowerBlameOther.
- {{.atomTag = android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER},
- {.puller = new StatsCompanionServicePuller(
- android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}},
-
// DebugElapsedClock.
{{.atomTag = android::util::DEBUG_ELAPSED_CLOCK},
{.additiveFields = {1, 2, 3, 4},
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 070a4f8..d952be5 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2539,7 +2539,8 @@
mCalled = true;
if (mAutoFillResetNeeded) {
- getAutofillManager().onInvisibleForAutofill();
+ // If stopped without changing the configurations, the response should expire.
+ getAutofillManager().onInvisibleForAutofill(!mChangingConfigurations);
} else if (mIntent != null
&& mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)
&& mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY)) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index c3b07c8..2010cc98 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4064,6 +4064,7 @@
* @hide
*/
@SystemApi
+ @TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public boolean switchUser(@NonNull UserHandle user) {
if (user == null) {
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 70262b0..0c5e67c 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -464,6 +464,9 @@
|| appDir.equals(instrumentedAppDir)) {
outZipPaths.clear();
outZipPaths.add(instrumentationAppDir);
+ if (!instrumentationAppDir.equals(instrumentedAppDir)) {
+ outZipPaths.add(instrumentedAppDir);
+ }
// Only add splits if the app did not request isolated split loading.
if (!aInfo.requestsIsolatedSplitLoading()) {
@@ -472,7 +475,6 @@
}
if (!instrumentationAppDir.equals(instrumentedAppDir)) {
- outZipPaths.add(instrumentedAppDir);
if (instrumentedSplitAppDirs != null) {
Collections.addAll(outZipPaths, instrumentedSplitAppDirs);
}
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index bdc7b99..5a4622e 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -15,6 +15,8 @@
*/
package android.app;
+import static android.annotation.SystemApi.Client.MODULE_APPS;
+
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -105,6 +107,7 @@
private static final String ATT_ORIG_IMP = "orig_imp";
private static final String ATT_PARENT_CHANNEL = "parent";
private static final String ATT_CONVERSATION_ID = "conv_id";
+ private static final String ATT_DEMOTE = "dem";
private static final String DELIMITER = ",";
/**
@@ -194,6 +197,7 @@
private boolean mImportanceLockedDefaultApp;
private String mParentId = null;
private String mConversationId = null;
+ private boolean mDemoted = false;
/**
* Creates a notification channel.
@@ -260,6 +264,7 @@
mOriginalImportance = in.readInt();
mParentId = in.readString();
mConversationId = in.readString();
+ mDemoted = in.readBoolean();
}
@Override
@@ -317,6 +322,7 @@
dest.writeInt(mOriginalImportance);
dest.writeString(mParentId);
dest.writeString(mConversationId);
+ dest.writeBoolean(mDemoted);
}
/**
@@ -350,9 +356,13 @@
}
/**
+ * Allows users to block notifications sent through this channel, if this channel belongs to
+ * a package that is signed with the system signature. If the channel does not belong to a
+ * package that is signed with the system signature, this method does nothing.
+ * @param blockableSystem if {@code true}, allows users to block notifications on this channel.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi(client = MODULE_APPS)
@TestApi
public void setBlockableSystem(boolean blockableSystem) {
mBlockableSystem = blockableSystem;
@@ -386,8 +396,6 @@
return input;
}
- // Modifiable by apps on channel creation.
-
/**
* @hide
*/
@@ -395,6 +403,8 @@
mId = id;
}
+ // Modifiable by apps on channel creation.
+
/**
* Sets what group this channel belongs to.
*
@@ -767,6 +777,20 @@
}
/**
+ * @hide
+ */
+ public void setDemoted(boolean demoted) {
+ mDemoted = demoted;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean isDemoted() {
+ return mDemoted;
+ }
+
+ /**
* Returns whether the user has chosen the importance of this channel, either to affirm the
* initial selection from the app, or changed it to be higher or lower.
* @see #getImportance()
@@ -829,6 +853,7 @@
setOriginalImportance(safeInt(parser, ATT_ORIG_IMP, DEFAULT_IMPORTANCE));
setConversationId(parser.getAttributeValue(null, ATT_PARENT_CHANNEL),
parser.getAttributeValue(null, ATT_CONVERSATION_ID));
+ setDemoted(safeBool(parser, ATT_DEMOTE, false));
}
@Nullable
@@ -959,6 +984,9 @@
if (getConversationId() != null) {
out.attribute(null, ATT_CONVERSATION_ID, getConversationId());
}
+ if (isDemoted()) {
+ out.attribute(null, ATT_DEMOTE, Boolean.toString(isDemoted()));
+ }
// mImportanceLockedDefaultApp and mImportanceLockedByOEM have a different source of
// truth and so aren't written to this xml file
@@ -1118,7 +1146,8 @@
&& mImportanceLockedDefaultApp == that.mImportanceLockedDefaultApp
&& mOriginalImportance == that.mOriginalImportance
&& Objects.equals(getParentChannelId(), that.getParentChannelId())
- && Objects.equals(getConversationId(), that.getConversationId());
+ && Objects.equals(getConversationId(), that.getConversationId())
+ && isDemoted() == that.isDemoted();
}
@Override
@@ -1129,7 +1158,7 @@
isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(),
getAudioAttributes(), isBlockableSystem(), mAllowBubbles,
mImportanceLockedByOEM, mImportanceLockedDefaultApp, mOriginalImportance,
- mParentId, mConversationId);
+ mParentId, mConversationId, mDemoted);
result = 31 * result + Arrays.hashCode(mVibration);
return result;
}
@@ -1176,7 +1205,8 @@
+ ", mImportanceLockedDefaultApp=" + mImportanceLockedDefaultApp
+ ", mOriginalImp=" + mOriginalImportance
+ ", mParent=" + mParentId
- + ", mConversationId=" + mConversationId;
+ + ", mConversationId=" + mConversationId
+ + ", mDemoted=" + mDemoted;
}
/** @hide */
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index d23754e..7ab85a4 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -1208,8 +1208,7 @@
WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null;
if (r != null) {
- applyConfigurationToResourcesLocked(config, compat, tmpConfig,
- defaultDisplayMetrics, key, r);
+ applyConfigurationToResourcesLocked(config, compat, tmpConfig, key, r);
} else {
mResourceImpls.removeAt(i);
}
@@ -1224,8 +1223,7 @@
}
applyConfigurationToResourcesLocked(config, compat, tmpConfig,
- defaultDisplayMetrics, resourcesWithLoaders.resourcesKey(),
- resources.getImpl());
+ resourcesWithLoaders.resourcesKey(), resources.getImpl());
}
return changes != 0;
@@ -1236,40 +1234,33 @@
private void applyConfigurationToResourcesLocked(@NonNull Configuration config,
@Nullable CompatibilityInfo compat, Configuration tmpConfig,
- DisplayMetrics defaultDisplayMetrics, ResourcesKey key, ResourcesImpl resourcesImpl) {
+ ResourcesKey key, ResourcesImpl resourcesImpl) {
if (DEBUG || DEBUG_CONFIGURATION) {
Slog.v(TAG, "Changing resources "
+ resourcesImpl + " config to: " + config);
}
int displayId = key.mDisplayId;
- boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
- DisplayMetrics dm = defaultDisplayMetrics;
final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
- if (!isDefaultDisplay || hasOverrideConfiguration) {
- tmpConfig.setTo(config);
+ tmpConfig.setTo(config);
- // Get new DisplayMetrics based on the DisplayAdjustments given
- // to the ResourcesImpl. Update a copy if the CompatibilityInfo
- // changed, because the ResourcesImpl object will handle the
- // update internally.
- DisplayAdjustments daj = resourcesImpl.getDisplayAdjustments();
- if (compat != null) {
- daj = new DisplayAdjustments(daj);
- daj.setCompatibilityInfo(compat);
- }
- dm = getDisplayMetrics(displayId, daj);
-
- if (!isDefaultDisplay) {
- applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
- }
-
- if (hasOverrideConfiguration) {
- tmpConfig.updateFrom(key.mOverrideConfiguration);
- }
- resourcesImpl.updateConfiguration(tmpConfig, dm, compat);
- } else {
- resourcesImpl.updateConfiguration(config, dm, compat);
+ // Get new DisplayMetrics based on the DisplayAdjustments given to the ResourcesImpl. Update
+ // a copy if the CompatibilityInfo changed, because the ResourcesImpl object will handle the
+ // update internally.
+ DisplayAdjustments daj = resourcesImpl.getDisplayAdjustments();
+ if (compat != null) {
+ daj = new DisplayAdjustments(daj);
+ daj.setCompatibilityInfo(compat);
}
+ daj.setConfiguration(config);
+ DisplayMetrics dm = getDisplayMetrics(displayId, daj);
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
+ }
+
+ if (hasOverrideConfiguration) {
+ tmpConfig.updateFrom(key.mOverrideConfiguration);
+ }
+ resourcesImpl.updateConfiguration(tmpConfig, dm, compat);
}
/**
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index 6df92a7..a34be5c 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -75,9 +75,15 @@
mLifecycleSequence.clear();
if (finish >= start) {
- // just go there
- for (int i = start + 1; i <= finish; i++) {
- mLifecycleSequence.add(i);
+ if (start == ON_START && finish == ON_STOP) {
+ // A case when we from start to stop state soon, we don't need to go
+ // through the resumed, paused state.
+ mLifecycleSequence.add(ON_STOP);
+ } else {
+ // just go there
+ for (int i = start + 1; i <= finish; i++) {
+ mLifecycleSequence.add(i);
+ }
}
} else { // finish < start, can't just cycle down
if (start == ON_PAUSE && finish == ON_RESUME) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 3860508..679de8a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1827,6 +1827,7 @@
*/
@RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
@SystemApi
+ @TestApi
public void startActivityAsUser(@RequiresPermission @NonNull Intent intent,
@NonNull UserHandle user) {
throw new RuntimeException("Not implemented. Must override in a subclass.");
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 5ca50c2..1f502a1 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3379,6 +3379,10 @@
* etc. This library is versioned and backwards compatible. Clients
* should check its version via {@link android.ext.services.Version
* #getVersionCode()} and avoid calling APIs added in later versions.
+ * <p>
+ * This shared library no longer exists since Android R.
+ *
+ * @see #getServicesSystemSharedLibraryPackageName()
*
* @hide
*/
@@ -4745,6 +4749,9 @@
/**
* Get the name of the package hosting the services shared library.
+ * <p>
+ * Note that this package is no longer a shared library since Android R. It is now a package
+ * that hosts for a bunch of updatable services that the system binds to.
*
* @return The library host package.
*
diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java
index a001ada..38d3137 100644
--- a/core/java/android/content/pm/parsing/ApkParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkParseUtils.java
@@ -65,6 +65,7 @@
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.Trace;
+import android.os.ext.SdkExtensions;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -1615,11 +1616,72 @@
);
}
+ 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;
+ }
+ if (parser.getName().equals("extension-sdk")) {
+ final ParseResult result =
+ parseExtensionSdk(parseInput, parsingPackage, res, parser);
+ if (!result.isSuccess()) {
+ return result;
+ }
+ } else {
+ Slog.w(TAG, "Unknown element under <uses-sdk>: " + parser.getName()
+ + " at " + parsingPackage.getBaseCodePath() + " "
+ + parser.getPositionDescription());
+ }
+ XmlUtils.skipCurrentTag(parser);
+ }
+
parsingPackage.setMinSdkVersion(minSdkVersion)
.setTargetSdkVersion(targetSdkVersion);
}
+ return parseInput.success(parsingPackage);
+ }
- XmlUtils.skipCurrentTag(parser);
+ private static ParseResult parseExtensionSdk(
+ ParseInput parseInput,
+ ParsingPackage parsingPackage,
+ Resources res,
+ XmlResourceParser parser
+ ) throws IOException, XmlPullParserException {
+ TypedArray sa = res.obtainAttributes(parser,
+ com.android.internal.R.styleable.AndroidManifestExtensionSdk);
+ int sdkVersion = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestExtensionSdk_sdkVersion, -1);
+ int minVersion = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestExtensionSdk_minExtensionVersion,
+ -1);
+ sa.recycle();
+
+ if (sdkVersion < 0) {
+ return parseInput.error(
+ PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "<extension-sdk> must specify an sdkVersion >= 0");
+ }
+ if (minVersion < 0) {
+ return parseInput.error(
+ PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "<extension-sdk> must specify minExtensionVersion >= 0");
+ }
+
+ try {
+ int version = SdkExtensions.getExtensionVersion(sdkVersion);
+ if (version < minVersion) {
+ return parseInput.error(
+ PackageManager.INSTALL_FAILED_OLDER_SDK,
+ "Package requires " + sdkVersion + " extension version " + minVersion
+ + " which exceeds device version " + version);
+ }
+ } catch (RuntimeException e) {
+ return parseInput.error(
+ PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "Specified sdkVersion " + sdkVersion + " is not valid");
+ }
return parseInput.success(parsingPackage);
}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index a45648f..7bddc1d 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1539,10 +1539,15 @@
* <p><code>p' = Rp</code></p>
* <p>where <code>p</code> is in the device sensor coordinate system, and
* <code>p'</code> is in the camera-oriented coordinate system.</p>
+ * <p>If {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is UNDEFINED, the quaternion rotation cannot
+ * be accurately represented by the camera device, and will be represented by
+ * default values matching its default facing.</p>
* <p><b>Units</b>:
* Quaternion coefficients</p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
* <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
+ *
+ * @see CameraCharacteristics#LENS_POSE_REFERENCE
*/
@PublicKey
@NonNull
@@ -1577,6 +1582,8 @@
* <p>When {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is GYROSCOPE, then this position is relative to
* the center of the primary gyroscope on the device. The axis definitions are the same as
* with PRIMARY_CAMERA.</p>
+ * <p>When {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is UNDEFINED, this position cannot be accurately
+ * represented by the camera device, and will be represented as <code>(0, 0, 0)</code>.</p>
* <p><b>Units</b>: Meters</p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
* <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
@@ -1714,20 +1721,24 @@
new Key<float[]>("android.lens.radialDistortion", float[].class);
/**
- * <p>The origin for {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}.</p>
+ * <p>The origin for {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}, and the accuracy of
+ * {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation} and {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}.</p>
* <p>Different calibration methods and use cases can produce better or worse results
* depending on the selected coordinate origin.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #LENS_POSE_REFERENCE_PRIMARY_CAMERA PRIMARY_CAMERA}</li>
* <li>{@link #LENS_POSE_REFERENCE_GYROSCOPE GYROSCOPE}</li>
+ * <li>{@link #LENS_POSE_REFERENCE_UNDEFINED UNDEFINED}</li>
* </ul></p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
* <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
+ * @see CameraCharacteristics#LENS_POSE_ROTATION
* @see CameraCharacteristics#LENS_POSE_TRANSLATION
* @see #LENS_POSE_REFERENCE_PRIMARY_CAMERA
* @see #LENS_POSE_REFERENCE_GYROSCOPE
+ * @see #LENS_POSE_REFERENCE_UNDEFINED
*/
@PublicKey
@NonNull
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index ec13a36..2377ccd 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -366,6 +366,20 @@
*/
public static final int LENS_POSE_REFERENCE_GYROSCOPE = 1;
+ /**
+ * <p>The camera device cannot represent the values of {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}
+ * and {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} accurately enough. One such example is a camera device
+ * on the cover of a foldable phone: in order to measure the pose translation and rotation,
+ * some kind of hinge position sensor would be needed.</p>
+ * <p>The value of {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation} must be all zeros, and
+ * {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} must be values matching its default facing.</p>
+ *
+ * @see CameraCharacteristics#LENS_POSE_ROTATION
+ * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+ * @see CameraCharacteristics#LENS_POSE_REFERENCE
+ */
+ public static final int LENS_POSE_REFERENCE_UNDEFINED = 2;
+
//
// Enumeration values for CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
//
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 9b305b32..6f0d135 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -3027,10 +3027,15 @@
* <p><code>p' = Rp</code></p>
* <p>where <code>p</code> is in the device sensor coordinate system, and
* <code>p'</code> is in the camera-oriented coordinate system.</p>
+ * <p>If {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is UNDEFINED, the quaternion rotation cannot
+ * be accurately represented by the camera device, and will be represented by
+ * default values matching its default facing.</p>
* <p><b>Units</b>:
* Quaternion coefficients</p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
* <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
+ *
+ * @see CameraCharacteristics#LENS_POSE_REFERENCE
*/
@PublicKey
@NonNull
@@ -3065,6 +3070,8 @@
* <p>When {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is GYROSCOPE, then this position is relative to
* the center of the primary gyroscope on the device. The axis definitions are the same as
* with PRIMARY_CAMERA.</p>
+ * <p>When {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is UNDEFINED, this position cannot be accurately
+ * represented by the camera device, and will be represented as <code>(0, 0, 0)</code>.</p>
* <p><b>Units</b>: Meters</p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
* <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 799dff9..fb5f136 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -26,6 +26,7 @@
import android.app.KeyguardManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Point;
import android.media.projection.MediaProjection;
import android.os.Handler;
@@ -400,10 +401,10 @@
if (display == null) {
// TODO: We cannot currently provide any override configurations for metrics on displays
// other than the display the context is associated with.
- final Context context = mContext.getDisplayId() == displayId
- ? mContext : mContext.getApplicationContext();
+ final Resources resources = mContext.getDisplayId() == displayId
+ ? mContext.getResources() : null;
- display = mGlobal.getCompatibleDisplay(displayId, context.getResources());
+ display = mGlobal.getCompatibleDisplay(displayId, resources);
if (display != null) {
mDisplays.put(displayId, display);
}
diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java
index 921f0f2..5cb3361 100644
--- a/core/java/android/os/image/DynamicSystemClient.java
+++ b/core/java/android/os/image/DynamicSystemClient.java
@@ -256,9 +256,13 @@
mService.send(msg);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to get status from installation service");
- mExecutor.execute(() -> {
+ if (mExecutor != null) {
+ mExecutor.execute(() -> {
+ mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e);
+ });
+ } else {
mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e);
- });
+ }
}
}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 2583292..5a1ba7f 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -160,6 +160,7 @@
* Grant default permissions to currently active LUI app
* @param packageName The package name for the LUI app
* @param user The user handle
+ * @param executor The executor for the callback
* @param callback The callback provided by caller to be notified when grant completes
* @hide
*/
@@ -181,6 +182,7 @@
* Revoke default permissions to currently active LUI app
* @param packageNames The package names for the LUI apps
* @param user The user handle
+ * @param executor The executor for the callback
* @param callback The callback provided by caller to be notified when grant completes
* @hide
*/
@@ -198,6 +200,72 @@
}
}
+ /**
+ * Grant default permissions to currently active Ims services
+ * @param packageNames The package names for the Ims services
+ * @param user The user handle
+ * @param executor The executor for the callback
+ * @param callback The callback provided by caller to be notified when grant completes
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS)
+ public void grantDefaultPermissionsToEnabledImsServices(
+ @NonNull String[] packageNames, @NonNull UserHandle user,
+ @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
+ try {
+ mPermissionManager.grantDefaultPermissionsToEnabledImsServices(
+ packageNames, user.getIdentifier());
+ executor.execute(() -> callback.accept(true));
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Grant default permissions to currently enabled telephony data services
+ * @param packageNames The package name for the services
+ * @param user The user handle
+ * @param executor The executor for the callback
+ * @param callback The callback provided by caller to be notified when grant completes
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS)
+ public void grantDefaultPermissionsToEnabledTelephonyDataServices(
+ @NonNull String[] packageNames, @NonNull UserHandle user,
+ @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
+ try {
+ mPermissionManager.grantDefaultPermissionsToEnabledTelephonyDataServices(
+ packageNames, user.getIdentifier());
+ executor.execute(() -> callback.accept(true));
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Revoke default permissions to currently active telephony data services
+ * @param packageNames The package name for the services
+ * @param user The user handle
+ * @param executor The executor for the callback
+ * @param callback The callback provided by caller to be notified when revoke completes
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS)
+ public void revokeDefaultPermissionsFromDisabledTelephonyDataServices(
+ @NonNull String[] packageNames, @NonNull UserHandle user,
+ @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
+ try {
+ mPermissionManager.revokeDefaultPermissionsFromDisabledTelephonyDataServices(
+ packageNames, user.getIdentifier());
+ executor.execute(() -> callback.accept(true));
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
private List<SplitPermissionInfo> splitPermissionInfoListToNonParcelableList(
List<SplitPermissionInfoParcelable> parcelableList) {
final int size = parcelableList.size();
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 6650cf2..53f4615 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -424,6 +424,7 @@
* @hide
*/
@SystemApi
+ @TestApi
@NonNull
@RequiresPermission(READ_DEVICE_CONFIG)
public static Properties getProperties(@NonNull String namespace, @NonNull String ... names) {
@@ -593,6 +594,7 @@
* @hide
*/
@SystemApi
+ @TestApi
@RequiresPermission(WRITE_DEVICE_CONFIG)
public static boolean setProperties(@NonNull Properties properties) throws BadConfigException {
ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
@@ -817,6 +819,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public static class BadConfigException extends Exception {}
/**
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index ef8a286..1453608 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -363,7 +363,7 @@
* <p>
* Type: INTEGER (int)
*
- * @see #FLAG_DIR_BLOCKS_TREE
+ * @see #FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE
* @see #FLAG_DIR_PREFERS_GRID
* @see #FLAG_DIR_PREFERS_LAST_MODIFIED
* @see #FLAG_DIR_SUPPORTS_CREATE
@@ -567,7 +567,7 @@
* @see Intent#ACTION_OPEN_DOCUMENT_TREE
* @see #COLUMN_FLAGS
*/
- public static final int FLAG_DIR_BLOCKS_TREE = 1 << 15;
+ public static final int FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE = 1 << 15;
}
/**
diff --git a/core/java/android/provider/SearchIndexablesContract.java b/core/java/android/provider/SearchIndexablesContract.java
index 298628e..8fc13b7 100644
--- a/core/java/android/provider/SearchIndexablesContract.java
+++ b/core/java/android/provider/SearchIndexablesContract.java
@@ -98,16 +98,14 @@
/**
- * Dynamic indexable raw data names.
- *
- * @hide
+ * The raw data name of dynamic index. This is used to compose the index path of provider
+ * for dynamic index.
*/
public static final String DYNAMIC_INDEXABLES_RAW = "dynamic_indexables_raw";
/**
- * ContentProvider path for dynamic indexable raw data.
- *
- * @hide
+ * ContentProvider path for dynamic index. This is used to get the raw data of dynamic index
+ * from provider.
*/
public static final String DYNAMIC_INDEXABLES_RAW_PATH =
SETTINGS + "/" + DYNAMIC_INDEXABLES_RAW;
diff --git a/core/java/android/provider/SearchIndexablesProvider.java b/core/java/android/provider/SearchIndexablesProvider.java
index 68284b4..f4d0cb4 100644
--- a/core/java/android/provider/SearchIndexablesProvider.java
+++ b/core/java/android/provider/SearchIndexablesProvider.java
@@ -204,11 +204,9 @@
* @param projection list of {@link android.provider.SearchIndexablesContract.RawData} columns
* to put into the cursor. If {@code null} all supported columns should be
* included.
- *
- * @hide
*/
@Nullable
- public Cursor queryDynamicRawData(String[] projection) {
+ public Cursor queryDynamicRawData(@Nullable String[] projection) {
// By default no-op;
return null;
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4f84183..1d759af 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -95,6 +95,7 @@
* The Settings provider contains global system-level device preferences.
*/
public final class Settings {
+ private static final boolean DEFAULT_OVERRIDEABLE_BY_RESTORE = false;
// Intent actions for Settings
@@ -2149,6 +2150,11 @@
*/
public static final String CALL_METHOD_FLAGS_KEY = "_flags";
+ /**
+ * @hide - String argument extra to the fast-path call()-based requests
+ */
+ public static final String CALL_METHOD_OVERRIDEABLE_BY_RESTORE_KEY = "_overrideable_by_restore";
+
/** @hide - Private call() method to write to 'system' table */
public static final String CALL_METHOD_PUT_SYSTEM = "PUT_system";
@@ -2517,7 +2523,8 @@
}
public boolean putStringForUser(ContentResolver cr, String name, String value,
- String tag, boolean makeDefault, final int userHandle) {
+ String tag, boolean makeDefault, final int userHandle,
+ boolean overrideableByRestore) {
try {
Bundle arg = new Bundle();
arg.putString(Settings.NameValueTable.VALUE, value);
@@ -2528,6 +2535,9 @@
if (makeDefault) {
arg.putBoolean(CALL_METHOD_MAKE_DEFAULT_KEY, true);
}
+ if (overrideableByRestore) {
+ arg.putBoolean(CALL_METHOD_OVERRIDEABLE_BY_RESTORE_KEY, true);
+ }
IContentProvider cp = mProviderHolder.getProvider(cr);
cp.call(cr.getPackageName(), cr.getFeatureId(),
mProviderHolder.mUri.getAuthority(), mCallSetCommand, name, arg);
@@ -2736,6 +2746,8 @@
public ArrayMap<String, String> getStringsForPrefix(ContentResolver cr, String prefix,
List<String> names) {
+ String namespace = prefix.substring(0, prefix.length() - 1);
+ DeviceConfig.enforceReadPermission(ActivityThread.currentApplication(), namespace);
ArrayMap<String, String> keyValues = new ArrayMap<>();
int currentGeneration = -1;
@@ -3078,10 +3090,36 @@
return putStringForUser(resolver, name, value, resolver.getUserId());
}
+ /**
+ * Store a name/value pair into the database. Values written by this method will be
+ * overridden if a restore happens in the future.
+ *
+ * @param resolver to access the database with
+ * @param name to store
+ * @param value to associate with the name
+ *
+ * @return true if the value was set, false on database errors
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE)
+ @SystemApi
+ public static boolean putString(@NonNull ContentResolver resolver,
+ @NonNull String name, @Nullable String value, boolean overrideableByRestore) {
+ return putStringForUser(resolver, name, value, resolver.getUserId(),
+ overrideableByRestore);
+ }
+
/** @hide */
@UnsupportedAppUsage
public static boolean putStringForUser(ContentResolver resolver, String name, String value,
int userHandle) {
+ return putStringForUser(resolver, name, value, userHandle,
+ DEFAULT_OVERRIDEABLE_BY_RESTORE);
+ }
+
+ private static boolean putStringForUser(ContentResolver resolver, String name, String value,
+ int userHandle, boolean overrideableByRestore) {
if (MOVED_TO_SECURE.contains(name)) {
Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"
+ " to android.provider.Settings.Secure, value is unchanged.");
@@ -3092,7 +3130,8 @@
+ " to android.provider.Settings.Global, value is unchanged.");
return false;
}
- return sNameValueCache.putStringForUser(resolver, name, value, null, false, userHandle);
+ return sNameValueCache.putStringForUser(resolver, name, value, null, false, userHandle,
+ overrideableByRestore);
}
/**
@@ -3416,7 +3455,7 @@
// need to store the adjusted configuration as the initial settings.
Settings.System.putStringForUser(
cr, SYSTEM_LOCALES, outConfig.getLocales().toLanguageTags(),
- userHandle);
+ userHandle, DEFAULT_OVERRIDEABLE_BY_RESTORE);
}
}
}
@@ -3449,7 +3488,8 @@
int userHandle) {
return Settings.System.putFloatForUser(cr, FONT_SCALE, config.fontScale, userHandle) &&
Settings.System.putStringForUser(
- cr, SYSTEM_LOCALES, config.getLocales().toLanguageTags(), userHandle);
+ cr, SYSTEM_LOCALES, config.getLocales().toLanguageTags(), userHandle,
+ DEFAULT_OVERRIDEABLE_BY_RESTORE);
}
/** @hide */
@@ -5251,6 +5291,24 @@
}
/**
+ * Store a name/value pair into the database. Values written by this method will be
+ * overridden if a restore happens in the future.
+ *
+ * @param resolver to access the database with
+ * @param name to store
+ * @param value to associate with the name
+ * @return true if the value was set, false on database errors
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE)
+ public static boolean putString(ContentResolver resolver, String name,
+ String value, boolean overrideableByRestore) {
+ return putStringForUser(resolver, name, value, /* tag */ null, /* makeDefault */ false,
+ resolver.getUserId(), overrideableByRestore);
+ }
+
+ /**
* Store a name/value pair into the database.
* @param resolver to access the database with
* @param name to store
@@ -5265,22 +5323,23 @@
@UnsupportedAppUsage
public static boolean putStringForUser(ContentResolver resolver, String name, String value,
int userHandle) {
- return putStringForUser(resolver, name, value, null, false, userHandle);
+ return putStringForUser(resolver, name, value, null, false, userHandle,
+ DEFAULT_OVERRIDEABLE_BY_RESTORE);
}
/** @hide */
@UnsupportedAppUsage
public static boolean putStringForUser(@NonNull ContentResolver resolver,
@NonNull String name, @Nullable String value, @Nullable String tag,
- boolean makeDefault, @UserIdInt int userHandle) {
+ boolean makeDefault, @UserIdInt int userHandle, boolean overrideableByRestore) {
if (MOVED_TO_GLOBAL.contains(name)) {
Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.Secure"
+ " to android.provider.Settings.Global");
return Global.putStringForUser(resolver, name, value,
- tag, makeDefault, userHandle);
+ tag, makeDefault, userHandle, DEFAULT_OVERRIDEABLE_BY_RESTORE);
}
return sNameValueCache.putStringForUser(resolver, name, value, tag,
- makeDefault, userHandle);
+ makeDefault, userHandle, overrideableByRestore);
}
/**
@@ -5329,7 +5388,7 @@
@NonNull String name, @Nullable String value, @Nullable String tag,
boolean makeDefault) {
return putStringForUser(resolver, name, value, tag, makeDefault,
- resolver.getUserId());
+ resolver.getUserId(), DEFAULT_OVERRIDEABLE_BY_RESTORE);
}
/**
@@ -12940,7 +12999,29 @@
*/
public static boolean putString(ContentResolver resolver,
String name, String value) {
- return putStringForUser(resolver, name, value, null, false, resolver.getUserId());
+ return putStringForUser(resolver, name, value, null, false, resolver.getUserId(),
+ DEFAULT_OVERRIDEABLE_BY_RESTORE);
+ }
+
+ /**
+ * Store a name/value pair into the database.
+ *
+ * @param resolver to access the database with
+ * @param name to store
+ * @param value to associate with the name
+ * @param tag to associated with the setting.
+ * @param makeDefault whether to make the value the default one.
+ * @param overrideableByRestore whether restore can override this value
+ * @return true if the value was set, false on database errors
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE)
+ public static boolean putString(@NonNull ContentResolver resolver,
+ @NonNull String name, @Nullable String value, @Nullable String tag,
+ boolean makeDefault, boolean overrideableByRestore) {
+ return putStringForUser(resolver, name, value, tag, makeDefault,
+ resolver.getUserId(), overrideableByRestore);
}
/**
@@ -12989,7 +13070,7 @@
@NonNull String name, @Nullable String value, @Nullable String tag,
boolean makeDefault) {
return putStringForUser(resolver, name, value, tag, makeDefault,
- resolver.getUserId());
+ resolver.getUserId(), DEFAULT_OVERRIDEABLE_BY_RESTORE);
}
/**
@@ -13051,13 +13132,14 @@
@UnsupportedAppUsage
public static boolean putStringForUser(ContentResolver resolver,
String name, String value, int userHandle) {
- return putStringForUser(resolver, name, value, null, false, userHandle);
+ return putStringForUser(resolver, name, value, null, false, userHandle,
+ DEFAULT_OVERRIDEABLE_BY_RESTORE);
}
/** @hide */
public static boolean putStringForUser(@NonNull ContentResolver resolver,
@NonNull String name, @Nullable String value, @Nullable String tag,
- boolean makeDefault, @UserIdInt int userHandle) {
+ boolean makeDefault, @UserIdInt int userHandle, boolean overrideableByRestore) {
if (LOCAL_LOGV) {
Log.v(TAG, "Global.putString(name=" + name + ", value=" + value
+ " for " + userHandle);
@@ -13067,10 +13149,10 @@
Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.Global"
+ " to android.provider.Settings.Secure, value is unchanged.");
return Secure.putStringForUser(resolver, name, value, tag,
- makeDefault, userHandle);
+ makeDefault, userHandle, overrideableByRestore);
}
return sNameValueCache.putStringForUser(resolver, name, value, tag,
- makeDefault, userHandle);
+ makeDefault, userHandle, overrideableByRestore);
}
/**
@@ -13937,7 +14019,8 @@
static boolean putString(@NonNull ContentResolver resolver, @NonNull String namespace,
@NonNull String name, @Nullable String value, boolean makeDefault) {
return sNameValueCache.putStringForUser(resolver, createCompositeName(namespace, name),
- value, null, makeDefault, resolver.getUserId());
+ value, null, makeDefault, resolver.getUserId(),
+ DEFAULT_OVERRIDEABLE_BY_RESTORE);
}
/**
@@ -14277,46 +14360,74 @@
/**
* Activity Action: Show setting page to process the addition of Wi-Fi networks to the user's
- * saved network list. The app should send a new intent with an extra that holds a maximum of
- * five {@link android.net.wifi.WifiConfiguration} that specify credentials for the networks to
- * be added to the user's database. The Intent should be sent via the {@link
- * android.app.Activity#startActivityForResult(Intent, int)} API.
+ * saved network list. The app should send a new intent with an extra that holds a maximum
+ * of five {@link android.net.wifi.WifiNetworkSuggestion} that specify credentials for the
+ * networks to be added to the user's database. The Intent should be sent via the
+ * {@link android.app.Activity#startActivityForResult(Intent, int)} API.
* <p>
* Note: The app sending the Intent to add the credentials doesn't get any ownership over the
* newly added network(s). For the Wi-Fi stack, these networks will look like the user
* manually added them from the Settings UI.
* <p>
- * Input: The app should put parcelable array list to
- * {@link android.net.wifi.WifiConfiguration} into the
- * {@link #EXTRA_WIFI_CONFIGURATION_LIST} extra.
+ * Input: The app should put parcelable array list of
+ * {@link android.net.wifi.WifiNetworkSuggestion} into the {@link #EXTRA_WIFI_NETWORK_LIST}
+ * extra.
* <p>
* Output: After {@link android.app.Activity#startActivityForResult(Intent, int)}, the
* callback {@link android.app.Activity#onActivityResult(int, int, Intent)} will have a
* result code {@link android.app.Activity#RESULT_OK} to indicate user pressed the save
* button to save the networks or {@link android.app.Activity#RESULT_CANCELED} to indicate
* that the user rejected the request. Additionally, an integer array list, stored in
- * {@link #EXTRA_WIFI_CONFIGURATION_RESULT_LIST}, will indicate the process result of
- * each network.
+ * {@link #EXTRA_WIFI_NETWORK_RESULT_LIST}, will indicate the process result of each network.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_WIFI_ADD_NETWORKS =
"android.settings.WIFI_ADD_NETWORKS";
/**
- * A bundle extra of {@link #ACTION_WIFI_ADD_NETWORKS} intent action that indicates all the
- * {@link android.net.wifi.WifiConfiguration} that would be saved.
+ * A bundle extra of {@link #ACTION_WIFI_ADD_NETWORKS} intent action that indicates the list
+ * of the {@link android.net.wifi.WifiNetworkSuggestion} elements. The maximum count of the
+ * {@link android.net.wifi.WifiNetworkSuggestion} elements in the list will be five.
+ * <p>
+ * For example:
+ * To provide credentials for one open and one WPA2 networks:
+ *
+ * <pre>{@code
+ * final WifiNetworkSuggestion suggestion1 =
+ * new WifiNetworkSuggestion.Builder()
+ * .setSsid("test111111")
+ * .build();
+ * final WifiNetworkSuggestion suggestion2 =
+ * new WifiNetworkSuggestion.Builder()
+ * .setSsid("test222222")
+ * .setWpa2Passphrase("test123456")
+ * .build();
+ * final List<WifiNetworkSuggestion> suggestionsList = new ArrayList<>;
+ * suggestionsList.add(suggestion1);
+ * suggestionsList.add(suggestion2);
+ * Bundle bundle = new Bundle();
+ * bundle.putParcelableArrayList(Settings.EXTRA_WIFI_NETWORK_LIST,(ArrayList<? extends
+ * Parcelable>) suggestionsList);
+ * final Intent intent = new Intent(Settings.ACTION_WIFI_ADD_NETWORKS);
+ * intent.putExtras(bundle);
+ * startActivityForResult(intent, 0);
+ * }</pre>
*/
- public static final String EXTRA_WIFI_CONFIGURATION_LIST =
- "android.provider.extra.WIFI_CONFIGURATION_LIST";
+ public static final String EXTRA_WIFI_NETWORK_LIST =
+ "android.provider.extra.WIFI_NETWORK_LIST";
/**
* A bundle extra of the result of {@link #ACTION_WIFI_ADD_NETWORKS} intent action that
- * indicates the action result of the saved {@link android.net.wifi.WifiConfiguration}. It's
- * value of AddWifiResult interface, and will be 1:1 mapping to the element in {@link
- * #EXTRA_WIFI_CONFIGURATION_LIST}.
+ * indicates the action result of the saved {@link android.net.wifi.WifiNetworkSuggestion}.
+ * Its value is a list of integers, and all the elements will be 1:1 mapping to the elements
+ * in {@link #EXTRA_WIFI_NETWORK_LIST}, if user press cancel to cancel the add networks
+ * request, then its value will be null.
+ * <p>
+ * Note: The integer value will be one of the {@link #ADD_WIFI_RESULT_SUCCESS},
+ * {@link #ADD_WIFI_RESULT_ADD_OR_UPDATE_FAILED}, or {@link #ADD_WIFI_RESULT_ALREADY_EXISTS}}.
*/
- public static final String EXTRA_WIFI_CONFIGURATION_RESULT_LIST =
- "android.provider.extra.WIFI_CONFIGURATION_RESULT_LIST";
+ public static final String EXTRA_WIFI_NETWORK_RESULT_LIST =
+ "android.provider.extra.WIFI_NETWORK_RESULT_LIST";
/** @hide */
@Retention(RetentionPolicy.SOURCE)
diff --git a/core/java/android/service/watchdog/ExplicitHealthCheckService.java b/core/java/android/service/watchdog/ExplicitHealthCheckService.java
index 619c507..9950143 100644
--- a/core/java/android/service/watchdog/ExplicitHealthCheckService.java
+++ b/core/java/android/service/watchdog/ExplicitHealthCheckService.java
@@ -24,6 +24,7 @@
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -48,7 +49,8 @@
* <p>To extend this class, you must declare the service in your manifest file with the
* {@link android.Manifest.permission#BIND_EXPLICIT_HEALTH_CHECK_SERVICE} permission,
* and include an intent filter with the {@link #SERVICE_INTERFACE} action. In adddition,
- * your implementation must live in {@link PackageManger#SYSTEM_SHARED_LIBRARY_SERVICES}.
+ * your implementation must live in
+ * {@link PackageManager#getServicesSystemSharedLibraryPackageName()}.
* For example:</p>
* <pre>
* <service android:name=".FooExplicitHealthCheckService"
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index c9d3b92..a4fe6aa 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -985,7 +985,7 @@
* TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused.
*/
public void onRegistrationFailed(@NonNull CellIdentity cellIdentity, @NonNull String chosenPlmn,
- @NetworkRegistrationInfo.Domain int domain, int causeCode, int additionalCauseCode) {
+ int domain, int causeCode, int additionalCauseCode) {
// default implementation empty
}
@@ -1277,7 +1277,7 @@
}
public void onRegistrationFailed(@NonNull CellIdentity cellIdentity,
- @NonNull String chosenPlmn, @NetworkRegistrationInfo.Domain int domain,
+ @NonNull String chosenPlmn, int domain,
int causeCode, int additionalCauseCode) {
PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
if (psl == null) return;
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 4dffa62..e25826c 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -701,7 +701,7 @@
*/
public void notifyRegistrationFailed(int slotIndex, int subId,
@NonNull CellIdentity cellIdentity, @NonNull String chosenPlmn,
- @NetworkRegistrationInfo.Domain int domain, int causeCode, int additionalCauseCode) {
+ int domain, int causeCode, int additionalCauseCode) {
try {
sRegistry.notifyRegistrationFailed(slotIndex, subId, cellIdentity,
chosenPlmn, domain, causeCode, additionalCauseCode);
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 9d22d30..eb4af1c 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -59,10 +59,9 @@
DEFAULT_FLAGS.put("settings_wifi_details_datausage_header", "false");
DEFAULT_FLAGS.put("settings_skip_direction_mutable", "true");
DEFAULT_FLAGS.put(SETTINGS_WIFITRACKER2, "false");
- DEFAULT_FLAGS.put("settings_work_profile", "true");
DEFAULT_FLAGS.put("settings_controller_loading_enhancement", "false");
DEFAULT_FLAGS.put("settings_conditionals", "false");
- DEFAULT_FLAGS.put(NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false");
+ DEFAULT_FLAGS.put(NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true");
}
/**
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index f324113..9921bf0 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.DeadSystemException;
@@ -400,7 +401,7 @@
* @param message The message you would like logged.
* @hide
*/
- // @SystemApi(client= SystemApi.Client.MODULE_LIBRARIES) // TODO Uncomment once http://ag/9956147 is in.
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static int logToRadioBuffer(@Level int priority, @Nullable String tag,
@Nullable String message) {
return println_native(LOG_ID_RADIO, priority, tag, message);
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 178b3c0..904c510 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -454,7 +454,7 @@
mResources = res;
mDisplayAdjustments = mResources != null
? new DisplayAdjustments(mResources.getConfiguration())
- : daj != null ? new DisplayAdjustments(daj) : null;
+ : daj != null ? new DisplayAdjustments(daj) : new DisplayAdjustments();
mIsValid = true;
// Cache properties that cannot change as long as the display is valid.
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
new file mode 100644
index 0000000..5c494c1
--- /dev/null
+++ b/core/java/android/view/ImeFocusController.java
@@ -0,0 +1,234 @@
+/*
+ * 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.view;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.annotation.UiThread;
+import android.util.Log;
+import android.view.inputmethod.InputMethodManager;
+
+import com.android.internal.inputmethod.InputMethodDebug;
+import com.android.internal.inputmethod.StartInputFlags;
+import com.android.internal.inputmethod.StartInputReason;
+
+/**
+ * Responsible for IME focus handling inside {@link ViewRootImpl}.
+ * @hide
+ */
+public final class ImeFocusController {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "ImeFocusController";
+
+ private final ViewRootImpl mViewRootImpl;
+ private boolean mHasImeFocus = false;
+ private View mServedView;
+ private View mNextServedView;
+
+ @UiThread
+ ImeFocusController(@NonNull ViewRootImpl viewRootImpl) {
+ mViewRootImpl = viewRootImpl;
+ }
+
+ private InputMethodManagerDelegate getImmDelegate() {
+ return mViewRootImpl.mContext.getSystemService(InputMethodManager.class).getDelegate();
+ }
+
+ @UiThread
+ void onTraversal(boolean hasWindowFocus, WindowManager.LayoutParams windowAttribute) {
+ final boolean hasImeFocus = updateImeFocusable(windowAttribute, false /* force */);
+ if (!hasWindowFocus || isInLocalFocusMode(windowAttribute)) {
+ return;
+ }
+ if (hasImeFocus == mHasImeFocus) {
+ return;
+ }
+ mHasImeFocus = hasImeFocus;
+ if (mHasImeFocus) {
+ onPreWindowFocus(true /* hasWindowFocus */, windowAttribute);
+ onPostWindowFocus(mViewRootImpl.mView.findFocus(), true /* hasWindowFocus */,
+ windowAttribute);
+ }
+ }
+
+ @UiThread
+ void onPreWindowFocus(boolean hasWindowFocus, WindowManager.LayoutParams windowAttribute) {
+ if (!mHasImeFocus || isInLocalFocusMode(windowAttribute)) {
+ return;
+ }
+ if (hasWindowFocus) {
+ getImmDelegate().setCurrentRootView(mViewRootImpl);
+ }
+ }
+
+ @UiThread
+ boolean updateImeFocusable(WindowManager.LayoutParams windowAttribute, boolean force) {
+ final boolean hasImeFocus = WindowManager.LayoutParams.mayUseInputMethod(
+ windowAttribute.flags);
+ if (force) {
+ mHasImeFocus = hasImeFocus;
+ }
+ return hasImeFocus;
+ }
+
+ @UiThread
+ void onPostWindowFocus(View focusedView, boolean hasWindowFocus,
+ WindowManager.LayoutParams windowAttribute) {
+ if (!hasWindowFocus || !mHasImeFocus || isInLocalFocusMode(windowAttribute)) {
+ return;
+ }
+ if (DEBUG) {
+ Log.v(TAG, "onWindowFocus: " + focusedView
+ + " softInputMode=" + InputMethodDebug.softInputModeToString(
+ windowAttribute.softInputMode));
+ }
+
+ boolean forceFocus = false;
+ if (getImmDelegate().isRestartOnNextWindowFocus(true /* reset */)) {
+ if (DEBUG) Log.v(TAG, "Restarting due to isRestartOnNextWindowFocus as true");
+ forceFocus = true;
+ }
+ // Update mNextServedView when focusedView changed.
+ final View viewForWindowFocus = focusedView != null ? focusedView : mViewRootImpl.mView;
+ onViewFocusChanged(viewForWindowFocus, true);
+
+ getImmDelegate().startInputAsyncOnWindowFocusGain(viewForWindowFocus,
+ windowAttribute.softInputMode, windowAttribute.flags, forceFocus);
+ }
+
+ public boolean checkFocus(boolean forceNewFocus, boolean startInput) {
+ if (!getImmDelegate().isCurrentRootView(mViewRootImpl)
+ || (mServedView == mNextServedView && !forceNewFocus)) {
+ return false;
+ }
+ if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
+ + " next=" + mNextServedView
+ + " force=" + forceNewFocus
+ + " package="
+ + (mServedView != null ? mServedView.getContext().getPackageName() : "<none>"));
+
+ // Close the connection when no next served view coming.
+ if (mNextServedView == null) {
+ getImmDelegate().finishInput();
+ getImmDelegate().closeCurrentIme();
+ return false;
+ }
+ mServedView = mNextServedView;
+ getImmDelegate().finishComposingText();
+
+ if (startInput) {
+ getImmDelegate().startInput(StartInputReason.CHECK_FOCUS, null, 0, 0, 0);
+ }
+ return true;
+ }
+
+ @UiThread
+ void onViewFocusChanged(View view, boolean hasFocus) {
+ if (view == null || view.isTemporarilyDetached()) {
+ return;
+ }
+ if (!getImmDelegate().isCurrentRootView(view.getViewRootImpl())) {
+ return;
+ }
+ if (mServedView == view || !view.hasImeFocus() || !view.hasWindowFocus()) {
+ return;
+ }
+ mNextServedView = hasFocus ? view : null;
+ mViewRootImpl.dispatchCheckFocus();
+ }
+
+ @UiThread
+ void onViewDetachedFromWindow(View view) {
+ if (!getImmDelegate().isCurrentRootView(view.getViewRootImpl())) {
+ return;
+ }
+ if (mServedView == view) {
+ mNextServedView = null;
+ mViewRootImpl.dispatchCheckFocus();
+ }
+ }
+
+ @UiThread
+ void onWindowDismissed() {
+ if (!getImmDelegate().isCurrentRootView(mViewRootImpl)) {
+ return;
+ }
+ if (mServedView != null) {
+ getImmDelegate().finishInput();
+ }
+ getImmDelegate().setCurrentRootView(null);
+ mHasImeFocus = false;
+ }
+
+ /**
+ * @param windowAttribute {@link WindowManager.LayoutParams} to be checked.
+ * @return Whether the window is in local focus mode or not.
+ */
+ @AnyThread
+ private static boolean isInLocalFocusMode(WindowManager.LayoutParams windowAttribute) {
+ return (windowAttribute.flags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0;
+ }
+
+ int onProcessImeInputStage(Object token, InputEvent event,
+ WindowManager.LayoutParams windowAttribute,
+ InputMethodManager.FinishedInputEventCallback callback) {
+ if (!mHasImeFocus || isInLocalFocusMode(windowAttribute)) {
+ return InputMethodManager.DISPATCH_NOT_HANDLED;
+ }
+ final InputMethodManager imm =
+ mViewRootImpl.mContext.getSystemService(InputMethodManager.class);
+ if (imm == null) {
+ return InputMethodManager.DISPATCH_NOT_HANDLED;
+ }
+ return imm.dispatchInputEvent(event, token, callback, mViewRootImpl.mHandler);
+ }
+
+ /**
+ * A delegate implementing some basic {@link InputMethodManager} APIs.
+ * @hide
+ */
+ public interface InputMethodManagerDelegate {
+ boolean startInput(@StartInputReason int startInputReason, View focusedView,
+ @StartInputFlags int startInputFlags,
+ @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags);
+ void startInputAsyncOnWindowFocusGain(View rootView,
+ @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags,
+ boolean forceNewFocus);
+ void finishInput();
+ void closeCurrentIme();
+ void finishComposingText();
+ void setCurrentRootView(ViewRootImpl rootView);
+ boolean isCurrentRootView(ViewRootImpl rootView);
+ boolean isRestartOnNextWindowFocus(boolean reset);
+ }
+
+ public View getServedView() {
+ return mServedView;
+ }
+
+ public View getNextServedView() {
+ return mNextServedView;
+ }
+
+ public void setServedView(View view) {
+ mServedView = view;
+ }
+
+ public void setNextServedView(View view) {
+ mNextServedView = view;
+ }
+}
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 6589e75..69d0105 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -34,6 +34,7 @@
import android.util.SparseSetArray;
import android.view.InsetsController.LayoutInsetsDuringAnimation;
import android.view.InsetsState.InternalInsetsSide;
+import android.view.InsetsState.InternalInsetsType;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimationCallback.AnimationBounds;
@@ -92,6 +93,7 @@
mController = controller;
mInitialInsetsState = new InsetsState(state, true /* copySources */);
mCurrentInsets = getInsetsFromState(mInitialInsetsState, frame, null /* typeSideMap */);
+ mPendingInsets = mCurrentInsets;
mHiddenInsets = calculateInsets(mInitialInsetsState, frame, controls, false /* shown */,
null /* typeSideMap */);
mShownInsets = calculateInsets(mInitialInsetsState, frame, controls, true /* shown */,
@@ -131,6 +133,10 @@
return mTypes;
}
+ boolean controlsInternalType(@InternalInsetsType int type) {
+ return InsetsState.toInternalType(mTypes).contains(type);
+ }
+
@Override
public void setInsetsAndAlpha(Insets insets, float alpha, float fraction) {
if (mFinished) {
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 775490c..e2739c4 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -28,6 +28,7 @@
import android.annotation.NonNull;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.net.InvalidPacketException.ErrorCode;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Log;
@@ -61,15 +62,9 @@
private static final int ANIMATION_DURATION_SHOW_MS = 275;
private static final int ANIMATION_DURATION_HIDE_MS = 340;
- private static final int DIRECTION_NONE = 0;
- private static final int DIRECTION_SHOW = 1;
- private static final int DIRECTION_HIDE = 2;
static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
- @IntDef ({DIRECTION_NONE, DIRECTION_SHOW, DIRECTION_HIDE})
- private @interface AnimationDirection{}
-
/**
* Layout mode during insets animation: The views should be laid out as if the changing inset
* types are fully shown. Before starting the animation, {@link View#onApplyWindowInsets} will
@@ -101,6 +96,28 @@
@interface LayoutInsetsDuringAnimation {
}
+ /** Not running an animation. */
+ @VisibleForTesting
+ public static final int ANIMATION_TYPE_NONE = -1;
+
+ /** Running animation will show insets */
+ @VisibleForTesting
+ public static final int ANIMATION_TYPE_SHOW = 0;
+
+ /** Running animation will hide insets */
+ @VisibleForTesting
+ public static final int ANIMATION_TYPE_HIDE = 1;
+
+ /** Running animation is controlled by user via {@link #controlWindowInsetsAnimation} */
+ @VisibleForTesting
+ public static final int ANIMATION_TYPE_USER = 2;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {ANIMATION_TYPE_NONE, ANIMATION_TYPE_SHOW, ANIMATION_TYPE_HIDE,
+ ANIMATION_TYPE_USER})
+ @interface AnimationType {
+ }
+
/**
* Translation animation evaluator.
*/
@@ -145,7 +162,6 @@
public void onReady(WindowInsetsAnimationController controller, int types) {
mController = controller;
- mAnimationDirection = mShow ? DIRECTION_SHOW : DIRECTION_HIDE;
mAnimator = ObjectAnimator.ofObject(
controller,
new InsetsProperty(),
@@ -176,7 +192,6 @@
}
private void onAnimationFinish() {
- mAnimationDirection = DIRECTION_NONE;
mController.finish(mShow);
}
@@ -193,6 +208,20 @@
}
}
+ /**
+ * Represents a running animation
+ */
+ private static class RunningAnimation {
+
+ RunningAnimation(InsetsAnimationControlImpl control, int type) {
+ this.control = control;
+ this.type = type;
+ }
+
+ final InsetsAnimationControlImpl control;
+ final @AnimationType int type;
+ }
+
private final String TAG = "InsetsControllerImpl";
private final InsetsState mState = new InsetsState();
@@ -203,7 +232,7 @@
private final ViewRootImpl mViewRoot;
private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
- private final ArrayList<InsetsAnimationControlImpl> mAnimationControls = new ArrayList<>();
+ private final ArrayList<RunningAnimation> mRunningAnimations = new ArrayList<>();
private final ArrayList<InsetsAnimationControlImpl> mTmpFinishedControls = new ArrayList<>();
private WindowInsets mLastInsets;
@@ -213,7 +242,6 @@
private final Rect mLastLegacyContentInsets = new Rect();
private final Rect mLastLegacyStableInsets = new Rect();
- private @AnimationDirection int mAnimationDirection;
private int mPendingTypesToShow;
@@ -226,7 +254,7 @@
mViewRoot = viewRoot;
mAnimCallback = () -> {
mAnimCallbackScheduled = false;
- if (mAnimationControls.isEmpty()) {
+ if (mRunningAnimations.isEmpty()) {
return;
}
if (mViewRoot.mView == null) {
@@ -236,9 +264,9 @@
mTmpFinishedControls.clear();
InsetsState state = new InsetsState(mState, true /* copySources */);
- for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
- InsetsAnimationControlImpl control = mAnimationControls.get(i);
- if (mAnimationControls.get(i).applyChangeInsets(state)) {
+ for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
+ InsetsAnimationControlImpl control = mRunningAnimations.get(i).control;
+ if (control.applyChangeInsets(state)) {
mTmpFinishedControls.add(control);
}
}
@@ -349,18 +377,13 @@
int typesReady = 0;
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
for (int i = internalTypes.size() - 1; i >= 0; i--) {
- InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
- if (mAnimationDirection == DIRECTION_HIDE) {
- // Only one animator (with multiple InsetsType) can run at a time.
- // previous one should be cancelled for simplicity.
- cancelExistingAnimation();
- } else if (consumer.isRequestedVisible()
- && (mAnimationDirection == DIRECTION_NONE
- || mAnimationDirection == DIRECTION_HIDE)) {
+ @InternalInsetsType int internalType = internalTypes.valueAt(i);
+ @AnimationType int animationType = getAnimationType(internalType);
+ InsetsSourceConsumer consumer = getSourceConsumer(internalType);
+ if (mState.getSource(internalType).isVisible() && animationType == ANIMATION_TYPE_NONE
+ || animationType == ANIMATION_TYPE_SHOW) {
// no-op: already shown or animating in (because window visibility is
// applied before starting animation).
- // TODO: When we have more than one types: handle specific case when
- // show animation is going on, but the current type is not becoming visible.
continue;
}
typesReady |= InsetsState.toPublicType(consumer.getType());
@@ -377,12 +400,11 @@
int typesReady = 0;
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
for (int i = internalTypes.size() - 1; i >= 0; i--) {
- InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
- if (mAnimationDirection == DIRECTION_SHOW) {
- cancelExistingAnimation();
- } else if (!consumer.isRequestedVisible()
- && (mAnimationDirection == DIRECTION_NONE
- || mAnimationDirection == DIRECTION_HIDE)) {
+ @InternalInsetsType int internalType = internalTypes.valueAt(i);
+ @AnimationType int animationType = getAnimationType(internalType);
+ InsetsSourceConsumer consumer = getSourceConsumer(internalType);
+ if (!mState.getSource(internalType).isVisible() && animationType == ANIMATION_TYPE_NONE
+ || animationType == ANIMATION_TYPE_HIDE) {
// no-op: already hidden or animating out.
continue;
}
@@ -394,11 +416,13 @@
@Override
public void controlWindowInsetsAnimation(@InsetsType int types, long durationMs,
WindowInsetsAnimationControlListener listener) {
- controlWindowInsetsAnimation(types, listener, false /* fromIme */, durationMs);
+ controlWindowInsetsAnimation(types, listener, false /* fromIme */, durationMs,
+ ANIMATION_TYPE_USER);
}
private void controlWindowInsetsAnimation(@InsetsType int types,
- WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs) {
+ WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs,
+ @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)) {
@@ -406,12 +430,12 @@
return;
}
controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, false /* fade */,
- getLayoutInsetsDuringAnimationMode(types));
+ animationType, getLayoutInsetsDuringAnimationMode(types));
}
private void controlAnimationUnchecked(@InsetsType int types,
WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme,
- long durationMs, boolean fade,
+ long durationMs, boolean fade, @AnimationType int animationType,
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
if (types == 0) {
// nothing to animate.
@@ -444,7 +468,7 @@
final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(controls,
frame, mState, listener, typesReady, this, durationMs, fade,
layoutInsetsDuringAnimation);
- mAnimationControls.add(controller);
+ mRunningAnimations.add(new RunningAnimation(controller, animationType));
}
/**
@@ -523,10 +547,10 @@
}
private void cancelExistingControllers(@InsetsType int types) {
- for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
- InsetsAnimationControlImpl control = mAnimationControls.get(i);
+ for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
+ InsetsAnimationControlImpl control = mRunningAnimations.get(i).control;
if ((control.getTypes() & types) != 0) {
- cancelAnimation(control);
+ cancelAnimation(control, true /* invokeCallback */);
}
}
}
@@ -534,7 +558,7 @@
@VisibleForTesting
@Override
public void notifyFinished(InsetsAnimationControlImpl controller, boolean shown) {
- mAnimationControls.remove(controller);
+ cancelAnimation(controller, false /* invokeCallback */);
if (shown) {
showDirectly(controller.getTypes());
} else {
@@ -554,17 +578,24 @@
}
void notifyControlRevoked(InsetsSourceConsumer consumer) {
- for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
- InsetsAnimationControlImpl control = mAnimationControls.get(i);
+ for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
+ InsetsAnimationControlImpl control = mRunningAnimations.get(i).control;
if ((control.getTypes() & toPublicType(consumer.getType())) != 0) {
- cancelAnimation(control);
+ cancelAnimation(control, true /* invokeCallback */);
}
}
}
- private void cancelAnimation(InsetsAnimationControlImpl control) {
- control.onCancelled();
- mAnimationControls.remove(control);
+ private void cancelAnimation(InsetsAnimationControlImpl control, boolean invokeCallback) {
+ if (invokeCallback) {
+ control.onCancelled();
+ }
+ for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
+ if (mRunningAnimations.get(i).control == control) {
+ mRunningAnimations.remove(i);
+ break;
+ }
+ }
}
private void applyLocalVisibilityOverride() {
@@ -622,8 +653,15 @@
}
}
- boolean isAnimating() {
- return mAnimationDirection != DIRECTION_NONE;
+ @VisibleForTesting
+ public @AnimationType int getAnimationType(@InternalInsetsType int type) {
+ for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
+ InsetsAnimationControlImpl control = mRunningAnimations.get(i).control;
+ if (control.controlsInternalType(type)) {
+ return mRunningAnimations.get(i).type;
+ }
+ }
+ return ANIMATION_TYPE_NONE;
}
private InsetsSourceConsumer createConsumerOfType(int type) {
@@ -665,8 +703,8 @@
// and hidden state insets are correct.
controlAnimationUnchecked(
types, listener, mState.getDisplayFrame(), fromIme, listener.getDurationMs(),
- true /* fade */, show
- ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
+ 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/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index b2a5d91..8a1b45a 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.InsetsController.ANIMATION_TYPE_NONE;
+
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.view.InsetsState.InternalInsetsType;
@@ -172,7 +174,7 @@
private void applyHiddenToControl() {
if (mSourceControl == null || mSourceControl.getLeash() == null
- || mController.isAnimating()) {
+ || mController.getAnimationType(mType) != ANIMATION_TYPE_NONE) {
return;
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index ff8455a..38416ee 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -122,6 +122,8 @@
private static native void nativeSetColor(long transactionObj, long nativeObject, float[] color);
private static native void nativeSetFlags(long transactionObj, long nativeObject,
int flags, int mask);
+ private static native void nativeSetFrameRateSelectionPriority(long transactionObj,
+ long nativeObject, int priority);
private static native void nativeSetWindowCrop(long transactionObj, long nativeObject,
int l, int t, int r, int b);
private static native void nativeSetCornerRadius(long transactionObj, long nativeObject,
@@ -157,7 +159,6 @@
private static native DisplayedContentSample nativeGetDisplayedContentSample(
IBinder displayToken, long numFrames, long timestamp);
private static native int nativeGetActiveConfig(IBinder displayToken);
- private static native boolean nativeSetActiveConfig(IBinder displayToken, int id);
private static native boolean nativeSetDesiredDisplayConfigSpecs(IBinder displayToken,
SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs);
private static native SurfaceControl.DesiredDisplayConfigSpecs
@@ -1480,16 +1481,6 @@
/**
- * @hide
- */
- public static boolean setActiveConfig(IBinder displayToken, int id) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
- }
- return nativeSetActiveConfig(displayToken, id);
- }
-
- /**
* Contains information about desired display configuration.
*
* @hide
@@ -2245,6 +2236,19 @@
}
/**
+ * This information is passed to SurfaceFlinger to decide which window should have a
+ * priority when deciding about the refresh rate of the display. All windows have the
+ * lowest priority by default.
+ * @hide
+ */
+ @NonNull
+ public Transaction setFrameRateSelectionPriority(@NonNull SurfaceControl sc, int priority) {
+ sc.checkNotReleased();
+ nativeSetFrameRateSelectionPriority(mNativeObject, sc.mNativeObject, priority);
+ return this;
+ }
+
+ /**
* Request that a given surface and it's sub-tree be shown.
*
* @param sc The surface to show.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 13d609b..6724e9d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -130,7 +130,6 @@
import android.view.contentcapture.ContentCaptureSession;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
import android.view.inspector.InspectableProperty;
import android.view.inspector.InspectableProperty.EnumEntry;
import android.view.inspector.InspectableProperty.FlagEntry;
@@ -7942,12 +7941,12 @@
if (isPressed()) {
setPressed(false);
}
- if (mAttachInfo != null && mAttachInfo.mHasWindowFocus) {
- notifyFocusChangeToInputMethodManager(false /* hasFocus */);
+ if (hasWindowFocus()) {
+ notifyFocusChangeToImeFocusController(false /* hasFocus */);
}
onFocusLost();
- } else if (mAttachInfo != null && mAttachInfo.mHasWindowFocus) {
- notifyFocusChangeToInputMethodManager(true /* hasFocus */);
+ } else if (hasWindowFocus()) {
+ notifyFocusChangeToImeFocusController(true /* hasFocus */);
}
invalidate(true);
@@ -7964,23 +7963,15 @@
}
/**
- * Notify {@link InputMethodManager} about the focus change of the {@link View}.
- *
- * <p>Does nothing when {@link InputMethodManager} is not available.</p>
+ * Notify {@link ImeFocusController} about the focus change of the {@link View}.
*
* @param hasFocus {@code true} when the {@link View} is being focused.
*/
- private void notifyFocusChangeToInputMethodManager(boolean hasFocus) {
- final InputMethodManager imm =
- getContext().getSystemService(InputMethodManager.class);
- if (imm == null) {
+ private void notifyFocusChangeToImeFocusController(boolean hasFocus) {
+ if (mAttachInfo == null) {
return;
}
- if (hasFocus) {
- imm.focusIn(this);
- } else {
- imm.focusOut(this);
- }
+ mAttachInfo.mViewRootImpl.getImeFocusController().onViewFocusChanged(this, hasFocus);
}
/** @hide */
@@ -12611,11 +12602,10 @@
return findViewInsideOutShouldExist(root, mNextFocusForwardId);
case FOCUS_BACKWARD: {
if (mID == View.NO_ID) return null;
- final int id = mID;
return root.findViewByPredicateInsideOut(this, new Predicate<View>() {
@Override
public boolean test(View t) {
- return t.mNextFocusForwardId == id;
+ return t.findViewById(t.mNextFocusForwardId) == View.this;
}
});
}
@@ -13918,7 +13908,7 @@
mPrivateFlags3 &= ~PFLAG3_TEMPORARY_DETACH;
onFinishTemporaryDetach();
if (hasWindowFocus() && hasFocus()) {
- notifyFocusChangeToInputMethodManager(true /* hasFocus */);
+ notifyFocusChangeToImeFocusController(true /* hasFocus */);
}
notifyEnterOrExitForAutoFillIfNeeded(true);
notifyAppearedOrDisappearedForContentCaptureIfNeeded(true);
@@ -14326,13 +14316,13 @@
}
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
- notifyFocusChangeToInputMethodManager(false /* hasFocus */);
+ notifyFocusChangeToImeFocusController(false /* hasFocus */);
}
removeLongPressCallback();
removeTapCallback();
onFocusLost();
} else if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
- notifyFocusChangeToInputMethodManager(true /* hasFocus */);
+ notifyFocusChangeToImeFocusController(true /* hasFocus */);
}
refreshDrawableState();
@@ -14349,6 +14339,14 @@
}
/**
+ * @return {@code true} if this view is in a window that currently has IME focusable state.
+ * @hide
+ */
+ public boolean hasImeFocus() {
+ return mAttachInfo != null && mAttachInfo.mHasImeFocus;
+ }
+
+ /**
* Dispatch a view visibility change down the view hierarchy.
* ViewGroups should override to route to their children.
* @param changedView The view whose visibility changed. Could be 'this' or
@@ -19644,7 +19642,7 @@
rebuildOutline();
if (isFocused()) {
- notifyFocusChangeToInputMethodManager(true /* hasFocus */);
+ notifyFocusChangeToImeFocusController(true /* hasFocus */);
}
}
@@ -20227,9 +20225,8 @@
onDetachedFromWindow();
onDetachedFromWindowInternal();
- InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
- if (imm != null) {
- imm.onViewDetachedFromWindow(this);
+ if (info != null) {
+ info.mViewRootImpl.getImeFocusController().onViewDetachedFromWindow(this);
}
ListenerInfo li = mListenerInfo;
@@ -28565,6 +28562,11 @@
boolean mHasWindowFocus;
/**
+ * Indicates whether the view's window has IME focused.
+ */
+ boolean mHasImeFocus;
+
+ /**
* The current visibility of the window.
*/
int mWindowVisibility;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2ef944f..17b945b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -453,7 +453,6 @@
boolean mReportNextDraw;
boolean mFullRedrawNeeded;
boolean mNewSurfaceNeeded;
- boolean mLastWasImTarget;
boolean mForceNextWindowRelayout;
CountDownLatch mWindowDrawCountDown;
@@ -619,6 +618,16 @@
InputEventConsistencyVerifier.isInstrumentationEnabled() ?
new InputEventConsistencyVerifier(this, 0) : null;
+ private final ImeFocusController mImeFocusController;
+
+ /**
+ * @return {@link ImeFocusController} for this instance.
+ */
+ @NonNull
+ public ImeFocusController getImeFocusController() {
+ return mImeFocusController;
+ }
+
private final InsetsController mInsetsController = new InsetsController(this);
private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker();
@@ -704,6 +713,7 @@
}
loadSystemProperties();
+ mImeFocusController = new ImeFocusController(this);
}
public static void addFirstDrawHandler(Runnable callback) {
@@ -1054,11 +1064,6 @@
}
}
- /** Whether the window is in local focus mode or not */
- private boolean isInLocalFocusMode() {
- return (mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0;
- }
-
@UnsupportedAppUsage
public int getWindowFlags() {
return mWindowAttributes.flags;
@@ -2892,19 +2897,7 @@
mViewVisibility = viewVisibility;
mHadWindowFocus = hasWindowFocus;
- if (hasWindowFocus && !isInLocalFocusMode()) {
- final boolean imTarget = WindowManager.LayoutParams
- .mayUseInputMethod(mWindowAttributes.flags);
- if (imTarget != mLastWasImTarget) {
- mLastWasImTarget = imTarget;
- InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
- if (imm != null && imTarget) {
- imm.onPreWindowFocus(mView, hasWindowFocus);
- imm.onPostWindowFocus(mView, mView.findFocus(),
- mWindowAttributes.softInputMode, mWindowAttributes.flags);
- }
- }
- }
+ mImeFocusController.onTraversal(hasWindowFocus, mWindowAttributes);
// Remember if we must report the next draw.
if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
@@ -3072,14 +3065,10 @@
}
mAttachInfo.mHasWindowFocus = hasWindowFocus;
+ mAttachInfo.mHasImeFocus = mImeFocusController.updateImeFocusable(
+ mWindowAttributes, true /* force */);
+ mImeFocusController.onPreWindowFocus(hasWindowFocus, mWindowAttributes);
- mLastWasImTarget = WindowManager.LayoutParams
- .mayUseInputMethod(mWindowAttributes.flags);
-
- InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
- if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
- imm.onPreWindowFocus(mView, hasWindowFocus);
- }
if (mView != null) {
mAttachInfo.mKeyDispatchState.reset();
mView.dispatchWindowFocusChanged(hasWindowFocus);
@@ -3091,11 +3080,10 @@
// Note: must be done after the focus change callbacks,
// so all of the view state is set up correctly.
+ mImeFocusController.onPostWindowFocus(mView.findFocus(), hasWindowFocus,
+ mWindowAttributes);
+
if (hasWindowFocus) {
- if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
- imm.onPostWindowFocus(mView, mView.findFocus(),
- mWindowAttributes.softInputMode, mWindowAttributes.flags);
- }
// Clear the forward bit. We can just do this directly, since
// the window manager doesn't care about it.
mWindowAttributes.softInputMode &=
@@ -4891,10 +4879,7 @@
enqueueInputEvent(event, null, 0, true);
} break;
case MSG_CHECK_FOCUS: {
- InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
- if (imm != null) {
- imm.checkFocus();
- }
+ getImeFocusController().checkFocus(false, true);
} break;
case MSG_CLOSE_SYSTEM_DIALOGS: {
if (mView != null) {
@@ -5458,23 +5443,20 @@
@Override
protected int onProcess(QueuedInputEvent q) {
- if (mLastWasImTarget && !isInLocalFocusMode()) {
- InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
- if (imm != null) {
- final InputEvent event = q.mEvent;
- if (DEBUG_IMF) Log.v(mTag, "Sending input event to IME: " + event);
- int result = imm.dispatchInputEvent(event, q, this, mHandler);
- if (result == InputMethodManager.DISPATCH_HANDLED) {
- return FINISH_HANDLED;
- } else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
- // The IME could not handle it, so skip along to the next InputStage
- return FORWARD;
- } else {
- return DEFER; // callback will be invoked later
- }
- }
+ final int result = mImeFocusController.onProcessImeInputStage(
+ q, q.mEvent, mWindowAttributes, this);
+ switch (result) {
+ case InputMethodManager.DISPATCH_IN_PROGRESS:
+ // callback will be invoked later
+ return DEFER;
+ case InputMethodManager.DISPATCH_NOT_HANDLED:
+ // The IME could not handle it, so skip along to the next InputStage
+ return FORWARD;
+ case InputMethodManager.DISPATCH_HANDLED:
+ return FINISH_HANDLED;
+ default:
+ throw new IllegalStateException("Unexpected result=" + result);
}
- return FORWARD;
}
@Override
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 7d5564e..ccfbd7e 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -487,11 +487,8 @@
ViewRootImpl root = mRoots.get(index);
View view = root.getView();
- if (view != null) {
- InputMethodManager imm = view.getContext().getSystemService(InputMethodManager.class);
- if (imm != null) {
- imm.windowDismissed(mViews.get(index).getWindowToken());
- }
+ if (root != null) {
+ root.getImeFocusController().onWindowDismissed();
}
boolean deferred = root.die(immediate);
if (view != null) {
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 3dfeffb..9cbba87 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -1444,6 +1444,29 @@
return null;
}
+ /**
+ *
+ * Sets an {@link IWindowMagnificationConnection} that manipulates window magnification.
+ *
+ * @param connection The connection that manipulates window magnification.
+ * @hide
+ */
+ public void setWindowMagnificationConnection(@Nullable
+ IWindowMagnificationConnection connection) {
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.setWindowMagnificationConnection(connection);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error setting window magnfication connection", re);
+ }
+ }
+
private IAccessibilityManager getServiceLocked() {
if (mService == null) {
tryConnectToServiceLocked(null);
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index fcaaa2e..7f8fdf8 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -25,6 +25,7 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityManagerClient;
+import android.view.accessibility.IWindowMagnificationConnection;
import android.view.IWindow;
/**
@@ -86,4 +87,5 @@
oneway void registerSystemAction(in RemoteAction action, int actionId);
oneway void unregisterSystemAction(int actionId);
+ oneway void setWindowMagnificationConnection(in IWindowMagnificationConnection connection);
}
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
new file mode 100644
index 0000000..0b45c6b
--- /dev/null
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
@@ -0,0 +1,75 @@
+/*
+ * 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.view.accessibility;
+
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.view.accessibility.IWindowMagnificationConnectionCallback;
+
+/**
+ * Interface for interaction between {@link AccessibilityManagerService}
+ * and {@link WindowMagnification} in SystemUI.
+ *
+ * @hide
+ */
+oneway interface IWindowMagnificationConnection {
+
+ /**
+ * Enables window magnification on specifed display with specified center and scale.
+ *
+ * @param displayId The logical display id.
+ * @param scale magnification scale.
+ * @param centerX the screen-relative X coordinate around which to center,
+ * or {@link Float#NaN} to leave unchanged.
+ * @param centerY the screen-relative Y coordinate around which to center,
+ * or {@link Float#NaN} to leave unchanged.
+ */
+ void enableWindowMagnification(int displayId, float scale, float centerX, float centerY);
+
+ /**
+ * Sets the scale of the window magnifier on specifed display.
+ *
+ * @param displayId The logical display id.
+ * @param scale magnification scale.
+ */
+ void setScale(int displayId, float scale);
+
+ /**
+ * Disables window magnification on specifed display.
+ *
+ * @param displayId The logical display id.
+ */
+ void disableWindowMagnification(int displayId);
+
+ /**
+ * Moves the window magnifier on the specifed display.
+ *
+ * @param offsetX the amount in pixels to offset the window magnifier in the X direction, in
+ * current screen pixels.
+ * @param offsetY the amount in pixels to offset the window magnifier in the Y direction, in
+ * current screen pixels.
+ */
+ void moveWindowMagnifier(int displayId, float offsetX, float offsetY);
+
+ /**
+ * Sets {@link IWindowMagnificationConnectionCallback} to receive the request or the callback.
+ *
+ *
+ * @param callback the interface to be called.
+ */
+ void setConnectionCallback(in IWindowMagnificationConnectionCallback callback);
+}
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
new file mode 100644
index 0000000..7327bb5
--- /dev/null
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.view.accessibility;
+
+import android.graphics.Rect;
+
+/**
+ * interface to notify the change of the window magnifier bounds and request to change
+ * the magnification mode.
+ *
+ * @hide
+ */
+ oneway interface IWindowMagnificationConnectionCallback {
+
+ /**
+ * Called when the bounds of the window magnifier is changed.
+ *
+ * @param displayId The logical display id.
+ * @param bounds The window magnifier bounds in screen coordinates.
+ */
+ void onWindowMagnifierBoundsChanged(int display, in Rect bounds);
+ /**
+ * Changes the magnification mode on specified display. It is invoked by System UI when the
+ * switch button is toggled.
+ *
+ * @param displayId The logical display id.
+ * @param magnificationMode new magnification mode.
+ */
+ void onChangeMagnificationMode(int display, int magnificationMode);
+}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 9c04b39..c159f89 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -230,6 +230,7 @@
/** @hide */ public static final int ACTION_VIEW_ENTERED = 2;
/** @hide */ public static final int ACTION_VIEW_EXITED = 3;
/** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
+ /** @hide */ public static final int ACTION_RESPONSE_EXPIRED = 5;
/** @hide */ public static final int NO_LOGGING = 0;
/** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
@@ -776,11 +777,19 @@
*
* @see AutofillClient#autofillClientIsVisibleForAutofill()
*
+ * @param isExpiredResponse The response has expired or not
+ *
* {@hide}
*/
- public void onInvisibleForAutofill() {
+ public void onInvisibleForAutofill(boolean isExpiredResponse) {
synchronized (mLock) {
mOnInvisibleCalled = true;
+
+ if (isExpiredResponse) {
+ // Notify service the response has expired.
+ updateSessionLocked(/* id= */ null, /* bounds= */ null, /* value= */ null,
+ ACTION_RESPONSE_EXPIRED, /* flags= */ 0);
+ }
}
}
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index ae2fb8e..d5d631a 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -758,8 +758,9 @@
Context context;
if (mTargetView != null) {
context = mTargetView.getContext();
- } else if (mIMM.mServedView != null) {
- context = mIMM.mServedView.getContext();
+ } else if (mIMM.mCurRootView != null) {
+ final View servedView = mIMM.mCurRootView.getImeFocusController().getServedView();
+ context = servedView != null ? servedView.getContext() : null;
} else {
context = null;
}
diff --git a/core/java/android/view/inputmethod/InlineSuggestion.java b/core/java/android/view/inputmethod/InlineSuggestion.java
index c10144e..a32ea4b 100644
--- a/core/java/android/view/inputmethod/InlineSuggestion.java
+++ b/core/java/android/view/inputmethod/InlineSuggestion.java
@@ -19,6 +19,7 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Parcelable;
@@ -61,6 +62,20 @@
private final @Nullable IInlineContentProvider mContentProvider;
/**
+ * Creates a new {@link InlineSuggestion}, for testing purpose.
+ *
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ public static InlineSuggestion newInlineSuggestion(@NonNull InlineSuggestionInfo info) {
+ return new InlineSuggestion(info, null);
+ }
+
+
+
+
+ /**
* Inflates a view with the content of this suggestion at a specific size.
* The size must be between the {@link InlinePresentationSpec#getMinSize() min size}
* and the {@link InlinePresentationSpec#getMaxSize() max size} of the presentation
@@ -271,10 +286,10 @@
};
@DataClass.Generated(
- time = 1575933636929L,
+ time = 1578972138081L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestion.java",
- inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\npublic void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.view.View>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
+ inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.view.View>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionInfo.java b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
index 07fce31..195b63a 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.os.Parcelable;
import android.view.inline.InlinePresentationSpec;
@@ -53,6 +54,19 @@
/** Hints for the type of data being suggested. */
private final @Nullable String[] mAutofillHints;
+ /**
+ * Creates a new {@link InlineSuggestionInfo}, for testing purpose.
+ *
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ public static InlineSuggestionInfo newInlineSuggestionInfo(
+ @NonNull InlinePresentationSpec presentationSpec,
+ @NonNull @Source String source,
+ @Nullable String[] autofillHints) {
+ return new InlineSuggestionInfo(presentationSpec, source, autofillHints);
+ }
@@ -247,10 +261,10 @@
};
@DataClass.Generated(
- time = 1574406074120L,
+ time = 1578972121865L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionInfo.java",
- inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mPresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
+ inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mPresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.view.inline.InlinePresentationSpec,java.lang.String,java.lang.String[])\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsResponse.java b/core/java/android/view/inputmethod/InlineSuggestionsResponse.java
index 924a5ee..be833df 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsResponse.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsResponse.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.os.Parcelable;
import com.android.internal.util.DataClass;
@@ -33,6 +34,18 @@
public final class InlineSuggestionsResponse implements Parcelable {
private final @NonNull List<InlineSuggestion> mInlineSuggestions;
+ /**
+ * Creates a new {@link InlineSuggestionsResponse}, for testing purpose.
+ *
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ public static InlineSuggestionsResponse newInlineSuggestionsResponse(
+ @NonNull List<InlineSuggestion> inlineSuggestions) {
+ return new InlineSuggestionsResponse(inlineSuggestions);
+ }
+
// Code below generated by codegen v1.0.14.
@@ -151,10 +164,10 @@
};
@DataClass.Generated(
- time = 1574406147911L,
+ time = 1578972149519L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsResponse.java",
- inputSignatures = "private final @android.annotation.NonNull java.util.List<android.view.inputmethod.InlineSuggestion> mInlineSuggestions\nclass InlineSuggestionsResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
+ inputSignatures = "private final @android.annotation.NonNull java.util.List<android.view.inputmethod.InlineSuggestion> mInlineSuggestions\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionsResponse newInlineSuggestionsResponse(java.util.List<android.view.inputmethod.InlineSuggestion>)\nclass InlineSuggestionsResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index f3007a7..904e736 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -64,6 +64,7 @@
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;
@@ -364,10 +365,10 @@
boolean mActive = false;
/**
- * {@code true} if next {@link #onPostWindowFocus(View, View, int, int)} needs to
+ * {@code true} if next {@link ImeFocusController#onPostWindowFocus} needs to
* restart input.
*/
- boolean mRestartOnNextWindowFocus = true;
+ private boolean mRestartOnNextWindowFocus = true;
/**
* As reported by IME through InputConnection.
@@ -380,22 +381,8 @@
* This is the root view of the overall window that currently has input
* method focus.
*/
- @UnsupportedAppUsage
- View mCurRootView;
- /**
- * This is the view that should currently be served by an input method,
- * regardless of the state of setting that up.
- */
- // See comment to mH field in regard to @UnsupportedAppUsage
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
- View mServedView;
- /**
- * This is then next view that will be served by the input method, when
- * we get around to updating things.
- */
- // See comment to mH field in regard to @UnsupportedAppUsage
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
- View mNextServedView;
+ @GuardedBy("mH")
+ ViewRootImpl mCurRootView;
/**
* This is set when we are in the process of connecting, to determine
* when we have actually finished.
@@ -489,6 +476,8 @@
final Pool<PendingEvent> mPendingEventPool = new SimplePool<>(20);
final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20);
+ final DelegateImpl mDelegate = new DelegateImpl();
+
// -----------------------------------------------------------
static final int MSG_DUMP = 1;
@@ -564,6 +553,178 @@
return servedView.hasWindowFocus() || isAutofillUIShowing(servedView);
}
+ private final class DelegateImpl implements
+ ImeFocusController.InputMethodManagerDelegate {
+ /**
+ * Used by {@link ImeFocusController} to start input connection.
+ */
+ @Override
+ public boolean startInput(@StartInputReason int startInputReason, View focusedView,
+ @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
+ int windowFlags) {
+ synchronized (mH) {
+ mCurrentTextBoxAttribute = null;
+ mCompletions = null;
+ mServedConnecting = true;
+ if (getServedViewLocked() != null && !getServedViewLocked().onCheckIsTextEditor()) {
+ // servedView has changed and it's not editable.
+ maybeCallServedViewChangedLocked(null);
+ }
+ }
+ return startInputInner(startInputReason,
+ focusedView != null ? focusedView.getWindowToken() : null, startInputFlags,
+ softInputMode, windowFlags);
+ }
+
+ /**
+ * Used by {@link ImeFocusController} to finish input connection.
+ */
+ @Override
+ public void finishInput() {
+ synchronized (mH) {
+ finishInputLocked();
+ }
+ }
+
+ /**
+ * Used by {@link ImeFocusController} to hide current input method editor.
+ */
+ @Override
+ public void closeCurrentIme() {
+ closeCurrentInput();
+ }
+
+ /**
+ * For {@link ImeFocusController} to start input asynchronously when focus gain.
+ */
+ @Override
+ public void startInputAsyncOnWindowFocusGain(View focusedView,
+ @SoftInputModeFlags int softInputMode, int windowFlags, boolean forceNewFocus) {
+ final boolean forceNewFocus1 = forceNewFocus;
+ final int startInputFlags = getStartInputFlags(focusedView, 0);
+
+ if (mWindowFocusGainFuture != null) {
+ mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */);
+ }
+ mWindowFocusGainFuture = mStartInputWorker.submit(() -> {
+ synchronized (mH) {
+ if (mCurRootView == null) {
+ 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;
+ }
+ }
+
+ // 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 {
+ if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
+ mService.startInputOrWindowGainedFocus(
+ StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
+ focusedView.getWindowToken(), startInputFlags, softInputMode,
+ windowFlags,
+ null, null, 0 /* missingMethodFlags */,
+ mCurRootView.mContext.getApplicationInfo().targetSdkVersion);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ });
+ }
+
+ /**
+ * Used by {@link ImeFocusController} to finish current composing text.
+ */
+ @Override
+ public void finishComposingText() {
+ if (mServedInputConnectionWrapper != null) {
+ mServedInputConnectionWrapper.finishComposingText();
+ }
+ }
+
+ /**
+ * Used for {@link ImeFocusController} to set the current focused root view.
+ */
+ @Override
+ public void setCurrentRootView(ViewRootImpl rootView) {
+ // If the mCurRootView is losing window focus, release the strong reference to it
+ // so as not to prevent it from being garbage-collected.
+ if (mWindowFocusGainFuture != null) {
+ mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */);
+ mWindowFocusGainFuture = null;
+ }
+ synchronized (mH) {
+ mCurRootView = rootView;
+ }
+ }
+
+ /**
+ * Used for {@link ImeFocusController} to return if the root view from the
+ * controller is this {@link InputMethodManager} currently focused.
+ * TODO: Address event-order problem when get current root view in multi-threads.
+ */
+ @Override
+ public boolean isCurrentRootView(ViewRootImpl rootView) {
+ synchronized (mH) {
+ return mCurRootView == rootView;
+ }
+ }
+
+ /**
+ * For {@link ImeFocusController#checkFocus} if needed to force check new focus.
+ */
+ @Override
+ public boolean isRestartOnNextWindowFocus(boolean reset) {
+ final boolean result = mRestartOnNextWindowFocus;
+ if (reset) {
+ mRestartOnNextWindowFocus = false;
+ }
+ return result;
+ }
+ }
+
+ /** @hide */
+ public DelegateImpl getDelegate() {
+ return mDelegate;
+ }
+
+ private View getServedViewLocked() {
+ return mCurRootView != null ? mCurRootView.getImeFocusController().getServedView() : null;
+ }
+
+ private View getNextServedViewLocked() {
+ return mCurRootView != null ? mCurRootView.getImeFocusController().getNextServedView()
+ : null;
+ }
+
+ private void setServedViewLocked(View view) {
+ if (mCurRootView != null) {
+ mCurRootView.getImeFocusController().setServedView(view);
+ }
+ }
+
+ private void setNextServedViewLocked(View view) {
+ if (mCurRootView != null) {
+ mCurRootView.getImeFocusController().setNextServedView(view);
+ }
+ }
+
+ /**
+ * Returns {@code true} when the given view has been served by Input Method.
+ */
+ private boolean hasServedByInputMethodLocked(View view) {
+ final View servedView = getServedViewLocked();
+ return (servedView == view
+ || (servedView != null && servedView.checkInputConnectionProxy(view)));
+ }
+
class H extends Handler {
H(Looper looper) {
super(looper, null, true);
@@ -629,7 +790,8 @@
clearBindingLocked();
// If we were actively using the last input method, then
// we would like to re-connect to the next input method.
- if (mServedView != null && mServedView.isFocused()) {
+ final View servedView = getServedViewLocked();
+ if (servedView != null && servedView.isFocused()) {
mServedConnecting = true;
}
startInput = mActive;
@@ -664,11 +826,16 @@
}
// Check focus again in case that "onWindowFocus" is called before
// handling this message.
- if (mServedView != null && canStartInput(mServedView)) {
- if (checkFocusNoStartInput(mRestartOnNextWindowFocus)) {
+ final View servedView;
+ synchronized (mH) {
+ servedView = getServedViewLocked();
+ }
+ if (servedView != null && canStartInput(servedView)) {
+ if (mCurRootView != null && mCurRootView.getImeFocusController()
+ .checkFocus(mRestartOnNextWindowFocus, false)) {
final int reason = active ? StartInputReason.ACTIVATED_BY_IMMS
: StartInputReason.DEACTIVATED_BY_IMMS;
- startInputInner(reason, null, 0, 0, 0);
+ mDelegate.startInput(reason, null, 0, 0, 0);
}
}
return;
@@ -1212,10 +1379,7 @@
checkFocus();
synchronized (mH) {
- return (mServedView == view
- || (mServedView != null
- && mServedView.checkInputConnectionProxy(view)))
- && mCurrentTextBoxAttribute != null;
+ return hasServedByInputMethodLocked(view) && mCurrentTextBoxAttribute != null;
}
}
@@ -1225,7 +1389,7 @@
public boolean isActive() {
checkFocus();
synchronized (mH) {
- return mServedView != null && mCurrentTextBoxAttribute != null;
+ return getServedViewLocked() != null && mCurrentTextBoxAttribute != null;
}
}
@@ -1286,11 +1450,14 @@
*/
@UnsupportedAppUsage
void finishInputLocked() {
- mNextServedView = null;
mActivityViewToScreenMatrix = null;
- if (mServedView != null) {
- if (DEBUG) Log.v(TAG, "FINISH INPUT: mServedView=" + dumpViewInfo(mServedView));
- mServedView = null;
+ setNextServedViewLocked(null);
+ if (getServedViewLocked() != null) {
+ if (DEBUG) {
+ Log.v(TAG, "FINISH INPUT: mServedView="
+ + dumpViewInfo(getServedViewLocked()));
+ }
+ setServedViewLocked(null);
mCompletions = null;
mServedConnecting = false;
clearConnectionLocked();
@@ -1307,8 +1474,7 @@
checkFocus();
synchronized (mH) {
- if (mServedView != view && (mServedView == null
- || !mServedView.checkInputConnectionProxy(view))) {
+ if (!hasServedByInputMethodLocked(view)) {
return;
}
@@ -1332,8 +1498,7 @@
checkFocus();
synchronized (mH) {
- if (mServedView != view && (mServedView == null
- || !mServedView.checkInputConnectionProxy(view))) {
+ if (!hasServedByInputMethodLocked(view)) {
return;
}
@@ -1447,8 +1612,7 @@
checkFocus();
synchronized (mH) {
- if (mServedView != view && (mServedView == null
- || !mServedView.checkInputConnectionProxy(view))) {
+ if (!hasServedByInputMethodLocked(view)) {
return false;
}
@@ -1539,7 +1703,8 @@
ResultReceiver resultReceiver) {
checkFocus();
synchronized (mH) {
- if (mServedView == null || mServedView.getWindowToken() != windowToken) {
+ final View servedView = getServedViewLocked();
+ if (servedView == null || servedView.getWindowToken() != windowToken) {
return false;
}
@@ -1566,7 +1731,8 @@
**/
public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) {
synchronized (mH) {
- if (mServedView == null || mServedView.getWindowToken() != windowToken) {
+ final View servedView = getServedViewLocked();
+ if (servedView == null || servedView.getWindowToken() != windowToken) {
return;
}
if (mCurMethod != null) {
@@ -1617,8 +1783,7 @@
checkFocus();
synchronized (mH) {
- if (mServedView != view && (mServedView == null
- || !mServedView.checkInputConnectionProxy(view))) {
+ if (!hasServedByInputMethodLocked(view)) {
return;
}
@@ -1645,7 +1810,7 @@
final View view;
synchronized (mH) {
- view = mServedView;
+ view = getServedViewLocked();
// Make sure we have a window token for the served view.
if (DEBUG) {
@@ -1664,10 +1829,7 @@
Log.e(TAG, "ABORT input: ServedView must be attached to a Window");
return false;
}
- startInputFlags |= StartInputFlags.VIEW_HAS_FOCUS;
- if (view.onCheckIsTextEditor()) {
- startInputFlags |= StartInputFlags.IS_TEXT_EDITOR;
- }
+ startInputFlags = getStartInputFlags(view, startInputFlags);
softInputMode = view.getViewRootImpl().mWindowAttributes.softInputMode;
windowFlags = view.getViewRootImpl().mWindowAttributes.flags;
}
@@ -1690,7 +1852,7 @@
// The view is running on a different thread than our own, so
// we need to reschedule our work for over there.
if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread");
- vh.post(() -> startInputInner(startInputReason, null, 0, 0, 0));
+ vh.post(() -> mDelegate.startInput(startInputReason, null, 0, 0, 0));
return false;
}
@@ -1709,11 +1871,12 @@
synchronized (mH) {
// Now that we are locked again, validate that our state hasn't
// changed.
- if (mServedView != view || !mServedConnecting) {
+ final View servedView = getServedViewLocked();
+ if (servedView != view || !mServedConnecting) {
// Something else happened, so abort.
if (DEBUG) Log.v(TAG,
"Starting input: finished by someone else. view=" + dumpViewInfo(view)
- + " mServedView=" + dumpViewInfo(mServedView)
+ + " servedView=" + dumpViewInfo(servedView)
+ " mServedConnecting=" + mServedConnecting);
return false;
}
@@ -1804,101 +1967,12 @@
return true;
}
- /**
- * When the focused window is dismissed, this method is called to finish the
- * input method started before.
- * @hide
- */
- @UnsupportedAppUsage
- public void windowDismissed(IBinder appWindowToken) {
- checkFocus();
- synchronized (mH) {
- if (mServedView != null &&
- mServedView.getWindowToken() == appWindowToken) {
- finishInputLocked();
- }
- if (mCurRootView != null &&
- mCurRootView.getWindowToken() == appWindowToken) {
- mCurRootView = null;
- }
+ private int getStartInputFlags(View focusedView, int startInputFlags) {
+ startInputFlags |= StartInputFlags.VIEW_HAS_FOCUS;
+ if (focusedView.onCheckIsTextEditor()) {
+ startInputFlags |= StartInputFlags.IS_TEXT_EDITOR;
}
- }
-
- /**
- * Call this when a view receives focus.
- * @hide
- */
- @UnsupportedAppUsage
- public void focusIn(View view) {
- synchronized (mH) {
- focusInLocked(view);
- }
- }
-
- void focusInLocked(View view) {
- if (DEBUG) Log.v(TAG, "focusIn: " + dumpViewInfo(view));
-
- if (view != null && view.isTemporarilyDetached()) {
- // This is a request from a view that is temporarily detached from a window.
- if (DEBUG) Log.v(TAG, "Temporarily detached view, ignoring");
- return;
- }
-
- if (mCurRootView != view.getRootView()) {
- // This is a request from a window that isn't in the window with
- // IME focus, so ignore it.
- if (DEBUG) Log.v(TAG, "Not IME target window, ignoring");
- return;
- }
-
- mNextServedView = view;
- scheduleCheckFocusLocked(view);
- }
-
- /**
- * Call this when a view loses focus.
- * @hide
- */
- @UnsupportedAppUsage
- public void focusOut(View view) {
- synchronized (mH) {
- if (DEBUG) Log.v(TAG, "focusOut: view=" + dumpViewInfo(view)
- + " mServedView=" + dumpViewInfo(mServedView));
- if (mServedView != view) {
- // The following code would auto-hide the IME if we end up
- // with no more views with focus. This can happen, however,
- // whenever we go into touch mode, so it ends up hiding
- // at times when we don't really want it to. For now it
- // seems better to just turn it all off.
- // TODO: Check view.isTemporarilyDetached() when re-enable the following code.
- if (false && canStartInput(view)) {
- mNextServedView = null;
- scheduleCheckFocusLocked(view);
- }
- }
- }
- }
-
- /**
- * Call this when a view is being detached from a {@link android.view.Window}.
- * @hide
- */
- public void onViewDetachedFromWindow(View view) {
- synchronized (mH) {
- if (DEBUG) Log.v(TAG, "onViewDetachedFromWindow: view=" + dumpViewInfo(view)
- + " mServedView=" + dumpViewInfo(mServedView));
- if (mServedView == view) {
- mNextServedView = null;
- scheduleCheckFocusLocked(view);
- }
- }
- }
-
- static void scheduleCheckFocusLocked(View view) {
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
- if (viewRootImpl != null) {
- viewRootImpl.dispatchCheckFocus();
- }
+ return startInputFlags;
}
/**
@@ -1906,54 +1980,12 @@
*/
@UnsupportedAppUsage
public void checkFocus() {
- if (checkFocusNoStartInput(false)) {
- startInputInner(StartInputReason.CHECK_FOCUS, null, 0, 0, 0);
- }
- }
-
- private boolean checkFocusNoStartInput(boolean forceNewFocus) {
- // This is called a lot, so short-circuit before locking.
- if (mServedView == mNextServedView && !forceNewFocus) {
- return false;
- }
-
- final ControlledInputConnectionWrapper ic;
synchronized (mH) {
- if (mServedView == mNextServedView && !forceNewFocus) {
- return false;
- }
- if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
- + " next=" + mNextServedView
- + " forceNewFocus=" + forceNewFocus
- + " package="
- + (mServedView != null ? mServedView.getContext().getPackageName() : "<none>"));
-
- if (mNextServedView == null) {
- finishInputLocked();
- // In this case, we used to have a focused view on the window,
- // but no longer do. We should make sure the input method is
- // no longer shown, since it serves no purpose.
- closeCurrentInput();
- return false;
- }
-
- ic = mServedInputConnectionWrapper;
-
- mServedView = mNextServedView;
- mCurrentTextBoxAttribute = null;
- mCompletions = null;
- mServedConnecting = true;
- // servedView has changed and it's not editable.
- if (!mServedView.onCheckIsTextEditor()) {
- maybeCallServedViewChangedLocked(null);
+ if (mCurRootView != null) {
+ mCurRootView.getImeFocusController().checkFocus(false /* forceNewFocus */,
+ true /* startInput */);
}
}
-
- if (ic != null) {
- ic.finishComposingText();
- }
-
- return true;
}
@UnsupportedAppUsage
@@ -1966,92 +1998,6 @@
}
/**
- * Called by ViewAncestor when its window gets input focus.
- * @hide
- */
- public void onPostWindowFocus(View rootView, View focusedView,
- @SoftInputModeFlags int softInputMode, int windowFlags) {
- boolean forceNewFocus = false;
- synchronized (mH) {
- if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
- + " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode)
- + " flags=#" + Integer.toHexString(windowFlags));
- if (mRestartOnNextWindowFocus) {
- if (DEBUG) Log.v(TAG, "Restarting due to mRestartOnNextWindowFocus");
- mRestartOnNextWindowFocus = false;
- forceNewFocus = true;
- }
- focusInLocked(focusedView != null ? focusedView : rootView);
- }
-
- int startInputFlags = 0;
- if (focusedView != null) {
- startInputFlags |= StartInputFlags.VIEW_HAS_FOCUS;
- if (focusedView.onCheckIsTextEditor()) {
- startInputFlags |= StartInputFlags.IS_TEXT_EDITOR;
- }
- }
-
- final boolean forceNewFocus1 = forceNewFocus;
- final int startInputFlags1 = startInputFlags;
- if (mWindowFocusGainFuture != null) {
- mWindowFocusGainFuture.cancel(false/* mayInterruptIfRunning */);
- }
- mWindowFocusGainFuture = mStartInputWorker.submit(() -> {
- if (checkFocusNoStartInput(forceNewFocus1)) {
- // 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 (startInputInner(StartInputReason.WINDOW_FOCUS_GAIN, rootView.getWindowToken(),
- startInputFlags1, softInputMode, windowFlags)) {
- return;
- }
- }
-
- // For some reason we didn't do a startInput + windowFocusGain, so
- // we'll just do a window focus gain and call it a day.
- synchronized (mH) {
- try {
- if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
- mService.startInputOrWindowGainedFocus(
- StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
- rootView.getWindowToken(), startInputFlags1, softInputMode, windowFlags,
- null, null, 0 /* missingMethodFlags */,
- rootView.getContext().getApplicationInfo().targetSdkVersion);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- });
- }
-
- /** @hide */
- @UnsupportedAppUsage
- public void onPreWindowFocus(View rootView, boolean hasWindowFocus) {
- synchronized (mH) {
- if (rootView == null) {
- mCurRootView = null;
- } if (hasWindowFocus) {
- mCurRootView = rootView;
- } else if (rootView == mCurRootView) {
- // If the mCurRootView is losing window focus, release the strong reference to it
- // so as not to prevent it from being garbage-collected.
- mCurRootView = null;
- if (mWindowFocusGainFuture != null) {
- mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */);
- mWindowFocusGainFuture = null;
- }
- } else {
- if (DEBUG) {
- Log.v(TAG, "Ignoring onPreWindowFocus()."
- + " mCurRootView=" + mCurRootView + " rootView=" + rootView);
- }
- }
- }
- }
-
- /**
* Register for IME state callbacks and applying visibility in
* {@link android.view.ImeInsetsSourceConsumer}.
* @hide
@@ -2090,10 +2036,11 @@
*/
public boolean requestImeShow(ResultReceiver resultReceiver) {
synchronized (mH) {
- if (mServedView == null) {
+ final View servedView = getServedViewLocked();
+ if (servedView == null) {
return false;
}
- showSoftInput(mServedView, 0 /* flags */, resultReceiver);
+ showSoftInput(servedView, 0 /* flags */, resultReceiver);
return true;
}
}
@@ -2135,9 +2082,8 @@
checkFocus();
synchronized (mH) {
- if ((mServedView != view && (mServedView == null
- || !mServedView.checkInputConnectionProxy(view)))
- || mCurrentTextBoxAttribute == null || mCurMethod == null) {
+ if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null
+ || mCurMethod == null) {
return;
}
@@ -2185,12 +2131,17 @@
return;
}
- final boolean focusChanged = mServedView != mNextServedView;
+ final View servedView;
+ final View nextServedView;
+ synchronized (mH) {
+ servedView = getServedViewLocked();
+ nextServedView = getNextServedViewLocked();
+ }
+ final boolean focusChanged = servedView != nextServedView;
checkFocus();
synchronized (mH) {
- if ((mServedView != view && (mServedView == null
- || !mServedView.checkInputConnectionProxy(view)))
- || mCurrentTextBoxAttribute == null || mCurMethod == null) {
+ if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null
+ || mCurMethod == null) {
return;
}
try {
@@ -2258,9 +2209,8 @@
checkFocus();
synchronized (mH) {
- if ((mServedView != view && (mServedView == null
- || !mServedView.checkInputConnectionProxy(view)))
- || mCurrentTextBoxAttribute == null || mCurMethod == null) {
+ if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null
+ || mCurMethod == null) {
return;
}
@@ -2296,9 +2246,8 @@
checkFocus();
synchronized (mH) {
- if ((mServedView != view &&
- (mServedView == null || !mServedView.checkInputConnectionProxy(view)))
- || mCurrentTextBoxAttribute == null || mCurMethod == null) {
+ if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null
+ || mCurMethod == null) {
return;
}
// If immediate bit is set, we will call updateCursorAnchorInfo() even when the data has
@@ -2354,9 +2303,8 @@
checkFocus();
synchronized (mH) {
- if ((mServedView != view && (mServedView == null
- || !mServedView.checkInputConnectionProxy(view)))
- || mCurrentTextBoxAttribute == null || mCurMethod == null) {
+ if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null
+ || mCurMethod == null) {
return;
}
try {
@@ -2577,8 +2525,9 @@
synchronized (mH) {
ViewRootImpl viewRootImpl = targetView != null ? targetView.getViewRootImpl() : null;
if (viewRootImpl == null) {
- if (mServedView != null) {
- viewRootImpl = mServedView.getViewRootImpl();
+ final View servedView = getServedViewLocked();
+ if (servedView != null) {
+ viewRootImpl = servedView.getViewRootImpl();
}
}
if (viewRootImpl != null) {
@@ -2759,6 +2708,7 @@
/**
* Show the settings for enabling subtypes of the specified input method.
+ *
* @param imiId An input method, whose subtypes settings will be shown. If imiId is null,
* subtypes of all input methods will be shown.
*/
@@ -3057,8 +3007,8 @@
p.println(" mFullscreenMode=" + mFullscreenMode);
p.println(" mCurMethod=" + mCurMethod);
p.println(" mCurRootView=" + mCurRootView);
- p.println(" mServedView=" + mServedView);
- p.println(" mNextServedView=" + mNextServedView);
+ p.println(" mServedView=" + getServedViewLocked());
+ p.println(" mNextServedView=" + getNextServedViewLocked());
p.println(" mServedConnecting=" + mServedConnecting);
if (mCurrentTextBoxAttribute != null) {
p.println(" mCurrentTextBoxAttribute:");
@@ -3134,6 +3084,8 @@
sb.append(",window=" + view.getWindowToken());
sb.append(",displayId=" + view.getContext().getDisplayId());
sb.append(",temporaryDetach=" + view.isTemporarilyDetached());
+ sb.append(",hasImeFocus=" + view.hasImeFocus());
+
return sb.toString();
}
}
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 1aa2aec..bda12b0 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -113,7 +113,7 @@
* Returns the text that was used to generate these links.
*/
@NonNull
- public String getText() {
+ public CharSequence getText() {
return mFullText;
}
@@ -370,8 +370,8 @@
}
/**
- * @return ordered list of locale preferences that can be used to disambiguate
- * the provided text
+ * Returns an ordered list of locale preferences that can be used to disambiguate the
+ * provided text.
*/
@Nullable
public LocaleList getDefaultLocales() {
@@ -379,7 +379,8 @@
}
/**
- * @return The config representing the set of entities to look for
+ * Returns the config representing the set of entities to look for
+ *
* @see Builder#setEntityConfig(EntityConfig)
*/
@Nullable
@@ -398,8 +399,8 @@
}
/**
- * @return reference time based on which relative dates (e.g. "tomorrow") should be
- * interpreted.
+ * Returns reference time based on which relative dates (e.g. "tomorrow") should be
+ * interpreted.
*/
@Nullable
public ZonedDateTime getReferenceTime() {
@@ -473,6 +474,9 @@
}
/**
+ * Sets ordered list of locale preferences that may be used to disambiguate the
+ * provided text.
+ *
* @param defaultLocales ordered list of locale preferences that may be used to
* disambiguate the provided text. If no locale preferences exist,
* set this to null or an empty locale list.
@@ -524,9 +528,11 @@
}
/**
- * @param referenceTime reference time based on which relative dates (e.g. "tomorrow"
- * should be interpreted. This should usually be the time when the text was
- * originally composed.
+ * Sets the reference time based on which relative dates (e.g.
+ * "tomorrow") should be interpreted.
+ *
+ * @param referenceTime reference time based on which relative dates. This should
+ * usually be the time when the text was originally composed.
*
* @return this builder
*/
@@ -716,6 +722,8 @@
}
/**
+ * Adds a TextLink.
+ *
* @see #addLink(int, int, Map)
* @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled.
*/
diff --git a/core/java/android/view/textclassifier/TextLinksParams.java b/core/java/android/view/textclassifier/TextLinksParams.java
index b7d63bf..f12b0d7 100644
--- a/core/java/android/view/textclassifier/TextLinksParams.java
+++ b/core/java/android/view/textclassifier/TextLinksParams.java
@@ -113,7 +113,7 @@
return TextLinks.STATUS_UNSUPPORTED_CHARACTER;
}
- if (!textString.startsWith(textLinks.getText())) {
+ if (!textString.startsWith(textLinks.getText().toString())) {
return TextLinks.STATUS_DIFFERENT_TEXT;
}
if (textLinks.getLinks().isEmpty()) {
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 3fdedc8..93659a4 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -371,7 +371,9 @@
// targets during boot. Needs to read settings directly here.
String shortcutTargets = Settings.Secure.getStringForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, mUserId);
- if (TextUtils.isEmpty(shortcutTargets)) {
+ // A11y warning dialog updates settings to empty string, when user disables a11y shortcut.
+ // Only fallback to default a11y service, when setting is never updated.
+ if (shortcutTargets == null) {
shortcutTargets = mContext.getString(R.string.config_defaultAccessibilityService);
}
return !TextUtils.isEmpty(shortcutTargets);
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index a9f0175..457c033 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -75,11 +75,14 @@
private static final char SERVICES_SEPARATOR = ':';
private static final TextUtils.SimpleStringSplitter sStringColonSplitter =
new TextUtils.SimpleStringSplitter(SERVICES_SEPARATOR);
+ @UserShortcutType
private static final int ACCESSIBILITY_BUTTON_USER_TYPE = convertToUserType(
ACCESSIBILITY_BUTTON);
+ @UserShortcutType
private static final int ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE = convertToUserType(
ACCESSIBILITY_SHORTCUT_KEY);
+ @ShortcutType
private int mShortcutType;
private final List<AccessibilityButtonTarget> mTargets = new ArrayList<>();
private AlertDialog mAlertDialog;
@@ -211,9 +214,14 @@
mShortcutType = getIntent().getIntExtra(AccessibilityManager.EXTRA_SHORTCUT_TYPE,
ACCESSIBILITY_BUTTON);
+ if ((mShortcutType != ACCESSIBILITY_BUTTON)
+ && (mShortcutType != ACCESSIBILITY_SHORTCUT_KEY)) {
+ throw new IllegalStateException("Unexpected shortcut type: " + mShortcutType);
+ }
+
mTargets.addAll(getServiceTargets(this, mShortcutType));
- mTargetAdapter = new TargetAdapter(mTargets);
+ mTargetAdapter = new TargetAdapter(mTargets, mShortcutType);
mAlertDialog = new AlertDialog.Builder(this)
.setAdapter(mTargetAdapter, /* listener= */ null)
.setPositiveButton(
@@ -357,16 +365,21 @@
private static class TargetAdapter extends BaseAdapter {
@ShortcutMenuMode
private int mShortcutMenuMode = ShortcutMenuMode.LAUNCH;
+ @ShortcutType
+ private int mShortcutButtonType;
private List<AccessibilityButtonTarget> mButtonTargets;
- TargetAdapter(List<AccessibilityButtonTarget> targets) {
+ TargetAdapter(List<AccessibilityButtonTarget> targets,
+ @ShortcutType int shortcutButtonType) {
this.mButtonTargets = targets;
+ this.mShortcutButtonType = shortcutButtonType;
}
- void setShortcutMenuMode(int shortcutMenuMode) {
+ void setShortcutMenuMode(@ShortcutMenuMode int shortcutMenuMode) {
mShortcutMenuMode = shortcutMenuMode;
}
+ @ShortcutMenuMode
int getShortcutMenuMode() {
return mShortcutMenuMode;
}
@@ -441,14 +454,16 @@
private void updateLegacyActionItemVisibility(@NonNull Context context,
@NonNull ViewHolder holder) {
- final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT);
+ final boolean isLaunchMenuMode = (mShortcutMenuMode == ShortcutMenuMode.LAUNCH);
+ final boolean isHardwareButtonTriggered =
+ (mShortcutButtonType == ACCESSIBILITY_SHORTCUT_KEY);
- holder.mLabelView.setEnabled(!isEditMenuMode);
- holder.mViewItem.setEnabled(!isEditMenuMode);
+ holder.mLabelView.setEnabled(isLaunchMenuMode || isHardwareButtonTriggered);
+ holder.mViewItem.setEnabled(isLaunchMenuMode || isHardwareButtonTriggered);
holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
holder.mViewItem.setVisibility(View.VISIBLE);
holder.mSwitchItem.setVisibility(View.GONE);
- holder.mItemContainer.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
+ holder.mItemContainer.setVisibility(isLaunchMenuMode ? View.GONE : View.VISIBLE);
}
private void updateInvisibleActionItemVisibility(@NonNull Context context,
@@ -574,8 +589,6 @@
ams.notifyAccessibilityButtonClicked(getDisplayId(), target.getId());
} else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
switchServiceState(target);
- } else {
- throw new IllegalArgumentException("Unsupported shortcut type:" + mShortcutType);
}
}
@@ -611,13 +624,15 @@
ComponentName.unflattenFromString(target.getId());
switch (target.getFragmentType()) {
+ case AccessibilityServiceFragmentType.LEGACY:
+ onLegacyTargetDeleted(targetComponentName);
+ break;
case AccessibilityServiceFragmentType.INVISIBLE:
onInvisibleTargetDeleted(targetComponentName);
break;
case AccessibilityServiceFragmentType.INTUITIVE:
onIntuitiveTargetDeleted(targetComponentName);
break;
- case AccessibilityServiceFragmentType.LEGACY:
case AccessibilityServiceFragmentType.BOUNCE:
// Do nothing
break;
@@ -633,6 +648,12 @@
}
}
+ private void onLegacyTargetDeleted(ComponentName componentName) {
+ if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
+ optOutValueFromSettings(this, ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE, componentName);
+ }
+ }
+
private void onInvisibleTargetDeleted(ComponentName componentName) {
if (mShortcutType == ACCESSIBILITY_BUTTON) {
optOutValueFromSettings(this, ACCESSIBILITY_BUTTON_USER_TYPE, componentName);
@@ -648,8 +669,6 @@
ACCESSIBILITY_BUTTON_USER_TYPE, componentName)) {
disableService(componentName);
}
- } else {
- throw new IllegalArgumentException("Unsupported shortcut type:" + mShortcutType);
}
}
@@ -658,8 +677,6 @@
optOutValueFromSettings(this, ACCESSIBILITY_BUTTON_USER_TYPE, componentName);
} else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
optOutValueFromSettings(this, ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE, componentName);
- } else {
- throw new IllegalArgumentException("Unsupported shortcut type:" + mShortcutType);
}
}
@@ -772,7 +789,7 @@
* @param componentName The component name that need to be opted out from Settings.
*/
private void optOutValueFromSettings(
- Context context, int shortcutType, ComponentName componentName) {
+ Context context, @UserShortcutType int shortcutType, ComponentName componentName) {
final StringJoiner joiner = new StringJoiner(String.valueOf(SERVICES_SEPARATOR));
final String targetsKey = convertToKey(shortcutType);
final String targetsValue = Settings.Secure.getString(context.getContentResolver(),
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 221cd6d..ef9b3d10 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -561,7 +561,7 @@
flags |= Document.FLAG_SUPPORTS_MOVE;
if (shouldBlockFromTree(docId)) {
- flags |= Document.FLAG_DIR_BLOCKS_TREE;
+ flags |= Document.FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE;
}
} else {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index b47080f..50a60a9 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -423,6 +423,14 @@
transaction->setFlags(ctrl, flags, mask);
}
+static void nativeSetFrameRateSelectionPriority(JNIEnv* env, jclass clazz, jlong transactionObj,
+ jlong nativeObject, jint priority) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+ transaction->setFrameRateSelectionPriority(ctrl, priority);
+}
+
static void nativeSetTransparentRegionHint(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jobject regionObj) {
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
@@ -818,13 +826,6 @@
return static_cast<jint>(SurfaceComposerClient::getActiveConfig(token));
}
-static jboolean nativeSetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj, jint id) {
- sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
- if (token == NULL) return JNI_FALSE;
- status_t err = SurfaceComposerClient::setActiveConfig(token, static_cast<int>(id));
- return err == NO_ERROR ? JNI_TRUE : JNI_FALSE;
-}
-
static jintArray nativeGetDisplayColorModes(JNIEnv* env, jclass, jobject tokenObj) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == NULL) return NULL;
@@ -1362,6 +1363,8 @@
(void*)nativeSetColorSpaceAgnostic },
{"nativeSetFlags", "(JJII)V",
(void*)nativeSetFlags },
+ {"nativeSetFrameRateSelectionPriority", "(JJI)V",
+ (void*)nativeSetFrameRateSelectionPriority },
{"nativeSetWindowCrop", "(JJIIII)V",
(void*)nativeSetWindowCrop },
{"nativeSetCornerRadius", "(JJF)V",
@@ -1390,8 +1393,6 @@
(void*)nativeGetDisplayConfigs },
{"nativeGetActiveConfig", "(Landroid/os/IBinder;)I",
(void*)nativeGetActiveConfig },
- {"nativeSetActiveConfig", "(Landroid/os/IBinder;I)Z",
- (void*)nativeSetActiveConfig },
{"nativeSetDesiredDisplayConfigSpecs",
"(Landroid/os/IBinder;Landroid/view/SurfaceControl$DesiredDisplayConfigSpecs;)Z",
(void*)nativeSetDesiredDisplayConfigSpecs },
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 39ea45a..05b573a 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -174,6 +174,8 @@
static constexpr const uint64_t UPPER_HALF_WORD_MASK = 0xFFFF'FFFF'0000'0000;
static constexpr const uint64_t LOWER_HALF_WORD_MASK = 0x0000'0000'FFFF'FFFF;
+static constexpr const char* kCurProfileDirPath = "/data/misc/profiles/cur";
+
/**
* The maximum value that the gUSAPPoolSizeMax variable may take. This value
* is a mirror of ZygoteServer.USAP_POOL_SIZE_MAX_LIMIT
@@ -1382,6 +1384,49 @@
freecon(dataDataContext);
}
+/**
+ * Like isolateAppData(), isolate jit profile directories, so apps don't see what
+ * other apps are installed by reading content inside /data/misc/profiles/cur.
+ *
+ * The implementation is similar to isolateAppData(), it creates a tmpfs
+ * on /data/misc/profiles/cur, and bind mounts related package profiles to it.
+ */
+static void isolateJitProfile(JNIEnv* env, jobjectArray pkg_data_info_list,
+ uid_t uid, const char* process_name, jstring managed_nice_name,
+ fail_fn_t fail_fn) {
+
+ auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
+ const userid_t user_id = multiuser_get_user_id(uid);
+
+ int size = (pkg_data_info_list != nullptr) ? env->GetArrayLength(pkg_data_info_list) : 0;
+ // Size should be a multiple of 3, as it contains list of <package_name, volume_uuid, inode>
+ if ((size % 3) != 0) {
+ fail_fn(CREATE_ERROR("Wrong pkg_inode_list size %d", size));
+ }
+
+ // Mount (namespace) tmpfs on profile directory, so apps no longer access
+ // the original profile directory anymore.
+ MountAppDataTmpFs(kCurProfileDirPath, fail_fn);
+
+ // Create profile directory for this user.
+ std::string actualCurUserProfile = StringPrintf("%s/%d", kCurProfileDirPath, user_id);
+ PrepareDir(actualCurUserProfile.c_str(), DEFAULT_DATA_DIR_PERMISSION, AID_ROOT, AID_ROOT,
+ fail_fn);
+
+ for (int i = 0; i < size; i += 3) {
+ jstring package_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i));
+ std::string packageName = extract_fn(package_str).value();
+
+ std::string actualCurPackageProfile = StringPrintf("%s/%s", actualCurUserProfile.c_str(),
+ packageName.c_str());
+ std::string mirrorCurPackageProfile = StringPrintf("/data_mirror/cur_profiles/%d/%s",
+ user_id, packageName.c_str());
+
+ PrepareDir(actualCurPackageProfile, DEFAULT_DATA_DIR_PERMISSION, uid, uid, fail_fn);
+ BindMount(mirrorCurPackageProfile, actualCurPackageProfile, fail_fn);
+ }
+}
+
// Utility routine to specialize a zygote child process.
static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits,
@@ -1432,8 +1477,8 @@
// so they can't even traverse CE / DE directories.
if (pkg_data_info_list != nullptr
&& GetBoolProperty(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true)) {
- isolateAppData(env, pkg_data_info_list, uid, process_name, managed_nice_name,
- fail_fn);
+ isolateAppData(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
+ isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
}
// If this zygote isn't root, it won't be able to create a process group,
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 082a289..1fcc8ac 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -39,6 +39,7 @@
"/apex/com.android.media/javalib/updatable-media.jar",
"/apex/com.android.mediaprovider/javalib/framework-mediaprovider.jar",
"/apex/com.android.os.statsd/javalib/framework-statsd.jar",
+ "/apex/com.android.permission/javalib/framework-permission.jar",
"/apex/com.android.sdkext/javalib/framework-sdkextensions.jar",
"/apex/com.android.wifi/javalib/framework-wifi.jar",
"/apex/com.android.tethering/javalib/framework-tethering.jar",
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 8f9c041..da8c944 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -165,6 +165,11 @@
(section).args = "security -L"
];
+ optional android.util.PersistedLogProto persisted_logs = 1116 [
+ (section).type = SECTION_COMMAND,
+ (section).args = "/system/bin/sh /system/bin/incident-helper-cmd -l run persisted_logs --limit 10MB"
+ ];
+
// Stack dumps
optional android.os.BackTraceProto native_traces = 1200 [
(section).type = SECTION_TOMBSTONE,
diff --git a/core/proto/android/stats/sysui/notification_enums.proto b/core/proto/android/stats/sysui/notification_enums.proto
new file mode 100644
index 0000000..0983702
--- /dev/null
+++ b/core/proto/android/stats/sysui/notification_enums.proto
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package android.stats.sysui;
+
+// Enum used in NotificationReported and NotificationChannelModified atoms
+enum NotificationImportance { // Constants from NotificationManager.java
+ IMPORTANCE_UNSPECIFIED = -1000; // Should not occur for real notifications.
+ IMPORTANCE_NONE = 0; // No importance: does not show in the shade.
+ IMPORTANCE_MIN = 1; // Minimum to show in the shade.
+ IMPORTANCE_LOW = 2; // Shows in shade, maybe status bar, no buzz/beep.
+ IMPORTANCE_DEFAULT = 3; // Shows everywhere, makes noise, no heads-up.
+ IMPORTANCE_HIGH = 4; // Shows everywhere, makes noise, heads-up, may full-screen.
+}
diff --git a/core/proto/android/util/log.proto b/core/proto/android/util/log.proto
index 09870ae..a214a1a 100644
--- a/core/proto/android/util/log.proto
+++ b/core/proto/android/util/log.proto
@@ -94,3 +94,16 @@
repeated BinaryLogEntry binary_logs = 2;
}
+message PersistedLogProto {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ repeated TextLogEntry main_logs = 1;
+ repeated TextLogEntry radio_logs = 2;
+ repeated TextLogEntry events_logs = 3;
+ repeated TextLogEntry system_logs = 4;
+ repeated TextLogEntry crash_logs = 5;
+ repeated TextLogEntry stats_logs = 6;
+ repeated TextLogEntry security_logs = 7;
+ repeated TextLogEntry kernel_logs = 8;
+}
+
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2665e8a..f8c5166 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -362,7 +362,6 @@
<protected-broadcast android:name="android.net.wifi.WIFI_STATE_CHANGED" />
<protected-broadcast android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" />
<protected-broadcast android:name="android.net.wifi.WIFI_CREDENTIAL_CHANGED" />
- <protected-broadcast android:name="android.net.wifi.WIFI_SCAN_AVAILABLE" />
<protected-broadcast android:name="android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED" />
<protected-broadcast android:name="android.net.wifi.rtt.action.WIFI_RTT_STATE_CHANGED" />
<protected-broadcast android:name="android.net.wifi.SCAN_RESULTS" />
@@ -377,6 +376,7 @@
<protected-broadcast android:name="android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION" />
<protected-broadcast android:name="android.net.wifi.action.PASSPOINT_LAUNCH_OSU_VIEW" />
<protected-broadcast android:name="android.net.wifi.action.WIFI_NETWORK_SUGGESTION_POST_CONNECTION" />
+ <protected-broadcast android:name="android.net.wifi.action.WIFI_SCAN_AVAILABLE" />
<protected-broadcast android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" />
<protected-broadcast android:name="android.net.wifi.supplicant.STATE_CHANGE" />
<protected-broadcast android:name="android.net.wifi.p2p.STATE_CHANGED" />
@@ -3980,6 +3980,13 @@
<permission android:name="android.permission.BACKUP"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows an application to make modifications to device settings such that these
+ modifications will be overridden by settings restore..
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE"
+ android:protectionLevel="signature|setup" />
+
<!-- @SystemApi Allows application to manage
{@link android.security.keystore.recovery.RecoveryController}.
<p>Not for use by third-party applications.
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 6435cdd..94f3b8a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2005,6 +2005,15 @@
<attr name="maxSdkVersion" />
</declare-styleable>
+ <!-- The <code>extension-sdk</code> tag is a child of the <uses-sdk> tag,
+ and specifies required extension sdk features. -->
+ <declare-styleable name="AndroidManifestExtensionSdk">
+ <!-- The extension SDK version that this tag refers to. -->
+ <attr name="sdkVersion" format="integer" />
+ <!-- The minimum version of the extension SDK this application requires.-->
+ <attr name="minExtensionVersion" format="integer" />
+ </declare-styleable>
+
<!-- The <code>library</code> tag declares that this apk is providing itself
as a shared library for other applications to use. It can only be used
with apks that are built in to the system image. Other apks can link to
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6f554f02..a78195b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4294,4 +4294,7 @@
<!-- Class name of the custom country detector to be used. -->
<string name="config_customCountryDetector" translatable="false">com.android.server.location.ComprehensiveCountryDetector</string>
+
+ <!-- Package name of the required service extension package. -->
+ <string name="config_servicesExtensionPackage" translatable="false">android.ext.services</string>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 9860830..36dbcbd 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3008,6 +3008,10 @@
<public name="supportsInlineSuggestions" />
<public name="crossProfile" />
<public name="canTakeScreenshot"/>
+ <!-- @hide @SystemApi -->
+ <public name="sdkVersion" />
+ <!-- @hide @SystemApi -->
+ <public name="minExtensionVersion" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 43c966c..669b41e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3818,4 +3818,5 @@
<java-symbol type="string" name="capability_desc_canTakeScreenshot" />
<java-symbol type="string" name="capability_title_canTakeScreenshot" />
+ <java-symbol type="string" name="config_servicesExtensionPackage" />
</resources>
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 2df6d1c..3836d6f 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -84,6 +84,11 @@
":FrameworksCoreTests_install_split_feature_a",
":FrameworksCoreTests_install_use_perm_good",
":FrameworksCoreTests_install_uses_feature",
+ ":FrameworksCoreTests_install_uses_sdk_0",
+ ":FrameworksCoreTests_install_uses_sdk_q0",
+ ":FrameworksCoreTests_install_uses_sdk_r",
+ ":FrameworksCoreTests_install_uses_sdk_r0",
+ ":FrameworksCoreTests_install_uses_sdk_r5",
":FrameworksCoreTests_install_verifier_bad",
":FrameworksCoreTests_install_verifier_good",
":FrameworksCoreTests_keyset_permdef_sa_unone",
diff --git a/core/tests/coretests/apks/install_uses_sdk/Android.bp b/core/tests/coretests/apks/install_uses_sdk/Android.bp
new file mode 100644
index 0000000..92b09ed
--- /dev/null
+++ b/core/tests/coretests/apks/install_uses_sdk/Android.bp
@@ -0,0 +1,39 @@
+android_test_helper_app {
+ name: "FrameworksCoreTests_install_uses_sdk_r0",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ manifest: "AndroidManifest-r0.xml",
+
+ srcs: ["**/*.java"],
+}
+
+android_test_helper_app {
+ name: "FrameworksCoreTests_install_uses_sdk_r5",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ manifest: "AndroidManifest-r5.xml",
+
+ srcs: ["**/*.java"],
+}
+
+android_test_helper_app {
+ name: "FrameworksCoreTests_install_uses_sdk_q0",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ manifest: "AndroidManifest-q0.xml",
+
+ srcs: ["**/*.java"],
+}
+
+android_test_helper_app {
+ name: "FrameworksCoreTests_install_uses_sdk_r",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ manifest: "AndroidManifest-r.xml",
+
+ srcs: ["**/*.java"],
+}
+
+android_test_helper_app {
+ name: "FrameworksCoreTests_install_uses_sdk_0",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ manifest: "AndroidManifest-0.xml",
+
+ srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-0.xml b/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-0.xml
new file mode 100644
index 0000000..634228b
--- /dev/null
+++ b/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-0.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.install_uses_sdk">
+
+ <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29">
+ <!-- This is invalid, because there is no sdk version specified -->
+ <extension-sdk android:minExtensionVersion="5" />
+ </uses-sdk>
+
+ <application>
+ </application>
+</manifest>
diff --git a/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-q0.xml b/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-q0.xml
new file mode 100644
index 0000000..8994966
--- /dev/null
+++ b/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-q0.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.install_uses_sdk">
+
+ <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29">
+ <!-- This fails because 29 doesn't have an extension sdk -->
+ <extension-sdk android:sdkVersion="29" android:minExtensionVersion="0" />
+ </uses-sdk>
+
+ <application>
+ </application>
+</manifest>
diff --git a/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r.xml b/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r.xml
new file mode 100644
index 0000000..0d0d8b9
--- /dev/null
+++ b/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.install_uses_sdk">
+
+ <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29">
+ <!-- This is invalid, because there is no minimum extension version specified -->
+ <extension-sdk android:sdkVersion="10000" />
+ </uses-sdk>
+
+ <application>
+ </application>
+</manifest>
diff --git a/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r0.xml b/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r0.xml
new file mode 100644
index 0000000..a987afa
--- /dev/null
+++ b/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r0.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.install_uses_sdk">
+
+ <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29">
+ <extension-sdk android:sdkVersion="10000" android:minExtensionVersion="0" />
+ </uses-sdk>
+
+ <application>
+ </application>
+</manifest>
diff --git a/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r5.xml b/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r5.xml
new file mode 100644
index 0000000..9860096
--- /dev/null
+++ b/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r5.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.install_uses_sdk">
+
+ <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29">
+ <!-- This will fail to install, because minExtensionVersion is not met -->
+ <extension-sdk android:sdkVersion="10000" android:minExtensionVersion="5" />
+ </uses-sdk>
+
+ <application>
+ </application>
+</manifest>
diff --git a/core/tests/coretests/apks/install_uses_sdk/res/values/strings.xml b/core/tests/coretests/apks/install_uses_sdk/res/values/strings.xml
new file mode 100644
index 0000000..3b8b3b1
--- /dev/null
+++ b/core/tests/coretests/apks/install_uses_sdk/res/values/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Just need this dummy file to have something to build. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="dummy">dummy</string>
+</resources>
diff --git a/core/tests/coretests/src/android/app/appsearch/SearchResultsTest.java b/core/tests/coretests/src/android/app/appsearch/SearchResultsTest.java
new file mode 100644
index 0000000..21259cc
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/SearchResultsTest.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.app.appsearch;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import androidx.test.filters.SmallTest;
+
+import com.google.android.icing.proto.DocumentProto;
+import com.google.android.icing.proto.SearchResultProto;
+
+import org.junit.Test;
+
+@SmallTest
+public class SearchResultsTest {
+
+ @Test
+ public void testSearchResultsEqual() {
+ final String uri = "testUri";
+ final String schemaType = "testSchema";
+ SearchResultProto.ResultProto result1 = SearchResultProto.ResultProto.newBuilder()
+ .setDocument(DocumentProto.newBuilder()
+ .setUri(uri)
+ .setSchema(schemaType)
+ .build())
+ .build();
+ SearchResultProto searchResults1 = SearchResultProto.newBuilder()
+ .addResults(result1)
+ .build();
+ SearchResults res1 = new SearchResults(searchResults1);
+ SearchResultProto.ResultProto result2 = SearchResultProto.ResultProto.newBuilder()
+ .setDocument(DocumentProto.newBuilder()
+ .setUri(uri)
+ .setSchema(schemaType)
+ .build())
+ .build();
+ SearchResultProto searchResults2 = SearchResultProto.newBuilder()
+ .addResults(result2)
+ .build();
+ SearchResults res2 = new SearchResults(searchResults2);
+ assertThat(res1.toString()).isEqualTo(res2.toString());
+ }
+
+ @Test
+ public void buildSearchSpecWithoutTermMatchType() {
+ assertThrows(RuntimeException.class, () -> SearchSpec.newBuilder()
+ .setSchemaTypes("testSchemaType")
+ .build());
+ }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index 1410f4f..09ea1b1 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -124,7 +124,7 @@
assertArrayEquals(new int[] {}, path(ON_START));
assertArrayEquals(new int[] {ON_RESUME}, path(ON_RESUME));
assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE}, path(ON_PAUSE));
- assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE, ON_STOP}, path(ON_STOP));
+ assertArrayEquals(new int[] {ON_STOP}, path(ON_STOP));
assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY}, path(ON_DESTROY));
}
@@ -362,7 +362,9 @@
public void testClosestStateResolutionFromOnStart() {
mClientRecord.setState(ON_START);
assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
- new int[] {ON_CREATE, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY})));
+ new int[] {ON_CREATE, ON_RESUME, ON_PAUSE, ON_DESTROY})));
+ assertEquals(ON_STOP, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_STOP})));
assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
new int[] {ON_CREATE})));
}
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
index 4402190..dfd762b 100644
--- a/core/tests/coretests/src/android/content/pm/PackageParserTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.apex.ApexInfo;
import android.content.Context;
@@ -486,4 +487,34 @@
assertTrue((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0);
assertTrue((pi.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0);
}
+
+ @Test
+ public void testUsesSdk() throws Exception {
+ parsePackage("install_uses_sdk.apk_r0", R.raw.install_uses_sdk_r0, x -> x);
+ try {
+ parsePackage("install_uses_sdk.apk_r5", R.raw.install_uses_sdk_r5, x -> x);
+ fail("Expected parsing exception due to incompatible extension SDK version");
+ } catch (PackageParser.PackageParserException expected) {
+ assertEquals(PackageManager.INSTALL_FAILED_OLDER_SDK, expected.error);
+ }
+ try {
+ parsePackage("install_uses_sdk.apk_q0", R.raw.install_uses_sdk_q0, x -> x);
+ fail("Expected parsing exception due to non-existent extension SDK");
+ } catch (PackageParser.PackageParserException expected) {
+ assertEquals(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, expected.error);
+ }
+ try {
+ parsePackage("install_uses_sdk.apk_r", R.raw.install_uses_sdk_r, x -> x);
+ fail("Expected parsing exception due to unspecified extension SDK version");
+ } catch (PackageParser.PackageParserException expected) {
+ assertEquals(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, expected.error);
+ }
+ try {
+ parsePackage("install_uses_sdk.apk_0", R.raw.install_uses_sdk_0, x -> x);
+ fail("Expected parsing exception due to unspecified extension SDK");
+ } catch (PackageParser.PackageParserException expected) {
+ assertEquals(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, expected.error);
+ }
+
+ }
}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 1db96b1..628f7ec 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -16,9 +16,13 @@
package android.view;
+import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
+import static android.view.InsetsController.ANIMATION_TYPE_NONE;
+import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
@@ -40,12 +44,15 @@
import android.view.WindowInsets.Type;
import android.view.WindowManager.BadTokenException;
import android.view.WindowManager.LayoutParams;
+import android.view.test.InsetsModeSession;
import android.widget.TextView;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.AfterClass;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -69,6 +76,17 @@
private SurfaceSession mSession = new SurfaceSession();
private SurfaceControl mLeash;
private ViewRootImpl mViewRoot;
+ private static InsetsModeSession sInsetsModeSession;
+
+ @BeforeClass
+ public static void setupOnce() {
+ sInsetsModeSession = new InsetsModeSession(NEW_INSETS_MODE_FULL);
+ }
+
+ @AfterClass
+ public static void tearDownOnce() {
+ sInsetsModeSession.close();
+ }
@Before
public void setup() {
@@ -86,6 +104,11 @@
}
mController = new InsetsController(mViewRoot);
final Rect rect = new Rect(5, 5, 5, 5);
+ mController.getState().getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 10));
+ mController.getState().getSource(ITYPE_NAVIGATION_BAR).setFrame(
+ new Rect(0, 90, 100, 100));
+ mController.getState().getSource(ITYPE_IME).setFrame(new Rect(0, 50, 100, 100));
+ mController.getState().setDisplayFrame(new Rect(0, 0, 100, 100));
mController.calculateInsets(
false,
false,
@@ -93,7 +116,6 @@
Insets.of(10, 10, 10, 10), rect, rect, rect, rect),
rect, rect, SOFT_INPUT_ADJUST_RESIZE);
mController.onFrameChanged(new Rect(0, 0, 100, 100));
- mController.getState().setDisplayFrame(new Rect(0, 0, 100, 100));
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -205,18 +227,24 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
int types = Type.navigationBars() | Type.systemBars();
- // test show select types.
- mController.show(types);
+ // test hide select types.
+ mController.hide(types);
+ assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
+ assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
mController.cancelExistingAnimation();
- assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
- assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
+ assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(ITYPE_STATUS_BAR));
+ assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
// test hide all
- mController.hide(types);
+ mController.show(types);
+ assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
+ assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_STATUS_BAR));
mController.cancelExistingAnimation();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -271,30 +299,38 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// start two animations and see if previous is cancelled and final state is reached.
- mController.show(Type.navigationBars());
- mController.show(Type.systemBars());
- mController.cancelExistingAnimation();
- assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
- assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
-
mController.hide(Type.navigationBars());
mController.hide(Type.systemBars());
+ assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
+ assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
mController.cancelExistingAnimation();
assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
+ mController.show(Type.navigationBars());
+ mController.show(Type.systemBars());
+ assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
+ assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_STATUS_BAR));
+ mController.cancelExistingAnimation();
+ assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
+
int types = Type.navigationBars() | Type.systemBars();
// show two at a time and hide one by one.
mController.show(types);
mController.hide(Type.navigationBars());
+ assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
+ assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(ITYPE_STATUS_BAR));
mController.cancelExistingAnimation();
assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.hide(Type.systemBars());
+ assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
+ assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
mController.cancelExistingAnimation();
assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
index e23c51e..8e24907 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
@@ -46,6 +46,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@@ -193,4 +194,15 @@
}
});
}
+
+ @Test
+ public void testSetWindowMagnificationConnection() throws Exception {
+ AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
+ IWindowMagnificationConnection connection = Mockito.mock(
+ IWindowMagnificationConnection.class);
+
+ manager.setWindowMagnificationConnection(connection);
+
+ verify(mMockService).setWindowMagnificationConnection(connection);
+ }
}
diff --git a/core/tests/overlaytests/remount/host/AndroidTest.xml b/core/tests/overlaytests/remount/host/AndroidTest.xml
index 11eadf1a..087b731 100644
--- a/core/tests/overlaytests/remount/host/AndroidTest.xml
+++ b/core/tests/overlaytests/remount/host/AndroidTest.xml
@@ -19,9 +19,6 @@
<option name="test-tag" value="OverlayRemountedTest" />
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="remount" />
- </target_preparer>
<test class="com.android.tradefed.testtype.HostTest">
<option name="jar" value="OverlayRemountedTest.jar" />
diff --git a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlayHostTest.java b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlayHostTest.java
deleted file mode 100644
index 84af187..0000000
--- a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlayHostTest.java
+++ /dev/null
@@ -1,114 +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.overlaytest.remounted;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-
-import org.junit.Rule;
-import org.junit.rules.RuleChain;
-import org.junit.rules.TemporaryFolder;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class OverlayHostTest extends BaseHostJUnit4Test {
- private static final long TIME_OUT_MS = 30000;
- private static final String RES_INSTRUMENTATION_ARG = "res";
- private static final String OVERLAY_INSTRUMENTATION_ARG = "overlays";
- private static final String RESOURCES_TYPE_SUFFIX = "_type";
- private static final String RESOURCES_DATA_SUFFIX = "_data";
-
- public final TemporaryFolder mTemporaryFolder = new TemporaryFolder();
- public final SystemPreparer mPreparer = new SystemPreparer(mTemporaryFolder, this::getDevice);
-
- @Rule
- public final RuleChain ruleChain = RuleChain.outerRule(mTemporaryFolder).around(mPreparer);
- private Map<String, String> mLastResults;
-
- /**
- * Retrieves the values of the resources in the test package. The test package must use the
- * {@link com.android.overlaytest.remounted.target.ResourceRetrievalRunner} instrumentation.
- **/
- void retrieveResource(String testPackageName, List<String> requiredOverlayPaths,
- String... resourceNames) throws DeviceNotAvailableException {
- final HashMap<String, String> args = new HashMap<>();
- if (!requiredOverlayPaths.isEmpty()) {
- // Enclose the require overlay paths in quotes so the arguments will be string arguments
- // rather than file arguments.
- args.put(OVERLAY_INSTRUMENTATION_ARG,
- String.format("\"%s\"", String.join(" ", requiredOverlayPaths)));
- }
-
- if (resourceNames.length == 0) {
- throw new IllegalArgumentException("Must specify at least one resource to retrieve.");
- }
-
- // Pass the names of the resources to retrieve into the test as one string.
- args.put(RES_INSTRUMENTATION_ARG,
- String.format("\"%s\"", String.join(" ", resourceNames)));
-
- runDeviceTests(getDevice(), null, testPackageName, null, null, null, TIME_OUT_MS,
- TIME_OUT_MS, TIME_OUT_MS, false, false, args);
-
- // Retrieve the results of the most recently run test.
- mLastResults = (getLastDeviceRunResults().getRunMetrics() == mLastResults) ? null :
- getLastDeviceRunResults().getRunMetrics();
- }
-
- /** Returns the base resource directories of the specified packages. */
- List<String> getPackagePaths(String... packageNames)
- throws DeviceNotAvailableException {
- final ArrayList<String> paths = new ArrayList<>();
- for (String packageName : packageNames) {
- // Use the package manager shell command to find the path of the package.
- final String result = getDevice().executeShellCommand(
- String.format("pm dump %s | grep \"resourcePath=\"", packageName));
- assertNotNull("Failed to find path for package " + packageName, result);
- int splitIndex = result.indexOf('=');
- assertTrue(splitIndex >= 0);
- paths.add(result.substring(splitIndex + 1).trim());
- }
- return paths;
- }
-
- /** Builds the full name of a resource in the form package:type/entry. */
- String resourceName(String pkg, String type, String entry) {
- return String.format("%s:%s/%s", pkg, type, entry);
- }
-
- /**
- * Asserts that the type and data of a a previously retrieved is the same as expected.
- * @param resourceName the full name of the resource in the form package:type/entry
- * @param type the expected {@link android.util.TypedValue} type of the resource
- * @param data the expected value of the resource when coerced to a string using
- * {@link android.util.TypedValue#coerceToString()}
- **/
- void assertResource(String resourceName, int type, String data) {
- assertNotNull("Failed to get test results", mLastResults);
- assertNotEquals("No resource values were retrieved", mLastResults.size(), 0);
- assertEquals("" + type, mLastResults.get(resourceName + RESOURCES_TYPE_SUFFIX));
- assertEquals("" + data, mLastResults.get(resourceName + RESOURCES_DATA_SUFFIX));
- }
-}
diff --git a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlaySharedLibraryTest.java b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlaySharedLibraryTest.java
index 4939e16..06b2ac8 100644
--- a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlaySharedLibraryTest.java
+++ b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/OverlaySharedLibraryTest.java
@@ -16,17 +16,21 @@
package com.android.overlaytest.remounted;
+import static org.junit.Assert.assertTrue;
+
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
-import java.util.Collections;
-import java.util.List;
-
@RunWith(DeviceJUnit4ClassRunner.class)
-public class OverlaySharedLibraryTest extends OverlayHostTest {
+public class OverlaySharedLibraryTest extends BaseHostJUnit4Test {
private static final String TARGET_APK = "OverlayRemountedTest_Target.apk";
private static final String TARGET_PACKAGE = "com.android.overlaytest.remounted.target";
private static final String SHARED_LIBRARY_APK =
@@ -38,6 +42,17 @@
private static final String SHARED_LIBRARY_OVERLAY_PACKAGE =
"com.android.overlaytest.remounted.shared_library.overlay";
+ public final TemporaryFolder temporaryFolder = new TemporaryFolder();
+ public final SystemPreparer preparer = new SystemPreparer(temporaryFolder, this::getDevice);
+
+ @Rule
+ public final RuleChain ruleChain = RuleChain.outerRule(temporaryFolder).around(preparer);
+
+ @Before
+ public void startBefore() throws DeviceNotAvailableException {
+ getDevice().waitForDeviceAvailable();
+ }
+
@Test
public void testSharedLibrary() throws Exception {
final String targetResource = resourceName(TARGET_PACKAGE, "bool",
@@ -45,23 +60,20 @@
final String libraryResource = resourceName(SHARED_LIBRARY_PACKAGE, "bool",
"shared_library_overlaid");
- mPreparer.pushResourceFile(SHARED_LIBRARY_APK, "/product/app/SharedLibrary.apk")
+ preparer.pushResourceFile(SHARED_LIBRARY_APK, "/product/app/SharedLibrary.apk")
.installResourceApk(SHARED_LIBRARY_OVERLAY_APK, SHARED_LIBRARY_OVERLAY_PACKAGE)
.reboot()
.setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, false)
.installResourceApk(TARGET_APK, TARGET_PACKAGE);
// The shared library resource is not currently overlaid.
- retrieveResource(Collections.emptyList(), targetResource, libraryResource);
- assertResource(targetResource, 0x12 /* TYPE_INT_BOOLEAN */, "false");
- assertResource(libraryResource, 0x12 /* TYPE_INT_BOOLEAN */, "false");
+ assertResource(targetResource, "false");
+ assertResource(libraryResource, "false");
// Overlay the shared library resource.
- mPreparer.setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, true);
- retrieveResource(getPackagePaths(SHARED_LIBRARY_OVERLAY_PACKAGE), targetResource,
- libraryResource);
- assertResource(targetResource, 0x12 /* TYPE_INT_BOOLEAN */, "true");
- assertResource(libraryResource, 0x12 /* TYPE_INT_BOOLEAN */, "true");
+ preparer.setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, true);
+ assertResource(targetResource, "true");
+ assertResource(libraryResource, "true");
}
@Test
@@ -71,20 +83,27 @@
final String libraryResource = resourceName(SHARED_LIBRARY_PACKAGE, "bool",
"shared_library_overlaid");
- mPreparer.pushResourceFile(SHARED_LIBRARY_APK, "/product/app/SharedLibrary.apk")
+ preparer.pushResourceFile(SHARED_LIBRARY_APK, "/product/app/SharedLibrary.apk")
.installResourceApk(SHARED_LIBRARY_OVERLAY_APK, SHARED_LIBRARY_OVERLAY_PACKAGE)
.setOverlayEnabled(SHARED_LIBRARY_OVERLAY_PACKAGE, true)
.reboot()
.installResourceApk(TARGET_APK, TARGET_PACKAGE);
- retrieveResource(getPackagePaths(SHARED_LIBRARY_OVERLAY_PACKAGE), targetResource,
- libraryResource);
- assertResource(targetResource, 0x12 /* TYPE_INT_BOOLEAN */, "true");
- assertResource(libraryResource, 0x12 /* TYPE_INT_BOOLEAN */, "true");
+ assertResource(targetResource, "true");
+ assertResource(libraryResource, "true");
}
- private void retrieveResource(List<String> requiredOverlayPaths, String... resourceNames)
+ /** Builds the full name of a resource in the form package:type/entry. */
+ String resourceName(String pkg, String type, String entry) {
+ return String.format("%s:%s/%s", pkg, type, entry);
+ }
+
+ void assertResource(String resourceName, String expectedValue)
throws DeviceNotAvailableException {
- retrieveResource(TARGET_PACKAGE, requiredOverlayPaths, resourceNames);
+ final String result = getDevice().executeShellCommand(
+ String.format("cmd overlay lookup %s %s", TARGET_PACKAGE, resourceName));
+ assertTrue(String.format("expected: <[%s]> in: <[%s]>", expectedValue, result),
+ result.equals(expectedValue + "\n") ||
+ result.endsWith("-> " + expectedValue + "\n"));
}
}
diff --git a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/SystemPreparer.java b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/SystemPreparer.java
index 7028f2f..8696091 100644
--- a/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/SystemPreparer.java
+++ b/core/tests/overlaytests/remount/host/src/com/android/overlaytest/remounted/SystemPreparer.java
@@ -38,8 +38,7 @@
import java.util.concurrent.TimeoutException;
class SystemPreparer extends ExternalResource {
- private static final long REBOOT_SLEEP_MS = 30000;
- private static final long OVERLAY_ENABLE_TIMEOUT_MS = 20000;
+ private static final long OVERLAY_ENABLE_TIMEOUT_MS = 30000;
// The paths of the files pushed onto the device through this rule.
private ArrayList<String> mPushedFiles = new ArrayList<>();
@@ -59,6 +58,7 @@
SystemPreparer pushResourceFile(String resourcePath,
String outputPath) throws DeviceNotAvailableException, IOException {
final ITestDevice device = mDeviceProvider.getDevice();
+ device.executeAdbCommand("remount");
assertTrue(device.pushFile(copyResourceToTemp(resourcePath), outputPath));
mPushedFiles.add(outputPath);
return this;
@@ -77,7 +77,7 @@
/** Sets the enable state of an overlay pacakage. */
SystemPreparer setOverlayEnabled(String packageName, boolean enabled)
- throws ExecutionException, TimeoutException {
+ throws ExecutionException, DeviceNotAvailableException {
final ITestDevice device = mDeviceProvider.getDevice();
// Wait for the overlay to change its enabled state.
@@ -86,8 +86,10 @@
device.executeShellCommand(String.format("cmd overlay %s %s",
enabled ? "enable" : "disable", packageName));
- final String pattern = (enabled ? "[x]" : "[ ]") + " " + packageName;
- if (device.executeShellCommand("cmd overlay list").contains(pattern)) {
+ final String result = device.executeShellCommand("cmd overlay dump " + packageName);
+ final int startIndex = result.indexOf("mIsEnabled");
+ final int endIndex = result.indexOf('\n', startIndex);
+ if (result.substring(startIndex, endIndex).contains((enabled) ? "true" : "false")) {
return true;
}
}
@@ -98,6 +100,8 @@
try {
enabledListener.get(OVERLAY_ENABLE_TIMEOUT_MS, MILLISECONDS);
} catch (InterruptedException ignored) {
+ } catch (TimeoutException e) {
+ throw new IllegalStateException(device.executeShellCommand("cmd overlay list"));
}
return this;
@@ -106,14 +110,7 @@
/** Restarts the device and waits until after boot is completed. */
SystemPreparer reboot() throws DeviceNotAvailableException {
final ITestDevice device = mDeviceProvider.getDevice();
- device.executeShellCommand("stop");
- device.executeShellCommand("start");
- try {
- // Sleep until the device is ready for test execution.
- Thread.sleep(REBOOT_SLEEP_MS);
- } catch (InterruptedException ignored) {
- }
-
+ device.reboot();
return this;
}
@@ -141,12 +138,14 @@
protected void after() {
final ITestDevice device = mDeviceProvider.getDevice();
try {
+ device.executeAdbCommand("remount");
for (final String file : mPushedFiles) {
device.deleteFile(file);
}
for (final String packageName : mInstalledPackages) {
device.uninstallPackage(packageName);
}
+ device.reboot();
} catch (DeviceNotAvailableException e) {
Assert.fail(e.toString());
}
diff --git a/core/tests/overlaytests/remount/target/AndroidManifest.xml b/core/tests/overlaytests/remount/target/AndroidManifest.xml
index 32fec43..dc07dca 100644
--- a/core/tests/overlaytests/remount/target/AndroidManifest.xml
+++ b/core/tests/overlaytests/remount/target/AndroidManifest.xml
@@ -23,8 +23,4 @@
<uses-library android:name="com.android.overlaytest.remounted.shared_library"
android:required="true" />
</application>
-
- <instrumentation android:name="com.android.overlaytest.remounted.target.ResourceRetrievalRunner"
- android:targetPackage="com.android.overlaytest.remounted.target"
- android:label="Remounted system RRO tests" />
</manifest>
diff --git a/core/tests/overlaytests/remount/target/src/com/android/overlaytest/remounted/target/ResourceRetrievalRunner.java b/core/tests/overlaytests/remount/target/src/com/android/overlaytest/remounted/target/ResourceRetrievalRunner.java
deleted file mode 100644
index 2e4c211..0000000
--- a/core/tests/overlaytests/remount/target/src/com/android/overlaytest/remounted/target/ResourceRetrievalRunner.java
+++ /dev/null
@@ -1,140 +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.overlaytest.remounted.target;
-
-import android.app.Activity;
-import android.app.Instrumentation;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.util.Log;
-import android.util.TypedValue;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.concurrent.Executor;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeUnit;
-
-/**
- * An {@link Instrumentation} that retrieves the value of specified resources within the
- * application.
- **/
-public class ResourceRetrievalRunner extends Instrumentation {
- private static final String TAG = ResourceRetrievalRunner.class.getSimpleName();
-
- // A list of whitespace separated resource names of which to retrieve the resource values.
- private static final String RESOURCE_LIST_TAG = "res";
-
- // A list of whitespace separated overlay package paths that must be present before retrieving
- // resource values.
- private static final String REQUIRED_OVERLAYS_LIST_TAG = "overlays";
-
- // The suffixes of the keys returned from the instrumentation. To retrieve the type of a
- // resource looked up with the instrumentation, append the {@link #RESOURCES_TYPE_SUFFIX} suffix
- // to the end of the name of the resource. For the value of a resource, use
- // {@link #RESOURCES_DATA_SUFFIX} instead.
- private static final String RESOURCES_TYPE_SUFFIX = "_type";
- private static final String RESOURCES_DATA_SUFFIX = "_data";
-
- // The amount of time in seconds to wait for the overlays to be present in the AssetManager.
- private static final int OVERLAY_PATH_TIMEOUT = 60;
-
- private final ArrayList<String> mResourceNames = new ArrayList<>();
- private final ArrayList<String> mOverlayPaths = new ArrayList<>();
- private final Bundle mResult = new Bundle();
-
- /**
- * Receives the instrumentation arguments and runs the resource retrieval.
- * The entry with key {@link #RESOURCE_LIST_TAG} in the {@link Bundle} arguments is a
- * whitespace separated string of resource names of which to retrieve the resource values.
- * The entry with key {@link #REQUIRED_OVERLAYS_LIST_TAG} in the {@link Bundle} arguments is a
- * whitespace separated string of overlay package paths prefixes that must be present before
- * retrieving the resource values.
- */
- @Override
- public void onCreate(Bundle arguments) {
- super.onCreate(arguments);
- mResourceNames.addAll(Arrays.asList(arguments.getString(RESOURCE_LIST_TAG).split(" ")));
- if (arguments.containsKey(REQUIRED_OVERLAYS_LIST_TAG)) {
- mOverlayPaths.addAll(Arrays.asList(
- arguments.getString(REQUIRED_OVERLAYS_LIST_TAG).split(" ")));
- }
- start();
- }
-
- @Override
- public void onStart() {
- final Resources res = getContext().getResources();
- res.getAssets().setResourceResolutionLoggingEnabled(true);
-
- if (!mOverlayPaths.isEmpty()) {
- Log.d(TAG, String.format("Waiting for overlay paths [%s]",
- String.join(",", mOverlayPaths)));
-
- // Wait for all required overlays to be present in the AssetManager.
- final FutureTask<Boolean> overlayListener = new FutureTask<>(() -> {
- while (!mOverlayPaths.isEmpty()) {
- final String[] apkPaths = res.getAssets().getApkPaths();
- for (String path : apkPaths) {
- for (String overlayPath : mOverlayPaths) {
- if (path.startsWith(overlayPath)) {
- mOverlayPaths.remove(overlayPath);
- break;
- }
- }
- }
- }
- return true;
- });
-
- try {
- final Executor executor = (t) -> new Thread(t).start();
- executor.execute(overlayListener);
- overlayListener.get(OVERLAY_PATH_TIMEOUT, TimeUnit.SECONDS);
- } catch (Exception e) {
- Log.e(TAG, String.format("Failed to wait for required overlays [%s]",
- String.join(",", mOverlayPaths)), e);
- finish(Activity.RESULT_CANCELED, mResult);
- }
- }
-
- // Retrieve the values for each resource passed in.
- final TypedValue typedValue = new TypedValue();
- for (final String resourceName : mResourceNames) {
- try {
- final int resId = res.getIdentifier(resourceName, null, null);
- res.getValue(resId, typedValue, true);
- Log.d(TAG, String.format("Resolution for 0x%s: %s", Integer.toHexString(resId),
- res.getAssets().getLastResourceResolution()));
- } catch (Resources.NotFoundException e) {
- Log.e(TAG, "Failed to retrieve value for resource " + resourceName, e);
- finish(Activity.RESULT_CANCELED, mResult);
- }
-
- putValue(resourceName, typedValue);
- }
-
- finish(Activity.RESULT_OK, mResult);
- }
-
- private void putValue(String resourceName, TypedValue value) {
- mResult.putInt(resourceName + RESOURCES_TYPE_SUFFIX, value.type);
- final CharSequence textValue = value.coerceToString();
- mResult.putString(resourceName + RESOURCES_DATA_SUFFIX,
- textValue == null ? "null" : textValue.toString());
- }
-}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 0574775..877ef26 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -180,7 +180,6 @@
<assign-permission name="android.permission.ACCESS_LOWPAN_STATE" uid="lowpan" />
<assign-permission name="android.permission.MANAGE_LOWPAN_INTERFACES" uid="lowpan" />
- <assign-permission name="android.permission.BATTERY_STATS" uid="statsd" />
<assign-permission name="android.permission.DUMP" uid="statsd" />
<assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="statsd" />
<assign-permission name="android.permission.STATSCOMPANION" uid="statsd" />
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index b93759f..c445885 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -108,10 +108,26 @@
}
}
+// FIXME: Share with the version in android_bitmap.cpp?
+// 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},
+}};
+
sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) {
if (dataspace == HAL_DATASPACE_UNKNOWN) {
return SkColorSpace::MakeSRGB();
}
+ if (dataspace == HAL_DATASPACE_DCI_P3) {
+ // This cannot be handled by the switch statements below because it
+ // 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);
+ }
skcms_Matrix3x3 gamut;
switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
@@ -152,10 +168,12 @@
return SkColorSpace::MakeRGB({2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, 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:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut);
+ case HAL_DATASPACE_TRANSFER_SMPTE_170M:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, gamut);
case HAL_DATASPACE_TRANSFER_UNSPECIFIED:
return nullptr;
- case HAL_DATASPACE_TRANSFER_SMPTE_170M:
- case HAL_DATASPACE_TRANSFER_ST2084:
case HAL_DATASPACE_TRANSFER_HLG:
default:
ALOGV("Unsupported Gamma: %d", dataspace);
diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
index 751bb6a..127d00c 100644
--- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
+++ b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
@@ -110,7 +110,6 @@
* Logs the status of a location report received from the HAL
*/
public void logReceivedLocationStatus(boolean isSuccessful) {
- StatsLog.write(StatsLog.GPS_LOCATION_STATUS_REPORTED, isSuccessful);
if (!isSuccessful) {
mLocationFailureStatistics.addItem(1.0);
return;
@@ -127,7 +126,6 @@
DEFAULT_TIME_BETWEEN_FIXES_MILLISECS, desiredTimeBetweenFixesMilliSeconds)) - 1;
if (numReportMissed > 0) {
for (int i = 0; i < numReportMissed; i++) {
- StatsLog.write(StatsLog.GPS_LOCATION_STATUS_REPORTED, false);
mLocationFailureStatistics.addItem(1.0);
}
}
@@ -138,7 +136,6 @@
*/
public void logTimeToFirstFixMilliSecs(int timeToFirstFixMilliSeconds) {
mTimeToFirstFixSecStatistics.addItem((double) (timeToFirstFixMilliSeconds / 1000));
- StatsLog.write(StatsLog.GPS_TIME_TO_FIRST_FIX_REPORTED, timeToFirstFixMilliSeconds);
}
/**
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index ece5335..8cd3c6e 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -598,6 +598,11 @@
private boolean mMuteHapticChannels = true;
private HashSet<String> mTags = new HashSet<String>();
private Bundle mBundle;
+ private int mPrivacySensitive = PRIVACY_SENSITIVE_DEFAULT;
+
+ private static final int PRIVACY_SENSITIVE_DEFAULT = -1;
+ private static final int PRIVACY_SENSITIVE_DISABLED = 0;
+ private static final int PRIVACY_SENSITIVE_ENABLED = 1;
/**
* Constructs a new Builder with the defaults.
@@ -638,11 +643,20 @@
if (mMuteHapticChannels) {
aa.mFlags |= FLAG_MUTE_HAPTIC;
}
- // capturing for camcorder of communication is private by default to
- // reflect legacy behavior
- if (aa.mSource == MediaRecorder.AudioSource.VOICE_COMMUNICATION
- || aa.mSource == MediaRecorder.AudioSource.CAMCORDER) {
+
+ if (mPrivacySensitive == PRIVACY_SENSITIVE_DEFAULT) {
+ // capturing for camcorder or communication is private by default to
+ // reflect legacy behavior
+ if (mSource == MediaRecorder.AudioSource.VOICE_COMMUNICATION
+ || mSource == MediaRecorder.AudioSource.CAMCORDER) {
+ aa.mFlags |= FLAG_CAPTURE_PRIVATE;
+ } else {
+ aa.mFlags &= ~FLAG_CAPTURE_PRIVATE;
+ }
+ } else if (mPrivacySensitive == PRIVACY_SENSITIVE_ENABLED) {
aa.mFlags |= FLAG_CAPTURE_PRIVATE;
+ } else {
+ aa.mFlags &= ~FLAG_CAPTURE_PRIVATE;
}
aa.mTags = (HashSet<String>) mTags.clone();
aa.mFormattedTags = TextUtils.join(";", mTags);
@@ -967,6 +981,20 @@
mMuteHapticChannels = muted;
return this;
}
+
+ /**
+ * @hide
+ * Indicates if an AudioRecord build with this AudioAttributes is privacy sensitive or not.
+ * See {@link AudioRecord.Builder#setPrivacySensitive(boolean)}.
+ * @param privacySensitive True if capture must be marked as privacy sensitive,
+ * false otherwise.
+ * @return the same Builder instance.
+ */
+ public @NonNull Builder setPrivacySensitive(boolean privacySensitive) {
+ mPrivacySensitive =
+ privacySensitive ? PRIVACY_SENSITIVE_ENABLED : PRIVACY_SENSITIVE_DISABLED;
+ return this;
+ }
};
@Override
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index fd3523d..4d26b8d 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -315,7 +315,7 @@
* @hide
* Class constructor with {@link AudioAttributes} and {@link AudioFormat}.
* @param attributes a non-null {@link AudioAttributes} instance. Use
- * {@link AudioAttributes.Builder#setAudioSource(int)} for configuring the audio
+ * {@link AudioAttributes.Builder#setCapturePreset(int)} for configuring the audio
* source for this instance.
* @param format a non-null {@link AudioFormat} instance describing the format of the data
* that will be recorded through this AudioRecord. See {@link AudioFormat.Builder} for
@@ -754,17 +754,10 @@
"Cannot request private capture with source: " + source);
}
- int flags = mAttributes.getAllFlags();
- if (mPrivacySensitive == PRIVACY_SENSITIVE_DISABLED) {
- flags &= ~AudioAttributes.FLAG_CAPTURE_PRIVATE;
- } else if (mPrivacySensitive == PRIVACY_SENSITIVE_ENABLED) {
- flags |= AudioAttributes.FLAG_CAPTURE_PRIVATE;
- }
- if (flags != mAttributes.getAllFlags()) {
- mAttributes = new AudioAttributes.Builder(mAttributes)
- .replaceFlags(flags)
- .build();
- }
+ mAttributes = new AudioAttributes.Builder(mAttributes)
+ .setInternalCapturePreset(source)
+ .setPrivacySensitive(mPrivacySensitive == PRIVACY_SENSITIVE_ENABLED)
+ .build();
}
try {
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
index 5dd0b1c..aa38e51 100644
--- a/media/java/android/media/IMediaRoute2Provider.aidl
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -24,8 +24,7 @@
*/
oneway interface IMediaRoute2Provider {
void setClient(IMediaRoute2ProviderClient client);
- void requestCreateSession(String packageName, String routeId,
- String routeFeature, long requestId);
+ void requestCreateSession(String packageName, String routeId, long requestId);
void releaseSession(String sessionId);
void selectRoute(String sessionId, String routeId);
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 3cdaa07..2d3e185 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -47,12 +47,12 @@
List<MediaRoute2Info> getSystemRoutes();
void registerClient2(IMediaRouter2Client client, String packageName);
void unregisterClient2(IMediaRouter2Client client);
- void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route, in Intent request);
+ void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route,
+ in Intent request);
void requestSetVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int volume);
void requestUpdateVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int direction);
- void requestCreateSession(IMediaRouter2Client client, in MediaRoute2Info route,
- String routeFeature, int requestId);
+ void requestCreateSession(IMediaRouter2Client client, in MediaRoute2Info route, int requestId);
void setDiscoveryRequest2(IMediaRouter2Client client, in RouteDiscoveryPreference preference);
void selectRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route);
void deselectRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route);
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 021e23e..239dfed 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -134,59 +134,204 @@
*/
public static final int DEVICE_TYPE_BLUETOOTH = 3;
- @NonNull
final String mId;
- @Nullable
- final String mProviderId;
- @NonNull
final CharSequence mName;
- @Nullable
- final CharSequence mDescription;
- @Nullable
- final @ConnectionState int mConnectionState;
- @Nullable
- final Uri mIconUri;
- @Nullable
- final String mClientPackageName;
- @NonNull
final List<String> mFeatures;
+ @DeviceType
+ final int mDeviceType;
+ final Uri mIconUri;
+ final CharSequence mDescription;
+ @ConnectionState
+ final int mConnectionState;
+ final String mClientPackageName;
final int mVolume;
final int mVolumeMax;
final int mVolumeHandling;
- final @DeviceType int mDeviceType;
- @Nullable
final Bundle mExtras;
+ final String mProviderId;
MediaRoute2Info(@NonNull Builder builder) {
mId = builder.mId;
- mProviderId = builder.mProviderId;
mName = builder.mName;
+ mFeatures = builder.mFeatures;
+ mDeviceType = builder.mDeviceType;
+ mIconUri = builder.mIconUri;
mDescription = builder.mDescription;
mConnectionState = builder.mConnectionState;
- mIconUri = builder.mIconUri;
mClientPackageName = builder.mClientPackageName;
- mFeatures = builder.mFeatures;
- mVolume = builder.mVolume;
- mVolumeMax = builder.mVolumeMax;
mVolumeHandling = builder.mVolumeHandling;
- mDeviceType = builder.mDeviceType;
+ mVolumeMax = builder.mVolumeMax;
+ mVolume = builder.mVolume;
mExtras = builder.mExtras;
+ mProviderId = builder.mProviderId;
}
MediaRoute2Info(@NonNull Parcel in) {
mId = in.readString();
- mProviderId = in.readString();
mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mFeatures = in.createStringArrayList();
+ mDeviceType = in.readInt();
+ mIconUri = in.readParcelable(null);
mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mConnectionState = in.readInt();
- mIconUri = in.readParcelable(null);
mClientPackageName = in.readString();
- mFeatures = in.createStringArrayList();
- mVolume = in.readInt();
- mVolumeMax = in.readInt();
mVolumeHandling = in.readInt();
- mDeviceType = in.readInt();
+ mVolumeMax = in.readInt();
+ mVolume = in.readInt();
mExtras = in.readBundle();
+ mProviderId = in.readString();
+ }
+
+ /**
+ * Gets the id of the route. The routes which are given by {@link MediaRouter2} will have
+ * unique IDs.
+ * <p>
+ * In order to ensure uniqueness in {@link MediaRouter2} side, the value of this method
+ * can be different from what was set in {@link MediaRoute2ProviderService}.
+ *
+ * @see Builder#Builder(String, CharSequence)
+ */
+ @NonNull
+ public String getId() {
+ if (mProviderId != null) {
+ return toUniqueId(mProviderId, mId);
+ } else {
+ return mId;
+ }
+ }
+
+ /**
+ * Gets the user-visible name of the route.
+ */
+ @NonNull
+ public CharSequence getName() {
+ return mName;
+ }
+
+ /**
+ * Gets the supported features of the route.
+ */
+ @NonNull
+ public List<String> getFeatures() {
+ return mFeatures;
+ }
+
+ /**
+ * Gets the type of the receiver device associated with this route.
+ *
+ * @return The type of the receiver device associated with this route:
+ * {@link #DEVICE_TYPE_REMOTE_TV}, {@link #DEVICE_TYPE_REMOTE_SPEAKER},
+ * {@link #DEVICE_TYPE_BLUETOOTH}.
+ */
+ @DeviceType
+ public int getDeviceType() {
+ return mDeviceType;
+ }
+
+ /**
+ * Gets the URI of the icon representing this route.
+ * <p>
+ * This icon will be used in picker UIs if available.
+ *
+ * @return The URI of the icon representing this route, or null if none.
+ */
+ @Nullable
+ public Uri getIconUri() {
+ return mIconUri;
+ }
+
+ /**
+ * Gets the user-visible description of the route.
+ */
+ @Nullable
+ public CharSequence getDescription() {
+ return mDescription;
+ }
+
+ /**
+ * Gets the connection state of the route.
+ *
+ * @return The connection state of this route: {@link #CONNECTION_STATE_DISCONNECTED},
+ * {@link #CONNECTION_STATE_CONNECTING}, or {@link #CONNECTION_STATE_CONNECTED}.
+ */
+ @ConnectionState
+ public int getConnectionState() {
+ return mConnectionState;
+ }
+
+ /**
+ * Gets the package name of the client that uses the route.
+ * Returns null if no clients use this route.
+ * @hide
+ */
+ @Nullable
+ public String getClientPackageName() {
+ return mClientPackageName;
+ }
+
+ /**
+ * Gets information about how volume is handled on the route.
+ *
+ * @return {@link #PLAYBACK_VOLUME_FIXED} or {@link #PLAYBACK_VOLUME_VARIABLE}
+ */
+ public int getVolumeHandling() {
+ return mVolumeHandling;
+ }
+
+ /**
+ * Gets the maximum volume of the route.
+ */
+ public int getVolumeMax() {
+ return mVolumeMax;
+ }
+
+ /**
+ * Gets the current volume of the route. This may be invalid if the route is not selected.
+ */
+ public int getVolume() {
+ return mVolume;
+ }
+
+ @Nullable
+ public Bundle getExtras() {
+ return mExtras == null ? null : new Bundle(mExtras);
+ }
+
+ /**
+ * Gets the original id set by {@link Builder#Builder(String, CharSequence)}.
+ * @hide
+ */
+ @NonNull
+ public String getOriginalId() {
+ return mId;
+ }
+
+ /**
+ * Gets the provider id of the route. It is assigned automatically by
+ * {@link com.android.server.media.MediaRouterService}.
+ *
+ * @return provider id of the route or null if it's not set.
+ * @hide
+ */
+ @Nullable
+ public String getProviderId() {
+ return mProviderId;
+ }
+
+ /**
+ * Returns if the route has at least one of the specified route features.
+ *
+ * @param features the list of route features to consider
+ * @return true if the route has at least one feature in the list
+ */
+ public boolean hasAnyFeatures(@NonNull Collection<String> features) {
+ Objects.requireNonNull(features, "features must not be null");
+ for (String feature : features) {
+ if (getFeatures().contains(feature)) {
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -213,172 +358,49 @@
return false;
}
MediaRoute2Info other = (MediaRoute2Info) obj;
+
+ // Note: mExtras is not included.
return Objects.equals(mId, other.mId)
- && Objects.equals(mProviderId, other.mProviderId)
&& Objects.equals(mName, other.mName)
+ && Objects.equals(mFeatures, other.mFeatures)
+ && (mDeviceType == other.mDeviceType)
+ && Objects.equals(mIconUri, other.mIconUri)
&& Objects.equals(mDescription, other.mDescription)
&& (mConnectionState == other.mConnectionState)
- && Objects.equals(mIconUri, other.mIconUri)
&& Objects.equals(mClientPackageName, other.mClientPackageName)
- && Objects.equals(mFeatures, other.mFeatures)
- && (mVolume == other.mVolume)
- && (mVolumeMax == other.mVolumeMax)
&& (mVolumeHandling == other.mVolumeHandling)
- && (mDeviceType == other.mDeviceType)
- //TODO: This will be evaluated as false in most cases. Try not to.
- && Objects.equals(mExtras, other.mExtras);
+ && (mVolumeMax == other.mVolumeMax)
+ && (mVolume == other.mVolume)
+ && Objects.equals(mProviderId, other.mProviderId);
}
@Override
public int hashCode() {
- return Objects.hash(mId, mName, mDescription, mConnectionState, mIconUri,
- mFeatures, mVolume, mVolumeMax, mVolumeHandling, mDeviceType);
+ // Note: mExtras is not included.
+ return Objects.hash(mId, mName, mFeatures, mDeviceType, mIconUri, mDescription,
+ mConnectionState, mClientPackageName, mVolumeHandling, mVolumeMax, mVolume,
+ mProviderId);
}
- /**
- * Gets the id of the route. The routes which are given by {@link MediaRouter2} will have
- * unique IDs.
- * <p>
- * In order to ensure uniqueness in {@link MediaRouter2} side, the value of this method
- * can be different from what was set in {@link MediaRoute2ProviderService}.
- *
- * @see Builder#Builder(String, CharSequence)
- */
- @NonNull
- public String getId() {
- if (mProviderId != null) {
- return toUniqueId(mProviderId, mId);
- } else {
- return mId;
- }
- }
-
- /**
- * Gets the original id set by {@link Builder#Builder(String, CharSequence)}.
- * @hide
- */
- @NonNull
- public String getOriginalId() {
- return mId;
- }
-
- /**
- * Gets the provider id of the route. It is assigned automatically by
- * {@link com.android.server.media.MediaRouterService}.
- *
- * @return provider id of the route or null if it's not set.
- * @hide
- */
- @Nullable
- public String getProviderId() {
- return mProviderId;
- }
-
- @NonNull
- public CharSequence getName() {
- return mName;
- }
-
- @Nullable
- public CharSequence getDescription() {
- return mDescription;
- }
-
- /**
- * Gets the connection state of the route.
- *
- * @return The connection state of this route: {@link #CONNECTION_STATE_DISCONNECTED},
- * {@link #CONNECTION_STATE_CONNECTING}, or {@link #CONNECTION_STATE_CONNECTED}.
- */
- @ConnectionState
- public int getConnectionState() {
- return mConnectionState;
- }
-
- /**
- * Gets the URI of the icon representing this route.
- * <p>
- * This icon will be used in picker UIs if available.
- *
- * @return The URI of the icon representing this route, or null if none.
- */
- @Nullable
- public Uri getIconUri() {
- return mIconUri;
- }
-
- /**
- * Gets the package name of the client that uses the route.
- * Returns null if no clients use this.
- * @hide
- */
- @Nullable
- public String getClientPackageName() {
- return mClientPackageName;
- }
-
- /**
- * Gets the supported categories of the route.
- */
- @NonNull
- public List<String> getFeatures() {
- return mFeatures;
- }
-
- /**
- * Gets the type of the receiver device associated with this route.
- *
- * @return The type of the receiver device associated with this route:
- * {@link #DEVICE_TYPE_REMOTE_TV}, {@link #DEVICE_TYPE_REMOTE_SPEAKER},
- * {@link #DEVICE_TYPE_BLUETOOTH}.
- */
- @DeviceType
- public int getDeviceType() {
- return mDeviceType;
- }
-
- /**
- * Gets the current volume of the route. This may be invalid if the route is not selected.
- */
- public int getVolume() {
- return mVolume;
- }
-
- /**
- * Gets the maximum volume of the route.
- */
- public int getVolumeMax() {
- return mVolumeMax;
- }
-
- /**
- * Gets information about how volume is handled on the route.
- *
- * @return {@link #PLAYBACK_VOLUME_FIXED} or {@link #PLAYBACK_VOLUME_VARIABLE}
- */
- public int getVolumeHandling() {
- return mVolumeHandling;
- }
-
- @Nullable
- public Bundle getExtras() {
- return mExtras;
- }
-
- /**
- * Returns if the route has at least one of the specified route features.
- *
- * @param features the list of route features to consider
- * @return true if the route has at least one feature in the list
- */
- public boolean hasAnyFeatures(@NonNull Collection<String> features) {
- Objects.requireNonNull(features, "features must not be null");
- for (String feature : features) {
- if (getFeatures().contains(feature)) {
- return true;
- }
- }
- return false;
+ @Override
+ public String toString() {
+ // Note: mExtras is not printed here.
+ StringBuilder result = new StringBuilder()
+ .append("MediaRoute2Info{ ")
+ .append("id=").append(getId())
+ .append(", name=").append(getName())
+ .append(", features=").append(getFeatures())
+ .append(", deviceType=").append(getDeviceType())
+ .append(", iconUri=").append(getIconUri())
+ .append(", description=").append(getDescription())
+ .append(", connectionState=").append(getConnectionState())
+ .append(", clientPackageName=").append(getClientPackageName())
+ .append(", volumeHandling=").append(getVolumeHandling())
+ .append(", volumeMax=").append(getVolumeMax())
+ .append(", volume=").append(getVolume())
+ .append(", providerId=").append(getProviderId())
+ .append(" }");
+ return result.toString();
}
@Override
@@ -389,36 +411,18 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mId);
- dest.writeString(mProviderId);
TextUtils.writeToParcel(mName, dest, flags);
+ dest.writeStringList(mFeatures);
+ dest.writeInt(mDeviceType);
+ dest.writeParcelable(mIconUri, flags);
TextUtils.writeToParcel(mDescription, dest, flags);
dest.writeInt(mConnectionState);
- dest.writeParcelable(mIconUri, flags);
dest.writeString(mClientPackageName);
- dest.writeStringList(mFeatures);
- dest.writeInt(mVolume);
- dest.writeInt(mVolumeMax);
dest.writeInt(mVolumeHandling);
- dest.writeInt(mDeviceType);
+ dest.writeInt(mVolumeMax);
+ dest.writeInt(mVolume);
dest.writeBundle(mExtras);
- }
-
- @Override
- public String toString() {
- StringBuilder result = new StringBuilder()
- .append("MediaRouteInfo{ ")
- .append("id=").append(getId())
- .append(", name=").append(getName())
- .append(", description=").append(getDescription())
- .append(", connectionState=").append(getConnectionState())
- .append(", iconUri=").append(getIconUri())
- .append(", volume=").append(getVolume())
- .append(", volumeMax=").append(getVolumeMax())
- .append(", volumeHandling=").append(getVolumeHandling())
- .append(", deviceType=").append(getDeviceType())
- .append(", providerId=").append(getProviderId())
- .append(" }");
- return result.toString();
+ dest.writeString(mProviderId);
}
/**
@@ -426,20 +430,21 @@
*/
public static final class Builder {
final String mId;
- String mProviderId;
final CharSequence mName;
+ final List<String> mFeatures;
+
+ @DeviceType
+ int mDeviceType = DEVICE_TYPE_UNKNOWN;
+ Uri mIconUri;
CharSequence mDescription;
@ConnectionState
int mConnectionState;
- Uri mIconUri;
String mClientPackageName;
- List<String> mFeatures;
- int mVolume;
- int mVolumeMax;
int mVolumeHandling = PLAYBACK_VOLUME_FIXED;
- @DeviceType
- int mDeviceType = DEVICE_TYPE_UNKNOWN;
+ int mVolumeMax;
+ int mVolume;
Bundle mExtras;
+ String mProviderId;
/**
* Constructor for builder to create {@link MediaRoute2Info}.
@@ -448,8 +453,8 @@
* obtained from {@link MediaRouter2} can be different from what was set in
* {@link MediaRoute2ProviderService}.
* </p>
- * @param id
- * @param name
+ * @param id The ID of the route. Must not be empty.
+ * @param name The user-visible name of the route.
*/
public Builder(@NonNull String id, @NonNull CharSequence name) {
if (TextUtils.isEmpty(id)) {
@@ -463,40 +468,91 @@
mFeatures = new ArrayList<>();
}
+ /**
+ * Constructor for builder to create {@link MediaRoute2Info} with
+ * existing {@link MediaRoute2Info} instance.
+ *
+ * @param routeInfo the existing instance to copy data from.
+ */
public Builder(@NonNull MediaRoute2Info routeInfo) {
- if (routeInfo == null) {
- throw new IllegalArgumentException("route info must not be null");
- }
+ Objects.requireNonNull(routeInfo, "routeInfo must not be null");
+
mId = routeInfo.mId;
mName = routeInfo.mName;
-
- if (!TextUtils.isEmpty(routeInfo.mProviderId)) {
- setProviderId(routeInfo.mProviderId);
- }
+ mFeatures = new ArrayList<>(routeInfo.mFeatures);
+ mDeviceType = routeInfo.mDeviceType;
+ mIconUri = routeInfo.mIconUri;
mDescription = routeInfo.mDescription;
mConnectionState = routeInfo.mConnectionState;
- mIconUri = routeInfo.mIconUri;
- setClientPackageName(routeInfo.mClientPackageName);
- mFeatures = new ArrayList<>(routeInfo.mFeatures);
- setVolume(routeInfo.mVolume);
- setVolumeMax(routeInfo.mVolumeMax);
- setVolumeHandling(routeInfo.mVolumeHandling);
- setDeviceType(routeInfo.mDeviceType);
+ mClientPackageName = routeInfo.mClientPackageName;
+ mVolumeHandling = routeInfo.mVolumeHandling;
+ mVolumeMax = routeInfo.mVolumeMax;
+ mVolume = routeInfo.mVolume;
if (routeInfo.mExtras != null) {
mExtras = new Bundle(routeInfo.mExtras);
}
+ mProviderId = routeInfo.mProviderId;
}
/**
- * Sets the provider id of the route.
- * @hide
+ * Adds a feature for the route.
*/
@NonNull
- public Builder setProviderId(@NonNull String providerId) {
- if (TextUtils.isEmpty(providerId)) {
- throw new IllegalArgumentException("providerId must not be null or empty");
+ public Builder addFeature(@NonNull String feature) {
+ if (TextUtils.isEmpty(feature)) {
+ throw new IllegalArgumentException("feature must not be null or empty");
}
- mProviderId = providerId;
+ mFeatures.add(feature);
+ return this;
+ }
+
+ /**
+ * Adds features for the route. A route must support at least one route type.
+ */
+ @NonNull
+ public Builder addFeatures(@NonNull Collection<String> features) {
+ Objects.requireNonNull(features, "features must not be null");
+ for (String feature : features) {
+ addFeature(feature);
+ }
+ return this;
+ }
+
+ /**
+ * Clears the features of the route. A route must support at least one route type.
+ */
+ @NonNull
+ public Builder clearFeatures() {
+ mFeatures.clear();
+ return this;
+ }
+
+ /**
+ * Sets the route's device type.
+ */
+ @NonNull
+ public Builder setDeviceType(@DeviceType int deviceType) {
+ mDeviceType = deviceType;
+ return this;
+ }
+
+ /**
+ * Sets the URI of the icon representing this route.
+ * <p>
+ * This icon will be used in picker UIs if available.
+ * </p><p>
+ * The URI must be one of the following formats:
+ * <ul>
+ * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
+ * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
+ * </li>
+ * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
+ * </ul>
+ * </p>
+ */
+ @NonNull
+ public Builder setIconUri(@Nullable Uri iconUri) {
+ mIconUri = iconUri;
return this;
}
@@ -523,26 +579,6 @@
}
/**
- * Sets the URI of the icon representing this route.
- * <p>
- * This icon will be used in picker UIs if available.
- * </p><p>
- * The URI must be one of the following formats:
- * <ul>
- * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
- * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})
- * </li>
- * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
- * </ul>
- * </p>
- */
- @NonNull
- public Builder setIconUri(@Nullable Uri iconUri) {
- mIconUri = iconUri;
- return this;
- }
-
- /**
* Sets the package name of the app using the route.
*/
@NonNull
@@ -552,57 +588,6 @@
}
/**
- * Clears the features of the route.
- */
- @NonNull
- public Builder clearFeatures() {
- mFeatures = new ArrayList<>();
- return this;
- }
-
- /**
- * Adds features for the route.
- */
- @NonNull
- public Builder addFeatures(@NonNull Collection<String> features) {
- Objects.requireNonNull(features, "features must not be null");
- for (String feature : features) {
- addFeature(feature);
- }
- return this;
- }
-
- /**
- * Adds a feature for the route.
- */
- @NonNull
- public Builder addFeature(@NonNull String feature) {
- if (TextUtils.isEmpty(feature)) {
- throw new IllegalArgumentException("feature must not be null or empty");
- }
- mFeatures.add(feature);
- return this;
- }
-
- /**
- * Sets the route's current volume, or 0 if unknown.
- */
- @NonNull
- public Builder setVolume(int volume) {
- mVolume = volume;
- return this;
- }
-
- /**
- * Sets the route's maximum volume, or 0 if unknown.
- */
- @NonNull
- public Builder setVolumeMax(int volumeMax) {
- mVolumeMax = volumeMax;
- return this;
- }
-
- /**
* Sets the route's volume handling.
*/
@NonNull
@@ -612,28 +597,61 @@
}
/**
- * Sets the route's device type.
+ * Sets the route's maximum volume, or 0 if unknown.
*/
@NonNull
- public Builder setDeviceType(@DeviceType int deviceType) {
- mDeviceType = deviceType;
+ public Builder setVolumeMax(int volumeMax) {
+ mVolumeMax = volumeMax;
+ return this;
+ }
+
+ /**
+ * Sets the route's current volume, or 0 if unknown.
+ */
+ @NonNull
+ public Builder setVolume(int volume) {
+ mVolume = volume;
return this;
}
/**
* Sets a bundle of extras for the route.
+ * <p>
+ * Note: The extras will not affect the result of {@link MediaRoute2Info#equals(Object)}.
*/
@NonNull
public Builder setExtras(@Nullable Bundle extras) {
+ if (extras == null) {
+ mExtras = null;
+ return this;
+ }
mExtras = new Bundle(extras);
return this;
}
/**
+ * Sets the provider id of the route.
+ * @hide
+ */
+ @NonNull
+ public Builder setProviderId(@NonNull String providerId) {
+ if (TextUtils.isEmpty(providerId)) {
+ throw new IllegalArgumentException("providerId must not be null or empty");
+ }
+ mProviderId = providerId;
+ return this;
+ }
+
+ /**
* Builds the {@link MediaRoute2Info media route info}.
+ *
+ * @throws IllegalArgumentException if no features are added.
*/
@NonNull
public MediaRoute2Info build() {
+ if (mFeatures.isEmpty()) {
+ throw new IllegalArgumentException("features must not be empty!");
+ }
return new MediaRoute2Info(this);
}
}
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 6d9aea5..5a3de6d 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -161,8 +161,8 @@
* @param sessionInfo information of the new session.
* The {@link RoutingSessionInfo#getId() id} of the session must be unique.
* @param requestId id of the previous request to create this session provided in
- * {@link #onCreateSession(String, String, String, long)}
- * @see #onCreateSession(String, String, String, long)
+ * {@link #onCreateSession(String, String, long)}
+ * @see #onCreateSession(String, String, long)
* @hide
*/
public final void notifySessionCreated(@NonNull RoutingSessionInfo sessionInfo,
@@ -196,8 +196,8 @@
* Notifies clients of that the session could not be created.
*
* @param requestId id of the previous request to create the session provided in
- * {@link #onCreateSession(String, String, String, long)}.
- * @see #onCreateSession(String, String, String, long)
+ * {@link #onCreateSession(String, String, long)}.
+ * @see #onCreateSession(String, String, long)
* @hide
*/
public final void notifySessionCreationFailed(long requestId) {
@@ -289,16 +289,15 @@
*
* @param packageName the package name of the application that selected the route
* @param routeId the id of the route initially being connected
- * @param routeFeature the route feature of the new session
* @param requestId the id of this session creation request
*
- * @see RoutingSessionInfo.Builder#Builder(String, String, String)
+ * @see RoutingSessionInfo.Builder#Builder(String, String)
* @see RoutingSessionInfo.Builder#addSelectedRoute(String)
* @see RoutingSessionInfo.Builder#setControlHints(Bundle)
* @hide
*/
public abstract void onCreateSession(@NonNull String packageName, @NonNull String routeId,
- @NonNull String routeFeature, long requestId);
+ long requestId);
/**
* Called when the session should be released. A client of the session or system can request
@@ -433,14 +432,12 @@
}
@Override
- public void requestCreateSession(String packageName, String routeId,
- String routeFeature, long requestId) {
+ public void requestCreateSession(String packageName, String routeId, long requestId) {
if (!checkCallerisSystem()) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onCreateSession,
- MediaRoute2ProviderService.this, packageName, routeId, routeFeature,
- requestId));
+ MediaRoute2ProviderService.this, packageName, routeId, requestId));
}
@Override
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index bc4da10..971b08d 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -284,26 +284,20 @@
* Requests the media route provider service to create a session with the given route.
*
* @param route the route you want to create a session with.
- * @param routeFeature the route feature of the session. Should not be empty.
*
* @see SessionCallback#onSessionCreated
* @see SessionCallback#onSessionCreationFailed
* @hide
*/
@NonNull
- public void requestCreateSession(@NonNull MediaRoute2Info route,
- @NonNull String routeFeature) {
+ public void requestCreateSession(@NonNull MediaRoute2Info route) {
Objects.requireNonNull(route, "route must not be null");
- if (TextUtils.isEmpty(routeFeature)) {
- throw new IllegalArgumentException("routeFeature must not be empty");
- }
// TODO: Check the given route exists
- // TODO: Check the route supports the given routeFeature
final int requestId;
requestId = mSessionCreationRequestCnt.getAndIncrement();
- SessionCreationRequest request = new SessionCreationRequest(requestId, route, routeFeature);
+ SessionCreationRequest request = new SessionCreationRequest(requestId, route);
mSessionCreationRequests.add(request);
Client2 client;
@@ -312,7 +306,7 @@
}
if (client != null) {
try {
- mMediaRouterService.requestCreateSession(client, route, routeFeature, requestId);
+ mMediaRouterService.requestCreateSession(client, route, requestId);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to request to create session.", ex);
mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler,
@@ -468,27 +462,18 @@
mSessionCreationRequests.remove(matchingRequest);
MediaRoute2Info requestedRoute = matchingRequest.mRoute;
- String requestedRouteFeature = matchingRequest.mRouteFeature;
if (sessionInfo == null) {
// TODO: We may need to distinguish between failure and rejection.
// One way can be introducing 'reason'.
- notifySessionCreationFailed(requestedRoute, requestedRouteFeature);
- return;
- } else if (!TextUtils.equals(requestedRouteFeature,
- sessionInfo.getRouteFeature())) {
- Log.w(TAG, "The session has different route feature from what we requested. "
- + "(requested=" + requestedRouteFeature
- + ", actual=" + sessionInfo.getRouteFeature()
- + ")");
- notifySessionCreationFailed(requestedRoute, requestedRouteFeature);
+ notifySessionCreationFailed(requestedRoute);
return;
} else if (!sessionInfo.getSelectedRoutes().contains(requestedRoute.getId())) {
Log.w(TAG, "The session does not contain the requested route. "
+ "(requestedRouteId=" + requestedRoute.getId()
+ ", actualRoutes=" + sessionInfo.getSelectedRoutes()
+ ")");
- notifySessionCreationFailed(requestedRoute, requestedRouteFeature);
+ notifySessionCreationFailed(requestedRoute);
return;
} else if (!TextUtils.equals(requestedRoute.getProviderId(),
sessionInfo.getProviderId())) {
@@ -496,7 +481,7 @@
+ "(requested route's providerId=" + requestedRoute.getProviderId()
+ ", actual providerId=" + sessionInfo.getProviderId()
+ ")");
- notifySessionCreationFailed(requestedRoute, requestedRouteFeature);
+ notifySessionCreationFailed(requestedRoute);
return;
}
}
@@ -617,10 +602,10 @@
}
}
- private void notifySessionCreationFailed(MediaRoute2Info route, String routeFeature) {
+ private void notifySessionCreationFailed(MediaRoute2Info route) {
for (SessionCallbackRecord record: mSessionCallbackRecords) {
record.mExecutor.execute(
- () -> record.mSessionCallback.onSessionCreationFailed(route, routeFeature));
+ () -> record.mSessionCallback.onSessionCreationFailed(route));
}
}
@@ -688,10 +673,8 @@
* Called when the session creation request failed.
*
* @param requestedRoute the route info which was used for the request
- * @param requestedRouteFeature the route feature which was used for the request
*/
- public void onSessionCreationFailed(@NonNull MediaRoute2Info requestedRoute,
- @NonNull String requestedRouteFeature) {}
+ public void onSessionCreationFailed(@NonNull MediaRoute2Info requestedRoute) {}
/**
* Called when the session info has changed.
@@ -753,16 +736,6 @@
}
/**
- * @return the feature which is used by the session mainly.
- */
- @NonNull
- public String getRouteFeature() {
- synchronized (mControllerLock) {
- return mSessionInfo.getRouteFeature();
- }
- }
-
- /**
* @return the control hints used to control routing session if available.
*/
@Nullable
@@ -1012,7 +985,6 @@
StringBuilder result = new StringBuilder()
.append("RoutingController{ ")
.append("sessionId=").append(getSessionId())
- .append(", routeFeature=").append(getRouteFeature())
.append(", selectedRoutes={")
.append(selectedRoutes)
.append("}")
@@ -1122,13 +1094,10 @@
final class SessionCreationRequest {
public final MediaRoute2Info mRoute;
- public final String mRouteFeature;
public final int mRequestId;
- SessionCreationRequest(int requestId, @NonNull MediaRoute2Info route,
- @NonNull String routeFeature) {
+ SessionCreationRequest(int requestId, @NonNull MediaRoute2Info route) {
mRoute = route;
- mRouteFeature = routeFeature;
mRequestId = requestId;
}
}
diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java
index 96acf6c..228adde 100644
--- a/media/java/android/media/RoutingSessionInfo.java
+++ b/media/java/android/media/RoutingSessionInfo.java
@@ -51,7 +51,6 @@
final String mId;
final String mClientPackageName;
- final String mRouteFeature;
@Nullable
final String mProviderId;
final List<String> mSelectedRoutes;
@@ -66,7 +65,6 @@
mId = builder.mId;
mClientPackageName = builder.mClientPackageName;
- mRouteFeature = builder.mRouteFeature;
mProviderId = builder.mProviderId;
// TODO: Needs to check that the routes already have unique IDs.
@@ -87,7 +85,6 @@
mId = ensureString(src.readString());
mClientPackageName = ensureString(src.readString());
- mRouteFeature = ensureString(src.readString());
mProviderId = src.readString();
mSelectedRoutes = ensureList(src.createStringArrayList());
@@ -119,7 +116,7 @@
* In order to ensure uniqueness in {@link MediaRouter2} side, the value of this method
* can be different from what was set in {@link MediaRoute2ProviderService}.
*
- * @see Builder#Builder(String, String, String)
+ * @see Builder#Builder(String, String)
*/
@NonNull
public String getId() {
@@ -131,7 +128,7 @@
}
/**
- * Gets the original id set by {@link Builder#Builder(String, String, String)}.
+ * Gets the original id set by {@link Builder#Builder(String, String)}.
* @hide
*/
@NonNull
@@ -148,15 +145,6 @@
}
/**
- * Gets the route feature of the session.
- * Routes that don't have the feature can't be selected into the session.
- */
- @NonNull
- public String getRouteFeature() {
- return mRouteFeature;
- }
-
- /**
* Gets the provider id of the session.
* @hide
*/
@@ -214,7 +202,6 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mId);
dest.writeString(mClientPackageName);
- dest.writeString(mRouteFeature);
dest.writeString(mProviderId);
dest.writeStringList(mSelectedRoutes);
dest.writeStringList(mSelectableRoutes);
@@ -235,7 +222,6 @@
RoutingSessionInfo other = (RoutingSessionInfo) obj;
return Objects.equals(mId, other.mId)
&& Objects.equals(mClientPackageName, other.mClientPackageName)
- && Objects.equals(mRouteFeature, other.mRouteFeature)
&& Objects.equals(mProviderId, other.mProviderId)
&& Objects.equals(mSelectedRoutes, other.mSelectedRoutes)
&& Objects.equals(mSelectableRoutes, other.mSelectableRoutes)
@@ -245,7 +231,7 @@
@Override
public int hashCode() {
- return Objects.hash(mId, mClientPackageName, mRouteFeature, mProviderId,
+ return Objects.hash(mId, mClientPackageName, mProviderId,
mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferrableRoutes);
}
@@ -254,7 +240,6 @@
StringBuilder result = new StringBuilder()
.append("RoutingSessionInfo{ ")
.append("sessionId=").append(mId)
- .append(", routeFeature=").append(mRouteFeature)
.append(", selectedRoutes={")
.append(String.join(",", mSelectedRoutes))
.append("}")
@@ -295,7 +280,6 @@
public static final class Builder {
final String mId;
final String mClientPackageName;
- final String mRouteFeature;
String mProviderId;
final List<String> mSelectedRoutes;
final List<String> mSelectableRoutes;
@@ -314,22 +298,16 @@
* @param id ID of the session. Must not be empty.
* @param clientPackageName package name of the client app which uses this session.
* If is is unknown, then just use an empty string.
- * @param routeFeature the route feature of session. Must not be empty.
* @see MediaRoute2Info#getId()
*/
- public Builder(@NonNull String id, @NonNull String clientPackageName,
- @NonNull String routeFeature) {
+ public Builder(@NonNull String id, @NonNull String clientPackageName) {
if (TextUtils.isEmpty(id)) {
throw new IllegalArgumentException("id must not be empty");
}
Objects.requireNonNull(clientPackageName, "clientPackageName must not be null");
- if (TextUtils.isEmpty(routeFeature)) {
- throw new IllegalArgumentException("routeFeature must not be empty");
- }
mId = id;
mClientPackageName = clientPackageName;
- mRouteFeature = routeFeature;
mSelectedRoutes = new ArrayList<>();
mSelectableRoutes = new ArrayList<>();
mDeselectableRoutes = new ArrayList<>();
@@ -347,7 +325,6 @@
mId = sessionInfo.mId;
mClientPackageName = sessionInfo.mClientPackageName;
- mRouteFeature = sessionInfo.mRouteFeature;
mProviderId = sessionInfo.mProviderId;
mSelectedRoutes = new ArrayList<>(sessionInfo.mSelectedRoutes);
diff --git a/media/java/android/media/tv/tuner/Descrambler.java b/media/java/android/media/tv/tuner/Descrambler.java
index 0143582..23016e9 100644
--- a/media/java/android/media/tv/tuner/Descrambler.java
+++ b/media/java/android/media/tv/tuner/Descrambler.java
@@ -34,7 +34,7 @@
*/
public class Descrambler implements AutoCloseable {
/** @hide */
- @IntDef(prefix = "PID_TYPE_", value = {PID_TYPE_T, PID_TYPE_MMPT})
+ @IntDef(prefix = "PID_TYPE_", value = {PID_TYPE_T, PID_TYPE_MMTP})
@Retention(RetentionPolicy.SOURCE)
public @interface PidType {}
@@ -45,7 +45,7 @@
/**
* Packet ID is used to specify packets in MMTP.
*/
- public static final int PID_TYPE_MMPT = 2;
+ public static final int PID_TYPE_MMTP = 2;
private long mNativeContext;
diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java
index 8e579bf..a9a15d9 100644
--- a/media/java/android/media/tv/tuner/Lnb.java
+++ b/media/java/android/media/tv/tuner/Lnb.java
@@ -40,7 +40,8 @@
@SystemApi
public class Lnb implements AutoCloseable {
/** @hide */
- @IntDef({VOLTAGE_NONE, VOLTAGE_5V, VOLTAGE_11V, VOLTAGE_12V, VOLTAGE_13V, VOLTAGE_14V,
+ @IntDef(prefix = "VOLTAGE_",
+ value = {VOLTAGE_NONE, VOLTAGE_5V, VOLTAGE_11V, VOLTAGE_12V, VOLTAGE_13V, VOLTAGE_14V,
VOLTAGE_15V, VOLTAGE_18V, VOLTAGE_19V})
@Retention(RetentionPolicy.SOURCE)
public @interface Voltage {}
@@ -83,7 +84,8 @@
public static final int VOLTAGE_19V = Constants.LnbVoltage.VOLTAGE_19V;
/** @hide */
- @IntDef({TONE_NONE, TONE_CONTINUOUS})
+ @IntDef(prefix = "TONE_",
+ value = {TONE_NONE, TONE_CONTINUOUS})
@Retention(RetentionPolicy.SOURCE)
public @interface Tone {}
@@ -97,7 +99,8 @@
public static final int TONE_CONTINUOUS = Constants.LnbTone.CONTINUOUS;
/** @hide */
- @IntDef({POSITION_UNDEFINED, POSITION_A, POSITION_B})
+ @IntDef(prefix = "POSITION_",
+ value = {POSITION_UNDEFINED, POSITION_A, POSITION_B})
@Retention(RetentionPolicy.SOURCE)
public @interface Position {}
@@ -114,6 +117,37 @@
*/
public static final int POSITION_B = Constants.LnbPosition.POSITION_B;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "EVENT_TYPE_",
+ value = {EVENT_TYPE_DISEQC_RX_OVERFLOW, EVENT_TYPE_DISEQC_RX_TIMEOUT,
+ EVENT_TYPE_DISEQC_RX_PARITY_ERROR, EVENT_TYPE_LNB_OVERLOAD})
+ public @interface EventType {}
+
+ /**
+ * Outgoing Diseqc message overflow.
+ * @hide
+ */
+ public static final int EVENT_TYPE_DISEQC_RX_OVERFLOW =
+ Constants.LnbEventType.DISEQC_RX_OVERFLOW;
+ /**
+ * Outgoing Diseqc message isn't delivered on time.
+ * @hide
+ */
+ public static final int EVENT_TYPE_DISEQC_RX_TIMEOUT =
+ Constants.LnbEventType.DISEQC_RX_TIMEOUT;
+ /**
+ * Incoming Diseqc message has parity error.
+ * @hide
+ */
+ public static final int EVENT_TYPE_DISEQC_RX_PARITY_ERROR =
+ Constants.LnbEventType.DISEQC_RX_PARITY_ERROR;
+ /**
+ * LNB is overload.
+ * @hide
+ */
+ public static final int EVENT_TYPE_LNB_OVERLOAD = Constants.LnbEventType.LNB_OVERLOAD;
+
int mId;
LnbCallback mCallback;
Context mContext;
diff --git a/media/java/android/media/tv/tuner/LnbCallback.java b/media/java/android/media/tv/tuner/LnbCallback.java
index 99bbf86..5155f60 100644
--- a/media/java/android/media/tv/tuner/LnbCallback.java
+++ b/media/java/android/media/tv/tuner/LnbCallback.java
@@ -17,6 +17,8 @@
package android.media.tv.tuner;
+import android.media.tv.tuner.Lnb.EventType;
+
/**
* Callback interface for receiving information from LNBs.
*
@@ -26,7 +28,7 @@
/**
* Invoked when there is a LNB event.
*/
- void onEvent(int lnbEventType);
+ void onEvent(@EventType int lnbEventType);
/**
* Invoked when there is a new DiSEqC message.
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 6f5ba84..3a8ae92 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -16,6 +16,7 @@
package android.media.tv.tuner;
+import android.annotation.BytesLong;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -23,11 +24,13 @@
import android.annotation.SystemApi;
import android.content.Context;
import android.media.tv.tuner.TunerConstants.FilterStatus;
-import android.media.tv.tuner.TunerConstants.FilterSubtype;
import android.media.tv.tuner.TunerConstants.FrontendScanType;
import android.media.tv.tuner.TunerConstants.Result;
import android.media.tv.tuner.dvr.Dvr;
-import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
+import android.media.tv.tuner.dvr.DvrCallback;
+import android.media.tv.tuner.dvr.DvrSettings;
+import android.media.tv.tuner.filter.Filter.Subtype;
+import android.media.tv.tuner.filter.Filter.Type;
import android.media.tv.tuner.filter.FilterEvent;
import android.media.tv.tuner.filter.TimeFilter;
import android.media.tv.tuner.frontend.FrontendCallback;
@@ -88,6 +91,33 @@
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,
+ @Nullable OnResourceLostListener listener) {
+ mContext = context;
+ }
+
+ /**
+ * Shares the frontend resource with another Tuner instance
+ *
+ * @param tuner the Tuner instance to share frontend resource with.
+ *
+ * @hide
+ */
+ public void shareFrontendFromTuner(@NonNull Tuner tuner) {
+ }
+
+
private long mNativeContext; // used by native jMediaTuner
/** @hide */
@@ -119,13 +149,13 @@
private native int nativeStopScan();
private native int nativeSetLnb(int lnbId);
private native int nativeSetLna(boolean enable);
- private native FrontendStatus[] nativeGetFrontendStatus(int[] statusTypes);
+ private native FrontendStatus nativeGetFrontendStatus(int[] statusTypes);
private native int nativeGetAvSyncHwId(Filter filter);
private native long nativeGetAvSyncTime(int avSyncId);
private native int nativeConnectCiCam(int ciCamId);
private native int nativeDisconnectCiCam();
private native FrontendInfo nativeGetFrontendInfo(int id);
- private native Filter nativeOpenFilter(int type, int subType, int bufferSize);
+ private native Filter nativeOpenFilter(int type, int subType, long bufferSize);
private native TimeFilter nativeOpenTimeFilter();
private native List<Integer> nativeGetLnbIds();
@@ -133,7 +163,7 @@
private native Descrambler nativeOpenDescrambler();
- private native Dvr nativeOpenDvr(int type, int bufferSize);
+ private native Dvr nativeOpenDvr(int type, long bufferSize);
private static native DemuxCapabilities nativeGetDemuxCapabilities();
@@ -159,6 +189,21 @@
void onFilterStatusChanged(@NonNull Filter filter, @FilterStatus int status);
}
+
+ /**
+ * Listener for resource lost.
+ *
+ * @hide
+ */
+ public interface OnResourceLostListener {
+ /**
+ * Invoked when resource lost.
+ *
+ * @param tuner the tuner instance whose resource is being reclaimed.
+ */
+ void onResourceLost(@NonNull Tuner tuner);
+ }
+
@Nullable
private EventHandler createEventHandler() {
Looper looper;
@@ -221,23 +266,32 @@
/**
* Stops a previous tuning.
*
- * If the method completes successfully the frontend is no longer tuned and no data
+ * <p>If the method completes successfully, the frontend is no longer tuned and no data
* will be sent to attached filters.
*
* @return result status of the operation.
+ *
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @Result
public int stopTune() {
+ TunerUtils.checkTunerPermission(mContext);
return nativeStopTune();
}
/**
* Scan channels.
+ *
+ * @param settings A {@link FrontendSettings} to configure the frontend.
+ * @param scanType The scan type.
+ *
* @hide
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public int scan(@NonNull FrontendSettings settings, @FrontendScanType int scanType,
@NonNull @CallbackExecutor Executor executor, @NonNull ScanCallback scanCallback) {
+ TunerUtils.checkTunerPermission(mContext);
mScanCallback = scanCallback;
mScanCallbackExecutor = executor;
return nativeScan(settings.getType(), settings, scanType);
@@ -255,6 +309,7 @@
* @hide
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @Result
public int stopScan() {
TunerUtils.checkTunerPermission(mContext);
int retVal = nativeStopScan();
@@ -266,42 +321,49 @@
/**
* Sets Low-Noise Block downconverter (LNB) for satellite frontend.
*
- * This assigns a hardware LNB resource to the satellite tuner. It can be
+ * <p>This assigns a hardware LNB resource to the satellite tuner. It can be
* called multiple times to update LNB assignment.
*
* @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);
return nativeSetLnb(lnb.mId);
}
/**
* Enable or Disable Low Noise Amplifier (LNA).
*
- * @param enable true to activate LNA module; false to deactivate LNA
+ * @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
public int setLna(boolean enable) {
+ TunerUtils.checkTunerPermission(mContext);
return nativeSetLna(enable);
}
/**
* Gets the statuses of the frontend.
*
- * This retrieve the statuses of the frontend for given status types.
+ * <p>This retrieve the statuses of the frontend for given status types.
*
- * @param statusTypes an array of status type which the caller request.
- *
- * @return statuses an array of statuses which response the caller's
- * request.
+ * @param statusTypes an array of status types which the caller requests.
+ * @return statuses which response the caller's requests.
* @hide
*/
- public FrontendStatus[] getFrontendStatus(int[] statusTypes) {
+ @Nullable
+ public FrontendStatus getFrontendStatus(int[] statusTypes) {
return nativeGetFrontendStatus(statusTypes);
}
@@ -310,59 +372,77 @@
*
* @param filter the filter instance for the hardware sync ID.
* @return the id of hardware A/V sync.
+ *
* @hide
*/
- public int getAvSyncHwId(Filter filter) {
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ public int getAvSyncHwId(@NonNull Filter filter) {
+ TunerUtils.checkTunerPermission(mContext);
return nativeGetAvSyncHwId(filter);
}
+
/**
- * Gets the current timestamp for A/V sync
+ * Gets the current timestamp for Audio/Video sync
*
- * The timestamp is maintained by hardware. The timestamp based on 90KHz, and it's format is the
- * same as PTS (Presentation Time Stamp).
+ * <p>The timestamp is maintained by hardware. The timestamp based on 90KHz, and it's format is
+ * the same as PTS (Presentation Time Stamp).
*
* @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) {
+ TunerUtils.checkTunerPermission(mContext);
return nativeGetAvSyncTime(avSyncHwId);
}
-
/**
* Connects Conditional Access Modules (CAM) through Common Interface (CI)
*
- * The demux uses the output from the frontend as the input by default, and must change to use
- * the output from CI-CAM as the input after this call.
+ * <p>The demux uses the output from the frontend as the input by default, and must change to
+ * use the output from CI-CAM as the input after this call.
*
* @param ciCamId specify CI-CAM Id to connect.
* @return result status of the operation.
+ *
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int connectCiCam(int ciCamId) {
+ TunerUtils.checkTunerPermission(mContext);
return nativeConnectCiCam(ciCamId);
}
/**
* Disconnects Conditional Access Modules (CAM)
*
- * The demux will use the output from the frontend as the input after this call.
+ * <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
public int disconnectCiCam() {
+ TunerUtils.checkTunerPermission(mContext);
return nativeDisconnectCiCam();
}
/**
- * Retrieve the frontend information.
+ * Gets the frontend information.
+ *
+ * @return The frontend information. {@code null} if the operation failed.
+ *
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @Nullable
public FrontendInfo getFrontendInfo() {
+ TunerUtils.checkTunerPermission(mContext);
if (mFrontend == null) {
throw new IllegalStateException("frontend is not initialized");
}
@@ -370,10 +450,13 @@
}
/**
- * Gets frontend ID.
+ * 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");
}
@@ -382,6 +465,7 @@
/**
* Gets Demux capabilities.
+ *
* @hide
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@@ -424,8 +508,26 @@
private Filter() {}
}
- private Filter openFilter(@FilterType int mainType, @FilterSubtype int subType, int bufferSize,
- FilterCallback cb) {
+ /**
+ * Opens a filter object based on the given types and buffer size.
+ *
+ * @param mainType the main type of the filter.
+ * @param subType the subtype of the filter.
+ * @param bufferSize the buffer size of the filter to be opened in bytes. The buffer holds the
+ * data output from the filter.
+ * @param executor the executor on which callback will be invoked. The default event handler
+ * executor is used if it's {@code null}.
+ * @param cb the callback to receive notifications from filter.
+ * @return the opened filter. {@code null} if the operation failed.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @Nullable
+ public Filter openFilter(@Type int mainType, @Subtype int subType,
+ @BytesLong long bufferSize, @CallbackExecutor @Nullable Executor executor,
+ @Nullable FilterCallback cb) {
+ TunerUtils.checkTunerPermission(mContext);
Filter filter = nativeOpenFilter(
mainType, TunerUtils.getFilterSubtype(mainType, subType), bufferSize);
if (filter != null) {
@@ -437,6 +539,42 @@
return filter;
}
+ /**
+ * Opens an LNB (low-noise block downconverter) object.
+ *
+ * @param executor the executor on which callback will be invoked. The default event handler
+ * executor is used if it's {@code null}.
+ * @param cb the callback to receive notifications from LNB.
+ * @return the opened LNB object. {@code null} if the operation failed.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @Nullable
+ public Lnb openLnb(@CallbackExecutor @Nullable Executor executor, LnbCallback cb) {
+ return openLnbByName(null, executor, cb);
+ }
+
+ /**
+ * Opens an LNB (low-noise block downconverter) object.
+ *
+ * @param name the LNB name.
+ * @param executor the executor on which callback will be invoked. The default event handler
+ * executor is used if it's {@code null}.
+ * @param cb the callback to receive notifications from LNB.
+ * @return the opened LNB object. {@code null} if the operation failed.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @Nullable
+ public Lnb openLnbByName(@Nullable String name, @CallbackExecutor @Nullable Executor executor,
+ LnbCallback cb) {
+ TunerUtils.checkTunerPermission(mContext);
+ // TODO: use resource manager to get LNB ID.
+ return new Lnb(0);
+ }
+
private List<Integer> getLnbIds() {
mLnbIds = nativeGetLnbIds();
return mLnbIds;
@@ -484,7 +622,24 @@
return nativeOpenDescrambler();
}
- private Dvr openDvr(int type, int bufferSize) {
+ /**
+ * Open a DVR (Digital Video Record) instance.
+ *
+ * @param type the DVR type to be opened.
+ * @param bufferSize the buffer size of the output in bytes. It's used to hold output data of
+ * the attached filters.
+ * @param executor the executor on which callback will be invoked. The default event handler
+ * executor is used if it's {@code null}.
+ * @param cb the callback to receive notifications from DVR.
+ * @return the opened DVR object. {@code null} if the operation failed.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @Nullable
+ public Dvr openDvr(@DvrSettings.Type int type, @BytesLong long bufferSize,
+ @CallbackExecutor @Nullable Executor executor, DvrCallback cb) {
+ TunerUtils.checkTunerPermission(mContext);
Dvr dvr = nativeOpenDvr(type, bufferSize);
return dvr;
}
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
index 202b979..fe4c954 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -36,69 +36,42 @@
*/
@SystemApi
public final class TunerConstants {
- /** @hide */
+ /**
+ * Invalid TS packet ID.
+ * @hide
+ */
public static final int INVALID_TS_PID = Constants.Constant.INVALID_TS_PID;
- /** @hide */
+ /**
+ * Invalid stream ID.
+ * @hide
+ */
public static final int INVALID_STREAM_ID = Constants.Constant.INVALID_STREAM_ID;
/** @hide */
- @IntDef({FRONTEND_EVENT_TYPE_LOCKED, FRONTEND_EVENT_TYPE_NO_SIGNAL,
- FRONTEND_EVENT_TYPE_LOST_LOCK})
+ @IntDef(prefix = "FRONTEND_EVENT_TYPE_",
+ value = {FRONTEND_EVENT_TYPE_LOCKED, FRONTEND_EVENT_TYPE_NO_SIGNAL,
+ FRONTEND_EVENT_TYPE_LOST_LOCK})
@Retention(RetentionPolicy.SOURCE)
public @interface FrontendEventType {}
- /** @hide */
+ /**
+ * Frontend locked.
+ * @hide
+ */
public static final int FRONTEND_EVENT_TYPE_LOCKED = Constants.FrontendEventType.LOCKED;
- /** @hide */
+ /**
+ * No signal detected.
+ * @hide
+ */
public static final int FRONTEND_EVENT_TYPE_NO_SIGNAL = Constants.FrontendEventType.NO_SIGNAL;
- /** @hide */
+ /**
+ * Frontend lock lost.
+ * @hide
+ */
public static final int FRONTEND_EVENT_TYPE_LOST_LOCK = Constants.FrontendEventType.LOST_LOCK;
/** @hide */
- @IntDef({FILTER_SUBTYPE_UNDEFINED, FILTER_SUBTYPE_SECTION, FILTER_SUBTYPE_PES,
- FILTER_SUBTYPE_AUDIO, FILTER_SUBTYPE_VIDEO, FILTER_SUBTYPE_DOWNLOAD,
- FILTER_SUBTYPE_RECORD, FILTER_SUBTYPE_TS, FILTER_SUBTYPE_PCR, FILTER_SUBTYPE_TEMI,
- FILTER_SUBTYPE_MMPT, FILTER_SUBTYPE_NTP, FILTER_SUBTYPE_IP_PAYLOAD, FILTER_SUBTYPE_IP,
- FILTER_SUBTYPE_PAYLOAD_THROUGH, FILTER_SUBTYPE_TLV, FILTER_SUBTYPE_PTP, })
- @Retention(RetentionPolicy.SOURCE)
- public @interface FilterSubtype {}
- /** @hide */
- public static final int FILTER_SUBTYPE_UNDEFINED = 0;
- /** @hide */
- public static final int FILTER_SUBTYPE_SECTION = 1;
- /** @hide */
- public static final int FILTER_SUBTYPE_PES = 2;
- /** @hide */
- public static final int FILTER_SUBTYPE_AUDIO = 3;
- /** @hide */
- public static final int FILTER_SUBTYPE_VIDEO = 4;
- /** @hide */
- public static final int FILTER_SUBTYPE_DOWNLOAD = 5;
- /** @hide */
- public static final int FILTER_SUBTYPE_RECORD = 6;
- /** @hide */
- public static final int FILTER_SUBTYPE_TS = 7;
- /** @hide */
- public static final int FILTER_SUBTYPE_PCR = 8;
- /** @hide */
- public static final int FILTER_SUBTYPE_TEMI = 9;
- /** @hide */
- public static final int FILTER_SUBTYPE_MMPT = 10;
- /** @hide */
- public static final int FILTER_SUBTYPE_NTP = 11;
- /** @hide */
- public static final int FILTER_SUBTYPE_IP_PAYLOAD = 12;
- /** @hide */
- public static final int FILTER_SUBTYPE_IP = 13;
- /** @hide */
- public static final int FILTER_SUBTYPE_PAYLOAD_THROUGH = 14;
- /** @hide */
- public static final int FILTER_SUBTYPE_TLV = 15;
- /** @hide */
- public static final int FILTER_SUBTYPE_PTP = 16;
-
- /** @hide */
@IntDef(flag = true, prefix = "FILTER_STATUS_", value = {FILTER_STATUS_DATA_READY,
FILTER_STATUS_LOW_WATER, FILTER_STATUS_HIGH_WATER, FILTER_STATUS_OVERFLOW})
@Retention(RetentionPolicy.SOURCE)
@@ -129,93 +102,29 @@
*/
public static final int FILTER_STATUS_OVERFLOW = Constants.DemuxFilterStatus.OVERFLOW;
- /**
- * Indexes can be tagged through TS (Transport Stream) header.
- *
- * @hide
- */
- @IntDef(flag = true, value = {TS_INDEX_FIRST_PACKET, TS_INDEX_PAYLOAD_UNIT_START_INDICATOR,
- TS_INDEX_CHANGE_TO_NOT_SCRAMBLED, TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED,
- TS_INDEX_CHANGE_TO_ODD_SCRAMBLED, TS_INDEX_DISCONTINUITY_INDICATOR,
- TS_INDEX_RANDOM_ACCESS_INDICATOR, TS_INDEX_PRIORITY_INDICATOR, TS_INDEX_PCR_FLAG,
- TS_INDEX_OPCR_FLAG, TS_INDEX_SPLICING_POINT_FLAG, TS_INDEX_PRIVATE_DATA,
- TS_INDEX_ADAPTATION_EXTENSION_FLAG})
+
+ /** @hide */
@Retention(RetentionPolicy.SOURCE)
- public @interface TsIndex {}
+ @IntDef(prefix = "INDEX_TYPE_", value =
+ {INDEX_TYPE_NONE, INDEX_TYPE_SC, INDEX_TYPE_SC_HEVC})
+ public @interface ScIndexType {}
/**
- * TS index FIRST_PACKET.
+ * Start Code Index is not used.
* @hide
*/
- public static final int TS_INDEX_FIRST_PACKET = Constants.DemuxTsIndex.FIRST_PACKET;
+ public static final int INDEX_TYPE_NONE = Constants.DemuxRecordScIndexType.NONE;
/**
- * TS index PAYLOAD_UNIT_START_INDICATOR.
+ * Start Code index.
* @hide
*/
- public static final int TS_INDEX_PAYLOAD_UNIT_START_INDICATOR =
- Constants.DemuxTsIndex.PAYLOAD_UNIT_START_INDICATOR;
+ public static final int INDEX_TYPE_SC = Constants.DemuxRecordScIndexType.SC;
/**
- * TS index CHANGE_TO_NOT_SCRAMBLED.
+ * Start Code index for HEVC.
* @hide
*/
- public static final int TS_INDEX_CHANGE_TO_NOT_SCRAMBLED =
- Constants.DemuxTsIndex.CHANGE_TO_NOT_SCRAMBLED;
- /**
- * TS index CHANGE_TO_EVEN_SCRAMBLED.
- * @hide
- */
- public static final int TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED =
- Constants.DemuxTsIndex.CHANGE_TO_EVEN_SCRAMBLED;
- /**
- * TS index CHANGE_TO_ODD_SCRAMBLED.
- * @hide
- */
- public static final int TS_INDEX_CHANGE_TO_ODD_SCRAMBLED =
- Constants.DemuxTsIndex.CHANGE_TO_ODD_SCRAMBLED;
- /**
- * TS index DISCONTINUITY_INDICATOR.
- * @hide
- */
- public static final int TS_INDEX_DISCONTINUITY_INDICATOR =
- Constants.DemuxTsIndex.DISCONTINUITY_INDICATOR;
- /**
- * TS index RANDOM_ACCESS_INDICATOR.
- * @hide
- */
- public static final int TS_INDEX_RANDOM_ACCESS_INDICATOR =
- Constants.DemuxTsIndex.RANDOM_ACCESS_INDICATOR;
- /**
- * TS index PRIORITY_INDICATOR.
- * @hide
- */
- public static final int TS_INDEX_PRIORITY_INDICATOR = Constants.DemuxTsIndex.PRIORITY_INDICATOR;
- /**
- * TS index PCR_FLAG.
- * @hide
- */
- public static final int TS_INDEX_PCR_FLAG = Constants.DemuxTsIndex.PCR_FLAG;
- /**
- * TS index OPCR_FLAG.
- * @hide
- */
- public static final int TS_INDEX_OPCR_FLAG = Constants.DemuxTsIndex.OPCR_FLAG;
- /**
- * TS index SPLICING_POINT_FLAG.
- * @hide
- */
- public static final int TS_INDEX_SPLICING_POINT_FLAG =
- Constants.DemuxTsIndex.SPLICING_POINT_FLAG;
- /**
- * TS index PRIVATE_DATA.
- * @hide
- */
- public static final int TS_INDEX_PRIVATE_DATA = Constants.DemuxTsIndex.PRIVATE_DATA;
- /**
- * TS index ADAPTATION_EXTENSION_FLAG.
- * @hide
- */
- public static final int TS_INDEX_ADAPTATION_EXTENSION_FLAG =
- Constants.DemuxTsIndex.ADAPTATION_EXTENSION_FLAG;
+ public static final int INDEX_TYPE_SC_HEVC = Constants.DemuxRecordScIndexType.SC_HEVC;
+
/**
* Indexes can be tagged by Start Code in PES (Packetized Elementary Stream)
@@ -322,156 +231,6 @@
public static final int FRONTEND_SCAN_BLIND = Constants.FrontendScanType.SCAN_BLIND;
- /** @hide */
- @IntDef({FRONTEND_STATUS_TYPE_DEMOD_LOCK, FRONTEND_STATUS_TYPE_SNR, FRONTEND_STATUS_TYPE_BER,
- FRONTEND_STATUS_TYPE_PER, FRONTEND_STATUS_TYPE_PRE_BER,
- FRONTEND_STATUS_TYPE_SIGNAL_QUALITY, FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH,
- FRONTEND_STATUS_TYPE_SYMBOL_RATE, FRONTEND_STATUS_TYPE_FEC,
- FRONTEND_STATUS_TYPE_MODULATION, FRONTEND_STATUS_TYPE_SPECTRAL,
- FRONTEND_STATUS_TYPE_LNB_VOLTAGE, FRONTEND_STATUS_TYPE_PLP_ID,
- FRONTEND_STATUS_TYPE_EWBS, FRONTEND_STATUS_TYPE_AGC, FRONTEND_STATUS_TYPE_LNA,
- FRONTEND_STATUS_TYPE_LAYER_ERROR, FRONTEND_STATUS_TYPE_VBER_CN,
- FRONTEND_STATUS_TYPE_LBER_CN, FRONTEND_STATUS_TYPE_XER_CN, FRONTEND_STATUS_TYPE_MER,
- FRONTEND_STATUS_TYPE_FREQ_OFFSET, FRONTEND_STATUS_TYPE_HIERARCHY,
- FRONTEND_STATUS_TYPE_RF_LOCK, FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO})
- @Retention(RetentionPolicy.SOURCE)
- public @interface FrontendStatusType {}
-
- /**
- * Lock status for Demod.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_DEMOD_LOCK =
- Constants.FrontendStatusType.DEMOD_LOCK;
- /**
- * Signal to Noise Ratio.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_SNR = Constants.FrontendStatusType.SNR;
- /**
- * Bit Error Ratio.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_BER = Constants.FrontendStatusType.BER;
- /**
- * Packages Error Ratio.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_PER = Constants.FrontendStatusType.PER;
- /**
- * Bit Error Ratio before FEC.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_PRE_BER = Constants.FrontendStatusType.PRE_BER;
- /**
- * Signal Quality (0..100). Good data over total data in percent can be
- * used as a way to present Signal Quality.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_SIGNAL_QUALITY =
- Constants.FrontendStatusType.SIGNAL_QUALITY;
- /**
- * Signal Strength.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH =
- Constants.FrontendStatusType.SIGNAL_STRENGTH;
- /**
- * Symbol Rate.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE =
- Constants.FrontendStatusType.SYMBOL_RATE;
- /**
- * Forward Error Correction Type.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_FEC = Constants.FrontendStatusType.FEC;
- /**
- * Modulation Type.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_MODULATION =
- Constants.FrontendStatusType.MODULATION;
- /**
- * Spectral Inversion Type.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_SPECTRAL = Constants.FrontendStatusType.SPECTRAL;
- /**
- * LNB Voltage.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_LNB_VOLTAGE =
- Constants.FrontendStatusType.LNB_VOLTAGE;
- /**
- * Physical Layer Pipe ID.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_PLP_ID = Constants.FrontendStatusType.PLP_ID;
- /**
- * Status for Emergency Warning Broadcasting System.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_EWBS = Constants.FrontendStatusType.EWBS;
- /**
- * Automatic Gain Control.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_AGC = Constants.FrontendStatusType.AGC;
- /**
- * Low Noise Amplifier.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_LNA = Constants.FrontendStatusType.LNA;
- /**
- * Error status by layer.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_LAYER_ERROR =
- Constants.FrontendStatusType.LAYER_ERROR;
- /**
- * CN value by VBER.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_VBER_CN = Constants.FrontendStatusType.VBER_CN;
- /**
- * CN value by LBER.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_LBER_CN = Constants.FrontendStatusType.LBER_CN;
- /**
- * CN value by XER.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_XER_CN = Constants.FrontendStatusType.XER_CN;
- /**
- * Moduration Error Ratio.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_MER = Constants.FrontendStatusType.MER;
- /**
- * Difference between tuning frequency and actual locked frequency.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_FREQ_OFFSET =
- Constants.FrontendStatusType.FREQ_OFFSET;
- /**
- * Hierarchy for DVBT.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_HIERARCHY = Constants.FrontendStatusType.HIERARCHY;
- /**
- * Lock status for RF.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_RF_LOCK = Constants.FrontendStatusType.RF_LOCK;
- /**
- * PLP information in a frequency band for ATSC3.0 frontend.
- * @hide
- */
- public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO =
- Constants.FrontendStatusType.ATSC3_PLP_INFO;
/** @hide */
@LongDef({FEC_UNDEFINED, FEC_AUTO, FEC_1_2, FEC_1_3, FEC_1_4, FEC_1_5, FEC_2_3, FEC_2_5,
@@ -716,23 +475,6 @@
/** @hide */
- @IntDef({FILTER_SETTINGS_TS, FILTER_SETTINGS_MMTP, FILTER_SETTINGS_IP, FILTER_SETTINGS_TLV,
- FILTER_SETTINGS_ALP})
- @Retention(RetentionPolicy.SOURCE)
- public @interface FilterSettingsType {}
- /** @hide */
- public static final int FILTER_SETTINGS_TS = Constants.DemuxFilterMainType.TS;
- /** @hide */
- public static final int FILTER_SETTINGS_MMTP = Constants.DemuxFilterMainType.MMTP;
- /** @hide */
- public static final int FILTER_SETTINGS_IP = Constants.DemuxFilterMainType.IP;
- /** @hide */
- public static final int FILTER_SETTINGS_TLV = Constants.DemuxFilterMainType.TLV;
- /** @hide */
- public static final int FILTER_SETTINGS_ALP = Constants.DemuxFilterMainType.ALP;
-
-
- /** @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/TunerUtils.java b/media/java/android/media/tv/tuner/TunerUtils.java
index 8780b72..30aaa02 100644
--- a/media/java/android/media/tv/tuner/TunerUtils.java
+++ b/media/java/android/media/tv/tuner/TunerUtils.java
@@ -19,9 +19,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.tv.tuner.V1_0.Constants;
-import android.media.tv.tuner.TunerConstants.FilterSubtype;
-import android.media.tv.tuner.filter.FilterConfiguration;
-import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
+import android.media.tv.tuner.filter.Filter;
/**
* Utility class for tuner framework.
@@ -50,91 +48,91 @@
* @param mainType filter main type.
* @param subtype filter subtype.
*/
- public static int getFilterSubtype(@FilterType int mainType, @FilterSubtype int subtype) {
- if (mainType == FilterConfiguration.FILTER_TYPE_TS) {
+ public static int getFilterSubtype(@Filter.Type int mainType, @Filter.Subtype int subtype) {
+ if (mainType == Filter.TYPE_TS) {
switch (subtype) {
- case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
+ case Filter.SUBTYPE_UNDEFINED:
return Constants.DemuxTsFilterType.UNDEFINED;
- case TunerConstants.FILTER_SUBTYPE_SECTION:
+ case Filter.SUBTYPE_SECTION:
return Constants.DemuxTsFilterType.SECTION;
- case TunerConstants.FILTER_SUBTYPE_PES:
+ case Filter.SUBTYPE_PES:
return Constants.DemuxTsFilterType.PES;
- case TunerConstants.FILTER_SUBTYPE_TS:
+ case Filter.SUBTYPE_TS:
return Constants.DemuxTsFilterType.TS;
- case TunerConstants.FILTER_SUBTYPE_AUDIO:
+ case Filter.SUBTYPE_AUDIO:
return Constants.DemuxTsFilterType.AUDIO;
- case TunerConstants.FILTER_SUBTYPE_VIDEO:
+ case Filter.SUBTYPE_VIDEO:
return Constants.DemuxTsFilterType.VIDEO;
- case TunerConstants.FILTER_SUBTYPE_PCR:
+ case Filter.SUBTYPE_PCR:
return Constants.DemuxTsFilterType.PCR;
- case TunerConstants.FILTER_SUBTYPE_RECORD:
+ case Filter.SUBTYPE_RECORD:
return Constants.DemuxTsFilterType.RECORD;
- case TunerConstants.FILTER_SUBTYPE_TEMI:
+ case Filter.SUBTYPE_TEMI:
return Constants.DemuxTsFilterType.TEMI;
default:
break;
}
- } else if (mainType == FilterConfiguration.FILTER_TYPE_MMTP) {
+ } else if (mainType == Filter.TYPE_MMTP) {
switch (subtype) {
- case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
+ case Filter.SUBTYPE_UNDEFINED:
return Constants.DemuxMmtpFilterType.UNDEFINED;
- case TunerConstants.FILTER_SUBTYPE_SECTION:
+ case Filter.SUBTYPE_SECTION:
return Constants.DemuxMmtpFilterType.SECTION;
- case TunerConstants.FILTER_SUBTYPE_PES:
+ case Filter.SUBTYPE_PES:
return Constants.DemuxMmtpFilterType.PES;
- case TunerConstants.FILTER_SUBTYPE_MMPT:
+ case Filter.SUBTYPE_MMTP:
return Constants.DemuxMmtpFilterType.MMTP;
- case TunerConstants.FILTER_SUBTYPE_AUDIO:
+ case Filter.SUBTYPE_AUDIO:
return Constants.DemuxMmtpFilterType.AUDIO;
- case TunerConstants.FILTER_SUBTYPE_VIDEO:
+ case Filter.SUBTYPE_VIDEO:
return Constants.DemuxMmtpFilterType.VIDEO;
- case TunerConstants.FILTER_SUBTYPE_RECORD:
+ case Filter.SUBTYPE_RECORD:
return Constants.DemuxMmtpFilterType.RECORD;
- case TunerConstants.FILTER_SUBTYPE_DOWNLOAD:
+ case Filter.SUBTYPE_DOWNLOAD:
return Constants.DemuxMmtpFilterType.DOWNLOAD;
default:
break;
}
- } else if (mainType == FilterConfiguration.FILTER_TYPE_IP) {
+ } else if (mainType == Filter.TYPE_IP) {
switch (subtype) {
- case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
+ case Filter.SUBTYPE_UNDEFINED:
return Constants.DemuxIpFilterType.UNDEFINED;
- case TunerConstants.FILTER_SUBTYPE_SECTION:
+ case Filter.SUBTYPE_SECTION:
return Constants.DemuxIpFilterType.SECTION;
- case TunerConstants.FILTER_SUBTYPE_NTP:
+ case Filter.SUBTYPE_NTP:
return Constants.DemuxIpFilterType.NTP;
- case TunerConstants.FILTER_SUBTYPE_IP_PAYLOAD:
+ case Filter.SUBTYPE_IP_PAYLOAD:
return Constants.DemuxIpFilterType.IP_PAYLOAD;
- case TunerConstants.FILTER_SUBTYPE_IP:
+ case Filter.SUBTYPE_IP:
return Constants.DemuxIpFilterType.IP;
- case TunerConstants.FILTER_SUBTYPE_PAYLOAD_THROUGH:
+ case Filter.SUBTYPE_PAYLOAD_THROUGH:
return Constants.DemuxIpFilterType.PAYLOAD_THROUGH;
default:
break;
}
- } else if (mainType == FilterConfiguration.FILTER_TYPE_TLV) {
+ } else if (mainType == Filter.TYPE_TLV) {
switch (subtype) {
- case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
+ case Filter.SUBTYPE_UNDEFINED:
return Constants.DemuxTlvFilterType.UNDEFINED;
- case TunerConstants.FILTER_SUBTYPE_SECTION:
+ case Filter.SUBTYPE_SECTION:
return Constants.DemuxTlvFilterType.SECTION;
- case TunerConstants.FILTER_SUBTYPE_TLV:
+ case Filter.SUBTYPE_TLV:
return Constants.DemuxTlvFilterType.TLV;
- case TunerConstants.FILTER_SUBTYPE_PAYLOAD_THROUGH:
+ case Filter.SUBTYPE_PAYLOAD_THROUGH:
return Constants.DemuxTlvFilterType.PAYLOAD_THROUGH;
default:
break;
}
- } else if (mainType == FilterConfiguration.FILTER_TYPE_ALP) {
+ } else if (mainType == Filter.TYPE_ALP) {
switch (subtype) {
- case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
+ case Filter.SUBTYPE_UNDEFINED:
return Constants.DemuxAlpFilterType.UNDEFINED;
- case TunerConstants.FILTER_SUBTYPE_SECTION:
+ case Filter.SUBTYPE_SECTION:
return Constants.DemuxAlpFilterType.SECTION;
- case TunerConstants.FILTER_SUBTYPE_PTP:
+ case Filter.SUBTYPE_PTP:
return Constants.DemuxAlpFilterType.PTP;
- case TunerConstants.FILTER_SUBTYPE_PAYLOAD_THROUGH:
+ case Filter.SUBTYPE_PAYLOAD_THROUGH:
return Constants.DemuxAlpFilterType.PAYLOAD_THROUGH;
default:
break;
diff --git a/media/java/android/media/tv/tuner/dvr/Dvr.java b/media/java/android/media/tv/tuner/dvr/Dvr.java
index 95508d3..a17773c 100644
--- a/media/java/android/media/tv/tuner/dvr/Dvr.java
+++ b/media/java/android/media/tv/tuner/dvr/Dvr.java
@@ -17,11 +17,16 @@
package android.media.tv.tuner.dvr;
import android.annotation.BytesLong;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.hardware.tv.tuner.V1_0.Constants;
import android.media.tv.tuner.Tuner.Filter;
import android.media.tv.tuner.TunerConstants.Result;
import android.os.ParcelFileDescriptor;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Digital Video Record (DVR) interface provides record control on Demux's output buffer and
* playback control on Demux's input buffer.
@@ -29,6 +34,37 @@
* @hide
*/
public class Dvr {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "PLAYBACK_STATUS_",
+ value = {PLAYBACK_STATUS_EMPTY, PLAYBACK_STATUS_ALMOST_EMPTY,
+ PLAYBACK_STATUS_ALMOST_FULL, PLAYBACK_STATUS_FULL})
+ @interface PlaybackStatus {}
+
+ /**
+ * The space of the playback is empty.
+ */
+ public static final int PLAYBACK_STATUS_EMPTY = Constants.PlaybackStatus.SPACE_EMPTY;
+ /**
+ * The space of the playback is almost empty.
+ *
+ * <p> the threshold is set in {@link DvrSettings}.
+ */
+ public static final int PLAYBACK_STATUS_ALMOST_EMPTY =
+ Constants.PlaybackStatus.SPACE_ALMOST_EMPTY;
+ /**
+ * The space of the playback is almost full.
+ *
+ * <p> the threshold is set in {@link DvrSettings}.
+ */
+ public static final int PLAYBACK_STATUS_ALMOST_FULL =
+ Constants.PlaybackStatus.SPACE_ALMOST_FULL;
+ /**
+ * The space of the playback is full.
+ */
+ public static final int PLAYBACK_STATUS_FULL = Constants.PlaybackStatus.SPACE_FULL;
+
+
private long mNativeContext;
private DvrCallback mCallback;
diff --git a/media/java/android/media/tv/tuner/dvr/DvrCallback.java b/media/java/android/media/tv/tuner/dvr/DvrCallback.java
index 3d140f0..ee0cfa7d 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrCallback.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrCallback.java
@@ -16,6 +16,9 @@
package android.media.tv.tuner.dvr;
+import android.media.tv.tuner.TunerConstants.FilterStatus;
+import android.media.tv.tuner.dvr.Dvr.PlaybackStatus;
+
/**
* Callback interface for receiving information from DVR interfaces.
*
@@ -25,9 +28,9 @@
/**
* Invoked when record status changed.
*/
- void onRecordStatusChanged(int status);
+ void onRecordStatusChanged(@FilterStatus int status);
/**
* Invoked when playback status changed.
*/
- void onPlaybackStatusChanged(int status);
+ void onPlaybackStatusChanged(@PlaybackStatus int status);
}
diff --git a/media/java/android/media/tv/tuner/dvr/DvrSettings.java b/media/java/android/media/tv/tuner/dvr/DvrSettings.java
index 46efd7a..49e875a 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrSettings.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrSettings.java
@@ -19,8 +19,11 @@
import android.annotation.BytesLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
import android.hardware.tv.tuner.V1_0.Constants;
import android.media.tv.tuner.TunerConstants.FilterStatus;
+import android.media.tv.tuner.TunerUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -94,9 +97,13 @@
/**
* Creates a builder for {@link DvrSettings}.
+ *
+ * @param context the context of the caller.
*/
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@NonNull
- public static Builder newBuilder() {
+ public static Builder builder(Context context) {
+ TunerUtils.checkTunerPermission(context);
return new Builder();
}
diff --git a/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java
index f0fe533..fcca6a1 100644
--- a/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java
@@ -16,20 +16,123 @@
package android.media.tv.tuner.filter;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.hardware.tv.tuner.V1_0.Constants;
+import android.media.tv.tuner.TunerUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Filter configuration for a ALP filter.
* @hide
*/
public class AlpFilterConfiguration extends FilterConfiguration {
- private int mPacketType;
- private int mLengthType;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "LENGTH_TYPE_", value =
+ {LENGTH_TYPE_UNDEFINED, LENGTH_TYPE_WITHOUT_ADDITIONAL_HEADER,
+ LENGTH_TYPE_WITH_ADDITIONAL_HEADER})
+ public @interface LengthType {}
- public AlpFilterConfiguration(Settings settings) {
+ /**
+ * Length type not defined.
+ */
+ public static final int LENGTH_TYPE_UNDEFINED = Constants.DemuxAlpLengthType.UNDEFINED;
+ /**
+ * Length does NOT include additional header.
+ */
+ public static final int LENGTH_TYPE_WITHOUT_ADDITIONAL_HEADER =
+ Constants.DemuxAlpLengthType.WITHOUT_ADDITIONAL_HEADER;
+ /**
+ * Length includes additional header.
+ */
+ public static final int LENGTH_TYPE_WITH_ADDITIONAL_HEADER =
+ Constants.DemuxAlpLengthType.WITH_ADDITIONAL_HEADER;
+
+
+ private final int mPacketType;
+ private final int mLengthType;
+
+ public AlpFilterConfiguration(Settings settings, int packetType, int lengthType) {
super(settings);
+ mPacketType = packetType;
+ mLengthType = lengthType;
}
@Override
public int getType() {
return FilterConfiguration.FILTER_TYPE_ALP;
}
+
+ /**
+ * Gets packet type.
+ */
+ @FilterConfiguration.PacketType
+ public int getPacketType() {
+ return mPacketType;
+ }
+ /**
+ * Gets length type.
+ */
+ @LengthType
+ public int getLengthType() {
+ return mLengthType;
+ }
+
+ /**
+ * Creates a builder for {@link AlpFilterConfiguration}.
+ *
+ * @param context the context of the caller.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @NonNull
+ public static Builder builder(@NonNull Context context) {
+ TunerUtils.checkTunerPermission(context);
+ return new Builder();
+ }
+
+ /**
+ * Builder for {@link AlpFilterConfiguration}.
+ */
+ public static class Builder extends FilterConfiguration.Builder<Builder> {
+ private int mPacketType;
+ private int mLengthType;
+
+ private Builder() {
+ }
+
+ /**
+ * Sets packet type.
+ */
+ @NonNull
+ public Builder setPacketType(@FilterConfiguration.PacketType int packetType) {
+ mPacketType = packetType;
+ return this;
+ }
+ /**
+ * Sets length type.
+ */
+ @NonNull
+ public Builder setLengthType(@LengthType int lengthType) {
+ mLengthType = lengthType;
+ return this;
+ }
+
+ /**
+ * Builds a {@link AlpFilterConfiguration} object.
+ */
+ @NonNull
+ public AlpFilterConfiguration build() {
+ return new AlpFilterConfiguration(mSettings, mPacketType, mLengthType);
+ }
+
+ @Override
+ Builder self() {
+ return this;
+ }
+ }
}
diff --git a/media/java/android/media/tv/tuner/filter/AvSettings.java b/media/java/android/media/tv/tuner/filter/AvSettings.java
index a7c49d5..93eaaa4 100644
--- a/media/java/android/media/tv/tuner/filter/AvSettings.java
+++ b/media/java/android/media/tv/tuner/filter/AvSettings.java
@@ -16,21 +16,82 @@
package android.media.tv.tuner.filter;
-import android.media.tv.tuner.TunerConstants;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
import android.media.tv.tuner.TunerUtils;
/**
* Filter Settings for a Video and Audio.
+ *
* @hide
*/
public class AvSettings extends Settings {
- private boolean mIsPassthrough;
+ private final boolean mIsPassthrough;
- private AvSettings(int mainType, boolean isAudio) {
+ private AvSettings(int mainType, boolean isAudio, boolean isPassthrough) {
super(TunerUtils.getFilterSubtype(
mainType,
isAudio
- ? TunerConstants.FILTER_SUBTYPE_AUDIO
- : TunerConstants.FILTER_SUBTYPE_VIDEO));
+ ? Filter.SUBTYPE_AUDIO
+ : Filter.SUBTYPE_VIDEO));
+ mIsPassthrough = isPassthrough;
+ }
+
+ /**
+ * Checks whether it's passthrough.
+ */
+ public boolean isPassthrough() {
+ return mIsPassthrough;
+ }
+
+ /**
+ * Creates a builder for {@link AvSettings}.
+ *
+ * @param context the context of the caller.
+ * @param mainType the filter main type.
+ * @param isAudio {@code true} if it's audio settings; {@code false} if it's video settings.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @NonNull
+ public static Builder builder(
+ @NonNull Context context, @Filter.Type int mainType, boolean isAudio) {
+ TunerUtils.checkTunerPermission(context);
+ return new Builder(mainType, isAudio);
+ }
+
+ /**
+ * Builder for {@link AvSettings}.
+ */
+ public static class Builder extends Settings.Builder<Builder> {
+ private final boolean mIsAudio;
+ private boolean mIsPassthrough;
+
+ private Builder(int mainType, boolean isAudio) {
+ super(mainType);
+ mIsAudio = isAudio;
+ }
+
+ /**
+ * Sets whether it's passthrough.
+ */
+ @NonNull
+ public Builder setPassthrough(boolean isPassthrough) {
+ mIsPassthrough = isPassthrough;
+ return this;
+ }
+
+ /**
+ * Builds a {@link AvSettings} object.
+ */
+ @NonNull
+ public AvSettings build() {
+ return new AvSettings(mMainType, mIsAudio, mIsPassthrough);
+ }
+
+ @Override
+ Builder self() {
+ return this;
+ }
}
}
diff --git a/media/java/android/media/tv/tuner/filter/DownloadSettings.java b/media/java/android/media/tv/tuner/filter/DownloadSettings.java
index 0742b11..fa7744a 100644
--- a/media/java/android/media/tv/tuner/filter/DownloadSettings.java
+++ b/media/java/android/media/tv/tuner/filter/DownloadSettings.java
@@ -16,7 +16,9 @@
package android.media.tv.tuner.filter;
-import android.media.tv.tuner.TunerConstants;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
import android.media.tv.tuner.TunerUtils;
/**
@@ -24,9 +26,63 @@
* @hide
*/
public class DownloadSettings extends Settings {
- private int mDownloadId;
+ private final int mDownloadId;
- public DownloadSettings(int mainType) {
- super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_DOWNLOAD));
+ private DownloadSettings(int mainType, int downloadId) {
+ super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_DOWNLOAD));
+ mDownloadId = downloadId;
+ }
+
+ /**
+ * Gets download ID.
+ */
+ public int getDownloadId() {
+ return mDownloadId;
+ }
+
+ /**
+ * Creates a builder for {@link DownloadSettings}.
+ *
+ * @param context the context of the caller.
+ * @param mainType the filter main type.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @NonNull
+ public static Builder builder(@NonNull Context context, @Filter.Type int mainType) {
+ TunerUtils.checkTunerPermission(context);
+ return new Builder(mainType);
+ }
+
+ /**
+ * Builder for {@link DownloadSettings}.
+ */
+ public static class Builder extends Settings.Builder<Builder> {
+ private int mDownloadId;
+
+ private Builder(int mainType) {
+ super(mainType);
+ }
+
+ /**
+ * Sets download ID.
+ */
+ @NonNull
+ public Builder setDownloadId(int downloadId) {
+ mDownloadId = downloadId;
+ return this;
+ }
+
+ /**
+ * Builds a {@link DownloadSettings} object.
+ */
+ @NonNull
+ public DownloadSettings build() {
+ return new DownloadSettings(mMainType, mDownloadId);
+ }
+
+ @Override
+ Builder self() {
+ return this;
+ }
}
}
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 804c0c5..3f6154b 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -17,9 +17,15 @@
package android.media.tv.tuner.filter;
import android.annotation.BytesLong;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.tv.tuner.V1_0.Constants;
import android.media.tv.tuner.Tuner.FilterCallback;
+import android.media.tv.tuner.TunerConstants.Result;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* Tuner data filter.
@@ -29,6 +35,128 @@
* @hide
*/
public class Filter implements AutoCloseable {
+ /** @hide */
+ @IntDef(prefix = "TYPE_",
+ value = {TYPE_TS, TYPE_MMTP, TYPE_IP, TYPE_TLV, TYPE_ALP})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ /**
+ * TS filter type.
+ */
+ public static final int TYPE_TS = Constants.DemuxFilterMainType.TS;
+ /**
+ * MMTP filter type.
+ */
+ public static final int TYPE_MMTP = Constants.DemuxFilterMainType.MMTP;
+ /**
+ * IP filter type.
+ */
+ public static final int TYPE_IP = Constants.DemuxFilterMainType.IP;
+ /**
+ * TLV filter type.
+ */
+ public static final int TYPE_TLV = Constants.DemuxFilterMainType.TLV;
+ /**
+ * ALP filter type.
+ */
+ public static final int TYPE_ALP = Constants.DemuxFilterMainType.ALP;
+
+ /** @hide */
+ @IntDef(prefix = "SUBTYPE_",
+ value = {SUBTYPE_UNDEFINED, SUBTYPE_SECTION, SUBTYPE_PES, SUBTYPE_AUDIO, SUBTYPE_VIDEO,
+ SUBTYPE_DOWNLOAD, SUBTYPE_RECORD, SUBTYPE_TS, SUBTYPE_PCR, SUBTYPE_TEMI,
+ SUBTYPE_MMTP, SUBTYPE_NTP, SUBTYPE_IP_PAYLOAD, SUBTYPE_IP,
+ SUBTYPE_PAYLOAD_THROUGH, SUBTYPE_TLV, SUBTYPE_PTP, })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Subtype {}
+ /**
+ * Filter subtype undefined.
+ * @hide
+ */
+ public static final int SUBTYPE_UNDEFINED = 0;
+ /**
+ * Section filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_SECTION = 1;
+ /**
+ * PES filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_PES = 2;
+ /**
+ * Audio filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_AUDIO = 3;
+ /**
+ * Video filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_VIDEO = 4;
+ /**
+ * Download filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_DOWNLOAD = 5;
+ /**
+ * Record filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_RECORD = 6;
+ /**
+ * TS filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_TS = 7;
+ /**
+ * PCR filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_PCR = 8;
+ /**
+ * TEMI filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_TEMI = 9;
+ /**
+ * MMTP filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_MMTP = 10;
+ /**
+ * NTP filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_NTP = 11;
+ /**
+ * Payload filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_IP_PAYLOAD = 12;
+ /**
+ * IP filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_IP = 13;
+ /**
+ * Payload through filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_PAYLOAD_THROUGH = 14;
+ /**
+ * TLV filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_TLV = 15;
+ /**
+ * PTP filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_PTP = 16;
+
+
private long mNativeContext;
private FilterCallback mCallback;
private final int mId;
@@ -56,6 +184,7 @@
* @param config the configuration of the filter.
* @return result status of the operation.
*/
+ @Result
public int configure(@NonNull FilterConfiguration config) {
int subType = -1;
Settings s = config.getSettings();
@@ -68,6 +197,7 @@
/**
* Gets the filter Id.
*/
+ @Result
public int getId() {
return nativeGetId();
}
@@ -84,6 +214,7 @@
* use demux as data source if the filter instance is NULL.
* @return result status of the operation.
*/
+ @Result
public int setDataSource(@Nullable Filter source) {
return nativeSetDataSource(source);
}
@@ -93,6 +224,7 @@
*
* @return result status of the operation.
*/
+ @Result
public int start() {
return nativeStartFilter();
}
@@ -103,6 +235,7 @@
*
* @return result status of the operation.
*/
+ @Result
public int stop() {
return nativeStopFilter();
}
@@ -112,6 +245,7 @@
*
* @return result status of the operation.
*/
+ @Result
public int flush() {
return nativeFlushFilter();
}
@@ -124,6 +258,7 @@
* @param size the maximum number of bytes to read.
* @return the number of bytes read.
*/
+ @Result
public int read(@NonNull byte[] buffer, @BytesLong long offset, @BytesLong long size) {
size = Math.min(size, buffer.length - offset);
return nativeRead(buffer, offset, size);
diff --git a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
index 6496627..c901e2b 100644
--- a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
@@ -32,8 +32,12 @@
@SystemApi
public abstract class FilterConfiguration {
- /** @hide */
- @IntDef({FILTER_TYPE_TS, FILTER_TYPE_MMTP, FILTER_TYPE_IP, FILTER_TYPE_TLV, FILTER_TYPE_ALP})
+ /**
+ * TODO: moved to Filter. Remove it here.
+ * @hide
+ */
+ @IntDef(prefix = "FILTER_TYPE_", value =
+ {FILTER_TYPE_TS, FILTER_TYPE_MMTP, FILTER_TYPE_IP, FILTER_TYPE_TLV, FILTER_TYPE_ALP})
@Retention(RetentionPolicy.SOURCE)
public @interface FilterType {}
@@ -58,6 +62,30 @@
*/
public static final int FILTER_TYPE_ALP = Constants.DemuxFilterMainType.ALP;
+
+ /** @hide */
+ @IntDef(prefix = "PACKET_TYPE_", value =
+ {PACKET_TYPE_IPV4, PACKET_TYPE_COMPRESSED, PACKET_TYPE_SIGNALING})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PacketType {}
+
+ /**
+ * IP v4 packet type.
+ * @hide
+ */
+ public static final int PACKET_TYPE_IPV4 = 0;
+ /**
+ * Compressed packet type.
+ * @hide
+ */
+ public static final int PACKET_TYPE_COMPRESSED = 2;
+ /**
+ * Signaling packet type.
+ * @hide
+ */
+ public static final int PACKET_TYPE_SIGNALING = 4;
+
+
@Nullable
/* package */ final Settings mSettings;
@@ -77,4 +105,27 @@
public Settings getSettings() {
return mSettings;
}
+
+ /**
+ * Builder for {@link FilterConfiguration}.
+ *
+ * @param <T> The subclass to be built.
+ * @hide
+ */
+ public abstract static class Builder<T extends Builder<T>> {
+ /* package */ Settings mSettings;
+
+ /* package */ Builder() {
+ }
+
+ /**
+ * Sets filter settings.
+ */
+ @Nullable
+ public T setFrequency(Settings settings) {
+ mSettings = settings;
+ return self();
+ }
+ /* package */ abstract T self();
+ }
}
diff --git a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
index c896368..98edf10 100644
--- a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
@@ -16,23 +16,152 @@
package android.media.tv.tuner.filter;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.Size;
+import android.content.Context;
+import android.media.tv.tuner.TunerUtils;
+
/**
* Filter configuration for a IP filter.
* @hide
*/
public class IpFilterConfiguration extends FilterConfiguration {
- private byte[] mSrcIpAddress;
- private byte[] mDstIpAddress;
- private int mSrcPort;
- private int mDstPort;
- private boolean mPassthrough;
+ private final byte[] mSrcIpAddress;
+ private final byte[] mDstIpAddress;
+ private final int mSrcPort;
+ private final int mDstPort;
+ private final boolean mPassthrough;
- public IpFilterConfiguration(Settings settings) {
+ public IpFilterConfiguration(Settings settings, byte[] srcAddr, byte[] dstAddr, int srcPort,
+ int dstPort, boolean passthrough) {
super(settings);
+ mSrcIpAddress = srcAddr;
+ mDstIpAddress = dstAddr;
+ mSrcPort = srcPort;
+ mDstPort = dstPort;
+ mPassthrough = passthrough;
}
@Override
public int getType() {
return FilterConfiguration.FILTER_TYPE_IP;
}
+
+ /**
+ * Gets source IP address.
+ */
+ @Size(min = 4, max = 16)
+ public byte[] getSrcIpAddress() {
+ return mSrcIpAddress;
+ }
+ /**
+ * Gets destination IP address.
+ */
+ @Size(min = 4, max = 16)
+ public byte[] getDstIpAddress() {
+ return mDstIpAddress;
+ }
+ /**
+ * Gets source port.
+ */
+ public int getSrcPort() {
+ return mSrcPort;
+ }
+ /**
+ * Gets destination port.
+ */
+ public int getDstPort() {
+ return mDstPort;
+ }
+ /**
+ * Checks whether the filter is passthrough.
+ *
+ * @return {@code true} if the data from IP subtype go to next filter directly;
+ * {@code false} otherwise.
+ */
+ public boolean isPassthrough() {
+ return mPassthrough;
+ }
+
+ /**
+ * Creates a builder for {@link IpFilterConfiguration}.
+ *
+ * @param context the context of the caller.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @NonNull
+ public static Builder builder(@NonNull Context context) {
+ TunerUtils.checkTunerPermission(context);
+ return new Builder();
+ }
+
+ /**
+ * Builder for {@link IpFilterConfiguration}.
+ */
+ public static class Builder extends FilterConfiguration.Builder<Builder> {
+ private byte[] mSrcIpAddress;
+ private byte[] mDstIpAddress;
+ private int mSrcPort;
+ private int mDstPort;
+ private boolean mPassthrough;
+
+ private Builder() {
+ }
+
+ /**
+ * Sets source IP address.
+ */
+ @NonNull
+ public Builder setSrcIpAddress(byte[] srcIpAddress) {
+ mSrcIpAddress = srcIpAddress;
+ return this;
+ }
+ /**
+ * Sets destination IP address.
+ */
+ @NonNull
+ public Builder setDstIpAddress(byte[] dstIpAddress) {
+ mDstIpAddress = dstIpAddress;
+ return this;
+ }
+ /**
+ * Sets source port.
+ */
+ @NonNull
+ public Builder setSrcPort(int srcPort) {
+ mSrcPort = srcPort;
+ return this;
+ }
+ /**
+ * Sets destination port.
+ */
+ @NonNull
+ public Builder setDstPort(int dstPort) {
+ mDstPort = dstPort;
+ return this;
+ }
+ /**
+ * Sets passthrough.
+ */
+ @NonNull
+ public Builder setPassthrough(boolean passthrough) {
+ mPassthrough = passthrough;
+ return this;
+ }
+
+ /**
+ * Builds a {@link IpFilterConfiguration} object.
+ */
+ @NonNull
+ public IpFilterConfiguration build() {
+ return new IpFilterConfiguration(
+ mSettings, mSrcIpAddress, mDstIpAddress, mSrcPort, mDstPort, mPassthrough);
+ }
+
+ @Override
+ Builder self() {
+ return this;
+ }
+ }
}
diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java
index 37f94ae..0b5c56b 100644
--- a/media/java/android/media/tv/tuner/filter/MediaEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java
@@ -16,6 +16,7 @@
package android.media.tv.tuner.filter;
+import android.annotation.BytesLong;
import android.annotation.Nullable;
import android.media.tv.tuner.Tuner.Filter;
@@ -28,23 +29,27 @@
private final int mStreamId;
private final boolean mIsPtsPresent;
private final long mPts;
- private final int mDataLength;
+ private final long mDataLength;
+ private final long mOffset;
private final Object mLinearBuffer;
private final boolean mIsSecureMemory;
+ private final long mDataId;
private final int mMpuSequenceNumber;
private final boolean mIsPrivateData;
private final AudioDescriptor mExtraMetaData;
// This constructor is used by JNI code only
- private MediaEvent(int streamId, boolean isPtsPresent, long pts, int dataLength, Object buffer,
- boolean isSecureMemory, int mpuSequenceNumber, boolean isPrivateData,
- AudioDescriptor extraMetaData) {
+ private MediaEvent(int streamId, boolean isPtsPresent, long pts, long dataLength, long offset,
+ Object buffer, boolean isSecureMemory, long dataId, int mpuSequenceNumber,
+ boolean isPrivateData, AudioDescriptor extraMetaData) {
mStreamId = streamId;
mIsPtsPresent = isPtsPresent;
mPts = pts;
mDataLength = dataLength;
+ mOffset = offset;
mLinearBuffer = buffer;
mIsSecureMemory = isSecureMemory;
+ mDataId = dataId;
mMpuSequenceNumber = mpuSequenceNumber;
mIsPrivateData = isPrivateData;
mExtraMetaData = extraMetaData;
@@ -76,11 +81,20 @@
/**
* Gets data size in bytes of audio or video frame.
*/
- public int getDataLength() {
+ @BytesLong
+ public long getDataLength() {
return mDataLength;
}
/**
+ * The offset in the memory block which is shared among multiple Media Events.
+ */
+ @BytesLong
+ public long getOffset() {
+ return mOffset;
+ }
+
+ /**
* Gets a linear buffer associated to the memory where audio or video data stays.
* TODO: use LinearBuffer when it's ready.
*
@@ -101,6 +115,15 @@
}
/**
+ * Gets the ID which is used by HAL to provide additional information for AV data.
+ *
+ * <p>For secure audio, it's the audio handle used by Audio Track.
+ */
+ public long getAvDataId() {
+ return mDataId;
+ }
+
+ /**
* Gets MPU sequence number of filtered data.
*/
public int getMpuSequenceNumber() {
diff --git a/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
index 9045ce6..248f23a 100644
--- a/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
@@ -16,19 +16,78 @@
package android.media.tv.tuner.filter;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.media.tv.tuner.TunerUtils;
+
/**
* Filter configuration for a MMTP filter.
* @hide
*/
public class MmtpFilterConfiguration extends FilterConfiguration {
- private int mMmtpPid;
+ private final int mMmtpPid;
- public MmtpFilterConfiguration(Settings settings) {
+ public MmtpFilterConfiguration(Settings settings, int mmtpPid) {
super(settings);
+ mMmtpPid = mmtpPid;
}
@Override
public int getType() {
return FilterConfiguration.FILTER_TYPE_MMTP;
}
+
+ /**
+ * Gets MMTP PID.
+ *
+ * <p>Packet ID is used to specify packets in MMTP.
+ */
+ public int getMmtpPid() {
+ return mMmtpPid;
+ }
+
+ /**
+ * Creates a builder for {@link IpFilterConfiguration}.
+ *
+ * @param context the context of the caller.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @NonNull
+ public static Builder builder(@NonNull Context context) {
+ TunerUtils.checkTunerPermission(context);
+ return new Builder();
+ }
+
+ /**
+ * Builder for {@link IpFilterConfiguration}.
+ */
+ public static class Builder extends FilterConfiguration.Builder<Builder> {
+ private int mMmtpPid;
+
+ private Builder() {
+ }
+
+ /**
+ * Sets MMTP PID.
+ */
+ @NonNull
+ public Builder setMmtpPid(int mmtpPid) {
+ mMmtpPid = mmtpPid;
+ return this;
+ }
+
+ /**
+ * Builds a {@link IpFilterConfiguration} object.
+ */
+ @NonNull
+ public MmtpFilterConfiguration build() {
+ return new MmtpFilterConfiguration(mSettings, mMmtpPid);
+ }
+
+ @Override
+ Builder self() {
+ return this;
+ }
+ }
}
diff --git a/media/java/android/media/tv/tuner/filter/PesSettings.java b/media/java/android/media/tv/tuner/filter/PesSettings.java
index bfa1f8c..0f83597 100644
--- a/media/java/android/media/tv/tuner/filter/PesSettings.java
+++ b/media/java/android/media/tv/tuner/filter/PesSettings.java
@@ -20,9 +20,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
-import android.media.tv.tuner.TunerConstants;
import android.media.tv.tuner.TunerUtils;
-import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
/**
* Filter Settings for a PES Data.
@@ -34,8 +32,8 @@
private final int mStreamId;
private final boolean mIsRaw;
- private PesSettings(@FilterType int mainType, int streamId, boolean isRaw) {
- super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_PES));
+ private PesSettings(@Filter.Type int mainType, int streamId, boolean isRaw) {
+ super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_PES));
mStreamId = streamId;
mIsRaw = isRaw;
}
@@ -65,7 +63,7 @@
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@NonNull
- public static Builder builder(@NonNull Context context, @FilterType int mainType) {
+ public static Builder builder(@NonNull Context context, @Filter.Type int mainType) {
TunerUtils.checkTunerPermission(context);
return new Builder(mainType);
}
diff --git a/media/java/android/media/tv/tuner/filter/RecordSettings.java b/media/java/android/media/tv/tuner/filter/RecordSettings.java
index 701868a..4e9d67f 100644
--- a/media/java/android/media/tv/tuner/filter/RecordSettings.java
+++ b/media/java/android/media/tv/tuner/filter/RecordSettings.java
@@ -16,18 +16,230 @@
package android.media.tv.tuner.filter;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+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;
+import java.lang.annotation.RetentionPolicy;
+
/**
* The Settings for the record in DVR.
* @hide
*/
public class RecordSettings extends Settings {
- private int mIndexType;
- private int mIndexMask;
+ /**
+ * Indexes can be tagged through TS (Transport Stream) header.
+ *
+ * @hide
+ */
+ @IntDef(flag = true,
+ prefix = "TS_INDEX_",
+ value = {TS_INDEX_FIRST_PACKET, TS_INDEX_PAYLOAD_UNIT_START_INDICATOR,
+ TS_INDEX_CHANGE_TO_NOT_SCRAMBLED, TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED,
+ TS_INDEX_CHANGE_TO_ODD_SCRAMBLED, TS_INDEX_DISCONTINUITY_INDICATOR,
+ TS_INDEX_RANDOM_ACCESS_INDICATOR, TS_INDEX_PRIORITY_INDICATOR,
+ TS_INDEX_PCR_FLAG, TS_INDEX_OPCR_FLAG, TS_INDEX_SPLICING_POINT_FLAG,
+ TS_INDEX_PRIVATE_DATA, TS_INDEX_ADAPTATION_EXTENSION_FLAG})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TsIndexMask {}
- public RecordSettings(int mainType) {
- super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_RECORD));
+ /**
+ * TS index FIRST_PACKET.
+ * @hide
+ */
+ public static final int TS_INDEX_FIRST_PACKET = Constants.DemuxTsIndex.FIRST_PACKET;
+ /**
+ * TS index PAYLOAD_UNIT_START_INDICATOR.
+ * @hide
+ */
+ public static final int TS_INDEX_PAYLOAD_UNIT_START_INDICATOR =
+ Constants.DemuxTsIndex.PAYLOAD_UNIT_START_INDICATOR;
+ /**
+ * TS index CHANGE_TO_NOT_SCRAMBLED.
+ * @hide
+ */
+ public static final int TS_INDEX_CHANGE_TO_NOT_SCRAMBLED =
+ Constants.DemuxTsIndex.CHANGE_TO_NOT_SCRAMBLED;
+ /**
+ * TS index CHANGE_TO_EVEN_SCRAMBLED.
+ * @hide
+ */
+ public static final int TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED =
+ Constants.DemuxTsIndex.CHANGE_TO_EVEN_SCRAMBLED;
+ /**
+ * TS index CHANGE_TO_ODD_SCRAMBLED.
+ * @hide
+ */
+ public static final int TS_INDEX_CHANGE_TO_ODD_SCRAMBLED =
+ Constants.DemuxTsIndex.CHANGE_TO_ODD_SCRAMBLED;
+ /**
+ * TS index DISCONTINUITY_INDICATOR.
+ * @hide
+ */
+ public static final int TS_INDEX_DISCONTINUITY_INDICATOR =
+ Constants.DemuxTsIndex.DISCONTINUITY_INDICATOR;
+ /**
+ * TS index RANDOM_ACCESS_INDICATOR.
+ * @hide
+ */
+ public static final int TS_INDEX_RANDOM_ACCESS_INDICATOR =
+ Constants.DemuxTsIndex.RANDOM_ACCESS_INDICATOR;
+ /**
+ * TS index PRIORITY_INDICATOR.
+ * @hide
+ */
+ public static final int TS_INDEX_PRIORITY_INDICATOR = Constants.DemuxTsIndex.PRIORITY_INDICATOR;
+ /**
+ * TS index PCR_FLAG.
+ * @hide
+ */
+ public static final int TS_INDEX_PCR_FLAG = Constants.DemuxTsIndex.PCR_FLAG;
+ /**
+ * TS index OPCR_FLAG.
+ * @hide
+ */
+ public static final int TS_INDEX_OPCR_FLAG = Constants.DemuxTsIndex.OPCR_FLAG;
+ /**
+ * TS index SPLICING_POINT_FLAG.
+ * @hide
+ */
+ public static final int TS_INDEX_SPLICING_POINT_FLAG =
+ Constants.DemuxTsIndex.SPLICING_POINT_FLAG;
+ /**
+ * TS index PRIVATE_DATA.
+ * @hide
+ */
+ public static final int TS_INDEX_PRIVATE_DATA = Constants.DemuxTsIndex.PRIVATE_DATA;
+ /**
+ * TS index ADAPTATION_EXTENSION_FLAG.
+ * @hide
+ */
+ public static final int TS_INDEX_ADAPTATION_EXTENSION_FLAG =
+ Constants.DemuxTsIndex.ADAPTATION_EXTENSION_FLAG;
+ /**
+ * @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,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ScIndexMask {}
+
+
+ private final int mTsIndexMask;
+ private final int mScIndexType;
+ private final int mScIndexMask;
+
+ private RecordSettings(int mainType, int tsIndexType, int scIndexType, int scIndexMask) {
+ super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_RECORD));
+ mTsIndexMask = tsIndexType;
+ mScIndexType = scIndexType;
+ mScIndexMask = scIndexMask;
}
+
+ /**
+ * Gets TS index mask.
+ */
+ @TsIndexMask
+ public int getTsIndexMask() {
+ return mTsIndexMask;
+ }
+ /**
+ * Gets Start Code index type.
+ */
+ @ScIndexType
+ public int getScIndexType() {
+ return mScIndexType;
+ }
+ /**
+ * Gets Start Code index mask.
+ */
+ @ScIndexMask
+ public int getScIndexMask() {
+ return mScIndexMask;
+ }
+
+ /**
+ * Creates a builder for {@link RecordSettings}.
+ *
+ * @param context the context of the caller.
+ * @param mainType the filter main type.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @NonNull
+ public static Builder builder(@NonNull Context context, @Filter.Type int mainType) {
+ TunerUtils.checkTunerPermission(context);
+ return new Builder(mainType);
+ }
+
+ /**
+ * Builder for {@link RecordSettings}.
+ */
+ public static class Builder extends Settings.Builder<Builder> {
+ private int mTsIndexMask;
+ private int mScIndexType;
+ private int mScIndexMask;
+
+ private Builder(int mainType) {
+ super(mainType);
+ }
+
+ /**
+ * Sets TS index mask.
+ */
+ @NonNull
+ public Builder setTsIndexMask(@TsIndexMask int indexMask) {
+ mTsIndexMask = indexMask;
+ return this;
+ }
+ /**
+ * Sets index type.
+ */
+ @NonNull
+ public Builder setScIndexType(@ScIndexType int indexType) {
+ mScIndexType = indexType;
+ return this;
+ }
+ /**
+ * Sets Start Code index mask.
+ */
+ @NonNull
+ public Builder setScIndexMask(@ScIndexMask int indexMask) {
+ mScIndexMask = indexMask;
+ return this;
+ }
+
+ /**
+ * Builds a {@link RecordSettings} object.
+ */
+ @NonNull
+ public RecordSettings build() {
+ return new RecordSettings(mMainType, mTsIndexMask, mScIndexType, mScIndexMask);
+ }
+
+ @Override
+ Builder self() {
+ return this;
+ }
+ }
+
}
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettings.java b/media/java/android/media/tv/tuner/filter/SectionSettings.java
index 36e3d7c..b8d0fad 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettings.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettings.java
@@ -16,7 +16,6 @@
package android.media.tv.tuner.filter;
-import android.media.tv.tuner.TunerConstants;
import android.media.tv.tuner.TunerUtils;
/**
@@ -26,6 +25,6 @@
public class SectionSettings extends Settings {
SectionSettings(int mainType) {
- super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_SECTION));
+ super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_SECTION));
}
}
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
index 414ea67..a2d42d8 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
@@ -16,18 +16,115 @@
package android.media.tv.tuner.filter;
-import java.util.List;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.media.tv.tuner.TunerUtils;
/**
- * Bits Settings for Section Filter.
+ * Bits Settings for Section Filters.
* @hide
*/
public class SectionSettingsWithSectionBits extends SectionSettings {
- private List<Byte> mFilter;
- private List<Byte> mMask;
- private List<Byte> mMode;
+ private final byte[] mFilter;
+ private final byte[] mMask;
+ private final byte[] mMode;
- private SectionSettingsWithSectionBits(int mainType) {
+
+ private SectionSettingsWithSectionBits(int mainType, byte[] filter, byte[] mask, byte[] mode) {
super(mainType);
+ mFilter = filter;
+ mMask = mask;
+ mMode = mode;
+ }
+
+ /**
+ * Gets the bytes configured for Section Filter
+ */
+ public byte[] getFilterBytes() {
+ return mFilter;
+ }
+ /**
+ * Gets bit mask.
+ *
+ * <p>The bits in the bytes are used for filtering.
+ */
+ public byte[] getMask() {
+ return mMask;
+ }
+ /**
+ * Gets mode.
+ *
+ * <p>Do positive match at the bit position of the configured bytes when the bit at same
+ * position of the mode is 0.
+ * <p>Do negative match at the bit position of the configured bytes when the bit at same
+ * position of the mode is 1.
+ */
+ public byte[] getMode() {
+ return mMode;
+ }
+
+ /**
+ * Creates a builder for {@link SectionSettingsWithSectionBits}.
+ *
+ * @param context the context of the caller.
+ * @param mainType the filter main type.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @NonNull
+ public static Builder builder(@NonNull Context context, @Filter.Type int mainType) {
+ TunerUtils.checkTunerPermission(context);
+ return new Builder(mainType);
+ }
+
+ /**
+ * Builder for {@link SectionSettingsWithSectionBits}.
+ */
+ public static class Builder extends Settings.Builder<Builder> {
+ private byte[] mFilter;
+ private byte[] mMask;
+ private byte[] mMode;
+
+ private Builder(int mainType) {
+ super(mainType);
+ }
+
+ /**
+ * Sets filter bytes.
+ */
+ @NonNull
+ public Builder setFilter(byte[] filter) {
+ mFilter = filter;
+ return this;
+ }
+ /**
+ * Sets bit mask.
+ */
+ @NonNull
+ public Builder setMask(byte[] mask) {
+ mMask = mask;
+ return this;
+ }
+ /**
+ * Sets mode.
+ */
+ @NonNull
+ public Builder setMode(byte[] mode) {
+ mMode = mode;
+ return this;
+ }
+
+ /**
+ * Builds a {@link SectionSettingsWithSectionBits} object.
+ */
+ @NonNull
+ public SectionSettingsWithSectionBits build() {
+ return new SectionSettingsWithSectionBits(mMainType, mFilter, mMask, mMode);
+ }
+
+ @Override
+ Builder self() {
+ return this;
+ }
}
}
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
index 0df1d73..0c9cd2b 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
@@ -16,15 +16,91 @@
package android.media.tv.tuner.filter;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.media.tv.tuner.TunerUtils;
+
/**
* Table information for Section Filter.
* @hide
*/
public class SectionSettingsWithTableInfo extends SectionSettings {
- private int mTableId;
- private int mVersion;
+ private final int mTableId;
+ private final int mVersion;
- private SectionSettingsWithTableInfo(int mainType) {
+ private SectionSettingsWithTableInfo(int mainType, int tableId, int version) {
super(mainType);
+ mTableId = tableId;
+ mVersion = version;
}
+
+ /**
+ * Gets table ID.
+ */
+ public int getTableId() {
+ return mTableId;
+ }
+ /**
+ * Gets version.
+ */
+ public int getVersion() {
+ return mVersion;
+ }
+
+ /**
+ * Creates a builder for {@link SectionSettingsWithTableInfo}.
+ *
+ * @param context the context of the caller.
+ * @param mainType the filter main type.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @NonNull
+ public static Builder builder(@NonNull Context context, @Filter.Type int mainType) {
+ TunerUtils.checkTunerPermission(context);
+ return new Builder(mainType);
+ }
+
+ /**
+ * Builder for {@link SectionSettingsWithTableInfo}.
+ */
+ public static class Builder extends Settings.Builder<Builder> {
+ private int mTableId;
+ private int mVersion;
+
+ private Builder(int mainType) {
+ super(mainType);
+ }
+
+ /**
+ * Sets table ID.
+ */
+ @NonNull
+ public Builder setTableId(int tableId) {
+ mTableId = tableId;
+ return this;
+ }
+ /**
+ * Sets version.
+ */
+ @NonNull
+ public Builder setVersion(int version) {
+ mVersion = version;
+ return this;
+ }
+
+ /**
+ * Builds a {@link SectionSettingsWithTableInfo} object.
+ */
+ @NonNull
+ public SectionSettingsWithTableInfo build() {
+ return new SectionSettingsWithTableInfo(mMainType, mTableId, mVersion);
+ }
+
+ @Override
+ Builder self() {
+ return this;
+ }
+ }
+
}
diff --git a/media/java/android/media/tv/tuner/filter/Settings.java b/media/java/android/media/tv/tuner/filter/Settings.java
index 9155926..d697280 100644
--- a/media/java/android/media/tv/tuner/filter/Settings.java
+++ b/media/java/android/media/tv/tuner/filter/Settings.java
@@ -39,4 +39,20 @@
public int getType() {
return mType;
}
+
+
+ /**
+ * Builder for {@link Settings}.
+ *
+ * @param <T> The subclass to be built.
+ * @hide
+ */
+ public abstract static class Builder<T extends Builder<T>> {
+ /* package */ final int mMainType;
+
+ /* package */ Builder(int mainType) {
+ mMainType = mainType;
+ }
+ /* package */ abstract T self();
+ }
}
diff --git a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
index de8ee75..eb97fc0 100644
--- a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
@@ -16,21 +16,118 @@
package android.media.tv.tuner.filter;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.media.tv.tuner.TunerUtils;
+
/**
* Filter configuration for a TLV filter.
* @hide
*/
public class TlvFilterConfiguration extends FilterConfiguration {
- private int mPacketType;
- private boolean mIsCompressedIpPacket;
- private boolean mPassthrough;
+ private final int mPacketType;
+ private final boolean mIsCompressedIpPacket;
+ private final boolean mPassthrough;
- public TlvFilterConfiguration(Settings settings) {
+ public TlvFilterConfiguration(Settings settings, int packetType, boolean isCompressed,
+ boolean passthrough) {
super(settings);
+ mPacketType = packetType;
+ mIsCompressedIpPacket = isCompressed;
+ mPassthrough = passthrough;
}
@Override
public int getType() {
return FilterConfiguration.FILTER_TYPE_TLV;
}
+
+ /**
+ * Gets packet type.
+ */
+ @FilterConfiguration.PacketType
+ public int getPacketType() {
+ return mPacketType;
+ }
+ /**
+ * Checks whether the data is compressed IP packet.
+ *
+ * @return {@code true} if the filtered data is compressed IP packet; {@code false} otherwise.
+ */
+ public boolean isCompressedIpPacket() {
+ return mIsCompressedIpPacket;
+ }
+ /**
+ * Checks whether it's passthrough.
+ *
+ * @return {@code true} if the data from TLV subtype go to next filter directly;
+ * {@code false} otherwise.
+ */
+ public boolean isPassthrough() {
+ return mPassthrough;
+ }
+
+ /**
+ * Creates a builder for {@link TlvFilterConfiguration}.
+ *
+ * @param context the context of the caller.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @NonNull
+ public static Builder builder(@NonNull Context context) {
+ TunerUtils.checkTunerPermission(context);
+ return new Builder();
+ }
+
+ /**
+ * Builder for {@link TlvFilterConfiguration}.
+ */
+ public static class Builder extends FilterConfiguration.Builder<Builder> {
+ private int mPacketType;
+ private boolean mIsCompressedIpPacket;
+ private boolean mPassthrough;
+
+ private Builder() {
+ }
+
+ /**
+ * Sets packet type.
+ */
+ @NonNull
+ public Builder setPacketType(@FilterConfiguration.PacketType int packetType) {
+ mPacketType = packetType;
+ return this;
+ }
+ /**
+ * Sets whether the data is compressed IP packet.
+ */
+ @NonNull
+ public Builder setIsCompressedIpPacket(boolean isCompressedIpPacket) {
+ mIsCompressedIpPacket = isCompressedIpPacket;
+ return this;
+ }
+ /**
+ * Sets whether it's passthrough.
+ */
+ @NonNull
+ public Builder setPassthrough(boolean passthrough) {
+ mPassthrough = passthrough;
+ return this;
+ }
+
+ /**
+ * Builds a {@link TlvFilterConfiguration} object.
+ */
+ @NonNull
+ public TlvFilterConfiguration build() {
+ return new TlvFilterConfiguration(
+ mSettings, mPacketType, mIsCompressedIpPacket, mPassthrough);
+ }
+
+ @Override
+ Builder self() {
+ return this;
+ }
+ }
}
diff --git a/media/java/android/media/tv/tuner/filter/TsRecordEvent.java b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
index fa4dd72..1b8485e 100644
--- a/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
+++ b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
@@ -16,12 +16,8 @@
package android.media.tv.tuner.filter;
-import android.annotation.IntDef;
import android.media.tv.tuner.Tuner.Filter;
-import android.media.tv.tuner.TunerConstants;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
/**
* Filter event sent from {@link Filter} objects for TS record data.
@@ -29,47 +25,17 @@
* @hide
*/
public class TsRecordEvent extends FilterEvent {
- /**
- * @hide
- */
- @IntDef(flag = true, value = {
- TunerConstants.TS_INDEX_FIRST_PACKET,
- TunerConstants.TS_INDEX_PAYLOAD_UNIT_START_INDICATOR,
- TunerConstants.TS_INDEX_CHANGE_TO_NOT_SCRAMBLED,
- TunerConstants.TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED,
- TunerConstants.TS_INDEX_CHANGE_TO_ODD_SCRAMBLED,
- TunerConstants.TS_INDEX_DISCONTINUITY_INDICATOR,
- TunerConstants.TS_INDEX_RANDOM_ACCESS_INDICATOR,
- TunerConstants.TS_INDEX_PRIORITY_INDICATOR,
- TunerConstants.TS_INDEX_PCR_FLAG,
- TunerConstants.TS_INDEX_OPCR_FLAG,
- TunerConstants.TS_INDEX_SPLICING_POINT_FLAG,
- TunerConstants.TS_INDEX_PRIVATE_DATA,
- TunerConstants.TS_INDEX_ADAPTATION_EXTENSION_FLAG,
- 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,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface IndexMask {}
private final int mPid;
- private final int mIndexMask;
+ private final int mTsIndexMask;
+ private final int mScIndexMask;
private final long mByteNumber;
// This constructor is used by JNI code only
- private TsRecordEvent(int pid, int indexMask, long byteNumber) {
+ private TsRecordEvent(int pid, int tsIndexMask, int scIndexMask, long byteNumber) {
mPid = pid;
- mIndexMask = indexMask;
+ mTsIndexMask = tsIndexMask;
+ mScIndexMask = scIndexMask;
mByteNumber = byteNumber;
}
@@ -81,13 +47,20 @@
}
/**
- * Gets index mask.
- *
- * <p>The index type is one of TS, SC, and SC-HEVC, and is set when configuring the filter.
+ * Gets TS index mask.
*/
- @IndexMask
- public int getIndexMask() {
- return mIndexMask;
+ @RecordSettings.TsIndexMask
+ public int getTsIndexMask() {
+ return mTsIndexMask;
+ }
+ /**
+ * Gets SC index mask.
+ *
+ * <p>The index type is SC or SC-HEVC, and is set when configuring the filter.
+ */
+ @RecordSettings.ScIndexMask
+ public int getScIndexMask() {
+ return mScIndexMask;
}
/**
diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java
index d92c786..aa64df5 100644
--- a/media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java
+++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java
@@ -17,26 +17,31 @@
package android.media.tv.tuner.frontend;
/**
- * Analog Capabilities.
+ * Capabilities for analog tuners.
+ *
* @hide
*/
public class AnalogFrontendCapabilities extends FrontendCapabilities {
+ @AnalogFrontendSettings.SignalType
private final int mTypeCap;
+ @AnalogFrontendSettings.SifStandard
private final int mSifStandardCap;
- AnalogFrontendCapabilities(int typeCap, int sifStandardCap) {
+ // Called by JNI code.
+ private AnalogFrontendCapabilities(int typeCap, int sifStandardCap) {
mTypeCap = typeCap;
mSifStandardCap = sifStandardCap;
}
+
/**
- * Gets type capability.
+ * Gets analog signal type capability.
*/
@AnalogFrontendSettings.SignalType
- public int getTypeCapability() {
+ public int getSignalTypeCapability() {
return mTypeCap;
}
/**
- * Gets SIF standard capability.
+ * Gets Standard Interchange Format (SIF) capability.
*/
@AnalogFrontendSettings.SifStandard
public int getSifStandardCapability() {
diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
index 5b3bffc4..44dbcc0 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
@@ -112,31 +112,31 @@
public @interface Rolloff {}
/**
- * Roll Off undefined.
+ * Rolloff range undefined.
*/
public static final int ROLLOFF_UNDEFINED = Constants.FrontendDvbsRolloff.UNDEFINED;
/**
- * Roll Off 0_35.
+ * Rolloff range 0,35.
*/
public static final int ROLLOFF_0_35 = Constants.FrontendDvbsRolloff.ROLLOFF_0_35;
/**
- * Roll Off 0_25.
+ * Rolloff range 0,25.
*/
public static final int ROLLOFF_0_25 = Constants.FrontendDvbsRolloff.ROLLOFF_0_25;
/**
- * Roll Off 0_2.
+ * Rolloff range 0,20.
*/
public static final int ROLLOFF_0_20 = Constants.FrontendDvbsRolloff.ROLLOFF_0_20;
/**
- * Roll Off 0_15.
+ * Rolloff range 0,15.
*/
public static final int ROLLOFF_0_15 = Constants.FrontendDvbsRolloff.ROLLOFF_0_15;
/**
- * Roll Off 0_1.
+ * Rolloff range 0,10.
*/
public static final int ROLLOFF_0_10 = Constants.FrontendDvbsRolloff.ROLLOFF_0_10;
/**
- * Roll Off 0_5.
+ * Rolloff range 0,5.
*/
public static final int ROLLOFF_0_5 = Constants.FrontendDvbsRolloff.ROLLOFF_0_5;
@@ -188,6 +188,25 @@
*/
public static final int STANDARD_S2X = Constants.FrontendDvbsStandard.S2X;
+ /** @hide */
+ @IntDef(prefix = "VCM_MODE_",
+ value = {VCM_MODE_UNDEFINED, VCM_MODE_AUTO, VCM_MODE_MANUAL})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface VcmMode {}
+
+ /**
+ * VCM mode undefined.
+ */
+ public static final int VCM_MODE_UNDEFINED = Constants.FrontendDvbsVcmMode.UNDEFINED;
+ /**
+ * Auto VCM mode.
+ */
+ public static final int VCM_MODE_AUTO = Constants.FrontendDvbsVcmMode.AUTO;
+ /**
+ * Manual VCM mode.
+ */
+ public static final int VCM_MODE_MANUAL = Constants.FrontendDvbsVcmMode.MANUAL;
+
private final int mModulation;
private final DvbsCodeRate mCoderate;
@@ -196,9 +215,10 @@
private final int mPilot;
private final int mInputStreamId;
private final int mStandard;
+ private final int mVcmMode;
private DvbsFrontendSettings(int frequency, int modulation, DvbsCodeRate coderate,
- int symbolRate, int rolloff, int pilot, int inputStreamId, int standard) {
+ int symbolRate, int rolloff, int pilot, int inputStreamId, int standard, int vcm) {
super(frequency);
mModulation = modulation;
mCoderate = coderate;
@@ -207,6 +227,7 @@
mPilot = pilot;
mInputStreamId = inputStreamId;
mStandard = standard;
+ mVcmMode = vcm;
}
/**
@@ -256,6 +277,13 @@
public int getStandard() {
return mStandard;
}
+ /**
+ * Gets VCM mode.
+ */
+ @VcmMode
+ public int getVcmMode() {
+ return mVcmMode;
+ }
/**
* Creates a builder for {@link DvbsFrontendSettings}.
@@ -280,6 +308,7 @@
private int mPilot;
private int mInputStreamId;
private int mStandard;
+ private int mVcmMode;
private Builder() {
}
@@ -340,6 +369,14 @@
mStandard = standard;
return this;
}
+ /**
+ * Sets VCM mode.
+ */
+ @NonNull
+ public Builder setVcmMode(@VcmMode int vcm) {
+ mVcmMode = vcm;
+ return this;
+ }
/**
* Builds a {@link DvbsFrontendSettings} object.
@@ -347,7 +384,7 @@
@NonNull
public DvbsFrontendSettings build() {
return new DvbsFrontendSettings(mFrequency, mModulation, mCoderate, mSymbolRate,
- mRolloff, mPilot, mInputStreamId, mStandard);
+ mRolloff, mPilot, mInputStreamId, mStandard, mVcmMode);
}
@Override
diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
index f0469b7..9a82de0 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
@@ -110,7 +110,7 @@
*/
public static final int BANDWIDTH_5MHZ = Constants.FrontendDvbtBandwidth.BANDWIDTH_5MHZ;
/**
- * 1.7 MHz bandwidth.
+ * 1,7 MHz bandwidth.
*/
public static final int BANDWIDTH_1_7MHZ = Constants.FrontendDvbtBandwidth.BANDWIDTH_1_7MHZ;
/**
@@ -232,39 +232,39 @@
*/
public static final int CODERATE_AUTO = Constants.FrontendDvbtCoderate.AUTO;
/**
- * 1_2 code rate.
+ * 1/2 code rate.
*/
public static final int CODERATE_1_2 = Constants.FrontendDvbtCoderate.CODERATE_1_2;
/**
- * 2_3 code rate.
+ * 2/3 code rate.
*/
public static final int CODERATE_2_3 = Constants.FrontendDvbtCoderate.CODERATE_2_3;
/**
- * 3_4 code rate.
+ * 3/4 code rate.
*/
public static final int CODERATE_3_4 = Constants.FrontendDvbtCoderate.CODERATE_3_4;
/**
- * 5_6 code rate.
+ * 5/6 code rate.
*/
public static final int CODERATE_5_6 = Constants.FrontendDvbtCoderate.CODERATE_5_6;
/**
- * 7_8 code rate.
+ * 7/8 code rate.
*/
public static final int CODERATE_7_8 = Constants.FrontendDvbtCoderate.CODERATE_7_8;
/**
- * 4_5 code rate.
+ * 4/5 code rate.
*/
public static final int CODERATE_3_5 = Constants.FrontendDvbtCoderate.CODERATE_3_5;
/**
- * 4_5 code rate.
+ * 4/5 code rate.
*/
public static final int CODERATE_4_5 = Constants.FrontendDvbtCoderate.CODERATE_4_5;
/**
- * 6_7 code rate.
+ * 6/7 code rate.
*/
public static final int CODERATE_6_7 = Constants.FrontendDvbtCoderate.CODERATE_6_7;
/**
- * 8_9 code rate.
+ * 8/9 code rate.
*/
public static final int CODERATE_8_9 = Constants.FrontendDvbtCoderate.CODERATE_8_9;
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java
index 7350bc0..e4f66b8 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java
@@ -17,7 +17,8 @@
package android.media.tv.tuner.frontend;
/**
- * Frontend Capabilities.
+ * Frontend capabilities.
+ *
* @hide
*/
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 99e8dd2..360c84a 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
@@ -16,77 +16,98 @@
package android.media.tv.tuner.frontend;
+import android.annotation.NonNull;
import android.media.tv.tuner.frontend.FrontendSettings.Type;
+import android.media.tv.tuner.frontend.FrontendStatus.FrontendStatusType;
+import android.util.Range;
/**
- * Frontend info.
+ * This class is used to specify meta information of a frontend.
+ *
* @hide
*/
public class FrontendInfo {
private final int mId;
private final int mType;
- private final int mMinFrequency;
- private final int mMaxFrequency;
- private final int mMinSymbolRate;
- private final int mMaxSymbolRate;
+ private final Range<Integer> mFrequencyRange;
+ private final Range<Integer> mSymbolRateRange;
private final int mAcquireRange;
private final int mExclusiveGroupId;
private final int[] mStatusCaps;
private final FrontendCapabilities mFrontendCap;
- FrontendInfo(int id, int type, int minFrequency, int maxFrequency, int minSymbolRate,
+ private FrontendInfo(int id, int type, int minFrequency, int maxFrequency, int minSymbolRate,
int maxSymbolRate, int acquireRange, int exclusiveGroupId, int[] statusCaps,
FrontendCapabilities frontendCap) {
mId = id;
mType = type;
- mMinFrequency = minFrequency;
- mMaxFrequency = maxFrequency;
- mMinSymbolRate = minSymbolRate;
- mMaxSymbolRate = maxSymbolRate;
+ mFrequencyRange = new Range<>(minFrequency, maxFrequency);
+ mSymbolRateRange = new Range<>(minSymbolRate, maxSymbolRate);
mAcquireRange = acquireRange;
mExclusiveGroupId = exclusiveGroupId;
mStatusCaps = statusCaps;
mFrontendCap = frontendCap;
}
- /** Gets frontend ID. */
+ /**
+ * Gets frontend ID.
+ */
public int getId() {
return mId;
}
- /** Gets frontend type. */
+ /**
+ * Gets frontend type.
+ */
@Type
public int getType() {
return mType;
}
- /** Gets min frequency. */
- public int getMinFrequency() {
- return mMinFrequency;
+
+ /**
+ * Gets supported frequency range in Hz.
+ */
+ @NonNull
+ public Range<Integer> getFrequencyRange() {
+ return mFrequencyRange;
}
- /** Gets max frequency. */
- public int getMaxFrequency() {
- return mMaxFrequency;
+
+ /**
+ * Gets symbol rate range in symbols per second.
+ */
+ @NonNull
+ public Range<Integer> getSymbolRateRange() {
+ return mSymbolRateRange;
}
- /** Gets min symbol rate. */
- public int getMinSymbolRate() {
- return mMinSymbolRate;
- }
- /** Gets max symbol rate. */
- public int getMaxSymbolRate() {
- return mMaxSymbolRate;
- }
- /** Gets acquire range. */
+
+ /**
+ * Gets acquire range in Hz.
+ *
+ * <p>The maximum frequency difference the frontend can detect.
+ */
public int getAcquireRange() {
return mAcquireRange;
}
- /** Gets exclusive group ID. */
+ /**
+ * Gets exclusive group ID.
+ *
+ * <p>Frontends with the same exclusive group ID indicates they can't function at same time. For
+ * instance, they share some hardware modules.
+ */
public int getExclusiveGroupId() {
return mExclusiveGroupId;
}
- /** Gets status capabilities. */
+ /**
+ * Gets status capabilities.
+ *
+ * @return An array of supported status types.
+ */
+ @FrontendStatusType
public int[] getStatusCapabilities() {
return mStatusCaps;
}
- /** Gets frontend capability. */
+ /**
+ * Gets frontend capabilities.
+ */
public FrontendCapabilities getFrontendCapability() {
return mFrontendCap;
}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index f973d88..088adff 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -16,11 +16,15 @@
package android.media.tv.tuner.frontend;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.hardware.tv.tuner.V1_0.Constants;
import android.media.tv.tuner.Lnb;
-import android.media.tv.tuner.TunerConstants;
import android.media.tv.tuner.TunerConstants.FrontendInnerFec;
import android.media.tv.tuner.TunerConstants.FrontendModulation;
-import android.media.tv.tuner.TunerConstants.FrontendStatusType;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* Frontend status
@@ -29,204 +33,409 @@
*/
public class FrontendStatus {
- private final int mType;
- private final Object mValue;
+ /** @hide */
+ @IntDef({FRONTEND_STATUS_TYPE_DEMOD_LOCK, FRONTEND_STATUS_TYPE_SNR, FRONTEND_STATUS_TYPE_BER,
+ FRONTEND_STATUS_TYPE_PER, FRONTEND_STATUS_TYPE_PRE_BER,
+ FRONTEND_STATUS_TYPE_SIGNAL_QUALITY, FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH,
+ FRONTEND_STATUS_TYPE_SYMBOL_RATE, FRONTEND_STATUS_TYPE_FEC,
+ FRONTEND_STATUS_TYPE_MODULATION, FRONTEND_STATUS_TYPE_SPECTRAL,
+ FRONTEND_STATUS_TYPE_LNB_VOLTAGE, FRONTEND_STATUS_TYPE_PLP_ID,
+ FRONTEND_STATUS_TYPE_EWBS, FRONTEND_STATUS_TYPE_AGC, FRONTEND_STATUS_TYPE_LNA,
+ FRONTEND_STATUS_TYPE_LAYER_ERROR, FRONTEND_STATUS_TYPE_VBER_CN,
+ FRONTEND_STATUS_TYPE_LBER_CN, FRONTEND_STATUS_TYPE_XER_CN, FRONTEND_STATUS_TYPE_MER,
+ FRONTEND_STATUS_TYPE_FREQ_OFFSET, FRONTEND_STATUS_TYPE_HIERARCHY,
+ FRONTEND_STATUS_TYPE_RF_LOCK, FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendStatusType {}
- private FrontendStatus(int type, Object value) {
- mType = type;
- mValue = value;
+ /**
+ * Lock status for Demod.
+ */
+ public static final int FRONTEND_STATUS_TYPE_DEMOD_LOCK =
+ Constants.FrontendStatusType.DEMOD_LOCK;
+ /**
+ * Signal to Noise Ratio.
+ */
+ public static final int FRONTEND_STATUS_TYPE_SNR = Constants.FrontendStatusType.SNR;
+ /**
+ * Bit Error Ratio.
+ */
+ public static final int FRONTEND_STATUS_TYPE_BER = Constants.FrontendStatusType.BER;
+ /**
+ * Packages Error Ratio.
+ */
+ public static final int FRONTEND_STATUS_TYPE_PER = Constants.FrontendStatusType.PER;
+ /**
+ * Bit Error Ratio before FEC.
+ */
+ public static final int FRONTEND_STATUS_TYPE_PRE_BER = Constants.FrontendStatusType.PRE_BER;
+ /**
+ * Signal Quality (0..100). Good data over total data in percent can be
+ * used as a way to present Signal Quality.
+ */
+ public static final int FRONTEND_STATUS_TYPE_SIGNAL_QUALITY =
+ Constants.FrontendStatusType.SIGNAL_QUALITY;
+ /**
+ * Signal Strength.
+ */
+ public static final int FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH =
+ Constants.FrontendStatusType.SIGNAL_STRENGTH;
+ /**
+ * Symbol Rate in symbols per second.
+ */
+ public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE =
+ Constants.FrontendStatusType.SYMBOL_RATE;
+ /**
+ * Forward Error Correction Type.
+ */
+ public static final int FRONTEND_STATUS_TYPE_FEC = Constants.FrontendStatusType.FEC;
+ /**
+ * Modulation Type.
+ */
+ public static final int FRONTEND_STATUS_TYPE_MODULATION =
+ Constants.FrontendStatusType.MODULATION;
+ /**
+ * Spectral Inversion Type.
+ */
+ public static final int FRONTEND_STATUS_TYPE_SPECTRAL = Constants.FrontendStatusType.SPECTRAL;
+ /**
+ * LNB Voltage.
+ */
+ public static final int FRONTEND_STATUS_TYPE_LNB_VOLTAGE =
+ Constants.FrontendStatusType.LNB_VOLTAGE;
+ /**
+ * Physical Layer Pipe ID.
+ */
+ public static final int FRONTEND_STATUS_TYPE_PLP_ID = Constants.FrontendStatusType.PLP_ID;
+ /**
+ * Status for Emergency Warning Broadcasting System.
+ */
+ public static final int FRONTEND_STATUS_TYPE_EWBS = Constants.FrontendStatusType.EWBS;
+ /**
+ * Automatic Gain Control.
+ */
+ public static final int FRONTEND_STATUS_TYPE_AGC = Constants.FrontendStatusType.AGC;
+ /**
+ * Low Noise Amplifier.
+ */
+ public static final int FRONTEND_STATUS_TYPE_LNA = Constants.FrontendStatusType.LNA;
+ /**
+ * Error status by layer.
+ */
+ public static final int FRONTEND_STATUS_TYPE_LAYER_ERROR =
+ Constants.FrontendStatusType.LAYER_ERROR;
+ /**
+ * CN value by VBER.
+ */
+ public static final int FRONTEND_STATUS_TYPE_VBER_CN = Constants.FrontendStatusType.VBER_CN;
+ /**
+ * CN value by LBER.
+ */
+ public static final int FRONTEND_STATUS_TYPE_LBER_CN = Constants.FrontendStatusType.LBER_CN;
+ /**
+ * CN value by XER.
+ */
+ public static final int FRONTEND_STATUS_TYPE_XER_CN = Constants.FrontendStatusType.XER_CN;
+ /**
+ * Modulation Error Ratio.
+ */
+ public static final int FRONTEND_STATUS_TYPE_MER = Constants.FrontendStatusType.MER;
+ /**
+ * Difference between tuning frequency and actual locked frequency.
+ */
+ public static final int FRONTEND_STATUS_TYPE_FREQ_OFFSET =
+ Constants.FrontendStatusType.FREQ_OFFSET;
+ /**
+ * Hierarchy for DVBT.
+ */
+ public static final int FRONTEND_STATUS_TYPE_HIERARCHY = Constants.FrontendStatusType.HIERARCHY;
+ /**
+ * Lock status for RF.
+ */
+ public static final int FRONTEND_STATUS_TYPE_RF_LOCK = Constants.FrontendStatusType.RF_LOCK;
+ /**
+ * PLP information in a frequency band for ATSC-3.0 frontend.
+ */
+ public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO =
+ Constants.FrontendStatusType.ATSC3_PLP_INFO;
+
+
+ private Boolean mIsDemodLocked;
+ private Integer mSnr;
+ private Integer mBer;
+ private Integer mPer;
+ private Integer mPerBer;
+ private Integer mSignalQuality;
+ private Integer mSignalStrength;
+ private Integer mSymbolRate;
+ private Long mInnerFec;
+ private Integer mModulation;
+ private Integer mInversion;
+ private Integer mLnbVoltage;
+ private Integer mPlpId;
+ private Boolean mIsEwbs;
+ private Integer mAgc;
+ private Boolean mIsLnaOn;
+ private boolean[] mIsLayerErrors;
+ private Integer mVberCn;
+ private Integer mLberCn;
+ private Integer mXerCn;
+ private Integer mMer;
+ private Integer mFreqOffset;
+ private Integer mHierarchy;
+ private Boolean mIsRfLocked;
+ private Atsc3PlpInfo[] mPlpInfo;
+
+ // Constructed and fields set by JNI code.
+ private FrontendStatus() {
}
- /** Gets frontend status type. */
- @FrontendStatusType
- public int getStatusType() {
- return mType;
- }
- /** Lock status for Demod in True/False. */
- public boolean getIsDemodLocked() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_DEMOD_LOCK) {
+ /**
+ * Lock status for Demod.
+ */
+ public boolean isDemodLocked() {
+ if (mIsDemodLocked == null) {
throw new IllegalStateException();
}
- return (Boolean) mValue;
- }
- /** SNR value measured by 0.001 dB. */
- public int getSnr() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_SNR) {
- throw new IllegalStateException();
- }
- return (int) mValue;
- }
- /** The number of error bit per 1 billion bits. */
- public int getBer() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_BER) {
- throw new IllegalStateException();
- }
- return (int) mValue;
- }
- /** The number of error package per 1 billion packages. */
- public int getPer() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_PER) {
- throw new IllegalStateException();
- }
- return (int) mValue;
- }
- /** The number of error bit per 1 billion bits before FEC. */
- public int getPerBer() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_PRE_BER) {
- throw new IllegalStateException();
- }
- return (int) mValue;
- }
- /** Signal Quality in percent. */
- public int getSignalQuality() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_SIGNAL_QUALITY) {
- throw new IllegalStateException();
- }
- return (int) mValue;
- }
- /** Signal Strength measured by 0.001 dBm. */
- public int getSignalStrength() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH) {
- throw new IllegalStateException();
- }
- return (int) mValue;
- }
- /** Symbols per second. */
- public int getSymbolRate() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_SYMBOL_RATE) {
- throw new IllegalStateException();
- }
- return (int) mValue;
+ return mIsDemodLocked;
}
/**
- * Inner Forward Error Correction type as specified in ETSI EN 300 468 V1.15.1
+ * Gets Signal to Noise Ratio in thousandths of a deciBel (0.001dB).
+ */
+ public int getSnr() {
+ if (mSnr == null) {
+ throw new IllegalStateException();
+ }
+ return mSnr;
+ }
+ /**
+ * Gets Bit Error Ratio.
+ *
+ * <p>The number of error bit per 1 billion bits.
+ */
+ public int getBer() {
+ if (mBer == null) {
+ throw new IllegalStateException();
+ }
+ return mBer;
+ }
+
+ /**
+ * Gets Packages Error Ratio.
+ *
+ * <p>The number of error package per 1 billion packages.
+ */
+ public int getPer() {
+ if (mPer == null) {
+ throw new IllegalStateException();
+ }
+ return mPer;
+ }
+ /**
+ * Gets Bit Error Ratio before Forward Error Correction (FEC).
+ *
+ * <p>The number of error bit per 1 billion bits before FEC.
+ */
+ public int getPerBer() {
+ if (mPerBer == null) {
+ throw new IllegalStateException();
+ }
+ return mPerBer;
+ }
+ /**
+ * Gets Signal Quality in percent.
+ */
+ public int getSignalQuality() {
+ if (mSignalQuality == null) {
+ throw new IllegalStateException();
+ }
+ return mSignalQuality;
+ }
+ /**
+ * Gets Signal Strength in thousandths of a dBm (0.001dBm).
+ */
+ public int getSignalStrength() {
+ if (mSignalStrength == null) {
+ throw new IllegalStateException();
+ }
+ return mSignalStrength;
+ }
+ /**
+ * Gets symbol rate in symbols per second.
+ */
+ public int getSymbolRate() {
+ if (mSymbolRate == null) {
+ throw new IllegalStateException();
+ }
+ return mSymbolRate;
+ }
+ /**
+ * 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
public long getFec() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_FEC) {
+ if (mInnerFec == null) {
throw new IllegalStateException();
}
- return (long) mValue;
+ return mInnerFec;
}
- /** Modulation */
+ /**
+ * Gets modulation.
+ */
@FrontendModulation
public int getModulation() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_MODULATION) {
+ if (mModulation == null) {
throw new IllegalStateException();
}
- return (int) mValue;
+ return mModulation;
}
- /** Spectral Inversion for DVBC. */
+ /**
+ * Gets Spectral Inversion for DVBC.
+ */
@DvbcFrontendSettings.SpectralInversion
public int getSpectralInversion() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_SPECTRAL) {
+ if (mInversion == null) {
throw new IllegalStateException();
}
- return (int) mValue;
+ return mInversion;
}
- /** Power Voltage Type for LNB. */
+ /**
+ * Gets Power Voltage Type for LNB.
+ */
@Lnb.Voltage
public int getLnbVoltage() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_LNB_VOLTAGE) {
+ if (mLnbVoltage == null) {
throw new IllegalStateException();
}
- return (int) mValue;
+ return mLnbVoltage;
}
- /** PLP ID */
- public byte getPlpId() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_PLP_ID) {
+ /**
+ * Gets Physical Layer Pipe ID.
+ */
+ public int getPlpId() {
+ if (mPlpId == null) {
throw new IllegalStateException();
}
- return (byte) mValue;
+ return mPlpId;
}
- /** Emergency Warning Broadcasting System */
- public boolean getIsEwbs() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_EWBS) {
+ /**
+ * Checks whether it's Emergency Warning Broadcasting System
+ */
+ public boolean isEwbs() {
+ if (mIsEwbs == null) {
throw new IllegalStateException();
}
- return (Boolean) mValue;
+ return mIsEwbs;
}
- /** AGC value is normalized from 0 to 255. */
- public byte getAgc() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_AGC) {
+ /**
+ * Gets Automatic Gain Control value which is normalized from 0 to 255.
+ */
+ public int getAgc() {
+ if (mAgc == null) {
throw new IllegalStateException();
}
- return (byte) mValue;
+ return mAgc;
}
- /** LNA(Low Noise Amplifier) is on or not. */
- public boolean getLnaOn() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_LNA) {
+ /**
+ * Checks LNA (Low Noise Amplifier) is on or not.
+ */
+ public boolean isLnaOn() {
+ if (mIsLnaOn == null) {
throw new IllegalStateException();
}
- return (Boolean) mValue;
+ return mIsLnaOn;
}
- /** Error status by layer. */
- public boolean[] getIsLayerError() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_LAYER_ERROR) {
+ /**
+ * Gets Error status by layer.
+ */
+ @NonNull
+ public boolean[] getLayerErrors() {
+ if (mIsLayerErrors == null) {
throw new IllegalStateException();
}
- return (boolean[]) mValue;
+ return mIsLayerErrors;
}
- /** CN value by VBER measured by 0.001 dB. */
+ /**
+ * Gets CN value by VBER in thousandths of a deciBel (0.001dB).
+ */
public int getVberCn() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_VBER_CN) {
+ if (mVberCn == null) {
throw new IllegalStateException();
}
- return (int) mValue;
+ return mVberCn;
}
- /** CN value by LBER measured by 0.001 dB. */
+ /**
+ * Gets CN value by LBER in thousandths of a deciBel (0.001dB).
+ */
public int getLberCn() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_LBER_CN) {
+ if (mLberCn == null) {
throw new IllegalStateException();
}
- return (int) mValue;
+ return mLberCn;
}
- /** CN value by XER measured by 0.001 dB. */
+ /**
+ * Gets CN value by XER in thousandths of a deciBel (0.001dB).
+ */
public int getXerCn() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_XER_CN) {
+ if (mXerCn == null) {
throw new IllegalStateException();
}
- return (int) mValue;
+ return mXerCn;
}
- /** MER value measured by 0.001 dB. */
+ /**
+ * Gets Modulation Error Ratio in thousandths of a deciBel (0.001dB).
+ */
public int getMer() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_MER) {
+ if (mMer == null) {
throw new IllegalStateException();
}
- return (int) mValue;
+ return mMer;
}
- /** Frequency difference in Hertz. */
+ /**
+ * Gets frequency difference in Hz.
+ *
+ * <p>Difference between tuning frequency and actual locked frequency.
+ */
public int getFreqOffset() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_FREQ_OFFSET) {
+ if (mFreqOffset == null) {
throw new IllegalStateException();
}
- return (int) mValue;
+ return mFreqOffset;
}
- /** Hierarchy Type for DVBT. */
+ /**
+ * Gets hierarchy Type for DVBT.
+ */
@DvbtFrontendSettings.Hierarchy
public int getHierarchy() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_HIERARCHY) {
+ if (mHierarchy == null) {
throw new IllegalStateException();
}
- return (int) mValue;
+ return mHierarchy;
}
- /** Lock status for RF. */
- public boolean getIsRfLock() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_RF_LOCK) {
+ /**
+ * Gets lock status for RF.
+ */
+ public boolean isRfLock() {
+ if (mIsRfLocked == null) {
throw new IllegalStateException();
}
- return (Boolean) mValue;
+ return mIsRfLocked;
}
- /** A list of PLP status for tuned PLPs for ATSC3 frontend. */
+ /**
+ * Gets an array of PLP status for tuned PLPs for ATSC3 frontend.
+ */
+ @NonNull
public Atsc3PlpInfo[] getAtsc3PlpInfo() {
- if (mType != TunerConstants.FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO) {
+ if (mPlpInfo == null) {
throw new IllegalStateException();
}
- return (Atsc3PlpInfo[]) mValue;
+ return mPlpInfo;
}
- /** Status for each tuning PLPs. */
+ /**
+ * Status for each tuning Physical Layer Pipes.
+ */
public static class Atsc3PlpInfo {
private final int mPlpId;
private final boolean mIsLock;
@@ -238,17 +447,20 @@
mUec = uec;
}
- /** Gets PLP IDs. */
+ /**
+ * Gets Physical Layer Pipe ID.
+ */
public int getPlpId() {
return mPlpId;
}
- /** Gets Demod Lock/Unlock status of this particular PLP. */
- public boolean getIsLock() {
+ /**
+ * Gets Demod Lock/Unlock status of this particular PLP.
+ */
+ public boolean isLock() {
return mIsLock;
}
/**
- * Gets Uncorrectable Error Counts (UEC) of this particular PLP since last tune
- * operation.
+ * Gets Uncorrectable Error Counts (UEC) of this particular PLP since last tune operation.
*/
public int getUec() {
return mUec;
diff --git a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
index 7e6f188..a83d771 100644
--- a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
@@ -87,47 +87,47 @@
*/
public static final int CODERATE_AUTO = Constants.FrontendIsdbs3Coderate.AUTO;
/**
- * 1_3 code rate.
+ * 1/3 code rate.
*/
public static final int CODERATE_1_3 = Constants.FrontendIsdbs3Coderate.CODERATE_1_3;
/**
- * 2_5 code rate.
+ * 2/5 code rate.
*/
public static final int CODERATE_2_5 = Constants.FrontendIsdbs3Coderate.CODERATE_2_5;
/**
- * 1_2 code rate.
+ * 1/2 code rate.
*/
public static final int CODERATE_1_2 = Constants.FrontendIsdbs3Coderate.CODERATE_1_2;
/**
- * 3_5 code rate.
+ * 3/5 code rate.
*/
public static final int CODERATE_3_5 = Constants.FrontendIsdbs3Coderate.CODERATE_3_5;
/**
- * 2_3 code rate.
+ * 2/3 code rate.
*/
public static final int CODERATE_2_3 = Constants.FrontendIsdbs3Coderate.CODERATE_2_3;
/**
- * 3_4 code rate.
+ * 3/4 code rate.
*/
public static final int CODERATE_3_4 = Constants.FrontendIsdbs3Coderate.CODERATE_3_4;
/**
- * 7_9 code rate.
+ * 7/9 code rate.
*/
public static final int CODERATE_7_9 = Constants.FrontendIsdbs3Coderate.CODERATE_7_9;
/**
- * 4_5 code rate.
+ * 4/5 code rate.
*/
public static final int CODERATE_4_5 = Constants.FrontendIsdbs3Coderate.CODERATE_4_5;
/**
- * 5_6 code rate.
+ * 5/6 code rate.
*/
public static final int CODERATE_5_6 = Constants.FrontendIsdbs3Coderate.CODERATE_5_6;
/**
- * 7_8 code rate.
+ * 7/8 code rate.
*/
public static final int CODERATE_7_8 = Constants.FrontendIsdbs3Coderate.CODERATE_7_8;
/**
- * 9_10 code rate.
+ * 9/10 code rate.
*/
public static final int CODERATE_9_10 = Constants.FrontendIsdbs3Coderate.CODERATE_9_10;
@@ -138,11 +138,11 @@
public @interface Rolloff {}
/**
- * Roll off type undefined.
+ * Rolloff type undefined.
*/
public static final int ROLLOFF_UNDEFINED = Constants.FrontendIsdbs3Rolloff.UNDEFINED;
/**
- * 0.03 roll off type.
+ * 0,03 Rolloff.
*/
public static final int ROLLOFF_0_03 = Constants.FrontendIsdbs3Rolloff.ROLLOFF_0_03;
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
index fe100f8..bb809bf 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
@@ -96,23 +96,23 @@
*/
public static final int CODERATE_AUTO = Constants.FrontendIsdbsCoderate.AUTO;
/**
- * 1_2 code rate.
+ * 1/2 code rate.
*/
public static final int CODERATE_1_2 = Constants.FrontendIsdbsCoderate.CODERATE_1_2;
/**
- * 2_3 code rate.
+ * 2/3 code rate.
*/
public static final int CODERATE_2_3 = Constants.FrontendIsdbsCoderate.CODERATE_2_3;
/**
- * 3_4 code rate.
+ * 3/4 code rate.
*/
public static final int CODERATE_3_4 = Constants.FrontendIsdbsCoderate.CODERATE_3_4;
/**
- * 5_6 code rate.
+ * 5/6 code rate.
*/
public static final int CODERATE_5_6 = Constants.FrontendIsdbsCoderate.CODERATE_5_6;
/**
- * 7_8 code rate.
+ * 7/8 code rate.
*/
public static final int CODERATE_7_8 = Constants.FrontendIsdbsCoderate.CODERATE_7_8;
@@ -123,11 +123,11 @@
public @interface Rolloff {}
/**
- * Roll off type undefined.
+ * Rolloff type undefined.
*/
public static final int ROLLOFF_UNDEFINED = Constants.FrontendIsdbs3Rolloff.UNDEFINED;
/**
- * 0.35 roll off type.
+ * 0,35 rolloff.
*/
public static final int ROLLOFF_0_35 = Constants.FrontendIsdbsRolloff.ROLLOFF_0_35;
diff --git a/media/java/android/media/tv/tuner/frontend/ScanCallback.java b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
index 5e7d218..0479e55 100644
--- a/media/java/android/media/tv/tuner/frontend/ScanCallback.java
+++ b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
@@ -16,6 +16,7 @@
package android.media.tv.tuner.frontend;
+
/**
* Scan callback.
*
@@ -31,11 +32,11 @@
/** scan progress percent (0..100) */
void onProgress(int percent);
- /** Signal frequency in Hertz */
- void onFrequencyReport(int frequency);
+ /** Signal frequencies in Hertz */
+ void onFrequenciesReport(int[] frequency);
/** Symbols per second */
- void onSymbolRate(int rate);
+ void onSymbolRates(int[] rate);
/** Locked Plp Ids for DVBT2 frontend. */
void onPlpIds(int[] plpIds);
@@ -46,15 +47,24 @@
/** Stream Ids. */
void onInputStreamIds(int[] inputStreamIds);
- /** Locked signal standard. */
+ /** Locked signal standard for DVBS. */
void onDvbsStandard(@DvbsFrontendSettings.Standard int dvbsStandandard);
- /** Locked signal standard. */
+ /** Locked signal standard. for DVBT */
void onDvbtStandard(@DvbtFrontendSettings.Standard int dvbtStandard);
+ /** Locked signal SIF standard for Analog. */
+ void onAnalogSifStandard(@AnalogFrontendSettings.SifStandard int sif);
+
/** PLP status in a tuned frequency band for ATSC3 frontend. */
void onAtsc3PlpInfos(Atsc3PlpInfo[] atsc3PlpInfos);
+ /** Frontend hierarchy. */
+ void onHierarchy(@DvbtFrontendSettings.Hierarchy int hierarchy);
+
+ /** Frontend hierarchy. */
+ void onSignalType(@AnalogFrontendSettings.SignalType int signalType);
+
/** PLP information for ATSC3. */
class Atsc3PlpInfo {
private final int mPlpId;
diff --git a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
index ed93112..cc2d1b1 100644
--- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
@@ -167,8 +167,7 @@
}
@Override
- public void onCreateSession(String packageName, String routeId, String routeFeature,
- long requestId) {
+ public void onCreateSession(String packageName, String routeId, long requestId) {
MediaRoute2Info route = mRoutes.get(routeId);
if (route == null || TextUtils.equals(ROUTE_ID3_SESSION_CREATION_FAILED, routeId)) {
// Tell the router that session cannot be created by passing null as sessionInfo.
@@ -185,8 +184,7 @@
.build());
mRouteIdToSessionId.put(routeId, sessionId);
- RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
- sessionId, packageName, routeFeature)
+ RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(sessionId, packageName)
.addSelectedRoute(routeId)
.addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
.addTransferrableRoute(ROUTE_ID5_TO_TRANSFER_TO)
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRoute2InfoTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRoute2InfoTest.java
new file mode 100644
index 0000000..c46966f
--- /dev/null
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRoute2InfoTest.java
@@ -0,0 +1,363 @@
+/*
+ * 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 com.android.mediaroutertest;
+
+
+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.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.testng.Assert.assertThrows;
+
+import android.media.MediaRoute2Info;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests {@link MediaRoute2Info} and its {@link MediaRoute2Info.Builder builder}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MediaRoute2InfoTest {
+
+ public static final String TEST_ID = "test_id";
+ public static final String TEST_NAME = "test_name";
+ public static final String TEST_ROUTE_TYPE_0 = "test_route_type_0";
+ public static final String TEST_ROUTE_TYPE_1 = "test_route_type_1";
+ public static final int TEST_DEVICE_TYPE = MediaRoute2Info.DEVICE_TYPE_REMOTE_SPEAKER;
+ public static final Uri TEST_ICON_URI = Uri.parse("https://developer.android.com");
+ public static final String TEST_DESCRIPTION = "test_description";
+ public static final int TEST_CONNECTION_STATE = MediaRoute2Info.CONNECTION_STATE_CONNECTING;
+ public static final String TEST_CLIENT_PACKAGE_NAME = "com.test.client.package.name";
+ public static final int TEST_VOLUME_HANDLING = MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
+ public static final int TEST_VOLUME_MAX = 100;
+ public static final int TEST_VOLUME = 65;
+
+ public static final String TEST_KEY = "test_key";
+ public static final String TEST_VALUE = "test_value";
+
+ @Test
+ public void testBuilderConstructorWithInvalidValues() {
+ final String nullId = null;
+ final String nullName = null;
+ final String emptyId = "";
+ final String emptyName = "";
+ final String validId = "valid_id";
+ final String validName = "valid_name";
+
+ // ID is invalid
+ assertThrows(IllegalArgumentException.class,
+ () -> new MediaRoute2Info.Builder(nullId, validName));
+ assertThrows(IllegalArgumentException.class,
+ () -> new MediaRoute2Info.Builder(emptyId, validName));
+
+ // name is invalid
+ assertThrows(IllegalArgumentException.class,
+ () -> new MediaRoute2Info.Builder(validId, nullName));
+ assertThrows(IllegalArgumentException.class,
+ () -> new MediaRoute2Info.Builder(validId, emptyName));
+
+ // Both are invalid
+ assertThrows(IllegalArgumentException.class,
+ () -> new MediaRoute2Info.Builder(nullId, nullName));
+ assertThrows(IllegalArgumentException.class,
+ () -> new MediaRoute2Info.Builder(nullId, emptyName));
+ assertThrows(IllegalArgumentException.class,
+ () -> new MediaRoute2Info.Builder(emptyId, nullName));
+ assertThrows(IllegalArgumentException.class,
+ () -> new MediaRoute2Info.Builder(emptyId, emptyName));
+
+
+ // Null RouteInfo (1-argument constructor)
+ final MediaRoute2Info nullRouteInfo = null;
+ assertThrows(NullPointerException.class,
+ () -> new MediaRoute2Info.Builder(nullRouteInfo));
+ }
+
+ @Test
+ public void testBuilderBuildWithEmptyRouteTypesShouldThrowIAE() {
+ MediaRoute2Info.Builder builder = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME);
+ assertThrows(IllegalArgumentException.class, () -> builder.build());
+ }
+
+ @Test
+ public void testBuilderAndGettersOfMediaRoute2Info() {
+ Bundle extras = new Bundle();
+ extras.putString(TEST_KEY, TEST_VALUE);
+
+ MediaRoute2Info routeInfo = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+ .addFeature(TEST_ROUTE_TYPE_0)
+ .addFeature(TEST_ROUTE_TYPE_1)
+ .setDeviceType(TEST_DEVICE_TYPE)
+ .setIconUri(TEST_ICON_URI)
+ .setDescription(TEST_DESCRIPTION)
+ .setConnectionState(TEST_CONNECTION_STATE)
+ .setClientPackageName(TEST_CLIENT_PACKAGE_NAME)
+ .setVolumeHandling(TEST_VOLUME_HANDLING)
+ .setVolumeMax(TEST_VOLUME_MAX)
+ .setVolume(TEST_VOLUME)
+ .setExtras(extras)
+ .build();
+
+ assertEquals(TEST_ID, routeInfo.getId());
+ assertEquals(TEST_NAME, routeInfo.getName());
+
+ assertEquals(2, routeInfo.getFeatures().size());
+ assertEquals(TEST_ROUTE_TYPE_0, routeInfo.getFeatures().get(0));
+ assertEquals(TEST_ROUTE_TYPE_1, routeInfo.getFeatures().get(1));
+
+ assertEquals(TEST_DEVICE_TYPE, routeInfo.getDeviceType());
+ assertEquals(TEST_ICON_URI, routeInfo.getIconUri());
+ assertEquals(TEST_DESCRIPTION, routeInfo.getDescription());
+ assertEquals(TEST_CONNECTION_STATE, routeInfo.getConnectionState());
+ assertEquals(TEST_CLIENT_PACKAGE_NAME, routeInfo.getClientPackageName());
+ assertEquals(TEST_VOLUME_HANDLING, routeInfo.getVolumeHandling());
+ assertEquals(TEST_VOLUME_MAX, routeInfo.getVolumeMax());
+ assertEquals(TEST_VOLUME, routeInfo.getVolume());
+
+ Bundle extrasOut = routeInfo.getExtras();
+ assertNotNull(extrasOut);
+ assertTrue(extrasOut.containsKey(TEST_KEY));
+ assertEquals(TEST_VALUE, extrasOut.getString(TEST_KEY));
+ }
+
+ @Test
+ public void testBuilderSetExtrasWithNull() {
+ MediaRoute2Info routeInfo = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+ .addFeature(TEST_ROUTE_TYPE_0)
+ .setExtras(null)
+ .build();
+
+ assertNull(routeInfo.getExtras());
+ }
+
+ @Test
+ public void testBuilderaddFeatures() {
+ List<String> routeTypes = new ArrayList<>();
+ routeTypes.add(TEST_ROUTE_TYPE_0);
+ routeTypes.add(TEST_ROUTE_TYPE_1);
+
+ MediaRoute2Info routeInfo = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+ .addFeatures(routeTypes)
+ .build();
+
+ assertEquals(routeTypes, routeInfo.getFeatures());
+ }
+
+ @Test
+ public void testBuilderclearFeatures() {
+ MediaRoute2Info routeInfo = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+ .addFeature(TEST_ROUTE_TYPE_0)
+ .addFeature(TEST_ROUTE_TYPE_1)
+ // clearFeatures should clear the route types.
+ .clearFeatures()
+ .addFeature(TEST_ROUTE_TYPE_1)
+ .build();
+
+ assertEquals(1, routeInfo.getFeatures().size());
+ assertEquals(TEST_ROUTE_TYPE_1, routeInfo.getFeatures().get(0));
+ }
+
+ @Test
+ public void testhasAnyFeaturesReturnsFalse() {
+ MediaRoute2Info routeInfo = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+ .addFeature(TEST_ROUTE_TYPE_0)
+ .addFeature(TEST_ROUTE_TYPE_1)
+ .build();
+
+ List<String> testRouteTypes = new ArrayList<>();
+ testRouteTypes.add("non_matching_route_type_1");
+ testRouteTypes.add("non_matching_route_type_2");
+ testRouteTypes.add("non_matching_route_type_3");
+ testRouteTypes.add("non_matching_route_type_4");
+
+ assertFalse(routeInfo.hasAnyFeatures(testRouteTypes));
+ }
+
+ @Test
+ public void testhasAnyFeaturesReturnsTrue() {
+ MediaRoute2Info routeInfo = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+ .addFeature(TEST_ROUTE_TYPE_0)
+ .addFeature(TEST_ROUTE_TYPE_1)
+ .build();
+
+ List<String> testRouteTypes = new ArrayList<>();
+ testRouteTypes.add("non_matching_route_type_1");
+ testRouteTypes.add("non_matching_route_type_2");
+ testRouteTypes.add("non_matching_route_type_3");
+ testRouteTypes.add(TEST_ROUTE_TYPE_1);
+
+ assertTrue(routeInfo.hasAnyFeatures(testRouteTypes));
+ }
+
+ @Test
+ public void testEqualsCreatedWithSameArguments() {
+ Bundle extras = new Bundle();
+ extras.putString(TEST_KEY, TEST_VALUE);
+
+ MediaRoute2Info routeInfo1 = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+ .addFeature(TEST_ROUTE_TYPE_0)
+ .addFeature(TEST_ROUTE_TYPE_1)
+ .setDeviceType(TEST_DEVICE_TYPE)
+ .setIconUri(TEST_ICON_URI)
+ .setDescription(TEST_DESCRIPTION)
+ .setConnectionState(TEST_CONNECTION_STATE)
+ .setClientPackageName(TEST_CLIENT_PACKAGE_NAME)
+ .setVolumeHandling(TEST_VOLUME_HANDLING)
+ .setVolumeMax(TEST_VOLUME_MAX)
+ .setVolume(TEST_VOLUME)
+ .setExtras(extras)
+ .build();
+
+ MediaRoute2Info routeInfo2 = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+ .addFeature(TEST_ROUTE_TYPE_0)
+ .addFeature(TEST_ROUTE_TYPE_1)
+ .setDeviceType(TEST_DEVICE_TYPE)
+ .setIconUri(TEST_ICON_URI)
+ .setDescription(TEST_DESCRIPTION)
+ .setConnectionState(TEST_CONNECTION_STATE)
+ .setClientPackageName(TEST_CLIENT_PACKAGE_NAME)
+ .setVolumeHandling(TEST_VOLUME_HANDLING)
+ .setVolumeMax(TEST_VOLUME_MAX)
+ .setVolume(TEST_VOLUME)
+ .setExtras(extras)
+ .build();
+
+ assertEquals(routeInfo1, routeInfo2);
+ assertEquals(routeInfo1.hashCode(), routeInfo2.hashCode());
+ }
+
+ @Test
+ public void testEqualsCreatedWithBuilderCopyConstructor() {
+ Bundle extras = new Bundle();
+ extras.putString(TEST_KEY, TEST_VALUE);
+
+ MediaRoute2Info routeInfo1 = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+ .addFeature(TEST_ROUTE_TYPE_0)
+ .addFeature(TEST_ROUTE_TYPE_1)
+ .setDeviceType(TEST_DEVICE_TYPE)
+ .setIconUri(TEST_ICON_URI)
+ .setDescription(TEST_DESCRIPTION)
+ .setConnectionState(TEST_CONNECTION_STATE)
+ .setClientPackageName(TEST_CLIENT_PACKAGE_NAME)
+ .setVolumeHandling(TEST_VOLUME_HANDLING)
+ .setVolumeMax(TEST_VOLUME_MAX)
+ .setVolume(TEST_VOLUME)
+ .setExtras(extras)
+ .build();
+
+ MediaRoute2Info routeInfo2 = new MediaRoute2Info.Builder(routeInfo1).build();
+
+ assertEquals(routeInfo1, routeInfo2);
+ assertEquals(routeInfo1.hashCode(), routeInfo2.hashCode());
+ }
+
+ @Test
+ public void testEqualsReturnFalse() {
+ Bundle extras = new Bundle();
+ extras.putString(TEST_KEY, TEST_VALUE);
+
+ MediaRoute2Info routeInfo = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+ .addFeature(TEST_ROUTE_TYPE_0)
+ .addFeature(TEST_ROUTE_TYPE_1)
+ .setDeviceType(TEST_DEVICE_TYPE)
+ .setIconUri(TEST_ICON_URI)
+ .setDescription(TEST_DESCRIPTION)
+ .setConnectionState(TEST_CONNECTION_STATE)
+ .setClientPackageName(TEST_CLIENT_PACKAGE_NAME)
+ .setVolumeHandling(TEST_VOLUME_HANDLING)
+ .setVolumeMax(TEST_VOLUME_MAX)
+ .setVolume(TEST_VOLUME)
+ .setExtras(extras)
+ .build();
+
+ // Now, we will use copy constructor
+ assertNotEquals(routeInfo, new MediaRoute2Info.Builder(routeInfo)
+ .addFeature("randomRouteType")
+ .build());
+ assertNotEquals(routeInfo, new MediaRoute2Info.Builder(routeInfo)
+ .setDeviceType(TEST_DEVICE_TYPE + 1)
+ .build());
+ assertNotEquals(routeInfo, new MediaRoute2Info.Builder(routeInfo)
+ .setIconUri(Uri.parse("randomUri"))
+ .build());
+ assertNotEquals(routeInfo, new MediaRoute2Info.Builder(routeInfo)
+ .setDescription("randomDescription")
+ .build());
+ assertNotEquals(routeInfo, new MediaRoute2Info.Builder(routeInfo)
+ .setConnectionState(TEST_CONNECTION_STATE + 1)
+ .build());
+ assertNotEquals(routeInfo, new MediaRoute2Info.Builder(routeInfo)
+ .setClientPackageName("randomPackageName")
+ .build());
+ assertNotEquals(routeInfo, new MediaRoute2Info.Builder(routeInfo)
+ .setVolumeHandling(TEST_VOLUME_HANDLING + 1)
+ .build());
+ assertNotEquals(routeInfo, new MediaRoute2Info.Builder(routeInfo)
+ .setVolumeMax(TEST_VOLUME_MAX + 100)
+ .build());
+ assertNotEquals(routeInfo, new MediaRoute2Info.Builder(routeInfo)
+ .setVolume(TEST_VOLUME + 10)
+ .build());
+ // Note: Extras will not affect the equals.
+ }
+
+ @Test
+ public void testParcelingAndUnParceling() {
+ Bundle extras = new Bundle();
+ extras.putString(TEST_KEY, TEST_VALUE);
+
+ MediaRoute2Info routeInfo = new MediaRoute2Info.Builder(TEST_ID, TEST_NAME)
+ .addFeature(TEST_ROUTE_TYPE_0)
+ .addFeature(TEST_ROUTE_TYPE_1)
+ .setDeviceType(TEST_DEVICE_TYPE)
+ .setIconUri(TEST_ICON_URI)
+ .setDescription(TEST_DESCRIPTION)
+ .setConnectionState(TEST_CONNECTION_STATE)
+ .setClientPackageName(TEST_CLIENT_PACKAGE_NAME)
+ .setVolumeHandling(TEST_VOLUME_HANDLING)
+ .setVolumeMax(TEST_VOLUME_MAX)
+ .setVolume(TEST_VOLUME)
+ .setExtras(extras)
+ .build();
+
+ Parcel parcel = Parcel.obtain();
+ routeInfo.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ MediaRoute2Info routeInfoFromParcel = MediaRoute2Info.CREATOR.createFromParcel(parcel);
+ assertEquals(routeInfo, routeInfoFromParcel);
+ assertEquals(routeInfo.hashCode(), routeInfoFromParcel.hashCode());
+
+ // Check extras
+ Bundle extrasOut = routeInfoFromParcel.getExtras();
+ assertNotNull(extrasOut);
+ assertTrue(extrasOut.containsKey(TEST_KEY));
+ assertEquals(TEST_VALUE, extrasOut.getString(TEST_KEY));
+ }
+}
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
index 007229a..e782aae 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
@@ -16,17 +16,13 @@
package com.android.mediaroutertest;
-import static android.media.MediaRoute2Info.CONNECTION_STATE_CONNECTED;
import static android.media.MediaRoute2Info.CONNECTION_STATE_CONNECTING;
import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_SPEAKER;
-import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_TV;
-import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
import static com.android.mediaroutertest.MediaRouterManagerTest.FEATURES_ALL;
import static com.android.mediaroutertest.MediaRouterManagerTest.FEATURES_SPECIAL;
import static com.android.mediaroutertest.MediaRouterManagerTest.FEATURE_SAMPLE;
-import static com.android.mediaroutertest.MediaRouterManagerTest.FEATURE_SPECIAL;
import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID1;
import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID2;
import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID3_SESSION_CREATION_FAILED;
@@ -132,57 +128,6 @@
}
@Test
- public void testRouteInfoInequality() {
- MediaRoute2Info route = new MediaRoute2Info.Builder("id", "name")
- .setDescription("description")
- .setClientPackageName("com.android.mediaroutertest")
- .setConnectionState(CONNECTION_STATE_CONNECTING)
- .setIconUri(new Uri.Builder().path("icon").build())
- .addFeature(FEATURE_SAMPLE)
- .setVolume(5)
- .setVolumeMax(20)
- .setVolumeHandling(PLAYBACK_VOLUME_VARIABLE)
- .setDeviceType(DEVICE_TYPE_REMOTE_SPEAKER)
- .build();
-
- MediaRoute2Info routeDescription = new MediaRoute2Info.Builder(route)
- .setDescription("another description").build();
- assertNotEquals(route, routeDescription);
-
- MediaRoute2Info routeConnectionState = new MediaRoute2Info.Builder(route)
- .setConnectionState(CONNECTION_STATE_CONNECTED).build();
- assertNotEquals(route, routeConnectionState);
-
- MediaRoute2Info routeIcon = new MediaRoute2Info.Builder(route)
- .setIconUri(new Uri.Builder().path("new icon").build()).build();
- assertNotEquals(route, routeIcon);
-
- MediaRoute2Info routeClient = new MediaRoute2Info.Builder(route)
- .setClientPackageName("another.client.package").build();
- assertNotEquals(route, routeClient);
-
- MediaRoute2Info routeType = new MediaRoute2Info.Builder(route)
- .addFeature(FEATURE_SPECIAL).build();
- assertNotEquals(route, routeType);
-
- MediaRoute2Info routeVolume = new MediaRoute2Info.Builder(route)
- .setVolume(10).build();
- assertNotEquals(route, routeVolume);
-
- MediaRoute2Info routeVolumeMax = new MediaRoute2Info.Builder(route)
- .setVolumeMax(30).build();
- assertNotEquals(route, routeVolumeMax);
-
- MediaRoute2Info routeVolumeHandling = new MediaRoute2Info.Builder(route)
- .setVolumeHandling(PLAYBACK_VOLUME_FIXED).build();
- assertNotEquals(route, routeVolumeHandling);
-
- MediaRoute2Info routeDeviceType = new MediaRoute2Info.Builder(route)
- .setVolume(DEVICE_TYPE_REMOTE_TV).build();
- assertNotEquals(route, routeDeviceType);
- }
-
- @Test
public void testControlVolumeWithRouter() throws Exception {
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(FEATURES_ALL);
@@ -227,19 +172,8 @@
}
@Test
- public void testRequestCreateSessionWithInvalidArguments() {
- MediaRoute2Info route = new MediaRoute2Info.Builder("id", "name").build();
- String routeFeature = "routeFeature";
-
- // Tests null route
- assertThrows(NullPointerException.class,
- () -> mRouter2.requestCreateSession(null, routeFeature));
-
- // Tests null or empty route feature
- assertThrows(IllegalArgumentException.class,
- () -> mRouter2.requestCreateSession(route, null));
- assertThrows(IllegalArgumentException.class,
- () -> mRouter2.requestCreateSession(route, ""));
+ public void testRequestCreateSessionWithNullRoute() {
+ assertThrows(NullPointerException.class, () -> mRouter2.requestCreateSession(null));
}
@Test
@@ -261,14 +195,12 @@
public void onSessionCreated(RoutingController controller) {
assertNotNull(controller);
assertTrue(createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1));
- assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature()));
controllers.add(controller);
successLatch.countDown();
}
@Override
- public void onSessionCreationFailed(MediaRoute2Info requestedRoute,
- String requestedRouteFeature) {
+ public void onSessionCreationFailed(MediaRoute2Info requestedRoute) {
failureLatch.countDown();
}
};
@@ -279,7 +211,7 @@
try {
mRouter2.registerSessionCallback(mExecutor, sessionCallback);
- mRouter2.requestCreateSession(route, FEATURE_SAMPLE);
+ mRouter2.requestCreateSession(route);
assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// onSessionCreationFailed should not be called.
@@ -313,10 +245,8 @@
}
@Override
- public void onSessionCreationFailed(MediaRoute2Info requestedRoute,
- String requestedRouteFeature) {
+ public void onSessionCreationFailed(MediaRoute2Info requestedRoute) {
assertEquals(route, requestedRoute);
- assertTrue(TextUtils.equals(FEATURE_SAMPLE, requestedRouteFeature));
failureLatch.countDown();
}
};
@@ -327,7 +257,7 @@
try {
mRouter2.registerSessionCallback(mExecutor, sessionCallback);
- mRouter2.requestCreateSession(route, FEATURE_SAMPLE);
+ mRouter2.requestCreateSession(route);
assertTrue(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// onSessionCreated should not be called.
@@ -357,8 +287,7 @@
}
@Override
- public void onSessionCreationFailed(MediaRoute2Info requestedRoute,
- String requestedRouteFeature) {
+ public void onSessionCreationFailed(MediaRoute2Info requestedRoute) {
failureLatch.countDown();
}
};
@@ -375,8 +304,8 @@
try {
mRouter2.registerSessionCallback(mExecutor, sessionCallback);
- mRouter2.requestCreateSession(route1, FEATURE_SAMPLE);
- mRouter2.requestCreateSession(route2, FEATURE_SAMPLE);
+ mRouter2.requestCreateSession(route1);
+ mRouter2.requestCreateSession(route2);
assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// onSessionCreationFailed should not be called.
@@ -390,8 +319,6 @@
assertNotEquals(controller1.getSessionId(), controller2.getSessionId());
assertTrue(createRouteMap(controller1.getSelectedRoutes()).containsKey(ROUTE_ID1));
assertTrue(createRouteMap(controller2.getSelectedRoutes()).containsKey(ROUTE_ID2));
- assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller1.getRouteFeature()));
- assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller2.getRouteFeature()));
} finally {
releaseControllers(createdControllers);
@@ -422,8 +349,7 @@
}
@Override
- public void onSessionCreationFailed(MediaRoute2Info requestedRoute,
- String requestedRouteFeature) {
+ public void onSessionCreationFailed(MediaRoute2Info requestedRoute) {
failureLatch.countDown();
}
};
@@ -434,7 +360,7 @@
try {
mRouter2.registerSessionCallback(mExecutor, sessionCallback);
- mRouter2.requestCreateSession(route, FEATURE_SAMPLE);
+ mRouter2.requestCreateSession(route);
// Unregisters session callback
mRouter2.unregisterSessionCallback(sessionCallback);
@@ -470,7 +396,6 @@
public void onSessionCreated(RoutingController controller) {
assertNotNull(controller);
assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
- assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature()));
controllers.add(controller);
onSessionCreatedLatch.countDown();
}
@@ -523,7 +448,7 @@
try {
mRouter2.registerSessionCallback(mExecutor, sessionCallback);
- mRouter2.requestCreateSession(routeToCreateSessionWith, FEATURE_SAMPLE);
+ mRouter2.requestCreateSession(routeToCreateSessionWith);
assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals(1, controllers.size());
@@ -568,7 +493,6 @@
public void onSessionCreated(RoutingController controller) {
assertNotNull(controller);
assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
- assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature()));
controllers.add(controller);
onSessionCreatedLatch.countDown();
}
@@ -605,7 +529,7 @@
try {
mRouter2.registerSessionCallback(mExecutor, sessionCallback);
- mRouter2.requestCreateSession(routeToCreateSessionWith, FEATURE_SAMPLE);
+ mRouter2.requestCreateSession(routeToCreateSessionWith);
assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals(1, controllers.size());
@@ -647,7 +571,6 @@
public void onSessionCreated(RoutingController controller) {
assertNotNull(controller);
assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
- assertTrue(TextUtils.equals(FEATURE_SAMPLE, controller.getRouteFeature()));
controllers.add(controller);
onSessionCreatedLatch.countDown();
}
@@ -670,7 +593,7 @@
try {
mRouter2.registerSessionCallback(mExecutor, sessionCallback);
- mRouter2.requestCreateSession(routeToCreateSessionWith, FEATURE_SAMPLE);
+ mRouter2.requestCreateSession(routeToCreateSessionWith);
assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals(1, controllers.size());
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index ae15b91..83dd0c0 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -20,7 +20,6 @@
import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -143,20 +142,6 @@
clearCallbacks();
}
- //TODO: Move to a separate file
- @Test
- public void testMediaRoute2Info() {
- MediaRoute2Info routeInfo1 = new MediaRoute2Info.Builder("id", "name")
- .build();
- MediaRoute2Info routeInfo2 = new MediaRoute2Info.Builder(routeInfo1).build();
-
- MediaRoute2Info routeInfo3 = new MediaRoute2Info.Builder(routeInfo1)
- .setClientPackageName(mPackageName).build();
-
- assertEquals(routeInfo1, routeInfo2);
- assertNotEquals(routeInfo1, routeInfo3);
- }
-
/**
* Tests if routes are added correctly when a new callback is registered.
*/
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java
index 3f59736..704dca0 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/RoutingSessionInfoTest.java
@@ -57,65 +57,32 @@
public void testBuilderConstructorWithInvalidValues() {
final String nullId = null;
final String nullClientPackageName = null;
- final String nullRouteFeature = null;
final String emptyId = "";
// Note: An empty string as client package name is valid.
- final String emptyRouteFeature = "";
final String validId = TEST_ID;
final String validClientPackageName = TEST_CLIENT_PACKAGE_NAME;
- final String validRouteFeature = TEST_ROUTE_FEATURE;
// ID is invalid
assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
- nullId, validClientPackageName, validRouteFeature));
+ nullId, validClientPackageName));
assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
- emptyId, validClientPackageName, validRouteFeature));
+ emptyId, validClientPackageName));
// client package name is invalid (null)
assertThrows(NullPointerException.class, () -> new RoutingSessionInfo.Builder(
- validId, nullClientPackageName, validRouteFeature));
+ validId, nullClientPackageName));
- // route feature is invalid
+ // Both are invalid
assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
- validId, validClientPackageName, nullRouteFeature));
+ nullId, nullClientPackageName));
assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
- validId, validClientPackageName, emptyRouteFeature));
+ emptyId, nullClientPackageName));
+ }
- // Two arguments are invalid - (1) ID and clientPackageName
- assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
- nullId, nullClientPackageName, validRouteFeature));
- assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
- emptyId, nullClientPackageName, validRouteFeature));
-
- // Two arguments are invalid - (2) ID and routeFeature
- assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
- nullId, validClientPackageName, nullRouteFeature));
- assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
- nullId, validClientPackageName, emptyRouteFeature));
- assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
- emptyId, validClientPackageName, nullRouteFeature));
- assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
- emptyId, validClientPackageName, emptyRouteFeature));
-
- // Two arguments are invalid - (3) clientPackageName and routeFeature
- // Note that this throws NullPointerException.
- assertThrows(NullPointerException.class, () -> new RoutingSessionInfo.Builder(
- validId, nullClientPackageName, nullRouteFeature));
- assertThrows(NullPointerException.class, () -> new RoutingSessionInfo.Builder(
- validId, nullClientPackageName, emptyRouteFeature));
-
- // All arguments are invalid
- assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
- nullId, nullClientPackageName, nullRouteFeature));
- assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
- nullId, nullClientPackageName, emptyRouteFeature));
- assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
- emptyId, nullClientPackageName, nullRouteFeature));
- assertThrows(IllegalArgumentException.class, () -> new RoutingSessionInfo.Builder(
- emptyId, nullClientPackageName, emptyRouteFeature));
-
+ @Test
+ public void testBuilderCopyConstructorWithNull() {
// Null RouteInfo (1-argument constructor)
final RoutingSessionInfo nullRoutingSessionInfo = null;
assertThrows(NullPointerException.class,
@@ -127,13 +94,13 @@
// An empty string for client package name is valid. (for unknown cases)
// Creating builder with it should not throw any exception.
RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
- TEST_ID, "" /* clientPackageName*/, TEST_ROUTE_FEATURE);
+ TEST_ID, "" /* clientPackageName*/);
}
@Test
public void testBuilderBuildWithEmptySelectedRoutesThrowsIAE() {
RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
- TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE);
+ TEST_ID, TEST_CLIENT_PACKAGE_NAME);
// Note: Calling build() without adding any selected routes.
assertThrows(IllegalArgumentException.class, () -> builder.build());
}
@@ -141,7 +108,7 @@
@Test
public void testBuilderAddRouteMethodsWithIllegalArgumentsThrowsIAE() {
RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
- TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE);
+ TEST_ID, TEST_CLIENT_PACKAGE_NAME);
final String nullRouteId = null;
final String emptyRouteId = "";
@@ -168,7 +135,7 @@
@Test
public void testBuilderRemoveRouteMethodsWithIllegalArgumentsThrowsIAE() {
RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
- TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE);
+ TEST_ID, TEST_CLIENT_PACKAGE_NAME);
final String nullRouteId = null;
final String emptyRouteId = "";
@@ -198,7 +165,7 @@
controlHints.putString(TEST_KEY, TEST_VALUE);
RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
- TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+ TEST_ID, TEST_CLIENT_PACKAGE_NAME)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectedRoute(TEST_ROUTE_ID_1)
.addSelectableRoute(TEST_ROUTE_ID_2)
@@ -212,7 +179,6 @@
assertEquals(TEST_ID, sessionInfo.getId());
assertEquals(TEST_CLIENT_PACKAGE_NAME, sessionInfo.getClientPackageName());
- assertEquals(TEST_ROUTE_FEATURE, sessionInfo.getRouteFeature());
assertEquals(2, sessionInfo.getSelectedRoutes().size());
assertEquals(TEST_ROUTE_ID_0, sessionInfo.getSelectedRoutes().get(0));
@@ -239,7 +205,7 @@
@Test
public void testBuilderAddRouteMethodsWithBuilderCopyConstructor() {
RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
- TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+ TEST_ID, TEST_CLIENT_PACKAGE_NAME)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectableRoute(TEST_ROUTE_ID_2)
.addDeselectableRoute(TEST_ROUTE_ID_4)
@@ -273,7 +239,7 @@
@Test
public void testBuilderRemoveRouteMethods() {
RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
- TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+ TEST_ID, TEST_CLIENT_PACKAGE_NAME)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectedRoute(TEST_ROUTE_ID_1)
.removeSelectedRoute(TEST_ROUTE_ID_1)
@@ -308,7 +274,7 @@
@Test
public void testBuilderRemoveRouteMethodsWithBuilderCopyConstructor() {
RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
- TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+ TEST_ID, TEST_CLIENT_PACKAGE_NAME)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectedRoute(TEST_ROUTE_ID_1)
.addSelectableRoute(TEST_ROUTE_ID_2)
@@ -342,7 +308,7 @@
@Test
public void testBuilderClearRouteMethods() {
RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
- TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+ TEST_ID, TEST_CLIENT_PACKAGE_NAME)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectedRoute(TEST_ROUTE_ID_1)
.clearSelectedRoutes()
@@ -374,7 +340,7 @@
@Test
public void testBuilderClearRouteMethodsWithBuilderCopyConstructor() {
RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
- TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+ TEST_ID, TEST_CLIENT_PACKAGE_NAME)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectedRoute(TEST_ROUTE_ID_1)
.addSelectableRoute(TEST_ROUTE_ID_2)
@@ -408,7 +374,7 @@
controlHints.putString(TEST_KEY, TEST_VALUE);
RoutingSessionInfo sessionInfo1 = new RoutingSessionInfo.Builder(
- TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+ TEST_ID, TEST_CLIENT_PACKAGE_NAME)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectedRoute(TEST_ROUTE_ID_1)
.addSelectableRoute(TEST_ROUTE_ID_2)
@@ -421,7 +387,7 @@
.build();
RoutingSessionInfo sessionInfo2 = new RoutingSessionInfo.Builder(
- TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+ TEST_ID, TEST_CLIENT_PACKAGE_NAME)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectedRoute(TEST_ROUTE_ID_1)
.addSelectableRoute(TEST_ROUTE_ID_2)
@@ -443,7 +409,7 @@
controlHints.putString(TEST_KEY, TEST_VALUE);
RoutingSessionInfo sessionInfo1 = new RoutingSessionInfo.Builder(
- TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+ TEST_ID, TEST_CLIENT_PACKAGE_NAME)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectedRoute(TEST_ROUTE_ID_1)
.addSelectableRoute(TEST_ROUTE_ID_2)
@@ -467,7 +433,7 @@
controlHints.putString(TEST_KEY, TEST_VALUE);
RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
- TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+ TEST_ID, TEST_CLIENT_PACKAGE_NAME)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectedRoute(TEST_ROUTE_ID_1)
.addSelectableRoute(TEST_ROUTE_ID_2)
@@ -530,7 +496,7 @@
controlHints.putString(TEST_KEY, TEST_VALUE);
RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(
- TEST_ID, TEST_CLIENT_PACKAGE_NAME, TEST_ROUTE_FEATURE)
+ TEST_ID, TEST_CLIENT_PACKAGE_NAME)
.addSelectedRoute(TEST_ROUTE_ID_0)
.addSelectedRoute(TEST_ROUTE_ID_1)
.addSelectableRoute(TEST_ROUTE_ID_2)
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 41a9b24..5054281 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -49,7 +49,6 @@
import android.widget.ProgressBar;
import android.widget.TextView;
-import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.TrafficStatsConstants;
@@ -207,7 +206,7 @@
if (TextUtils.isEmpty(url)) url = mCm.getCaptivePortalServerUrl();
final CarrierConfigManager configManager = getApplicationContext()
.getSystemService(CarrierConfigManager.class);
- final int subId = getIntent().getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+ final int subId = getIntent().getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.getDefaultVoiceSubscriptionId());
final String[] portalURLs = configManager.getConfigForSubId(subId).getStringArray(
CarrierConfigManager.KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY);
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index beb3aff..9ccb837 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -417,7 +417,9 @@
break;
case STATUS_READY:
- builder.setContentText(getString(R.string.notification_install_completed));
+ String msgCompleted = getString(R.string.notification_install_completed);
+ builder.setContentText(msgCompleted)
+ .setStyle(new Notification.BigTextStyle().bigText(msgCompleted));
builder.addAction(new Notification.Action.Builder(
null, getString(R.string.notification_action_discard),
@@ -430,7 +432,9 @@
break;
case STATUS_IN_USE:
- builder.setContentText(getString(R.string.notification_dynsystem_in_use));
+ String msgInUse = getString(R.string.notification_dynsystem_in_use);
+ builder.setContentText(msgInUse)
+ .setStyle(new Notification.BigTextStyle().bigText(msgInUse));
builder.addAction(new Notification.Action.Builder(
null, getString(R.string.notification_action_uninstall),
@@ -460,7 +464,49 @@
}
private void postStatus(int status, int cause, Throwable detail) {
- Log.d(TAG, "postStatus(): statusCode=" + status + ", causeCode=" + cause);
+ String statusString;
+ String causeString;
+
+ switch (status) {
+ case STATUS_NOT_STARTED:
+ statusString = "NOT_STARTED";
+ break;
+ case STATUS_IN_PROGRESS:
+ statusString = "IN_PROGRESS";
+ break;
+ case STATUS_READY:
+ statusString = "READY";
+ break;
+ case STATUS_IN_USE:
+ statusString = "IN_USE";
+ break;
+ default:
+ statusString = "UNKNOWN";
+ break;
+ }
+
+ switch (cause) {
+ case CAUSE_INSTALL_COMPLETED:
+ causeString = "INSTALL_COMPLETED";
+ break;
+ case CAUSE_INSTALL_CANCELLED:
+ causeString = "INSTALL_CANCELLED";
+ break;
+ case CAUSE_ERROR_IO:
+ causeString = "ERROR_IO";
+ break;
+ case CAUSE_ERROR_INVALID_URL:
+ causeString = "ERROR_INVALID_URL";
+ break;
+ case CAUSE_ERROR_EXCEPTION:
+ causeString = "ERROR_EXCEPTION";
+ break;
+ default:
+ causeString = "CAUSE_NOT_SPECIFIED";
+ break;
+ }
+
+ Log.d(TAG, "status=" + statusString + ", cause=" + causeString);
boolean notifyOnNotificationBar = true;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
index 440315f..2d7d59c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
@@ -118,7 +118,7 @@
notifyChanged();
}
- setSummary(mWifiEntry.getSummary());
+ setSummary(mWifiEntry.getSummary(false /* concise */));
mContentDescription = buildContentDescription();
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
index 752a549..0f1e0ff 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
@@ -67,7 +67,7 @@
MockitoAnnotations.initMocks(this);
when(mMockWifiEntry.getTitle()).thenReturn(MOCK_TITLE);
- when(mMockWifiEntry.getSummary()).thenReturn(MOCK_SUMMARY);
+ when(mMockWifiEntry.getSummary(false /* concise */)).thenReturn(MOCK_SUMMARY);
when(mMockIconInjector.getIcon(0)).thenReturn(mMockDrawable0);
when(mMockIconInjector.getIcon(1)).thenReturn(mMockDrawable1);
@@ -112,7 +112,7 @@
final WifiEntryPreference pref =
new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
final String updatedSummary = "updated summary";
- when(mMockWifiEntry.getSummary()).thenReturn(updatedSummary);
+ when(mMockWifiEntry.getSummary(false /* concise */)).thenReturn(updatedSummary);
pref.refresh();
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 4443524..c913999 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -367,7 +367,9 @@
String value = getSettingValue(args);
String tag = getSettingTag(args);
final boolean makeDefault = getSettingMakeDefault(args);
- insertGlobalSetting(name, value, tag, makeDefault, requestingUserId, false);
+ final boolean overrideableByRestore = getSettingOverrideableByRestore(args);
+ insertGlobalSetting(name, value, tag, makeDefault, requestingUserId, false,
+ overrideableByRestore);
break;
}
@@ -375,13 +377,16 @@
String value = getSettingValue(args);
String tag = getSettingTag(args);
final boolean makeDefault = getSettingMakeDefault(args);
- insertSecureSetting(name, value, tag, makeDefault, requestingUserId, false);
+ final boolean overrideableByRestore = getSettingOverrideableByRestore(args);
+ insertSecureSetting(name, value, tag, makeDefault, requestingUserId, false,
+ overrideableByRestore);
break;
}
case Settings.CALL_METHOD_PUT_SYSTEM: {
String value = getSettingValue(args);
- insertSystemSetting(name, value, requestingUserId);
+ boolean overrideableByRestore = getSettingOverrideableByRestore(args);
+ insertSystemSetting(name, value, requestingUserId, overrideableByRestore);
break;
}
@@ -576,20 +581,23 @@
switch (table) {
case TABLE_GLOBAL: {
if (insertGlobalSetting(name, value, null, false,
- UserHandle.getCallingUserId(), false)) {
+ UserHandle.getCallingUserId(), false,
+ /* overrideableByRestore */ false)) {
return Uri.withAppendedPath(Settings.Global.CONTENT_URI, name);
}
} break;
case TABLE_SECURE: {
if (insertSecureSetting(name, value, null, false,
- UserHandle.getCallingUserId(), false)) {
+ UserHandle.getCallingUserId(), false,
+ /* overrideableByRestore */ false)) {
return Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name);
}
} break;
case TABLE_SYSTEM: {
- if (insertSystemSetting(name, value, UserHandle.getCallingUserId())) {
+ if (insertSystemSetting(name, value, UserHandle.getCallingUserId(),
+ /* overridableByRestore */ false)) {
return Uri.withAppendedPath(Settings.System.CONTENT_URI, name);
}
} break;
@@ -1075,7 +1083,8 @@
case MUTATION_OPERATION_INSERT: {
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_CONFIG,
UserHandle.USER_SYSTEM, name, value, null, makeDefault, true,
- resolveCallingPackage(), false, null);
+ resolveCallingPackage(), false, null,
+ /* overrideableByRestore */ false);
}
case MUTATION_OPERATION_DELETE: {
@@ -1179,14 +1188,15 @@
}
private boolean insertGlobalSetting(String name, String value, String tag,
- boolean makeDefault, int requestingUserId, boolean forceNotify) {
+ boolean makeDefault, int requestingUserId, boolean forceNotify,
+ boolean overrideableByRestore) {
if (DEBUG) {
Slog.v(LOG_TAG, "insertGlobalSetting(" + name + ", " + value + ", "
+ ", " + tag + ", " + makeDefault + ", " + requestingUserId
+ ", " + forceNotify + ")");
}
return mutateGlobalSetting(name, value, tag, makeDefault, requestingUserId,
- MUTATION_OPERATION_INSERT, forceNotify, 0);
+ MUTATION_OPERATION_INSERT, forceNotify, 0, overrideableByRestore);
}
private boolean deleteGlobalSetting(String name, int requestingUserId, boolean forceNotify) {
@@ -1221,6 +1231,15 @@
private boolean mutateGlobalSetting(String name, String value, String tag,
boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
int mode) {
+ // overrideableByRestore = false as by default settings values shouldn't be overrideable by
+ // restore.
+ return mutateGlobalSetting(name, value, tag, makeDefault, requestingUserId, operation,
+ forceNotify, mode, /* overrideableByRestore */ false);
+ }
+
+ private boolean mutateGlobalSetting(String name, String value, String tag,
+ boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
+ int mode, boolean overrideableByRestore) {
// Make sure the caller can change the settings - treated as secure.
enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
@@ -1239,7 +1258,8 @@
case MUTATION_OPERATION_INSERT: {
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_GLOBAL,
UserHandle.USER_SYSTEM, name, value, tag, makeDefault,
- getCallingPackage(), forceNotify, CRITICAL_GLOBAL_SETTINGS);
+ getCallingPackage(), forceNotify,
+ CRITICAL_GLOBAL_SETTINGS, overrideableByRestore);
}
case MUTATION_OPERATION_DELETE: {
@@ -1475,7 +1495,7 @@
) {
@Override
public boolean update(String value, boolean setDefault, String packageName,
- String tag, boolean forceNonSystemPackage) {
+ String tag, boolean forceNonSystemPackage, boolean overrideableByRestore) {
Slog.wtf(LOG_TAG, "update shouldn't be called on this instance.");
return false;
}
@@ -1484,14 +1504,15 @@
}
private boolean insertSecureSetting(String name, String value, String tag,
- boolean makeDefault, int requestingUserId, boolean forceNotify) {
+ boolean makeDefault, int requestingUserId, boolean forceNotify,
+ boolean overrideableByRestore) {
if (DEBUG) {
Slog.v(LOG_TAG, "insertSecureSetting(" + name + ", " + value + ", "
+ ", " + tag + ", " + makeDefault + ", " + requestingUserId
+ ", " + forceNotify + ")");
}
return mutateSecureSetting(name, value, tag, makeDefault, requestingUserId,
- MUTATION_OPERATION_INSERT, forceNotify, 0);
+ MUTATION_OPERATION_INSERT, forceNotify, 0, overrideableByRestore);
}
private boolean deleteSecureSetting(String name, int requestingUserId, boolean forceNotify) {
@@ -1529,6 +1550,15 @@
private boolean mutateSecureSetting(String name, String value, String tag,
boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
int mode) {
+ // overrideableByRestore = false as by default settings values shouldn't be overrideable by
+ // restore.
+ return mutateSecureSetting(name, value, tag, makeDefault, requestingUserId, operation,
+ forceNotify, mode, /* overrideableByRestore */ false);
+ }
+
+ private boolean mutateSecureSetting(String name, String value, String tag,
+ boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
+ int mode, boolean overrideableByRestore) {
// Make sure the caller can change the settings.
enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
@@ -1561,7 +1591,8 @@
case MUTATION_OPERATION_INSERT: {
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE,
owningUserId, name, value, tag, makeDefault,
- getCallingPackage(), forceNotify, CRITICAL_SECURE_SETTINGS);
+ getCallingPackage(), forceNotify, CRITICAL_SECURE_SETTINGS,
+ overrideableByRestore);
}
case MUTATION_OPERATION_DELETE: {
@@ -1637,13 +1668,15 @@
}
}
- private boolean insertSystemSetting(String name, String value, int requestingUserId) {
+ private boolean insertSystemSetting(String name, String value, int requestingUserId,
+ boolean overrideableByRestore) {
if (DEBUG) {
Slog.v(LOG_TAG, "insertSystemSetting(" + name + ", " + value + ", "
+ requestingUserId + ")");
}
- return mutateSystemSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT);
+ return mutateSystemSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT,
+ overrideableByRestore);
}
private boolean deleteSystemSetting(String name, int requestingUserId) {
@@ -1663,8 +1696,15 @@
return mutateSystemSetting(name, value, requestingUserId, MUTATION_OPERATION_UPDATE);
}
- private boolean mutateSystemSetting(String name, String value, int runAsUserId,
- int operation) {
+ private boolean mutateSystemSetting(String name, String value, int runAsUserId, int operation) {
+ // overrideableByRestore = false as by default settings values shouldn't be overrideable by
+ // restore.
+ return mutateSystemSetting(name, value, runAsUserId, operation,
+ /* overrideableByRestore */ false);
+ }
+
+ private boolean mutateSystemSetting(String name, String value, int runAsUserId, int operation,
+ boolean overrideableByRestore) {
if (!hasWriteSecureSettingsPermission()) {
// If the caller doesn't hold WRITE_SECURE_SETTINGS, we verify whether this
// operation is allowed for the calling package through appops.
@@ -1714,7 +1754,7 @@
validateSystemSettingValue(name, value);
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SYSTEM,
owningUserId, name, value, null, false, getCallingPackage(),
- false, null);
+ false, null, overrideableByRestore);
}
case MUTATION_OPERATION_DELETE: {
@@ -2051,7 +2091,8 @@
}
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE,
owningUserId, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, newProviders, tag,
- makeDefault, getCallingPackage(), forceNotify, CRITICAL_SECURE_SETTINGS);
+ makeDefault, getCallingPackage(), forceNotify, CRITICAL_SECURE_SETTINGS,
+ /* overrideableByRestore */ false);
}
private static void warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk(
@@ -2145,6 +2186,10 @@
return (args != null) && args.getBoolean(Settings.CALL_METHOD_MAKE_DEFAULT_KEY);
}
+ private static boolean getSettingOverrideableByRestore(Bundle args) {
+ return (args != null) && args.getBoolean(Settings.CALL_METHOD_OVERRIDEABLE_BY_RESTORE_KEY);
+ }
+
private static int getResetModeEnforcingPermission(Bundle args) {
final int mode = (args != null) ? args.getInt(Settings.CALL_METHOD_RESET_MODE_KEY) : 0;
switch (mode) {
@@ -2642,21 +2687,21 @@
public boolean insertSettingLocked(int type, int userId, String name, String value,
String tag, boolean makeDefault, String packageName, boolean forceNotify,
- Set<String> criticalSettings) {
+ Set<String> criticalSettings, boolean overrideableByRestore) {
return insertSettingLocked(type, userId, name, value, tag, makeDefault, false,
- packageName, forceNotify, criticalSettings);
+ packageName, forceNotify, criticalSettings, overrideableByRestore);
}
public boolean insertSettingLocked(int type, int userId, String name, String value,
String tag, boolean makeDefault, boolean forceNonSystemPackage, String packageName,
- boolean forceNotify, Set<String> criticalSettings) {
+ boolean forceNotify, Set<String> criticalSettings, boolean overrideableByRestore) {
final int key = makeKey(type, userId);
boolean success = false;
SettingsState settingsState = peekSettingsStateLocked(key);
if (settingsState != null) {
success = settingsState.insertSettingLocked(name, value,
- tag, makeDefault, forceNonSystemPackage, packageName);
+ tag, makeDefault, forceNonSystemPackage, packageName, overrideableByRestore);
}
if (success && criticalSettings != null && criticalSettings.contains(name)) {
@@ -3391,6 +3436,10 @@
* for this user from the old to the new version. When you add a new
* upgrade step you *must* update SETTINGS_VERSION.
*
+ * All settings modifications should be made through
+ * {@link SettingsState#insertSettingOverrideableByRestoreLocked(String, String, String,
+ * boolean, String)} so that restore can override those values if needed.
+ *
* This is an example of moving a setting from secure to global.
*
* // v119: Example settings changes.
@@ -3436,7 +3485,8 @@
// v120: Add double tap to wake setting.
if (currentVersion == 119) {
SettingsState secureSettings = getSecureSettingsLocked(userId);
- secureSettings.insertSettingLocked(Settings.Secure.DOUBLE_TAP_TO_WAKE,
+ secureSettings.insertSettingOverrideableByRestoreLocked(
+ Settings.Secure.DOUBLE_TAP_TO_WAKE,
getContext().getResources().getBoolean(
R.bool.def_double_tap_to_wake) ? "1" : "0", null, true,
SettingsState.SYSTEM_PACKAGE_NAME);
@@ -3461,7 +3511,7 @@
Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT);
if (defaultComponent != null && !defaultComponent.isEmpty() &&
currentSetting.isNull()) {
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT,
defaultComponent, null, true, SettingsState.SYSTEM_PACKAGE_NAME);
}
@@ -3476,7 +3526,7 @@
Setting currentSetting = globalSettings.getSettingLocked(
Settings.Global.ADD_USERS_WHEN_LOCKED);
if (currentSetting.isNull()) {
- globalSettings.insertSettingLocked(
+ globalSettings.insertSettingOverrideableByRestoreLocked(
Settings.Global.ADD_USERS_WHEN_LOCKED,
getContext().getResources().getBoolean(
R.bool.def_add_users_from_lockscreen) ? "1" : "0",
@@ -3490,8 +3540,9 @@
final SettingsState globalSettings = getGlobalSettingsLocked();
String defaultDisabledProfiles = (getContext().getResources().getString(
R.string.def_bluetooth_disabled_profiles));
- globalSettings.insertSettingLocked(Settings.Global.BLUETOOTH_DISABLED_PROFILES,
- defaultDisabledProfiles, null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+ globalSettings.insertSettingOverrideableByRestoreLocked(
+ Settings.Global.BLUETOOTH_DISABLED_PROFILES, defaultDisabledProfiles,
+ null, true, SettingsState.SYSTEM_PACKAGE_NAME);
currentVersion = 124;
}
@@ -3502,7 +3553,7 @@
Setting currentSetting = secureSettings.getSettingLocked(
Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
if (currentSetting.isNull()) {
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
getContext().getResources().getBoolean(
R.bool.def_show_ime_with_hard_keyboard) ? "1" : "0",
@@ -3531,7 +3582,7 @@
b.append(c.flattenToString());
start = false;
}
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Settings.Secure.ENABLED_VR_LISTENERS, b.toString(),
null, true, SettingsState.SYSTEM_PACKAGE_NAME);
}
@@ -3551,7 +3602,7 @@
Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS);
if (!showNotifications.isNull()) {
final SettingsState secureSettings = getSecureSettingsLocked(userId);
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
showNotifications.getValue(), null, true,
SettingsState.SYSTEM_PACKAGE_NAME);
@@ -3561,7 +3612,7 @@
Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
if (!allowPrivate.isNull()) {
final SettingsState secureSettings = getSecureSettingsLocked(userId);
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
allowPrivate.getValue(), null, true,
SettingsState.SYSTEM_PACKAGE_NAME);
@@ -3588,7 +3639,7 @@
final String oldValue = systemSecureSettings.getSettingLocked(
Settings.Secure.LONG_PRESS_TIMEOUT).getValue();
if (TextUtils.equals("500", oldValue)) {
- systemSecureSettings.insertSettingLocked(
+ systemSecureSettings.insertSettingOverrideableByRestoreLocked(
Settings.Secure.LONG_PRESS_TIMEOUT,
String.valueOf(getContext().getResources().getInteger(
R.integer.def_long_press_timeout_millis)),
@@ -3604,10 +3655,12 @@
getSettingLocked(Settings.Secure.DOZE_ENABLED).getValue());
if (dozeExplicitlyDisabled) {
- secureSettings.insertSettingLocked(Settings.Secure.DOZE_PICK_UP_GESTURE,
- "0", null, true, SettingsState.SYSTEM_PACKAGE_NAME);
- secureSettings.insertSettingLocked(Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
- "0", null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+ secureSettings.insertSettingOverrideableByRestoreLocked(
+ Settings.Secure.DOZE_PICK_UP_GESTURE, "0", null, true,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ secureSettings.insertSettingOverrideableByRestoreLocked(
+ Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, "0", null, true,
+ SettingsState.SYSTEM_PACKAGE_NAME);
}
currentVersion = 131;
}
@@ -3618,7 +3671,7 @@
final String oldValue = systemSecureSettings.getSettingLocked(
Settings.Secure.MULTI_PRESS_TIMEOUT).getValue();
if (TextUtils.equals(null, oldValue)) {
- systemSecureSettings.insertSettingLocked(
+ systemSecureSettings.insertSettingOverrideableByRestoreLocked(
Settings.Secure.MULTI_PRESS_TIMEOUT,
String.valueOf(getContext().getResources().getInteger(
R.integer.def_multi_press_timeout_millis)),
@@ -3633,7 +3686,7 @@
final SettingsState systemSecureSettings = getSecureSettingsLocked(userId);
String defaultSyncParentSounds = (getContext().getResources()
.getBoolean(R.bool.def_sync_parent_sounds) ? "1" : "0");
- systemSecureSettings.insertSettingLocked(
+ systemSecureSettings.insertSettingOverrideableByRestoreLocked(
Settings.Secure.SYNC_PARENT_SOUNDS, defaultSyncParentSounds,
null, true, SettingsState.SYSTEM_PACKAGE_NAME);
currentVersion = 133;
@@ -3646,9 +3699,9 @@
.isNull()) {
String defaultEndButtonBehavior = Integer.toString(getContext()
.getResources().getInteger(R.integer.def_end_button_behavior));
- systemSettings.insertSettingLocked(Settings.System.END_BUTTON_BEHAVIOR,
- defaultEndButtonBehavior, null, true,
- SettingsState.SYSTEM_PACKAGE_NAME);
+ systemSettings.insertSettingOverrideableByRestoreLocked(
+ Settings.System.END_BUTTON_BEHAVIOR, defaultEndButtonBehavior, null,
+ true, SettingsState.SYSTEM_PACKAGE_NAME);
}
currentVersion = 134;
}
@@ -3706,8 +3759,8 @@
if (ssaid.isNull() || ssaid.getValue() == null) {
// Android Id doesn't exist for this package so create it.
- ssaidSettings.insertSettingLocked(uid, legacySsaid, null, true,
- info.packageName);
+ ssaidSettings.insertSettingOverrideableByRestoreLocked(uid,
+ legacySsaid, null, true, info.packageName);
if (DEBUG) {
Slog.d(LOG_TAG, "Keep the legacy ssaid for uid=" + uid);
}
@@ -3727,13 +3780,14 @@
&& secureSetting.getSettingLocked(
Settings.Secure.INSTALL_NON_MARKET_APPS).getValue().equals("0")) {
- secureSetting.insertSettingLocked(Settings.Secure.INSTALL_NON_MARKET_APPS,
- "1", null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+ secureSetting.insertSettingOverrideableByRestoreLocked(
+ Settings.Secure.INSTALL_NON_MARKET_APPS, "1", null, true,
+ SettingsState.SYSTEM_PACKAGE_NAME);
// For managed profiles with profile owners, DevicePolicyManagerService
// may want to set the user restriction in this case
- secureSetting.insertSettingLocked(
- Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, "1", null, true,
- SettingsState.SYSTEM_PACKAGE_NAME);
+ secureSetting.insertSettingOverrideableByRestoreLocked(
+ Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, "1", null,
+ true, SettingsState.SYSTEM_PACKAGE_NAME);
}
currentVersion = 138;
}
@@ -3774,7 +3828,7 @@
Setting currentSetting = globalSettings.getSettingLocked(
Settings.Global.WIFI_WAKEUP_ENABLED);
if (currentSetting.isNull()) {
- globalSettings.insertSettingLocked(
+ globalSettings.insertSettingOverrideableByRestoreLocked(
Settings.Global.WIFI_WAKEUP_ENABLED,
getContext().getResources().getBoolean(
R.bool.def_wifi_wakeup_enabled) ? "1" : "0",
@@ -3796,8 +3850,9 @@
if (defaultValue != null) {
Slog.d(LOG_TAG, "Setting [" + defaultValue + "] as Autofill Service "
+ "for user " + userId);
- secureSettings.insertSettingLocked(Settings.Secure.AUTOFILL_SERVICE,
- defaultValue, null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+ secureSettings.insertSettingOverrideableByRestoreLocked(
+ Settings.Secure.AUTOFILL_SERVICE, defaultValue, null, true,
+ SettingsState.SYSTEM_PACKAGE_NAME);
}
}
@@ -3848,7 +3903,7 @@
final Setting currentSetting = globalSettings.getSettingLocked(
Global.DEFAULT_RESTRICT_BACKGROUND_DATA);
if (currentSetting.isNull()) {
- globalSettings.insertSettingLocked(
+ globalSettings.insertSettingOverrideableByRestoreLocked(
Global.DEFAULT_RESTRICT_BACKGROUND_DATA,
getContext().getResources().getBoolean(
R.bool.def_restrict_background_data) ? "1" : "0",
@@ -3867,7 +3922,7 @@
final String defaultValue = getContext().getResources().getString(
R.string.def_backup_manager_constants);
if (!TextUtils.isEmpty(defaultValue)) {
- systemSecureSettings.insertSettingLocked(
+ systemSecureSettings.insertSettingOverrideableByRestoreLocked(
Settings.Secure.BACKUP_MANAGER_CONSTANTS, defaultValue, null,
true, SettingsState.SYSTEM_PACKAGE_NAME);
}
@@ -3881,7 +3936,7 @@
final Setting currentSetting = globalSettings.getSettingLocked(
Settings.Global.MOBILE_DATA_ALWAYS_ON);
if (currentSetting.isNull()) {
- globalSettings.insertSettingLocked(
+ globalSettings.insertSettingOverrideableByRestoreLocked(
Settings.Global.MOBILE_DATA_ALWAYS_ON,
getContext().getResources().getBoolean(
R.bool.def_mobile_data_always_on) ? "1" : "0",
@@ -3917,7 +3972,7 @@
if (showNotificationBadges.isNull()) {
final boolean defaultValue = getContext().getResources().getBoolean(
com.android.internal.R.bool.config_notificationBadging);
- systemSecureSettings.insertSettingLocked(
+ systemSecureSettings.insertSettingOverrideableByRestoreLocked(
Secure.NOTIFICATION_BADGING,
defaultValue ? "1" : "0",
null, true, SettingsState.SYSTEM_PACKAGE_NAME);
@@ -3934,7 +3989,7 @@
final String defaultValue = getContext().getResources().getString(
R.string.def_backup_local_transport_parameters);
if (!TextUtils.isEmpty(defaultValue)) {
- systemSecureSettings.insertSettingLocked(
+ systemSecureSettings.insertSettingOverrideableByRestoreLocked(
Settings.Secure.BACKUP_LOCAL_TRANSPORT_PARAMETERS, defaultValue,
null, true, SettingsState.SYSTEM_PACKAGE_NAME);
}
@@ -3957,7 +4012,7 @@
if (currentSetting.isNull()) {
String defaultZenDuration = Integer.toString(getContext()
.getResources().getInteger(R.integer.def_zen_duration));
- globalSettings.insertSettingLocked(
+ globalSettings.insertSettingOverrideableByRestoreLocked(
Global.ZEN_DURATION, defaultZenDuration,
null, true, SettingsState.SYSTEM_PACKAGE_NAME);
}
@@ -3973,7 +4028,7 @@
final String defaultValue = getContext().getResources().getString(
R.string.def_backup_agent_timeout_parameters);
if (!TextUtils.isEmpty(defaultValue)) {
- globalSettings.insertSettingLocked(
+ globalSettings.insertSettingOverrideableByRestoreLocked(
Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS, defaultValue,
null, true,
SettingsState.SYSTEM_PACKAGE_NAME);
@@ -4002,7 +4057,7 @@
Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS);
// The default value is "1", check if user has turned it off.
if ("0".equals(showNotifications.getValue())) {
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, "0",
null /* tag */, false /* makeDefault */,
SettingsState.SYSTEM_PACKAGE_NAME);
@@ -4023,7 +4078,7 @@
String oldValue = globalSettings.getSettingLocked(
Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY).getValue();
if (TextUtils.equals(null, oldValue)) {
- globalSettings.insertSettingLocked(
+ globalSettings.insertSettingOverrideableByRestoreLocked(
Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY,
Integer.toString(getContext().getResources().getInteger(
R.integer.def_max_sound_trigger_detection_service_ops_per_day)),
@@ -4033,7 +4088,7 @@
oldValue = globalSettings.getSettingLocked(
Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT).getValue();
if (TextUtils.equals(null, oldValue)) {
- globalSettings.insertSettingLocked(
+ globalSettings.insertSettingOverrideableByRestoreLocked(
Settings.Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT,
Integer.toString(getContext().getResources().getInteger(
R.integer.def_sound_trigger_detection_service_op_timeout)),
@@ -4048,7 +4103,7 @@
final Setting currentSetting = secureSettings.getSettingLocked(
Secure.VOLUME_HUSH_GESTURE);
if (currentSetting.isNull()) {
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Secure.VOLUME_HUSH_GESTURE,
Integer.toString(Secure.VOLUME_HUSH_VIBRATE),
null, true, SettingsState.SYSTEM_PACKAGE_NAME);
@@ -4069,7 +4124,7 @@
final Setting currentSetting = settings.getSettingLocked(
Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY);
if (currentSetting.isDefaultFromSystem()) {
- settings.insertSettingLocked(
+ settings.insertSettingOverrideableByRestoreLocked(
Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY,
Integer.toString(getContext().getResources().getInteger(
R.integer
@@ -4098,7 +4153,7 @@
Setting currentHushUsedSetting = secureSettings.getSettingLocked(
Secure.HUSH_GESTURE_USED);
if (currentHushUsedSetting.isNull()) {
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Settings.Secure.HUSH_GESTURE_USED, "0", null, true,
SettingsState.SYSTEM_PACKAGE_NAME);
}
@@ -4106,7 +4161,7 @@
Setting currentRingerToggleCountSetting = secureSettings.getSettingLocked(
Secure.MANUAL_RINGER_TOGGLE_COUNT);
if (currentRingerToggleCountSetting.isNull()) {
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Settings.Secure.MANUAL_RINGER_TOGGLE_COUNT, "0", null, true,
SettingsState.SYSTEM_PACKAGE_NAME);
}
@@ -4125,7 +4180,7 @@
final Setting currentSetting = systemSettings.getSettingLocked(
Settings.System.VIBRATE_WHEN_RINGING);
if (currentSetting.isNull()) {
- systemSettings.insertSettingLocked(
+ systemSettings.insertSettingOverrideableByRestoreLocked(
Settings.System.VIBRATE_WHEN_RINGING,
getContext().getResources().getBoolean(
R.bool.def_vibrate_when_ringing) ? "1" : "0",
@@ -4149,18 +4204,18 @@
// ZEN_DURATION
if (!globalZenDuration.isNull()) {
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Secure.ZEN_DURATION, globalZenDuration.getValue(), null, false,
SettingsState.SYSTEM_PACKAGE_NAME);
// set global zen duration setting to null since it's deprecated
- globalSettings.insertSettingLocked(
+ globalSettings.insertSettingOverrideableByRestoreLocked(
Global.ZEN_DURATION, null, null, true,
SettingsState.SYSTEM_PACKAGE_NAME);
} else if (secureZenDuration.isNull()) {
String defaultZenDuration = Integer.toString(getContext()
.getResources().getInteger(R.integer.def_zen_duration));
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Secure.ZEN_DURATION, defaultZenDuration, null, true,
SettingsState.SYSTEM_PACKAGE_NAME);
}
@@ -4169,7 +4224,7 @@
final Setting currentShowZenSettingSuggestion = secureSettings.getSettingLocked(
Secure.SHOW_ZEN_SETTINGS_SUGGESTION);
if (currentShowZenSettingSuggestion.isNull()) {
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Secure.SHOW_ZEN_SETTINGS_SUGGESTION, "1",
null, true, SettingsState.SYSTEM_PACKAGE_NAME);
}
@@ -4178,7 +4233,7 @@
final Setting currentUpdatedSetting = secureSettings.getSettingLocked(
Secure.ZEN_SETTINGS_UPDATED);
if (currentUpdatedSetting.isNull()) {
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Secure.ZEN_SETTINGS_UPDATED, "0",
null, true, SettingsState.SYSTEM_PACKAGE_NAME);
}
@@ -4187,7 +4242,7 @@
final Setting currentSettingSuggestionViewed = secureSettings.getSettingLocked(
Secure.ZEN_SETTINGS_SUGGESTION_VIEWED);
if (currentSettingSuggestionViewed.isNull()) {
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Secure.ZEN_SETTINGS_SUGGESTION_VIEWED, "0",
null, true, SettingsState.SYSTEM_PACKAGE_NAME);
}
@@ -4210,20 +4265,20 @@
if (!globalChargingSoundEnabled.isNull()) {
if (secureChargingSoundsEnabled.isNull()) {
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Secure.CHARGING_SOUNDS_ENABLED,
globalChargingSoundEnabled.getValue(), null, false,
SettingsState.SYSTEM_PACKAGE_NAME);
}
// set global charging_sounds_enabled setting to null since it's deprecated
- globalSettings.insertSettingLocked(
+ globalSettings.insertSettingOverrideableByRestoreLocked(
Global.CHARGING_SOUNDS_ENABLED, null, null, true,
SettingsState.SYSTEM_PACKAGE_NAME);
} else if (secureChargingSoundsEnabled.isNull()) {
String defChargingSoundsEnabled = getContext().getResources()
.getBoolean(R.bool.def_charging_sounds_enabled) ? "1" : "0";
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Secure.CHARGING_SOUNDS_ENABLED, defChargingSoundsEnabled, null,
true, SettingsState.SYSTEM_PACKAGE_NAME);
}
@@ -4235,7 +4290,7 @@
if (secureChargingVibrationEnabled.isNull()) {
String defChargingVibrationEnabled = getContext().getResources()
.getBoolean(R.bool.def_charging_vibration_enabled) ? "1" : "0";
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Secure.CHARGING_VIBRATION_ENABLED, defChargingVibrationEnabled,
null, true, SettingsState.SYSTEM_PACKAGE_NAME);
}
@@ -4255,7 +4310,7 @@
currentSetting.getValue());
if ((currentSettingIntegerValue
& (1 << AudioManager.STREAM_VOICE_CALL)) == 0) {
- systemSettings.insertSettingLocked(
+ systemSettings.insertSettingOverrideableByRestoreLocked(
Settings.System.MUTE_STREAMS_AFFECTED,
Integer.toString(
currentSettingIntegerValue
@@ -4296,7 +4351,7 @@
? Secure.LOCATION_MODE_ON
: Secure.LOCATION_MODE_OFF;
}
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Secure.LOCATION_MODE, Integer.toString(defLocationMode),
null, true, SettingsState.SYSTEM_PACKAGE_NAME);
}
@@ -4318,7 +4373,7 @@
Setting currentRampingRingerSetting = globalSettings.getSettingLocked(
Settings.Global.APPLY_RAMPING_RINGER);
if (currentRampingRingerSetting.isNull()) {
- globalSettings.insertSettingLocked(
+ globalSettings.insertSettingOverrideableByRestoreLocked(
Settings.Global.APPLY_RAMPING_RINGER,
getContext().getResources().getBoolean(
R.bool.def_apply_ramping_ringer) ? "1" : "0", null,
@@ -4344,7 +4399,7 @@
if (!notificationVibrationIntensity.isNull()
&& ringVibrationIntensity.isNull()) {
- systemSettings.insertSettingLocked(
+ systemSettings.insertSettingOverrideableByRestoreLocked(
Settings.System.RING_VIBRATION_INTENSITY,
notificationVibrationIntensity.getValue(),
null , true, SettingsState.SYSTEM_PACKAGE_NAME);
@@ -4388,7 +4443,7 @@
if (awareEnabled.isNull()) {
final boolean defAwareEnabled = getContext().getResources().getBoolean(
R.bool.def_aware_enabled);
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Secure.AWARE_ENABLED, defAwareEnabled ? "1" : "0",
null, true, SettingsState.SYSTEM_PACKAGE_NAME);
}
@@ -4408,7 +4463,7 @@
if (skipGesture.isNull()) {
final boolean defSkipGesture = getContext().getResources().getBoolean(
R.bool.def_skip_gesture);
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Secure.SKIP_GESTURE, defSkipGesture ? "1" : "0",
null, true, SettingsState.SYSTEM_PACKAGE_NAME);
}
@@ -4419,7 +4474,7 @@
if (silenceGesture.isNull()) {
final boolean defSilenceGesture = getContext().getResources().getBoolean(
R.bool.def_silence_gesture);
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Secure.SILENCE_GESTURE, defSilenceGesture ? "1" : "0",
null, true, SettingsState.SYSTEM_PACKAGE_NAME);
}
@@ -4447,7 +4502,7 @@
if (awareLockEnabled.isNull()) {
final boolean defAwareLockEnabled = getContext().getResources().getBoolean(
R.bool.def_aware_lock_enabled);
- secureSettings.insertSettingLocked(
+ secureSettings.insertSettingOverrideableByRestoreLocked(
Secure.AWARE_LOCK_ENABLED, defAwareLockEnabled ? "1" : "0",
null, true, SettingsState.SYSTEM_PACKAGE_NAME);
}
@@ -4467,7 +4522,7 @@
currentSetting.getValue());
if ((currentSettingIntegerValue
& (1 << AudioManager.STREAM_BLUETOOTH_SCO)) == 0) {
- systemSettings.insertSettingLocked(
+ systemSettings.insertSettingOverrideableByRestoreLocked(
Settings.System.MUTE_STREAMS_AFFECTED,
Integer.toString(
currentSettingIntegerValue
@@ -4513,13 +4568,13 @@
if (oldValueWireless == null
|| TextUtils.equals(oldValueWireless, defaultValueWired)) {
if (!TextUtils.isEmpty(defaultValueWireless)) {
- globalSettings.insertSettingLocked(
+ globalSettings.insertSettingOverrideableByRestoreLocked(
Global.WIRELESS_CHARGING_STARTED_SOUND, defaultValueWireless,
null /* tag */, true /* makeDefault */,
SettingsState.SYSTEM_PACKAGE_NAME);
} else if (!TextUtils.isEmpty(defaultValueWired)) {
// if the wireless sound is empty, use the wired charging sound
- globalSettings.insertSettingLocked(
+ globalSettings.insertSettingOverrideableByRestoreLocked(
Global.WIRELESS_CHARGING_STARTED_SOUND, defaultValueWired,
null /* tag */, true /* makeDefault */,
SettingsState.SYSTEM_PACKAGE_NAME);
@@ -4528,7 +4583,7 @@
// wired charging sound
if (oldValueWired == null && !TextUtils.isEmpty(defaultValueWired)) {
- globalSettings.insertSettingLocked(
+ globalSettings.insertSettingOverrideableByRestoreLocked(
Global.CHARGING_STARTED_SOUND, defaultValueWired,
null /* tag */, true /* makeDefault */,
SettingsState.SYSTEM_PACKAGE_NAME);
@@ -4540,8 +4595,8 @@
// Version 184: Reset the default for Global Settings: NOTIFICATION_BUBBLES
// This is originally set in version 182, however, the default value changed
// so this step is to ensure the value is updated to the correct default.
- getGlobalSettingsLocked().insertSettingLocked(Global.NOTIFICATION_BUBBLES,
- getContext().getResources().getBoolean(
+ getGlobalSettingsLocked().insertSettingOverrideableByRestoreLocked(
+ Global.NOTIFICATION_BUBBLES, getContext().getResources().getBoolean(
R.bool.def_notification_bubbles) ? "1" : "0", null /* tag */,
true /* makeDefault */, SettingsState.SYSTEM_PACKAGE_NAME);
@@ -4617,7 +4672,7 @@
final boolean systemSet = SettingsState.isSystemPackage(getContext(),
setting.getPackageName(), callingUid, userId);
if (systemSet) {
- settings.insertSettingLocked(name, setting.getValue(),
+ settings.insertSettingOverrideableByRestoreLocked(name, setting.getValue(),
setting.getTag(), true, setting.getPackageName());
} else if (setting.getDefaultValue() != null && setting.isDefaultFromSystem()) {
// We had a bug where changes by non-system packages were marked
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 5b1b530..db18213a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -117,6 +117,8 @@
private static final String ATTR_NAMESPACE = "namespace";
private static final String ATTR_BANNED_HASH = "bannedHash";
+ private static final String ATTR_PRESERVE_IN_RESTORE = "preserve_in_restore";
+
/**
* Non-binary value will be written in this attributes.
*/
@@ -388,15 +390,25 @@
// The settings provider must hold its lock when calling here.
@GuardedBy("mLock")
- public boolean insertSettingLocked(String name, String value, String tag,
+ public boolean insertSettingOverrideableByRestoreLocked(String name, String value, String tag,
boolean makeDefault, String packageName) {
- return insertSettingLocked(name, value, tag, makeDefault, false, packageName);
+ return insertSettingLocked(name, value, tag, makeDefault, false, packageName,
+ /* overrideableByRestore */ true);
}
// The settings provider must hold its lock when calling here.
@GuardedBy("mLock")
public boolean insertSettingLocked(String name, String value, String tag,
- boolean makeDefault, boolean forceNonSystemPackage, String packageName) {
+ boolean makeDefault, String packageName) {
+ return insertSettingLocked(name, value, tag, makeDefault, false, packageName,
+ /* overrideableByRestore */ false);
+ }
+
+ // The settings provider must hold its lock when calling here.
+ @GuardedBy("mLock")
+ public boolean insertSettingLocked(String name, String value, String tag,
+ boolean makeDefault, boolean forceNonSystemPackage, String packageName,
+ boolean overrideableByRestore) {
if (TextUtils.isEmpty(name)) {
return false;
}
@@ -407,7 +419,8 @@
Setting newState;
if (oldState != null) {
- if (!oldState.update(value, makeDefault, packageName, tag, forceNonSystemPackage)) {
+ if (!oldState.update(value, makeDefault, packageName, tag, forceNonSystemPackage,
+ overrideableByRestore)) {
return false;
}
newState = oldState;
@@ -495,7 +508,8 @@
changedKeys.add(key); // key was added
} else if (state.value != value) {
oldValue = state.value;
- state.update(value, false, packageName, null, true);
+ state.update(value, false, packageName, null, true,
+ /* overrideableByRestore */ false);
changedKeys.add(key); // key was updated
} else {
// this key/value already exists, no change and no logging necessary
@@ -797,7 +811,8 @@
writeSingleSetting(mVersion, serializer, setting.getId(), setting.getName(),
setting.getValue(), setting.getDefaultValue(), setting.getPackageName(),
- setting.getTag(), setting.isDefaultFromSystem());
+ setting.getTag(), setting.isDefaultFromSystem(),
+ setting.isValuePreservedInRestore());
if (DEBUG_PERSISTENCE) {
Slog.i(LOG_TAG, "[PERSISTED]" + setting.getName() + "="
@@ -886,7 +901,8 @@
static void writeSingleSetting(int version, XmlSerializer serializer, String id,
String name, String value, String defaultValue, String packageName,
- String tag, boolean defaultSysSet) throws IOException {
+ String tag, boolean defaultSysSet, boolean isValuePreservedInRestore)
+ throws IOException {
if (id == null || isBinary(id) || name == null || isBinary(name)
|| packageName == null || isBinary(packageName)) {
// This shouldn't happen.
@@ -905,6 +921,9 @@
setValueAttribute(ATTR_TAG, ATTR_TAG_BASE64,
version, serializer, tag);
}
+ if (isValuePreservedInRestore) {
+ serializer.attribute(null, ATTR_PRESERVE_IN_RESTORE, Boolean.toString(true));
+ }
serializer.endTag(null, TAG_SETTING);
}
@@ -1041,6 +1060,10 @@
String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
String defaultValue = getValueAttribute(parser, ATTR_DEFAULT_VALUE,
ATTR_DEFAULT_VALUE_BASE64);
+ String isPreservedInRestoreString = parser.getAttributeValue(null,
+ ATTR_PRESERVE_IN_RESTORE);
+ boolean isPreservedInRestore = isPreservedInRestoreString != null
+ && Boolean.parseBoolean(isPreservedInRestoreString);
String tag = null;
boolean fromSystem = false;
if (defaultValue != null) {
@@ -1049,7 +1072,7 @@
tag = getValueAttribute(parser, ATTR_TAG, ATTR_TAG_BASE64);
}
mSettings.put(name, new Setting(name, value, defaultValue, packageName, tag,
- fromSystem, id));
+ fromSystem, id, isPreservedInRestore));
if (DEBUG_PERSISTENCE) {
Slog.i(LOG_TAG, "[RESTORED] " + name + "=" + value);
@@ -1133,6 +1156,8 @@
private String tag;
// Whether the default is set by the system
private boolean defaultFromSystem;
+ // Whether the value of this setting will be preserved when restore happens.
+ private boolean isValuePreservedInRestore;
public Setting(Setting other) {
name = other.name;
@@ -1142,25 +1167,38 @@
id = other.id;
defaultFromSystem = other.defaultFromSystem;
tag = other.tag;
+ isValuePreservedInRestore = other.isValuePreservedInRestore;
}
public Setting(String name, String value, boolean makeDefault, String packageName,
String tag) {
this.name = name;
- update(value, makeDefault, packageName, tag, false);
+ // overrideableByRestore = true as the first initialization isn't considered a
+ // modification.
+ update(value, makeDefault, packageName, tag, false,
+ /* overrideableByRestore */ true);
}
public Setting(String name, String value, String defaultValue,
String packageName, String tag, boolean fromSystem, String id) {
+ this(name, value, defaultValue, packageName, tag, fromSystem, id,
+ /* isOverrideableByRestore */ false);
+ }
+
+ Setting(String name, String value, String defaultValue,
+ String packageName, String tag, boolean fromSystem, String id,
+ boolean isValuePreservedInRestore) {
mNextId = Math.max(mNextId, Long.parseLong(id) + 1);
if (NULL_VALUE.equals(value)) {
value = null;
}
- init(name, value, tag, defaultValue, packageName, fromSystem, id);
+ init(name, value, tag, defaultValue, packageName, fromSystem, id,
+ isValuePreservedInRestore);
}
private void init(String name, String value, String tag, String defaultValue,
- String packageName, boolean fromSystem, String id) {
+ String packageName, boolean fromSystem, String id,
+ boolean isValuePreservedInRestore) {
this.name = name;
this.value = value;
this.tag = tag;
@@ -1168,6 +1206,7 @@
this.packageName = packageName;
this.id = id;
this.defaultFromSystem = fromSystem;
+ this.isValuePreservedInRestore = isValuePreservedInRestore;
}
public String getName() {
@@ -1198,6 +1237,10 @@
return defaultFromSystem;
}
+ public boolean isValuePreservedInRestore() {
+ return isValuePreservedInRestore;
+ }
+
public String getId() {
return id;
}
@@ -1208,7 +1251,9 @@
/** @return whether the value changed */
public boolean reset() {
- return update(this.defaultValue, false, packageName, null, true);
+ // overrideableByRestore = true as resetting to default value isn't considered a
+ // modification.
+ return update(this.defaultValue, false, packageName, null, true, true);
}
public boolean isTransient() {
@@ -1220,7 +1265,7 @@
}
public boolean update(String value, boolean setDefault, String packageName, String tag,
- boolean forceNonSystemPackage) {
+ boolean forceNonSystemPackage, boolean overrideableByRestore) {
if (NULL_VALUE.equals(value)) {
value = null;
}
@@ -1253,17 +1298,22 @@
}
}
+ // isValuePreservedInRestore shouldn't change back to false if it has been set to true.
+ boolean isPreserved = this.isValuePreservedInRestore || !overrideableByRestore;
+
// Is something gonna change?
if (Objects.equals(value, this.value)
&& Objects.equals(defaultValue, this.defaultValue)
&& Objects.equals(packageName, this.packageName)
&& Objects.equals(tag, this.tag)
- && defaultFromSystem == this.defaultFromSystem) {
+ && defaultFromSystem == this.defaultFromSystem
+ && isPreserved == this.isValuePreservedInRestore) {
return false;
}
init(name, value, tag, defaultValue, packageName, defaultFromSystem,
- String.valueOf(mNextId++));
+ String.valueOf(mNextId++), isPreserved);
+
return true;
}
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index 3f68554..b855d87 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -46,6 +46,18 @@
"\uD800ab\uDC00 " + // broken surrogate pairs
"日本語";
+ private static final String TEST_PACKAGE = "package";
+ private static final String SETTING_NAME = "test_setting";
+
+ private final Object mLock = new Object();
+
+ private File mSettingsFile;
+
+ @Override
+ protected void setUp() {
+ mSettingsFile = new File(getContext().getCacheDir(), "setting.xml");
+ mSettingsFile.delete();
+ }
public void testIsBinary() {
assertFalse(SettingsState.isBinary(" abc 日本語"));
@@ -99,10 +111,10 @@
checkWriteSingleSetting(serializer, CRAZY_STRING, null);
SettingsState.writeSingleSetting(
SettingsState.SETTINGS_VERSION_NEW_ENCODING,
- serializer, null, "k", "v", null, "package", null, false);
+ serializer, null, "k", "v", null, "package", null, false, false);
SettingsState.writeSingleSetting(
SettingsState.SETTINGS_VERSION_NEW_ENCODING,
- serializer, "1", "k", "v", null, null, null, false);
+ serializer, "1", "k", "v", null, null, null, false, false);
}
private void checkWriteSingleSetting(XmlSerializer serializer, String key, String value)
@@ -115,7 +127,7 @@
// Make sure the XML serializer won't crash.
SettingsState.writeSingleSetting(
SettingsState.SETTINGS_VERSION_NEW_ENCODING,
- serializer, "1", key, value, null, "package", null, false);
+ serializer, "1", key, value, null, "package", null, false, false);
}
/**
@@ -182,4 +194,57 @@
assertEquals("p2", s.getPackageName());
}
}
+
+ public void testInitializeSetting_preserveFlagNotSet() {
+ SettingsState settingsWriter = getSettingStateObject();
+ settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
+ settingsWriter.persistSyncLocked();
+
+ SettingsState settingsReader = getSettingStateObject();
+ assertFalse(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
+ }
+
+ public void testModifySetting_preserveFlagSet() {
+ SettingsState settingsWriter = getSettingStateObject();
+ settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
+ settingsWriter.insertSettingLocked(SETTING_NAME, "2", null, false, TEST_PACKAGE);
+ settingsWriter.persistSyncLocked();
+
+ SettingsState settingsReader = getSettingStateObject();
+ assertTrue(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
+ }
+
+ public void testModifySettingOverrideableByRestore_preserveFlagNotSet() {
+ SettingsState settingsWriter = getSettingStateObject();
+ settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
+ settingsWriter.insertSettingLocked(SETTING_NAME, "2", null, false, false, TEST_PACKAGE,
+ /* overrideableByRestore */ true);
+ settingsWriter.persistSyncLocked();
+
+ SettingsState settingsReader = getSettingStateObject();
+ assertFalse(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
+ }
+
+ public void testModifySettingOverrideableByRestore_preserveFlagAlreadySet_flagValueUnchanged() {
+ SettingsState settingsWriter = getSettingStateObject();
+ // Init the setting.
+ settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
+ // This modification will set isValuePreservedInRestore = true.
+ settingsWriter.insertSettingLocked(SETTING_NAME, "1", null, false, TEST_PACKAGE);
+ // This modification shouldn't change the value of isValuePreservedInRestore since it's
+ // already been set to true.
+ settingsWriter.insertSettingLocked(SETTING_NAME, "2", null, false, false, TEST_PACKAGE,
+ /* overrideableByRestore */ true);
+ settingsWriter.persistSyncLocked();
+
+ SettingsState settingsReader = getSettingStateObject();
+ assertTrue(settingsReader.getSettingLocked(SETTING_NAME).isValuePreservedInRestore());
+ }
+
+ private SettingsState getSettingStateObject() {
+ SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1,
+ SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
+ settingsState.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING);
+ return settingsState;
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index a58e3d7..65fc215 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -31,8 +31,8 @@
import static android.os.BatteryManager.EXTRA_PLUGGED;
import static android.os.BatteryManager.EXTRA_STATUS;
import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
+import static android.telephony.TelephonyManager.MODEM_COUNT_DUAL_MODEM;
-import static com.android.internal.telephony.PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -92,7 +92,6 @@
import android.util.SparseBooleanArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.widget.LockPatternUtils;
import com.android.settingslib.WirelessUtils;
@@ -446,7 +445,7 @@
*/
public List<SubscriptionInfo> getFilteredSubscriptionInfo(boolean forceReload) {
List<SubscriptionInfo> subscriptions = getSubscriptionInfo(false);
- if (subscriptions.size() == MAX_PHONE_COUNT_DUAL_SIM) {
+ if (subscriptions.size() == MODEM_COUNT_DUAL_MODEM) {
SubscriptionInfo info1 = subscriptions.get(0);
SubscriptionInfo info2 = subscriptions.get(1);
if (info1.getGroupUuid() != null && info1.getGroupUuid().equals(info2.getGroupUuid())) {
@@ -1074,7 +1073,7 @@
mHandler.sendEmptyMessage(MSG_AIRPLANE_MODE_CHANGED);
} else if (Intent.ACTION_SERVICE_STATE.equals(action)) {
ServiceState serviceState = ServiceState.newFromBundle(intent.getExtras());
- int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+ int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
if (DEBUG) {
Log.v(TAG, "action " + action + " serviceState=" + serviceState + " subId="
@@ -1236,8 +1235,8 @@
throw new IllegalArgumentException("only handles intent ACTION_SIM_STATE_CHANGED");
}
String stateExtra = intent.getStringExtra(Intent.EXTRA_SIM_STATE);
- int slotId = intent.getIntExtra(PhoneConstants.PHONE_KEY, 0);
- int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+ int slotId = intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 0);
+ int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
if (Intent.SIM_STATE_ABSENT.equals(stateExtra)) {
final String absentReason = intent
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index 895207d..898cd13 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -17,6 +17,8 @@
package com.android.systemui.accessibility;
import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.database.ContentObserver;
import android.os.Handler;
import android.provider.Settings;
@@ -35,10 +37,25 @@
private WindowMagnificationController mWindowMagnificationController;
private final Handler mHandler;
+ private Configuration mLastConfiguration;
+
@Inject
public WindowMagnification(Context context, @Main Handler mainHandler) {
super(context);
mHandler = mainHandler;
+ mLastConfiguration = new Configuration(context.getResources().getConfiguration());
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ final int configDiff = newConfig.diff(mLastConfiguration);
+ if ((configDiff & ActivityInfo.CONFIG_DENSITY) == 0) {
+ return;
+ }
+ mLastConfiguration.setTo(newConfig);
+ if (mWindowMagnificationController != null) {
+ mWindowMagnificationController.onConfigurationChanged(configDiff);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index bfac4fc..c243309 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -113,6 +113,7 @@
if (mMirrorView != null) {
return;
}
+ setInitialStartBounds();
createOverlayWindow();
}
@@ -178,9 +179,20 @@
}
}
- private void createMirrorWindow() {
- setInitialStartBounds();
+ /**
+ * Called when the configuration has changed, and it updates window magnification UI.
+ *
+ * @param configDiff a bit mask of the differences between the configurations
+ */
+ void onConfigurationChanged(int configDiff) {
+ // TODO(b/145780606): update toggle button UI.
+ if (mMirrorView != null) {
+ mWm.removeView(mMirrorView);
+ createMirrorWindow();
+ }
+ }
+ private void createMirrorWindow() {
// The window should be the size the mirrored surface will be but also add room for the
// border and the drag handle.
int dragViewHeight = (int) mContext.getResources().getDimension(
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 8987683..15c1c55 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -73,7 +73,6 @@
import com.android.systemui.bubbles.animation.ExpandedAnimationController;
import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
import com.android.systemui.bubbles.animation.StackAnimationController;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -716,22 +715,6 @@
return mExpandedBubble;
}
- /**
- * Sets the bubble that should be expanded and expands if needed.
- *
- * @param key the {@link NotificationEntry#key} associated with the bubble to expand.
- * @deprecated replaced by setSelectedBubble(Bubble) + setExpanded(true)
- */
- @Deprecated
- void setExpandedBubble(String key) {
- Bubble bubbleToExpand = mBubbleData.getBubbleWithKey(key);
- if (bubbleToExpand != null) {
- setSelectedBubble(bubbleToExpand);
- bubbleToExpand.setShowInShade(false);
- setExpanded(true);
- }
- }
-
// via BubbleData.Listener
void addBubble(Bubble bubble) {
if (DEBUG_BUBBLE_STACK_VIEW) {
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 820c042..f7fe064 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,6 +16,7 @@
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
@@ -191,9 +192,9 @@
}
private fun NotificationEntry.isPeopleNotification() =
- sbn.isPeopleNotification()
- private fun StatusBarNotification.isPeopleNotification() =
- peopleNotificationIdentifier.isPeopleNotification(this)
+ sbn.isPeopleNotification(channel)
+ private fun StatusBarNotification.isPeopleNotification(channel: NotificationChannel) =
+ peopleNotificationIdentifier.isPeopleNotification(this, channel)
private fun NotificationEntry.isHighPriority() =
highPriorityProvider.isHighPriority(this)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
index 2c6a165..2eec68b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/EventBatch.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.collection.coalescer;
+import androidx.annotation.Nullable;
+
import java.util.ArrayList;
import java.util.List;
@@ -35,6 +37,8 @@
*/
final List<CoalescedEvent> mMembers = new ArrayList<>();
+ @Nullable Runnable mCancelShortTimeout;
+
EventBatch(long createdTimestamp, String groupKey) {
mCreatedTimestamp = createdTimestamp;
this.mGroupKey = groupKey;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
index 8076616..f589038 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.coalescer;
+import static com.android.systemui.statusbar.notification.logging.NotifEvent.BATCH_MAX_TIMEOUT;
import static com.android.systemui.statusbar.notification.logging.NotifEvent.COALESCED_EVENT;
import static com.android.systemui.statusbar.notification.logging.NotifEvent.EARLY_BATCH_EMIT;
import static com.android.systemui.statusbar.notification.logging.NotifEvent.EMIT_EVENT_BATCH;
@@ -71,7 +72,8 @@
private final DelayableExecutor mMainExecutor;
private final SystemClock mClock;
private final NotifLog mLog;
- private final long mGroupLingerDuration;
+ private final long mMinGroupLingerDuration;
+ private final long mMaxGroupLingerDuration;
private BatchableNotificationHandler mHandler;
@@ -82,22 +84,28 @@
public GroupCoalescer(
@Main DelayableExecutor mainExecutor,
SystemClock clock, NotifLog log) {
- this(mainExecutor, clock, log, GROUP_LINGER_DURATION);
+ this(mainExecutor, clock, log, MIN_GROUP_LINGER_DURATION, MAX_GROUP_LINGER_DURATION);
}
/**
- * @param groupLingerDuration How long, in ms, that notifications that are members of a group
- * are delayed within the GroupCoalescer before being posted
+ * @param minGroupLingerDuration How long, in ms, to wait for another notification from the same
+ * group to arrive before emitting all pending events for that
+ * group. Each subsequent arrival of a group member resets the
+ * timer for that group.
+ * @param maxGroupLingerDuration The maximum time, in ms, that a group can linger in the
+ * coalescer before it's force-emitted.
*/
GroupCoalescer(
@Main DelayableExecutor mainExecutor,
SystemClock clock,
NotifLog log,
- long groupLingerDuration) {
+ long minGroupLingerDuration,
+ long maxGroupLingerDuration) {
mMainExecutor = mainExecutor;
mClock = clock;
mLog = log;
- mGroupLingerDuration = groupLingerDuration;
+ mMinGroupLingerDuration = minGroupLingerDuration;
+ mMaxGroupLingerDuration = maxGroupLingerDuration;
}
/**
@@ -115,7 +123,7 @@
private final NotificationHandler mListener = new NotificationHandler() {
@Override
public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
- maybeEmitBatch(sbn.getKey());
+ maybeEmitBatch(sbn);
applyRanking(rankingMap);
final boolean shouldCoalesce = handleNotificationPosted(sbn, rankingMap);
@@ -130,7 +138,7 @@
@Override
public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
- maybeEmitBatch(sbn.getKey());
+ maybeEmitBatch(sbn);
applyRanking(rankingMap);
mHandler.onNotificationRemoved(sbn, rankingMap);
}
@@ -140,7 +148,7 @@
StatusBarNotification sbn,
RankingMap rankingMap,
int reason) {
- maybeEmitBatch(sbn.getKey());
+ maybeEmitBatch(sbn);
applyRanking(rankingMap);
mHandler.onNotificationRemoved(sbn, rankingMap, reason);
}
@@ -152,13 +160,20 @@
}
};
- private void maybeEmitBatch(String memberKey) {
- CoalescedEvent event = mCoalescedEvents.get(memberKey);
+ private void maybeEmitBatch(StatusBarNotification sbn) {
+ final CoalescedEvent event = mCoalescedEvents.get(sbn.getKey());
+ final EventBatch batch = mBatches.get(sbn.getGroupKey());
if (event != null) {
mLog.log(EARLY_BATCH_EMIT,
String.format("Modification of %s triggered early emit of batched group %s",
- memberKey, requireNonNull(event.getBatch()).mGroupKey));
+ sbn.getKey(), requireNonNull(event.getBatch()).mGroupKey));
emitBatch(requireNonNull(event.getBatch()));
+ } else if (batch != null
+ && mClock.uptimeMillis() - batch.mCreatedTimestamp >= mMaxGroupLingerDuration) {
+ mLog.log(BATCH_MAX_TIMEOUT,
+ String.format("Modification of %s triggered timeout emit of batched group %s",
+ sbn.getKey(), batch.mGroupKey));
+ emitBatch(batch);
}
}
@@ -175,7 +190,8 @@
}
if (sbn.isGroup()) {
- EventBatch batch = startBatchingGroup(sbn.getGroupKey());
+ final EventBatch batch = getOrBuildBatch(sbn.getGroupKey());
+
CoalescedEvent event =
new CoalescedEvent(
sbn.getKey(),
@@ -183,10 +199,10 @@
sbn,
requireRanking(rankingMap, sbn.getKey()),
batch);
+ mCoalescedEvents.put(event.getKey(), event);
batch.mMembers.add(event);
-
- mCoalescedEvents.put(event.getKey(), event);
+ resetShortTimeout(batch);
return true;
} else {
@@ -194,27 +210,39 @@
}
}
- private EventBatch startBatchingGroup(final String groupKey) {
+ private EventBatch getOrBuildBatch(final String groupKey) {
EventBatch batch = mBatches.get(groupKey);
if (batch == null) {
- final EventBatch newBatch = new EventBatch(mClock.uptimeMillis(), groupKey);
- mBatches.put(groupKey, newBatch);
- mMainExecutor.executeDelayed(() -> emitBatch(newBatch), mGroupLingerDuration);
-
- batch = newBatch;
+ batch = new EventBatch(mClock.uptimeMillis(), groupKey);
+ mBatches.put(groupKey, batch);
}
return batch;
}
+ private void resetShortTimeout(EventBatch batch) {
+ if (batch.mCancelShortTimeout != null) {
+ batch.mCancelShortTimeout.run();
+ }
+ batch.mCancelShortTimeout =
+ mMainExecutor.executeDelayed(
+ () -> {
+ batch.mCancelShortTimeout = null;
+ emitBatch(batch);
+ },
+ mMinGroupLingerDuration);
+ }
+
private void emitBatch(EventBatch batch) {
if (batch != mBatches.get(batch.mGroupKey)) {
- // If we emit a batch early, we don't want to emit it a second time when its timeout
- // expires.
- return;
+ throw new IllegalStateException("Cannot emit out-of-date batch " + batch.mGroupKey);
}
if (batch.mMembers.isEmpty()) {
throw new IllegalStateException("Batch " + batch.mGroupKey + " cannot be empty");
}
+ if (batch.mCancelShortTimeout != null) {
+ batch.mCancelShortTimeout.run();
+ batch.mCancelShortTimeout = null;
+ }
mBatches.remove(batch.mGroupKey);
@@ -299,5 +327,6 @@
void onNotificationBatchPosted(List<CoalescedEvent> events);
}
- private static final int GROUP_LINGER_DURATION = 500;
+ private static final int MIN_GROUP_LINGER_DURATION = 50;
+ private static final int MAX_GROUP_LINGER_DURATION = 500;
}
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 3cc5e62..ccd7fa3 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
@@ -99,7 +99,8 @@
}
private boolean isPeopleNotification(NotificationEntry entry) {
- return mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn());
+ return mPeopleNotificationIdentifier.isPeopleNotification(
+ entry.getSbn(), entry.getChannel());
}
private boolean hasUserSetImportance(NotificationEntry entry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
index 2374cde..9adceb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
@@ -156,7 +156,8 @@
// GroupCoalescer labels:
"CoalescedEvent",
"EarlyBatchEmit",
- "EmitEventBatch"
+ "EmitEventBatch",
+ "BatchMaxTimeout"
};
private static final int TOTAL_EVENT_LABELS = EVENT_LABELS.length;
@@ -206,5 +207,6 @@
public static final int COALESCED_EVENT = COALESCER_EVENT_START_INDEX;
public static final int EARLY_BATCH_EMIT = COALESCER_EVENT_START_INDEX + 1;
public static final int EMIT_EVENT_BATCH = COALESCER_EVENT_START_INDEX + 2;
+ public static final int BATCH_MAX_TIMEOUT = COALESCER_EVENT_START_INDEX + 3;
private static final int TOTAL_COALESCER_EVENT_TYPES = 3;
}
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 452d1eb..5c90211 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
@@ -18,13 +18,14 @@
import android.app.Notification
import android.content.Context
+import android.app.NotificationChannel
import android.service.notification.StatusBarNotification
import android.util.FeatureFlagUtils
import javax.inject.Inject
import javax.inject.Singleton
interface PeopleNotificationIdentifier {
- fun isPeopleNotification(sbn: StatusBarNotification): Boolean
+ fun isPeopleNotification(sbn: StatusBarNotification, channel: NotificationChannel): Boolean
}
@Singleton
@@ -33,12 +34,13 @@
private val context: Context
) : PeopleNotificationIdentifier {
- override fun isPeopleNotification(sbn: StatusBarNotification) =
- (sbn.notification.notificationStyle == Notification.MessagingStyle::class.java &&
+ 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)
+ personExtractor.isPersonNotification(sbn)) &&
+ !channel.isDemoted
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index ec420f3..3b106cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -25,6 +25,7 @@
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_BUBBLE;
+import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_DEMOTE;
import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_FAVORITE;
import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_HOME;
import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_MUTE;
@@ -61,6 +62,7 @@
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.widget.Button;
+import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -151,6 +153,11 @@
closeControls(v, true);
};
+ private OnClickListener mOnDemoteClick = v -> {
+ mSelectedAction = ACTION_DEMOTE;
+ closeControls(v, true);
+ };
+
public NotificationConversationInfo(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -295,6 +302,8 @@
mContext.getDrawable(R.drawable.ic_notifications_alert), null, null, null);
}
+ ImageButton demote = findViewById(R.id.demote);
+ demote.setOnClickListener(mOnDemoteClick);
}
private void bindHeader() {
@@ -609,7 +618,7 @@
}
break;
case ACTION_DEMOTE:
- // TODO: when demotion status field exists on notificationchannel
+ mChannelToUpdate.setDemoted(!mChannelToUpdate.isDemoted());
break;
}
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 f05b585..cca100f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -26,6 +26,7 @@
import android.os.Looper;
import android.os.Message;
import android.provider.Settings.Global;
+import android.telephony.Annotation;
import android.telephony.CellSignalStrength;
import android.telephony.CellSignalStrengthCdma;
import android.telephony.NetworkRegistrationInfo;
@@ -37,7 +38,6 @@
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
-import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.TelephonyIntents;
@@ -53,7 +53,9 @@
import java.io.PrintWriter;
import java.util.BitSet;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.regex.Matcher;
@@ -77,12 +79,14 @@
final SubscriptionInfo mSubscriptionInfo;
// @VisibleForDemoMode
- final SparseArray<MobileIconGroup> mNetworkToIconLookup;
+ final Map<String, MobileIconGroup> mNetworkToIconLookup;
// Since some pieces of the phone state are interdependent we store it locally,
// this could potentially become part of MobileState for simplification/complication
// of code.
private int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ private boolean mCA = false;
+ private boolean mCAPlus = false;
private int mDataState = TelephonyManager.DATA_DISCONNECTED;
private ServiceState mServiceState;
private SignalStrength mSignalStrength;
@@ -93,9 +97,6 @@
boolean mInflateSignalStrengths = false;
@VisibleForTesting
boolean mIsShowingIconGracefully = false;
- // Some specific carriers have 5GE network which is special LTE CA network.
- private static final int NETWORK_TYPE_LTE_CA_5GE =
- TelephonyManager.getAllNetworkTypes().length + 1;
// TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
// need listener lists anymore.
@@ -106,7 +107,7 @@
super("MobileSignalController(" + info.getSubscriptionId() + ")", context,
NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler,
networkController);
- mNetworkToIconLookup = new SparseArray<>();
+ mNetworkToIconLookup = new HashMap<>();
mConfig = config;
mPhone = phone;
mDefaults = defaults;
@@ -213,29 +214,38 @@
private void mapIconSets() {
mNetworkToIconLookup.clear();
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyIcons.THREE_G);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyIcons.THREE_G);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_B, TelephonyIcons.THREE_G);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EHRPD, TelephonyIcons.THREE_G);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UMTS, TelephonyIcons.THREE_G);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_TD_SCDMA, TelephonyIcons.THREE_G);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EVDO_0),
+ TelephonyIcons.THREE_G);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EVDO_A),
+ TelephonyIcons.THREE_G);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EVDO_B),
+ TelephonyIcons.THREE_G);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EHRPD),
+ TelephonyIcons.THREE_G);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_UMTS),
+ TelephonyIcons.THREE_G);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_TD_SCDMA),
+ TelephonyIcons.THREE_G);
if (!mConfig.showAtLeast3G) {
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_UNKNOWN),
TelephonyIcons.UNKNOWN);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE, TelephonyIcons.E);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA, TelephonyIcons.ONE_X);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT, TelephonyIcons.ONE_X);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EDGE),
+ TelephonyIcons.E);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_CDMA),
+ TelephonyIcons.ONE_X);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_1xRTT),
+ TelephonyIcons.ONE_X);
mDefaultIcons = TelephonyIcons.G;
} else {
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_UNKNOWN),
TelephonyIcons.THREE_G);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE,
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EDGE),
TelephonyIcons.THREE_G);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA,
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_CDMA),
TelephonyIcons.THREE_G);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT,
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_1xRTT),
TelephonyIcons.THREE_G);
mDefaultIcons = TelephonyIcons.THREE_G;
}
@@ -250,33 +260,59 @@
hPlusGroup = TelephonyIcons.H_PLUS;
}
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSDPA, hGroup);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSUPA, hGroup);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPA, hGroup);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPAP, hPlusGroup);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_HSDPA), hGroup);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_HSUPA), hGroup);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_HSPA), hGroup);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_HSPAP), hPlusGroup);
if (mConfig.show4gForLte) {
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.FOUR_G);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_LTE),
+ TelephonyIcons.FOUR_G);
if (mConfig.hideLtePlus) {
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
+ mNetworkToIconLookup.put(toIconKeyCA(TelephonyManager.NETWORK_TYPE_LTE),
TelephonyIcons.FOUR_G);
} else {
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
+ mNetworkToIconLookup.put(toIconKeyCA(TelephonyManager.NETWORK_TYPE_LTE),
TelephonyIcons.FOUR_G_PLUS);
}
} else {
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.LTE);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_LTE),
+ TelephonyIcons.LTE);
if (mConfig.hideLtePlus) {
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
+ mNetworkToIconLookup.put(toIconKeyCA(TelephonyManager.NETWORK_TYPE_LTE),
TelephonyIcons.LTE);
} else {
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
+ mNetworkToIconLookup.put(toIconKeyCA(TelephonyManager.NETWORK_TYPE_LTE),
TelephonyIcons.LTE_PLUS);
}
}
- mNetworkToIconLookup.put(NETWORK_TYPE_LTE_CA_5GE,
+ mNetworkToIconLookup.put(toIconKeyCAPlus(TelephonyManager.NETWORK_TYPE_LTE),
TelephonyIcons.LTE_CA_5G_E);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_IWLAN, TelephonyIcons.WFC);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_IWLAN),
+ TelephonyIcons.WFC);
+ }
+
+ private String getIconKey() {
+ if (mCA) {
+ return toIconKeyCA(mDataNetType);
+ } else if (mCAPlus) {
+ return toIconKeyCAPlus(mDataNetType);
+ } else {
+ return toIconKey(mDataNetType);
+ }
+ }
+
+ // Some specific carriers have 5GE network which is special CA network.
+ private String toIconKeyCAPlus(@Annotation.NetworkType int networkType) {
+ return toIconKeyCA(networkType) + "_Plus";
+ }
+
+ private String toIconKeyCA(@Annotation.NetworkType int networkType) {
+ return toIconKey(networkType) + "_CA";
+ }
+
+ private String toIconKey(@Annotation.NetworkType int networkType) {
+ return Integer.toString(networkType);
}
private void updateInflateSignalStrength() {
@@ -523,10 +559,11 @@
nr5GIconGroup = adjustNr5GIconGroupByDisplayGraceTime(nr5GIconGroup);
}
+ String iconKey = getIconKey();
if (nr5GIconGroup != null) {
mCurrentState.iconGroup = nr5GIconGroup;
- } else if (mNetworkToIconLookup.indexOfKey(mDataNetType) >= 0) {
- mCurrentState.iconGroup = mNetworkToIconLookup.get(mDataNetType);
+ } else if (mNetworkToIconLookup.get(iconKey) != null) {
+ mCurrentState.iconGroup = mNetworkToIconLookup.get(iconKey);
} else {
mCurrentState.iconGroup = mDefaultIcons;
}
@@ -679,6 +716,8 @@
pw.println(" mSignalStrength=" + mSignalStrength + ",");
pw.println(" mDataState=" + mDataState + ",");
pw.println(" mDataNetType=" + mDataNetType + ",");
+ pw.println(" mCA=" + mCA + ",");
+ pw.println(" mCAPlus=" + mCAPlus + ",");
pw.println(" mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
pw.println(" isDataDisabled=" + isDataDisabled() + ",");
pw.println(" mIsShowingIconGracefully=" + mIsShowingIconGracefully + ",");
@@ -729,11 +768,13 @@
private void updateDataNetType(int networkType) {
mDataNetType = networkType;
+ mCA = false;
+ mCAPlus = false;
if (mDataNetType == TelephonyManager.NETWORK_TYPE_LTE) {
if (isCarrierSpecificDataIcon()) {
- mDataNetType = NETWORK_TYPE_LTE_CA_5GE;
+ mCAPlus = true;
} else if (mServiceState != null && mServiceState.isUsingCarrierAggregation()) {
- mDataNetType = TelephonyManager.NETWORK_TYPE_LTE_CA;
+ mCA = true;
}
}
}
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 679fa7e..6b3c5dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -22,8 +22,7 @@
import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE;
import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT;
import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
-
-import static com.android.internal.telephony.PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM;
+import static android.telephony.TelephonyManager.MODEM_COUNT_DUAL_MODEM;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -57,7 +56,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.DemoMode;
@@ -547,7 +545,7 @@
mReceiverHandler.post(this::handleConfigurationChanged);
break;
default:
- int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+ int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
if (SubscriptionManager.isValidSubscriptionId(subId)) {
if (mMobileSignalControllers.indexOfKey(subId) >= 0) {
@@ -582,7 +580,7 @@
}
private void filterMobileSubscriptionInSameGroup(List<SubscriptionInfo> subscriptions) {
- if (subscriptions.size() == MAX_PHONE_COUNT_DUAL_SIM) {
+ if (subscriptions.size() == MODEM_COUNT_DUAL_MODEM) {
SubscriptionInfo info1 = subscriptions.get(0);
SubscriptionInfo info2 = subscriptions.get(1);
if (info1.getGroupUuid() != null && info1.getGroupUuid().equals(info2.getGroupUuid())) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/Assert.java b/packages/SystemUI/src/com/android/systemui/util/Assert.java
index 096ac3f..f6e921e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Assert.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Assert.java
@@ -18,7 +18,7 @@
import android.os.Looper;
-import com.android.internal.annotations.VisibleForTesting;
+import androidx.annotation.VisibleForTesting;
/**
* Helper providing common assertions.
@@ -30,7 +30,9 @@
public static void isMainThread() {
if (!sMainLooper.isCurrentThread()) {
- throw new IllegalStateException("should be called from the main thread.");
+ throw new IllegalStateException("should be called from the main thread."
+ + " sMainLooper.threadName=" + sMainLooper.getThread().getName()
+ + " Thread.currentThread()=" + Thread.currentThread().getName());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 2e0fb3b..12da006 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -57,7 +57,6 @@
import android.testing.TestableContext;
import android.testing.TestableLooper;
-import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.systemui.DumpController;
import com.android.systemui.SysuiTestCase;
@@ -524,9 +523,9 @@
int subscription = simInited
? 1/* mock subid=1 */ : SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE;
if (data != null) intent.putExtras(data);
- intent.putExtra(PhoneConstants.PHONE_NAME_KEY, "Phone");
- intent.putExtra("subscription", subscription);
- intent.putExtra("slot", 0/* SLOT 1 */);
+
+ intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subscription);
+ intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 0);
return intent;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index e0b4b81..c3df3f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -356,8 +356,7 @@
// Switch which bubble is expanded
mBubbleController.selectBubble(mRow.getEntry().getKey());
- stackView.setExpandedBubble(mRow.getEntry().getKey());
- assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry());
+ mBubbleController.expandStack();
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
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 cc5514f..d852fa1 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
@@ -571,7 +571,7 @@
}
private NotificationEntry createNotification() {
- Notification.Builder n = new Notification.Builder(mContext, "")
+ Notification.Builder n = new Notification.Builder(mContext, "id")
.setSmallIcon(R.drawable.ic_person)
.setContentTitle("Title")
.setContentText("Text");
@@ -582,6 +582,7 @@
.setUid(TEST_UID)
.setId(mId++)
.setNotification(n.build())
+ .setChannel(new NotificationChannel("id", "", IMPORTANCE_DEFAULT))
.setUser(new UserHandle(ActivityManager.getCurrentUser()))
.build();
}
@@ -616,7 +617,7 @@
@Test
public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications() {
Assert.sMainLooper = TestableLooper.get(this).getLooper();
- Notification.Builder n = new Notification.Builder(mContext, "")
+ Notification.Builder n = new Notification.Builder(mContext, "di")
.setSmallIcon(R.drawable.ic_person)
.setContentTitle("Title")
.setContentText("Text");
@@ -628,6 +629,7 @@
.setId(mId++)
.setNotification(n.build())
.setUser(new UserHandle(ActivityManager.getCurrentUser()))
+ .setChannel(new NotificationChannel("id", "", IMPORTANCE_DEFAULT))
.build();
mEntryManager.addActiveNotificationForTest(mEntry);
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 93909dc..7c3665b 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,8 @@
final NotificationEntry entry = new NotificationEntryBuilder()
.setImportance(IMPORTANCE_HIGH)
.build();
- when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+ .thenReturn(false);
// THEN it has high priority
assertTrue(mHighPriorityProvider.isHighPriority(entry));
@@ -75,7 +76,8 @@
.setNotification(notification)
.setImportance(IMPORTANCE_LOW)
.build();
- when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(true);
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+ .thenReturn(true);
// THEN it has high priority
assertTrue(mHighPriorityProvider.isHighPriority(entry));
@@ -90,7 +92,8 @@
final NotificationEntry entry = new NotificationEntryBuilder()
.setNotification(notification)
.build();
- when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+ .thenReturn(false);
// THEN it has high priority
assertTrue(mHighPriorityProvider.isHighPriority(entry));
@@ -106,7 +109,8 @@
.setNotification(notification)
.setImportance(IMPORTANCE_LOW)
.build();
- when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+ .thenReturn(false);
// THEN it has high priority
assertTrue(mHighPriorityProvider.isHighPriority(entry));
@@ -122,7 +126,8 @@
.setNotification(notification)
.setImportance(IMPORTANCE_MIN)
.build();
- when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+ .thenReturn(false);
// THEN it does NOT have high priority
assertFalse(mHighPriorityProvider.isHighPriority(entry));
@@ -144,7 +149,8 @@
.setNotification(notification)
.setChannel(channel)
.build();
- when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(true);
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
+ .thenReturn(true);
// THEN it does NOT have high priority
assertFalse(mHighPriorityProvider.isHighPriority(entry));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
index e273191..7ab4846 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.collection
import android.app.Notification
+import android.app.NotificationChannel
import android.app.NotificationManager.IMPORTANCE_DEFAULT
import android.app.NotificationManager.IMPORTANCE_HIGH
import android.app.NotificationManager.IMPORTANCE_LOW
@@ -81,6 +82,7 @@
.setNotification(
Notification.Builder(mContext, "test")
.build())
+ .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
.setUser(mContext.getUser())
.setOverrideGroupKey("")
.build()
@@ -94,6 +96,7 @@
.setNotification(
Notification.Builder(mContext, "test")
.build())
+ .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
.setUser(mContext.getUser())
.setOverrideGroupKey("")
.build()
@@ -116,6 +119,7 @@
.setOpPkg("pkg")
.setTag("tag")
.setNotification(aN)
+ .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
.setUser(mContext.getUser())
.setOverrideGroupKey("")
.build()
@@ -130,6 +134,7 @@
.setOpPkg("pkg2")
.setTag("tag")
.setNotification(bN)
+ .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
.setUser(mContext.getUser())
.setOverrideGroupKey("")
.build()
@@ -149,6 +154,7 @@
.setTag("tag")
.setNotification(notif)
.setUser(mContext.user)
+ .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
.setOverrideGroupKey("")
.build()
@@ -168,6 +174,7 @@
.setTag("tag")
.setNotification(notif)
.setUser(mContext.user)
+ .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
.setOverrideGroupKey("")
.build()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
index 5e0baf2..86c1eb97 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
@@ -80,7 +80,8 @@
mExecutor,
mClock,
mLog,
- LINGER_DURATION);
+ MIN_LINGER_DURATION,
+ MAX_LINGER_DURATION);
mCoalescer.setNotificationHandler(mListener);
mCoalescer.attach(mListenerService);
@@ -96,7 +97,7 @@
new NotificationEntryBuilder()
.setId(0)
.setPkg(TEST_PACKAGE_A));
- mClock.advanceTime(LINGER_DURATION);
+ mClock.advanceTime(MIN_LINGER_DURATION);
// THEN the event is passed through to the handler
verify(mListener).onNotificationPosted(notif1.sbn, notif1.rankingMap);
@@ -144,12 +145,16 @@
.setId(1)
.setGroup(mContext, GROUP_1));
+ mClock.advanceTime(2);
+
NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
.setPkg(TEST_PACKAGE_A)
.setId(2)
.setGroup(mContext, GROUP_1)
.setGroupSummary(mContext, true));
+ mClock.advanceTime(3);
+
NotifEvent notif3 = mNoMan.postNotif(new NotificationEntryBuilder()
.setPkg(TEST_PACKAGE_A)
.setId(3)
@@ -161,7 +166,7 @@
verify(mListener, never()).onNotificationBatchPosted(anyList());
// WHEN enough time passes
- mClock.advanceTime(LINGER_DURATION);
+ mClock.advanceTime(MIN_LINGER_DURATION);
// THEN the coalesced notifs are applied. The summary is sorted to the front.
verify(mListener).onNotificationBatchPosted(Arrays.asList(
@@ -212,7 +217,7 @@
// WHEN the time runs out on the remainder of the group
clearInvocations(mListener);
- mClock.advanceTime(LINGER_DURATION);
+ mClock.advanceTime(MIN_LINGER_DURATION);
// THEN no lingering batch is applied
verify(mListener, never()).onNotificationBatchPosted(anyList());
@@ -225,11 +230,13 @@
.setPkg(TEST_PACKAGE_A)
.setId(1)
.setGroup(mContext, GROUP_1));
+ mClock.advanceTime(2);
NotifEvent notif2a = mNoMan.postNotif(new NotificationEntryBuilder()
.setPkg(TEST_PACKAGE_A)
.setId(2)
.setContentTitle(mContext, "Version 1")
.setGroup(mContext, GROUP_1));
+ mClock.advanceTime(4);
// WHEN one of them gets updated
NotifEvent notif2b = mNoMan.postNotif(new NotificationEntryBuilder()
@@ -248,7 +255,7 @@
any(RankingMap.class));
// THEN second, the update is emitted
- mClock.advanceTime(LINGER_DURATION);
+ mClock.advanceTime(MIN_LINGER_DURATION);
verify(mListener).onNotificationBatchPosted(Collections.singletonList(
new CoalescedEvent(notif2b.key, 0, notif2b.sbn, notif2b.ranking, null)
));
@@ -308,14 +315,61 @@
.setId(17));
// THEN they have the new rankings when they are eventually emitted
- mClock.advanceTime(LINGER_DURATION);
+ mClock.advanceTime(MIN_LINGER_DURATION);
verify(mListener).onNotificationBatchPosted(Arrays.asList(
new CoalescedEvent(notif1.key, 0, notif1.sbn, ranking1b, null),
new CoalescedEvent(notif2.key, 1, notif2.sbn, ranking2b, null)
));
}
- private static final long LINGER_DURATION = 4700;
+ @Test
+ public void testMaxLingerDuration() {
+ // GIVEN five coalesced notifications that have collectively taken 20ms to arrive, 2ms
+ // longer than the max linger duration
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_A)
+ .setId(1)
+ .setGroup(mContext, GROUP_1));
+ mClock.advanceTime(4);
+ NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_A)
+ .setId(2)
+ .setGroup(mContext, GROUP_1));
+ mClock.advanceTime(4);
+ NotifEvent notif3 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_A)
+ .setId(3)
+ .setGroup(mContext, GROUP_1));
+ mClock.advanceTime(4);
+ NotifEvent notif4 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_A)
+ .setId(4)
+ .setGroup(mContext, GROUP_1));
+ mClock.advanceTime(4);
+ NotifEvent notif5 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_A)
+ .setId(5)
+ .setGroup(mContext, GROUP_1));
+ mClock.advanceTime(4);
+
+ // WHEN a sixth notification arrives
+ NotifEvent notif6 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_A)
+ .setId(6)
+ .setGroup(mContext, GROUP_1));
+
+ // THEN the first five notifications are emitted in a batch
+ verify(mListener).onNotificationBatchPosted(Arrays.asList(
+ new CoalescedEvent(notif1.key, 0, notif1.sbn, notif1.ranking, null),
+ new CoalescedEvent(notif2.key, 1, notif2.sbn, notif2.ranking, null),
+ new CoalescedEvent(notif3.key, 2, notif3.sbn, notif3.ranking, null),
+ new CoalescedEvent(notif4.key, 3, notif4.sbn, notif4.ranking, null),
+ new CoalescedEvent(notif5.key, 4, notif5.sbn, notif5.ranking, null)
+ ));
+ }
+
+ private static final long MIN_LINGER_DURATION = 5;
+ private static final long MAX_LINGER_DURATION = 18;
private static final String TEST_PACKAGE_A = "com.test.package_a";
private static final String TEST_PACKAGE_B = "com.test.package_b";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 0bf458c..61f0b26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -62,6 +62,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
+import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
@@ -684,6 +685,34 @@
}
@Test
+ public void testDemote() throws Exception {
+ mNotificationInfo.bindNotification(
+ mShortcutManager,
+ mLauncherApps,
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mEntry,
+ null,
+ null,
+ null,
+ true);
+
+
+ ImageButton demote = mNotificationInfo.findViewById(R.id.demote);
+ demote.performClick();
+ mTestableLooper.processAllMessages();
+
+ ArgumentCaptor<NotificationChannel> captor =
+ ArgumentCaptor.forClass(NotificationChannel.class);
+ verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+ anyString(), anyInt(), captor.capture());
+ assertTrue(captor.getValue().isDemoted());
+ }
+
+ @Test
public void testMute_mute() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
mConversationChannel.setImportance(IMPORTANCE_DEFAULT);
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 068707f..21100458ad 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -237,6 +237,9 @@
// Inform the user that the current network may not support using a randomized MAC address.
NOTE_NETWORK_NO_MAC_RANDOMIZATION_SUPPORT = 56;
+ // Inform the user that EAP failure occurs
+ NOTE_WIFI_EAP_FAILURE = 57;
+
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 36a9e0eb..f3a415e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -94,6 +94,7 @@
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
+import android.view.accessibility.IWindowMagnificationConnection;
import com.android.internal.R;
import com.android.internal.accessibility.AccessibilityShortcutController;
@@ -106,6 +107,7 @@
import com.android.internal.util.IntPair;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -204,6 +206,8 @@
private AccessibilityInputFilter mInputFilter;
+ private WindowMagnificationManager mWindowMagnificationMgr;
+
private boolean mHasInputFilter;
private KeyEventDispatcher mKeyEventDispatcher;
@@ -877,11 +881,8 @@
*/
@Override
public void notifyAccessibilityButtonVisibilityChanged(boolean shown) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Caller does not hold permission "
- + android.Manifest.permission.STATUS_BAR_SERVICE);
- }
+ mSecurityPolicy.enforceCallingOrSelfPermission(
+ android.Manifest.permission.STATUS_BAR_SERVICE);
synchronized (mLock) {
notifyAccessibilityButtonVisibilityChangedLocked(shown);
}
@@ -1147,8 +1148,8 @@
@ShortcutType int shortcutType) {
Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ intent.putExtra(AccessibilityManager.EXTRA_SHORTCUT_TYPE, shortcutType);
final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle();
- bundle.putInt(AccessibilityManager.EXTRA_SHORTCUT_TYPE, shortcutType);
mContext.startActivityAsUser(intent, bundle, UserHandle.of(mCurrentUserId));
}
@@ -1883,11 +1884,12 @@
}
private boolean readAccessibilityShortcutKeySettingLocked(AccessibilityUserState userState) {
+ final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userState.mUserId);
final Set<String> targetsFromSetting = new ArraySet<>();
- readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
- userState.mUserId, targetsFromSetting, str -> str);
- if (targetsFromSetting.isEmpty()) {
- // Fall back to device's default a11y service.
+ readColonDelimitedStringToSet(settingValue, targetsFromSetting, false, str -> str);
+ // Fall back to device's default a11y service, only when setting is never updated.
+ if (settingValue == null) {
final String defaultService = mContext.getString(
R.string.config_defaultAccessibilityService);
if (!TextUtils.isEmpty(defaultService)) {
@@ -2588,6 +2590,24 @@
}
@Override
+ public void setWindowMagnificationConnection(
+ IWindowMagnificationConnection connection) throws RemoteException {
+ mSecurityPolicy.enforceCallingOrSelfPermission(
+ android.Manifest.permission.STATUS_BAR_SERVICE);
+
+ getWindowMagnificationMgr().setConnection(connection);
+ }
+
+ WindowMagnificationManager getWindowMagnificationMgr() {
+ synchronized (mLock) {
+ if (mWindowMagnificationMgr == null) {
+ mWindowMagnificationMgr = new WindowMagnificationManager();
+ }
+ return mWindowMagnificationMgr;
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
synchronized (mLock) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index 7dbec7c..7a42cd1 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -550,4 +550,17 @@
Binder.restoreCallingIdentity(identityToken);
}
}
+
+ /**
+ * Enforcing permission check to IPC caller or grant it if it's not through IPC.
+ *
+ * @param permission The permission to check
+ */
+ public void enforceCallingOrSelfPermission(@NonNull String permission) {
+ if (mContext.checkCallingOrSelfPermission(permission)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Caller does not hold permission "
+ + permission);
+ }
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
new file mode 100644
index 0000000..351c9e0
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
@@ -0,0 +1,110 @@
+/*
+ * 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.accessibility.magnification;
+
+import static android.os.IBinder.DeathRecipient;
+
+import android.annotation.NonNull;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.accessibility.IWindowMagnificationConnection;
+import android.view.accessibility.IWindowMagnificationConnectionCallback;
+
+/**
+ * A wrapper of {@link IWindowMagnificationConnection}.
+ */
+class WindowMagnificationConnectionWrapper {
+
+ private static final boolean DBG = false;
+ private static final String TAG = "WindowMagnificationConnectionWrapper";
+
+ private final @NonNull IWindowMagnificationConnection mConnection;
+
+ WindowMagnificationConnectionWrapper(@NonNull IWindowMagnificationConnection connection) {
+ mConnection = connection;
+ }
+
+ //Should not use this instance anymore after calling it.
+ void unlinkToDeath(@NonNull DeathRecipient deathRecipient) {
+ mConnection.asBinder().unlinkToDeath(deathRecipient, 0);
+ }
+
+ void linkToDeath(@NonNull DeathRecipient deathRecipient) throws RemoteException {
+ mConnection.asBinder().linkToDeath(deathRecipient, 0);
+ }
+
+ boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY) {
+ try {
+ mConnection.enableWindowMagnification(displayId, scale, centerX, centerY);
+ } catch (RemoteException e) {
+ if (DBG) {
+ Slog.e(TAG, "Error calling enableWindowMagnification()");
+ }
+ return false;
+ }
+ return true;
+ }
+
+ boolean setScale(int displayId, float scale) {
+ try {
+ mConnection.setScale(displayId, scale);
+ } catch (RemoteException e) {
+ if (DBG) {
+ Slog.e(TAG, "Error calling setScale()");
+ }
+ return false;
+ }
+ return true;
+ }
+
+ boolean disableWindowMagnification(int displayId) {
+ try {
+ mConnection.disableWindowMagnification(displayId);
+ } catch (RemoteException e) {
+ if (DBG) {
+ Slog.e(TAG, "Error calling disableWindowMagnification()");
+ }
+ return false;
+ }
+ return true;
+ }
+
+ boolean moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
+ try {
+ mConnection.moveWindowMagnifier(displayId, offsetX, offsetY);
+ } catch (RemoteException e) {
+ if (DBG) {
+ Slog.e(TAG, "Error calling moveWindowMagnifier()");
+ }
+ return false;
+ }
+ return true;
+ }
+
+ boolean setConnectionCallback(IWindowMagnificationConnectionCallback connectionCallback) {
+ try {
+ mConnection.setConnectionCallback(connectionCallback);
+ } catch (RemoteException e) {
+ if (DBG) {
+ Slog.e(TAG, "Error calling setConnectionCallback()");
+ }
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
new file mode 100644
index 0000000..00db329
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -0,0 +1,98 @@
+/*
+ * 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.accessibility.magnification;
+
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.accessibility.IWindowMagnificationConnection;
+import android.view.accessibility.IWindowMagnificationConnectionCallback;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * A class to manipulate window magnification through {@link WindowMagnificationConnectionWrapper}.
+ */
+public final class WindowMagnificationManager {
+
+ private static final String TAG = "WindowMagnificationMgr";
+ private final Object mLock = new Object();
+ @VisibleForTesting
+ @Nullable WindowMagnificationConnectionWrapper mConnectionWrapper;
+ private ConnectionCallback mConnectionCallback;
+
+ /**
+ * Sets {@link IWindowMagnificationConnection}.
+ * @param connection {@link IWindowMagnificationConnection}
+ */
+ public void setConnection(@Nullable IWindowMagnificationConnection connection) {
+ synchronized (mLock) {
+ //Reset connectionWrapper.
+ if (mConnectionWrapper != null) {
+ mConnectionWrapper.setConnectionCallback(null);
+ if (mConnectionCallback != null) {
+ mConnectionCallback.mExpiredDeathRecipient = true;
+ }
+ mConnectionWrapper.unlinkToDeath(mConnectionCallback);
+ mConnectionWrapper = null;
+ }
+ if (connection != null) {
+ mConnectionWrapper = new WindowMagnificationConnectionWrapper(connection);
+ }
+
+ if (mConnectionWrapper != null) {
+ try {
+ mConnectionCallback = new ConnectionCallback();
+ mConnectionWrapper.linkToDeath(mConnectionCallback);
+ mConnectionWrapper.setConnectionCallback(mConnectionCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "setConnection failed", e);
+ mConnectionWrapper = null;
+ }
+ }
+ }
+ }
+
+ private class ConnectionCallback extends IWindowMagnificationConnectionCallback.Stub implements
+ IBinder.DeathRecipient {
+ private boolean mExpiredDeathRecipient = false;
+
+ @Override
+ public void onWindowMagnifierBoundsChanged(int display, Rect frame) throws RemoteException {
+ }
+
+ @Override
+ public void onChangeMagnificationMode(int display, int magnificationMode)
+ throws RemoteException {
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ if (mExpiredDeathRecipient) {
+ Slog.w(TAG, "binderDied DeathRecipient is expired");
+ return;
+ }
+ mConnectionWrapper.unlinkToDeath(this);
+ mConnectionWrapper = null;
+ mConnectionCallback = null;
+ }
+ }
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 7d354d2..cdd7510 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -19,6 +19,7 @@
import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
+import static android.view.autofill.AutofillManager.ACTION_RESPONSE_EXPIRED;
import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED;
@@ -269,6 +270,9 @@
@GuardedBy("mLock")
private final LocalLog mWtfHistory;
+ @GuardedBy("mLock")
+ private boolean mExpiredResponse;
+
/**
* Map of {@link MetricsEvent#AUTOFILL_REQUEST} metrics, keyed by fill request id.
*/
@@ -690,6 +694,7 @@
@GuardedBy("mLock")
private void requestNewFillResponseLocked(@NonNull ViewState viewState, int newState,
int flags) {
+ mExpiredResponse = false;
if (mForAugmentedAutofillOnly || mRemoteFillService == null) {
if (sVerbose) {
Slog.v(TAG, "requestNewFillResponse(): triggering augmented autofill instead "
@@ -1307,6 +1312,9 @@
}
}
+ // The client becomes invisible for the authentication, the response is effective.
+ mExpiredResponse = false;
+
final Parcelable result = data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT);
final Bundle newClientState = data.getBundle(AutofillManager.EXTRA_CLIENT_STATE);
if (sDebug) {
@@ -2322,16 +2330,18 @@
* @param id The id of the view that is entered.
* @param viewState The view that is entered.
* @param flags The flag that was passed by the AutofillManager.
+ *
+ * @return {@code true} if a new fill response is requested.
*/
@GuardedBy("mLock")
- private void requestNewFillResponseOnViewEnteredIfNecessaryLocked(@NonNull AutofillId id,
+ private boolean requestNewFillResponseOnViewEnteredIfNecessaryLocked(@NonNull AutofillId id,
@NonNull ViewState viewState, int flags) {
if ((flags & FLAG_MANUAL_REQUEST) != 0) {
mForAugmentedAutofillOnly = false;
if (sDebug) Slog.d(TAG, "Re-starting session on view " + id + " and flags " + flags);
maybeRequestInlineSuggestionsRequestThenFillLocked(viewState,
ViewState.STATE_RESTARTED_SESSION, flags);
- return;
+ return true;
}
// If it's not, then check if it it should start a partition.
@@ -2342,12 +2352,14 @@
}
maybeRequestInlineSuggestionsRequestThenFillLocked(viewState,
ViewState.STATE_STARTED_PARTITION, flags);
+ return true;
} else {
if (sVerbose) {
Slog.v(TAG, "Not starting new partition for view " + id + ": "
+ viewState.getStateAsString());
}
}
+ return false;
}
/**
@@ -2355,7 +2367,7 @@
*
* @param id The id of the view that is entered
*
- * @return {@code true} iff a new partition should be started
+ * @return {@code true} if a new partition should be started
*/
@GuardedBy("mLock")
private boolean shouldStartNewPartitionLocked(@NonNull AutofillId id) {
@@ -2363,6 +2375,13 @@
return true;
}
+ if (mExpiredResponse) {
+ if (sDebug) {
+ Slog.d(TAG, "Starting a new partition because the response has expired.");
+ }
+ return true;
+ }
+
final int numResponses = mResponses.size();
if (numResponses >= AutofillManagerService.getPartitionMaxCount()) {
Slog.e(TAG, "Not starting a new partition on " + id + " because session " + this.id
@@ -2414,6 +2433,14 @@
+ id + " destroyed");
return;
}
+ if (action == ACTION_RESPONSE_EXPIRED) {
+ mExpiredResponse = true;
+ if (sDebug) {
+ Slog.d(TAG, "Set the response has expired.");
+ }
+ return;
+ }
+
id.setSessionId(this.id);
if (sVerbose) {
Slog.v(TAG, "updateLocked(" + this.id + "): id=" + id + ", action="
@@ -2577,7 +2604,9 @@
return;
}
- requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags);
+ if (requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags)) {
+ return;
+ }
if (isSameViewEntered) {
return;
@@ -3678,6 +3707,8 @@
return "VIEW_EXITED";
case ACTION_VALUE_CHANGED:
return "VALUE_CHANGED";
+ case ACTION_RESPONSE_EXPIRED:
+ return "RESPONSE_EXPIRED";
default:
return "UNKNOWN_" + action;
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index c1e23e4..5e495b9 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -39,6 +39,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkPolicyManager.RULE_NONE;
import static android.net.NetworkPolicyManager.uidRulesToString;
@@ -5840,11 +5841,6 @@
} else {
newNc.addCapability(NET_CAPABILITY_FOREGROUND);
}
- if (nai.isSuspended()) {
- newNc.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
- } else {
- newNc.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
- }
if (nai.partialConnectivity) {
newNc.addCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY);
} else {
@@ -5852,6 +5848,11 @@
}
newNc.setPrivateDnsBroken(nai.networkCapabilities.isPrivateDnsBroken());
+ // TODO : remove this once all factories are updated to send NOT_SUSPENDED
+ if (!newNc.hasTransport(TRANSPORT_CELLULAR)) {
+ newNc.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ }
+
return newNc;
}
@@ -5896,6 +5897,17 @@
// on this network. We might have been called by rematchNetworkAndRequests when a
// network changed foreground state.
processListenRequests(nai);
+ final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ if (prevSuspended != suspended) {
+ // TODO (b/73132094) : remove this call once the few users of onSuspended and
+ // onResumed have been removed.
+ notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED
+ : ConnectivityManager.CALLBACK_RESUMED);
+ // updateNetworkInfo will mix in the suspended info from the capabilities and
+ // take appropriate action for the network having possibly changed state.
+ updateNetworkInfo(nai, nai.networkInfo);
+ }
} else {
// If the requestable capabilities have changed or the score changed, we can't have been
// called by rematchNetworkAndRequests, so it's safe to start a rematch.
@@ -5903,6 +5915,9 @@
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
+ // TODO : static analysis indicates that prevNc can't be null here (getAndSetNetworkCaps
+ // never returns null), so mark the relevant members and functions in nai as @NonNull and
+ // remove this test
if (prevNc != null) {
final boolean oldMetered = prevNc.isMetered();
final boolean newMetered = newNc.isMetered();
@@ -6597,10 +6612,30 @@
}
}
- private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) {
+ @NonNull
+ private NetworkInfo mixInInfo(@NonNull final NetworkAgentInfo nai, @NonNull NetworkInfo info) {
+ final NetworkInfo newInfo = new NetworkInfo(info);
+ // The suspended bit is managed in NetworkCapabilities.
+ final boolean suspended =
+ !nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ if (suspended && info.getDetailedState() == NetworkInfo.DetailedState.CONNECTED) {
+ // Only override the state with SUSPENDED if the network is currently in CONNECTED
+ // state. This is because the network could have been suspended before connecting,
+ // or it could be disconnecting while being suspended, and in both these cases
+ // the state should not be overridden. Note that the only detailed state that
+ // maps to State.CONNECTED is DetailedState.CONNECTED, so there is also no need to
+ // worry about multiple different substates of CONNECTED.
+ newInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, info.getReason(),
+ info.getExtraInfo());
+ }
+ return newInfo;
+ }
+
+ private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo info) {
+ final NetworkInfo newInfo = mixInInfo(networkAgent, info);
+
final NetworkInfo.State state = newInfo.getState();
NetworkInfo oldInfo = null;
- final int oldScore = networkAgent.getCurrentScore();
synchronized (networkAgent) {
oldInfo = networkAgent.networkInfo;
networkAgent.networkInfo = newInfo;
@@ -6682,17 +6717,6 @@
}
} else if (networkAgent.created && (oldInfo.getState() == NetworkInfo.State.SUSPENDED ||
state == NetworkInfo.State.SUSPENDED)) {
- // going into or coming out of SUSPEND: re-score and notify
- if (networkAgent.getCurrentScore() != oldScore) {
- rematchAllNetworksAndRequests();
- }
- updateCapabilities(networkAgent.getCurrentScore(), networkAgent,
- networkAgent.networkCapabilities);
- // TODO (b/73132094) : remove this call once the few users of onSuspended and
- // onResumed have been removed.
- notifyNetworkCallbacks(networkAgent, (state == NetworkInfo.State.SUSPENDED ?
- ConnectivityManager.CALLBACK_SUSPENDED :
- ConnectivityManager.CALLBACK_RESUMED));
mLegacyTypeTracker.update(networkAgent);
}
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index dc393d1..e9db9c8 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -16,6 +16,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.PERMISSION_GRANTED;
import static android.location.LocationManager.FUSED_PROVIDER;
import static android.location.LocationManager.GPS_PROVIDER;
@@ -83,6 +85,7 @@
import com.android.internal.content.PackageMonitor;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
@@ -459,8 +462,10 @@
Log.d(TAG, "[u" + userId + "] location enabled = " + isLocationEnabledForUser(userId));
}
- Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION);
- intent.putExtra(LocationManager.EXTRA_LOCATION_ENABLED, isLocationEnabledForUser(userId));
+ Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION)
+ .putExtra(LocationManager.EXTRA_LOCATION_ENABLED, isLocationEnabledForUser(userId))
+ .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
for (LocationProviderManager manager : mProviderManagers) {
@@ -929,9 +934,11 @@
// update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility
mSettingsStore.setLocationProviderAllowed(mName, useable, userId);
- Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION);
- intent.putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName);
- intent.putExtra(LocationManager.EXTRA_PROVIDER_ENABLED, useable);
+ Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION)
+ .putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName)
+ .putExtra(LocationManager.EXTRA_PROVIDER_ENABLED, useable)
+ .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
}
@@ -1396,20 +1403,19 @@
private String getResolutionPermission(int resolutionLevel) {
switch (resolutionLevel) {
case RESOLUTION_LEVEL_FINE:
- return android.Manifest.permission.ACCESS_FINE_LOCATION;
+ return ACCESS_FINE_LOCATION;
case RESOLUTION_LEVEL_COARSE:
- return android.Manifest.permission.ACCESS_COARSE_LOCATION;
+ return ACCESS_COARSE_LOCATION;
default:
return null;
}
}
private int getAllowedResolutionLevel(int pid, int uid) {
- if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
- pid, uid) == PERMISSION_GRANTED) {
+ if (mContext.checkPermission(ACCESS_FINE_LOCATION, pid, uid) == PERMISSION_GRANTED) {
return RESOLUTION_LEVEL_FINE;
- } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION,
- pid, uid) == PERMISSION_GRANTED) {
+ } else if (mContext.checkPermission(ACCESS_COARSE_LOCATION, pid, uid)
+ == PERMISSION_GRANTED) {
return RESOLUTION_LEVEL_COARSE;
} else {
return RESOLUTION_LEVEL_NONE;
@@ -1420,59 +1426,28 @@
return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid());
}
- private void checkResolutionLevelIsSufficientForGeofenceUse(int allowedResolutionLevel) {
- if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
- throw new SecurityException("Geofence usage requires ACCESS_FINE_LOCATION permission");
- }
+ private boolean checkCallingOrSelfLocationPermission() {
+ return mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) == PERMISSION_GRANTED
+ || mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
+ == PERMISSION_GRANTED;
}
- @GuardedBy("mLock")
- private int getMinimumResolutionLevelForProviderUseLocked(String provider) {
- if (GPS_PROVIDER.equals(provider) || PASSIVE_PROVIDER.equals(provider)) {
- // gps and passive providers require FINE permission
- return RESOLUTION_LEVEL_FINE;
- } else if (NETWORK_PROVIDER.equals(provider) || FUSED_PROVIDER.equals(provider)) {
- // network and fused providers are ok with COARSE or FINE
- return RESOLUTION_LEVEL_COARSE;
- } else {
- for (LocationProviderManager lp : mProviderManagers) {
- if (!lp.getName().equals(provider)) {
- continue;
- }
-
- ProviderProperties properties = lp.getProperties();
- if (properties != null) {
- if (properties.mRequiresSatellite) {
- // provider requiring satellites require FINE permission
- return RESOLUTION_LEVEL_FINE;
- } else if (properties.mRequiresNetwork || properties.mRequiresCell) {
- // provider requiring network and or cell require COARSE or FINE
- return RESOLUTION_LEVEL_COARSE;
- }
- }
- }
+ private void enforceCallingOrSelfLocationPermission() {
+ if (checkCallingOrSelfLocationPermission()) {
+ return;
}
- return RESOLUTION_LEVEL_FINE; // if in doubt, require FINE
+ throw new SecurityException("uid " + Binder.getCallingUid() + " does not have "
+ + ACCESS_COARSE_LOCATION + " or " + ACCESS_FINE_LOCATION + ".");
}
- @GuardedBy("mLock")
- private void checkResolutionLevelIsSufficientForProviderUseLocked(int allowedResolutionLevel,
- String providerName) {
- int requiredResolutionLevel = getMinimumResolutionLevelForProviderUseLocked(providerName);
- if (allowedResolutionLevel < requiredResolutionLevel) {
- switch (requiredResolutionLevel) {
- case RESOLUTION_LEVEL_FINE:
- throw new SecurityException("\"" + providerName + "\" location provider " +
- "requires ACCESS_FINE_LOCATION permission.");
- case RESOLUTION_LEVEL_COARSE:
- throw new SecurityException("\"" + providerName + "\" location provider " +
- "requires ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission.");
- default:
- throw new SecurityException("Insufficient permission for \"" + providerName +
- "\" location provider.");
- }
+ private void enforceCallingOrSelfPackageName(String packageName) {
+ int uid = Binder.getCallingUid();
+ if (ArrayUtils.contains(mPackageManager.getPackagesForUid(uid), packageName)) {
+ return;
}
+
+ throw new SecurityException("invalid package \"" + packageName + "\" for uid " + uid);
}
public static int resolutionLevelToOp(int allowedResolutionLevel) {
@@ -1548,7 +1523,10 @@
*/
@Override
public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
- int allowedResolutionLevel = getCallerAllowedResolutionLevel();
+ if (!checkCallingOrSelfLocationPermission()) {
+ return Collections.emptyList();
+ }
+
synchronized (mLock) {
ArrayList<String> providers = new ArrayList<>(mProviderManagers.size());
for (LocationProviderManager manager : mProviderManagers) {
@@ -1556,9 +1534,6 @@
if (FUSED_PROVIDER.equals(name)) {
continue;
}
- if (allowedResolutionLevel < getMinimumResolutionLevelForProviderUseLocked(name)) {
- continue;
- }
if (enabledOnly && !manager.isUseable()) {
continue;
}
@@ -2002,33 +1977,18 @@
return sanitizedRequest;
}
- private void checkPackageName(String packageName) {
- if (packageName == null) {
- throw new SecurityException("invalid package name: " + null);
- }
- int uid = Binder.getCallingUid();
- String[] packages = mPackageManager.getPackagesForUid(uid);
- if (packages == null) {
- throw new SecurityException("invalid UID " + uid);
- }
- for (String pkg : packages) {
- if (packageName.equals(pkg)) return;
- }
- throw new SecurityException("invalid package name: " + packageName);
- }
-
@Override
public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
PendingIntent intent, String packageName, String featureId,
String listenerIdentifier) {
Objects.requireNonNull(listenerIdentifier);
+ enforceCallingOrSelfLocationPermission();
+ enforceCallingOrSelfPackageName(packageName);
+
synchronized (mLock) {
if (request == null) request = DEFAULT_LOCATION_REQUEST;
- checkPackageName(packageName);
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
- checkResolutionLevelIsSufficientForProviderUseLocked(allowedResolutionLevel,
- request.getProvider());
WorkSource workSource = request.getWorkSource();
if (workSource != null && !workSource.isEmpty()) {
mContext.enforceCallingOrSelfPermission(
@@ -2135,7 +2095,7 @@
@Override
public void removeUpdates(ILocationListener listener, PendingIntent intent,
String packageName) {
- checkPackageName(packageName);
+ enforceCallingOrSelfPackageName(packageName);
int pid = Binder.getCallingPid();
int uid = Binder.getCallingUid();
@@ -2197,12 +2157,12 @@
@Override
public Location getLastLocation(LocationRequest r, String packageName, String featureId) {
+ enforceCallingOrSelfLocationPermission();
+ enforceCallingOrSelfPackageName(packageName);
+
synchronized (mLock) {
LocationRequest request = r != null ? r : DEFAULT_LOCATION_REQUEST;
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
- checkPackageName(packageName);
- checkResolutionLevelIsSufficientForProviderUseLocked(allowedResolutionLevel,
- request.getProvider());
// no need to sanitize this request, as only the provider name is used
final int pid = Binder.getCallingPid();
@@ -2348,7 +2308,7 @@
public boolean injectLocation(Location location) {
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to inject location");
- mContext.enforceCallingPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
+ mContext.enforceCallingPermission(ACCESS_FINE_LOCATION,
"Access Fine Location permission not granted to inject Location");
synchronized (mLock) {
@@ -2374,17 +2334,14 @@
String packageName, String featureId, String listenerIdentifier) {
Objects.requireNonNull(listenerIdentifier);
+ mContext.enforceCallingOrSelfPermission(ACCESS_FINE_LOCATION, null);
+ enforceCallingOrSelfPackageName(packageName);
+
if (request == null) request = DEFAULT_LOCATION_REQUEST;
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
- checkResolutionLevelIsSufficientForGeofenceUse(allowedResolutionLevel);
if (intent == null) {
throw new IllegalArgumentException("invalid pending intent: " + null);
}
- checkPackageName(packageName);
- synchronized (mLock) {
- checkResolutionLevelIsSufficientForProviderUseLocked(allowedResolutionLevel,
- request.getProvider());
- }
// Require that caller can manage given document
boolean callerHasLocationHardwarePermission =
mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE)
@@ -2430,7 +2387,7 @@
if (intent == null) {
throw new IllegalArgumentException("invalid pending intent: " + null);
}
- checkPackageName(packageName);
+ enforceCallingOrSelfPackageName(packageName);
if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
@@ -2517,36 +2474,30 @@
@Override
public boolean sendExtraCommand(String providerName, String command, Bundle extras) {
- if (providerName == null) {
- // throw NullPointerException to remain compatible with previous implementation
- throw new NullPointerException();
- }
+ Objects.requireNonNull(providerName);
+ Objects.requireNonNull(command);
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS, null);
+ enforceCallingOrSelfLocationPermission();
- synchronized (mLock) {
- checkResolutionLevelIsSufficientForProviderUseLocked(getCallerAllowedResolutionLevel(),
- providerName);
+ mLocationUsageLogger.logLocationApiUsage(
+ LocationStatsEnums.USAGE_STARTED,
+ LocationStatsEnums.API_SEND_EXTRA_COMMAND,
+ providerName);
- mLocationUsageLogger.logLocationApiUsage(
- LocationStatsEnums.USAGE_STARTED,
- LocationStatsEnums.API_SEND_EXTRA_COMMAND,
- providerName);
-
- LocationProviderManager manager = getLocationProviderManager(providerName);
- if (manager != null) {
- manager.sendExtraCommand(Binder.getCallingUid(), Binder.getCallingPid(), command,
- extras);
- }
-
- mLocationUsageLogger.logLocationApiUsage(
- LocationStatsEnums.USAGE_ENDED,
- LocationStatsEnums.API_SEND_EXTRA_COMMAND,
- providerName);
-
- return true;
+ LocationProviderManager manager = getLocationProviderManager(providerName);
+ if (manager != null) {
+ manager.sendExtraCommand(Binder.getCallingUid(), Binder.getCallingPid(), command,
+ extras);
}
+
+ mLocationUsageLogger.logLocationApiUsage(
+ LocationStatsEnums.USAGE_ENDED,
+ LocationStatsEnums.API_SEND_EXTRA_COMMAND,
+ providerName);
+
+ return true;
}
@Override
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 7b4fd37..b464422 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -29,6 +29,7 @@
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
+import android.os.Process;
import android.os.SystemProperties;
import android.provider.DeviceConfig;
import android.text.TextUtils;
@@ -36,6 +37,7 @@
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.LongArrayQueue;
+import android.util.MathUtils;
import android.util.Slog;
import android.util.Xml;
@@ -117,6 +119,12 @@
// Whether explicit health checks are enabled or not
private static final boolean DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED = true;
+ @VisibleForTesting
+ static final int DEFAULT_BOOT_LOOP_TRIGGER_COUNT = 5;
+ static final long DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS = TimeUnit.MINUTES.toMillis(10);
+ private static final String PROP_RESCUE_BOOT_COUNT = "sys.rescue_boot_count";
+ private static final String PROP_RESCUE_BOOT_START = "sys.rescue_boot_start";
+
private long mNumberOfNativeCrashPollsRemaining;
private static final int DB_VERSION = 1;
@@ -152,6 +160,7 @@
private final Runnable mSyncStateWithScheduledReason = this::syncStateWithScheduledReason;
private final Runnable mSaveToFile = this::saveToFile;
private final SystemClock mSystemClock;
+ private final BootThreshold mBootThreshold;
@GuardedBy("mLock")
private boolean mIsPackagesReady;
// Flag to control whether explicit health checks are supported or not
@@ -169,6 +178,7 @@
@FunctionalInterface
@VisibleForTesting
interface SystemClock {
+ // TODO: Add elapsedRealtime to this interface
long uptimeMillis();
}
@@ -198,6 +208,8 @@
mConnectivityModuleConnector = connectivityModuleConnector;
mSystemClock = clock;
mNumberOfNativeCrashPollsRemaining = NUMBER_OF_NATIVE_CRASH_POLLS;
+ mBootThreshold = new BootThreshold(DEFAULT_BOOT_LOOP_TRIGGER_COUNT,
+ DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS);
loadFromFile();
sPackageWatchdog = this;
}
@@ -411,6 +423,35 @@
}
}
+ /**
+ * Called when the system server boots. If the system server is detected to be in a boot loop,
+ * query each observer and perform the mitigation action with the lowest user impact.
+ */
+ public void noteBoot() {
+ synchronized (mLock) {
+ if (mBootThreshold.incrementAndTest()) {
+ mBootThreshold.reset();
+ PackageHealthObserver currentObserverToNotify = null;
+ int currentObserverImpact = Integer.MAX_VALUE;
+ for (int i = 0; i < mAllObservers.size(); i++) {
+ final ObserverInternal observer = mAllObservers.valueAt(i);
+ PackageHealthObserver registeredObserver = observer.registeredObserver;
+ if (registeredObserver != null) {
+ int impact = registeredObserver.onBootLoop();
+ if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE
+ && impact < currentObserverImpact) {
+ currentObserverToNotify = registeredObserver;
+ currentObserverImpact = impact;
+ }
+ }
+ }
+ if (currentObserverToNotify != null) {
+ currentObserverToNotify.executeBootLoopMitigation();
+ }
+ }
+ }
+ }
+
// TODO(b/120598832): Optimize write? Maybe only write a separate smaller file? Also
// avoid holding lock?
// This currently adds about 7ms extra to shutdown thread
@@ -519,6 +560,22 @@
boolean execute(@Nullable VersionedPackage versionedPackage,
@FailureReasons int failureReason);
+
+ /**
+ * Called when the system server has booted several times within a window of time, defined
+ * by {@link #mBootThreshold}
+ */
+ default @PackageHealthObserverImpact int onBootLoop() {
+ return PackageHealthObserverImpact.USER_IMPACT_NONE;
+ }
+
+ /**
+ * Executes mitigation for {@link #onBootLoop}
+ */
+ default boolean executeBootLoopMitigation() {
+ return false;
+ }
+
// TODO(b/120598832): Ensure uniqueness?
/**
* Identifier for the observer, should not change across device updates otherwise the
@@ -1367,4 +1424,62 @@
return value > 0 ? value : Long.MAX_VALUE;
}
}
+
+ /**
+ * Handles the thresholding logic for system server boots.
+ */
+ static class BootThreshold {
+
+ private final int mBootTriggerCount;
+ private final long mTriggerWindow;
+
+ BootThreshold(int bootTriggerCount, long triggerWindow) {
+ this.mBootTriggerCount = bootTriggerCount;
+ this.mTriggerWindow = triggerWindow;
+ }
+
+ public void reset() {
+ setStart(0);
+ setCount(0);
+ }
+
+ private int getCount() {
+ return SystemProperties.getInt(PROP_RESCUE_BOOT_COUNT, 0);
+ }
+
+ private void setCount(int count) {
+ SystemProperties.set(PROP_RESCUE_BOOT_COUNT, Integer.toString(count));
+ }
+
+ public long getStart() {
+ return SystemProperties.getLong(PROP_RESCUE_BOOT_START, 0);
+ }
+
+ public void setStart(long start) {
+ final long now = android.os.SystemClock.elapsedRealtime();
+ final long newStart = MathUtils.constrain(start, 0, now);
+ SystemProperties.set(PROP_RESCUE_BOOT_START, Long.toString(newStart));
+ }
+
+ /** Increments the boot counter, and returns whether the device is bootlooping. */
+ public boolean incrementAndTest() {
+ final long now = android.os.SystemClock.elapsedRealtime();
+ if (now - getStart() < 0) {
+ Slog.e(TAG, "Window was less than zero. Resetting start to current time.");
+ setStart(now);
+ }
+ final long window = now - getStart();
+ if (window >= mTriggerWindow) {
+ setCount(1);
+ setStart(now);
+ return false;
+ } else {
+ int count = getCount() + 1;
+ setCount(count);
+ EventLogTags.writeRescueNote(Process.ROOT_UID, count, window);
+ return count >= mBootTriggerCount;
+ }
+ }
+
+ }
}
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index 3dafc64..e8e3b39d 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -27,17 +27,16 @@
import android.os.Build;
import android.os.Environment;
import android.os.FileUtils;
+import android.os.Process;
import android.os.RecoverySystem;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
-import android.text.format.DateUtils;
import android.util.ExceptionUtils;
import android.util.Log;
import android.util.MathUtils;
import android.util.Slog;
-import android.util.SparseArray;
import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
@@ -80,12 +79,6 @@
static final int LEVEL_FACTORY_RESET = 4;
@VisibleForTesting
static final String PROP_RESCUE_BOOT_COUNT = "sys.rescue_boot_count";
- /**
- * The boot trigger window size must always be greater than Watchdog's deadlock timeout
- * {@link Watchdog#DEFAULT_TIMEOUT}.
- */
- @VisibleForTesting
- static final long BOOT_TRIGGER_WINDOW_MILLIS = 600 * DateUtils.SECOND_IN_MILLIS;
@VisibleForTesting
static final String TAG = "RescueParty";
@@ -93,18 +86,11 @@
private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue";
- private static final String PROP_RESCUE_BOOT_START = "sys.rescue_boot_start";
private static final String PROP_VIRTUAL_DEVICE = "ro.hardware.virtual_device";
private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT
| ApplicationInfo.FLAG_SYSTEM;
-
- /** Threshold for boot loops */
- private static final Threshold sBoot = new BootThreshold();
- /** Threshold for app crash loops */
- private static SparseArray<Threshold> sApps = new SparseArray<>();
-
/** Register the Rescue Party observer as a Package Watchdog health observer */
public static void registerHealthObserver(Context context) {
PackageWatchdog.getInstance(context).registerHealthObserver(
@@ -141,19 +127,6 @@
}
/**
- * Take note of a boot event. If we notice too many of these events
- * happening in rapid succession, we'll send out a rescue party.
- */
- public static void noteBoot(Context context) {
- if (isDisabled()) return;
- if (sBoot.incrementAndTest()) {
- sBoot.reset();
- incrementRescueLevel(sBoot.uid);
- executeRescueLevel(context);
- }
- }
-
- /**
* Check if we're currently attempting to reboot for a factory reset.
*/
public static boolean isAttemptingFactoryReset() {
@@ -170,11 +143,6 @@
}
@VisibleForTesting
- static void resetAllThresholds() {
- sBoot.reset();
- }
-
- @VisibleForTesting
static long getElapsedRealtime() {
return SystemClock.elapsedRealtime();
}
@@ -187,6 +155,14 @@
}
/**
+ * Get the current rescue level.
+ */
+ private static int getRescueLevel() {
+ return MathUtils.constrain(SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE),
+ LEVEL_NONE, LEVEL_FACTORY_RESET);
+ }
+
+ /**
* Escalate to the next rescue level. After incrementing the level you'll
* probably want to call {@link #executeRescueLevel(Context)}.
*/
@@ -366,90 +342,29 @@
}
@Override
+ public int onBootLoop() {
+ if (isDisabled()) {
+ return PackageHealthObserverImpact.USER_IMPACT_NONE;
+ }
+ return mapRescueLevelToUserImpact(getRescueLevel());
+ }
+
+ @Override
+ public boolean executeBootLoopMitigation() {
+ if (isDisabled()) {
+ return false;
+ }
+ incrementRescueLevel(Process.ROOT_UID);
+ executeRescueLevel(mContext);
+ return true;
+ }
+
+ @Override
public String getName() {
return NAME;
}
}
- /**
- * Threshold that can be triggered if a number of events occur within a
- * window of time.
- */
- private abstract static class Threshold {
- public abstract int getCount();
- public abstract void setCount(int count);
- public abstract long getStart();
- public abstract void setStart(long start);
-
- private final int uid;
- private final int triggerCount;
- private final long triggerWindow;
-
- public Threshold(int uid, int triggerCount, long triggerWindow) {
- this.uid = uid;
- this.triggerCount = triggerCount;
- this.triggerWindow = triggerWindow;
- }
-
- public void reset() {
- setCount(0);
- setStart(0);
- }
-
- /**
- * @return if this threshold has been triggered
- */
- public boolean incrementAndTest() {
- final long now = getElapsedRealtime();
- final long window = now - getStart();
- if (window > triggerWindow) {
- setCount(1);
- setStart(now);
- return false;
- } else {
- int count = getCount() + 1;
- setCount(count);
- EventLogTags.writeRescueNote(uid, count, window);
- Slog.w(TAG, "Noticed " + count + " events for UID " + uid + " in last "
- + (window / 1000) + " sec");
- return (count >= triggerCount);
- }
- }
- }
-
- /**
- * Specialization of {@link Threshold} for monitoring boot events. It stores
- * counters in system properties for robustness.
- */
- private static class BootThreshold extends Threshold {
- public BootThreshold() {
- // We're interested in TRIGGER_COUNT events in any
- // BOOT_TRIGGER_WINDOW_MILLIS second period; this window is super relaxed because
- // booting can take a long time if forced to dexopt things.
- super(android.os.Process.ROOT_UID, TRIGGER_COUNT, BOOT_TRIGGER_WINDOW_MILLIS);
- }
-
- @Override
- public int getCount() {
- return SystemProperties.getInt(PROP_RESCUE_BOOT_COUNT, 0);
- }
-
- @Override
- public void setCount(int count) {
- SystemProperties.set(PROP_RESCUE_BOOT_COUNT, Integer.toString(count));
- }
-
- @Override
- public long getStart() {
- return SystemProperties.getLong(PROP_RESCUE_BOOT_START, 0);
- }
-
- @Override
- public void setStart(long start) {
- SystemProperties.set(PROP_RESCUE_BOOT_START, Long.toString(start));
- }
- }
-
private static int[] getAllUserIds() {
int[] userIds = { UserHandle.USER_SYSTEM };
try {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 396b977..bcc3bdb 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -44,6 +44,7 @@
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
+import static com.android.server.storage.StorageUserConnection.REMOTE_TIMEOUT_SECONDS;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -1985,16 +1986,28 @@
Slog.i(TAG, "Mounting volume " + vol);
mVold.mount(vol.id, vol.mountFlags, vol.mountUserId, new IVoldMountCallback.Stub() {
@Override
- public boolean onVolumeChecking(FileDescriptor deviceFd, String path,
+ public boolean onVolumeChecking(FileDescriptor fd, String path,
String internalPath) {
vol.path = path;
vol.internalPath = internalPath;
+ ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd);
try {
- mStorageSessionController.onVolumeMount(deviceFd, vol);
+ mStorageSessionController.onVolumeMount(pfd, vol);
return true;
} catch (ExternalStorageServiceException e) {
- Slog.i(TAG, "Failed to mount volume " + vol, e);
+ Slog.e(TAG, "Failed to mount volume " + vol, e);
+
+ Slog.i(TAG, "Scheduling reset in one minute");
+ mHandler.removeMessages(H_RESET);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(H_RESET),
+ TimeUnit.SECONDS.toMillis(REMOTE_TIMEOUT_SECONDS * 2));
return false;
+ } finally {
+ try {
+ pfd.close();
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to close FUSE device fd", e);
+ }
}
}
});
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 37026fd..a98b83b 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -402,7 +402,7 @@
}
public ParcelFileDescriptor getStatisticsStream() {
- mContext.enforceCallingPermission(
+ mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BATTERY_STATS, null);
//Slog.i("foo", "SENDING BATTERY INFO:");
//mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index c1ab551..d66aec5 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -451,15 +451,6 @@
&& !isLingering();
}
- /**
- * Returns whether this network is currently suspended. A network is suspended if it is still
- * connected but data temporarily fails to transfer. See {@link NetworkInfo.State#SUSPENDED}
- * and {@link NetworkCapabilities#NET_CAPABILITY_NOT_SUSPENDED}.
- */
- public boolean isSuspended() {
- return networkInfo.getState() == NetworkInfo.State.SUSPENDED;
- }
-
// Does this network satisfy request?
public boolean satisfies(NetworkRequest request) {
return created &&
diff --git a/services/core/java/com/android/server/integrity/IntegrityFileManager.java b/services/core/java/com/android/server/integrity/IntegrityFileManager.java
index d763bae..fffe7d9 100644
--- a/services/core/java/com/android/server/integrity/IntegrityFileManager.java
+++ b/services/core/java/com/android/server/integrity/IntegrityFileManager.java
@@ -185,6 +185,10 @@
&& tmpDir.renameTo(mStagingDir))) {
throw new IOException("Error switching staging/rules directory");
}
+
+ for (File file : mStagingDir.listFiles()) {
+ file.delete();
+ }
}
}
diff --git a/services/core/java/com/android/server/integrity/model/IndexingFileConstants.java b/services/core/java/com/android/server/integrity/model/IndexingFileConstants.java
index d21febb..0c4052a 100644
--- a/services/core/java/com/android/server/integrity/model/IndexingFileConstants.java
+++ b/services/core/java/com/android/server/integrity/model/IndexingFileConstants.java
@@ -18,9 +18,9 @@
/** A helper class containing special indexing file constants. */
public final class IndexingFileConstants {
- // We empirically experimented with different block sizes and identified that 250 is in the
+ // We empirically experimented with different block sizes and identified that 50 is in the
// optimal range of efficient computation.
- public static final int INDEXING_BLOCK_SIZE = 250;
+ public static final int INDEXING_BLOCK_SIZE = 50;
public static final String START_INDEXING_KEY = "START_KEY";
public static final String END_INDEXING_KEY = "END_KEY";
diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
index 03392ab..87eee4e 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
@@ -28,9 +28,9 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
-import java.util.TreeSet;
import java.util.stream.Collectors;
/** Helper class to identify the necessary indexes that needs to be read. */
@@ -93,19 +93,28 @@
return keyToIndexMap;
}
- private RuleIndexRange searchIndexingKeysRangeContainingKey(
+ private static RuleIndexRange searchIndexingKeysRangeContainingKey(
LinkedHashMap<String, Integer> indexMap, String searchedKey) {
- TreeSet<String> keyTreeSet =
- indexMap.keySet().stream()
- .filter(key -> !key.matches(START_INDEXING_KEY) && !key.matches(
- END_INDEXING_KEY))
- .collect(Collectors.toCollection(TreeSet::new));
-
- String minIndex = keyTreeSet.floor(searchedKey);
- String maxIndex = keyTreeSet.higher(searchedKey);
-
+ List<String> keys = indexMap.keySet().stream().collect(Collectors.toList());
+ List<String> identifiedKeyRange =
+ searchKeysRangeContainingKey(keys, searchedKey, 0, keys.size() - 1);
return new RuleIndexRange(
- indexMap.get(minIndex == null ? START_INDEXING_KEY : minIndex),
- indexMap.get(maxIndex == null ? END_INDEXING_KEY : maxIndex));
+ indexMap.get(identifiedKeyRange.get(0)), indexMap.get(identifiedKeyRange.get(1)));
+ }
+
+ private static List<String> searchKeysRangeContainingKey(
+ List<String> sortedKeyList, String key, int startIndex, int endIndex) {
+ if (endIndex - startIndex == 1) {
+ return Arrays.asList(sortedKeyList.get(startIndex), sortedKeyList.get(endIndex));
+ }
+
+ int midKeyIndex = startIndex + ((endIndex - startIndex) / 2);
+ String midKey = sortedKeyList.get(midKeyIndex);
+
+ if (key.compareTo(midKey) >= 0) {
+ return searchKeysRangeContainingKey(sortedKeyList, key, midKeyIndex, endIndex);
+ } else {
+ return searchKeysRangeContainingKey(sortedKeyList, key, startIndex, midKeyIndex);
+ }
}
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index 0f8561e..4943c25 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -24,6 +24,7 @@
import android.app.ActivityManager;
import android.app.admin.PasswordMetrics;
import android.os.ShellCommand;
+import android.text.TextUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
@@ -195,6 +196,9 @@
}
private LockscreenCredential getOldCredential() {
+ if (TextUtils.isEmpty(mOld)) {
+ return LockscreenCredential.createNone();
+ }
if (mLockPatternUtils.isLockPasswordEnabled(mCurrentUserId)) {
final int quality = mLockPatternUtils.getKeyguardStoredPasswordQuality(mCurrentUserId);
if (LockPatternUtils.isQualityAlphabeticPassword(quality)) {
@@ -202,12 +206,15 @@
} else {
return LockscreenCredential.createPin(mOld);
}
- } else if (mLockPatternUtils.isLockPatternEnabled(mCurrentUserId)) {
+ }
+ if (mLockPatternUtils.isLockPatternEnabled(mCurrentUserId)) {
return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
mOld.getBytes()));
- } else {
- return LockscreenCredential.createNone();
}
+ // User supplied some old credential but the device has neither password nor pattern,
+ // so just return a password credential (and let it be rejected during LSS verification)
+ return LockscreenCredential.createPassword(mOld);
+
}
private boolean runSetPattern() {
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 408c1c9..b186771 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -49,8 +49,7 @@
mCallback = callback;
}
- public abstract void requestCreateSession(String packageName, String routeId,
- String routeType, long requestId);
+ public abstract void requestCreateSession(String packageName, String routeId, long requestId);
public abstract void releaseSession(String sessionId);
public abstract void selectRoute(String sessionId, String routeId);
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index 3840d02..4b992be 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -73,11 +73,9 @@
}
@Override
- public void requestCreateSession(String packageName, String routeId, String routeType,
- long requestId) {
+ public void requestCreateSession(String packageName, String routeId, long requestId) {
if (mConnectionReady) {
- mActiveConnection.requestCreateSession(packageName, routeId, routeType,
- requestId);
+ mActiveConnection.requestCreateSession(packageName, routeId, requestId);
updateBinding();
}
}
@@ -429,11 +427,9 @@
mClient.dispose();
}
- public void requestCreateSession(String packageName, String routeId, String routeType,
- long requestId) {
+ public void requestCreateSession(String packageName, String routeId, long requestId) {
try {
- mProvider.requestCreateSession(packageName, routeId,
- routeType, requestId);
+ mProvider.requestCreateSession(packageName, routeId, requestId);
} catch (RemoteException ex) {
Slog.e(TAG, "Failed to deliver request to create a session.", ex);
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 161afb5..45d50b3 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -178,18 +178,14 @@
}
public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route,
- String routeFeature, int requestId) {
+ int requestId) {
Objects.requireNonNull(client, "client must not be null");
Objects.requireNonNull(route, "route must not be null");
- if (TextUtils.isEmpty(routeFeature)) {
- throw new IllegalArgumentException("routeFeature must not be empty");
- }
final long token = Binder.clearCallingIdentity();
-
try {
synchronized (mLock) {
- requestCreateSessionLocked(client, route, routeFeature, requestId);
+ requestCreateSessionLocked(client, route, requestId);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -450,7 +446,7 @@
}
private void requestCreateSessionLocked(@NonNull IMediaRouter2Client client,
- @NonNull MediaRoute2Info route, @NonNull String routeFeature, long requestId) {
+ @NonNull MediaRoute2Info route, long requestId) {
final IBinder binder = client.asBinder();
final Client2Record clientRecord = mAllClientRecords.get(binder);
@@ -462,8 +458,7 @@
if (clientRecord != null) {
clientRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::requestCreateSessionOnHandler,
- clientRecord.mUserRecord.mHandler,
- clientRecord, route, routeFeature, requestId));
+ clientRecord.mUserRecord.mHandler, clientRecord, route, requestId));
}
}
@@ -624,7 +619,7 @@
if (clientRecord != null && managerRecord.mTrusted) {
//TODO: select route feature properly
requestCreateSessionLocked(clientRecord.mClient, route,
- route.getFeatures().get(0), uniqueRequestId);
+ uniqueRequestId);
}
}
}
@@ -985,7 +980,7 @@
}
private void requestCreateSessionOnHandler(Client2Record clientRecord,
- MediaRoute2Info route, String routeFeature, long requestId) {
+ MediaRoute2Info route, long requestId) {
final MediaRoute2Provider provider = findProvider(route.getProviderId());
if (provider == null) {
@@ -995,20 +990,13 @@
return;
}
- if (!route.getFeatures().contains(routeFeature)) {
- Slog.w(TAG, "Ignoring session creation request since the given route=" + route
- + " doesn't support the given feature=" + routeFeature);
- notifySessionCreationFailed(clientRecord, toClientRequestId(requestId));
- return;
- }
-
// TODO: Apply timeout for each request (How many seconds should we wait?)
- SessionCreationRequest request = new SessionCreationRequest(
- clientRecord, route, routeFeature, requestId);
+ SessionCreationRequest request =
+ new SessionCreationRequest(clientRecord, route, requestId);
mSessionCreationRequests.add(request);
provider.requestCreateSession(clientRecord.mPackageName, route.getOriginalId(),
- routeFeature, requestId);
+ requestId);
}
private void selectRouteOnHandler(@NonNull Client2Record clientRecord,
@@ -1173,15 +1161,11 @@
}
String originalRouteId = matchingRequest.mRoute.getId();
- String originalRouteFeature = matchingRequest.mRouteFeature;
Client2Record client2Record = matchingRequest.mClientRecord;
- if (!sessionInfo.getSelectedRoutes().contains(originalRouteId)
- || !TextUtils.equals(originalRouteFeature,
- sessionInfo.getRouteFeature())) {
+ if (!sessionInfo.getSelectedRoutes().contains(originalRouteId)) {
Slog.w(TAG, "Created session doesn't match the original request."
+ " originalRouteId=" + originalRouteId
- + ", originalRouteFeature=" + originalRouteFeature
+ ", requestId=" + requestId + ", sessionInfo=" + sessionInfo);
notifySessionCreationFailed(matchingRequest.mClientRecord,
toClientRequestId(requestId));
@@ -1470,15 +1454,12 @@
final class SessionCreationRequest {
public final Client2Record mClientRecord;
public final MediaRoute2Info mRoute;
- public final String mRouteFeature;
public final long mRequestId;
SessionCreationRequest(@NonNull Client2Record clientRecord,
- @NonNull MediaRoute2Info route,
- @NonNull String routeFeature, long requestId) {
+ @NonNull MediaRoute2Info route, long requestId) {
mClientRecord = clientRecord;
mRoute = route;
- mRouteFeature = routeFeature;
mRequestId = requestId;
}
}
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index c80a898..aad9636 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -460,8 +460,8 @@
// Binder call
@Override
public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route,
- String routeType, int requestId) {
- mService2.requestCreateSession(client, route, routeType, requestId);
+ int requestId) {
+ mService2.requestCreateSession(client, route, requestId);
}
// Binder call
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 6695227..3759ba9 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -96,8 +96,7 @@
}
@Override
- public void requestCreateSession(String packageName, String routeId, String routeType,
- long requestId) {
+ public void requestCreateSession(String packageName, String routeId, long requestId) {
// Do nothing
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 6331dd4..b1c38d1 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -400,10 +400,10 @@
} finally {
IoUtils.closeQuietly(fis);
}
- // After all of the sessions were loaded, they are ready to be sealed and validated
+ // Re-sealing the sealed sessions.
for (int i = 0; i < mSessions.size(); ++i) {
PackageInstallerSession session = mSessions.valueAt(i);
- session.sealAndValidateIfNecessary();
+ session.sealIfNecessary();
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 165bdeb..71555c9 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1374,15 +1374,13 @@
}
/**
- * 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.
+ * If session should be sealed, then it's sealed to prevent further modification.
+ * If the session can't be sealed then it's destroyed.
*
* <p> This is meant to be called after all of the sessions are loaded and added to
* PackageInstallerService
*/
- void sealAndValidateIfNecessary() {
+ void sealIfNecessary() {
synchronized (mLock) {
if (!mShouldBeSealed || isStagedAndInTerminalState()) {
return;
@@ -1391,9 +1389,7 @@
List<PackageInstallerSession> childSessions = getChildSessions();
synchronized (mLock) {
try {
- sealAndValidateLocked(childSessions);
- } catch (StreamingException e) {
- Slog.e(TAG, "Streaming failed", e);
+ sealLocked(childSessions);
} catch (PackageManagerException e) {
Slog.e(TAG, "Package not valid", e);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 410017a..d0f91c2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1534,7 +1534,7 @@
final @Nullable String mAppPredictionServicePackage;
final @Nullable String mIncidentReportApproverPackage;
final @Nullable String[] mTelephonyPackages;
- final @NonNull String mServicesSystemSharedLibraryPackageName;
+ final @NonNull String mServicesExtensionPackageName;
final @NonNull String mSharedSystemSharedLibraryPackageName;
private final PackageUsage mPackageUsage = new PackageUsage();
@@ -3303,9 +3303,7 @@
} else {
mIntentFilterVerifier = null;
}
- mServicesSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr(
- PackageManager.SYSTEM_SHARED_LIBRARY_SERVICES,
- SharedLibraryInfo.VERSION_UNDEFINED);
+ mServicesExtensionPackageName = getRequiredServicesExtensionPackageLPr();
mSharedSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr(
PackageManager.SYSTEM_SHARED_LIBRARY_SHARED,
SharedLibraryInfo.VERSION_UNDEFINED);
@@ -3315,7 +3313,7 @@
mRequiredUninstallerPackage = null;
mIntentFilterVerifierComponent = null;
mIntentFilterVerifier = null;
- mServicesSystemSharedLibraryPackageName = null;
+ mServicesExtensionPackageName = null;
mSharedSystemSharedLibraryPackageName = null;
}
// PermissionController hosts default permission granting and role management, so it's a
@@ -3745,6 +3743,19 @@
}
}
+ @NonNull
+ private String getRequiredServicesExtensionPackageLPr() {
+ String servicesExtensionPackage =
+ ensureSystemPackageName(
+ mContext.getString(R.string.config_servicesExtensionPackage));
+ if (TextUtils.isEmpty(servicesExtensionPackage)) {
+ throw new RuntimeException(
+ "Required services extension package is missing, check "
+ + "config_servicesExtensionPackage.");
+ }
+ return servicesExtensionPackage;
+ }
+
private @NonNull String getRequiredInstallerLPr() {
final Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
@@ -5467,7 +5478,7 @@
public @NonNull String getServicesSystemSharedLibraryPackageName() {
// allow instant applications
synchronized (mLock) {
- return mServicesSystemSharedLibraryPackageName;
+ return mServicesExtensionPackageName;
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 6ee8648..66a2b01 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3395,6 +3395,9 @@
private void dispatchUserAddedIntent(@NonNull UserInfo userInfo) {
Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
+ // Also, add the UserHandle for mainline modules which can't use the @hide
+ // EXTRA_USER_HANDLE.
+ addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userInfo.id));
mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
android.Manifest.permission.MANAGE_USERS);
MetricsLogger.count(mContext, userInfo.isGuest() ? TRON_GUEST_CREATED
@@ -3678,9 +3681,12 @@
// wiping the user's system directory and removing from the user list
long ident = Binder.clearCallingIdentity();
try {
- Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
- addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- mContext.sendOrderedBroadcastAsUser(addedIntent, UserHandle.ALL,
+ Intent removedIntent = new Intent(Intent.ACTION_USER_REMOVED);
+ removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ // Also, add the UserHandle for mainline modules which can't use the @hide
+ // EXTRA_USER_HANDLE.
+ removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId));
+ mContext.sendOrderedBroadcastAsUser(removedIntent, UserHandle.ALL,
android.Manifest.permission.MANAGE_USERS,
new BroadcastReceiver() {
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 7d0072a..5c0dd9a 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -360,7 +360,7 @@
*/
boolean enableForPackageInApex(String packageName, long installedVersion,
int rollbackDataPolicy) {
- // TODO(b/142712057): Extract the new version number of apk-in-apex
+ // TODO(b/147666157): Extract the new version number of apk-in-apex
// The new version for the apk-in-apex is set to 0 for now. If the package is then further
// updated via non-staged install flow, then RollbackManagerServiceImpl#onPackageReplaced()
// will be called and this rollback will be deleted. Other ways of package update have not
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index eefcde6..de48939 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -43,6 +43,7 @@
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.Process;
import android.os.SystemClock;
@@ -78,6 +79,7 @@
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -134,6 +136,7 @@
private final Context mContext;
private final HandlerThread mHandlerThread;
+ private final Executor mExecutor;
private final Installer mInstaller;
private final RollbackPackageHealthObserver mPackageHealthObserver;
private final AppDataRollbackHelper mAppDataRollbackHelper;
@@ -173,6 +176,7 @@
mHandlerThread = new HandlerThread("RollbackManagerServiceHandler");
mHandlerThread.start();
Watchdog.getInstance().addThread(getHandler(), HANDLER_THREAD_TIMEOUT_DURATION_MILLIS);
+ mExecutor = new HandlerExecutor(getHandler());
for (UserInfo userInfo : UserManager.get(mContext).getUsers(true)) {
registerUserCallbacks(userInfo.getUserHandle());
@@ -409,7 +413,6 @@
CountDownLatch latch = new CountDownLatch(1);
getHandler().post(() -> {
- updateRollbackLifetimeDurationInMillis();
synchronized (mLock) {
mRollbacks.clear();
mRollbacks.addAll(mRollbackStore.loadRollbacks());
@@ -520,11 +523,13 @@
@AnyThread
void onBootCompleted() {
- getHandler().post(() -> updateRollbackLifetimeDurationInMillis());
- // Also posts to handler thread
- scheduleExpiration(0);
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
+ mExecutor, properties -> updateRollbackLifetimeDurationInMillis());
getHandler().post(() -> {
+ updateRollbackLifetimeDurationInMillis();
+ runExpiration();
+
// Check to see if any rollback-enabled staged sessions or staged
// rollback sessions been applied.
List<Rollback> enabling = new ArrayList<>();
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index b9ef7b3..6686de9 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -179,10 +179,10 @@
// Use the version of the metadata package that was installed before
// we rolled back for logging purposes.
- VersionedPackage oldModuleMetadataPackage = null;
+ VersionedPackage oldLogPackage = null;
for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
if (packageRollback.getPackageName().equals(moduleMetadataPackageName)) {
- oldModuleMetadataPackage = packageRollback.getVersionRolledBackFrom();
+ oldLogPackage = packageRollback.getVersionRolledBackFrom();
break;
}
}
@@ -194,13 +194,13 @@
return;
}
if (sessionInfo.isStagedSessionApplied()) {
- logEvent(oldModuleMetadataPackage,
+ 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(oldModuleMetadataPackage,
+ logEvent(oldLogPackage,
StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, "");
}
@@ -213,6 +213,23 @@
if (packageRollback.getVersionRolledBackFrom().equals(failedPackage)) {
return rollback;
}
+ // TODO(b/147666157): Extract version number of apk-in-apex so that we don't have
+ // to rely on complicated reasoning as below
+
+ // Due to b/147666157, for apk in apex, we do not know the version we are rolling
+ // back from. But if a package X is embedded in apex A exclusively (not embedded in
+ // any other apex), which is not guaranteed, then it is sufficient to check only
+ // package names here, as the version of failedPackage and the PackageRollbackInfo
+ // can't be different. If failedPackage has a higher version, then it must have
+ // been updated somehow. There are two ways: it was updated by an update of apex A
+ // or updated directly as apk. In both cases, this rollback would have gotten
+ // expired when onPackageReplaced() was called. Since the rollback exists, it has
+ // same version as failedPackage.
+ if (packageRollback.isApkInApex()
+ && packageRollback.getVersionRolledBackFrom().getPackageName()
+ .equals(failedPackage.getPackageName())) {
+ return rollback;
+ }
}
}
return null;
@@ -245,12 +262,12 @@
}
private BroadcastReceiver listenForStagedSessionReady(RollbackManager rollbackManager,
- int rollbackId, @Nullable VersionedPackage moduleMetadataPackage) {
+ int rollbackId, @Nullable VersionedPackage logPackage) {
BroadcastReceiver sessionUpdatedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
handleStagedSessionChange(rollbackManager,
- rollbackId, this /* BroadcastReceiver */, moduleMetadataPackage);
+ rollbackId, this /* BroadcastReceiver */, logPackage);
}
};
IntentFilter sessionUpdatedFilter =
@@ -260,7 +277,7 @@
}
private void handleStagedSessionChange(RollbackManager rollbackManager, int rollbackId,
- BroadcastReceiver listener, @Nullable VersionedPackage moduleMetadataPackage) {
+ BroadcastReceiver listener, @Nullable VersionedPackage logPackage) {
PackageInstaller packageInstaller =
mContext.getPackageManager().getPackageInstaller();
List<RollbackInfo> recentRollbacks =
@@ -274,15 +291,19 @@
packageInstaller.getSessionInfo(sessionId);
if (sessionInfo.isStagedSessionReady() && markStagedSessionHandled(rollbackId)) {
mContext.unregisterReceiver(listener);
- saveLastStagedRollbackId(rollbackId);
- logEvent(moduleMetadataPackage,
+ 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);
+ }
+ logEvent(logPackage,
StatsLog
.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED,
WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN,
"");
} else if (sessionInfo.isStagedSessionFailed()
&& markStagedSessionHandled(rollbackId)) {
- logEvent(moduleMetadataPackage,
+ logEvent(logPackage,
StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN,
"");
@@ -362,12 +383,12 @@
}
}
- private static void logEvent(@Nullable VersionedPackage moduleMetadataPackage, int type,
+ private static void logEvent(@Nullable VersionedPackage logPackage, int type,
int rollbackReason, @NonNull String failingPackageName) {
Slog.i(TAG, "Watchdog event occurred of type: " + rollbackTypeToString(type));
- if (moduleMetadataPackage != null) {
- StatsLog.logWatchdogRollbackOccurred(type, moduleMetadataPackage.getPackageName(),
- moduleMetadataPackage.getVersionCode(), rollbackReason, failingPackageName);
+ if (logPackage != null) {
+ StatsLog.logWatchdogRollbackOccurred(type, logPackage.getPackageName(),
+ logPackage.getVersionCode(), rollbackReason, failingPackageName);
}
}
diff --git a/services/core/java/com/android/server/stats/IonMemoryUtil.java b/services/core/java/com/android/server/stats/pull/IonMemoryUtil.java
similarity index 93%
rename from services/core/java/com/android/server/stats/IonMemoryUtil.java
rename to services/core/java/com/android/server/stats/pull/IonMemoryUtil.java
index c9be96f..fde0a59 100644
--- a/services/core/java/com/android/server/stats/IonMemoryUtil.java
+++ b/services/core/java/com/android/server/stats/pull/IonMemoryUtil.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.stats;
+package com.android.server.stats.pull;
import android.os.FileUtils;
import android.util.Slog;
@@ -30,8 +30,11 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-/** Utility methods for reading ion memory stats. */
-final class IonMemoryUtil {
+/**
+ * Utility methods for reading ion memory stats.
+ * TODO: Consider making package private after puller migration
+ */
+public final class IonMemoryUtil {
private static final String TAG = "IonMemoryUtil";
/** Path to debugfs file for the system ion heap. */
@@ -50,7 +53,7 @@
* Returns value of the total size in bytes of the system ion heap from
* /sys/kernel/debug/ion/heaps/system.
*/
- static long readSystemIonHeapSizeFromDebugfs() {
+ public static long readSystemIonHeapSizeFromDebugfs() {
return parseIonHeapSizeFromDebugfs(readFile(DEBUG_SYSTEM_ION_HEAP_FILE));
}
@@ -78,7 +81,7 @@
* Returns values of allocation sizes in bytes on the system ion heap from
* /sys/kernel/debug/ion/heaps/system.
*/
- static List<IonAllocations> readProcessSystemIonHeapSizesFromDebugfs() {
+ public static List<IonAllocations> readProcessSystemIonHeapSizesFromDebugfs() {
return parseProcessIonHeapSizesFromDebugfs(readFile(DEBUG_SYSTEM_ION_HEAP_FILE));
}
@@ -130,7 +133,7 @@
}
/** Summary information about process ion allocations. */
- static final class IonAllocations {
+ public static final class IonAllocations {
/** PID these allocations belong to. */
public int pid;
/** Size of all individual allocations added together. */
diff --git a/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
similarity index 89%
rename from services/core/java/com/android/server/stats/ProcfsMemoryUtil.java
rename to services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
index c1eacce..638dfd2 100644
--- a/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java
+++ b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.stats;
+package com.android.server.stats.pull;
import static android.os.Process.PROC_OUT_STRING;
@@ -22,7 +22,7 @@
import java.util.function.BiConsumer;
-final class ProcfsMemoryUtil {
+public final class ProcfsMemoryUtil {
private static final int[] CMDLINE_OUT = new int[] { PROC_OUT_STRING };
private static final String[] STATUS_KEYS = new String[] {
"Uid:",
@@ -39,7 +39,7 @@
* VmSwap fields in /proc/pid/status in kilobytes or null if not available.
*/
@Nullable
- static MemorySnapshot readMemorySnapshotFromProcfs(int pid) {
+ public static MemorySnapshot readMemorySnapshotFromProcfs(int pid) {
long[] output = new long[STATUS_KEYS.length];
output[0] = -1;
Process.readProcLines("/proc/" + pid + "/status", STATUS_KEYS, output);
@@ -63,7 +63,7 @@
* Returns content of /proc/pid/cmdline (e.g. /system/bin/statsd) or an empty string
* if the file is not available.
*/
- static String readCmdlineFromProcfs(int pid) {
+ public static String readCmdlineFromProcfs(int pid) {
String[] cmdline = new String[1];
if (!Process.readProcFile("/proc/" + pid + "/cmdline", CMDLINE_OUT, cmdline, null, null)) {
return "";
@@ -71,7 +71,7 @@
return cmdline[0];
}
- static void forEachPid(BiConsumer<Integer, String> func) {
+ public static void forEachPid(BiConsumer<Integer, String> func) {
int[] pids = new int[1024];
pids = Process.getPids("/proc", pids);
for (int pid : pids) {
@@ -86,7 +86,7 @@
}
}
- static final class MemorySnapshot {
+ public static final class MemorySnapshot {
public int uid;
public int rssHighWaterMarkInKilobytes;
public int rssInKilobytes;
diff --git a/services/core/java/com/android/server/stats/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
similarity index 60%
rename from services/core/java/com/android/server/stats/StatsPullAtomService.java
rename to services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 1f7ed8a..3bc860a 100644
--- a/services/core/java/com/android/server/stats/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -14,22 +14,23 @@
* limitations under the License.
*/
-package com.android.server.stats;
+package com.android.server.stats.pull;
import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED;
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
+import static android.os.Debug.getIonHeapsSizeKb;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static android.os.Process.getUidForPid;
import static android.os.storage.VolumeInfo.TYPE_PRIVATE;
import static android.os.storage.VolumeInfo.TYPE_PUBLIC;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
-import static com.android.server.stats.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
-import static com.android.server.stats.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
-import static com.android.server.stats.ProcfsMemoryUtil.forEachPid;
-import static com.android.server.stats.ProcfsMemoryUtil.readCmdlineFromProcfs;
-import static com.android.server.stats.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
+import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
+import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
+import static com.android.server.stats.pull.ProcfsMemoryUtil.forEachPid;
+import static com.android.server.stats.pull.ProcfsMemoryUtil.readCmdlineFromProcfs;
+import static com.android.server.stats.pull.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -141,8 +142,8 @@
import com.android.server.am.MemoryStatUtil.MemoryStat;
import com.android.server.notification.NotificationManagerService;
import com.android.server.role.RoleManagerInternal;
-import com.android.server.stats.IonMemoryUtil.IonAllocations;
-import com.android.server.stats.ProcfsMemoryUtil.MemorySnapshot;
+import com.android.server.stats.pull.IonMemoryUtil.IonAllocations;
+import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot;
import com.android.server.storage.DiskStatsFileLogger;
import com.android.server.storage.DiskStatsLoggingService;
@@ -210,6 +211,8 @@
@Override
public void onStart() {
mStatsManager = (StatsManager) mContext.getSystemService(Context.STATS_MANAGER);
+ mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ mTelephony = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
// Used to initialize the CPU Frequency atom.
PowerProfile powerProfile = new PowerProfile(mContext);
@@ -263,11 +266,12 @@
registerProcessMemoryHighWaterMark();
registerProcessMemorySnapshot();
registerSystemIonHeapSize();
+ registerIonHeapSize();
registerProcessSystemIonHeapSize();
registerTemperature();
registerCoolingDevice();
- registerBinderCalls();
- registerBinderCallsExceptions();
+ registerBinderCallsStats();
+ registerBinderCallsStatsExceptions();
registerLooperStats();
registerDiskStats();
registerDirectoryUsage();
@@ -812,19 +816,96 @@
}
private void registerWifiActivityInfo() {
- // No op.
+ int tagId = StatsLog.WIFI_ACTIVITY_INFO;
+ mStatsManager.registerPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ (atomTag, data) -> pullWifiActivityInfo(atomTag, data),
+ BackgroundThread.getExecutor()
+ );
}
- private void pullWifiActivityInfo() {
- // No op.
+ private WifiManager mWifiManager;
+ private TelephonyManager mTelephony;
+
+ private int pullWifiActivityInfo(int atomTag, List<StatsEvent> pulledData) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
+ mWifiManager.getWifiActivityEnergyInfoAsync(
+ new Executor() {
+ @Override
+ public void execute(Runnable runnable) {
+ // run the listener on the binder thread, if it was run on the main
+ // thread it would deadlock since we would be waiting on ourselves
+ runnable.run();
+ }
+ },
+ info -> {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info);
+ wifiReceiver.send(0, bundle);
+ }
+ );
+ final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
+ if (wifiInfo == null) {
+ return StatsManager.PULL_SKIP;
+ }
+ StatsEvent e = StatsEvent.newBuilder()
+ .setAtomId(atomTag)
+ .writeLong(wifiInfo.getTimeSinceBootMillis())
+ .writeInt(wifiInfo.getStackState())
+ .writeLong(wifiInfo.getControllerTxDurationMillis())
+ .writeLong(wifiInfo.getControllerRxDurationMillis())
+ .writeLong(wifiInfo.getControllerIdleDurationMillis())
+ .writeLong(wifiInfo.getControllerEnergyUsedMicroJoules())
+ .build();
+ pulledData.add(e);
+ } catch (RuntimeException e) {
+ Slog.e(TAG, "failed to getWifiActivityEnergyInfoAsync", e);
+ return StatsManager.PULL_SKIP;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return StatsManager.PULL_SUCCESS;
}
private void registerModemActivityInfo() {
- // No op.
+ int tagId = StatsLog.MODEM_ACTIVITY_INFO;
+ mStatsManager.registerPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ (atomTag, data) -> pullModemActivityInfo(atomTag, data),
+ BackgroundThread.getExecutor()
+ );
}
- private void pullModemActivityInfo() {
- // No op.
+ private int pullModemActivityInfo(int atomTag, List<StatsEvent> pulledData) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
+ mTelephony.requestModemActivityInfo(modemReceiver);
+ final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
+ if (modemInfo == null) {
+ return StatsManager.PULL_SKIP;
+ }
+ StatsEvent e = StatsEvent.newBuilder()
+ .setAtomId(atomTag)
+ .writeLong(modemInfo.getTimestamp())
+ .writeLong(modemInfo.getSleepTimeMillis())
+ .writeLong(modemInfo.getIdleTimeMillis())
+ .writeLong(modemInfo.getTransmitPowerInfo().get(0).getTimeInMillis())
+ .writeLong(modemInfo.getTransmitPowerInfo().get(1).getTimeInMillis())
+ .writeLong(modemInfo.getTransmitPowerInfo().get(2).getTimeInMillis())
+ .writeLong(modemInfo.getTransmitPowerInfo().get(3).getTimeInMillis())
+ .writeLong(modemInfo.getTransmitPowerInfo().get(4).getTimeInMillis())
+ .writeLong(modemInfo.getReceiveTimeMillis())
+ .build();
+ pulledData.add(e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return StatsManager.PULL_SUCCESS;
}
private void registerBluetoothActivityInfo() {
@@ -923,75 +1004,426 @@
}
private void registerProcessMemoryState() {
- // No op.
+ int tagId = StatsLog.PROCESS_MEMORY_STATE;
+ PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ .setAdditiveFields(new int[] {4, 5, 6, 7, 8})
+ .build();
+ mStatsManager.registerPullAtomCallback(
+ tagId,
+ metadata,
+ (atomTag, data) -> pullProcessMemoryState(atomTag, data),
+ BackgroundThread.getExecutor()
+ );
}
- private void pullProcessMemoryState() {
- // No op.
+ private int pullProcessMemoryState(int atomTag, List<StatsEvent> pulledData) {
+ List<ProcessMemoryState> processMemoryStates =
+ LocalServices.getService(ActivityManagerInternal.class)
+ .getMemoryStateForProcesses();
+ for (ProcessMemoryState processMemoryState : processMemoryStates) {
+ final MemoryStat memoryStat = readMemoryStatFromFilesystem(processMemoryState.uid,
+ processMemoryState.pid);
+ if (memoryStat == null) {
+ continue;
+ }
+ StatsEvent e = StatsEvent.newBuilder()
+ .setAtomId(atomTag)
+ .writeInt(processMemoryState.uid)
+ .writeString(processMemoryState.processName)
+ .writeInt(processMemoryState.oomScore)
+ .writeLong(memoryStat.pgfault)
+ .writeLong(memoryStat.pgmajfault)
+ .writeLong(memoryStat.rssInBytes)
+ .writeLong(memoryStat.cacheInBytes)
+ .writeLong(memoryStat.swapInBytes)
+ .writeLong(-1) // unused
+ .writeLong(-1) // unused
+ .writeInt(-1) // unused
+ .build();
+ pulledData.add(e);
+ }
+ return StatsManager.PULL_SUCCESS;
+ }
+
+ /**
+ * Which native processes to snapshot memory for.
+ *
+ * <p>Processes are matched by their cmdline in procfs. Example: cat /proc/pid/cmdline returns
+ * /system/bin/statsd for the stats daemon.
+ */
+ private static final Set<String> MEMORY_INTERESTING_NATIVE_PROCESSES = Sets.newHashSet(
+ "/system/bin/statsd", // Stats daemon.
+ "/system/bin/surfaceflinger",
+ "/system/bin/apexd", // APEX daemon.
+ "/system/bin/audioserver",
+ "/system/bin/cameraserver",
+ "/system/bin/drmserver",
+ "/system/bin/healthd",
+ "/system/bin/incidentd",
+ "/system/bin/installd",
+ "/system/bin/lmkd", // Low memory killer daemon.
+ "/system/bin/logd",
+ "media.codec",
+ "media.extractor",
+ "media.metrics",
+ "/system/bin/mediadrmserver",
+ "/system/bin/mediaserver",
+ "/system/bin/performanced",
+ "/system/bin/tombstoned",
+ "/system/bin/traced", // Perfetto.
+ "/system/bin/traced_probes", // Perfetto.
+ "webview_zygote",
+ "zygote",
+ "zygote64");
+
+ /**
+ * Lowest available uid for apps.
+ *
+ * <p>Used to quickly discard memory snapshots of the zygote forks from native process
+ * measurements.
+ */
+ private static final int MIN_APP_UID = 10_000;
+
+ private static boolean isAppUid(int uid) {
+ return uid >= MIN_APP_UID;
}
private void registerProcessMemoryHighWaterMark() {
- // No op.
+ int tagId = StatsLog.PROCESS_MEMORY_HIGH_WATER_MARK;
+ mStatsManager.registerPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ (atomTag, data) -> pullProcessMemoryHighWaterMark(atomTag, data),
+ BackgroundThread.getExecutor()
+ );
}
- private void pullProcessMemoryHighWaterMark() {
- // No op.
+ private int pullProcessMemoryHighWaterMark(int atomTag, List<StatsEvent> pulledData) {
+ List<ProcessMemoryState> managedProcessList =
+ LocalServices.getService(ActivityManagerInternal.class)
+ .getMemoryStateForProcesses();
+ for (ProcessMemoryState managedProcess : managedProcessList) {
+ final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid);
+ if (snapshot == null) {
+ continue;
+ }
+ StatsEvent e = StatsEvent.newBuilder()
+ .setAtomId(atomTag)
+ .writeInt(managedProcess.uid)
+ .writeString(managedProcess.processName)
+ // RSS high-water mark in bytes.
+ .writeLong(snapshot.rssHighWaterMarkInKilobytes * 1024L)
+ .writeInt(snapshot.rssHighWaterMarkInKilobytes)
+ .build();
+ pulledData.add(e);
+ }
+ forEachPid((pid, cmdLine) -> {
+ if (!MEMORY_INTERESTING_NATIVE_PROCESSES.contains(cmdLine)) {
+ return;
+ }
+ final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid);
+ if (snapshot == null) {
+ return;
+ }
+ // Sometimes we get here a process that is not included in the whitelist. It comes
+ // from forking the zygote for an app. We can ignore that sample because this process
+ // is collected by ProcessMemoryState.
+ if (isAppUid(snapshot.uid)) {
+ return;
+ }
+ StatsEvent e = StatsEvent.newBuilder()
+ .setAtomId(atomTag)
+ .writeInt(snapshot.uid)
+ .writeString(cmdLine)
+ // RSS high-water mark in bytes.
+ .writeLong(snapshot.rssHighWaterMarkInKilobytes * 1024L)
+ .writeInt(snapshot.rssHighWaterMarkInKilobytes)
+ .build();
+ pulledData.add(e);
+ });
+ // Invoke rss_hwm_reset binary to reset RSS HWM counters for all processes.
+ SystemProperties.set("sys.rss_hwm_reset.on", "1");
+ return StatsManager.PULL_SUCCESS;
}
private void registerProcessMemorySnapshot() {
- // No op.
+ int tagId = StatsLog.PROCESS_MEMORY_SNAPSHOT;
+ mStatsManager.registerPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ (atomTag, data) -> pullProcessMemorySnapshot(atomTag, data),
+ BackgroundThread.getExecutor()
+ );
}
- private void pullProcessMemorySnapshot() {
- // No op.
+ private int pullProcessMemorySnapshot(int atomTag, List<StatsEvent> pulledData) {
+ List<ProcessMemoryState> managedProcessList =
+ LocalServices.getService(ActivityManagerInternal.class)
+ .getMemoryStateForProcesses();
+ for (ProcessMemoryState managedProcess : managedProcessList) {
+ final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid);
+ if (snapshot == null) {
+ continue;
+ }
+ StatsEvent e = StatsEvent.newBuilder()
+ .writeInt(managedProcess.uid)
+ .writeString(managedProcess.processName)
+ .writeInt(managedProcess.pid)
+ .writeInt(managedProcess.oomScore)
+ .writeInt(snapshot.rssInKilobytes)
+ .writeInt(snapshot.anonRssInKilobytes)
+ .writeInt(snapshot.swapInKilobytes)
+ .writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes)
+ .build();
+ pulledData.add(e);
+ }
+ forEachPid((pid, cmdLine) -> {
+ if (!MEMORY_INTERESTING_NATIVE_PROCESSES.contains(cmdLine)) {
+ return;
+ }
+ final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid);
+ if (snapshot == null) {
+ return;
+ }
+ // Sometimes we get here a process that is not included in the whitelist. It comes
+ // from forking the zygote for an app. We can ignore that sample because this process
+ // is collected by ProcessMemoryState.
+ if (isAppUid(snapshot.uid)) {
+ return;
+ }
+ StatsEvent e = StatsEvent.newBuilder()
+ .setAtomId(atomTag)
+ .writeInt(snapshot.uid)
+ .writeString(cmdLine)
+ .writeInt(pid)
+ .writeInt(-1001) // Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1.
+ .writeInt(snapshot.rssInKilobytes)
+ .writeInt(snapshot.anonRssInKilobytes)
+ .writeInt(snapshot.swapInKilobytes)
+ .writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes)
+ .build();
+ pulledData.add(e);
+ });
+ return StatsManager.PULL_SUCCESS;
}
private void registerSystemIonHeapSize() {
- // No op.
+ int tagId = StatsLog.SYSTEM_ION_HEAP_SIZE;
+ mStatsManager.registerPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ (atomTag, data) -> pullSystemIonHeapSize(atomTag, data),
+ BackgroundThread.getExecutor()
+ );
}
- private void pullSystemIonHeapSize() {
- // No op.
+ private int pullSystemIonHeapSize(int atomTag, List<StatsEvent> pulledData) {
+ final long systemIonHeapSizeInBytes = readSystemIonHeapSizeFromDebugfs();
+ StatsEvent e = StatsEvent.newBuilder()
+ .setAtomId(atomTag)
+ .writeLong(systemIonHeapSizeInBytes)
+ .build();
+ pulledData.add(e);
+ return StatsManager.PULL_SUCCESS;
+ }
+
+ private void registerIonHeapSize() {
+ int tagId = StatsLog.ION_HEAP_SIZE;
+ mStatsManager.registerPullAtomCallback(
+ tagId,
+ /* PullAtomMetadata */ null,
+ (atomTag, data) -> pullIonHeapSize(atomTag, data),
+ Executors.newSingleThreadExecutor()
+ );
+ }
+
+ private int pullIonHeapSize(int atomTag, List<StatsEvent> pulledData) {
+ int ionHeapSizeInKilobytes = (int) getIonHeapsSizeKb();
+ StatsEvent e = StatsEvent.newBuilder()
+ .setAtomId(atomTag)
+ .writeInt(ionHeapSizeInKilobytes)
+ .build();
+ pulledData.add(e);
+ return StatsManager.PULL_SUCCESS;
}
private void registerProcessSystemIonHeapSize() {
- // No op.
+ int tagId = StatsLog.PROCESS_SYSTEM_ION_HEAP_SIZE;
+ mStatsManager.registerPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ (atomTag, data) -> pullProcessSystemIonHeapSize(atomTag, data),
+ BackgroundThread.getExecutor()
+ );
}
- private void pullProcessSystemIonHeapSize() {
- // No op.
+ private int pullProcessSystemIonHeapSize(int atomTag, List<StatsEvent> pulledData) {
+ List<IonAllocations> result = readProcessSystemIonHeapSizesFromDebugfs();
+ for (IonAllocations allocations : result) {
+ StatsEvent e = StatsEvent.newBuilder()
+ .setAtomId(atomTag)
+ .writeInt(getUidForPid(allocations.pid))
+ .writeString(readCmdlineFromProcfs(allocations.pid))
+ .writeInt((int) (allocations.totalSizeInBytes / 1024))
+ .writeInt(allocations.count)
+ .writeInt((int) (allocations.maxSizeInBytes / 1024))
+ .build();
+ pulledData.add(e);
+ }
+ return StatsManager.PULL_SUCCESS;
}
private void registerTemperature() {
- // No op.
+ int tagId = StatsLog.TEMPERATURE;
+ mStatsManager.registerPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ (atomTag, data) -> pullTemperature(atomTag, data),
+ BackgroundThread.getExecutor()
+ );
}
- private void pullTemperature() {
- // No op.
+ private int pullTemperature(int atomTag, List<StatsEvent> pulledData) {
+ IThermalService thermalService = getIThermalService();
+ if (thermalService == null) {
+ return StatsManager.PULL_SKIP;
+ }
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ List<Temperature> temperatures = thermalService.getCurrentTemperatures();
+ for (Temperature temp : temperatures) {
+ StatsEvent e = StatsEvent.newBuilder()
+ .setAtomId(atomTag)
+ .writeInt(temp.getType())
+ .writeString(temp.getName())
+ .writeInt((int) (temp.getValue() * 10))
+ .writeInt(temp.getStatus())
+ .build();
+ pulledData.add(e);
+ }
+ } catch (RemoteException e) {
+ // Should not happen.
+ Slog.e(TAG, "Disconnected from thermal service. Cannot pull temperatures.");
+ return StatsManager.PULL_SKIP;
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ return StatsManager.PULL_SUCCESS;
}
private void registerCoolingDevice() {
- // No op.
+ int tagId = StatsLog.COOLING_DEVICE;
+ mStatsManager.registerPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ (atomTag, data) -> pullCooldownDevice(atomTag, data),
+ BackgroundThread.getExecutor()
+ );
}
- private void pullCooldownDevice() {
- // No op.
+ private int pullCooldownDevice(int atomTag, List<StatsEvent> pulledData) {
+ IThermalService thermalService = getIThermalService();
+ if (thermalService == null) {
+ return StatsManager.PULL_SKIP;
+ }
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ List<CoolingDevice> devices = thermalService.getCurrentCoolingDevices();
+ for (CoolingDevice device : devices) {
+ StatsEvent e = StatsEvent.newBuilder()
+ .setAtomId(atomTag)
+ .writeInt(device.getType())
+ .writeString(device.getName())
+ .writeInt((int) (device.getValue()))
+ .build();
+ pulledData.add(e);
+ }
+ } catch (RemoteException e) {
+ // Should not happen.
+ Slog.e(TAG, "Disconnected from thermal service. Cannot pull temperatures.");
+ return StatsManager.PULL_SKIP;
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ return StatsManager.PULL_SUCCESS;
}
- private void registerBinderCalls() {
- // No op.
+ private void registerBinderCallsStats() {
+ int tagId = StatsLog.BINDER_CALLS;
+ PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ .setAdditiveFields(new int[] {4, 5, 6, 8, 12})
+ .build();
+ mStatsManager.registerPullAtomCallback(
+ tagId,
+ metadata,
+ (atomTag, data) -> pullBinderCallsStats(atomTag, data),
+ BackgroundThread.getExecutor()
+ );
}
- private void pullBinderCalls() {
- // No op.
+ private int pullBinderCallsStats(int atomTag, List<StatsEvent> pulledData) {
+ BinderCallsStatsService.Internal binderStats =
+ LocalServices.getService(BinderCallsStatsService.Internal.class);
+ if (binderStats == null) {
+ Slog.e(TAG, "failed to get binderStats");
+ return StatsManager.PULL_SKIP;
+ }
+
+ List<ExportedCallStat> callStats = binderStats.getExportedCallStats();
+ binderStats.reset();
+ for (ExportedCallStat callStat : callStats) {
+ StatsEvent e = StatsEvent.newBuilder()
+ .setAtomId(atomTag)
+ .writeInt(callStat.workSourceUid)
+ .writeString(callStat.className)
+ .writeString(callStat.methodName)
+ .writeLong(callStat.callCount)
+ .writeLong(callStat.exceptionCount)
+ .writeLong(callStat.latencyMicros)
+ .writeLong(callStat.maxLatencyMicros)
+ .writeLong(callStat.cpuTimeMicros)
+ .writeLong(callStat.maxCpuTimeMicros)
+ .writeLong(callStat.maxReplySizeBytes)
+ .writeLong(callStat.maxRequestSizeBytes)
+ .writeLong(callStat.recordedCallCount)
+ .writeInt(callStat.screenInteractive ? 1 : 0)
+ .writeInt(callStat.callingUid)
+ .build();
+ pulledData.add(e);
+ }
+ return StatsManager.PULL_SUCCESS;
}
- private void registerBinderCallsExceptions() {
- // No op.
+ private void registerBinderCallsStatsExceptions() {
+ int tagId = StatsLog.BINDER_CALLS_EXCEPTIONS;
+ mStatsManager.registerPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ (atomTag, data) -> pullBinderCallsStatsExceptions(atomTag, data),
+ BackgroundThread.getExecutor()
+ );
}
- private void pullBinderCallsExceptions() {
- // No op.
+ private int pullBinderCallsStatsExceptions(int atomTag, List<StatsEvent> pulledData) {
+ BinderCallsStatsService.Internal binderStats =
+ LocalServices.getService(BinderCallsStatsService.Internal.class);
+ if (binderStats == null) {
+ Slog.e(TAG, "failed to get binderStats");
+ return StatsManager.PULL_SKIP;
+ }
+
+ ArrayMap<String, Integer> exceptionStats = binderStats.getExportedExceptionStats();
+ // TODO: decouple binder calls exceptions with the rest of the binder calls data so that we
+ // can reset the exception stats.
+ for (Map.Entry<String, Integer> entry : exceptionStats.entrySet()) {
+ StatsEvent e = StatsEvent.newBuilder()
+ .setAtomId(atomTag)
+ .writeString(entry.getKey())
+ .writeInt(entry.getValue())
+ .build();
+ pulledData.add(e);
+ }
+ return StatsManager.PULL_SUCCESS;
}
private void registerLooperStats() {
@@ -1113,28 +1545,119 @@
// No op.
}
- private void registerDeviceCalculatedPowerUse() {
- // No op.
+ // TODO: move to top of file when all migrations are complete
+ private BatteryStatsHelper mBatteryStatsHelper = null;
+ private static final int MAX_BATTERY_STATS_HELPER_FREQUENCY_MS = 1000;
+ private long mBatteryStatsHelperTimestampMs = -MAX_BATTERY_STATS_HELPER_FREQUENCY_MS;
+ private static final long MILLI_AMP_HR_TO_NANO_AMP_SECS = 1_000_000L * 3600L;
+
+ private BatteryStatsHelper getBatteryStatsHelper() {
+ if (mBatteryStatsHelper == null) {
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ // clearCallingIdentity required for BatteryStatsHelper.checkWifiOnly().
+ mBatteryStatsHelper = new BatteryStatsHelper(mContext, false);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ mBatteryStatsHelper.create((Bundle) null);
+ }
+ long currentTime = SystemClock.elapsedRealtime();
+ if (currentTime - mBatteryStatsHelperTimestampMs >= MAX_BATTERY_STATS_HELPER_FREQUENCY_MS) {
+ // Load BatteryStats and do all the calculations.
+ mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.USER_ALL);
+ // Calculations are done so we don't need to save the raw BatteryStats data in RAM.
+ mBatteryStatsHelper.clearStats();
+ mBatteryStatsHelperTimestampMs = currentTime;
+ }
+ return mBatteryStatsHelper;
}
- private void pullDeviceCalculatedPowerUse() {
- // No op.
+ private long milliAmpHrsToNanoAmpSecs(double mAh) {
+ return (long) (mAh * MILLI_AMP_HR_TO_NANO_AMP_SECS + 0.5);
+ }
+
+ private void registerDeviceCalculatedPowerUse() {
+ int tagId = StatsLog.DEVICE_CALCULATED_POWER_USE;
+ mStatsManager.registerPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ (atomTag, data) -> pullDeviceCalculatedPowerUse(atomTag, data),
+ BackgroundThread.getExecutor()
+ );
+ }
+
+ private int pullDeviceCalculatedPowerUse(int atomTag, List<StatsEvent> pulledData) {
+ BatteryStatsHelper bsHelper = getBatteryStatsHelper();
+ StatsEvent e = StatsEvent.newBuilder()
+ .setAtomId(atomTag)
+ .writeLong(milliAmpHrsToNanoAmpSecs(bsHelper.getComputedPower()))
+ .build();
+ pulledData.add(e);
+ return StatsManager.PULL_SUCCESS;
}
private void registerDeviceCalculatedPowerBlameUid() {
- // No op.
+ int tagId = StatsLog.DEVICE_CALCULATED_POWER_BLAME_UID;
+ mStatsManager.registerPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ (atomTag, data) -> pullDeviceCalculatedPowerBlameUid(atomTag, data),
+ BackgroundThread.getExecutor()
+ );
}
- private void pullDeviceCalculatedPowerBlameUid() {
- // No op.
+ private int pullDeviceCalculatedPowerBlameUid(int atomTag, List<StatsEvent> pulledData) {
+ final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
+ if (sippers == null) {
+ return StatsManager.PULL_SKIP;
+ }
+
+ for (BatterySipper bs : sippers) {
+ if (bs.drainType != bs.drainType.APP) {
+ continue;
+ }
+ StatsEvent e = StatsEvent.newBuilder()
+ .setAtomId(atomTag)
+ .writeInt(bs.uidObj.getUid())
+ .writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah))
+ .build();
+ pulledData.add(e);
+ }
+ return StatsManager.PULL_SUCCESS;
}
private void registerDeviceCalculatedPowerBlameOther() {
- // No op.
+ int tagId = StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER;
+ mStatsManager.registerPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ (atomTag, data) -> pullDeviceCalculatedPowerBlameOther(atomTag, data),
+ BackgroundThread.getExecutor()
+ );
}
- private void pullDeviceCalculatedPowerBlameOther() {
- // No op.
+ private int pullDeviceCalculatedPowerBlameOther(int atomTag, List<StatsEvent> pulledData) {
+ final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
+ if (sippers == null) {
+ return StatsManager.PULL_SKIP;
+ }
+
+ for (BatterySipper bs : sippers) {
+ if (bs.drainType == bs.drainType.APP) {
+ continue; // This is a separate atom; see pullDeviceCalculatedPowerBlameUid().
+ }
+ if (bs.drainType == bs.drainType.USER) {
+ continue; // This is not supported. We purposefully calculate over USER_ALL.
+ }
+ StatsEvent e = StatsEvent.newBuilder()
+ .setAtomId(atomTag)
+ .writeInt(bs.drainType.ordinal())
+ .writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah))
+ .build();
+ pulledData.add(e);
+ }
+ return StatsManager.PULL_SUCCESS;
}
private void registerDebugElapsedClock() {
diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index f4fb93a..baef5d6 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -40,9 +40,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
-import java.io.FileDescriptor;
import java.util.Objects;
/**
@@ -82,7 +80,7 @@
* @throws ExternalStorageServiceException if the session fails to start
* @throws IllegalStateException if a session has already been created for {@code vol}
*/
- public void onVolumeMount(FileDescriptor deviceFd, VolumeInfo vol)
+ public void onVolumeMount(ParcelFileDescriptor deviceFd, VolumeInfo vol)
throws ExternalStorageServiceException {
if (!shouldHandle(vol)) {
return;
@@ -102,8 +100,8 @@
mConnections.put(userId, connection);
}
Slog.i(TAG, "Creating and starting session with id: " + sessionId);
- connection.startSession(sessionId, new ParcelFileDescriptor(deviceFd),
- vol.getPath().getPath(), vol.getInternalPath().getPath());
+ connection.startSession(sessionId, deviceFd, vol.getPath().getPath(),
+ vol.getInternalPath().getPath());
}
}
@@ -185,7 +183,7 @@
* This call removes all sessions for the user that is being stopped;
* this will make sure that we don't rebind to the service needlessly.
*/
- public void onUserStopping(int userId) throws ExternalStorageServiceException {
+ public void onUserStopping(int userId) {
if (!shouldHandle(null)) {
return;
}
diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java
index c02ded8..dd18f4e 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -60,7 +60,8 @@
*/
public final class StorageUserConnection {
private static final String TAG = "StorageUserConnection";
- private static final int REMOTE_TIMEOUT_SECONDS = 15;
+
+ public static final int REMOTE_TIMEOUT_SECONDS = 5;
private final Object mLock = new Object();
private final Context mContext;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 19bc560..b596d2a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1680,6 +1680,7 @@
if (root == this) {
task.setRootProcess(proc);
}
+ proc.addActivityIfNeeded(this);
}
boolean hasProcess() {
@@ -6363,6 +6364,14 @@
* aspect ratio.
*/
boolean shouldUseSizeCompatMode() {
+ if (inMultiWindowMode() || getWindowConfiguration().hasWindowDecorCaption()) {
+ final ActivityRecord root = task != null ? task.getRootActivity() : null;
+ if (root != null && root != this && !root.shouldUseSizeCompatMode()) {
+ // If the root activity doesn't use size compatibility mode, the activities above
+ // are forced to be the same for consistent visual appearance.
+ return false;
+ }
+ }
return !isResizeable() && (info.isFixedOrientation() || info.hasFixedAspectRatio())
// The configuration of non-standard type should be enforced by system.
&& isActivityTypeStandard()
@@ -6910,7 +6919,7 @@
// Update last reported values.
final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();
- setLastReportedConfiguration(mAtmService.getGlobalConfiguration(), newMergedOverrideConfig);
+ setLastReportedConfiguration(getProcessGlobalConfiguration(), newMergedOverrideConfig);
if (mState == INITIALIZING) {
// No need to relaunch or schedule new config for activity that hasn't been launched
@@ -7009,6 +7018,11 @@
return true;
}
+ /** Get process configuration, or global config if the process is not set. */
+ private Configuration getProcessGlobalConfiguration() {
+ return app != null ? app.getConfiguration() : mAtmService.getGlobalConfiguration();
+ }
+
/**
* When assessing a configuration change, decide if the changes flags and the new configurations
* should cause the Activity to relaunch.
@@ -7121,7 +7135,7 @@
startRelaunching();
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
pendingNewIntents, configChangeFlags,
- new MergedConfiguration(mAtmService.getGlobalConfiguration(),
+ new MergedConfiguration(getProcessGlobalConfiguration(),
getMergedOverrideConfiguration()),
preserveWindow);
final ActivityLifecycleItem lifecycleItem;
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index aa90248..0a68408 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -848,8 +848,6 @@
if (DEBUG_ALL) Slog.v(TAG, "Launching: " + r);
- proc.addActivityIfNeeded(r);
-
final LockTaskController lockTaskController = mService.getLockTaskController();
if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
|| task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index e308f6b..47e8b87 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6743,7 +6743,7 @@
return;
}
process.mIsImeProcess = true;
- process.registerDisplayConfigurationListenerLocked(displayContent);
+ process.registerDisplayConfigurationListener(displayContent);
}
}
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 1e60ce8..7b23e2d 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -134,8 +134,8 @@
resolveOverrideConfiguration(newParentConfig);
mFullConfiguration.setTo(newParentConfig);
mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
+ onMergedOverrideConfigurationChanged();
if (!mResolvedTmpConfig.equals(mResolvedOverrideConfiguration)) {
- onMergedOverrideConfigurationChanged();
// This depends on the assumption that change-listeners don't do
// their own override resolution. This way, dependent hierarchies
// can stay properly synced-up with a primary hierarchy's constraints.
@@ -147,6 +147,10 @@
mResolvedOverrideConfiguration);
}
}
+ for (int i = mChangeListeners.size() - 1; i >= 0; --i) {
+ mChangeListeners.get(i).onMergedOverrideConfigurationChanged(
+ mMergedOverrideConfiguration);
+ }
if (forwardToChildren) {
for (int i = getChildCount() - 1; i >= 0; --i) {
final ConfigurationContainer child = getChildAt(i);
@@ -545,6 +549,7 @@
}
mChangeListeners.add(listener);
listener.onRequestedOverrideConfigurationChanged(mResolvedOverrideConfiguration);
+ listener.onMergedOverrideConfigurationChanged(mMergedOverrideConfiguration);
}
void unregisterConfigurationChangeListener(ConfigurationContainerListener listener) {
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainerListener.java b/services/core/java/com/android/server/wm/ConfigurationContainerListener.java
index dc4939d..3d84e17 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainerListener.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainerListener.java
@@ -24,5 +24,8 @@
public interface ConfigurationContainerListener {
/** {@see ConfigurationContainer#onRequestedOverrideConfigurationChanged} */
- void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration);
+ default void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {}
+
+ /** Called when new merged override configuration is reported. */
+ default void onMergedOverrideConfigurationChanged(Configuration mergedOverrideConfiguration) {}
}
diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
index e0a7b18..2cb7d5a 100644
--- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java
+++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
@@ -33,6 +33,27 @@
private final HighRefreshRateBlacklist mHighRefreshRateBlacklist;
private final WindowManagerService mWmService;
+ /**
+ * The following constants represent priority of the window. SF uses this information when
+ * deciding which window has a priority when deciding about the refresh rate of the screen.
+ * Priority 0 is considered the highest priority. -1 means that the priority is unset.
+ */
+ static final int LAYER_PRIORITY_UNSET = -1;
+ /** Windows that are in focus and voted for the preferred mode ID have the highest priority. */
+ static final int LAYER_PRIORITY_FOCUSED_WITH_MODE = 0;
+ /**
+ * This is a default priority for all windows that are in focus, but have not requested a
+ * specific mode ID.
+ */
+ static final int LAYER_PRIORITY_FOCUSED_WITHOUT_MODE = 1;
+ /**
+ * Windows that are not in focus, but voted for a specific mode ID should be
+ * acknowledged by SF. For example, there are two applications in a split screen.
+ * One voted for a given mode ID, and the second one doesn't care. Even though the
+ * second one might be in focus, we can honor the mode ID of the first one.
+ */
+ static final int LAYER_PRIORITY_NOT_FOCUSED_WITH_MODE = 2;
+
RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo,
HighRefreshRateBlacklist blacklist) {
mLowRefreshRateId = findLowRefreshRateModeId(displayInfo);
@@ -92,4 +113,28 @@
}
return 0;
}
+
+ /**
+ * Calculate the priority based on whether the window is in focus and whether the application
+ * voted for a specific refresh rate.
+ *
+ * TODO(b/144307188): This is a very basic algorithm version. Explore other signals that might
+ * be useful in edge cases when we are deciding which layer should get priority when deciding
+ * about the refresh rate.
+ */
+ int calculatePriority(WindowState w) {
+ boolean isFocused = w.isFocused();
+ int preferredModeId = getPreferredModeId(w);
+
+ if (!isFocused && preferredModeId > 0) {
+ return LAYER_PRIORITY_NOT_FOCUSED_WITH_MODE;
+ }
+ if (isFocused && preferredModeId == 0) {
+ return LAYER_PRIORITY_FOCUSED_WITHOUT_MODE;
+ }
+ if (isFocused && preferredModeId > 0) {
+ return LAYER_PRIORITY_FOCUSED_WITH_MODE;
+ }
+ return LAYER_PRIORITY_UNSET;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 9a40b1b1..ceb38f7 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.os.Build.VERSION_CODES.Q;
import static android.view.Display.INVALID_DISPLAY;
@@ -178,8 +179,10 @@
// Last configuration that was reported to the process.
private final Configuration mLastReportedConfiguration;
+ private final Configuration mNewOverrideConfig = new Configuration();
// Registered display id as a listener to override config change
private int mDisplayId;
+ private ActivityRecord mConfigActivityRecord;
/** Whether our process is currently running a {@link RecentsAnimation} */
private boolean mRunningRecentsAnimation;
@@ -327,6 +330,12 @@
return mDisplayId != INVALID_DISPLAY;
}
+ /** @return {@code true} if the process registered to an activity as a config listener. */
+ @VisibleForTesting
+ boolean registeredForActivityConfigChanges() {
+ return mConfigActivityRecord != null;
+ }
+
void postPendingUiCleanMsg(boolean pendingUiClean) {
if (mListener == null) return;
// Posting on handler so WM lock isn't held when we call into AM.
@@ -483,7 +492,10 @@
@Override
protected ConfigurationContainer getParent() {
- return null;
+ // Returning RootWindowContainer as the parent, so that this process controller always
+ // has full configuration and overrides (e.g. from display) are always added on top of
+ // global config.
+ return mAtm.mRootWindowContainer;
}
@HotPath(caller = HotPath.PROCESS_CHANGE)
@@ -507,10 +519,12 @@
return;
}
mActivities.add(r);
+ updateActivityConfigurationListener();
}
void removeActivity(ActivityRecord r) {
mActivities.remove(r);
+ updateActivityConfigurationListener();
}
void makeFinishingForProcessRemoved() {
@@ -521,6 +535,7 @@
void clearActivities() {
mActivities.clear();
+ updateActivityConfigurationListener();
}
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
@@ -964,19 +979,20 @@
mAtm.mH.sendMessage(m);
}
- void registerDisplayConfigurationListenerLocked(DisplayContent displayContent) {
+ void registerDisplayConfigurationListener(DisplayContent displayContent) {
if (displayContent == null) {
return;
}
- // A process can only register to one display to listener to the override configuration
+ // A process can only register to one display to listen to the override configuration
// change. Unregister existing listener if it has one before register the new one.
- unregisterDisplayConfigurationListenerLocked();
+ unregisterDisplayConfigurationListener();
+ unregisterActivityConfigurationListener();
mDisplayId = displayContent.mDisplayId;
displayContent.registerConfigurationChangeListener(this);
}
@VisibleForTesting
- void unregisterDisplayConfigurationListenerLocked() {
+ void unregisterDisplayConfigurationListener() {
if (mDisplayId == INVALID_DISPLAY) {
return;
}
@@ -986,6 +1002,48 @@
displayContent.unregisterConfigurationChangeListener(this);
}
mDisplayId = INVALID_DISPLAY;
+ onMergedOverrideConfigurationChanged(Configuration.EMPTY);
+ }
+
+ private void registerActivityConfigurationListener(ActivityRecord activityRecord) {
+ if (activityRecord == null) {
+ return;
+ }
+ // A process can only register to one activityRecord to listen to the override configuration
+ // change. Unregister existing listener if it has one before register the new one.
+ unregisterDisplayConfigurationListener();
+ unregisterActivityConfigurationListener();
+ mConfigActivityRecord = activityRecord;
+ activityRecord.registerConfigurationChangeListener(this);
+ }
+
+ private void unregisterActivityConfigurationListener() {
+ if (mConfigActivityRecord == null) {
+ return;
+ }
+ mConfigActivityRecord.unregisterConfigurationChangeListener(this);
+ mConfigActivityRecord = null;
+ onMergedOverrideConfigurationChanged(Configuration.EMPTY);
+ }
+
+ /**
+ * Check if activity configuration override for the activity process needs an update and perform
+ * if needed. By default we try to override the process configuration to match the top activity
+ * config to increase app compatibility with multi-window and multi-display. The process will
+ * always track the configuration of the non-finishing activity last added to the process.
+ */
+ private void updateActivityConfigurationListener() {
+ for (int i = mActivities.size() - 1; i >= 0; i--) {
+ final ActivityRecord activityRecord = mActivities.get(i);
+ if (!activityRecord.finishing && !activityRecord.containsListener(this)) {
+ // Eligible activity is found, update listener.
+ registerActivityConfigurationListener(activityRecord);
+ return;
+ }
+ }
+
+ // No eligible activities found, let's remove the configuration listener.
+ unregisterActivityConfigurationListener();
}
@Override
@@ -995,8 +1053,11 @@
}
@Override
- public void onRequestedOverrideConfigurationChanged(Configuration newOverrideConfig) {
- super.onRequestedOverrideConfigurationChanged(newOverrideConfig);
+ public void onMergedOverrideConfigurationChanged(Configuration mergedOverrideConfig) {
+ // Make sure that we don't accidentally override the activity type.
+ mNewOverrideConfig.setTo(mergedOverrideConfig);
+ mNewOverrideConfig.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
+ super.onRequestedOverrideConfigurationChanged(mNewOverrideConfig);
updateConfiguration();
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c2eb0e4..a4297416 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -657,6 +657,13 @@
private KeyInterceptionInfo mKeyInterceptionInfo;
/**
+ * This information is passed to SurfaceFlinger to decide which window should have a priority
+ * when deciding about the refresh rate of the display. All windows have the lowest priority by
+ * default. The variable is cached, so we do not send too many updates to SF.
+ */
+ int mFrameRateSelectionPriority = RefreshRatePolicy.LAYER_PRIORITY_UNSET;
+
+ /**
* @return The insets state as requested by the client, i.e. the dispatched insets state
* for which the visibilities are overridden with what the client requested.
*/
@@ -3395,7 +3402,7 @@
"Reporting new frame to %s: %s", this,
mWindowFrames.mCompatFrame);
final MergedConfiguration mergedConfiguration =
- new MergedConfiguration(mWmService.mRoot.getConfiguration(),
+ new MergedConfiguration(getProcessGlobalConfiguration(),
getMergedOverrideConfiguration());
setLastReportedMergedConfiguration(mergedConfiguration);
@@ -5165,6 +5172,24 @@
}
}
+
+ /**
+ * Notifies SF about the priority of the window, if it changed. SF then uses this information
+ * to decide which window's desired rendering rate should have a priority when deciding about
+ * the refresh rate of the screen. Priority
+ * {@link RefreshRatePolicy#LAYER_PRIORITY_FOCUSED_WITH_MODE} is considered the highest.
+ */
+ @VisibleForTesting
+ void updateFrameRateSelectionPriorityIfNeeded() {
+ final int priority = getDisplayContent().getDisplayPolicy().getRefreshRatePolicy()
+ .calculatePriority(this);
+ if (mFrameRateSelectionPriority != priority) {
+ mFrameRateSelectionPriority = priority;
+ getPendingTransaction().setFrameRateSelectionPriority(mSurfaceControl,
+ mFrameRateSelectionPriority);
+ }
+ }
+
@Override
void prepareSurfaces() {
final Dimmer dimmer = getDimmer();
@@ -5173,6 +5198,8 @@
applyDims(dimmer);
}
updateSurfacePosition();
+ // Send information to SufaceFlinger about the priority of the current window.
+ updateFrameRateSelectionPriorityIfNeeded();
mWinAnimator.prepareSurfaceLocked(true);
super.prepareSurfaces();
diff --git a/services/core/jni/com_android_server_GraphicsStatsService.cpp b/services/core/jni/com_android_server_GraphicsStatsService.cpp
index 9353fbd..7644ade 100644
--- a/services/core/jni/com_android_server_GraphicsStatsService.cpp
+++ b/services/core/jni/com_android_server_GraphicsStatsService.cpp
@@ -178,15 +178,16 @@
}
// graphicsStatsPullCallback is invoked by statsd service to pull GRAPHICS_STATS atom.
-static bool graphicsStatsPullCallback(int32_t atom_tag, pulled_stats_event_list* data,
- const void* cookie) {
+static status_pull_atom_return_t graphicsStatsPullCallback(int32_t atom_tag,
+ pulled_stats_event_list* data,
+ void* cookie) {
JNIEnv* env = getJNIEnv();
if (!env) {
return false;
}
if (gGraphicsStatsServiceObject == nullptr) {
ALOGE("Failed to get graphicsstats service");
- return false;
+ return STATS_PULL_SKIP;
}
for (bool lastFullDay : {true, false}) {
@@ -198,7 +199,7 @@
env->ExceptionDescribe();
env->ExceptionClear();
ALOGE("Failed to invoke graphicsstats service");
- return false;
+ return STATS_PULL_SKIP;
}
if (!jdata) {
// null means data is not available for that day.
@@ -217,7 +218,7 @@
if (!success) {
ALOGW("Parse failed on GraphicsStatsPuller error='%s' dataSize='%d'",
serviceDump.InitializationErrorString().c_str(), dataSize);
- return false;
+ return STATS_PULL_SKIP;
}
for (int stat_index = 0; stat_index < serviceDump.stats_size(); stat_index++) {
@@ -244,7 +245,7 @@
stats_event_build(event);
}
}
- return true;
+ return STATS_PULL_SUCCESS;
}
// Register a puller for GRAPHICS_STATS atom with the statsd service.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7fdb4c4..b8b0dbf 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1095,7 +1095,7 @@
String globalProxySpec = null;
String globalProxyExclusionList = null;
- ArrayMap<String, TrustAgentInfo> trustAgentInfos = new ArrayMap<>();
+ @NonNull ArrayMap<String, TrustAgentInfo> trustAgentInfos = new ArrayMap<>();
List<String> crossProfileWidgetProviders;
@@ -1650,6 +1650,7 @@
}
}
+ @NonNull
private ArrayMap<String, TrustAgentInfo> getAllTrustAgentInfos(
XmlPullParser parser, String tag) throws XmlPullParserException, IOException {
int outerDepthDAM = parser.getDepth();
@@ -2434,11 +2435,133 @@
migrateUserRestrictionsIfNecessaryLocked();
// TODO PO may not have a class name either due to b/17652534. Address that too.
-
updateDeviceOwnerLocked();
}
}
+ /**
+ * Checks if the device is in COMP mode, and if so migrates it to managed profile on a
+ * corporate owned device.
+ */
+ @GuardedBy("getLockObject()")
+ private void maybeMigrateToProfileOnOrganizationOwnedDeviceLocked() {
+ logIfVerbose("Checking whether we need to migrate COMP ");
+ final int doUserId = mOwners.getDeviceOwnerUserId();
+ if (doUserId == UserHandle.USER_NULL) {
+ logIfVerbose("No DO found, skipping migration.");
+ return;
+ }
+
+ final List<UserInfo> profiles = mUserManager.getProfiles(doUserId);
+ if (profiles.size() != 2) {
+ if (profiles.size() == 1) {
+ logIfVerbose("Profile not found, skipping migration.");
+ } else {
+ Slog.wtf(LOG_TAG, "Found " + profiles.size() + " profiles, skipping migration");
+ }
+ return;
+ }
+
+ final int poUserId = getManagedUserId(doUserId);
+ if (poUserId < 0) {
+ Slog.wtf(LOG_TAG, "Found DO and a profile, but it is not managed, skipping migration");
+ return;
+ }
+
+ final ActiveAdmin doAdmin = getDeviceOwnerAdminLocked();
+ final ActiveAdmin poAdmin = getProfileOwnerAdminLocked(poUserId);
+ if (doAdmin == null || poAdmin == null) {
+ Slog.wtf(LOG_TAG, "Failed to get either PO or DO admin, aborting migration.");
+ return;
+ }
+
+ final ComponentName doAdminComponent = mOwners.getDeviceOwnerComponent();
+ final ComponentName poAdminComponent = mOwners.getProfileOwnerComponent(poUserId);
+ if (doAdminComponent == null || poAdminComponent == null) {
+ Slog.wtf(LOG_TAG, "Cannot find PO or DO component name, aborting migration.");
+ return;
+ }
+ if (!doAdminComponent.getPackageName().equals(poAdminComponent.getPackageName())) {
+ Slog.e(LOG_TAG, "DO and PO are different packages, aborting migration.");
+ return;
+ }
+
+ Slog.i(LOG_TAG, String.format(
+ "Migrating COMP to PO on a corp owned device; primary user: %d; profile: %d",
+ doUserId, poUserId));
+
+ Slog.i(LOG_TAG, "Giving the PO additional power...");
+ markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(poAdminComponent, poUserId);
+ Slog.i(LOG_TAG, "Migrating DO policies to PO...");
+ moveDoPoliciesToProfileParentAdmin(doAdmin, poAdmin.getParentActiveAdmin());
+ saveSettingsLocked(poUserId);
+ Slog.i(LOG_TAG, "Clearing the DO...");
+ final ComponentName doAdminReceiver = doAdmin.info.getComponent();
+ clearDeviceOwnerLocked(doAdmin, doUserId);
+ // TODO(b/143516163): If we have a power cut here, we might leave active admin. Consider if
+ // it is worth the complexity to make it more robust.
+ Slog.i(LOG_TAG, "Removing admin artifacts...");
+ // TODO(b/143516163): Clean up application restrictions in UserManager.
+ removeAdminArtifacts(doAdminReceiver, doUserId);
+ Slog.i(LOG_TAG, "Migration complete.");
+
+ // Note: KeyChain keys are not removed and will remain accessible for the apps that have
+ // been given grants to use them.
+ }
+
+ private void moveDoPoliciesToProfileParentAdmin(ActiveAdmin doAdmin, ActiveAdmin parentAdmin) {
+ // The following policies can be already controlled via parent instance, skip if so.
+ if (parentAdmin.mPasswordPolicy.quality == PASSWORD_QUALITY_UNSPECIFIED) {
+ parentAdmin.mPasswordPolicy = doAdmin.mPasswordPolicy;
+ }
+ if (parentAdmin.passwordHistoryLength == ActiveAdmin.DEF_PASSWORD_HISTORY_LENGTH) {
+ parentAdmin.passwordHistoryLength = doAdmin.passwordHistoryLength;
+ }
+ if (parentAdmin.passwordExpirationTimeout == ActiveAdmin.DEF_PASSWORD_HISTORY_LENGTH) {
+ parentAdmin.passwordExpirationTimeout = doAdmin.passwordExpirationTimeout;
+ }
+ if (parentAdmin.maximumFailedPasswordsForWipe
+ == ActiveAdmin.DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE) {
+ parentAdmin.maximumFailedPasswordsForWipe = doAdmin.maximumFailedPasswordsForWipe;
+ }
+ if (parentAdmin.maximumTimeToUnlock == ActiveAdmin.DEF_MAXIMUM_TIME_TO_UNLOCK) {
+ parentAdmin.maximumTimeToUnlock = doAdmin.maximumTimeToUnlock;
+ }
+ if (parentAdmin.strongAuthUnlockTimeout
+ == DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS) {
+ parentAdmin.strongAuthUnlockTimeout = doAdmin.strongAuthUnlockTimeout;
+ }
+ parentAdmin.disabledKeyguardFeatures |=
+ doAdmin.disabledKeyguardFeatures & PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
+
+ parentAdmin.trustAgentInfos.putAll(doAdmin.trustAgentInfos);
+
+ // The following policies weren't available to PO, but will be available after migration.
+ parentAdmin.disableCamera = doAdmin.disableCamera;
+
+ // TODO(b/143516163): Uncomment once corresponding APIs are available via parent instance.
+ // parentAdmin.disableScreenCapture = doAdmin.disableScreenCapture;
+ // parentAdmin.accountTypesWithManagementDisabled.addAll(
+ // doAdmin.accountTypesWithManagementDisabled);
+
+ moveDoUserRestrictionsToCopeParent(doAdmin, parentAdmin);
+
+ // TODO(b/143516163): migrate network and security logging state, currently they are
+ // turned off when DO is removed.
+ }
+
+ private void moveDoUserRestrictionsToCopeParent(ActiveAdmin doAdmin, ActiveAdmin parentAdmin) {
+ if (doAdmin.userRestrictions == null) {
+ return;
+ }
+ for (final String restriction : doAdmin.userRestrictions.keySet()) {
+ if (UserRestrictionsUtils.canProfileOwnerOfOrganizationOwnedDeviceChange(restriction)) {
+ parentAdmin.userRestrictions.putBoolean(
+ restriction, doAdmin.userRestrictions.getBoolean(restriction));
+ }
+ }
+ }
+
/** Apply default restrictions that haven't been applied to profile owners yet. */
private void maybeSetDefaultProfileOwnerUserRestrictions() {
synchronized (getLockObject()) {
@@ -3625,6 +3748,9 @@
break;
case SystemService.PHASE_ACTIVITY_MANAGER_READY:
maybeStartSecurityLogMonitorOnActivityManagerReady();
+ synchronized (getLockObject()) {
+ maybeMigrateToProfileOnOrganizationOwnedDeviceLocked();
+ }
break;
case SystemService.PHASE_BOOT_COMPLETED:
ensureDeviceOwnerUserStarted(); // TODO Consider better place to do this.
@@ -7456,8 +7582,7 @@
return;
}
Objects.requireNonNull(who, "ComponentName is null");
- // TODO (b/145286957) Refactor security checks
- enforceDeviceOwnerOrProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
+ enforceProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
mInjector.binderWithCleanCallingIdentity(() ->
mInjector.settingsGlobalPutInt(Settings.Global.AUTO_TIME, enabled ? 1 : 0));
@@ -7478,7 +7603,7 @@
return false;
}
Objects.requireNonNull(who, "ComponentName is null");
- enforceDeviceOwnerOrProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
+ enforceProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
return mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) > 0;
}
@@ -7492,8 +7617,7 @@
return;
}
Objects.requireNonNull(who, "ComponentName is null");
- // TODO (b/145286957) Refactor security checks
- enforceDeviceOwnerOrProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
+ enforceProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
mInjector.binderWithCleanCallingIdentity(() ->
mInjector.settingsGlobalPutInt(Global.AUTO_TIME_ZONE, enabled ? 1 : 0));
@@ -7514,7 +7638,7 @@
return false;
}
Objects.requireNonNull(who, "ComponentName is null");
- enforceDeviceOwnerOrProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
+ enforceProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
return mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) > 0;
}
@@ -9060,23 +9184,22 @@
"Only profile owner, device owner and system may call this method.");
}
- private ActiveAdmin enforceDeviceOwnerOrProfileOwnerOnUser0OrProfileOwnerOrganizationOwned() {
+ private void enforceProfileOwnerOnUser0OrProfileOwnerOrganizationOwned() {
synchronized (getLockObject()) {
- // Check if there is a device owner
- ActiveAdmin deviceOwner = getActiveAdminWithPolicyForUidLocked(null,
- DeviceAdminInfo.USES_POLICY_DEVICE_OWNER, mInjector.binderGetCallingUid());
- if (deviceOwner != null) return deviceOwner;
+ // Check if there is a device owner or profile owner of an organization-owned device
+ ActiveAdmin owner = getActiveAdminWithPolicyForUidLocked(null,
+ DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER,
+ mInjector.binderGetCallingUid());
+ if (owner != null) {
+ return;
+ }
- ActiveAdmin profileOwner = getActiveAdminWithPolicyForUidLocked(null,
+ // Checks whether the caller is a profile owner on user 0 rather than
+ // checking whether the active admin is on user 0
+ owner = getActiveAdminWithPolicyForUidLocked(null,
DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, mInjector.binderGetCallingUid());
-
- // Check if there is a profile owner of an organization owned device
- if (isProfileOwnerOfOrganizationOwnedDevice(profileOwner)) return profileOwner;
-
- // Check if there is a profile owner called on user 0
- if (profileOwner != null) {
- enforceCallerSystemUserHandle();
- return profileOwner;
+ if (owner != null && owner.getUserHandle().isSystem()) {
+ return;
}
}
throw new SecurityException("No active admin found");
@@ -12922,37 +13045,43 @@
// Grant access under lock.
synchronized (getLockObject()) {
- // Sanity check: Make sure that the user has a profile owner and that the specified
- // component is the profile owner of that user.
- if (!isProfileOwner(who, userId)) {
- throw new IllegalArgumentException(String.format(
- "Component %s is not a Profile Owner of user %d",
- who.flattenToString(), userId));
- }
+ markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(who, userId);
+ }
+ }
- Slog.i(LOG_TAG, String.format(
- "Marking %s as profile owner on organization-owned device for user %d",
+ @GuardedBy("getLockObject()")
+ private void markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(
+ ComponentName who, int userId) {
+ // Sanity check: Make sure that the user has a profile owner and that the specified
+ // component is the profile owner of that user.
+ if (!isProfileOwner(who, userId)) {
+ throw new IllegalArgumentException(String.format(
+ "Component %s is not a Profile Owner of user %d",
who.flattenToString(), userId));
+ }
- // First, set restriction on removing the profile.
- mInjector.binderWithCleanCallingIdentity(() -> {
- // Clear restriction as user.
- UserHandle parentUser = mUserManager.getProfileParent(UserHandle.of(userId));
- if (!parentUser.isSystem()) {
- throw new IllegalStateException(
- String.format("Only the profile owner of a managed profile on the"
+ Slog.i(LOG_TAG, String.format(
+ "Marking %s as profile owner on organization-owned device for user %d",
+ who.flattenToString(), userId));
+
+ // First, set restriction on removing the profile.
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ // Clear restriction as user.
+ final UserHandle parentUser = mUserManager.getProfileParent(UserHandle.of(userId));
+ if (!parentUser.isSystem()) {
+ throw new IllegalStateException(
+ String.format("Only the profile owner of a managed profile on the"
+ " primary user can be granted access to device identifiers, not"
+ " on user %d", parentUser.getIdentifier()));
- }
+ }
- mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, true,
- parentUser);
- });
+ mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, true,
+ parentUser);
+ });
- // markProfileOwnerOfOrganizationOwnedDevice will trigger writing of the profile owner
- // data, no need to do it manually.
- mOwners.markProfileOwnerOfOrganizationOwnedDevice(userId);
- }
+ // markProfileOwnerOfOrganizationOwnedDevice will trigger writing of the profile owner
+ // data, no need to do it manually.
+ mOwners.markProfileOwnerOfOrganizationOwnedDevice(userId);
}
private void pushMeteredDisabledPackagesLocked(int userId) {
@@ -14916,4 +15045,10 @@
return packages == null ? Collections.EMPTY_LIST : packages;
}
}
+
+ private void logIfVerbose(String message) {
+ if (VERBOSE_LOG) {
+ Slog.d(LOG_TAG, message);
+ }
+ }
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0bd7392..109a6fd 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -218,7 +218,7 @@
private static final String STATS_COMPANION_LIFECYCLE_CLASS =
"com.android.server.stats.StatsCompanion$Lifecycle";
private static final String STATS_PULL_ATOM_SERVICE_CLASS =
- "com.android.server.stats.StatsPullAtomService";
+ "com.android.server.stats.pull.StatsPullAtomService";
private static final String USB_SERVICE_CLASS =
"com.android.server.usb.UsbService$Lifecycle";
private static final String MIDI_SERVICE_CLASS =
@@ -758,7 +758,7 @@
// note that we just booted, which might send out a rescue party if
// we're stuck in a runtime restart loop.
RescueParty.registerHealthObserver(mSystemContext);
- RescueParty.noteBoot(mSystemContext);
+ PackageWatchdog.getInstance(mSystemContext).noteBoot();
// Manages LEDs and display backlight so we need it to bring up the display.
t.traceBegin("StartLightsService");
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 96fedf9..3d9f11f 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -21,6 +21,7 @@
"services.core",
"services.net",
"service-jobscheduler",
+ "service-permission",
"androidx.test.runner",
"mockito-target-extended-minus-junit4",
"platform-test-annotations",
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index 30d89d3..c3602f8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -24,6 +24,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.RescueParty.LEVEL_FACTORY_RESET;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -144,7 +145,6 @@
doReturn(CURRENT_NETWORK_TIME_MILLIS).when(() -> RescueParty.getElapsedRealtime());
- RescueParty.resetAllThresholds();
FlagNamespaceUtils.resetKnownResetNamespacesFlagCounterForTest();
SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL,
@@ -160,28 +160,28 @@
@Test
public void testBootLoopDetectionWithExecutionForAllRescueLevels() {
- noteBoot(RescueParty.TRIGGER_COUNT);
+ noteBoot();
verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
- noteBoot(RescueParty.TRIGGER_COUNT);
+ noteBoot();
verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES);
assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
- noteBoot(RescueParty.TRIGGER_COUNT);
+ noteBoot();
verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS);
assertEquals(RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
- noteBoot(RescueParty.TRIGGER_COUNT);
+ noteBoot();
verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
- assertEquals(RescueParty.LEVEL_FACTORY_RESET,
+ assertEquals(LEVEL_FACTORY_RESET,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
}
@@ -208,48 +208,15 @@
notePersistentAppCrash();
verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
- assertEquals(RescueParty.LEVEL_FACTORY_RESET,
- SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
- }
-
- @Test
- public void testBootLoopDetectionWithWrongInterval() {
- noteBoot(RescueParty.TRIGGER_COUNT - 1);
-
- // last boot is just outside of the boot loop detection window
- doReturn(CURRENT_NETWORK_TIME_MILLIS + RescueParty.BOOT_TRIGGER_WINDOW_MILLIS + 1).when(
- () -> RescueParty.getElapsedRealtime());
- noteBoot(/*numTimes=*/1);
-
- assertEquals(RescueParty.LEVEL_NONE,
- SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
- }
-
- @Test
- public void testBootLoopDetectionWithProperInterval() {
- noteBoot(RescueParty.TRIGGER_COUNT - 1);
-
- // last boot is just inside of the boot loop detection window
- doReturn(CURRENT_NETWORK_TIME_MILLIS + RescueParty.BOOT_TRIGGER_WINDOW_MILLIS).when(
- () -> RescueParty.getElapsedRealtime());
- noteBoot(/*numTimes=*/1);
-
- verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
- assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
- SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
- }
-
- @Test
- public void testBootLoopDetectionWithWrongTriggerCount() {
- noteBoot(RescueParty.TRIGGER_COUNT - 1);
- assertEquals(RescueParty.LEVEL_NONE,
+ assertEquals(LEVEL_FACTORY_RESET,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
}
@Test
public void testIsAttemptingFactoryReset() {
- noteBoot(RescueParty.TRIGGER_COUNT * 4);
-
+ for (int i = 0; i < LEVEL_FACTORY_RESET; i++) {
+ noteBoot();
+ }
verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
assertTrue(RescueParty.isAttemptingFactoryReset());
}
@@ -306,7 +273,7 @@
// Ensure that no action is taken for cases where the failure reason is unknown
SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
- RescueParty.LEVEL_FACTORY_RESET));
+ LEVEL_FACTORY_RESET));
assertEquals(observer.onHealthCheckFailed(null, PackageWatchdog.FAILURE_REASON_UNKNOWN),
PackageHealthObserverImpact.USER_IMPACT_NONE);
@@ -342,7 +309,7 @@
SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
- RescueParty.LEVEL_FACTORY_RESET));
+ LEVEL_FACTORY_RESET));
assertEquals(observer.onHealthCheckFailed(null,
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING),
PackageHealthObserverImpact.USER_IMPACT_HIGH);
@@ -366,10 +333,8 @@
eq(resetMode), anyInt()));
}
- private void noteBoot(int numTimes) {
- for (int i = 0; i < numTimes; i++) {
- RescueParty.noteBoot(mMockContext);
- }
+ private void noteBoot() {
+ RescuePartyObserver.getInstance(mMockContext).executeBootLoopMitigation();
}
private void notePersistentAppCrash() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index f2e118d..e0e374b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -50,6 +50,7 @@
import android.os.BatteryManagerInternal;
import android.os.Looper;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import com.android.server.AppStateTracker;
@@ -95,6 +96,7 @@
.initMocks(this)
.strictness(Strictness.LENIENT)
.mockStatic(LocalServices.class)
+ .mockStatic(ServiceManager.class)
.startMocking();
// Called in JobSchedulerService constructor.
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index ace15eb..556f636 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -44,6 +44,7 @@
"servicestests-utils",
"service-appsearch",
"service-jobscheduler",
+ "service-permission",
// TODO: remove once Android migrates to JUnit 4.12,
// which provides assertThrows
"testng",
diff --git a/services/tests/servicestests/res/raw/comp_device_owner.xml b/services/tests/servicestests/res/raw/comp_device_owner.xml
new file mode 100644
index 0000000..0a10242
--- /dev/null
+++ b/services/tests/servicestests/res/raw/comp_device_owner.xml
@@ -0,0 +1,8 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<root>
+ <device-owner package="com.android.frameworks.servicestests"
+ name=""
+ component="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1"
+ userRestrictionsMigrated="true" />
+ <device-owner-context userId="0" />
+</root>
diff --git a/services/tests/servicestests/res/raw/comp_policies_primary.xml b/services/tests/servicestests/res/raw/comp_policies_primary.xml
new file mode 100644
index 0000000..1e1a0ef
--- /dev/null
+++ b/services/tests/servicestests/res/raw/comp_policies_primary.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<policies setup-complete="true" provisioning-state="3">
+ <admin name="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1">
+ <policies flags="991"/>
+ <password-history-length value="33" />
+ </admin>
+</policies>
diff --git a/services/tests/servicestests/res/raw/comp_policies_profile_another_package.xml b/services/tests/servicestests/res/raw/comp_policies_profile_another_package.xml
new file mode 100644
index 0000000..141315e
--- /dev/null
+++ b/services/tests/servicestests/res/raw/comp_policies_profile_another_package.xml
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<policies setup-complete="true" provisioning-state="3">
+ <admin name="com.another.package.name/whatever.random.class">
+ <policies flags="991"/>
+ </admin>
+</policies>
diff --git a/services/tests/servicestests/res/raw/comp_policies_profile_same_package.xml b/services/tests/servicestests/res/raw/comp_policies_profile_same_package.xml
new file mode 100644
index 0000000..c874dcc
--- /dev/null
+++ b/services/tests/servicestests/res/raw/comp_policies_profile_same_package.xml
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<policies setup-complete="true" provisioning-state="3">
+ <admin name="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1">
+ <policies flags="991"/>
+ </admin>
+</policies>
diff --git a/services/tests/servicestests/res/raw/comp_profile_owner_another_package.xml b/services/tests/servicestests/res/raw/comp_profile_owner_another_package.xml
new file mode 100644
index 0000000..d65ba78
--- /dev/null
+++ b/services/tests/servicestests/res/raw/comp_profile_owner_another_package.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<root>
+ <profile-owner package="com.another.package.name"
+ name="com.another.package.name"
+ component="com.another.package.name/whatever.random.class"
+ userRestrictionsMigrated="true"/>
+</root>
diff --git a/services/tests/servicestests/res/raw/comp_profile_owner_same_package.xml b/services/tests/servicestests/res/raw/comp_profile_owner_same_package.xml
new file mode 100644
index 0000000..7f98c91c
--- /dev/null
+++ b/services/tests/servicestests/res/raw/comp_profile_owner_same_package.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<root>
+ <profile-owner package="com.android.frameworks.servicestests"
+ name="com.android.frameworks.servicestests"
+ component="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1"
+ userRestrictionsMigrated="true"/>
+</root>
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
new file mode 100644
index 0000000..09466e7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.accessibility.magnification;
+
+
+import static org.mockito.Mockito.verify;
+
+import android.os.RemoteException;
+import android.view.Display;
+import android.view.accessibility.IWindowMagnificationConnection;
+import android.view.accessibility.IWindowMagnificationConnectionCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for WindowMagnificationConnectionWrapper. We don't test {@code
+ * WindowMagnificationConnectionWrapper#linkToDeath(IBinder.DeathRecipient)} since it's tested in
+ * {@link WindowMagnificationManagerTest}.
+ */
+public class WindowMagnificationConnectionWrapperTest {
+
+ private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
+
+ @Mock
+ private IWindowMagnificationConnection mConnection;
+ @Mock
+ private IWindowMagnificationConnectionCallback mCallback;
+ private WindowMagnificationConnectionWrapper mConnectionWrapper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mConnectionWrapper = new WindowMagnificationConnectionWrapper(mConnection);
+ }
+
+ @Test
+ public void enableWindowMagnification() throws RemoteException {
+ mConnectionWrapper.enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f);
+ verify(mConnection).enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f);
+ }
+
+ @Test
+ public void setScale() throws RemoteException {
+ mConnectionWrapper.setScale(TEST_DISPLAY, 3.0f);
+ verify(mConnection).setScale(TEST_DISPLAY, 3.0f);
+ }
+
+ @Test
+ public void disableWindowMagnification() throws RemoteException {
+ mConnectionWrapper.disableWindowMagnification(TEST_DISPLAY);
+ verify(mConnection).disableWindowMagnification(TEST_DISPLAY);
+ }
+
+ @Test
+ public void moveWindowMagnifier() throws RemoteException {
+ mConnectionWrapper.moveWindowMagnifier(0, 100, 150);
+ verify(mConnection).moveWindowMagnifier(0, 100, 150);
+ }
+
+ @Test
+ public void setMirrorWindowCallback() throws RemoteException {
+ mConnectionWrapper.setConnectionCallback(mCallback);
+ verify(mConnection).setConnectionCallback(mCallback);
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
new file mode 100644
index 0000000..780a6c0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.accessibility.magnification;
+
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.view.accessibility.IWindowMagnificationConnection;
+import android.view.accessibility.IWindowMagnificationConnectionCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for WindowMagnificationManager.
+ */
+public class WindowMagnificationManagerTest {
+
+ private MockWindowMagnificationConnection mMockConnection;
+ private WindowMagnificationManager mWindowMagnificationManager;
+
+ @Before
+ public void setUp() {
+ mMockConnection = new MockWindowMagnificationConnection();
+ mWindowMagnificationManager = new WindowMagnificationManager();
+ }
+
+ @Test
+ public void setConnection_connectionIsNull_wrapperIsNullAndLinkToDeath() {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ assertNotNull(mWindowMagnificationManager.mConnectionWrapper);
+ verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
+ }
+
+ @Test
+ public void setConnection_connectionIsNull_setMirrorWindowCallbackAndHasWrapper()
+ throws RemoteException {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+
+ assertNotNull(mWindowMagnificationManager.mConnectionWrapper);
+ verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
+ verify(mMockConnection.getConnection()).setConnectionCallback(
+ any(IWindowMagnificationConnectionCallback.class));
+ }
+
+ @Test
+ public void binderDied_hasConnection_wrapperIsNullAndUnlinkToDeath() {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+
+ mMockConnection.getDeathRecipient().binderDied();
+
+ assertNull(mWindowMagnificationManager.mConnectionWrapper);
+ verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(),
+ 0);
+ }
+
+ /**
+ * This test simulates {@link WindowMagnificationManager#setConnection} is called by thread A
+ * and then the former connection is called by thread B. In this situation we should keep the
+ * new connection.
+ */
+ @Test
+ public void
+ setSecondConnectionAndFormerConnectionBinderDead_hasWrapperAndNotCallUnlinkToDeath() {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ MockWindowMagnificationConnection secondConnection =
+ new MockWindowMagnificationConnection();
+
+ mWindowMagnificationManager.setConnection(secondConnection.getConnection());
+ mMockConnection.getDeathRecipient().binderDied();
+
+ assertNotNull(mWindowMagnificationManager.mConnectionWrapper);
+ verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(), 0);
+ verify(secondConnection.asBinder(), never()).unlinkToDeath(
+ secondConnection.getDeathRecipient(), 0);
+ }
+
+ @Test
+ public void setNullConnection_hasConnection_wrapperIsNull() throws RemoteException {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+
+ mWindowMagnificationManager.setConnection(null);
+
+ assertNull(mWindowMagnificationManager.mConnectionWrapper);
+ verify(mMockConnection.getConnection()).setConnectionCallback(null);
+ }
+
+ private static class MockWindowMagnificationConnection {
+
+ private final IWindowMagnificationConnection mConnection;
+ private final Binder mBinder;
+ private IBinder.DeathRecipient mDeathRecipient;
+
+ MockWindowMagnificationConnection() {
+ mConnection = mock(IWindowMagnificationConnection.class);
+ mBinder = mock(Binder.class);
+ when(mConnection.asBinder()).thenReturn(mBinder);
+ doAnswer((invocation) -> {
+ mDeathRecipient = invocation.getArgument(0);
+ return null;
+ }).when(mBinder).linkToDeath(
+ any(IBinder.DeathRecipient.class), eq(0));
+ }
+
+ IWindowMagnificationConnection getConnection() {
+ return mConnection;
+ }
+
+ public IBinder.DeathRecipient getDeathRecipient() {
+ return mDeathRecipient;
+ }
+
+ Binder asBinder() {
+ return mBinder;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/impl/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/impl/AppSearchImplTest.java
new file mode 100644
index 0000000..4195679
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appsearch/impl/AppSearchImplTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.appsearch.impl;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.expectThrows;
+
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.os.UserHandle;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.google.android.icing.proto.IndexingConfig;
+import com.google.android.icing.proto.PropertyConfigProto;
+import com.google.android.icing.proto.SchemaProto;
+import com.google.android.icing.proto.SchemaTypeConfigProto;
+import com.google.android.icing.proto.TermMatchType;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class AppSearchImplTest {
+ private final Context mContext = InstrumentationRegistry.getContext();
+ private final @UserIdInt int mUserId = UserHandle.getCallingUserId();
+
+ @Test
+ public void testRewriteSchemaTypes() {
+ SchemaProto inSchema = SchemaProto.newBuilder()
+ .addTypes(SchemaTypeConfigProto.newBuilder()
+ .setSchemaType("TestType")
+ .addProperties(PropertyConfigProto.newBuilder()
+ .setPropertyName("subject")
+ .setDataType(PropertyConfigProto.DataType.Code.STRING)
+ .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
+ .setIndexingConfig(
+ IndexingConfig.newBuilder()
+ .setTokenizerType(
+ IndexingConfig.TokenizerType.Code.PLAIN)
+ .setTermMatchType(TermMatchType.Code.PREFIX)
+ .build()
+ ).build()
+ ).addProperties(PropertyConfigProto.newBuilder()
+ .setPropertyName("link")
+ .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT)
+ .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
+ .setSchemaType("RefType")
+ .build()
+ ).build()
+ ).build();
+
+ SchemaProto expectedSchema = SchemaProto.newBuilder()
+ .addTypes(SchemaTypeConfigProto.newBuilder()
+ .setSchemaType("com.android.server.appsearch.impl@42:TestType")
+ .addProperties(PropertyConfigProto.newBuilder()
+ .setPropertyName("subject")
+ .setDataType(PropertyConfigProto.DataType.Code.STRING)
+ .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
+ .setIndexingConfig(
+ IndexingConfig.newBuilder()
+ .setTokenizerType(
+ IndexingConfig.TokenizerType.Code.PLAIN)
+ .setTermMatchType(TermMatchType.Code.PREFIX)
+ .build()
+ ).build()
+ ).addProperties(PropertyConfigProto.newBuilder()
+ .setPropertyName("link")
+ .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT)
+ .setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
+ .setSchemaType("com.android.server.appsearch.impl@42:RefType")
+ .build()
+ ).build()
+ ).build();
+
+ AppSearchImpl impl = new AppSearchImpl(mContext, mUserId);
+ SchemaProto.Builder actualSchema = inSchema.toBuilder();
+ impl.rewriteSchemaTypes("com.android.server.appsearch.impl@42:", actualSchema);
+
+ assertThat(actualSchema.build()).isEqualTo(expectedSchema);
+ }
+
+ @Test
+ public void testPackageNotFound() {
+ AppSearchImpl impl = new AppSearchImpl(mContext, mUserId);
+ IllegalStateException e = expectThrows(
+ IllegalStateException.class,
+ () -> impl.setSchema(
+ /*callingUid=*/Integer.MAX_VALUE, SchemaProto.getDefaultInstance()));
+ assertThat(e).hasMessageThat().contains("Failed to look up package name");
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index 5f1f308..46b8371 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -15,6 +15,10 @@
*/
package com.android.server.devicepolicy;
+import static android.os.UserHandle.USER_SYSTEM;
+
+import static com.android.server.devicepolicy.DpmTestUtils.writeInputStreamToFile;
+
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
@@ -22,12 +26,14 @@
import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyManagerInternal;
+import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import com.android.frameworks.servicestests.R;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.devicepolicy.DevicePolicyManagerServiceTestable.OwnersTestable;
@@ -37,9 +43,13 @@
import java.util.Map;
import java.util.Set;
+// TODO (b/143516163): Fix old test cases and put into presubmit.
public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
private static final String USER_TYPE_EMPTY = "";
+ private static final int COPE_ADMIN1_APP_ID = 123;
+ private static final int COPE_ANOTHER_ADMIN_APP_ID = 125;
+ private static final int COPE_PROFILE_USER_ID = 11;
private DpmMockContext mContext;
@@ -85,7 +95,7 @@
// Set up UserManager
when(getServices().userManagerInternal.getBaseUserRestrictions(
- eq(UserHandle.USER_SYSTEM))).thenReturn(DpmTestUtils.newRestrictions(
+ eq(USER_SYSTEM))).thenReturn(DpmTestUtils.newRestrictions(
UserManager.DISALLOW_ADD_USER,
UserManager.DISALLOW_RECORD_AUDIO));
@@ -137,7 +147,7 @@
}
assertTrue(dpms.mOwners.hasDeviceOwner());
- assertFalse(dpms.mOwners.hasProfileOwner(UserHandle.USER_SYSTEM));
+ assertFalse(dpms.mOwners.hasProfileOwner(USER_SYSTEM));
assertTrue(dpms.mOwners.hasProfileOwner(10));
assertTrue(dpms.mOwners.hasProfileOwner(11));
assertFalse(dpms.mOwners.hasProfileOwner(12));
@@ -145,7 +155,7 @@
// Now all information should be migrated.
assertFalse(dpms.mOwners.getDeviceOwnerUserRestrictionsNeedsMigration());
assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(
- UserHandle.USER_SYSTEM));
+ USER_SYSTEM));
assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(10));
assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(11));
assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(12));
@@ -155,7 +165,7 @@
DpmTestUtils.newRestrictions(
UserManager.DISALLOW_RECORD_AUDIO
),
- newBaseRestrictions.get(UserHandle.USER_SYSTEM));
+ newBaseRestrictions.get(USER_SYSTEM));
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(
@@ -214,7 +224,7 @@
// Set up UserManager
when(getServices().userManagerInternal.getBaseUserRestrictions(
- eq(UserHandle.USER_SYSTEM))).thenReturn(DpmTestUtils.newRestrictions(
+ eq(USER_SYSTEM))).thenReturn(DpmTestUtils.newRestrictions(
UserManager.DISALLOW_ADD_USER,
UserManager.DISALLOW_RECORD_AUDIO,
UserManager.DISALLOW_SMS,
@@ -249,19 +259,19 @@
mContext.binder.restoreCallingIdentity(ident);
}
assertFalse(dpms.mOwners.hasDeviceOwner());
- assertTrue(dpms.mOwners.hasProfileOwner(UserHandle.USER_SYSTEM));
+ assertTrue(dpms.mOwners.hasProfileOwner(USER_SYSTEM));
// Now all information should be migrated.
assertFalse(dpms.mOwners.getDeviceOwnerUserRestrictionsNeedsMigration());
assertFalse(dpms.mOwners.getProfileOwnerUserRestrictionsNeedsMigration(
- UserHandle.USER_SYSTEM));
+ USER_SYSTEM));
// Check the new base restrictions.
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(
UserManager.DISALLOW_RECORD_AUDIO
),
- newBaseRestrictions.get(UserHandle.USER_SYSTEM));
+ newBaseRestrictions.get(USER_SYSTEM));
// Check the new owner restrictions.
DpmTestUtils.assertRestrictions(
@@ -270,7 +280,7 @@
UserManager.DISALLOW_SMS,
UserManager.DISALLOW_OUTGOING_CALLS
),
- dpms.getProfileOwnerAdminLocked(UserHandle.USER_SYSTEM).ensureUserRestrictions());
+ dpms.getProfileOwnerAdminLocked(USER_SYSTEM).ensureUserRestrictions());
}
// Test setting default restrictions for managed profile.
@@ -332,4 +342,92 @@
assertEquals(alreadySet.size(), 1);
assertTrue(alreadySet.contains(UserManager.DISALLOW_BLUETOOTH_SHARING));
}
+
+ public void testCompMigrationUnAffiliated_skipped() throws Exception {
+ prepareAdmin1AsDo();
+ prepareAdminAnotherPackageAsPo(COPE_PROFILE_USER_ID);
+
+ final DevicePolicyManagerServiceTestable dpms;
+ dpms = bootDpmsUp();
+
+ // DO should still be DO since no migration should happen.
+ assertTrue(dpms.mOwners.hasDeviceOwner());
+ }
+
+ public void testCompMigrationAffiliated() throws Exception {
+ prepareAdmin1AsDo();
+ prepareAdmin1AsPo(COPE_PROFILE_USER_ID);
+
+ // Secure lock screen is needed for password policy APIs to work.
+ when(getServices().lockPatternUtils.hasSecureLockScreen()).thenReturn(true);
+
+ final DevicePolicyManagerServiceTestable dpms;
+ dpms = bootDpmsUp();
+
+ // DO should cease to be DO.
+ assertFalse(dpms.mOwners.hasDeviceOwner());
+
+ final DpmMockContext poContext = new DpmMockContext(getServices(), mRealTestContext);
+ poContext.binder.callingUid = UserHandle.getUid(COPE_PROFILE_USER_ID, COPE_ADMIN1_APP_ID);
+
+ runAsCaller(poContext, dpms, dpm -> {
+ // Check that DO policy is now set on parent instance.
+ assertEquals(33, dpm.getParentProfileInstance(admin1).getPasswordHistoryLength(admin1));
+ // And NOT set on profile instance.
+ assertEquals(0, dpm.getPasswordHistoryLength(admin1));
+
+ // TODO(b/143516163): verify more policies.
+ });
+ }
+
+ private DevicePolicyManagerServiceTestable bootDpmsUp() {
+ DevicePolicyManagerServiceTestable dpms;
+ final long ident = mContext.binder.clearCallingIdentity();
+ try {
+ LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+
+ dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
+
+ dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY);
+ dpms.systemReady(SystemService.PHASE_ACTIVITY_MANAGER_READY);
+ dpms.systemReady(SystemService.PHASE_BOOT_COMPLETED);
+ } finally {
+ mContext.binder.restoreCallingIdentity(ident);
+ }
+ return dpms;
+ }
+
+ private void prepareAdmin1AsDo() throws Exception {
+ setUpPackageManagerForAdmin(admin1, UserHandle.getUid(USER_SYSTEM, COPE_ADMIN1_APP_ID));
+ final int xmlResource = R.raw.comp_policies_primary;
+ writeInputStreamToFile(getRawStream(xmlResource),
+ (new File(getServices().systemUserDataDir, "device_policies.xml"))
+ .getAbsoluteFile());
+ writeInputStreamToFile(getRawStream(R.raw.comp_device_owner),
+ (new File(getServices().dataDir, "device_owner_2.xml"))
+ .getAbsoluteFile());
+ }
+
+ private void prepareAdmin1AsPo(int profileUserId) throws Exception {
+ preparePo(profileUserId, admin1, R.raw.comp_profile_owner_same_package,
+ R.raw.comp_policies_profile_same_package, COPE_ADMIN1_APP_ID);
+ }
+
+ private void prepareAdminAnotherPackageAsPo(int profileUserId) throws Exception {
+ preparePo(profileUserId, adminAnotherPackage, R.raw.comp_profile_owner_another_package,
+ R.raw.comp_policies_profile_another_package, COPE_ANOTHER_ADMIN_APP_ID);
+ }
+
+ private void preparePo(int profileUserId, ComponentName admin, int profileOwnerXmlResId,
+ int policyXmlResId, int adminAppId) throws Exception {
+ final File profileDir = getServices().addUser(profileUserId, 0,
+ UserManager.USER_TYPE_PROFILE_MANAGED, USER_SYSTEM /* profile group */);
+ setUpPackageManagerForFakeAdmin(
+ admin, UserHandle.getUid(profileUserId, adminAppId), admin1);
+ writeInputStreamToFile(getRawStream(policyXmlResId),
+ (new File(profileDir, "device_policies.xml")).getAbsoluteFile());
+ writeInputStreamToFile(getRawStream(profileOwnerXmlResId),
+ (new File(profileDir, "profile_owner.xml")).getAbsoluteFile());
+ }
+
}
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 2cb7103..bfadeea 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -58,7 +58,6 @@
import static org.testng.Assert.assertThrows;
import android.Manifest.permission;
-import android.annotation.RawRes;
import android.app.Activity;
import android.app.AppOpsManager;
import android.app.Notification;
@@ -112,7 +111,6 @@
import org.mockito.stubbing.Answer;
import java.io.File;
-import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -5810,10 +5808,6 @@
return new File(parentDir, "device_policies.xml");
}
- private InputStream getRawStream(@RawRes int id) {
- return mRealTestContext.getResources().openRawResource(id);
- }
-
private void setUserSetupCompleteForUser(boolean isUserSetupComplete, int userhandle) {
when(getServices().settings.settingsSecureGetIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0,
userhandle)).thenReturn(isUserSetupComplete ? 1 : 0);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index a34c2ff..9a1a5fb 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -24,6 +24,7 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
+import android.annotation.RawRes;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
@@ -36,6 +37,7 @@
import android.os.UserHandle;
import android.test.AndroidTestCase;
+import java.io.InputStream;
import java.util.List;
public abstract class DpmTestBase extends AndroidTestCase {
@@ -256,4 +258,8 @@
invocation -> invocation.getArguments()[1]
);
}
+
+ protected InputStream getRawStream(@RawRes int id) {
+ return mRealTestContext.getResources().openRawResource(id);
+ }
}
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 6c2c144..068daf5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -15,6 +15,7 @@
*/
package com.android.server.devicepolicy;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -236,6 +237,13 @@
return ui == null ? null : getUserInfo(ui.profileGroupId);
}
);
+ when(userManager.getProfileParent(any(UserHandle.class))).thenAnswer(
+ invocation -> {
+ final UserHandle userHandle = (UserHandle) invocation.getArguments()[0];
+ final UserInfo ui = getUserInfo(userHandle.getIdentifier());
+ return ui == null ? UserHandle.USER_NULL : UserHandle.of(ui.profileGroupId);
+ }
+ );
when(userManager.getProfiles(anyInt())).thenAnswer(
invocation -> {
final int userId12 = (int) invocation.getArguments()[0];
diff --git a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
index 5aed194..a1810b9 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
@@ -16,6 +16,8 @@
package com.android.server.integrity;
+import static com.android.server.integrity.model.IndexingFileConstants.INDEXING_BLOCK_SIZE;
+
import static com.google.common.truth.Truth.assertThat;
import android.content.integrity.AppInstallMetadata;
@@ -135,14 +137,15 @@
Arrays.asList(packageNameRule, packageCertRule, versionCodeRule, randomRule);
mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, rules);
- AppInstallMetadata appInstallMetadata = new AppInstallMetadata.Builder()
- .setPackageName(packageName)
- .setAppCertificate(packageCert)
- .setVersionCode(version)
- .setInstallerName("abc")
- .setInstallerCertificate("abc")
- .setIsPreInstalled(true)
- .build();
+ AppInstallMetadata appInstallMetadata =
+ new AppInstallMetadata.Builder()
+ .setPackageName(packageName)
+ .setAppCertificate(packageCert)
+ .setVersionCode(version)
+ .setInstallerName("abc")
+ .setInstallerCertificate("abc")
+ .setIsPreInstalled(true)
+ .build();
List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata);
assertThat(rulesFetched)
@@ -158,13 +161,14 @@
// Create a rule set with 2500 package name indexed, 2500 app certificate indexed and
// 500 unindexed rules.
List<Rule> rules = new ArrayList<>();
+ int unindexedRuleCount = 70;
for (int i = 0; i < 2500; i++) {
rules.add(getPackageNameIndexedRule(String.format("%s%04d", packageName, i)));
rules.add(getAppCertificateIndexedRule(String.format("%s%04d", appCertificate, i)));
}
- for (int i = 0; i < 70; i++) {
+ for (int i = 0; i < unindexedRuleCount; i++) {
rules.add(getInstallerCertificateRule(String.format("%s%04d", installerName, i)));
}
@@ -174,18 +178,20 @@
// Read the rules for a specific rule.
String installedPackageName = String.format("%s%04d", packageName, 264);
String installedAppCertificate = String.format("%s%04d", appCertificate, 1264);
- AppInstallMetadata appInstallMetadata = new AppInstallMetadata.Builder()
- .setPackageName(installedPackageName)
- .setAppCertificate(installedAppCertificate)
- .setVersionCode(250)
- .setInstallerName("abc")
- .setInstallerCertificate("abc")
- .setIsPreInstalled(true)
- .build();
+ AppInstallMetadata appInstallMetadata =
+ new AppInstallMetadata.Builder()
+ .setPackageName(installedPackageName)
+ .setAppCertificate(installedAppCertificate)
+ .setVersionCode(250)
+ .setInstallerName("abc")
+ .setInstallerCertificate("abc")
+ .setIsPreInstalled(true)
+ .build();
List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata);
// Verify that we do not load all the rules and we have the necessary rules to evaluate.
- assertThat(rulesFetched.size()).isEqualTo(270);
+ assertThat(rulesFetched.size())
+ .isEqualTo(INDEXING_BLOCK_SIZE * 2 + unindexedRuleCount);
assertThat(rulesFetched)
.containsAllOf(
getPackageNameIndexedRule(installedPackageName),
@@ -195,27 +201,38 @@
private Rule getPackageNameIndexedRule(String packageName) {
return new Rule(
new StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME,
- packageName,
- /* isHashedValue= */ false),
+ AtomicFormula.PACKAGE_NAME, packageName, /* isHashedValue= */ false),
Rule.DENY);
}
private Rule getAppCertificateIndexedRule(String appCertificate) {
return new Rule(
new StringAtomicFormula(
- AtomicFormula.APP_CERTIFICATE,
- appCertificate,
- /* isHashedValue= */ false),
+ AtomicFormula.APP_CERTIFICATE, appCertificate, /* isHashedValue= */ false),
Rule.DENY);
}
private Rule getInstallerCertificateRule(String installerCert) {
return new Rule(
new StringAtomicFormula(
- AtomicFormula.INSTALLER_NAME,
- installerCert,
- /* isHashedValue= */ false),
+ AtomicFormula.INSTALLER_NAME, installerCert, /* isHashedValue= */ false),
Rule.DENY);
}
+
+ @Test
+ public void testStagingDirectoryCleared() throws Exception {
+ // We must push rules two times to ensure that staging directory is empty because we cleared
+ // it, rather than because original rules directory is empty.
+ mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST);
+ mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST);
+
+ assertStagingDirectoryCleared();
+ }
+
+ private void assertStagingDirectoryCleared() {
+ File stagingDir = new File(mTmpDir, "integrity_staging");
+ assertThat(stagingDir.exists()).isTrue();
+ assertThat(stagingDir.isDirectory()).isTrue();
+ assertThat(stagingDir.listFiles()).isEmpty();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/stats/IonMemoryUtilTest.java b/services/tests/servicestests/src/com/android/server/stats/pull/IonMemoryUtilTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/stats/IonMemoryUtilTest.java
rename to services/tests/servicestests/src/com/android/server/stats/pull/IonMemoryUtilTest.java
index 8cbf8e5..d4d4b4d 100644
--- a/services/tests/servicestests/src/com/android/server/stats/IonMemoryUtilTest.java
+++ b/services/tests/servicestests/src/com/android/server/stats/pull/IonMemoryUtilTest.java
@@ -13,16 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.stats;
+package com.android.server.stats.pull;
-import static com.android.server.stats.IonMemoryUtil.parseIonHeapSizeFromDebugfs;
-import static com.android.server.stats.IonMemoryUtil.parseProcessIonHeapSizesFromDebugfs;
+import static com.android.server.stats.pull.IonMemoryUtil.parseIonHeapSizeFromDebugfs;
+import static com.android.server.stats.pull.IonMemoryUtil.parseProcessIonHeapSizesFromDebugfs;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.filters.SmallTest;
-import com.android.server.stats.IonMemoryUtil.IonAllocations;
+import com.android.server.stats.pull.IonMemoryUtil.IonAllocations;
import org.junit.Test;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 7ac45f0..6f16574 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -227,6 +227,7 @@
assertEquals(expected.getLightColor(), actual.getLightColor());
assertEquals(expected.getParentChannelId(), actual.getParentChannelId());
assertEquals(expected.getConversationId(), actual.getConversationId());
+ assertEquals(expected.isDemoted(), actual.isDemoted());
}
private void compareChannelsParentChild(NotificationChannel parent,
@@ -355,6 +356,7 @@
channel2.setVibrationPattern(new long[]{100, 67, 145, 156});
channel2.setLightColor(Color.BLUE);
channel2.setConversationId("id1", "conversation");
+ channel2.setDemoted(true);
mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index a3e94599..ad63d07 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
@@ -56,6 +58,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.assertNull;
import static org.junit.Assert.assertTrue;
@@ -1175,4 +1178,160 @@
verify(mActivity).removeFromHistory(anyString());
}
+
+ @Test
+ public void testActivityOverridesProcessConfig() {
+ final WindowProcessController wpc = mActivity.app;
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertFalse(wpc.registeredForDisplayConfigChanges());
+
+ final ActivityRecord secondaryDisplayActivity =
+ createActivityOnDisplay(false /* defaultDisplay */, null /* process */);
+
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, mActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+ assertNotEquals(mActivity.getConfiguration(),
+ secondaryDisplayActivity.getConfiguration());
+ }
+
+ @Test
+ public void testActivityOverridesProcessConfig_TwoActivities() {
+ final WindowProcessController wpc = mActivity.app;
+ assertTrue(wpc.registeredForActivityConfigChanges());
+
+ final Task firstTaskRecord = mActivity.getTask();
+ final ActivityRecord secondActivityRecord =
+ new ActivityBuilder(mService).setTask(firstTaskRecord).setUseProcess(wpc).build();
+
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, secondActivityRecord.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+ }
+
+ @Test
+ public void testActivityOverridesProcessConfig_TwoActivities_SecondaryDisplay() {
+ final WindowProcessController wpc = mActivity.app;
+ assertTrue(wpc.registeredForActivityConfigChanges());
+
+ final ActivityRecord secondActivityRecord =
+ new ActivityBuilder(mService).setTask(mTask).setUseProcess(wpc).build();
+
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, secondActivityRecord.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+ }
+
+ @Test
+ public void testActivityOverridesProcessConfig_TwoActivities_DifferentTasks() {
+ final WindowProcessController wpc = mActivity.app;
+ assertTrue(wpc.registeredForActivityConfigChanges());
+
+ final ActivityRecord secondActivityRecord =
+ createActivityOnDisplay(true /* defaultDisplay */, wpc);
+
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, secondActivityRecord.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+ }
+
+ @Test
+ public void testActivityOnDifferentDisplayUpdatesProcessOverride() {
+ final ActivityRecord secondaryDisplayActivity =
+ createActivityOnDisplay(false /* defaultDisplay */, null /* process */);
+ final WindowProcessController wpc = secondaryDisplayActivity.app;
+ assertTrue(wpc.registeredForActivityConfigChanges());
+
+ final ActivityRecord secondActivityRecord =
+ createActivityOnDisplay(true /* defaultDisplay */, wpc);
+
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, secondActivityRecord.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+ assertFalse(wpc.registeredForDisplayConfigChanges());
+ }
+
+ @Test
+ public void testActivityReparentChangesProcessOverride() {
+ final WindowProcessController wpc = mActivity.app;
+ final Task initialTask = mActivity.getTask();
+ final Configuration initialConf =
+ new Configuration(mActivity.getMergedOverrideConfiguration());
+ assertEquals(0, mActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+ assertTrue(wpc.registeredForActivityConfigChanges());
+
+ // Create a new task with custom config to reparent the activity to.
+ final Task newTask =
+ new TaskBuilder(mSupervisor).setStack(initialTask.getStack()).build();
+ final Configuration newConfig = newTask.getConfiguration();
+ newConfig.densityDpi += 100;
+ newTask.onRequestedOverrideConfigurationChanged(newConfig);
+ assertEquals(newTask.getConfiguration().densityDpi, newConfig.densityDpi);
+
+ // Reparent the activity and verify that config override changed.
+ mActivity.reparent(newTask, 0 /* top */, "test");
+ assertEquals(mActivity.getConfiguration().densityDpi, newConfig.densityDpi);
+ assertEquals(mActivity.getMergedOverrideConfiguration().densityDpi, newConfig.densityDpi);
+
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration());
+ assertEquals(0, mActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+ }
+
+ @Test
+ public void testActivityReparentDoesntClearProcessOverride_TwoActivities() {
+ final WindowProcessController wpc = mActivity.app;
+ final Configuration initialConf =
+ new Configuration(mActivity.getMergedOverrideConfiguration());
+ final Task initialTask = mActivity.getTask();
+ final ActivityRecord secondActivity = new ActivityBuilder(mService).setTask(initialTask)
+ .setUseProcess(wpc).build();
+
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, secondActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+
+ // Create a new task with custom config to reparent the second activity to.
+ final Task newTask =
+ new TaskBuilder(mSupervisor).setStack(initialTask.getStack()).build();
+ final Configuration newConfig = newTask.getConfiguration();
+ newConfig.densityDpi += 100;
+ newTask.onRequestedOverrideConfigurationChanged(newConfig);
+
+ // Reparent the activity and verify that config override changed.
+ secondActivity.reparent(newTask, 0 /* top */, "test");
+
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, secondActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+ assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration());
+
+ // Reparent the first activity and verify that config override didn't change.
+ mActivity.reparent(newTask, 1 /* top */, "test");
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, secondActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+ assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration());
+ }
+
+ /**
+ * Creates an activity on display. For non-default display request it will also create a new
+ * display with custom DisplayInfo.
+ */
+ private ActivityRecord createActivityOnDisplay(boolean defaultDisplay,
+ WindowProcessController process) {
+ final DisplayContent display;
+ if (defaultDisplay) {
+ display = mRootWindowContainer.getDefaultDisplay();
+ } else {
+ display = new TestDisplayContent.Builder(mService, 2000, 1000).setDensityDpi(300)
+ .setPosition(DisplayContent.POSITION_TOP).build();
+ }
+ final ActivityStack stack = display.createStack(WINDOWING_MODE_UNDEFINED,
+ ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final Task task = new TaskBuilder(mSupervisor).setStack(stack).build();
+ return new ActivityBuilder(mService).setTask(task).setUseProcess(process).build();
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 0f22724..4beede9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -111,6 +111,7 @@
private int mConfigChanges;
private int mLaunchedFromPid;
private int mLaunchedFromUid;
+ private WindowProcessController mWpc;
ActivityBuilder(ActivityTaskManagerService service) {
mService = service;
@@ -201,6 +202,11 @@
return this;
}
+ ActivityBuilder setUseProcess(WindowProcessController wpc) {
+ mWpc = wpc;
+ return this;
+ }
+
ActivityRecord build() {
try {
mService.deferWindowLayout();
@@ -263,10 +269,16 @@
activity.setVisible(true);
}
- final WindowProcessController wpc = new WindowProcessController(mService,
- mService.mContext.getApplicationInfo(), mProcessName, mUid,
- UserHandle.getUserId(12345), mock(Object.class),
- mock(WindowProcessListener.class));
+ final WindowProcessController wpc;
+ if (mWpc != null) {
+ wpc = mWpc;
+ } else {
+ wpc = new WindowProcessController(mService,
+ mService.mContext.getApplicationInfo(), mProcessName, mUid,
+ UserHandle.getUserId(12345), mock(Object.class),
+ mock(WindowProcessListener.class));
+ wpc.setThread(mock(IApplicationThread.class));
+ }
wpc.setThread(mock(IApplicationThread.class));
activity.setProcess(wpc);
doReturn(wpc).when(mService).getProcessController(
diff --git a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
new file mode 100644
index 0000000..032edde
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
@@ -0,0 +1,161 @@
+/*
+ * 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 com.android.server.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * This file tests WM setting the priority on windows that is used in SF to determine at what
+ * frame rate the Display should run. Any changes to the algorithm should be reflected in these
+ * tests.
+ *
+ * Build/Install/Run: atest FrameRateSelectionPriority
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class FrameRateSelectionPriorityTests extends WindowTestsBase {
+
+ @Test
+ public void basicTest() {
+ final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
+ assertNotNull("Window state is created", appWindow);
+ assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+
+ appWindow.updateFrameRateSelectionPriorityIfNeeded();
+ // Priority doesn't change.
+ assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+
+ // Call the function a few times.
+ appWindow.updateFrameRateSelectionPriorityIfNeeded();
+ appWindow.updateFrameRateSelectionPriorityIfNeeded();
+
+ // Since nothing changed in the priority state, the transaction should not be updating.
+ verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
+ appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+ }
+
+ @Test
+ public void testApplicationInFocusWithoutModeId() {
+ final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
+ assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+ assertEquals(appWindow.getDisplayContent().getDisplayPolicy().getRefreshRatePolicy()
+ .getPreferredModeId(appWindow), 0);
+
+ appWindow.updateFrameRateSelectionPriorityIfNeeded();
+ // Priority stays MAX_VALUE.
+ assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+ verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
+ appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+
+ // Application is in focus.
+ appWindow.mToken.mDisplayContent.mCurrentFocus = appWindow;
+ appWindow.updateFrameRateSelectionPriorityIfNeeded();
+ // Priority changes to 1.
+ assertEquals(appWindow.mFrameRateSelectionPriority, 1);
+ verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
+ appWindow.getSurfaceControl(), 1);
+ }
+
+ @Test
+ public void testApplicationInFocusWithModeId() {
+ final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
+ assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+
+ // Application is in focus.
+ appWindow.mToken.mDisplayContent.mCurrentFocus = appWindow;
+ appWindow.updateFrameRateSelectionPriorityIfNeeded();
+ // Priority changes.
+ assertEquals(appWindow.mFrameRateSelectionPriority, 1);
+ // Update the mode ID to a requested number.
+ appWindow.mAttrs.preferredDisplayModeId = 1;
+ appWindow.updateFrameRateSelectionPriorityIfNeeded();
+ // Priority changes.
+ assertEquals(appWindow.mFrameRateSelectionPriority, 0);
+
+ // Remove the mode ID request.
+ appWindow.mAttrs.preferredDisplayModeId = 0;
+ appWindow.updateFrameRateSelectionPriorityIfNeeded();
+ // Priority changes.
+ assertEquals(appWindow.mFrameRateSelectionPriority, 1);
+
+ // Verify we called actions on Transactions correctly.
+ verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
+ appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+ verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
+ appWindow.getSurfaceControl(), 0);
+ verify(appWindow.getPendingTransaction(), times(2)).setFrameRateSelectionPriority(
+ appWindow.getSurfaceControl(), 1);
+ }
+
+ @Test
+ public void testApplicationNotInFocusWithModeId() {
+ final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
+ assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+
+ final WindowState inFocusWindow = createWindow(null, TYPE_APPLICATION, "inFocus");
+ appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow;
+
+ appWindow.updateFrameRateSelectionPriorityIfNeeded();
+ // The window is not in focus.
+ assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+
+ // Update the mode ID to a requested number.
+ appWindow.mAttrs.preferredDisplayModeId = 1;
+ appWindow.updateFrameRateSelectionPriorityIfNeeded();
+ // Priority changes.
+ assertEquals(appWindow.mFrameRateSelectionPriority, 2);
+
+ verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
+ appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+ verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
+ appWindow.getSurfaceControl(), 2);
+ }
+
+ @Test
+ public void testApplicationNotInFocusWithoutModeId() {
+ final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
+ assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+
+ final WindowState inFocusWindow = createWindow(null, TYPE_APPLICATION, "inFocus");
+ appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow;
+
+ appWindow.updateFrameRateSelectionPriorityIfNeeded();
+ // The window is not in focus.
+ assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+
+ // Make sure that the mode ID is not set.
+ appWindow.mAttrs.preferredDisplayModeId = 0;
+ appWindow.updateFrameRateSelectionPriorityIfNeeded();
+ // Priority doesn't change.
+ assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+
+ verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
+ appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 64db897..890e4ab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -391,6 +391,33 @@
assertEquals(null, compatTokens.get(0));
}
+ @Test
+ public void testShouldUseSizeCompatModeOnResizableTask() {
+ setUpApp(new TestDisplayContent.Builder(mService, 1000, 2500).build());
+
+ // Make the task root resizable.
+ mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mService)
+ .setTask(mTask)
+ .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .build();
+ assertTrue(activity.shouldUseSizeCompatMode());
+
+ // The non-resizable activity should not be size compat because it is on a resizable task
+ // in multi-window mode.
+ mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+ assertFalse(activity.shouldUseSizeCompatMode());
+
+ // The non-resizable activity should not be size compat because the display support
+ // changing windowing mode from fullscreen to freeform.
+ mStack.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+ mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
+ assertFalse(activity.shouldUseSizeCompatMode());
+ }
+
/**
* Setup {@link #mActivity} as a size-compat-mode-able activity with fixed aspect and/or
* orientation.
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index f5d08dc..eda1fb8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -250,4 +250,9 @@
return this;
}
+ @Override
+ public SurfaceControl.Transaction setFrameRateSelectionPriority(SurfaceControl sc,
+ int priority) {
+ return this;
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index 421a4582..db4fdc77 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.times;
import android.content.pm.ApplicationInfo;
+import android.content.res.Configuration;
import android.platform.test.annotations.Presubmit;
import org.junit.Before;
@@ -62,33 +63,33 @@
// Register to display 1 as a listener.
TestDisplayContent testDisplayContent1 = createTestDisplayContentInContainer();
- mWpc.registerDisplayConfigurationListenerLocked(testDisplayContent1);
+ mWpc.registerDisplayConfigurationListener(testDisplayContent1);
assertTrue(testDisplayContent1.containsListener(mWpc));
assertEquals(testDisplayContent1.mDisplayId, mWpc.getDisplayId());
// Move to display 2.
TestDisplayContent testDisplayContent2 = createTestDisplayContentInContainer();
- mWpc.registerDisplayConfigurationListenerLocked(testDisplayContent2);
+ mWpc.registerDisplayConfigurationListener(testDisplayContent2);
assertFalse(testDisplayContent1.containsListener(mWpc));
assertTrue(testDisplayContent2.containsListener(mWpc));
assertEquals(testDisplayContent2.mDisplayId, mWpc.getDisplayId());
// Null DisplayContent will not change anything.
- mWpc.registerDisplayConfigurationListenerLocked(null);
+ mWpc.registerDisplayConfigurationListener(null);
assertTrue(testDisplayContent2.containsListener(mWpc));
assertEquals(testDisplayContent2.mDisplayId, mWpc.getDisplayId());
// Unregister listener will remove the wpc from registered displays.
- mWpc.unregisterDisplayConfigurationListenerLocked();
+ mWpc.unregisterDisplayConfigurationListener();
assertFalse(testDisplayContent1.containsListener(mWpc));
assertFalse(testDisplayContent2.containsListener(mWpc));
assertEquals(INVALID_DISPLAY, mWpc.getDisplayId());
// Unregistration still work even if the display was removed.
- mWpc.registerDisplayConfigurationListenerLocked(testDisplayContent1);
+ mWpc.registerDisplayConfigurationListener(testDisplayContent1);
assertEquals(testDisplayContent1.mDisplayId, mWpc.getDisplayId());
mRootWindowContainer.removeChild(testDisplayContent1);
- mWpc.unregisterDisplayConfigurationListenerLocked();
+ mWpc.unregisterDisplayConfigurationListener();
assertEquals(INVALID_DISPLAY, mWpc.getDisplayId());
}
@@ -129,6 +130,24 @@
orderVerifier.verifyNoMoreInteractions();
}
+ @Test
+ public void testConfigurationForSecondaryScreen() {
+ final WindowProcessController wpc = new WindowProcessController(
+ mService, mock(ApplicationInfo.class), null, 0, -1, null, null);
+ // By default, the process should not listen to any display.
+ assertEquals(INVALID_DISPLAY, wpc.getDisplayId());
+
+ // Register to a new display as a listener.
+ final DisplayContent display = new TestDisplayContent.Builder(mService, 2000, 1000)
+ .setDensityDpi(300).setPosition(DisplayContent.POSITION_TOP).build();
+ wpc.registerDisplayConfigurationListener(display);
+
+ assertEquals(display.mDisplayId, wpc.getDisplayId());
+ final Configuration expectedConfig = mService.mRootWindowContainer.getConfiguration();
+ expectedConfig.updateFrom(display.getConfiguration());
+ assertEquals(expectedConfig, wpc.getConfiguration());
+ }
+
private TestDisplayContent createTestDisplayContentInContainer() {
return new TestDisplayContent.Builder(mService, 1000, 1500).build();
}
diff --git a/services/usage/java/com/android/server/usage/StorageStatsManagerInternal.java b/services/usage/java/com/android/server/usage/StorageStatsManagerInternal.java
new file mode 100644
index 0000000..a532548
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/StorageStatsManagerInternal.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.server.usage;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.pm.PackageStats;
+
+/**
+ * StorageStatsManager local system service interface.
+ *
+ * Only for use within the system server.
+ */
+public abstract class StorageStatsManagerInternal {
+ /**
+ * Class used to augment {@link PackageStats} with the data stored by the system on
+ * behalf of apps in system specific directories
+ * ({@link android.os.Environment#getDataSystemDirectory},
+ * {@link android.os.Environment#getDataSystemCeDirectory}, etc).
+ */
+ public interface StorageStatsAugmenter {
+ void augmentStatsForPackage(@NonNull PackageStats stats,
+ @NonNull String packageName, @UserIdInt int userId,
+ @NonNull String callingPackage);
+ void augmentStatsForUid(@NonNull PackageStats stats, int uid,
+ @NonNull String callingPackage);
+ void augmentStatsForUser(@NonNull PackageStats stats, @UserIdInt int userId,
+ @NonNull String callingPackage);
+ }
+
+ /**
+ * Register a {@link StorageStatsAugmenter}.
+ *
+ * @param augmenter the {@link StorageStatsAugmenter} object to be registered.
+ * @param tag the identifier to be used for debugging in logs/trace.
+ */
+ public abstract void registerStorageStatsAugmenter(@NonNull StorageStatsAugmenter augmenter,
+ @NonNull String tag);
+}
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 531a931..18b640f 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -18,6 +18,7 @@
import static com.android.internal.util.ArrayUtils.defeatNullable;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.usage.StorageStatsManagerInternal.StorageStatsAugmenter;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -46,6 +47,7 @@
import android.os.ParcelableException;
import android.os.StatFs;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.CrateInfo;
@@ -58,6 +60,7 @@
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.DataUnit;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseLongArray;
@@ -77,6 +80,8 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Consumer;
public class StorageStatsService extends IStorageStatsManager.Stub {
private static final String TAG = "StorageStatsService";
@@ -111,6 +116,9 @@
private final Installer mInstaller;
private final H mHandler;
+ private final CopyOnWriteArrayList<Pair<String, StorageStatsAugmenter>>
+ mStorageStatsAugmenters = new CopyOnWriteArrayList<>();
+
public StorageStatsService(Context context) {
mContext = Preconditions.checkNotNull(context);
mAppOps = Preconditions.checkNotNull(context.getSystemService(AppOpsManager.class));
@@ -139,6 +147,8 @@
}
}
});
+
+ LocalServices.addService(StorageStatsManagerInternal.class, new LocalService());
}
private void invalidateMounts() {
@@ -300,6 +310,12 @@
} catch (InstallerException e) {
throw new ParcelableException(new IOException(e.getMessage()));
}
+ if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) {
+ forEachStorageStatsAugmenter((storageStatsAugmenter) -> {
+ storageStatsAugmenter.augmentStatsForPackage(stats,
+ packageName, userId, callingPackage);
+ }, "queryStatsForPackage");
+ }
return translate(stats);
}
}
@@ -353,6 +369,12 @@
} catch (InstallerException e) {
throw new ParcelableException(new IOException(e.getMessage()));
}
+
+ if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) {
+ forEachStorageStatsAugmenter((storageStatsAugmenter) -> {
+ storageStatsAugmenter.augmentStatsForUid(stats, uid, callingPackage);
+ }, "queryStatsForUid");
+ }
return translate(stats);
}
@@ -379,6 +401,12 @@
} catch (InstallerException e) {
throw new ParcelableException(new IOException(e.getMessage()));
}
+
+ if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) {
+ forEachStorageStatsAugmenter((storageStatsAugmenter) -> {
+ storageStatsAugmenter.augmentStatsForUser(stats, userId, callingPackage);
+ }, "queryStatsForUser");
+ }
return translate(stats);
}
@@ -705,4 +733,29 @@
throw new ParcelableException(new IOException(e.getMessage()));
}
}
+
+ void forEachStorageStatsAugmenter(@NonNull Consumer<StorageStatsAugmenter> consumer,
+ @NonNull String queryTag) {
+ for (int i = 0, count = mStorageStatsAugmenters.size(); i < count; ++i) {
+ final Pair<String, StorageStatsAugmenter> pair = mStorageStatsAugmenters.get(i);
+ final String augmenterTag = pair.first;
+ final StorageStatsAugmenter storageStatsAugmenter = pair.second;
+
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, queryTag + ":" + augmenterTag);
+ try {
+ consumer.accept(storageStatsAugmenter);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+ }
+ }
+
+ private class LocalService extends StorageStatsManagerInternal {
+ @Override
+ public void registerStorageStatsAugmenter(
+ @NonNull StorageStatsAugmenter storageStatsAugmenter,
+ @NonNull String tag) {
+ mStorageStatsAugmenters.add(Pair.create(tag, storageStatsAugmenter));
+ }
+ }
}
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 05d4bf3..b8b203d 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -558,7 +558,7 @@
}
if (DBG) {
- Log.d(LOG_TAG, "No READ_PRIVILEGED_PHONE_STATE permission, "
+ Log.d(LOG_TAG, "No READ_PRIVILEDED_PHONE_STATE permission, "
+ "check carrier privilege next.");
}
@@ -566,33 +566,6 @@
}
/**
- * Ensure the caller (or self, if not processing an IPC) has
- * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or
- * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or carrier privileges.
- *
- * @throws SecurityException if the caller does not have the required permission/privileges
- */
- public static void enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
- Context context, int subId, String message) {
- if (context.checkCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- == PERMISSION_GRANTED) {
- return;
- }
-
- if (context.checkCallingOrSelfPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
- == PERMISSION_GRANTED) {
- return;
- }
-
- if (DBG) {
- Log.d(LOG_TAG, "No READ_PRIVILEGED_PHONE_STATE nor READ_PRECISE_PHONE_STATE permission"
- + ", check carrier privilege next.");
- }
-
- enforceCallingOrSelfCarrierPrivilege(context, subId, message);
- }
-
- /**
* Make sure the caller (or self, if not processing an IPC) has carrier privileges.
*
* @throws SecurityException if the caller does not have the required privileges
@@ -619,7 +592,7 @@
private static boolean checkCarrierPrivilegeForAnySubId(Context context, int uid) {
SubscriptionManager sm = (SubscriptionManager) context.getSystemService(
Context.TELEPHONY_SUBSCRIPTION_SERVICE);
- int[] activeSubIds = sm.getActiveSubscriptionIdList(/* visibleOnly */ false);
+ int[] activeSubIds = sm.getActiveAndHiddenSubscriptionIdList();
for (int activeSubId : activeSubIds) {
if (getCarrierPrivilegeStatus(context, activeSubId, uid)
== TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
index 2abcc76..a7ad884 100644
--- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
@@ -28,6 +28,8 @@
import android.os.SystemProperties;
import java.io.PrintWriter;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
/**
@@ -137,4 +139,12 @@
}
return ret;
}
+
+ /** Wait for latch to trigger */
+ public static void waitUntilReady(CountDownLatch latch, long timeoutMs) {
+ try {
+ latch.await(timeoutMs, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException ignored) {
+ }
+ }
}
diff --git a/telephony/framework-telephony-jarjar-rules.txt b/telephony/framework-telephony-jarjar-rules.txt
new file mode 100644
index 0000000..7cab806
--- /dev/null
+++ b/telephony/framework-telephony-jarjar-rules.txt
@@ -0,0 +1,4 @@
+rule android.telephony.Annotation* android.telephony.framework.Annotation@1
+rule com.android.i18n.phonenumbers.** com.android.telephony.framework.phonenumbers.@1
+#TODO: add jarjar rules for statically linked util classes
+
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 8ed4ee5..fee6d3f 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -25,11 +25,12 @@
import android.annotation.SuppressAutoDoc;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.app.ActivityThread;
import android.app.PendingIntent;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.database.CursorWindow;
import android.net.Uri;
import android.os.Build;
@@ -423,7 +424,7 @@
String destinationAddress, String scAddress, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent) {
sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
- true /* persistMessage*/, ActivityThread.currentPackageName());
+ true /* persistMessage*/, null);
}
/**
@@ -633,7 +634,7 @@
String destinationAddress, String scAddress, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent) {
sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
- false /* persistMessage */, ActivityThread.currentPackageName());
+ false /* persistMessage */, null);
}
private void sendTextMessageInternal(
@@ -676,7 +677,7 @@
ISms iSms = getISmsServiceOrThrow();
if (iSms != null) {
iSms.sendTextForSubscriberWithOptions(subId,
- ActivityThread.currentPackageName(), destinationAddress,
+ null, destinationAddress,
scAddress,
text, sentIntent, deliveryIntent, persistMessage, finalPriority,
expectMore, finalValidity);
@@ -698,7 +699,7 @@
ISms iSms = getISmsServiceOrThrow();
if (iSms != null) {
iSms.sendTextForSubscriberWithOptions(getSubscriptionId(),
- ActivityThread.currentPackageName(), destinationAddress,
+ null, destinationAddress,
scAddress,
text, sentIntent, deliveryIntent, persistMessage, finalPriority,
expectMore, finalValidity);
@@ -920,7 +921,7 @@
String destinationAddress, String scAddress, ArrayList<String> parts,
ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
- deliveryIntents, true /* persistMessage*/, ActivityThread.currentPackageName());
+ deliveryIntents, true /* persistMessage*/, null);
}
/**
@@ -937,8 +938,9 @@
* subscription.
* </p>
*
- * @param packageName serves as the default package name if
- * {@link ActivityThread#currentPackageName()} is null.
+ * @param packageName serves as the default package name if the package name that is
+ * associated with the user id is null.
+ *
* @hide
*/
@SystemApi
@@ -948,9 +950,7 @@
@NonNull List<String> parts, @Nullable List<PendingIntent> sentIntents,
@Nullable List<PendingIntent> deliveryIntents, @NonNull String packageName) {
sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
- deliveryIntents, true /* persistMessage*/,
- ActivityThread.currentPackageName() == null
- ? packageName : ActivityThread.currentPackageName());
+ deliveryIntents, true /* persistMessage*/, packageName);
}
private void sendMultipartTextMessageInternal(
@@ -1051,7 +1051,7 @@
String destinationAddress, String scAddress, List<String> parts,
List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) {
sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
- deliveryIntents, false /* persistMessage*/, ActivityThread.currentPackageName());
+ deliveryIntents, false /* persistMessage*/, null);
}
/**
@@ -1213,7 +1213,7 @@
ISms iSms = getISmsServiceOrThrow();
if (iSms != null) {
iSms.sendMultipartTextForSubscriberWithOptions(subId,
- ActivityThread.currentPackageName(), destinationAddress,
+ null, destinationAddress,
scAddress, parts, sentIntents, deliveryIntents,
persistMessage, finalPriority, expectMore, finalValidity);
}
@@ -1235,7 +1235,7 @@
ISms iSms = getISmsServiceOrThrow();
if (iSms != null) {
iSms.sendMultipartTextForSubscriberWithOptions(getSubscriptionId(),
- ActivityThread.currentPackageName(), destinationAddress,
+ null, destinationAddress,
scAddress, parts, sentIntents, deliveryIntents,
persistMessage, finalPriority, expectMore, finalValidity);
}
@@ -1366,7 +1366,7 @@
public void onSuccess(int subId) {
try {
ISms iSms = getISmsServiceOrThrow();
- iSms.sendDataForSubscriber(subId, ActivityThread.currentPackageName(),
+ iSms.sendDataForSubscriber(subId, null,
destinationAddress, scAddress, destinationPort & 0xFFFF, data,
sentIntent, deliveryIntent);
} catch (RemoteException e) {
@@ -1492,7 +1492,6 @@
private void resolveSubscriptionForOperation(SubscriptionResolverResult resolverResult) {
int subId = getSubscriptionId();
boolean isSmsSimPickActivityNeeded = false;
- final Context context = ActivityThread.currentApplication().getApplicationContext();
try {
ISms iSms = getISmsService();
if (iSms != null) {
@@ -1514,14 +1513,14 @@
return;
}
// We need to ask the user pick an appropriate subid for the operation.
- Log.d(TAG, "resolveSubscriptionForOperation isSmsSimPickActivityNeeded is true for package "
- + context.getPackageName());
+ Log.d(TAG, "resolveSubscriptionForOperation isSmsSimPickActivityNeeded is true for calling"
+ + " package. ");
try {
// Create the SMS pick activity and call back once the activity is complete. Can't do
// it here because we do not have access to the activity context that is performing this
// operation.
// Requires that the calling process has the SEND_SMS permission.
- getITelephony().enqueueSmsPickResult(context.getOpPackageName(),
+ getITelephony().enqueueSmsPickResult(null,
new IIntegerConsumer.Stub() {
@Override
public void accept(int subId) {
@@ -1539,6 +1538,13 @@
}
}
+ /**
+ * To check the SDK version for SmsManager.sendResolverResult method.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.P)
+ private static final long GET_TARGET_SDK_VERSION_CODE_CHANGE = 145147528L;
+
private void sendResolverResult(SubscriptionResolverResult resolverResult, int subId,
boolean pickActivityShown) {
if (SubscriptionManager.isValidSubscriptionId(subId)) {
@@ -1546,7 +1552,8 @@
return;
}
- if (getTargetSdkVersion() <= Build.VERSION_CODES.P && !pickActivityShown) {
+ if (!Compatibility.isChangeEnabled(GET_TARGET_SDK_VERSION_CODE_CHANGE)
+ && !pickActivityShown) {
// Do not fail, return a success with an INVALID subid for apps targeting P or below
// that tried to perform an operation and the SMS disambiguation dialog was never shown,
// as these applications may not have been written to handle the failure case properly.
@@ -1559,19 +1566,6 @@
}
}
- private static int getTargetSdkVersion() {
- final Context context = ActivityThread.currentApplication().getApplicationContext();
- int targetSdk;
- try {
- targetSdk = context.getPackageManager().getApplicationInfo(
- context.getOpPackageName(), 0).targetSdkVersion;
- } catch (PackageManager.NameNotFoundException e) {
- // Default to old behavior if we can not find this.
- targetSdk = -1;
- }
- return targetSdk;
- }
-
private static ITelephony getITelephony() {
ITelephony binder = ITelephony.Stub.asInterface(
TelephonyFrameworkInitializer
@@ -1657,7 +1651,7 @@
ISms iSms = getISmsService();
if (iSms != null) {
success = iSms.copyMessageToIccEfForSubscriber(getSubscriptionId(),
- ActivityThread.currentPackageName(),
+ null,
status, pdu, smsc);
}
} catch (RemoteException ex) {
@@ -1698,7 +1692,7 @@
ISms iSms = getISmsService();
if (iSms != null) {
success = iSms.updateMessageOnIccEfForSubscriber(getSubscriptionId(),
- ActivityThread.currentPackageName(),
+ null,
messageIndex, STATUS_ON_ICC_FREE, null /* pdu */);
}
} catch (RemoteException ex) {
@@ -1741,7 +1735,7 @@
ISms iSms = getISmsService();
if (iSms != null) {
success = iSms.updateMessageOnIccEfForSubscriber(getSubscriptionId(),
- ActivityThread.currentPackageName(),
+ null,
messageIndex, newStatus, pdu);
}
} catch (RemoteException ex) {
@@ -1793,7 +1787,7 @@
if (iSms != null) {
records = iSms.getAllMessagesFromIccEfForSubscriber(
getSubscriptionId(),
- ActivityThread.currentPackageName());
+ null);
}
} catch (RemoteException ex) {
// ignore it
@@ -2617,7 +2611,7 @@
try {
ISms iccSms = getISmsServiceOrThrow();
return iccSms.createAppSpecificSmsToken(getSubscriptionId(),
- ActivityThread.currentPackageName(), intent);
+ null, intent);
} catch (RemoteException ex) {
ex.rethrowFromSystemServer();
@@ -2737,7 +2731,7 @@
try {
ISms iccSms = getISmsServiceOrThrow();
return iccSms.createAppSpecificSmsTokenWithPackageInfo(getSubscriptionId(),
- ActivityThread.currentPackageName(), prefixes, intent);
+ null, prefixes, intent);
} catch (RemoteException ex) {
ex.rethrowFromSystemServer();
@@ -2828,7 +2822,7 @@
ISms iccISms = getISmsServiceOrThrow();
if (iccISms != null) {
return iccISms.checkSmsShortCodeDestination(getSubscriptionId(),
- ActivityThread.currentPackageName(), null, destAddress, countryIso);
+ null, null, destAddress, countryIso);
}
} catch (RemoteException e) {
Log.e(TAG, "checkSmsShortCodeDestination() RemoteException", e);
@@ -2864,7 +2858,7 @@
ISms iSms = getISmsService();
if (iSms != null) {
smsc = iSms.getSmscAddressFromIccEfForSubscriber(
- getSubscriptionId(), ActivityThread.currentPackageName());
+ getSubscriptionId(), null);
}
} catch (RemoteException ex) {
// ignore it
@@ -2898,7 +2892,7 @@
ISms iSms = getISmsService();
if (iSms != null) {
return iSms.setSmscAddressOnIccEfForSubscriber(
- smsc, getSubscriptionId(), ActivityThread.currentPackageName());
+ smsc, getSubscriptionId(), null);
}
} catch (RemoteException ex) {
// ignore it
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 36f541f..b42ce35 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2232,6 +2232,7 @@
} else {
logd("putPhoneIdAndSubIdExtra: no valid subs");
intent.putExtra(PhoneConstants.PHONE_KEY, phoneId);
+ intent.putExtra(EXTRA_SLOT_INDEX, phoneId);
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 13aad7e..56ca8c7 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -37,7 +37,6 @@
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.WorkerThread;
-import android.app.ActivityThread;
import android.app.PendingIntent;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
@@ -380,8 +379,17 @@
// effort and get the context from the current activity thread.
if (mContext != null) {
return mContext.getOpPackageName();
+ } else {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) return null;
+ try {
+ return telephony.getCurrentPackageName();
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ return null;
+ }
}
- return ActivityThread.currentOpPackageName();
}
private String getFeatureId() {
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 9b739d3..8c11df4 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -27,6 +27,7 @@
import android.annotation.WorkerThread;
import android.os.Binder;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyFrameworkInitializer;
@@ -383,6 +384,8 @@
callback.setExecutor(executor);
try {
getITelephony().registerImsProvisioningChangedCallback(mSubId, callback.getBinder());
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
} catch (RemoteException | IllegalStateException e) {
throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index cdb95a8..a8e76b9 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2087,6 +2087,11 @@
int getRadioHalVersion();
/**
+ * Get the current calling package name.
+ */
+ String getCurrentPackageName();
+
+ /**
* Returns true if the specified type of application (e.g. {@link #APPTYPE_CSIM} is present
* on the UICC card.
* @hide
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 656628eb..8cc8cf4 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -18,10 +18,13 @@
import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
@@ -36,12 +39,14 @@
import android.net.ConnectivityModuleConnector;
import android.net.ConnectivityModuleConnector.ConnectivityModuleHealthListener;
import android.os.Handler;
+import android.os.SystemProperties;
import android.os.test.TestLooper;
import android.provider.DeviceConfig;
import android.util.AtomicFile;
import androidx.test.InstrumentationRegistry;
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.server.PackageWatchdog.HealthCheckState;
import com.android.server.PackageWatchdog.MonitoredPackage;
import com.android.server.PackageWatchdog.PackageHealthObserver;
@@ -54,11 +59,15 @@
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -88,6 +97,8 @@
private PackageManager mMockPackageManager;
@Captor
private ArgumentCaptor<ConnectivityModuleHealthListener> mConnectivityModuleCallbackCaptor;
+ private MockitoSession mSession;
+ private HashMap<String, String> mSystemSettingsMap;
@Before
public void setUp() throws Exception {
@@ -104,11 +115,47 @@
res.setLongVersionCode(VERSION_CODE);
return res;
});
+ mSession = ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .spyStatic(SystemProperties.class)
+ .startMocking();
+ mSystemSettingsMap = new HashMap<>();
+
+
+ // Mock SystemProperties setter and various getters
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ String key = invocationOnMock.getArgument(0);
+ String value = invocationOnMock.getArgument(1);
+
+ mSystemSettingsMap.put(key, value);
+ return null;
+ }
+ ).when(() -> SystemProperties.set(anyString(), anyString()));
+
+ doAnswer((Answer<Integer>) invocationOnMock -> {
+ String key = invocationOnMock.getArgument(0);
+ int defaultValue = invocationOnMock.getArgument(1);
+
+ String storedValue = mSystemSettingsMap.get(key);
+ return storedValue == null ? defaultValue : Integer.parseInt(storedValue);
+ }
+ ).when(() -> SystemProperties.getInt(anyString(), anyInt()));
+
+ doAnswer((Answer<Long>) invocationOnMock -> {
+ String key = invocationOnMock.getArgument(0);
+ long defaultValue = invocationOnMock.getArgument(1);
+
+ String storedValue = mSystemSettingsMap.get(key);
+ return storedValue == null ? defaultValue : Long.parseLong(storedValue);
+ }
+ ).when(() -> SystemProperties.getLong(anyString(), anyLong()));
}
@After
public void tearDown() throws Exception {
dropShellPermissions();
+ mSession.finishMocking();
}
@Test
@@ -968,6 +1015,54 @@
assertThat(persistentObserver.mHealthCheckFailedPackages).isEmpty();
}
+
+ /** Ensure that boot loop mitigation is done when the number of boots meets the threshold. */
+ @Test
+ public void testBootLoopDetection_meetsThreshold() {
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1);
+ watchdog.registerHealthObserver(bootObserver);
+ for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
+ watchdog.noteBoot();
+ }
+ assertThat(bootObserver.mitigatedBootLoop()).isTrue();
+ }
+
+
+ /**
+ * Ensure that boot loop mitigation is not done when the number of boots does not meet the
+ * threshold.
+ */
+ @Test
+ public void testBootLoopDetection_doesNotMeetThreshold() {
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1);
+ watchdog.registerHealthObserver(bootObserver);
+ for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT - 1; i++) {
+ watchdog.noteBoot();
+ }
+ assertThat(bootObserver.mitigatedBootLoop()).isFalse();
+ }
+
+ /**
+ * Ensure that boot loop mitigation is done for the observer with the lowest user impact
+ */
+ @Test
+ public void testBootLoopMitigationDoneForLowestUserImpact() {
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver bootObserver1 = new TestObserver(OBSERVER_NAME_1);
+ bootObserver1.setImpact(PackageHealthObserverImpact.USER_IMPACT_LOW);
+ TestObserver bootObserver2 = new TestObserver(OBSERVER_NAME_2);
+ bootObserver2.setImpact(PackageHealthObserverImpact.USER_IMPACT_MEDIUM);
+ watchdog.registerHealthObserver(bootObserver1);
+ watchdog.registerHealthObserver(bootObserver2);
+ for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
+ watchdog.noteBoot();
+ }
+ assertThat(bootObserver1.mitigatedBootLoop()).isTrue();
+ assertThat(bootObserver2.mitigatedBootLoop()).isFalse();
+ }
+
private void adoptShellPermissions(String... permissions) {
InstrumentationRegistry
.getInstrumentation()
@@ -1046,6 +1141,7 @@
private int mLastFailureReason;
private boolean mIsPersistent = false;
private boolean mMayObservePackages = false;
+ private boolean mMitigatedBootLoop = false;
final List<String> mHealthCheckFailedPackages = new ArrayList<>();
final List<String> mMitigatedPackages = new ArrayList<>();
@@ -1082,6 +1178,19 @@
return mMayObservePackages;
}
+ public int onBootLoop() {
+ return mImpact;
+ }
+
+ public boolean executeBootLoopMitigation() {
+ mMitigatedBootLoop = true;
+ return true;
+ }
+
+ public boolean mitigatedBootLoop() {
+ return mMitigatedBootLoop;
+ }
+
public int getLastFailureReason() {
return mLastFailureReason;
}
@@ -1090,6 +1199,10 @@
mIsPersistent = persistent;
}
+ public void setImpact(int impact) {
+ mImpact = impact;
+ }
+
public void setMayObservePackages(boolean mayObservePackages) {
mMayObservePackages = mayObservePackages;
}
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index 091edd4..98e7b4e 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -19,7 +19,10 @@
static_libs: ["androidx.test.rules", "cts-rollback-lib", "cts-install-lib"],
test_suites: ["general-tests"],
test_config: "RollbackTest.xml",
- java_resources: [":com.android.apex.apkrollback.test_v2"],
+ java_resources: [
+ ":com.android.apex.apkrollback.test_v2",
+ ":com.android.apex.apkrollback.test_v2Crashing"
+ ],
}
java_test_host {
@@ -79,4 +82,14 @@
key: "com.android.apex.apkrollback.test.key",
apps: ["TestAppAv2"],
installable: false,
+}
+
+apex {
+ name: "com.android.apex.apkrollback.test_v2Crashing",
+ manifest: "testdata/manifest_v2.json",
+ androidManifest: "testdata/AndroidManifest.xml",
+ file_contexts: ":apex.test-file_contexts",
+ key: "com.android.apex.apkrollback.test.key",
+ apps: ["TestAppACrashingV2"],
+ installable: false,
}
\ No newline at end of file
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index f6699fa..5a92d68 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -392,9 +392,6 @@
RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS,
Long.toString(expirationTime), false /* makeDefault*/);
- // Pull the new expiration time from DeviceConfig
- rm.reloadPersistedData();
-
// Uninstall TestApp.A
Uninstall.packages(TestApp.A);
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
@@ -457,9 +454,6 @@
RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS,
Long.toString(expirationTime), false /* makeDefault*/);
- // Pull the new expiration time from DeviceConfig
- rm.reloadPersistedData();
-
// Install app A with rollback enabled
Uninstall.packages(TestApp.A);
Install.single(TestApp.A1).commit();
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index ce20311..80491cd 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -500,8 +500,9 @@
APK_IN_APEX_TESTAPEX_NAME, 1, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v1.apex");
private static final TestApp TEST_APEX_WITH_APK_V2 = new TestApp("TestApexWithApkV2",
APK_IN_APEX_TESTAPEX_NAME, 2, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v2.apex");
- private static final TestApp TEST_APP_A_V2_UNKNOWN = new TestApp("Av2Unknown", TestApp.A, 0,
- /*isApex*/false, "TestAppAv2.apk");
+ private static final TestApp TEST_APEX_WITH_APK_V2_CRASHING = new TestApp(
+ "TestApexWithApkV2Crashing", APK_IN_APEX_TESTAPEX_NAME, 2, /*isApex*/true,
+ APK_IN_APEX_TESTAPEX_NAME + "_v2Crashing.apex");
@Test
public void testRollbackApexWithApk_Phase1() throws Exception {
@@ -523,7 +524,7 @@
assertThat(available).isStaged();
assertThat(available).packagesContainsExactly(
Rollback.from(TEST_APEX_WITH_APK_V2).to(TEST_APEX_WITH_APK_V1),
- Rollback.from(TEST_APP_A_V2_UNKNOWN).to(TestApp.A1));
+ Rollback.from(TestApp.A, 0).to(TestApp.A1));
RollbackUtils.rollback(available.getRollbackId(), TEST_APEX_WITH_APK_V2);
RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId());
@@ -531,7 +532,7 @@
assertThat(committed).isStaged();
assertThat(committed).packagesContainsExactly(
Rollback.from(TEST_APEX_WITH_APK_V2).to(TEST_APEX_WITH_APK_V1),
- Rollback.from(TEST_APP_A_V2_UNKNOWN).to(TestApp.A1));
+ Rollback.from(TestApp.A, 0).to(TestApp.A1));
assertThat(committed).causePackagesContainsExactly(TEST_APEX_WITH_APK_V2);
assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1);
@@ -548,6 +549,41 @@
InstallUtils.processUserData(TestApp.A);
}
+ /**
+ * Installs an apex with an apk that can crash.
+ */
+ @Test
+ public void testRollbackApexWithApkCrashing_Phase1() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ int sessionId = Install.single(TEST_APEX_WITH_APK_V2_CRASHING).setStaged()
+ .setEnableRollback().commit();
+ InstallUtils.waitForSessionReady(sessionId);
+ }
+
+ /**
+ * Verifies rollback has been enabled successfully. Then makes TestApp.A crash.
+ */
+ @Test
+ public void testRollbackApexWithApkCrashing_Phase2() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(APK_IN_APEX_TESTAPEX_NAME)).isEqualTo(2);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+
+ RollbackInfo available = RollbackUtils.getAvailableRollback(APK_IN_APEX_TESTAPEX_NAME);
+ assertThat(available).isStaged();
+ assertThat(available).packagesContainsExactly(
+ Rollback.from(TEST_APEX_WITH_APK_V2).to(TEST_APEX_WITH_APK_V1),
+ Rollback.from(TestApp.A, 0).to(TestApp.A1));
+
+ // Crash TestApp.A PackageWatchdog#TRIGGER_FAILURE_COUNT times to trigger rollback
+ RollbackUtils.sendCrashBroadcast(TestApp.A, 5);
+ }
+
+ @Test
+ public void testRollbackApexWithApkCrashing_Phase3() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(APK_IN_APEX_TESTAPEX_NAME)).isEqualTo(1);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ }
+
private static void runShellCommand(String cmd) {
ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
.executeShellCommand(cmd);
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 181e29a..672cbb0 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -261,6 +261,34 @@
runPhase("testRollbackApexWithApk_Phase3");
}
+ /**
+ * Tests that RollbackPackageHealthObserver is observing apk-in-apex.
+ */
+ @Test
+ public void testRollbackApexWithApkCrashing() throws Exception {
+ getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A");
+ CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
+ final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex";
+ final File apex = buildHelper.getTestFile(fileName);
+ if (!getDevice().isAdbRoot()) {
+ getDevice().enableAdbRoot();
+ }
+ getDevice().remountSystemWritable();
+ assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName));
+ getDevice().reboot();
+
+ // Install an apex with apk that crashes
+ runPhase("testRollbackApexWithApkCrashing_Phase1");
+ getDevice().reboot();
+ // Verify apex was installed and then crash the apk
+ runPhase("testRollbackApexWithApkCrashing_Phase2");
+ // Wait for crash to trigger rollback
+ assertTrue(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5)));
+ getDevice().waitForDeviceAvailable();
+ // Verify rollback occurred due to crash of apk-in-apex
+ runPhase("testRollbackApexWithApkCrashing_Phase3");
+ }
+
private void crashProcess(String processName, int numberOfCrashes) throws Exception {
String pid = "";
String lastPid = "invalid";
diff --git a/tests/WindowInsetsTests/Android.bp b/tests/WindowInsetsTests/Android.bp
new file mode 100644
index 0000000..12395e7
--- /dev/null
+++ b/tests/WindowInsetsTests/Android.bp
@@ -0,0 +1,22 @@
+// 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.
+
+android_test {
+ name: "WindowInsetsTests",
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+ certificate: "platform",
+ platform_apis: true,
+}
+
diff --git a/tests/WindowInsetsTests/AndroidManifest.xml b/tests/WindowInsetsTests/AndroidManifest.xml
new file mode 100644
index 0000000..8d33f70
--- /dev/null
+++ b/tests/WindowInsetsTests/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (018C) 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
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.test.windowinsetstests">
+
+ <application android:label="@string/activity_title">
+ <activity android:name=".WindowInsetsActivity"
+ android:theme="@android:style/Theme.Material"
+ android:windowSoftInputMode="adjustResize">
+
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/WindowInsetsTests/res/layout/window_inset_activity.xml b/tests/WindowInsetsTests/res/layout/window_inset_activity.xml
new file mode 100644
index 0000000..38e0029
--- /dev/null
+++ b/tests/WindowInsetsTests/res/layout/window_inset_activity.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:id="@+id/root">
+
+ <Button
+ android:id="@+id/button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:text="Hello insets" />
+
+</LinearLayout>
+
diff --git a/tests/WindowInsetsTests/res/values/strings.xml b/tests/WindowInsetsTests/res/values/strings.xml
new file mode 100644
index 0000000..242823d
--- /dev/null
+++ b/tests/WindowInsetsTests/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+ <string name="activity_title">Window Insets Tests</string>
+</resources>
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
new file mode 100644
index 0000000..b8b2de5
--- /dev/null
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.google.android.test.windowinsetstests;
+
+import android.animation.ObjectAnimator;
+import android.animation.TypeEvaluator;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.graphics.Insets;
+import android.os.Bundle;
+import android.util.Property;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowInsets.Type;
+import android.view.WindowInsetsAnimationCallback;
+import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
+import android.view.WindowInsetsAnimationControlListener;
+import android.view.WindowInsetsAnimationController;
+
+import com.google.android.test.windowinsetstests.R;
+
+public class WindowInsetsActivity extends Activity {
+
+ private View mRoot;
+ private View mButton;
+
+ private static class InsetsProperty extends Property<WindowInsetsAnimationController, Insets> {
+
+ private final View mViewToAnimate;
+ private final Insets mShowingInsets;
+
+ public InsetsProperty(View viewToAnimate, Insets showingInsets) {
+ super(Insets.class, "Insets");
+ mViewToAnimate = viewToAnimate;
+ mShowingInsets = showingInsets;
+ }
+
+ @Override
+ public Insets get(WindowInsetsAnimationController object) {
+ return object.getCurrentInsets();
+ }
+
+ @Override
+ public void set(WindowInsetsAnimationController object, Insets value) {
+ object.setInsetsAndAlpha(value, 1.0f, 0.5f);
+ if (mShowingInsets.bottom != 0) {
+ mViewToAnimate.setTranslationY(mShowingInsets.bottom - value.bottom);
+ } else if (mShowingInsets.right != 0) {
+ mViewToAnimate.setTranslationX(mShowingInsets.right - value.right);
+ } else if (mShowingInsets.left != 0) {
+ mViewToAnimate.setTranslationX(value.left - mShowingInsets.left);
+ }
+ }
+ };
+
+ float showY;
+ float hideY;
+ InsetsAnimation imeAnim;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.window_inset_activity);
+ mRoot = findViewById(R.id.root);
+ mButton = findViewById(R.id.button);
+ mButton.setOnClickListener(v -> {
+ if (!v.getRootWindowInsets().isVisible(Type.ime())) {
+ v.getWindowInsetsController().show(Type.ime());
+ } else {
+ 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
+ public void onPrepare(InsetsAnimation animation) {
+ if ((animation.getTypeMask() & Type.ime()) != 0) {
+ imeAnim = animation;
+ }
+ if (mRoot.getRootWindowInsets().isVisible(Type.ime())) {
+ showY = mButton.getTop();
+ } else {
+ hideY = mButton.getTop();
+ }
+ }
+
+ @Override
+ public WindowInsets onProgress(WindowInsets insets) {
+ mButton.setY(hideY + (showY - hideY) * imeAnim.getInterpolatedFraction());
+ return insets;
+ }
+
+ @Override
+ public AnimationBounds onStart(InsetsAnimation animation,
+ AnimationBounds bounds) {
+ return bounds;
+ }
+
+ @Override
+ public void onFinish(InsetsAnimation animation) {
+ imeAnim = null;
+ }
+ });
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ TypeEvaluator<Insets> evaluator = (fraction, startValue, endValue) -> Insets.of(
+ (int)(startValue.left + fraction * (endValue.left - startValue.left)),
+ (int)(startValue.top + fraction * (endValue.top - startValue.top)),
+ (int)(startValue.right + fraction * (endValue.right - startValue.right)),
+ (int)(startValue.bottom + fraction * (endValue.bottom - startValue.bottom)));
+
+ WindowInsetsAnimationControlListener listener = new WindowInsetsAnimationControlListener() {
+ @Override
+ public void onReady(WindowInsetsAnimationController controller, int types) {
+ ObjectAnimator animator = ObjectAnimator.ofObject(controller,
+ new InsetsProperty(findViewById(R.id.button),
+ controller.getShownStateInsets()),
+ evaluator, controller.getShownStateInsets(),
+ controller.getHiddenStateInsets());
+ animator.setRepeatCount(ValueAnimator.INFINITE);
+ animator.setRepeatMode(ValueAnimator.REVERSE);
+ animator.start();
+ }
+
+ @Override
+ public void onCancelled() {
+
+ }
+ };
+ }
+}
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index 11d5b25..1c69209 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -16,6 +16,7 @@
package com.android.server;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
@@ -74,6 +75,7 @@
final String typeName = ConnectivityManager.getNetworkTypeName(type);
mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
mNetworkCapabilities = new NetworkCapabilities();
+ mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
mNetworkCapabilities.addTransportType(transport);
switch (transport) {
case TRANSPORT_ETHERNET:
@@ -206,13 +208,11 @@
}
public void suspend() {
- mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, null, null);
- mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
}
public void resume() {
- mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
- mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ addCapability(NET_CAPABILITY_NOT_SUSPENDED);
}
public void disconnect() {
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index 8d99ac7..8eac3ea 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -234,6 +234,7 @@
try {
mLooper = setAsMain ? Looper.getMainLooper() : createLooper();
mTestableLooper = new TestableLooper(mLooper, false);
+ mTestableLooper.getLooper().getThread().setName(test.getClass().getName());
} catch (Exception e) {
throw new RuntimeException(e);
}
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 58e232c..cbce8a5 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -59,10 +59,22 @@
dst[i] = 0;
}
+static bool cmp_style_ids(ResourceId a, ResourceId b) {
+ // If one of a and b is from the framework package (package ID 0x01), and the
+ // other is a dynamic ID (package ID 0x00), then put the dynamic ID after the
+ // framework ID. This ensures that when AssetManager resolves the dynamic IDs,
+ // they will be in sorted order as expected by AssetManager.
+ if ((a.package_id() == kFrameworkPackageId && b.package_id() == 0x00) ||
+ (a.package_id() == 0x00 && b.package_id() == kFrameworkPackageId)) {
+ return b < a;
+ }
+ return a < b;
+}
+
static bool cmp_style_entries(const Style::Entry& a, const Style::Entry& b) {
if (a.key.id) {
if (b.key.id) {
- return a.key.id.value() < b.key.id.value();
+ return cmp_style_ids(a.key.id.value(), b.key.id.value());
}
return true;
} else if (!b.key.id) {
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index 8fbdd7f..af2293f 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -431,6 +431,47 @@
EXPECT_EQ("lib", iter->second);
}
+TEST_F(TableFlattenerTest, FlattenSharedLibraryWithStyle) {
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder().SetCompilationPackage("lib").SetPackageId(0x00).Build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("lib", 0x00)
+ .AddValue("lib:style/Theme",
+ ResourceId(0x00030001),
+ test::StyleBuilder()
+ .AddItem("lib:attr/bar", ResourceId(0x00010002),
+ ResourceUtils::TryParseInt("2"))
+ .AddItem("lib:attr/foo", ResourceId(0x00010001),
+ ResourceUtils::TryParseInt("1"))
+ .AddItem("android:attr/bar", ResourceId(0x01010002),
+ ResourceUtils::TryParseInt("4"))
+ .AddItem("android:attr/foo", ResourceId(0x01010001),
+ ResourceUtils::TryParseInt("3"))
+ .Build())
+ .Build();
+ ResourceTable result;
+ ASSERT_TRUE(Flatten(context.get(), {}, table.get(), &result));
+
+ Maybe<ResourceTable::SearchResult> search_result =
+ result.FindResource(test::ParseNameOrDie("lib:style/Theme"));
+ ASSERT_TRUE(search_result);
+ EXPECT_EQ(0x00u, search_result.value().package->id.value());
+ EXPECT_EQ(0x03u, search_result.value().type->id.value());
+ EXPECT_EQ(0x01u, search_result.value().entry->id.value());
+ ASSERT_EQ(1u, search_result.value().entry->values.size());
+ Value* value = search_result.value().entry->values[0]->value.get();
+ Style* style = ValueCast<Style>(value);
+ ASSERT_TRUE(style);
+ ASSERT_EQ(4u, style->entries.size());
+ // Ensure the attributes from the shared library come after the items from
+ // android.
+ EXPECT_EQ(0x01010001, style->entries[0].key.id.value());
+ EXPECT_EQ(0x01010002, style->entries[1].key.id.value());
+ EXPECT_EQ(0x00010001, style->entries[2].key.id.value());
+ EXPECT_EQ(0x00010002, style->entries[3].key.id.value());
+}
+
TEST_F(TableFlattenerTest, FlattenTableReferencingSharedLibraries) {
std::unique_ptr<IAaptContext> context =
test::ContextBuilder().SetCompilationPackage("app").SetPackageId(0x7f).Build();
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 27960c8..954d401 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -349,6 +349,7 @@
}
return true;
});
+ manifest_action["uses-sdk"]["extension-sdk"];
// Instrumentation actions.
manifest_action["instrumentation"].Action(RequiredNameIsJavaClassName);
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
index 99a26dc..3c55237 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
@@ -53,36 +53,38 @@
val executor = newThreadPool()
- command.javaSourceArgs.map { path ->
- executor.submitCallable {
- val transformer = SourceTransformer(command.protoLogImplClassNameArg,
- command.protoLogCacheClassNameArg, processor)
- val file = File(path)
- val text = injector.readText(file)
- val outSrc = try {
- val code = tryParse(text, path)
- if (containsProtoLogText(text, command.protoLogClassNameArg)) {
- transformer.processClass(text, path, packagePath(file, code), code)
- } else {
+ try {
+ command.javaSourceArgs.map { path ->
+ executor.submitCallable {
+ val transformer = SourceTransformer(command.protoLogImplClassNameArg,
+ command.protoLogCacheClassNameArg, processor)
+ val file = File(path)
+ val text = injector.readText(file)
+ val outSrc = try {
+ val code = tryParse(text, path)
+ if (containsProtoLogText(text, command.protoLogClassNameArg)) {
+ transformer.processClass(text, path, packagePath(file, code), code)
+ } else {
+ text
+ }
+ } catch (ex: ParsingException) {
+ // If we cannot parse this file, skip it (and log why). Compilation will
+ // fail in a subsequent build step.
+ injector.reportParseError(ex)
text
}
- } catch (ex: ParsingException) {
- // If we cannot parse this file, skip it (and log why). Compilation will fail
- // in a subsequent build step.
- injector.reportParseError(ex)
- text
+ path to outSrc
}
- path to outSrc
+ }.map { future ->
+ val (path, outSrc) = future.get()
+ outJar.putNextEntry(ZipEntry(path))
+ outJar.write(outSrc.toByteArray())
+ outJar.closeEntry()
}
- }.map { future ->
- val (path, outSrc) = future.get()
- outJar.putNextEntry(ZipEntry(path))
- outJar.write(outSrc.toByteArray())
- outJar.closeEntry()
+ } finally {
+ executor.shutdown()
}
- executor.shutdown()
-
val cacheSplit = command.protoLogCacheClassNameArg.split(".")
val cacheName = cacheSplit.last()
val cachePackage = cacheSplit.dropLast(1).joinToString(".")
@@ -153,30 +155,32 @@
val executor = newThreadPool()
- command.javaSourceArgs.map { path ->
- executor.submitCallable {
- val file = File(path)
- val text = injector.readText(file)
- if (containsProtoLogText(text, command.protoLogClassNameArg)) {
- try {
- val code = tryParse(text, path)
- builder.findLogCalls(code, path, packagePath(file, code))
- } catch (ex: ParsingException) {
- // If we cannot parse this file, skip it (and log why). Compilation will fail
- // in a subsequent build step.
- injector.reportParseError(ex)
+ try {
+ command.javaSourceArgs.map { path ->
+ executor.submitCallable {
+ val file = File(path)
+ val text = injector.readText(file)
+ if (containsProtoLogText(text, command.protoLogClassNameArg)) {
+ try {
+ val code = tryParse(text, path)
+ builder.findLogCalls(code, path, packagePath(file, code))
+ } catch (ex: ParsingException) {
+ // If we cannot parse this file, skip it (and log why). Compilation will
+ // fail in a subsequent build step.
+ injector.reportParseError(ex)
+ null
+ }
+ } else {
null
}
- } else {
- null
}
+ }.forEach { future ->
+ builder.addLogCalls(future.get() ?: return@forEach)
}
- }.forEach { future ->
- builder.addLogCalls(future.get() ?: return@forEach)
+ } finally {
+ executor.shutdown()
}
- executor.shutdown()
-
val out = injector.fileOutputStream(command.viewerConfigJsonArg)
out.write(builder.build().toByteArray())
out.close()
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 70c9bef..4c9ee85 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -70,6 +70,7 @@
"framework-annotations-lib",
"unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
"unsupportedappusage-annotation", // for dalvik.annotation.compat.UnsupportedAppUsage
+ "framework-telephony-stubs",
],
srcs: [
":framework-wifi-updatable-sources",
diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt
index 8f72040..d377ee6 100644
--- a/wifi/jarjar-rules.txt
+++ b/wifi/jarjar-rules.txt
@@ -1,5 +1,7 @@
rule android.net.InterfaceConfigurationParcel* @0
rule android.net.InterfaceConfiguration* com.android.server.x.wifi.net.InterfaceConfiguration@1
+rule android.net.NetworkFactory* com.android.server.x.wifi.net.NetworkFactory@1
+rule android.net.util.NetUtils* com.android.server.x.wifi.net.util.NetUtils@1
# We don't jar-jar the entire package because, we still use some classes (like
# AsyncChannel in com.android.internal.util) from these packages which are not
@@ -32,6 +34,7 @@
rule android.content.pm.BaseParceledListSlice* android.x.net.wifi.util.BaseParceledListSlice@1
rule android.content.pm.ParceledListSlice* android.x.net.wifi.util.ParceledListSlice@1
rule android.net.shared.Inet4AddressUtils* android.x.net.wifi.util.Inet4AddressUtils@1
+rule android.net.util.MacAddressUtils* android.x.net.wifi.util.MacAddressUtils@1
rule android.os.HandlerExecutor* android.x.net.wifi.util.HandlerExecutor@1
rule android.telephony.Annotation* android.x.net.wifi.util.TelephonyAnnotation@1
rule com.android.internal.util.AsyncChannel* android.x.net.wifi.util.AsyncChannel@1
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index f490766..67f1663 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -92,6 +92,8 @@
void allowAutojoinPasspoint(String fqdn, boolean enableAutoJoin);
+ void setMacRandomizationSettingPasspointEnabled(String fqdn, boolean enable);
+
boolean startScan(String packageName, String featureId);
List<ScanResult> getScanResults(String callingPackage, String callingFeatureId);
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index a9621fc..b2fbb40 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -856,18 +856,6 @@
/**
* @hide
- * For debug: date at which the config was last updated
- */
- public String updateTime;
-
- /**
- * @hide
- * For debug: date at which the config was last updated
- */
- public String creationTime;
-
- /**
- * @hide
* The WiFi configuration is considered to have no internet access for purpose of autojoining
* if there has been a report of it having no internet access, and, it never have had
* internet access in the past.
@@ -1493,12 +1481,6 @@
private String mConnectChoice;
/**
- * The system timestamp when we records the connectChoice. This value is obtained from
- * System.currentTimeMillis
- */
- private long mConnectChoiceTimestamp = INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
-
- /**
* Used to cache the temporary candidate during the network selection procedure. It will be
* kept updating once a new scan result has a higher score than current one
*/
@@ -1601,25 +1583,6 @@
mConnectChoice = newConnectChoice;
}
- /**
- * get the timeStamp when user select a choice over this configuration
- * @return returns when current connectChoice is set (time from System.currentTimeMillis)
- * @hide
- */
- public long getConnectChoiceTimestamp() {
- return mConnectChoiceTimestamp;
- }
-
- /**
- * set the timeStamp when user select a choice over this configuration
- * @param timeStamp, the timestamp set to connectChoiceTimestamp, expected timestamp should
- * be obtained from System.currentTimeMillis
- * @hide
- */
- public void setConnectChoiceTimestamp(long timeStamp) {
- mConnectChoiceTimestamp = timeStamp;
- }
-
/** Get the current Quality network selection status as a String (for debugging). */
@NonNull
public String getNetworkStatusString() {
@@ -1902,7 +1865,6 @@
setCandidate(source.getCandidate());
setCandidateScore(source.getCandidateScore());
setConnectChoice(source.getConnectChoice());
- setConnectChoiceTimestamp(source.getConnectChoiceTimestamp());
setHasEverConnected(source.getHasEverConnected());
}
@@ -1919,7 +1881,6 @@
if (getConnectChoice() != null) {
dest.writeInt(CONNECT_CHOICE_EXISTS);
dest.writeString(getConnectChoice());
- dest.writeLong(getConnectChoiceTimestamp());
} else {
dest.writeInt(CONNECT_CHOICE_NOT_EXISTS);
}
@@ -1938,10 +1899,8 @@
setNetworkSelectionBSSID(in.readString());
if (in.readInt() == CONNECT_CHOICE_EXISTS) {
setConnectChoice(in.readString());
- setConnectChoiceTimestamp(in.readLong());
} else {
setConnectChoice(null);
- setConnectChoiceTimestamp(INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
}
setHasEverConnected(in.readInt() != 0);
}
@@ -2165,9 +2124,6 @@
}
if (mNetworkSelectionStatus.getConnectChoice() != null) {
sbuf.append(" connect choice: ").append(mNetworkSelectionStatus.getConnectChoice());
- sbuf.append(" connect choice set time: ")
- .append(logTimeOfDay(
- mNetworkSelectionStatus.getConnectChoiceTimestamp()));
}
sbuf.append(" hasEverConnected: ")
.append(mNetworkSelectionStatus.getHasEverConnected()).append("\n");
@@ -2179,12 +2135,6 @@
sbuf.append(" numNoInternetAccessReports ");
sbuf.append(this.numNoInternetAccessReports).append("\n");
}
- if (this.updateTime != null) {
- sbuf.append(" update ").append(this.updateTime).append("\n");
- }
- if (this.creationTime != null) {
- sbuf.append(" creation ").append(this.creationTime).append("\n");
- }
if (this.validatedInternetAccess) sbuf.append(" validatedInternetAccess");
if (this.ephemeral) sbuf.append(" ephemeral");
if (this.osu) sbuf.append(" osu");
@@ -2731,8 +2681,6 @@
allowAutojoin = source.allowAutojoin;
numNoInternetAccessReports = source.numNoInternetAccessReports;
noInternetAccessExpected = source.noInternetAccessExpected;
- creationTime = source.creationTime;
- updateTime = source.updateTime;
shared = source.shared;
recentFailure.setAssociationStatus(source.recentFailure.getAssociationStatus());
mRandomizedMacAddress = source.mRandomizedMacAddress;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 2f7cf54..ec3de43 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2369,6 +2369,15 @@
}
/**
+ * Query whether the device supports Station (STA) + Access point (AP) concurrency or not.
+ *
+ * @return true if this device supports STA + AP concurrency, false otherwise.
+ */
+ public boolean isStaApConcurrencySupported() {
+ return isFeatureSupported(WIFI_FEATURE_AP_STA);
+ }
+
+ /**
* @deprecated Please use {@link android.content.pm.PackageManager#hasSystemFeature(String)}
* with {@link android.content.pm.PackageManager#FEATURE_WIFI_RTT} and
* {@link android.content.pm.PackageManager#FEATURE_WIFI_AWARE}.
@@ -4234,6 +4243,24 @@
}
/**
+ * Configure MAC randomization setting for a Passpoint profile.
+ * MAC randomization is enabled by default.
+ *
+ * @param fqdn the FQDN (fully qualified domain name) of the passpoint profile.
+ * @param enable true to enable MAC randomization, false to disable MAC randomization.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void setMacRandomizationSettingPasspointEnabled(@NonNull String fqdn, boolean enable) {
+ try {
+ mService.setMacRandomizationSettingPasspointEnabled(fqdn, enable);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Disable an ephemeral network.
*
* @param ssid in the format of WifiConfiguration's SSID.
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index c0e0890..2fba5a3 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -122,6 +122,12 @@
* Whether the setCredentialSharedWithUser have been called.
*/
private boolean mIsSharedWithUserSet;
+
+ /**
+ * Whether this network is initialized with auto-join enabled (the default) or not.
+ */
+ private boolean mIsInitialAutoJoinEnabled;
+
/**
* Pre-shared key for use with WAPI-PSK networks.
*/
@@ -148,6 +154,7 @@
mIsMetered = false;
mIsSharedWithUser = true;
mIsSharedWithUserSet = false;
+ mIsInitialAutoJoinEnabled = true;
mPriority = UNASSIGNED_PRIORITY;
mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
mWapiPskPassphrase = null;
@@ -440,6 +447,27 @@
return this;
}
+ /**
+ * Specifies whether the suggestion is created with auto-join enabled or disabled. The
+ * user may modify the auto-join configuration of a suggestion directly once the device
+ * associates to the network.
+ * <p>
+ * If auto-join is initialized as disabled the user may still be able to manually connect
+ * to the network. Therefore, disabling auto-join only makes sense if
+ * {@link #setCredentialSharedWithUser(boolean)} is set to true (the default) which
+ * itself implies a secure (non-open) network.
+ * <p>
+ * If not set, defaults to true (i.e. auto-join is initialized as enabled).
+ *
+ * @param enabled true for initializing with auto-join enabled (the default), false to
+ * initializing with auto-join disabled.
+ * @return Instance of (@link {@link Builder} to enable chaining of the builder method.
+ */
+ public @NonNull Builder setIsInitialAutoJoinEnabled(boolean enabled) {
+ mIsInitialAutoJoinEnabled = enabled;
+ return this;
+ }
+
private void setSecurityParamsInWifiConfiguration(
@NonNull WifiConfiguration configuration) {
if (!TextUtils.isEmpty(mWpa2PskPassphrase)) { // WPA-PSK network.
@@ -587,7 +615,6 @@
+ "suggestion with Passpoint configuration");
}
wifiConfiguration = buildWifiConfigurationForPasspoint();
-
} else {
if (mSsid == null) {
throw new IllegalStateException("setSsid should be invoked for suggestion");
@@ -608,6 +635,12 @@
}
mIsSharedWithUser = false;
}
+
+ if (!mIsSharedWithUser && !mIsInitialAutoJoinEnabled) {
+ throw new IllegalStateException("Should have not a network with both "
+ + "setIsUserAllowedToManuallyConnect and "
+ + "setIsAutoJoinEnabled set to false");
+ }
}
return new WifiNetworkSuggestion(
@@ -615,7 +648,8 @@
mPasspointConfiguration,
mIsAppInteractionRequired,
mIsUserInteractionRequired,
- mIsSharedWithUser);
+ mIsSharedWithUser,
+ mIsInitialAutoJoinEnabled);
}
}
@@ -642,6 +676,7 @@
* @hide
*/
public final boolean isUserInteractionRequired;
+
/**
* Whether app share credential with the user, allow user use provided credential to
* connect network manually.
@@ -649,6 +684,12 @@
*/
public final boolean isUserAllowedToManuallyConnect;
+ /**
+ * Whether the suggestion will be initialized as auto-joined or not.
+ * @hide
+ */
+ public final boolean isInitialAutoJoinEnabled;
+
/** @hide */
public WifiNetworkSuggestion() {
this.wifiConfiguration = null;
@@ -656,6 +697,7 @@
this.isAppInteractionRequired = false;
this.isUserInteractionRequired = false;
this.isUserAllowedToManuallyConnect = true;
+ this.isInitialAutoJoinEnabled = true;
}
/** @hide */
@@ -663,7 +705,8 @@
@Nullable PasspointConfiguration passpointConfiguration,
boolean isAppInteractionRequired,
boolean isUserInteractionRequired,
- boolean isUserAllowedToManuallyConnect) {
+ boolean isUserAllowedToManuallyConnect,
+ boolean isInitialAutoJoinEnabled) {
checkNotNull(networkConfiguration);
this.wifiConfiguration = networkConfiguration;
this.passpointConfiguration = passpointConfiguration;
@@ -671,6 +714,7 @@
this.isAppInteractionRequired = isAppInteractionRequired;
this.isUserInteractionRequired = isUserInteractionRequired;
this.isUserAllowedToManuallyConnect = isUserAllowedToManuallyConnect;
+ this.isInitialAutoJoinEnabled = isInitialAutoJoinEnabled;
}
public static final @NonNull Creator<WifiNetworkSuggestion> CREATOR =
@@ -682,7 +726,8 @@
in.readParcelable(null), // PasspointConfiguration
in.readBoolean(), // isAppInteractionRequired
in.readBoolean(), // isUserInteractionRequired
- in.readBoolean() // isSharedCredentialWithUser
+ in.readBoolean(), // isSharedCredentialWithUser
+ in.readBoolean() // isAutoJoinEnabled
);
}
@@ -704,6 +749,7 @@
dest.writeBoolean(isAppInteractionRequired);
dest.writeBoolean(isUserInteractionRequired);
dest.writeBoolean(isUserAllowedToManuallyConnect);
+ dest.writeBoolean(isInitialAutoJoinEnabled);
}
@Override
@@ -744,6 +790,7 @@
.append(", isAppInteractionRequired=").append(isAppInteractionRequired)
.append(", isUserInteractionRequired=").append(isUserInteractionRequired)
.append(", isUserAllowedToManuallyConnect=").append(isUserAllowedToManuallyConnect)
+ .append(", isInitialAutoJoinEnabled=").append(isInitialAutoJoinEnabled)
.append(" ]");
return sb.toString();
}
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 1822e84..7c335fc 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -431,6 +431,13 @@
private boolean mIsAutoJoinEnabled = true;
/**
+ * The mac randomization setting specifies whether a randomized or device MAC address will
+ * be used to connect to the passpoint network. If true, a randomized MAC will be used.
+ * Otherwise, the device MAC address will be used.
+ */
+ private boolean mIsMacRandomizationEnabled = true;
+
+ /**
* Configures the auto-association status of this Passpoint configuration. A value of true
* indicates that the configuration will be considered for auto-connection, a value of false
* indicates that only manual connection will work - the framework will not auto-associate to
@@ -444,6 +451,18 @@
}
/**
+ * Configures the MAC randomization setting for this Passpoint configuration.
+ * If set to true, the framework will use a randomized MAC address to connect to this Passpoint
+ * network. Otherwise, the framework will use the device MAC address.
+ *
+ * @param enabled true to use randomized MAC address, false to use device MAC address.
+ * @hide
+ */
+ public void setMacRandomizationEnabled(boolean enabled) {
+ mIsMacRandomizationEnabled = enabled;
+ }
+
+ /**
* Indicates whether the Passpoint configuration may be auto-connected to by the framework. A
* value of true indicates that auto-connection can happen, a value of false indicates that it
* cannot. However, even when auto-connection is not possible manual connection by the user is
@@ -459,6 +478,19 @@
}
/**
+ * Indicates whether a randomized MAC address or device MAC address will be used for
+ * connections to this Passpoint network. If true, a randomized MAC address will be used.
+ * Otherwise, the device MAC address will be used.
+ *
+ * @return true for MAC randomization enabled. False for disabled.
+ * @hide
+ */
+ @SystemApi
+ public boolean isMacRandomizationEnabled() {
+ return mIsMacRandomizationEnabled;
+ }
+
+ /**
* Constructor for creating PasspointConfiguration with default values.
*/
public PasspointConfiguration() {}
@@ -501,6 +533,7 @@
mAaaServerTrustedNames = source.mAaaServerTrustedNames;
mCarrierId = source.mCarrierId;
mIsAutoJoinEnabled = source.mIsAutoJoinEnabled;
+ mIsMacRandomizationEnabled = source.mIsMacRandomizationEnabled;
}
@Override
@@ -531,6 +564,7 @@
dest.writeBundle(bundle);
dest.writeInt(mCarrierId);
dest.writeBoolean(mIsAutoJoinEnabled);
+ dest.writeBoolean(mIsMacRandomizationEnabled);
}
@Override
@@ -562,6 +596,7 @@
&& mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes
&& mCarrierId == that.mCarrierId
&& mIsAutoJoinEnabled == that.mIsAutoJoinEnabled
+ && mIsMacRandomizationEnabled == that.mIsMacRandomizationEnabled
&& (mServiceFriendlyNames == null ? that.mServiceFriendlyNames == null
: mServiceFriendlyNames.equals(that.mServiceFriendlyNames));
}
@@ -572,7 +607,7 @@
mUpdateIdentifier, mCredentialPriority, mSubscriptionCreationTimeInMillis,
mSubscriptionExpirationTimeInMillis, mUsageLimitUsageTimePeriodInMinutes,
mUsageLimitStartTimeInMillis, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes,
- mServiceFriendlyNames, mCarrierId, mIsAutoJoinEnabled);
+ mServiceFriendlyNames, mCarrierId, mIsAutoJoinEnabled, mIsMacRandomizationEnabled);
}
@Override
@@ -627,6 +662,7 @@
}
builder.append("CarrierId:" + mCarrierId);
builder.append("IsAutoJoinEnabled:" + mIsAutoJoinEnabled);
+ builder.append("mIsMacRandomizationEnabled:" + mIsMacRandomizationEnabled);
return builder.toString();
}
@@ -733,6 +769,7 @@
config.setServiceFriendlyNames(friendlyNamesMap);
config.mCarrierId = in.readInt();
config.mIsAutoJoinEnabled = in.readBoolean();
+ config.mIsMacRandomizationEnabled = in.readBoolean();
return config;
}
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index 3c13562d..1cf3825 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -187,6 +187,11 @@
}
@Override
+ public void setMacRandomizationSettingPasspointEnabled(String fqdn, boolean enable) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public boolean startScan(String packageName, String featureId) {
throw new UnsupportedOperationException();
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 5bdc344..983ac82 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -1706,6 +1706,17 @@
verify(mWifiService).allowAutojoinPasspoint(fqdn, true);
}
+ /**
+ * Test behavior of
+ * {@link WifiManager#setMacRandomizationSettingPasspointEnabled(String, boolean)}
+ */
+ @Test
+ public void testSetMacRandomizationSettingPasspointEnabled() throws Exception {
+ final String fqdn = "FullyQualifiedDomainName";
+ mWifiManager.setMacRandomizationSettingPasspointEnabled(fqdn, true);
+ verify(mWifiService).setMacRandomizationSettingPasspointEnabled(fqdn, true);
+ }
+
/**
* Test behavior of {@link WifiManager#disconnect()}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index ac91544..cb1b774 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -32,8 +32,6 @@
*/
@SmallTest
public class WifiNetworkSuggestionTest {
- private static final int TEST_UID = 45677;
- private static final int TEST_UID_OTHER = 45673;
private static final String TEST_SSID = "\"Test123\"";
private static final String TEST_BSSID = "12:12:12:12:12:12";
private static final String TEST_SSID_1 = "\"Test1234\"";
@@ -61,7 +59,8 @@
assertEquals(WifiConfiguration.METERED_OVERRIDE_NONE,
suggestion.wifiConfiguration.meteredOverride);
assertEquals(-1, suggestion.wifiConfiguration.priority);
- assertEquals(false, suggestion.isUserAllowedToManuallyConnect);
+ assertFalse(suggestion.isUserAllowedToManuallyConnect);
+ assertTrue(suggestion.isInitialAutoJoinEnabled);
}
/**
@@ -90,7 +89,8 @@
assertEquals(WifiConfiguration.METERED_OVERRIDE_NONE,
suggestion.wifiConfiguration.meteredOverride);
assertEquals(0, suggestion.wifiConfiguration.priority);
- assertEquals(false, suggestion.isUserAllowedToManuallyConnect);
+ assertFalse(suggestion.isUserAllowedToManuallyConnect);
+ assertTrue(suggestion.isInitialAutoJoinEnabled);
}
/**
@@ -105,6 +105,7 @@
.setSsid(TEST_SSID)
.setWpa2Passphrase(TEST_PRESHARED_KEY)
.setIsUserInteractionRequired(true)
+ .setIsInitialAutoJoinEnabled(false)
.setIsMetered(true)
.build();
@@ -119,6 +120,7 @@
suggestion.wifiConfiguration.meteredOverride);
assertEquals(-1, suggestion.wifiConfiguration.priority);
assertTrue(suggestion.isUserAllowedToManuallyConnect);
+ assertFalse(suggestion.isInitialAutoJoinEnabled);
}
/**
@@ -140,6 +142,7 @@
assertNull(suggestion.wifiConfiguration.preSharedKey);
assertTrue(suggestion.wifiConfiguration.requirePMF);
assertFalse(suggestion.isUserAllowedToManuallyConnect);
+ assertTrue(suggestion.isInitialAutoJoinEnabled);
}
/**
@@ -152,6 +155,7 @@
.setSsid(TEST_SSID)
.setWpa3Passphrase(TEST_PRESHARED_KEY)
.setCredentialSharedWithUser(true)
+ .setIsInitialAutoJoinEnabled(false)
.build();
assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
@@ -161,6 +165,7 @@
suggestion.wifiConfiguration.preSharedKey);
assertTrue(suggestion.wifiConfiguration.requirePMF);
assertTrue(suggestion.isUserAllowedToManuallyConnect);
+ assertFalse(suggestion.isInitialAutoJoinEnabled);
}
@@ -191,6 +196,7 @@
// allowedSuiteBCiphers are set according to the loaded certificate and cannot be tested
// here.
assertTrue(suggestion.isUserAllowedToManuallyConnect);
+ assertTrue(suggestion.isInitialAutoJoinEnabled);
}
/**
@@ -526,7 +532,7 @@
configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion(
- configuration, null, false, true, true);
+ configuration, null, false, true, true, false);
Parcel parcelW = Parcel.obtain();
suggestion.writeToParcel(parcelW, 0);
@@ -548,6 +554,8 @@
parcelSuggestion.isAppInteractionRequired);
assertEquals(suggestion.isUserInteractionRequired,
parcelSuggestion.isUserInteractionRequired);
+ assertEquals(suggestion.isInitialAutoJoinEnabled,
+ parcelSuggestion.isInitialAutoJoinEnabled);
}
/**
@@ -580,6 +588,8 @@
parcelSuggestion.isAppInteractionRequired);
assertEquals(suggestion.isUserInteractionRequired,
parcelSuggestion.isUserInteractionRequired);
+ assertEquals(suggestion.isInitialAutoJoinEnabled,
+ parcelSuggestion.isInitialAutoJoinEnabled);
}
/**
@@ -593,14 +603,14 @@
configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, null, true, false, true);
+ new WifiNetworkSuggestion(configuration, null, true, false, true, true);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID;
configuration1.BSSID = TEST_BSSID;
configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, null, false, true, true);
+ new WifiNetworkSuggestion(configuration1, null, false, true, true, false);
assertEquals(suggestion, suggestion1);
assertEquals(suggestion.hashCode(), suggestion1.hashCode());
@@ -616,13 +626,13 @@
configuration.SSID = TEST_SSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, null, false, false, true);
+ new WifiNetworkSuggestion(configuration, null, false, false, true, false);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID_1;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, null, false, false, true);
+ new WifiNetworkSuggestion(configuration1, null, false, false, true, false);
assertNotEquals(suggestion, suggestion1);
}
@@ -638,13 +648,13 @@
configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, null, false, false, true);
+ new WifiNetworkSuggestion(configuration, null, false, false, true, true);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID;
configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, null, false, false, true);
+ new WifiNetworkSuggestion(configuration1, null, false, false, true, true);
assertNotEquals(suggestion, suggestion1);
}
@@ -659,13 +669,13 @@
configuration.SSID = TEST_SSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, null, false, false, true);
+ new WifiNetworkSuggestion(configuration, null, false, false, true, true);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID;
configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, null, false, false, true);
+ new WifiNetworkSuggestion(configuration1, null, false, false, true, true);
assertNotEquals(suggestion, suggestion1);
}
@@ -714,9 +724,38 @@
*/
@Test(expected = IllegalStateException.class)
public void testSetIsUserAllowedToManuallyConnectToWithOpenNetwork() {
- WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
+ new WifiNetworkSuggestion.Builder()
.setSsid(TEST_SSID)
.setCredentialSharedWithUser(true)
.build();
}
+
+ /**
+ * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception
+ * when {@link WifiNetworkSuggestion.Builder#setIsInitialAutoJoinEnabled(boolean)} to
+ * false on a open network suggestion.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testSetIsAutoJoinDisabledWithOpenNetwork() {
+ new WifiNetworkSuggestion.Builder()
+ .setSsid(TEST_SSID)
+ .setIsInitialAutoJoinEnabled(false)
+ .build();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception
+ * when set both {@link WifiNetworkSuggestion.Builder#setIsInitialAutoJoinEnabled(boolean)}
+ * and {@link WifiNetworkSuggestion.Builder#setCredentialSharedWithUser(boolean)} (boolean)}
+ * to false on a network suggestion.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testSetIsAutoJoinDisabledWithSecureNetworkNotSharedWithUser() {
+ new WifiNetworkSuggestion.Builder()
+ .setSsid(TEST_SSID)
+ .setWpa2Passphrase(TEST_PRESHARED_KEY)
+ .setCredentialSharedWithUser(false)
+ .setIsInitialAutoJoinEnabled(false)
+ .build();
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
index 94054fd..603e78b 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -172,6 +172,7 @@
assertFalse(config.validate());
assertFalse(config.validateForR2());
assertTrue(config.isAutoJoinEnabled());
+ assertTrue(config.isMacRandomizationEnabled());
}
/**