Merge "Skip operation on content overlay if task's vanished" into sc-dev
diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp
index cd9be9b..8964668 100644
--- a/apex/appsearch/framework/Android.bp
+++ b/apex/appsearch/framework/Android.bp
@@ -57,12 +57,8 @@
// This list must be kept in sync with jarjar.txt
"modules-utils-preconditions",
],
- libs: ["unsupportedappusage"], // TODO(b/181887768) should be removed
defaults: ["framework-module-defaults"],
permitted_packages: ["android.app.appsearch"],
- aidl: {
- include_dirs: ["frameworks/base/core/java"], // TODO(b/146218515) should be removed
- },
jarjar_rules: "jarjar-rules.txt",
apex_available: ["com.android.appsearch"],
impl_library_visibility: [
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index b5e3662..82b6d62 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -25,7 +25,6 @@
import android.app.appsearch.aidl.IAppSearchResultCallback;
import android.app.appsearch.exceptions.AppSearchException;
import android.app.appsearch.util.SchemaMigrationUtil;
-import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -309,19 +308,6 @@
}
/**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- @UnsupportedAppUsage
- public void getByUri(
- @NonNull GetByUriRequest request,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull BatchResultCallback<String, GenericDocument> callback) {
- getByDocumentId(request.toGetByDocumentIdRequest(), executor, callback);
- }
-
- /**
* Gets {@link GenericDocument} objects by document IDs in a namespace from the {@link
* AppSearchSession} database.
*
@@ -521,19 +507,6 @@
}
/**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- @UnsupportedAppUsage
- public void remove(
- @NonNull RemoveByUriRequest request,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull BatchResultCallback<String, Void> callback) {
- remove(request.toRemoveByDocumentIdRequest(), executor, callback);
- }
-
- /**
* Removes {@link GenericDocument} objects by document IDs in a namespace from the {@link
* AppSearchSession} database.
*
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
index 0ee5e65..2e04d71 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
@@ -22,7 +22,6 @@
import android.app.appsearch.exceptions.IllegalSchemaException;
import android.app.appsearch.util.BundleUtil;
import android.app.appsearch.util.IndentingStringBuilder;
-import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.util.ArraySet;
@@ -643,60 +642,8 @@
}
}
- /**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- public static class Int64PropertyConfig extends PropertyConfig {
- @UnsupportedAppUsage
- Int64PropertyConfig(@NonNull Bundle bundle) {
- super(bundle);
- }
-
- /** Builder for {@link Int64PropertyConfig}. */
- public static final class Builder {
- private final String mPropertyName;
- private @Cardinality int mCardinality = CARDINALITY_OPTIONAL;
-
- /** Creates a new {@link Int64PropertyConfig.Builder}. */
- @UnsupportedAppUsage
- public Builder(@NonNull String propertyName) {
- mPropertyName = Objects.requireNonNull(propertyName);
- }
-
- /**
- * The cardinality of the property (whether it is optional, required or repeated).
- *
- * <p>If this method is not called, the default cardinality is {@link
- * PropertyConfig#CARDINALITY_OPTIONAL}.
- */
- @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
- @NonNull
- @UnsupportedAppUsage
- public Int64PropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
- Preconditions.checkArgumentInRange(
- cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality");
- mCardinality = cardinality;
- return this;
- }
-
- /** Constructs a new {@link Int64PropertyConfig} from the contents of this builder. */
- @NonNull
- @UnsupportedAppUsage
- public Int64PropertyConfig build() {
- Bundle bundle = new Bundle();
- bundle.putString(NAME_FIELD, mPropertyName);
- bundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_LONG);
- bundle.putInt(CARDINALITY_FIELD, mCardinality);
- return new Int64PropertyConfig(bundle);
- }
- }
- }
-
/** Configuration for a property containing a 64-bit integer. */
- // TODO(b/181887768): This should extend directly from PropertyConfig
- public static final class LongPropertyConfig extends Int64PropertyConfig {
+ public static final class LongPropertyConfig extends PropertyConfig {
LongPropertyConfig(@NonNull Bundle bundle) {
super(bundle);
}
@@ -896,8 +843,7 @@
/** Builder for {@link DocumentPropertyConfig}. */
public static final class Builder {
private final String mPropertyName;
- // TODO(b/181887768): This should be final
- private String mSchemaType;
+ private final String mSchemaType;
private @Cardinality int mCardinality = CARDINALITY_OPTIONAL;
private boolean mShouldIndexNestedProperties = false;
@@ -916,29 +862,6 @@
}
/**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- @UnsupportedAppUsage
- public Builder(@NonNull String propertyName) {
- mPropertyName = Objects.requireNonNull(propertyName);
- mSchemaType = null;
- }
-
- /**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- @UnsupportedAppUsage
- @NonNull
- public Builder setSchemaType(@NonNull String schemaType) {
- mSchemaType = Objects.requireNonNull(schemaType);
- return this;
- }
-
- /**
* The cardinality of the property (whether it is optional, required or repeated).
*
* <p>If this method is not called, the default cardinality is {@link
@@ -967,18 +890,6 @@
return this;
}
- /**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- @UnsupportedAppUsage
- @NonNull
- public DocumentPropertyConfig.Builder setIndexNestedProperties(
- boolean indexNestedProperties) {
- return setShouldIndexNestedProperties(indexNestedProperties);
- }
-
/** Constructs a new {@link PropertyConfig} from the contents of this builder. */
@NonNull
public DocumentPropertyConfig build() {
@@ -987,9 +898,7 @@
bundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_DOCUMENT);
bundle.putInt(CARDINALITY_FIELD, mCardinality);
bundle.putBoolean(INDEX_NESTED_PROPERTIES_FIELD, mShouldIndexNestedProperties);
- // TODO(b/181887768): Remove checkNotNull after the deprecated constructor (which
- // is the only way to get null here) is removed
- bundle.putString(SCHEMA_TYPE_FIELD, Objects.requireNonNull(mSchemaType));
+ bundle.putString(SCHEMA_TYPE_FIELD, mSchemaType);
return new DocumentPropertyConfig(bundle);
}
}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
index 5801972..4d27519 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
@@ -23,7 +23,6 @@
import android.annotation.SuppressLint;
import android.app.appsearch.util.BundleUtil;
import android.app.appsearch.util.IndentingStringBuilder;
-import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
@@ -131,17 +130,6 @@
return mBundle;
}
- /**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- @UnsupportedAppUsage
- @NonNull
- public String getUri() {
- return getId();
- }
-
/** Returns the unique identifier of the {@link GenericDocument}. */
@NonNull
public String getId() {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
deleted file mode 100644
index 7b05eac..0000000
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.appsearch;
-
-import android.annotation.NonNull;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-
-import com.android.internal.util.Preconditions;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
-@Deprecated
-public final class GetByUriRequest {
- /**
- * Schema type to be used in {@link GetByUriRequest.Builder#addProjection} to apply property
- * paths to all results, excepting any types that have had their own, specific property paths
- * set.
- */
- public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
-
- private final String mNamespace;
- private final Set<String> mIds;
- private final Map<String, List<String>> mTypePropertyPathsMap;
-
- GetByUriRequest(
- @NonNull String namespace,
- @NonNull Set<String> ids,
- @NonNull Map<String, List<String>> typePropertyPathsMap) {
- mNamespace = Objects.requireNonNull(namespace);
- mIds = Objects.requireNonNull(ids);
- mTypePropertyPathsMap = Objects.requireNonNull(typePropertyPathsMap);
- }
-
- /** Returns the namespace attached to the request. */
- @NonNull
- public String getNamespace() {
- return mNamespace;
- }
-
- /** Returns the set of document IDs attached to the request. */
- @NonNull
- public Set<String> getUris() {
- return Collections.unmodifiableSet(mIds);
- }
-
- /**
- * Returns a map from schema type to property paths to be used for projection.
- *
- * <p>If the map is empty, then all properties will be retrieved for all results.
- *
- * <p>Calling this function repeatedly is inefficient. Prefer to retain the Map returned by this
- * function, rather than calling it multiple times.
- */
- @NonNull
- public Map<String, List<String>> getProjections() {
- Map<String, List<String>> copy = new ArrayMap<>();
- for (Map.Entry<String, List<String>> entry : mTypePropertyPathsMap.entrySet()) {
- copy.put(entry.getKey(), new ArrayList<>(entry.getValue()));
- }
- return copy;
- }
-
- /**
- * Returns a map from schema type to property paths to be used for projection.
- *
- * <p>If the map is empty, then all properties will be retrieved for all results.
- *
- * <p>A more efficient version of {@link #getProjections}, but it returns a modifiable map. This
- * is not meant to be unhidden and should only be used by internal classes.
- *
- * @hide
- */
- @NonNull
- public Map<String, List<String>> getProjectionsInternal() {
- return mTypePropertyPathsMap;
- }
-
- /**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- @NonNull
- public GetByDocumentIdRequest toGetByDocumentIdRequest() {
- GetByDocumentIdRequest.Builder builder =
- new GetByDocumentIdRequest.Builder(mNamespace).addIds(mIds);
- for (Map.Entry<String, List<String>> projection : mTypePropertyPathsMap.entrySet()) {
- builder.addProjection(projection.getKey(), projection.getValue());
- }
- return builder.build();
- }
-
- /**
- * Builder for {@link GetByUriRequest} objects.
- *
- * <p>Once {@link #build} is called, the instance can no longer be used.
- */
- public static final class Builder {
- private final String mNamespace;
- private final Set<String> mIds = new ArraySet<>();
- private final Map<String, List<String>> mProjectionTypePropertyPaths = new ArrayMap<>();
- private boolean mBuilt = false;
-
- /**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- @UnsupportedAppUsage
- public Builder(@NonNull String namespace) {
- mNamespace = Objects.requireNonNull(namespace);
- }
-
- /**
- * Adds one or more document IDs to the request.
- *
- * @throws IllegalStateException if the builder has already been used.
- */
- @NonNull
- public Builder addUris(@NonNull String... ids) {
- Objects.requireNonNull(ids);
- return addUris(Arrays.asList(ids));
- }
-
- /**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- @UnsupportedAppUsage
- @NonNull
- public Builder addUris(@NonNull Collection<String> ids) {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
- Objects.requireNonNull(ids);
- mIds.addAll(ids);
- return this;
- }
-
- /**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- @UnsupportedAppUsage
- @NonNull
- public Builder addProjection(
- @NonNull String schemaType, @NonNull Collection<String> propertyPaths) {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
- Objects.requireNonNull(schemaType);
- Objects.requireNonNull(propertyPaths);
- List<String> propertyPathsList = new ArrayList<>(propertyPaths.size());
- for (String propertyPath : propertyPaths) {
- Objects.requireNonNull(propertyPath);
- propertyPathsList.add(propertyPath);
- }
- mProjectionTypePropertyPaths.put(schemaType, propertyPathsList);
- return this;
- }
-
- /**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- @UnsupportedAppUsage
- @NonNull
- public GetByUriRequest build() {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
- mBuilt = true;
- return new GetByUriRequest(mNamespace, mIds, mProjectionTypePropertyPaths);
- }
- }
-}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
deleted file mode 100644
index 9c74966..0000000
--- a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.appsearch;
-
-import android.annotation.NonNull;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.util.ArraySet;
-
-import com.android.internal.util.Preconditions;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
-@Deprecated
-public final class RemoveByUriRequest {
- private final String mNamespace;
- private final Set<String> mIds;
-
- RemoveByUriRequest(String namespace, Set<String> ids) {
- mNamespace = namespace;
- mIds = ids;
- }
-
- /** Returns the namespace to remove documents from. */
- @NonNull
- public String getNamespace() {
- return mNamespace;
- }
-
- /** Returns the set of document IDs attached to the request. */
- @NonNull
- public Set<String> getUris() {
- return Collections.unmodifiableSet(mIds);
- }
-
- /**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- @NonNull
- public RemoveByDocumentIdRequest toRemoveByDocumentIdRequest() {
- return new RemoveByDocumentIdRequest.Builder(mNamespace).addIds(mIds).build();
- }
-
- /**
- * Builder for {@link RemoveByUriRequest} objects.
- *
- * <p>Once {@link #build} is called, the instance can no longer be used.
- */
- public static final class Builder {
- private final String mNamespace;
- private final Set<String> mIds = new ArraySet<>();
- private boolean mBuilt = false;
-
- /**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- @UnsupportedAppUsage
- public Builder(@NonNull String namespace) {
- mNamespace = Objects.requireNonNull(namespace);
- }
-
- /**
- * Adds one or more document IDs to the request.
- *
- * @throws IllegalStateException if the builder has already been used.
- */
- @NonNull
- public Builder addUris(@NonNull String... ids) {
- Objects.requireNonNull(ids);
- return addUris(Arrays.asList(ids));
- }
-
- /**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- @UnsupportedAppUsage
- @NonNull
- public Builder addUris(@NonNull Collection<String> ids) {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
- Objects.requireNonNull(ids);
- mIds.addAll(ids);
- return this;
- }
-
- /**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- @UnsupportedAppUsage
- @NonNull
- public RemoveByUriRequest build() {
- Preconditions.checkState(!mBuilt, "Builder has already been used");
- mBuilt = true;
- return new RemoveByUriRequest(mNamespace, mIds);
- }
- }
-}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
index c388bde..e807803 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
@@ -18,7 +18,6 @@
import android.annotation.CurrentTimeMillisLong;
import android.annotation.NonNull;
-import android.compat.annotation.UnsupportedAppUsage;
import java.util.Objects;
@@ -67,8 +66,7 @@
/** Builder for {@link ReportUsageRequest} objects. */
public static final class Builder {
private final String mNamespace;
- // TODO(b/181887768): Make this final
- private String mDocumentId;
+ private final String mDocumentId;
private Long mUsageTimestampMillis;
/**
@@ -85,40 +83,6 @@
}
/**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- @UnsupportedAppUsage
- public Builder(@NonNull String namespace) {
- mNamespace = Objects.requireNonNull(namespace);
- }
-
- /**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- @UnsupportedAppUsage
- @NonNull
- public Builder setUri(@NonNull String uri) {
- mDocumentId = uri;
- return this;
- }
-
- /**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- @UnsupportedAppUsage
- @NonNull
- public ReportUsageRequest.Builder setUsageTimeMillis(
- @CurrentTimeMillisLong long usageTimestampMillis) {
- return setUsageTimestampMillis(usageTimestampMillis);
- }
-
- /**
* Sets the timestamp in milliseconds of the usage report (the time at which the document
* was used).
*
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
index 4beb667..f6a597c 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import com.android.internal.util.Preconditions;
@@ -84,17 +83,6 @@
}
/**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- @UnsupportedAppUsage
- @NonNull
- public List<MatchInfo> getMatches() {
- return getMatchInfos();
- }
-
- /**
* Returns a list of {@link MatchInfo}s providing information about how the document in {@link
* #getGenericDocument} matched the query.
*
@@ -196,17 +184,6 @@
return this;
}
- /**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- @UnsupportedAppUsage
- @NonNull
- public Builder addMatch(@NonNull MatchInfo matchInfo) {
- return addMatchInfo(matchInfo);
- }
-
/** Adds another match to this SearchResult. */
@NonNull
public Builder addMatchInfo(@NonNull MatchInfo matchInfo) {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
index 3e5a2ca..a3a4a23 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.util.ArraySet;
@@ -342,17 +341,6 @@
return mBundle.getString(NAMESPACE_FIELD, /*defaultValue=*/ "");
}
- /**
- * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
- * @hide
- */
- @Deprecated
- @UnsupportedAppUsage
- @NonNull
- public String getUri() {
- return getDocumentId();
- }
-
/** Returns the id of the {@link GenericDocument} that failed to be migrated. */
@NonNull
public String getDocumentId() {
diff --git a/apex/appsearch/service/Android.bp b/apex/appsearch/service/Android.bp
index b101895..b6521ff 100644
--- a/apex/appsearch/service/Android.bp
+++ b/apex/appsearch/service/Android.bp
@@ -52,7 +52,6 @@
libs: [
"framework-appsearch.impl",
"framework-statsd.stubs.module_lib",
- "unsupportedappusage", // TODO(b/181887768) should be removed
],
defaults: ["framework-system-server-module-defaults"],
permitted_packages: [
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java
index c44fd40..29048b2 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java
@@ -81,6 +81,14 @@
"sampling_interval_for_batch_call_stats";
public static final String KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS =
"sampling_interval_for_put_document_stats";
+ public static final String KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS =
+ "sampling_interval_for_initialize_stats";
+ public static final String KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS =
+ "sampling_interval_for_search_stats";
+ public static final String KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS =
+ "sampling_interval_for_global_search_stats";
+ public static final String KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS =
+ "sampling_interval_for_optimize_stats";
public static final String KEY_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES =
"limit_config_max_document_size_bytes";
public static final String KEY_LIMIT_CONFIG_MAX_DOCUMENT_COUNT =
@@ -95,6 +103,10 @@
KEY_SAMPLING_INTERVAL_DEFAULT,
KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS,
KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS,
+ KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS,
+ KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS,
+ KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS,
+ KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS,
KEY_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES,
KEY_LIMIT_CONFIG_MAX_DOCUMENT_COUNT,
KEY_BYTES_OPTIMIZE_THRESHOLD,
@@ -245,6 +257,58 @@
}
}
+ /**
+ * Returns cached value for sampling interval for initialize.
+ *
+ * <p>For example, sampling_interval=10 means that one out of every 10 stats was logged.
+ */
+ public int getCachedSamplingIntervalForInitializeStats() {
+ synchronized (mLock) {
+ throwIfClosedLocked();
+ return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS,
+ getCachedSamplingIntervalDefault());
+ }
+ }
+
+ /**
+ * Returns cached value for sampling interval for search.
+ *
+ * <p>For example, sampling_interval=10 means that one out of every 10 stats was logged.
+ */
+ public int getCachedSamplingIntervalForSearchStats() {
+ synchronized (mLock) {
+ throwIfClosedLocked();
+ return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS,
+ getCachedSamplingIntervalDefault());
+ }
+ }
+
+ /**
+ * Returns cached value for sampling interval for globalSearch.
+ *
+ * <p>For example, sampling_interval=10 means that one out of every 10 stats was logged.
+ */
+ public int getCachedSamplingIntervalForGlobalSearchStats() {
+ synchronized (mLock) {
+ throwIfClosedLocked();
+ return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS,
+ getCachedSamplingIntervalDefault());
+ }
+ }
+
+ /**
+ * Returns cached value for sampling interval for optimize.
+ *
+ * <p>For example, sampling_interval=10 means that one out of every 10 stats was logged.
+ */
+ public int getCachedSamplingIntervalForOptimizeStats() {
+ synchronized (mLock) {
+ throwIfClosedLocked();
+ return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS,
+ getCachedSamplingIntervalDefault());
+ }
+ }
+
/** Returns the maximum serialized size an indexed document can be, in bytes. */
public int getCachedLimitConfigMaxDocumentSizeBytes() {
synchronized (mLock) {
@@ -343,6 +407,10 @@
case KEY_SAMPLING_INTERVAL_DEFAULT:
case KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS:
case KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS:
+ case KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS:
+ case KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS:
+ case KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS:
+ case KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS:
synchronized (mLock) {
mBundleLocked.putInt(key, properties.getInt(key, DEFAULT_SAMPLING_INTERVAL));
}
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 1d66beb..db23a6d 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -59,6 +59,7 @@
import com.android.server.LocalManagerRegistry;
import com.android.server.SystemService;
import com.android.server.appsearch.external.localstorage.stats.CallStats;
+import com.android.server.appsearch.external.localstorage.stats.OptimizeStats;
import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore;
import com.android.server.appsearch.stats.StatsCollector;
import com.android.server.appsearch.util.PackageUtil;
@@ -1479,20 +1480,42 @@
private void checkForOptimize(AppSearchUserInstance instance, int mutateBatchSize) {
EXECUTOR.execute(() -> {
+ long totalLatencyStartMillis = SystemClock.elapsedRealtime();
+ OptimizeStats.Builder builder = new OptimizeStats.Builder();
try {
- instance.getAppSearchImpl().checkForOptimize(mutateBatchSize);
+ instance.getAppSearchImpl().checkForOptimize(mutateBatchSize, builder);
} catch (AppSearchException e) {
Log.w(TAG, "Error occurred when check for optimize", e);
+ } finally {
+ OptimizeStats oStats = builder
+ .setTotalLatencyMillis(
+ (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis))
+ .build();
+ if (oStats.getOriginalDocumentCount() > 0) {
+ // see if optimize has been run by checking originalDocumentCount
+ instance.getLogger().logStats(oStats);
+ }
}
});
}
private void checkForOptimize(AppSearchUserInstance instance) {
EXECUTOR.execute(() -> {
+ long totalLatencyStartMillis = SystemClock.elapsedRealtime();
+ OptimizeStats.Builder builder = new OptimizeStats.Builder();
try {
- instance.getAppSearchImpl().checkForOptimize();
+ instance.getAppSearchImpl().checkForOptimize(builder);
} catch (AppSearchException e) {
Log.w(TAG, "Error occurred when check for optimize", e);
+ } finally {
+ OptimizeStats oStats = builder
+ .setTotalLatencyMillis(
+ (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis))
+ .build();
+ if (oStats.getOriginalDocumentCount() > 0) {
+ // see if optimize has been run by checking originalDocumentCount
+ instance.getLogger().logStats(oStats);
+ }
}
});
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
index 830e76c..15916cc 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
@@ -55,6 +55,7 @@
import com.android.server.appsearch.external.localstorage.converter.SetSchemaResponseToProtoConverter;
import com.android.server.appsearch.external.localstorage.converter.TypePropertyPathToProtoConverter;
import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
+import com.android.server.appsearch.external.localstorage.stats.OptimizeStats;
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
import com.android.server.appsearch.external.localstorage.stats.SearchStats;
@@ -145,6 +146,9 @@
public final class AppSearchImpl implements Closeable {
private static final String TAG = "AppSearchImpl";
+ /** A value 0 means that there're no more pages in the search results. */
+ private static final long EMPTY_PAGE_TOKEN = 0;
+
@VisibleForTesting static final int CHECK_OPTIMIZE_INTERVAL = 100;
private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();
@@ -284,6 +288,9 @@
// Log the time it took to read the data that goes into the cache maps
if (initStatsBuilder != null) {
+ // In case there is some error for getAllNamespaces, we can still
+ // set the latency for preparation.
+ // If there is no error, the value will be overridden by the actual one later.
initStatsBuilder
.setStatusCode(
statusProtoToResultCode(
@@ -1135,6 +1142,16 @@
searchResultProto.getResultsCount(),
searchResultProto);
checkSuccess(searchResultProto.getStatus());
+ if (nextPageToken != EMPTY_PAGE_TOKEN
+ && searchResultProto.getNextPageToken() == EMPTY_PAGE_TOKEN) {
+ // At this point, we're guaranteed that this nextPageToken exists for this package,
+ // otherwise checkNextPageToken would've thrown an exception.
+ // Since the new token is 0, this is the last page. We should remove the old token
+ // from our cache since it no longer refers to this query.
+ synchronized (mNextPageTokensLocked) {
+ mNextPageTokensLocked.get(packageName).remove(nextPageToken);
+ }
+ }
return rewriteSearchResultProto(searchResultProto, mSchemaMapLocked);
} finally {
mReadWriteLock.readLock().unlock();
@@ -1326,8 +1343,7 @@
deleteResultProto.getStatus(), StatusProto.Code.OK, StatusProto.Code.NOT_FOUND);
// Update derived maps
- int numDocumentsDeleted =
- deleteResultProto.getDeleteStats().getNumDocumentsDeleted();
+ int numDocumentsDeleted = deleteResultProto.getDeleteStats().getNumDocumentsDeleted();
updateDocumentCountAfterRemovalLocked(packageName, numDocumentsDeleted);
} finally {
mReadWriteLock.writeLock().unlock();
@@ -2056,6 +2072,10 @@
}
private void addNextPageToken(String packageName, long nextPageToken) {
+ if (nextPageToken == EMPTY_PAGE_TOKEN) {
+ // There is no more pages. No need to add it.
+ return;
+ }
synchronized (mNextPageTokensLocked) {
Set<Long> tokens = mNextPageTokensLocked.get(packageName);
if (tokens == null) {
@@ -2068,6 +2088,11 @@
private void checkNextPageToken(String packageName, long nextPageToken)
throws AppSearchException {
+ if (nextPageToken == EMPTY_PAGE_TOKEN) {
+ // Swallow the check for empty page token, token = 0 means there is no more page and it
+ // won't return anything from Icing.
+ return;
+ }
synchronized (mNextPageTokensLocked) {
Set<Long> nextPageTokens = mNextPageTokensLocked.get(packageName);
if (nextPageTokens == null || !nextPageTokens.contains(nextPageToken)) {
@@ -2162,12 +2187,13 @@
* #CHECK_OPTIMIZE_INTERVAL}, {@link IcingSearchEngine#getOptimizeInfo()} will be triggered
* and the counter will be reset.
*/
- public void checkForOptimize(int mutationSize) throws AppSearchException {
+ public void checkForOptimize(int mutationSize, @Nullable OptimizeStats.Builder builder)
+ throws AppSearchException {
mReadWriteLock.writeLock().lock();
try {
mOptimizeIntervalCountLocked += mutationSize;
if (mOptimizeIntervalCountLocked >= CHECK_OPTIMIZE_INTERVAL) {
- checkForOptimize();
+ checkForOptimize(builder);
}
} finally {
mReadWriteLock.writeLock().unlock();
@@ -2183,14 +2209,15 @@
* <p>{@link IcingSearchEngine#optimize()} should be called only if {@link
* OptimizeStrategy#shouldOptimize(GetOptimizeInfoResultProto)} return true.
*/
- public void checkForOptimize() throws AppSearchException {
+ public void checkForOptimize(@Nullable OptimizeStats.Builder builder)
+ throws AppSearchException {
mReadWriteLock.writeLock().lock();
try {
GetOptimizeInfoResultProto optimizeInfo = getOptimizeInfoResultLocked();
checkSuccess(optimizeInfo.getStatus());
mOptimizeIntervalCountLocked = 0;
if (mOptimizeStrategy.shouldOptimize(optimizeInfo)) {
- optimize();
+ optimize(builder);
}
} finally {
mReadWriteLock.writeLock().unlock();
@@ -2201,13 +2228,18 @@
}
/** Triggers {@link IcingSearchEngine#optimize()} directly. */
- public void optimize() throws AppSearchException {
+ public void optimize(@Nullable OptimizeStats.Builder builder) throws AppSearchException {
mReadWriteLock.writeLock().lock();
try {
mLogUtil.piiTrace("optimize, request");
OptimizeResultProto optimizeResultProto = mIcingSearchEngineLocked.optimize();
mLogUtil.piiTrace(
"optimize, response", optimizeResultProto.getStatus(), optimizeResultProto);
+ if (builder != null) {
+ builder.setStatusCode(statusProtoToResultCode(optimizeResultProto.getStatus()));
+ AppSearchLoggerHelper.copyNativeStats(
+ optimizeResultProto.getOptimizeStats(), builder);
+ }
checkSuccess(optimizeResultProto.getStatus());
} finally {
mReadWriteLock.writeLock().unlock();
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java
index d92f4f0..98cedc7 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java
@@ -17,10 +17,10 @@
package com.android.server.appsearch.external.localstorage;
import android.annotation.NonNull;
-import android.app.appsearch.exceptions.AppSearchException;
import com.android.server.appsearch.external.localstorage.stats.CallStats;
import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
+import com.android.server.appsearch.external.localstorage.stats.OptimizeStats;
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
import com.android.server.appsearch.external.localstorage.stats.SearchStats;
@@ -37,19 +37,22 @@
*/
public interface AppSearchLogger {
/** Logs {@link CallStats} */
- void logStats(@NonNull CallStats stats) throws AppSearchException;
+ void logStats(@NonNull CallStats stats);
/** Logs {@link PutDocumentStats} */
- void logStats(@NonNull PutDocumentStats stats) throws AppSearchException;
+ void logStats(@NonNull PutDocumentStats stats);
/** Logs {@link InitializeStats} */
- void logStats(@NonNull InitializeStats stats) throws AppSearchException;
+ void logStats(@NonNull InitializeStats stats);
/** Logs {@link SearchStats} */
- void logStats(@NonNull SearchStats stats) throws AppSearchException;
+ void logStats(@NonNull SearchStats stats);
/** Logs {@link RemoveStats} */
- void logStats(@NonNull RemoveStats stats) throws AppSearchException;
+ void logStats(@NonNull RemoveStats stats);
+
+ /** Logs {@link OptimizeStats} */
+ void logStats(@NonNull OptimizeStats stats);
// TODO(b/173532925) Add remaining logStats once we add all the stats.
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java
index aa9200a..cd653e5 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java
@@ -19,12 +19,14 @@
import android.annotation.NonNull;
import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
+import com.android.server.appsearch.external.localstorage.stats.OptimizeStats;
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
import com.android.server.appsearch.external.localstorage.stats.SearchStats;
import com.google.android.icing.proto.DeleteStatsProto;
import com.google.android.icing.proto.InitializeStatsProto;
+import com.google.android.icing.proto.OptimizeStatsProto;
import com.google.android.icing.proto.PutDocumentStatsProto;
import com.google.android.icing.proto.QueryStatsProto;
@@ -92,8 +94,8 @@
.setSchemaTypeCount(fromNativeStats.getNumSchemaTypes());
}
- /*
- * Copy native Query stats to buiilder.
+ /**
+ * Copies native Query stats to builder.
*
* @param fromNativeStats Stats copied from.
* @param toStatsBuilder Stats copied to.
@@ -122,8 +124,8 @@
fromNativeStats.getDocumentRetrievalLatencyMs());
}
- /*
- * Copy native Query stats to buiilder.
+ /**
+ * Copies native Delete stats to builder.
*
* @param fromNativeStats Stats copied from.
* @param toStatsBuilder Stats copied to.
@@ -138,4 +140,28 @@
.setDeleteType(fromNativeStats.getDeleteType().getNumber())
.setDeletedDocumentCount(fromNativeStats.getNumDocumentsDeleted());
}
+
+ /**
+ * Copies native {@link OptimizeStatsProto} to builder.
+ *
+ * @param fromNativeStats Stats copied from.
+ * @param toStatsBuilder Stats copied to.
+ */
+ static void copyNativeStats(
+ @NonNull OptimizeStatsProto fromNativeStats,
+ @NonNull OptimizeStats.Builder toStatsBuilder) {
+ Objects.requireNonNull(fromNativeStats);
+ Objects.requireNonNull(toStatsBuilder);
+ toStatsBuilder
+ .setNativeLatencyMillis(fromNativeStats.getLatencyMs())
+ .setDocumentStoreOptimizeLatencyMillis(
+ fromNativeStats.getDocumentStoreOptimizeLatencyMs())
+ .setIndexRestorationLatencyMillis(fromNativeStats.getIndexRestorationLatencyMs())
+ .setOriginalDocumentCount(fromNativeStats.getNumOriginalDocuments())
+ .setDeletedDocumentCount(fromNativeStats.getNumDeletedDocuments())
+ .setExpiredDocumentCount(fromNativeStats.getNumExpiredDocuments())
+ .setStorageSizeBeforeBytes(fromNativeStats.getStorageSizeBefore())
+ .setStorageSizeAfterBytes(fromNativeStats.getStorageSizeAfter())
+ .setTimeSinceLastOptimizeMillis(fromNativeStats.getTimeSinceLastOptimizeMs());
+ }
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/OptimizeStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/OptimizeStats.java
new file mode 100644
index 0000000..83bd50f
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/OptimizeStats.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appsearch.external.localstorage.stats;
+
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchResult;
+
+import java.util.Objects;
+
+/**
+ * Class holds detailed stats for Optimize.
+ *
+ * @hide
+ */
+public final class OptimizeStats {
+ /**
+ * The status code returned by {@link AppSearchResult#getResultCode()} for the call or internal
+ * state.
+ */
+ @AppSearchResult.ResultCode private final int mStatusCode;
+
+ private final int mTotalLatencyMillis;
+ private final int mNativeLatencyMillis;
+
+ // Time used to optimize the document store in millis.
+ private final int mNativeDocumentStoreOptimizeLatencyMillis;
+
+ // Time used to restore the index in millis.
+ private final int mNativeIndexRestorationLatencyMillis;
+
+ // Number of documents before the optimization.
+ private final int mNativeOriginalDocumentCount;
+
+ // Number of documents deleted during the optimization.
+ private final int mNativeDeletedDocumentCount;
+
+ // Number of documents expired during the optimization.
+ private final int mNativeExpiredDocumentCount;
+
+ // Size of storage in bytes before the optimization.
+ private final long mNativeStorageSizeBeforeBytes;
+
+ // Size of storage in bytes after the optimization.
+ private final long mNativeStorageSizeAfterBytes;
+
+ // The amount of time in millis since the last optimization ran calculated using wall clock time
+ private final long mNativeTimeSinceLastOptimizeMillis;
+
+ OptimizeStats(@NonNull Builder builder) {
+ Objects.requireNonNull(builder);
+ mStatusCode = builder.mStatusCode;
+ mTotalLatencyMillis = builder.mTotalLatencyMillis;
+ mNativeLatencyMillis = builder.mNativeLatencyMillis;
+ mNativeDocumentStoreOptimizeLatencyMillis =
+ builder.mNativeDocumentStoreOptimizeLatencyMillis;
+ mNativeIndexRestorationLatencyMillis = builder.mNativeIndexRestorationLatencyMillis;
+ mNativeOriginalDocumentCount = builder.mNativeOriginalDocumentCount;
+ mNativeDeletedDocumentCount = builder.mNativeDeletedDocumentCount;
+ mNativeExpiredDocumentCount = builder.mNativeExpiredDocumentCount;
+ mNativeStorageSizeBeforeBytes = builder.mNativeStorageSizeBeforeBytes;
+ mNativeStorageSizeAfterBytes = builder.mNativeStorageSizeAfterBytes;
+ mNativeTimeSinceLastOptimizeMillis = builder.mNativeTimeSinceLastOptimizeMillis;
+ }
+
+ /** Returns status code for this optimization. */
+ @AppSearchResult.ResultCode
+ public int getStatusCode() {
+ return mStatusCode;
+ }
+
+ /** Returns total latency of this optimization in millis. */
+ public int getTotalLatencyMillis() {
+ return mTotalLatencyMillis;
+ }
+
+ /** Returns how much time in millis spent in the native code. */
+ public int getNativeLatencyMillis() {
+ return mNativeLatencyMillis;
+ }
+
+ /** Returns time used to optimize the document store in millis. */
+ public int getDocumentStoreOptimizeLatencyMillis() {
+ return mNativeDocumentStoreOptimizeLatencyMillis;
+ }
+
+ /** Returns time used to restore the index in millis. */
+ public int getIndexRestorationLatencyMillis() {
+ return mNativeIndexRestorationLatencyMillis;
+ }
+
+ /** Returns number of documents before the optimization. */
+ public int getOriginalDocumentCount() {
+ return mNativeOriginalDocumentCount;
+ }
+
+ /** Returns number of documents deleted during the optimization. */
+ public int getDeletedDocumentCount() {
+ return mNativeDeletedDocumentCount;
+ }
+
+ /** Returns number of documents expired during the optimization. */
+ public int getExpiredDocumentCount() {
+ return mNativeExpiredDocumentCount;
+ }
+
+ /** Returns size of storage in bytes before the optimization. */
+ public long getStorageSizeBeforeBytes() {
+ return mNativeStorageSizeBeforeBytes;
+ }
+
+ /** Returns size of storage in bytes after the optimization. */
+ public long getStorageSizeAfterBytes() {
+ return mNativeStorageSizeAfterBytes;
+ }
+
+ /**
+ * Returns the amount of time in millis since the last optimization ran calculated using wall
+ * clock time.
+ */
+ public long getTimeSinceLastOptimizeMillis() {
+ return mNativeTimeSinceLastOptimizeMillis;
+ }
+
+ /** Builder for {@link RemoveStats}. */
+ public static class Builder {
+ /**
+ * The status code returned by {@link AppSearchResult#getResultCode()} for the call or
+ * internal state.
+ */
+ @AppSearchResult.ResultCode int mStatusCode;
+
+ int mTotalLatencyMillis;
+ int mNativeLatencyMillis;
+ int mNativeDocumentStoreOptimizeLatencyMillis;
+ int mNativeIndexRestorationLatencyMillis;
+ int mNativeOriginalDocumentCount;
+ int mNativeDeletedDocumentCount;
+ int mNativeExpiredDocumentCount;
+ long mNativeStorageSizeBeforeBytes;
+ long mNativeStorageSizeAfterBytes;
+ long mNativeTimeSinceLastOptimizeMillis;
+
+ /** Sets the status code. */
+ @NonNull
+ public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) {
+ mStatusCode = statusCode;
+ return this;
+ }
+
+ /** Sets total latency in millis. */
+ @NonNull
+ public Builder setTotalLatencyMillis(int totalLatencyMillis) {
+ mTotalLatencyMillis = totalLatencyMillis;
+ return this;
+ }
+
+ /** Sets native latency in millis. */
+ @NonNull
+ public Builder setNativeLatencyMillis(int nativeLatencyMillis) {
+ mNativeLatencyMillis = nativeLatencyMillis;
+ return this;
+ }
+
+ /** Sets time used to optimize the document store. */
+ @NonNull
+ public Builder setDocumentStoreOptimizeLatencyMillis(
+ int documentStoreOptimizeLatencyMillis) {
+ mNativeDocumentStoreOptimizeLatencyMillis = documentStoreOptimizeLatencyMillis;
+ return this;
+ }
+
+ /** Sets time used to restore the index. */
+ @NonNull
+ public Builder setIndexRestorationLatencyMillis(int indexRestorationLatencyMillis) {
+ mNativeIndexRestorationLatencyMillis = indexRestorationLatencyMillis;
+ return this;
+ }
+
+ /** Sets number of documents before the optimization. */
+ @NonNull
+ public Builder setOriginalDocumentCount(int originalDocumentCount) {
+ mNativeOriginalDocumentCount = originalDocumentCount;
+ return this;
+ }
+
+ /** Sets number of documents deleted during the optimization. */
+ @NonNull
+ public Builder setDeletedDocumentCount(int deletedDocumentCount) {
+ mNativeDeletedDocumentCount = deletedDocumentCount;
+ return this;
+ }
+
+ /** Sets number of documents expired during the optimization. */
+ @NonNull
+ public Builder setExpiredDocumentCount(int expiredDocumentCount) {
+ mNativeExpiredDocumentCount = expiredDocumentCount;
+ return this;
+ }
+
+ /** Sets Storage size in bytes before optimization. */
+ @NonNull
+ public Builder setStorageSizeBeforeBytes(long storageSizeBeforeBytes) {
+ mNativeStorageSizeBeforeBytes = storageSizeBeforeBytes;
+ return this;
+ }
+
+ /** Sets storage size in bytes after optimization. */
+ @NonNull
+ public Builder setStorageSizeAfterBytes(long storageSizeAfterBytes) {
+ mNativeStorageSizeAfterBytes = storageSizeAfterBytes;
+ return this;
+ }
+
+ /**
+ * Sets the amount the time since the last optimize ran calculated using wall clock time.
+ */
+ @NonNull
+ public Builder setTimeSinceLastOptimizeMillis(long timeSinceLastOptimizeMillis) {
+ mNativeTimeSinceLastOptimizeMillis = timeSinceLastOptimizeMillis;
+ return this;
+ }
+
+ /** Creates a {@link OptimizeStats}. */
+ @NonNull
+ public OptimizeStats build() {
+ return new OptimizeStats(/* builder= */ this);
+ }
+ }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
index 2cbce10..fdf6a00 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
@@ -32,6 +32,7 @@
import com.android.server.appsearch.external.localstorage.AppSearchLogger;
import com.android.server.appsearch.external.localstorage.stats.CallStats;
import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
+import com.android.server.appsearch.external.localstorage.stats.OptimizeStats;
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
import com.android.server.appsearch.external.localstorage.stats.SearchStats;
@@ -145,7 +146,7 @@
}
@Override
- public void logStats(@NonNull InitializeStats stats) throws AppSearchException {
+ public void logStats(@NonNull InitializeStats stats) {
Objects.requireNonNull(stats);
synchronized (mLock) {
if (shouldLogForTypeLocked(CallStats.CALL_TYPE_INITIALIZE)) {
@@ -155,7 +156,7 @@
}
@Override
- public void logStats(@NonNull SearchStats stats) throws AppSearchException {
+ public void logStats(@NonNull SearchStats stats) {
Objects.requireNonNull(stats);
synchronized (mLock) {
if (shouldLogForTypeLocked(CallStats.CALL_TYPE_SEARCH)) {
@@ -165,10 +166,20 @@
}
@Override
- public void logStats(@NonNull RemoveStats stats) throws AppSearchException {
+ public void logStats(@NonNull RemoveStats stats) {
// TODO(b/173532925): Log stats
}
+ @Override
+ public void logStats(@NonNull OptimizeStats stats) {
+ Objects.requireNonNull(stats);
+ synchronized (mLock) {
+ if (shouldLogForTypeLocked(CallStats.CALL_TYPE_OPTIMIZE)) {
+ logStatsImplLocked(stats);
+ }
+ }
+ }
+
/**
* Removes cached UID for package.
*
@@ -326,6 +337,27 @@
stats.getResetStatusCode());
}
+ @GuardedBy("mLock")
+ private void logStatsImplLocked(@NonNull OptimizeStats stats) {
+ mLastPushTimeMillisLocked = SystemClock.elapsedRealtime();
+ ExtraStats extraStats = createExtraStatsLocked(/*packageName=*/ null,
+ CallStats.CALL_TYPE_OPTIMIZE);
+ AppSearchStatsLog.write(AppSearchStatsLog.APP_SEARCH_OPTIMIZE_STATS_REPORTED,
+ extraStats.mSamplingInterval,
+ extraStats.mSkippedSampleCount,
+ stats.getStatusCode(),
+ stats.getTotalLatencyMillis(),
+ stats.getNativeLatencyMillis(),
+ stats.getDocumentStoreOptimizeLatencyMillis(),
+ stats.getIndexRestorationLatencyMillis(),
+ stats.getOriginalDocumentCount(),
+ stats.getDeletedDocumentCount(),
+ stats.getExpiredDocumentCount(),
+ stats.getStorageSizeBeforeBytes(),
+ stats.getStorageSizeAfterBytes(),
+ stats.getTimeSinceLastOptimizeMillis());
+ }
+
/**
* Calculate the hash code as an integer by returning the last four bytes of its MD5.
*
@@ -464,15 +496,19 @@
return mConfig.getCachedSamplingIntervalForBatchCallStats();
case CallStats.CALL_TYPE_PUT_DOCUMENT:
return mConfig.getCachedSamplingIntervalForPutDocumentStats();
- case CallStats.CALL_TYPE_UNKNOWN:
case CallStats.CALL_TYPE_INITIALIZE:
+ return mConfig.getCachedSamplingIntervalForInitializeStats();
+ case CallStats.CALL_TYPE_SEARCH:
+ return mConfig.getCachedSamplingIntervalForSearchStats();
+ case CallStats.CALL_TYPE_GLOBAL_SEARCH:
+ return mConfig.getCachedSamplingIntervalForGlobalSearchStats();
+ case CallStats.CALL_TYPE_OPTIMIZE:
+ return mConfig.getCachedSamplingIntervalForOptimizeStats();
+ case CallStats.CALL_TYPE_UNKNOWN:
case CallStats.CALL_TYPE_SET_SCHEMA:
case CallStats.CALL_TYPE_GET_DOCUMENT:
case CallStats.CALL_TYPE_REMOVE_DOCUMENT_BY_ID:
- case CallStats.CALL_TYPE_SEARCH:
- case CallStats.CALL_TYPE_OPTIMIZE:
case CallStats.CALL_TYPE_FLUSH:
- case CallStats.CALL_TYPE_GLOBAL_SEARCH:
case CallStats.CALL_TYPE_REMOVE_DOCUMENT_BY_SEARCH:
// TODO(b/173532925) Some of them above will have dedicated sampling ratio config
default:
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index 6555107..a81d7d80 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-c7387d9b58726a23a0608a9365fb3a1b57b7b8a1
+Ie04f1ecc033faae8085afcb51eb9e40a298998d5
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 6c001f3..26c83ee 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -112,6 +112,8 @@
srcs: [
"android/os/Temperature.aidl",
"android/os/CoolingDevice.aidl",
+ "android/os/IHintManager.aidl",
+ "android/os/IHintSession.aidl",
"android/os/IThermalEventListener.aidl",
"android/os/IThermalStatusListener.aidl",
"android/os/IThermalService.aidl",
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 871d48b..32ea41b 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -158,7 +158,6 @@
import android.os.IBinder;
import android.os.IDumpstate;
import android.os.IHardwarePropertiesManager;
-import android.os.IHintManager;
import android.os.IPowerManager;
import android.os.IRecoverySystem;
import android.os.ISystemUpdateManager;
@@ -600,10 +599,7 @@
@Override
public PerformanceHintManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
- IBinder hintBinder = ServiceManager.getServiceOrThrow(
- Context.PERFORMANCE_HINT_SERVICE);
- IHintManager hintService = IHintManager.Stub.asInterface(hintBinder);
- return new PerformanceHintManager(hintService);
+ return PerformanceHintManager.create();
}});
registerService(Context.RECOVERY_SERVICE, RecoverySystem.class,
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index f5ab2ab..8398be1 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -3518,22 +3518,22 @@
}
/**
- * Determines whether a String Bluetooth address, such as "00:43:A8:23:10:F0"
+ * Determines whether a String Bluetooth address, such as "F0:43:A8:23:10:00"
* is a RANDOM STATIC address.
*
- * RANDOM STATIC: (addr & 0b11) == 0b11
- * RANDOM RESOLVABLE: (addr & 0b11) == 0b10
- * RANDOM non-RESOLVABLE: (addr & 0b11) == 0b00
+ * RANDOM STATIC: (addr & 0xC0) == 0xC0
+ * RANDOM RESOLVABLE: (addr & 0xC0) == 0x40
+ * RANDOM non-RESOLVABLE: (addr & 0xC0) == 0x00
*
* @param address Bluetooth address as string
- * @return true if the 2 Least Significant Bits of the address equals 0b11.
+ * @return true if the 2 Most Significant Bits of the address equals 0xC0.
*
* @hide
*/
public static boolean isAddressRandomStatic(@NonNull String address) {
requireNonNull(address);
return checkBluetoothAddress(address)
- && (Integer.parseInt(address.split(":")[5], 16) & 0b11) == 0b11;
+ && (Integer.parseInt(address.split(":")[0], 16) & 0xC0) == 0xC0;
}
/** {@hide} */
diff --git a/core/java/android/os/PerformanceHintManager.java b/core/java/android/os/PerformanceHintManager.java
index 6791844..a75b5ef 100644
--- a/core/java/android/os/PerformanceHintManager.java
+++ b/core/java/android/os/PerformanceHintManager.java
@@ -24,24 +24,23 @@
import com.android.internal.util.Preconditions;
import java.io.Closeable;
-import java.util.ArrayList;
/** The PerformanceHintManager allows apps to send performance hint to system. */
@SystemService(Context.PERFORMANCE_HINT_SERVICE)
public final class PerformanceHintManager {
- private static final String TAG = "PerformanceHintManager";
- private final IHintManager mService;
- // HAL preferred update rate
- private final long mPreferredRate;
+ private final long mNativeManagerPtr;
/** @hide */
- public PerformanceHintManager(IHintManager service) {
- mService = service;
- try {
- mPreferredRate = mService.getHintSessionPreferredRate();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ public static PerformanceHintManager create() throws ServiceManager.ServiceNotFoundException {
+ long nativeManagerPtr = nativeAcquireManager();
+ if (nativeManagerPtr == 0) {
+ throw new ServiceManager.ServiceNotFoundException(Context.PERFORMANCE_HINT_SERVICE);
}
+ return new PerformanceHintManager(nativeManagerPtr);
+ }
+
+ private PerformanceHintManager(long nativeManagerPtr) {
+ mNativeManagerPtr = nativeManagerPtr;
}
/**
@@ -57,16 +56,13 @@
*/
@Nullable
public Session createHintSession(@NonNull int[] tids, long initialTargetWorkDurationNanos) {
- try {
- IBinder token = new Binder();
- IHintSession session = mService.createHintSession(token, tids,
- initialTargetWorkDurationNanos);
- if (session == null) return null;
- return new Session(session, sNanoClock, mPreferredRate,
- initialTargetWorkDurationNanos);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ Preconditions.checkNotNull(tids, "tids cannot be null");
+ Preconditions.checkArgumentPositive(initialTargetWorkDurationNanos,
+ "the hint target duration should be positive.");
+ long nativeSessionPtr = nativeCreateSession(mNativeManagerPtr, tids,
+ initialTargetWorkDurationNanos);
+ if (nativeSessionPtr == 0) return null;
+ return new Session(nativeSessionPtr);
}
/**
@@ -75,7 +71,7 @@
* @return the preferred update rate supported by device software.
*/
public long getPreferredUpdateRateNanos() {
- return mPreferredRate;
+ return nativeGetPreferredUpdateRateNanos(mNativeManagerPtr);
}
/**
@@ -101,28 +97,21 @@
* <p>All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.</p>
*/
public static class Session implements Closeable {
- private final IHintSession mSession;
- private final NanoClock mElapsedRealtimeClock;
- // Target duration for choosing update rate
- private long mTargetDurationInNanos;
- // HAL preferred update rate
- private long mPreferredRate;
- // Last update timestamp
- private long mLastUpdateTimeStamp = -1L;
- // Cached samples
- private final ArrayList<Long> mActualDurationNanos;
- private final ArrayList<Long> mTimeStampNanos;
+ private long mNativeSessionPtr;
/** @hide */
- public Session(IHintSession session, NanoClock elapsedRealtimeClock, long preferredRate,
- long durationNanos) {
- mSession = session;
- mElapsedRealtimeClock = elapsedRealtimeClock;
- mTargetDurationInNanos = durationNanos;
- mPreferredRate = preferredRate;
- mActualDurationNanos = new ArrayList<Long>();
- mTimeStampNanos = new ArrayList<Long>();
- mLastUpdateTimeStamp = mElapsedRealtimeClock.nanos();
+ public Session(long nativeSessionPtr) {
+ mNativeSessionPtr = nativeSessionPtr;
+ }
+
+ /** @hide */
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } finally {
+ super.finalize();
+ }
}
/**
@@ -133,19 +122,7 @@
public void updateTargetWorkDuration(long targetDurationNanos) {
Preconditions.checkArgumentPositive(targetDurationNanos, "the hint target duration"
+ " should be positive.");
- try {
- mSession.updateTargetWorkDuration(targetDurationNanos);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- mTargetDurationInNanos = targetDurationNanos;
- /**
- * Most of the workload is target_duration dependent, so now clear the cached samples
- * as they are most likely obsolete.
- */
- mActualDurationNanos.clear();
- mTimeStampNanos.clear();
- mLastUpdateTimeStamp = mElapsedRealtimeClock.nanos();
+ nativeUpdateTargetWorkDuration(mNativeSessionPtr, targetDurationNanos);
}
/**
@@ -161,38 +138,7 @@
public void reportActualWorkDuration(long actualDurationNanos) {
Preconditions.checkArgumentPositive(actualDurationNanos, "the actual duration should"
+ " be positive.");
- final long now = mElapsedRealtimeClock.nanos();
- mActualDurationNanos.add(actualDurationNanos);
- mTimeStampNanos.add(now);
-
- /**
- * Use current sample to determine the rate limit. We can pick a shorter rate limit
- * if any sample underperformed, however, it could be the lower level system is slow
- * to react. So here we explicitly choose the rate limit with the latest sample.
- */
- long rateLimit =
- actualDurationNanos > mTargetDurationInNanos ? mPreferredRate
- : 10 * mPreferredRate;
-
- if (now - mLastUpdateTimeStamp <= rateLimit) {
- return;
- }
- Preconditions.checkState(mActualDurationNanos.size() == mTimeStampNanos.size());
- final int size = mActualDurationNanos.size();
- long[] actualDurationArray = new long[size];
- long[] timeStampArray = new long[size];
- for (int i = 0; i < size; i++) {
- actualDurationArray[i] = mActualDurationNanos.get(i);
- timeStampArray[i] = mTimeStampNanos.get(i);
- }
- try {
- mSession.reportActualWorkDuration(actualDurationArray, timeStampArray);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- mActualDurationNanos.clear();
- mTimeStampNanos.clear();
- mLastUpdateTimeStamp = now;
+ nativeReportActualWorkDuration(mNativeSessionPtr, actualDurationNanos);
}
/**
@@ -201,26 +147,20 @@
* <p>Once called, you should not call anything else on this object.</p>
*/
public void close() {
- try {
- mSession.close();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ if (mNativeSessionPtr != 0) {
+ nativeCloseSession(mNativeSessionPtr);
+ mNativeSessionPtr = 0;
}
}
}
- /**
- * The interface is to make the FakeClock for testing.
- * @hide
- */
- public interface NanoClock {
- /** Gets the current nanosecond instant of the clock. */
- long nanos();
- }
-
- private static final NanoClock sNanoClock = new NanoClock() {
- public long nanos() {
- return SystemClock.elapsedRealtimeNanos();
- }
- };
+ private static native long nativeAcquireManager();
+ private static native long nativeGetPreferredUpdateRateNanos(long nativeManagerPtr);
+ private static native long nativeCreateSession(long nativeManagerPtr,
+ int[] tids, long initialTargetWorkDurationNanos);
+ private static native void nativeUpdateTargetWorkDuration(long nativeSessionPtr,
+ long targetDurationNanos);
+ private static native void nativeReportActualWorkDuration(long nativeSessionPtr,
+ long actualDurationNanos);
+ private static native void nativeCloseSession(long nativeSessionPtr);
}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index a8dcbaf..6cbace4 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -241,7 +241,11 @@
private final ArraySet<String> mRollbackWhitelistedPackages = new ArraySet<>();
private final ArraySet<String> mWhitelistedStagedInstallers = new ArraySet<>();
- private final ArraySet<String> mAllowedVendorApexes = new ArraySet<>();
+ // A map from package name of vendor APEXes that can be updated to an installer package name
+ // allowed to install updates for it.
+ private final ArrayMap<String, String> mAllowedVendorApexes = new ArrayMap<>();
+
+ private String mModulesInstallerPackageName;
/**
* Map of system pre-defined, uniquely named actors; keys are namespace,
@@ -412,10 +416,14 @@
return mWhitelistedStagedInstallers;
}
- public Set<String> getAllowedVendorApexes() {
+ public Map<String, String> getAllowedVendorApexes() {
return mAllowedVendorApexes;
}
+ public String getModulesInstallerPackageName() {
+ return mModulesInstallerPackageName;
+ }
+
public ArraySet<String> getAppDataIsolationWhitelistedApps() {
return mAppDataIsolationWhitelistedApps;
}
@@ -1210,12 +1218,21 @@
case "whitelisted-staged-installer": {
if (allowAppConfigs) {
String pkgname = parser.getAttributeValue(null, "package");
+ boolean isModulesInstaller = XmlUtils.readBooleanAttribute(
+ parser, "isModulesInstaller", false);
if (pkgname == null) {
Slog.w(TAG, "<" + name + "> without package in " + permFile
+ " at " + parser.getPositionDescription());
} else {
mWhitelistedStagedInstallers.add(pkgname);
}
+ if (isModulesInstaller) {
+ if (mModulesInstallerPackageName != null) {
+ throw new IllegalStateException(
+ "Multiple modules installers");
+ }
+ mModulesInstallerPackageName = pkgname;
+ }
} else {
logNotAllowedInPartition(name, permFile, parser);
}
@@ -1224,11 +1241,18 @@
case "allowed-vendor-apex": {
if (allowVendorApex) {
String pkgName = parser.getAttributeValue(null, "package");
+ String installerPkgName = parser.getAttributeValue(
+ null, "installerPackage");
if (pkgName == null) {
Slog.w(TAG, "<" + name + "> without package in " + permFile
+ " at " + parser.getPositionDescription());
- } else {
- mAllowedVendorApexes.add(pkgName);
+ }
+ if (installerPkgName == null) {
+ Slog.w(TAG, "<" + name + "> without installerPackage in " + permFile
+ + " at " + parser.getPositionDescription());
+ }
+ if (pkgName != null && installerPkgName != null) {
+ mAllowedVendorApexes.put(pkgName, installerPkgName);
}
} else {
logNotAllowedInPartition(name, permFile, parser);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 91a19e0..6b9d375 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -146,6 +146,7 @@
"android_os_MemoryFile.cpp",
"android_os_MessageQueue.cpp",
"android_os_Parcel.cpp",
+ "android_os_PerformanceHintManager.cpp",
"android_os_SELinux.cpp",
"android_os_ServiceManager.cpp",
"android_os_SharedMemory.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 406ccde..2fd1e54 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -143,6 +143,7 @@
extern int register_android_os_ServiceManager(JNIEnv *env);
extern int register_android_os_MessageQueue(JNIEnv* env);
extern int register_android_os_Parcel(JNIEnv* env);
+extern int register_android_os_PerformanceHintManager(JNIEnv* env);
extern int register_android_os_SELinux(JNIEnv* env);
extern int register_android_os_VintfObject(JNIEnv *env);
extern int register_android_os_VintfRuntimeInfo(JNIEnv *env);
@@ -1518,6 +1519,7 @@
REG_JNI(register_android_os_SystemProperties),
REG_JNI(register_android_os_Binder),
REG_JNI(register_android_os_Parcel),
+ REG_JNI(register_android_os_PerformanceHintManager),
REG_JNI(register_android_os_HidlMemory),
REG_JNI(register_android_os_HidlSupport),
REG_JNI(register_android_os_HwBinder),
diff --git a/core/jni/android_os_PerformanceHintManager.cpp b/core/jni/android_os_PerformanceHintManager.cpp
new file mode 100644
index 0000000..d05a24f
--- /dev/null
+++ b/core/jni/android_os_PerformanceHintManager.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PerfHint-jni"
+
+#include "jni.h"
+
+#include <dlfcn.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <utils/Log.h>
+#include <vector>
+
+#include "core_jni_helpers.h"
+
+namespace android {
+
+namespace {
+
+struct APerformanceHintManager;
+struct APerformanceHintSession;
+
+typedef APerformanceHintManager* (*APH_getManager)();
+typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*,
+ size_t, int64_t);
+typedef int64_t (*APH_getPreferredUpdateRateNanos)(APerformanceHintManager* manager);
+typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
+typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
+typedef void (*APH_closeSession)(APerformanceHintSession* session);
+
+bool gAPerformanceHintBindingInitialized = false;
+APH_getManager gAPH_getManagerFn = nullptr;
+APH_createSession gAPH_createSessionFn = nullptr;
+APH_getPreferredUpdateRateNanos gAPH_getPreferredUpdateRateNanosFn = nullptr;
+APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
+APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
+APH_closeSession gAPH_closeSessionFn = nullptr;
+
+void ensureAPerformanceHintBindingInitialized() {
+ if (gAPerformanceHintBindingInitialized) return;
+
+ void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
+ LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!");
+
+ gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager");
+ LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr,
+ "Failed to find required symbol APerformanceHint_getManager!");
+
+ gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession");
+ LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
+ "Failed to find required symbol APerformanceHint_createSession!");
+
+ gAPH_getPreferredUpdateRateNanosFn =
+ (APH_getPreferredUpdateRateNanos)dlsym(handle_,
+ "APerformanceHint_getPreferredUpdateRateNanos");
+ LOG_ALWAYS_FATAL_IF(gAPH_getPreferredUpdateRateNanosFn == nullptr,
+ "Failed to find required symbol "
+ "APerformanceHint_getPreferredUpdateRateNanos!");
+
+ gAPH_updateTargetWorkDurationFn =
+ (APH_updateTargetWorkDuration)dlsym(handle_,
+ "APerformanceHint_updateTargetWorkDuration");
+ LOG_ALWAYS_FATAL_IF(gAPH_updateTargetWorkDurationFn == nullptr,
+ "Failed to find required symbol "
+ "APerformanceHint_updateTargetWorkDuration!");
+
+ gAPH_reportActualWorkDurationFn =
+ (APH_reportActualWorkDuration)dlsym(handle_,
+ "APerformanceHint_reportActualWorkDuration");
+ LOG_ALWAYS_FATAL_IF(gAPH_reportActualWorkDurationFn == nullptr,
+ "Failed to find required symbol "
+ "APerformanceHint_reportActualWorkDuration!");
+
+ gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession");
+ LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
+ "Failed to find required symbol APerformanceHint_closeSession!");
+
+ gAPerformanceHintBindingInitialized = true;
+}
+
+} // namespace
+
+static jlong nativeAcquireManager(JNIEnv* env, jclass clazz) {
+ ensureAPerformanceHintBindingInitialized();
+ return reinterpret_cast<jlong>(gAPH_getManagerFn());
+}
+
+static jlong nativeGetPreferredUpdateRateNanos(JNIEnv* env, jclass clazz, jlong nativeManagerPtr) {
+ ensureAPerformanceHintBindingInitialized();
+ return gAPH_getPreferredUpdateRateNanosFn(
+ reinterpret_cast<APerformanceHintManager*>(nativeManagerPtr));
+}
+
+static jlong nativeCreateSession(JNIEnv* env, jclass clazz, jlong nativeManagerPtr, jintArray tids,
+ jlong initialTargetWorkDurationNanos) {
+ ensureAPerformanceHintBindingInitialized();
+ if (tids == nullptr) return 0;
+ std::vector<int32_t> tidsVector;
+ ScopedIntArrayRO tidsArray(env, tids);
+ for (size_t i = 0; i < tidsArray.size(); ++i) {
+ tidsVector.push_back(static_cast<int32_t>(tidsArray[i]));
+ }
+ return reinterpret_cast<jlong>(
+ gAPH_createSessionFn(reinterpret_cast<APerformanceHintManager*>(nativeManagerPtr),
+ tidsVector.data(), tidsVector.size(),
+ initialTargetWorkDurationNanos));
+}
+
+static void nativeUpdateTargetWorkDuration(JNIEnv* env, jclass clazz, jlong nativeSessionPtr,
+ jlong targetDurationNanos) {
+ ensureAPerformanceHintBindingInitialized();
+ gAPH_updateTargetWorkDurationFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr),
+ targetDurationNanos);
+}
+
+static void nativeReportActualWorkDuration(JNIEnv* env, jclass clazz, jlong nativeSessionPtr,
+ jlong actualDurationNanos) {
+ ensureAPerformanceHintBindingInitialized();
+ gAPH_reportActualWorkDurationFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr),
+ actualDurationNanos);
+}
+
+static void nativeCloseSession(JNIEnv* env, jclass clazz, jlong nativeSessionPtr) {
+ ensureAPerformanceHintBindingInitialized();
+ gAPH_closeSessionFn(reinterpret_cast<APerformanceHintSession*>(nativeSessionPtr));
+}
+
+static const JNINativeMethod gPerformanceHintMethods[] = {
+ {"nativeAcquireManager", "()J", (void*)nativeAcquireManager},
+ {"nativeGetPreferredUpdateRateNanos", "(J)J", (void*)nativeGetPreferredUpdateRateNanos},
+ {"nativeCreateSession", "(J[IJ)J", (void*)nativeCreateSession},
+ {"nativeUpdateTargetWorkDuration", "(JJ)V", (void*)nativeUpdateTargetWorkDuration},
+ {"nativeReportActualWorkDuration", "(JJ)V", (void*)nativeReportActualWorkDuration},
+ {"nativeCloseSession", "(J)V", (void*)nativeCloseSession},
+};
+
+int register_android_os_PerformanceHintManager(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "android/os/PerformanceHintManager", gPerformanceHintMethods,
+ NELEM(gPerformanceHintMethods));
+}
+
+} // namespace android
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 6f17ea9..fb820cb 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -286,6 +286,7 @@
}
@Test
+ @FlakyTest(bugId = 194242735)
public void testHandleActivityConfigurationChanged_EnsureUpdatesProcessedInOrder()
throws Exception {
final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
diff --git a/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java b/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java
index d51004c..07e4333 100644
--- a/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java
@@ -16,6 +16,8 @@
package android.app.appsearch;
+import static android.app.appsearch.SearchSpec.TERM_MATCH_PREFIX;
+
import static com.google.common.truth.Truth.assertThat;
import static org.testng.Assert.expectThrows;
@@ -30,6 +32,8 @@
import org.junit.Before;
import org.junit.Test;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@@ -100,4 +104,127 @@
.isEqualTo(AppSearchResult.RESULT_INTERNAL_ERROR);
assertThat(appSearchException.getMessage()).startsWith("NullPointerException");
}
+
+ @Test
+ public void testGetEmptyNextPage() throws Exception {
+ // Set the schema.
+ CompletableFuture<AppSearchResult<SetSchemaResponse>> schemaFuture =
+ new CompletableFuture<>();
+ mSearchSession.setSchema(
+ new SetSchemaRequest.Builder()
+ .addSchemas(new AppSearchSchema.Builder("schema1").build())
+ .setForceOverride(true).build(),
+ mExecutor, mExecutor, schemaFuture::complete);
+ schemaFuture.get().getResultValue();
+
+ // Create a document and index it.
+ GenericDocument document1 = new GenericDocument.Builder<>("namespace", "id1",
+ "schema1").build();
+ CompletableFuture<AppSearchBatchResult<String, Void>> putDocumentsFuture =
+ new CompletableFuture<>();
+ mSearchSession.put(
+ new PutDocumentsRequest.Builder().addGenericDocuments(document1).build(),
+ mExecutor, new BatchResultCallback<String, Void>() {
+ @Override
+ public void onResult(AppSearchBatchResult<String, Void> result) {
+ putDocumentsFuture.complete(result);
+ }
+
+ @Override
+ public void onSystemError(Throwable throwable) {
+ putDocumentsFuture.completeExceptionally(throwable);
+ }
+ });
+ putDocumentsFuture.get();
+
+ // Search and get the first page.
+ SearchSpec searchSpec = new SearchSpec.Builder()
+ .setTermMatch(TERM_MATCH_PREFIX)
+ .setResultCountPerPage(1)
+ .build();
+ SearchResults searchResults = mSearchSession.search("", searchSpec);
+
+ CompletableFuture<AppSearchResult<List<SearchResult>>> getNextPageFuture =
+ new CompletableFuture<>();
+ searchResults.getNextPage(mExecutor, getNextPageFuture::complete);
+ List<SearchResult> results = getNextPageFuture.get().getResultValue();
+ assertThat(results).hasSize(1);
+ assertThat(results.get(0).getGenericDocument()).isEqualTo(document1);
+
+ // We get all documents, and it shouldn't fail if we keep calling getNextPage().
+ getNextPageFuture = new CompletableFuture<>();
+ searchResults.getNextPage(mExecutor, getNextPageFuture::complete);
+ results = getNextPageFuture.get().getResultValue();
+ assertThat(results).isEmpty();
+ }
+
+ @Test
+ public void testGetEmptyNextPage_multiPages() throws Exception {
+ // Set the schema.
+ CompletableFuture<AppSearchResult<SetSchemaResponse>> schemaFuture =
+ new CompletableFuture<>();
+ mSearchSession.setSchema(
+ new SetSchemaRequest.Builder()
+ .addSchemas(new AppSearchSchema.Builder("schema1").build())
+ .setForceOverride(true).build(),
+ mExecutor, mExecutor, schemaFuture::complete);
+ schemaFuture.get().getResultValue();
+
+ // Create a document and insert 3 package1 documents
+ GenericDocument document1 = new GenericDocument.Builder<>("namespace", "id1",
+ "schema1").build();
+ GenericDocument document2 = new GenericDocument.Builder<>("namespace", "id2",
+ "schema1").build();
+ GenericDocument document3 = new GenericDocument.Builder<>("namespace", "id3",
+ "schema1").build();
+ CompletableFuture<AppSearchBatchResult<String, Void>> putDocumentsFuture =
+ new CompletableFuture<>();
+ mSearchSession.put(
+ new PutDocumentsRequest.Builder()
+ .addGenericDocuments(document1, document2, document3).build(),
+ mExecutor, new BatchResultCallback<String, Void>() {
+ @Override
+ public void onResult(AppSearchBatchResult<String, Void> result) {
+ putDocumentsFuture.complete(result);
+ }
+
+ @Override
+ public void onSystemError(Throwable throwable) {
+ putDocumentsFuture.completeExceptionally(throwable);
+ }
+ });
+ putDocumentsFuture.get();
+
+ // Search for only 2 result per page
+ SearchSpec searchSpec = new SearchSpec.Builder()
+ .setTermMatch(TERM_MATCH_PREFIX)
+ .setResultCountPerPage(2)
+ .build();
+ SearchResults searchResults = mSearchSession.search("", searchSpec);
+
+ // Get the first page, it contains 2 results.
+ List<GenericDocument> outDocs = new ArrayList<>();
+ CompletableFuture<AppSearchResult<List<SearchResult>>> getNextPageFuture =
+ new CompletableFuture<>();
+ searchResults.getNextPage(mExecutor, getNextPageFuture::complete);
+ List<SearchResult> results = getNextPageFuture.get().getResultValue();
+ assertThat(results).hasSize(2);
+ outDocs.add(results.get(0).getGenericDocument());
+ outDocs.add(results.get(1).getGenericDocument());
+
+ // Get the second page, it contains only 1 result.
+ getNextPageFuture = new CompletableFuture<>();
+ searchResults.getNextPage(mExecutor, getNextPageFuture::complete);
+ results = getNextPageFuture.get().getResultValue();
+ assertThat(results).hasSize(1);
+ outDocs.add(results.get(0).getGenericDocument());
+
+ assertThat(outDocs).containsExactly(document1, document2, document3);
+
+ // We get all documents, and it shouldn't fail if we keep calling getNextPage().
+ getNextPageFuture = new CompletableFuture<>();
+ searchResults.getNextPage(mExecutor, getNextPageFuture::complete);
+ results = getNextPageFuture.get().getResultValue();
+ assertThat(results).isEmpty();
+ }
}
diff --git a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
index 7dea82d..69eb13f 100644
--- a/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
+++ b/core/tests/coretests/src/android/os/PerformanceHintManagerTest.java
@@ -22,12 +22,6 @@
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeNotNull;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
import android.os.PerformanceHintManager.Session;
@@ -120,92 +114,9 @@
}
@Test
- public void testRateLimitWithDurationFastEnough() throws Exception {
- FakeClock fakeClock = new FakeClock();
- Session s = new Session(mIHintSessionMock, fakeClock, RATE_1000, TARGET_166);
-
- reset(mIHintSessionMock);
- fakeClock.setNow(0);
- s.updateTargetWorkDuration(TARGET_166);
-
- s.reportActualWorkDuration(TARGET_166 - 1);
- s.reportActualWorkDuration(TARGET_166);
- // we should not see update as the rate should be 10X for over-perform case.
- verify(mIHintSessionMock, never()).reportActualWorkDuration(any(), any());
- fakeClock.incrementClock(10 * RATE_1000);
- s.reportActualWorkDuration(TARGET_166);
- verify(mIHintSessionMock, never()).reportActualWorkDuration(any(), any());
- fakeClock.incrementClock(1);
- s.reportActualWorkDuration(TARGET_166);
- // we should see update after rate limit
- verify(mIHintSessionMock, times(1)).reportActualWorkDuration(
- eq(new long[] {TARGET_166 - 1, TARGET_166, TARGET_166, TARGET_166}),
- eq(new long[] {0, 0, 10 * RATE_1000, 10 * RATE_1000 + 1}));
-
- reset(mIHintSessionMock);
- s.reportActualWorkDuration(TARGET_166);
- s.reportActualWorkDuration(TARGET_166 - 1);
- s.reportActualWorkDuration(TARGET_166 - 2);
- // we should not see update as the rate should be 10X for over-perform case.
- verify(mIHintSessionMock, never()).reportActualWorkDuration(any(), any());
- fakeClock.incrementClock(10 * RATE_1000 + 1);
- s.reportActualWorkDuration(TARGET_166);
- s.reportActualWorkDuration(TARGET_166 - 1);
- // we should see update now
- verify(mIHintSessionMock, times(1)).reportActualWorkDuration(
- eq(new long[] {TARGET_166, TARGET_166 - 1, TARGET_166 - 2, TARGET_166}),
- eq(new long[] {10 * RATE_1000 + 1, 10 * RATE_1000 + 1, 10 * RATE_1000 + 1,
- (10 * RATE_1000 + 1) * 2}));
- }
-
- @Test
- public void testRateLimitWithDurationTooSlow() throws Exception {
- FakeClock fakeClock = new FakeClock();
- Session s = new Session(mIHintSessionMock, fakeClock, RATE_1000, TARGET_166);
-
- reset(mIHintSessionMock);
- fakeClock.setNow(0);
- s.updateTargetWorkDuration(TARGET_166);
-
- verify(mIHintSessionMock, times(1)).updateTargetWorkDuration(eq(TARGET_166));
- // shouldn't update before rate limit
- s.reportActualWorkDuration(TARGET_166 + 1);
- verify(mIHintSessionMock, never()).reportActualWorkDuration(any(), any());
-
- // shouldn't update when the time is exactly at rate limit
- fakeClock.incrementClock(RATE_1000);
- s.reportActualWorkDuration(TARGET_166 + 1);
- verify(mIHintSessionMock, never()).reportActualWorkDuration(any(), any());
-
- // should be ready for sending hint
- fakeClock.incrementClock(1);
- s.reportActualWorkDuration(TARGET_166 + 1);
- verify(mIHintSessionMock, times(1)).reportActualWorkDuration(
- eq(new long[] {TARGET_166 + 1, TARGET_166 + 1, TARGET_166 + 1}),
- eq(new long[] {0 , RATE_1000, RATE_1000 + 1}));
- }
-
- @Test
public void testCloseHintSession() {
Session s = createSession();
assumeNotNull(s);
s.close();
}
-
- private static class FakeClock implements PerformanceHintManager.NanoClock {
- private long mCurrentTime = 0L;
-
- @Override
- public long nanos() {
- return mCurrentTime;
- }
-
- public void setNow(long nanos) {
- mCurrentTime = nanos;
- }
-
- public void incrementClock(long nanos) {
- mCurrentTime += nanos;
- }
- }
}
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index fe04f0d..c3b1cd74 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -28,7 +28,6 @@
import android.hardware.display.DisplayManager;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
-import android.os.PerformanceHintManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
@@ -856,36 +855,6 @@
callback.onPictureCaptured(picture);
}
- /** called by native */
- static PerformanceHintManager.Session createHintSession(int[] tids) {
- PerformanceHintManager performanceHintManager =
- ProcessInitializer.sInstance.getHintManager();
- if (performanceHintManager == null) {
- return null;
- }
- // Native code will always set a target duration before reporting actual durations.
- // So this is just a placeholder value that's never used.
- long targetDurationNanos = 16666667;
- return performanceHintManager.createHintSession(tids, targetDurationNanos);
- }
-
- /** called by native */
- static void updateTargetWorkDuration(PerformanceHintManager.Session session,
- long targetDurationNanos) {
- session.updateTargetWorkDuration(targetDurationNanos);
- }
-
- /** called by native */
- static void reportActualWorkDuration(PerformanceHintManager.Session session,
- long actualDurationNanos) {
- session.reportActualWorkDuration(actualDurationNanos);
- }
-
- /** called by native */
- static void closeHintSession(PerformanceHintManager.Session session) {
- session.close();
- }
-
/**
* Interface used to receive callbacks when Webview requests a surface control.
*
@@ -1152,7 +1121,6 @@
private boolean mIsolated = false;
private Context mContext;
private String mPackageName;
- private PerformanceHintManager mPerformanceHintManager;
private IGraphicsStats mGraphicsStatsService;
private IGraphicsStatsCallback mGraphicsStatsCallback = new IGraphicsStatsCallback.Stub() {
@Override
@@ -1164,10 +1132,6 @@
private ProcessInitializer() {
}
- synchronized PerformanceHintManager getHintManager() {
- return mPerformanceHintManager;
- }
-
synchronized void setPackageName(String name) {
if (mInitialized) return;
mPackageName = name;
@@ -1218,10 +1182,6 @@
initDisplayInfo();
- // HintManager and HintSession are designed to be accessible from isoalted processes
- // so not checking for isolated process here.
- initHintSession();
-
nSetIsHighEndGfx(ActivityManager.isHighEndGfx());
// Defensively clear out the context in case we were passed a context that can leak
// if we live longer than it, e.g. an activity context.
@@ -1265,11 +1225,6 @@
mDisplayInitialized = true;
}
- private void initHintSession() {
- if (mContext == null) return;
- mPerformanceHintManager = mContext.getSystemService(PerformanceHintManager.class);
- }
-
private void rotateBuffer() {
nRotateProcessStatsBuffer();
requestBuffer();
diff --git a/libs/WindowManager/Shell/res/color/one_handed_tutorial_background_color.xml b/libs/WindowManager/Shell/res/color/one_handed_tutorial_background_color.xml
new file mode 100644
index 0000000..4f56e0f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/one_handed_tutorial_background_color.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:color="?androidprv:attr/colorSurfaceVariant"/>
+</selector>
diff --git a/libs/WindowManager/Shell/res/drawable-hdpi/one_handed_tutorial.png b/libs/WindowManager/Shell/res/drawable-hdpi/one_handed_tutorial.png
deleted file mode 100644
index 6c1f1cf..0000000
--- a/libs/WindowManager/Shell/res/drawable-hdpi/one_handed_tutorial.png
+++ /dev/null
Binary files differ
diff --git a/libs/WindowManager/Shell/res/drawable/one_handed_tutorial_icon.xml b/libs/WindowManager/Shell/res/drawable/one_handed_tutorial_icon.xml
new file mode 100644
index 0000000..b32f34e
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/one_handed_tutorial_icon.xml
@@ -0,0 +1,14 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="32dp"
+ android:height="60dp"
+ android:viewportWidth="32"
+ android:viewportHeight="60">
+ <path
+ android:pathData="M1.9703,30.5041C1.9703,28.295 3.7612,26.5042 5.9703,26.5042H25.5551C27.7642,26.5042 29.5551,28.295 29.5551,30.5042V54.0296C29.5551,56.2387 27.7642,58.0296 25.5551,58.0296H5.9703C3.7612,58.0296 1.9703,56.2387 1.9703,54.0296V30.5041Z"
+ android:fillColor="#000000"
+ android:fillAlpha="0.16"/>
+ <path
+ android:pathData="M25.5254,2H6C3.7909,2 2,3.7909 2,6V54C2,56.2091 3.7909,58 6,58H25.5254C27.7346,58 29.5254,56.2091 29.5254,54V6C29.5254,3.7909 27.7346,2 25.5254,2ZM6,0C2.6863,0 0,2.6863 0,6V54C0,57.3137 2.6863,60 6,60H25.5254C28.8391,60 31.5254,57.3137 31.5254,54V6C31.5254,2.6863 28.8391,0 25.5254,0H6ZM12.2034,47.2336L12.8307,47.861L15.3178,45.3783V52.1277H16.2076V45.3783L18.6903,47.8654L19.322,47.2336L15.7627,43.6743L12.2034,47.2336ZM19.7034,55.0742H11.822V56.552H19.7034V55.0742Z"
+ android:fillColor="#000000"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml b/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml
index 0190aad..d29ed8b 100644
--- a/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml
+++ b/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml
@@ -31,7 +31,7 @@
android:layout_marginTop="6dp"
android:layout_marginBottom="0dp"
android:gravity="center_horizontal"
- android:src="@drawable/one_handed_tutorial"
+ android:src="@drawable/one_handed_tutorial_icon"
android:scaleType="centerInside" />
<TextView
@@ -45,7 +45,6 @@
android:fontFamily="google-sans-medium"
android:text="@string/one_handed_tutorial_title"
android:textSize="16sp"
- android:textStyle="bold"
android:textColor="@android:color/white"/>
<TextView
@@ -54,8 +53,8 @@
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_marginBottom="0dp"
- android:layout_marginStart="46dp"
- android:layout_marginEnd="46dp"
+ android:layout_marginStart="60dp"
+ android:layout_marginEnd="60dp"
android:gravity="center_horizontal"
android:fontFamily="roboto-regular"
android:text="@string/one_handed_tutorial_description"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 92e455c..c0df06f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -45,6 +45,7 @@
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.SurfaceControl;
+import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
@@ -205,6 +206,7 @@
* between bubble activities without needing both to be alive at the same time.
*/
private SurfaceView mAnimatingOutSurfaceView;
+ private boolean mAnimatingOutSurfaceReady;
/** Container for the animating-out SurfaceView. */
private FrameLayout mAnimatingOutSurfaceContainer;
@@ -811,6 +813,20 @@
mAnimatingOutSurfaceView.setZOrderOnTop(true);
mAnimatingOutSurfaceView.setCornerRadius(mCornerRadius);
mAnimatingOutSurfaceView.setLayoutParams(new ViewGroup.LayoutParams(0, 0));
+ mAnimatingOutSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {}
+
+ @Override
+ public void surfaceCreated(SurfaceHolder surfaceHolder) {
+ mAnimatingOutSurfaceReady = true;
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
+ mAnimatingOutSurfaceReady = false;
+ }
+ });
mAnimatingOutSurfaceContainer.addView(mAnimatingOutSurfaceView);
mAnimatingOutSurfaceContainer.setPadding(
@@ -2653,7 +2669,7 @@
return;
}
- if (!mIsExpanded) {
+ if (!mIsExpanded || !mAnimatingOutSurfaceReady) {
onComplete.accept(false);
return;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
index 481b948..3ccb9e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
@@ -20,7 +20,6 @@
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Rect;
-import android.util.Log;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.window.DisplayAreaAppearedInfo;
@@ -30,7 +29,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import androidx.core.content.ContextCompat;
import com.android.internal.annotations.GuardedBy;
import com.android.wm.shell.R;
@@ -48,14 +46,17 @@
public class OneHandedBackgroundPanelOrganizer extends DisplayAreaOrganizer
implements OneHandedTransitionCallback {
private static final String TAG = "OneHandedBackgroundPanelOrganizer";
+ private static final int THEME_COLOR_OFFSET = 10;
+ private final Context mContext;
private final Object mLock = new Object();
private final SurfaceSession mSurfaceSession = new SurfaceSession();
- private final float[] mDefaultColor;
private final Executor mMainExecutor;
private final OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
+ private float[] mDefaultColor;
+
/**
* The background to distinguish the boundary of translated windows and empty region when
* one handed mode triggered.
@@ -88,15 +89,14 @@
public OneHandedBackgroundPanelOrganizer(Context context, DisplayLayout displayLayout,
Executor executor) {
super(executor);
+ mContext = context;
// Ensure the mBkgBounds is portrait, due to OHM only support on portrait
if (displayLayout.height() > displayLayout.width()) {
mBkgBounds = new Rect(0, 0, displayLayout.width(), displayLayout.height());
} else {
mBkgBounds = new Rect(0, 0, displayLayout.height(), displayLayout.width());
}
- final int defaultColor = ContextCompat.getColor(context, R.color.GM2_grey_800);
- mDefaultColor = new float[]{Color.red(defaultColor) / 255.0f,
- Color.green(defaultColor) / 255.0f, Color.blue(defaultColor) / 255.0f};
+ updateThemeColors();
mMainExecutor = executor;
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
}
@@ -170,7 +170,6 @@
}
if (getBackgroundSurface() == null) {
- Log.w(TAG, "mBackgroundSurface is null !");
return;
}
@@ -201,6 +200,30 @@
}
}
+ /**
+ * onConfigurationChanged events for updating tutorial text.
+ */
+ public void onConfigurationChanged() {
+ synchronized (mLock) {
+ if (mBackgroundSurface == null) {
+ getBackgroundSurface();
+ } else {
+ removeBackgroundPanelLayer();
+ }
+ updateThemeColors();
+ showBackgroundPanelLayer();
+ }
+ }
+
+ private void updateThemeColors() {
+ synchronized (mLock) {
+ final int themeColor = mContext.getColor(R.color.one_handed_tutorial_background_color);
+ mDefaultColor = new float[]{(Color.red(themeColor) - THEME_COLOR_OFFSET) / 255.0f,
+ (Color.green(themeColor) - THEME_COLOR_OFFSET) / 255.0f,
+ (Color.blue(themeColor) - THEME_COLOR_OFFSET) / 255.0f};
+ }
+ }
+
void dump(@NonNull PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 2038cff..09cde38 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -658,12 +658,13 @@
}
private void onConfigChanged(Configuration newConfig) {
- if (mTutorialHandler == null) {
+ if (mTutorialHandler == null || mBackgroundPanelOrganizer == null) {
return;
}
if (!mIsOneHandedEnabled || newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
return;
}
+ mBackgroundPanelOrganizer.onConfigurationChanged();
mTutorialHandler.onConfigurationChanged();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
index d0206a4..97e04b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
@@ -25,8 +25,11 @@
import static com.android.wm.shell.onehanded.OneHandedState.STATE_EXITING;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE;
+import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.SystemProperties;
@@ -35,9 +38,13 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import android.view.animation.LinearInterpolator;
import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
import androidx.annotation.NonNull;
+import androidx.appcompat.view.ContextThemeWrapper;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
@@ -53,11 +60,14 @@
public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
OneHandedState.OnStateChangedListener {
private static final String TAG = "OneHandedTutorialHandler";
- private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE =
- "persist.debug.one_handed_offset_percentage";
+ private static final String OFFSET_PERCENTAGE = "persist.debug.one_handed_offset_percentage";
+ private static final String TRANSLATE_ANIMATION_DURATION =
+ "persist.debug.one_handed_translate_animation_duration";
+ private static final float START_TRANSITION_FRACTION = 0.7f;
private final float mTutorialHeightRatio;
private final WindowManager mWindowManager;
+ private final OneHandedAnimationCallback mAnimationCallback;
private @OneHandedState.State int mCurrentState;
private int mTutorialAreaHeight;
@@ -67,7 +77,9 @@
private @Nullable View mTutorialView;
private @Nullable ViewGroup mTargetViewContainer;
- private final OneHandedAnimationCallback mAnimationCallback;
+ private float mAlphaTransitionStart;
+ private ValueAnimator mAlphaAnimator;
+ private int mAlphaAnimationDurationMs;
public OneHandedTutorialHandler(Context context, WindowManager windowManager) {
mContext = context;
@@ -75,15 +87,35 @@
final float offsetPercentageConfig = context.getResources().getFraction(
R.fraction.config_one_handed_offset, 1, 1);
final int sysPropPercentageConfig = SystemProperties.getInt(
- ONE_HANDED_MODE_OFFSET_PERCENTAGE, Math.round(offsetPercentageConfig * 100.0f));
+ OFFSET_PERCENTAGE, Math.round(offsetPercentageConfig * 100.0f));
mTutorialHeightRatio = sysPropPercentageConfig / 100.0f;
+ final int animationDuration = context.getResources().getInteger(
+ R.integer.config_one_handed_translate_animation_duration);
+ mAlphaAnimationDurationMs = SystemProperties.getInt(TRANSLATE_ANIMATION_DURATION,
+ animationDuration);
mAnimationCallback = new OneHandedAnimationCallback() {
@Override
+ public void onOneHandedAnimationCancel(
+ OneHandedAnimationController.OneHandedTransitionAnimator animator) {
+ if (mAlphaAnimator != null) {
+ mAlphaAnimator.cancel();
+ }
+ }
+
+ @Override
public void onAnimationUpdate(float xPos, float yPos) {
if (!isAttached()) {
return;
}
- mTargetViewContainer.setTranslationY(yPos - mTutorialAreaHeight);
+ if (yPos < mAlphaTransitionStart) {
+ checkTransitionEnd();
+ return;
+ }
+ if (mAlphaAnimator == null || mAlphaAnimator.isStarted()
+ || mAlphaAnimator.isRunning()) {
+ return;
+ }
+ mAlphaAnimator.start();
}
};
}
@@ -94,12 +126,16 @@
switch (newState) {
case STATE_ENTERING:
createViewAndAttachToWindow(mContext);
+ updateThemeColor();
+ setupAlphaTransition(true /* isEntering */);
break;
case STATE_ACTIVE:
- case STATE_EXITING:
- // no - op
+ checkTransitionEnd();
+ setupAlphaTransition(false /* isEntering */);
break;
+ case STATE_EXITING:
case STATE_NONE:
+ checkTransitionEnd();
removeTutorialFromWindowManager();
break;
default:
@@ -119,6 +155,7 @@
mDisplayBounds = new Rect(0, 0, displayLayout.height(), displayLayout.width());
}
mTutorialAreaHeight = Math.round(mDisplayBounds.height() * mTutorialHeightRatio);
+ mAlphaTransitionStart = mTutorialAreaHeight * START_TRANSITION_FRACTION;
}
@VisibleForTesting
@@ -129,6 +166,7 @@
mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial, null);
mTargetViewContainer = new FrameLayout(context);
mTargetViewContainer.setClipChildren(false);
+ mTargetViewContainer.setAlpha(mCurrentState == STATE_ACTIVE ? 1.0f : 0.0f);
mTargetViewContainer.addView(mTutorialView);
mTargetViewContainer.setLayerType(LAYER_TYPE_HARDWARE, null);
@@ -192,6 +230,52 @@
removeTutorialFromWindowManager();
if (mCurrentState == STATE_ENTERING || mCurrentState == STATE_ACTIVE) {
createViewAndAttachToWindow(mContext);
+ updateThemeColor();
+ checkTransitionEnd();
+ }
+ }
+
+ private void updateThemeColor() {
+ if (mTutorialView == null) {
+ return;
+ }
+
+ final Context themedContext = new ContextThemeWrapper(mTutorialView.getContext(),
+ com.android.internal.R.style.Theme_DeviceDefault_DayNight);
+ final int textColorPrimary;
+ final int themedTextColorSecondary;
+ TypedArray ta = themedContext.obtainStyledAttributes(new int[]{
+ com.android.internal.R.attr.textColorPrimary,
+ com.android.internal.R.attr.textColorSecondary});
+ textColorPrimary = ta.getColor(0, 0);
+ themedTextColorSecondary = ta.getColor(1, 0);
+ ta.recycle();
+
+ final ImageView iconView = mTutorialView.findViewById(R.id.one_handed_tutorial_image);
+ iconView.setImageTintList(ColorStateList.valueOf(textColorPrimary));
+
+ final TextView tutorialTitle = mTutorialView.findViewById(R.id.one_handed_tutorial_title);
+ final TextView tutorialDesc = mTutorialView.findViewById(
+ R.id.one_handed_tutorial_description);
+ tutorialTitle.setTextColor(textColorPrimary);
+ tutorialDesc.setTextColor(themedTextColorSecondary);
+ }
+
+ private void setupAlphaTransition(boolean isEntering) {
+ final float start = isEntering ? 0.0f : 1.0f;
+ final float end = isEntering ? 1.0f : 0.0f;
+ mAlphaAnimator = ValueAnimator.ofFloat(start, end);
+ mAlphaAnimator.setInterpolator(new LinearInterpolator());
+ mAlphaAnimator.setDuration(mAlphaAnimationDurationMs);
+ mAlphaAnimator.addUpdateListener(
+ animator -> mTargetViewContainer.setAlpha((float) animator.getAnimatedValue()));
+ }
+
+ private void checkTransitionEnd() {
+ if (mAlphaAnimator != null && mAlphaAnimator.isRunning()) {
+ mAlphaAnimator.end();
+ mAlphaAnimator.removeAllUpdateListeners();
+ mAlphaAnimator = null;
}
}
@@ -206,5 +290,9 @@
pw.println(mDisplayBounds);
pw.print(innerPrefix + "mTutorialAreaHeight=");
pw.println(mTutorialAreaHeight);
+ pw.print(innerPrefix + "mAlphaTransitionStart=");
+ pw.println(mAlphaTransitionStart);
+ pw.print(innerPrefix + "mAlphaAnimationDurationMs=");
+ pw.println(mAlphaAnimationDurationMs);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index 107a3f8..56ad2be 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -37,6 +37,7 @@
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
@@ -57,6 +58,7 @@
import com.android.internal.graphics.palette.Palette;
import com.android.internal.graphics.palette.Quantizer;
import com.android.internal.graphics.palette.VariationalKMeansQuantizer;
+import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.common.TransactionPool;
@@ -368,8 +370,14 @@
if (DEBUG) {
Slog.d(TAG, "The icon is not an AdaptiveIconDrawable");
}
- // TODO process legacy icon(bitmap)
- createIconDrawable(iconDrawable, true);
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "legacy_icon_factory");
+ final ShapeIconFactory factory = new ShapeIconFactory(
+ SplashscreenContentDrawer.this.mContext,
+ scaledIconDpi, mFinalIconSize);
+ final Bitmap bitmap = factory.createScaledBitmapWithoutShadow(
+ iconDrawable, true /* shrinkNonAdaptiveIcons */);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ createIconDrawable(new BitmapDrawable(bitmap), true);
}
animationDuration = 0;
}
@@ -377,11 +385,15 @@
return fillViewWithIcon(mFinalIconSize, mFinalIconDrawable, animationDuration);
}
+ private class ShapeIconFactory extends BaseIconFactory {
+ protected ShapeIconFactory(Context context, int fillResIconDpi, int iconBitmapSize) {
+ super(context, fillResIconDpi, iconBitmapSize, true /* shapeDetection */);
+ }
+ }
+
private void createIconDrawable(Drawable iconDrawable, boolean legacy) {
if (legacy) {
mFinalIconDrawable = SplashscreenIconDrawableFactory.makeLegacyIconDrawable(
- mTmpAttrs.mIconBgColor != Color.TRANSPARENT
- ? mTmpAttrs.mIconBgColor : Color.WHITE,
iconDrawable, mDefaultIconSize, mFinalIconSize, mSplashscreenWorkerHandler);
} else {
mFinalIconDrawable = SplashscreenIconDrawableFactory.makeIconDrawable(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
index 211941f..ba9123d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
@@ -64,11 +64,10 @@
}
}
- static Drawable makeLegacyIconDrawable(@ColorInt int backgroundColor,
- @NonNull Drawable foregroundDrawable, int srcIconSize, int iconSize,
- Handler splashscreenWorkerHandler) {
- return new ImmobileIconDrawable(new LegacyIconDrawable(backgroundColor,
- foregroundDrawable), srcIconSize, iconSize, splashscreenWorkerHandler);
+ static Drawable makeLegacyIconDrawable(@NonNull Drawable iconDrawable, int srcIconSize,
+ int iconSize, Handler splashscreenWorkerHandler) {
+ return new ImmobileIconDrawable(iconDrawable, srcIconSize, iconSize,
+ splashscreenWorkerHandler);
}
private static class ImmobileIconDrawable extends Drawable {
@@ -179,65 +178,6 @@
}
}
- private static class LegacyIconDrawable extends MaskBackgroundDrawable {
- // reference FixedScaleDrawable
- // iconBounds = 0.7 * X * outerBounds, X is the scale of diagonal
- private static final float LEGACY_ICON_SCALE = .7f * .8f;
- private final Drawable mForegroundDrawable;
- private float mScaleX, mScaleY, mTransX, mTransY;
-
- LegacyIconDrawable(@ColorInt int backgroundColor, Drawable foregroundDrawable) {
- super(backgroundColor);
- mForegroundDrawable = foregroundDrawable;
- mScaleX = LEGACY_ICON_SCALE;
- mScaleY = LEGACY_ICON_SCALE;
- }
-
- @Override
- protected void updateLayerBounds(Rect bounds) {
- super.updateLayerBounds(bounds);
-
- if (mForegroundDrawable == null) {
- return;
- }
- float outerBoundsWidth = bounds.width();
- float outerBoundsHeight = bounds.height();
- float h = mForegroundDrawable.getIntrinsicHeight();
- float w = mForegroundDrawable.getIntrinsicWidth();
- mScaleX = LEGACY_ICON_SCALE;
- mScaleY = LEGACY_ICON_SCALE;
- if (h > w && w > 0) {
- mScaleX *= w / h;
- } else if (w > h && h > 0) {
- mScaleY *= h / w;
- }
- int innerBoundsWidth = (int) (0.5 + outerBoundsWidth * mScaleX);
- int innerBoundsHeight = (int) (0.5 + outerBoundsHeight * mScaleY);
- final Rect rect = new Rect(0, 0, innerBoundsWidth, innerBoundsHeight);
- mForegroundDrawable.setBounds(rect);
- mTransX = (outerBoundsWidth - innerBoundsWidth) / 2;
- mTransY = (outerBoundsHeight - innerBoundsHeight) / 2;
- invalidateSelf();
- }
-
- @Override
- public void draw(Canvas canvas) {
- super.draw(canvas);
- int saveCount = canvas.save();
- canvas.translate(mTransX, mTransY);
- if (mForegroundDrawable != null) {
- mForegroundDrawable.draw(canvas);
- }
- canvas.restoreToCount(saveCount);
- }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) {
- if (mForegroundDrawable != null) {
- mForegroundDrawable.setColorFilter(colorFilter);
- }
- }
- }
/**
* A lightweight AdaptiveIconDrawable which support foreground to be Animatable, and keep this
* drawable masked by config_icon_mask.
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index c4cdb7d..54367b8 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -44,8 +44,6 @@
#include <utils/StrongPointer.h>
#include <utils/Timers.h>
-#include <pthread.h>
-
#include <algorithm>
#include <atomic>
#include <vector>
@@ -60,10 +58,6 @@
struct {
jclass clazz;
jmethodID invokePictureCapturedCallback;
- jmethodID createHintSession;
- jmethodID updateTargetWorkDuration;
- jmethodID reportActualWorkDuration;
- jmethodID closeHintSession;
} gHardwareRenderer;
struct {
@@ -90,14 +84,6 @@
return env;
}
-static bool hasExceptionAndClear(JNIEnv* env) {
- if (GraphicsJNI::hasException(env)) {
- env->ExceptionClear();
- return true;
- }
- return false;
-}
-
typedef ANativeWindow* (*ANW_fromSurface)(JNIEnv* env, jobject surface);
ANW_fromSurface fromSurface;
@@ -147,67 +133,6 @@
}
};
-class HintSessionWrapper : public LightRefBase<HintSessionWrapper> {
-public:
- static sp<HintSessionWrapper> create(JNIEnv* env, RenderProxy* proxy) {
- if (!Properties::useHintManager) return nullptr;
-
- // Include UI thread (self), render thread, and thread pool.
- std::vector<int> tids = CommonPool::getThreadIds();
- tids.push_back(proxy->getRenderThreadTid());
- tids.push_back(pthread_gettid_np(pthread_self()));
-
- jintArray tidsArray = env->NewIntArray(tids.size());
- if (hasExceptionAndClear(env)) return nullptr;
- env->SetIntArrayRegion(tidsArray, 0, tids.size(), reinterpret_cast<jint*>(tids.data()));
- if (hasExceptionAndClear(env)) return nullptr;
- jobject session = env->CallStaticObjectMethod(
- gHardwareRenderer.clazz, gHardwareRenderer.createHintSession, tidsArray);
- if (hasExceptionAndClear(env) || !session) return nullptr;
- return new HintSessionWrapper(env, session);
- }
-
- ~HintSessionWrapper() {
- if (!mSession) return;
- JNIEnv* env = getenv(mVm);
- env->CallStaticVoidMethod(gHardwareRenderer.clazz, gHardwareRenderer.closeHintSession,
- mSession);
- hasExceptionAndClear(env);
- env->DeleteGlobalRef(mSession);
- mSession = nullptr;
- }
-
- void updateTargetWorkDuration(long targetDurationNanos) {
- if (!mSession) return;
- JNIEnv* env = getenv(mVm);
- env->CallStaticVoidMethod(gHardwareRenderer.clazz,
- gHardwareRenderer.updateTargetWorkDuration, mSession,
- static_cast<jlong>(targetDurationNanos));
- hasExceptionAndClear(env);
- }
-
- void reportActualWorkDuration(long actualDurationNanos) {
- if (!mSession) return;
- JNIEnv* env = getenv(mVm);
- env->CallStaticVoidMethod(gHardwareRenderer.clazz,
- gHardwareRenderer.reportActualWorkDuration, mSession,
- static_cast<jlong>(actualDurationNanos));
- hasExceptionAndClear(env);
- }
-
-private:
- HintSessionWrapper(JNIEnv* env, jobject jobject) {
- env->GetJavaVM(&mVm);
- if (jobject) {
- mSession = env->NewGlobalRef(jobject);
- LOG_ALWAYS_FATAL_IF(!mSession, "Failed to make global ref");
- }
- }
-
- JavaVM* mVm = nullptr;
- jobject mSession = nullptr;
-};
-
static void android_view_ThreadedRenderer_rotateProcessStatsBuffer(JNIEnv* env, jobject clazz) {
RenderProxy::rotateProcessStatsBuffer();
}
@@ -235,12 +160,6 @@
RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr);
ContextFactoryImpl factory(rootRenderNode);
RenderProxy* proxy = new RenderProxy(translucent, rootRenderNode, &factory);
- sp<HintSessionWrapper> wrapper = HintSessionWrapper::create(env, proxy);
- if (wrapper) {
- proxy->setHintSessionCallbacks(
- [wrapper](int64_t nanos) { wrapper->updateTargetWorkDuration(nanos); },
- [wrapper](int64_t nanos) { wrapper->reportActualWorkDuration(nanos); });
- }
return (jlong) proxy;
}
@@ -1059,18 +978,6 @@
gHardwareRenderer.invokePictureCapturedCallback = GetStaticMethodIDOrDie(env, hardwareRenderer,
"invokePictureCapturedCallback",
"(JLandroid/graphics/HardwareRenderer$PictureCapturedCallback;)V");
- gHardwareRenderer.createHintSession =
- GetStaticMethodIDOrDie(env, hardwareRenderer, "createHintSession",
- "([I)Landroid/os/PerformanceHintManager$Session;");
- gHardwareRenderer.updateTargetWorkDuration =
- GetStaticMethodIDOrDie(env, hardwareRenderer, "updateTargetWorkDuration",
- "(Landroid/os/PerformanceHintManager$Session;J)V");
- gHardwareRenderer.reportActualWorkDuration =
- GetStaticMethodIDOrDie(env, hardwareRenderer, "reportActualWorkDuration",
- "(Landroid/os/PerformanceHintManager$Session;J)V");
- gHardwareRenderer.closeHintSession =
- GetStaticMethodIDOrDie(env, hardwareRenderer, "closeHintSession",
- "(Landroid/os/PerformanceHintManager$Session;)V");
jclass aSurfaceTransactionCallbackClass =
FindClassOrDie(env, "android/graphics/HardwareRenderer$ASurfaceTransactionCallback");
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index db29e34..e7081df 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -16,6 +16,7 @@
#include "DrawFrameTask.h"
+#include <dlfcn.h>
#include <gui/TraceUtils.h>
#include <utils/Log.h>
#include <algorithm>
@@ -26,11 +27,63 @@
#include "../RenderNode.h"
#include "CanvasContext.h"
#include "RenderThread.h"
+#include "thread/CommonPool.h"
namespace android {
namespace uirenderer {
namespace renderthread {
+namespace {
+
+typedef APerformanceHintManager* (*APH_getManager)();
+typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*,
+ size_t, int64_t);
+typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
+typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
+typedef void (*APH_closeSession)(APerformanceHintSession* session);
+
+bool gAPerformanceHintBindingInitialized = false;
+APH_getManager gAPH_getManagerFn = nullptr;
+APH_createSession gAPH_createSessionFn = nullptr;
+APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
+APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
+APH_closeSession gAPH_closeSessionFn = nullptr;
+
+void ensureAPerformanceHintBindingInitialized() {
+ if (gAPerformanceHintBindingInitialized) return;
+
+ void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
+ LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!");
+
+ gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager");
+ LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr,
+ "Failed to find required symbol APerformanceHint_getManager!");
+
+ gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession");
+ LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
+ "Failed to find required symbol APerformanceHint_createSession!");
+
+ gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym(
+ handle_, "APerformanceHint_updateTargetWorkDuration");
+ LOG_ALWAYS_FATAL_IF(
+ gAPH_updateTargetWorkDurationFn == nullptr,
+ "Failed to find required symbol APerformanceHint_updateTargetWorkDuration!");
+
+ gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym(
+ handle_, "APerformanceHint_reportActualWorkDuration");
+ LOG_ALWAYS_FATAL_IF(
+ gAPH_reportActualWorkDurationFn == nullptr,
+ "Failed to find required symbol APerformanceHint_reportActualWorkDuration!");
+
+ gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession");
+ LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
+ "Failed to find required symbol APerformanceHint_closeSession!");
+
+ gAPerformanceHintBindingInitialized = true;
+}
+
+} // namespace
+
DrawFrameTask::DrawFrameTask()
: mRenderThread(nullptr)
, mContext(nullptr)
@@ -39,17 +92,13 @@
DrawFrameTask::~DrawFrameTask() {}
-void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context,
- RenderNode* targetNode) {
+void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode,
+ int32_t uiThreadId, int32_t renderThreadId) {
mRenderThread = thread;
mContext = context;
mTargetNode = targetNode;
-}
-
-void DrawFrameTask::setHintSessionCallbacks(std::function<void(int64_t)> updateTargetWorkDuration,
- std::function<void(int64_t)> reportActualWorkDuration) {
- mUpdateTargetWorkDuration = std::move(updateTargetWorkDuration);
- mReportActualWorkDuration = std::move(reportActualWorkDuration);
+ mUiThreadId = uiThreadId;
+ mRenderThreadId = renderThreadId;
}
void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) {
@@ -144,27 +193,25 @@
unblockUiThread();
}
- // These member callbacks are effectively const as they are set once during init, so it's safe
- // to use these directly instead of making local copies.
- if (mUpdateTargetWorkDuration && mReportActualWorkDuration) {
- constexpr int64_t kSanityCheckLowerBound = 100000; // 0.1ms
- constexpr int64_t kSanityCheckUpperBound = 10000000000; // 10s
- int64_t targetWorkDuration = frameDeadline - intendedVsync;
- targetWorkDuration = targetWorkDuration * Properties::targetCpuTimePercentage / 100;
- if (targetWorkDuration > kSanityCheckLowerBound &&
- targetWorkDuration < kSanityCheckUpperBound &&
- targetWorkDuration != mLastTargetWorkDuration) {
- mLastTargetWorkDuration = targetWorkDuration;
- mUpdateTargetWorkDuration(targetWorkDuration);
- }
- int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
- int64_t actualDuration = frameDuration -
- (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
- dequeueBufferDuration;
- if (actualDuration > kSanityCheckLowerBound && actualDuration < kSanityCheckUpperBound) {
- mReportActualWorkDuration(actualDuration);
- }
+ if (!mHintSessionWrapper) mHintSessionWrapper.emplace(mUiThreadId, mRenderThreadId);
+ constexpr int64_t kSanityCheckLowerBound = 100000; // 0.1ms
+ constexpr int64_t kSanityCheckUpperBound = 10000000000; // 10s
+ int64_t targetWorkDuration = frameDeadline - intendedVsync;
+ targetWorkDuration = targetWorkDuration * Properties::targetCpuTimePercentage / 100;
+ if (targetWorkDuration > kSanityCheckLowerBound &&
+ targetWorkDuration < kSanityCheckUpperBound &&
+ targetWorkDuration != mLastTargetWorkDuration) {
+ mLastTargetWorkDuration = targetWorkDuration;
+ mHintSessionWrapper->updateTargetWorkDuration(targetWorkDuration);
}
+ int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
+ int64_t actualDuration = frameDuration -
+ (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
+ dequeueBufferDuration;
+ if (actualDuration > kSanityCheckLowerBound && actualDuration < kSanityCheckUpperBound) {
+ mHintSessionWrapper->reportActualWorkDuration(actualDuration);
+ }
+
mLastDequeueBufferDuration = dequeueBufferDuration;
}
@@ -216,6 +263,44 @@
mSignal.signal();
}
+DrawFrameTask::HintSessionWrapper::HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId) {
+ if (!Properties::useHintManager) return;
+ if (uiThreadId < 0 || renderThreadId < 0) return;
+
+ ensureAPerformanceHintBindingInitialized();
+
+ APerformanceHintManager* manager = gAPH_getManagerFn();
+ if (!manager) return;
+
+ std::vector<int32_t> tids = CommonPool::getThreadIds();
+ tids.push_back(uiThreadId);
+ tids.push_back(renderThreadId);
+
+ // DrawFrameTask code will always set a target duration before reporting actual durations.
+ // So this is just a placeholder value that's never used.
+ int64_t dummyTargetDurationNanos = 16666667;
+ mHintSession =
+ gAPH_createSessionFn(manager, tids.data(), tids.size(), dummyTargetDurationNanos);
+}
+
+DrawFrameTask::HintSessionWrapper::~HintSessionWrapper() {
+ if (mHintSession) {
+ gAPH_closeSessionFn(mHintSession);
+ }
+}
+
+void DrawFrameTask::HintSessionWrapper::updateTargetWorkDuration(long targetDurationNanos) {
+ if (mHintSession) {
+ gAPH_updateTargetWorkDurationFn(mHintSession, targetDurationNanos);
+ }
+}
+
+void DrawFrameTask::HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {
+ if (mHintSession) {
+ gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos);
+ }
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 2455ea8..6a61a2b 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -16,8 +16,10 @@
#ifndef DRAWFRAMETASK_H
#define DRAWFRAMETASK_H
+#include <optional>
#include <vector>
+#include <performance_hint_private.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include <utils/StrongPointer.h>
@@ -60,9 +62,8 @@
DrawFrameTask();
virtual ~DrawFrameTask();
- void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode);
- void setHintSessionCallbacks(std::function<void(int64_t)> updateTargetWorkDuration,
- std::function<void(int64_t)> reportActualWorkDuration);
+ void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode,
+ int32_t uiThreadId, int32_t renderThreadId);
void setContentDrawBounds(int left, int top, int right, int bottom) {
mContentDrawBounds.set(left, top, right, bottom);
}
@@ -85,6 +86,18 @@
}
private:
+ class HintSessionWrapper {
+ public:
+ HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId);
+ ~HintSessionWrapper();
+
+ void updateTargetWorkDuration(long targetDurationNanos);
+ void reportActualWorkDuration(long actualDurationNanos);
+
+ private:
+ APerformanceHintSession* mHintSession = nullptr;
+ };
+
void postAndWait();
bool syncFrameState(TreeInfo& info);
void unblockUiThread();
@@ -95,6 +108,8 @@
RenderThread* mRenderThread;
CanvasContext* mContext;
RenderNode* mTargetNode = nullptr;
+ int32_t mUiThreadId = -1;
+ int32_t mRenderThreadId = -1;
Rect mContentDrawBounds;
/*********************************************
@@ -112,8 +127,7 @@
nsecs_t mLastDequeueBufferDuration = 0;
nsecs_t mLastTargetWorkDuration = 0;
- std::function<void(int64_t)> mUpdateTargetWorkDuration;
- std::function<void(int64_t)> mReportActualWorkDuration;
+ std::optional<HintSessionWrapper> mHintSessionWrapper;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index a77b5b5..c485ce2 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -29,6 +29,8 @@
#include "utils/Macros.h"
#include "utils/TimeUtils.h"
+#include <pthread.h>
+
namespace android {
namespace uirenderer {
namespace renderthread {
@@ -39,7 +41,8 @@
mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* {
return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
});
- mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode);
+ mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode,
+ pthread_gettid_np(pthread_self()), getRenderThreadTid());
}
RenderProxy::~RenderProxy() {
@@ -48,7 +51,7 @@
void RenderProxy::destroyContext() {
if (mContext) {
- mDrawFrameTask.setContext(nullptr, nullptr, nullptr);
+ mDrawFrameTask.setContext(nullptr, nullptr, nullptr, -1, -1);
// This is also a fence as we need to be certain that there are no
// outstanding mDrawFrame tasks posted before it is destroyed
mRenderThread.queue().runSync([this]() { delete mContext; });
@@ -76,12 +79,6 @@
mRenderThread.queue().runSync([this, name]() { mContext->setName(std::string(name)); });
}
-void RenderProxy::setHintSessionCallbacks(std::function<void(int64_t)> updateTargetWorkDuration,
- std::function<void(int64_t)> reportActualWorkDuration) {
- mDrawFrameTask.setHintSessionCallbacks(std::move(updateTargetWorkDuration),
- std::move(reportActualWorkDuration));
-}
-
void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) {
if (window) { ANativeWindow_acquire(window); }
mRenderThread.queue().post([this, win = window, enableTimeout]() mutable {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 1b0f22e..2b5405c 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -71,8 +71,6 @@
void setSwapBehavior(SwapBehavior swapBehavior);
bool loadSystemProperties();
void setName(const char* name);
- void setHintSessionCallbacks(std::function<void(int64_t)> updateTargetWorkDuration,
- std::function<void(int64_t)> reportActualWorkDuration);
void setSurface(ANativeWindow* window, bool enableTimeout = true);
void setSurfaceControl(ASurfaceControl* surfaceControl);
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index ac89fecd..8436ba4 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -416,20 +416,14 @@
return -1;
}
- MtpProperty* property = new MtpProperty(MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO,
- MTP_TYPE_STR, true);
- if (!property) {
- env->ThrowNew(clazz_io_exception, "Failed to obtain property.");
- return -1;
- }
-
- if (property->getDataType() != MTP_TYPE_STR) {
+ MtpProperty property(MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO, MTP_TYPE_STR, true);
+ if (property.getDataType() != MTP_TYPE_STR) {
env->ThrowNew(clazz_io_exception, "Unexpected property data type.");
return -1;
}
- property->setCurrentValue(propertyStr);
- if (!device->setDevicePropValueStr(property)) {
+ property.setCurrentValue(propertyStr);
+ if (!device->setDevicePropValueStr(&property)) {
env->ThrowNew(clazz_io_exception, "Failed to obtain property value.");
return -1;
}
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 3ee2c18..32b7a07 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -57,6 +57,7 @@
"net.c",
"obb.cpp",
"permission_manager.cpp",
+ "performance_hint.cpp",
"sensor.cpp",
"sharedmem.cpp",
"storage_manager.cpp",
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index de6db1a..f33e118 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -312,6 +312,13 @@
LIBANDROID_PLATFORM {
global:
+ APerformanceHint_getManager;
+ APerformanceHint_createSession;
+ APerformanceHint_getPreferredUpdateRateNanos;
+ APerformanceHint_updateTargetWorkDuration;
+ APerformanceHint_reportActualWorkDuration;
+ APerformanceHint_closeSession;
+ APerformanceHint_setIHintManagerForTesting;
extern "C++" {
ASurfaceControl_registerSurfaceStatsListener*;
ASurfaceControl_unregisterSurfaceStatsListener*;
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
new file mode 100644
index 0000000..95a2da9
--- /dev/null
+++ b/native/android/performance_hint.cpp
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "perf_hint"
+
+#include <utility>
+#include <vector>
+
+#include <android/os/IHintManager.h>
+#include <android/os/IHintSession.h>
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/IServiceManager.h>
+#include <performance_hint_private.h>
+#include <utils/SystemClock.h>
+
+using namespace android;
+using namespace android::os;
+
+struct APerformanceHintSession;
+
+struct APerformanceHintManager {
+public:
+ static APerformanceHintManager* getInstance();
+ APerformanceHintManager(sp<IHintManager> service, int64_t preferredRateNanos);
+ APerformanceHintManager() = delete;
+ ~APerformanceHintManager() = default;
+
+ APerformanceHintSession* createSession(const int32_t* threadIds, size_t size,
+ int64_t initialTargetWorkDurationNanos);
+ int64_t getPreferredRateNanos() const;
+
+private:
+ static APerformanceHintManager* create(sp<IHintManager> iHintManager);
+
+ sp<IHintManager> mHintManager;
+ const int64_t mPreferredRateNanos;
+};
+
+struct APerformanceHintSession {
+public:
+ APerformanceHintSession(sp<IHintSession> session, int64_t preferredRateNanos,
+ int64_t targetDurationNanos);
+ APerformanceHintSession() = delete;
+ ~APerformanceHintSession();
+
+ int updateTargetWorkDuration(int64_t targetDurationNanos);
+ int reportActualWorkDuration(int64_t actualDurationNanos);
+
+private:
+ friend struct APerformanceHintManager;
+
+ sp<IHintSession> mHintSession;
+ // HAL preferred update rate
+ const int64_t mPreferredRateNanos;
+ // Target duration for choosing update rate
+ int64_t mTargetDurationNanos;
+ // Last update timestamp
+ int64_t mLastUpdateTimestamp;
+ // Cached samples
+ std::vector<int64_t> mActualDurationsNanos;
+ std::vector<int64_t> mTimestampsNanos;
+};
+
+static IHintManager* gIHintManagerForTesting = nullptr;
+static APerformanceHintManager* gHintManagerForTesting = nullptr;
+
+// ===================================== APerformanceHintManager implementation
+APerformanceHintManager::APerformanceHintManager(sp<IHintManager> manager,
+ int64_t preferredRateNanos)
+ : mHintManager(std::move(manager)), mPreferredRateNanos(preferredRateNanos) {}
+
+APerformanceHintManager* APerformanceHintManager::getInstance() {
+ if (gHintManagerForTesting) return gHintManagerForTesting;
+ if (gIHintManagerForTesting) {
+ APerformanceHintManager* manager = create(gIHintManagerForTesting);
+ gIHintManagerForTesting = nullptr;
+ return manager;
+ }
+ static APerformanceHintManager* instance = create(nullptr);
+ return instance;
+}
+
+APerformanceHintManager* APerformanceHintManager::create(sp<IHintManager> manager) {
+ if (!manager) {
+ manager = interface_cast<IHintManager>(
+ defaultServiceManager()->checkService(String16("performance_hint")));
+ }
+ if (manager == nullptr) {
+ ALOGE("%s: PerformanceHint service is not ready ", __FUNCTION__);
+ return nullptr;
+ }
+ int64_t preferredRateNanos = -1L;
+ binder::Status ret = manager->getHintSessionPreferredRate(&preferredRateNanos);
+ if (!ret.isOk()) {
+ ALOGE("%s: PerformanceHint cannot get preferred rate. %s", __FUNCTION__,
+ ret.exceptionMessage().c_str());
+ return nullptr;
+ }
+ if (preferredRateNanos <= 0) {
+ ALOGE("%s: PerformanceHint invalid preferred rate.", __FUNCTION__);
+ return nullptr;
+ }
+ return new APerformanceHintManager(std::move(manager), preferredRateNanos);
+}
+
+APerformanceHintSession* APerformanceHintManager::createSession(
+ const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos) {
+ sp<IBinder> token = sp<BBinder>::make();
+ std::vector<int32_t> tids(threadIds, threadIds + size);
+ sp<IHintSession> session;
+ binder::Status ret =
+ mHintManager->createHintSession(token, tids, initialTargetWorkDurationNanos, &session);
+ if (!ret.isOk() || !session) {
+ return nullptr;
+ }
+ return new APerformanceHintSession(std::move(session), mPreferredRateNanos,
+ initialTargetWorkDurationNanos);
+}
+
+int64_t APerformanceHintManager::getPreferredRateNanos() const {
+ return mPreferredRateNanos;
+}
+
+// ===================================== APerformanceHintSession implementation
+
+APerformanceHintSession::APerformanceHintSession(sp<IHintSession> session,
+ int64_t preferredRateNanos,
+ int64_t targetDurationNanos)
+ : mHintSession(std::move(session)),
+ mPreferredRateNanos(preferredRateNanos),
+ mTargetDurationNanos(targetDurationNanos),
+ mLastUpdateTimestamp(elapsedRealtimeNano()) {}
+
+APerformanceHintSession::~APerformanceHintSession() {
+ binder::Status ret = mHintSession->close();
+ if (!ret.isOk()) {
+ ALOGE("%s: HintSession close failed: %s", __FUNCTION__, ret.exceptionMessage().c_str());
+ }
+}
+
+int APerformanceHintSession::updateTargetWorkDuration(int64_t targetDurationNanos) {
+ if (targetDurationNanos <= 0) {
+ ALOGE("%s: targetDurationNanos must be positive", __FUNCTION__);
+ return EINVAL;
+ }
+ binder::Status ret = mHintSession->updateTargetWorkDuration(targetDurationNanos);
+ if (!ret.isOk()) {
+ ALOGE("%s: HintSessionn updateTargetWorkDuration failed: %s", __FUNCTION__,
+ ret.exceptionMessage().c_str());
+ return EPIPE;
+ }
+ mTargetDurationNanos = targetDurationNanos;
+ /**
+ * Most of the workload is target_duration dependent, so now clear the cached samples
+ * as they are most likely obsolete.
+ */
+ mActualDurationsNanos.clear();
+ mTimestampsNanos.clear();
+ mLastUpdateTimestamp = elapsedRealtimeNano();
+ return 0;
+}
+
+int APerformanceHintSession::reportActualWorkDuration(int64_t actualDurationNanos) {
+ if (actualDurationNanos <= 0) {
+ ALOGE("%s: actualDurationNanos must be positive", __FUNCTION__);
+ return EINVAL;
+ }
+ int64_t now = elapsedRealtimeNano();
+ mActualDurationsNanos.push_back(actualDurationNanos);
+ mTimestampsNanos.push_back(now);
+
+ /**
+ * Use current sample to determine the rate limit. We can pick a shorter rate limit
+ * if any sample underperformed, however, it could be the lower level system is slow
+ * to react. So here we explicitly choose the rate limit with the latest sample.
+ */
+ int64_t rateLimit = actualDurationNanos > mTargetDurationNanos ? mPreferredRateNanos
+ : 10 * mPreferredRateNanos;
+ if (now - mLastUpdateTimestamp <= rateLimit) return 0;
+
+ binder::Status ret =
+ mHintSession->reportActualWorkDuration(mActualDurationsNanos, mTimestampsNanos);
+ mActualDurationsNanos.clear();
+ mTimestampsNanos.clear();
+ if (!ret.isOk()) {
+ ALOGE("%s: HintSession reportActualWorkDuration failed: %s", __FUNCTION__,
+ ret.exceptionMessage().c_str());
+ return EPIPE;
+ }
+ mLastUpdateTimestamp = now;
+ return 0;
+}
+
+// ===================================== C API
+APerformanceHintManager* APerformanceHint_getManager() {
+ return APerformanceHintManager::getInstance();
+}
+
+APerformanceHintSession* APerformanceHint_createSession(APerformanceHintManager* manager,
+ const int32_t* threadIds, size_t size,
+ int64_t initialTargetWorkDurationNanos) {
+ return manager->createSession(threadIds, size, initialTargetWorkDurationNanos);
+}
+
+int64_t APerformanceHint_getPreferredUpdateRateNanos(APerformanceHintManager* manager) {
+ return manager->getPreferredRateNanos();
+}
+
+int APerformanceHint_updateTargetWorkDuration(APerformanceHintSession* session,
+ int64_t targetDurationNanos) {
+ return session->updateTargetWorkDuration(targetDurationNanos);
+}
+
+int APerformanceHint_reportActualWorkDuration(APerformanceHintSession* session,
+ int64_t actualDurationNanos) {
+ return session->reportActualWorkDuration(actualDurationNanos);
+}
+
+void APerformanceHint_closeSession(APerformanceHintSession* session) {
+ delete session;
+}
+
+void APerformanceHint_setIHintManagerForTesting(void* iManager) {
+ delete gHintManagerForTesting;
+ gHintManagerForTesting = nullptr;
+ gIHintManagerForTesting = static_cast<IHintManager*>(iManager);
+}
diff --git a/native/android/tests/performance_hint/Android.bp b/native/android/tests/performance_hint/Android.bp
new file mode 100644
index 0000000..fdc1bc6
--- /dev/null
+++ b/native/android/tests/performance_hint/Android.bp
@@ -0,0 +1,65 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+cc_test {
+ name: "PerformanceHintNativeTestCases",
+
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+
+ srcs: ["PerformanceHintNativeTest.cpp"],
+
+ shared_libs: [
+ "libandroid",
+ "liblog",
+ "libbinder",
+ "libpowermanager",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libbase",
+ "libgmock",
+ "libgtest",
+ ],
+ stl: "c++_shared",
+
+ test_suites: [
+ "device-tests",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ header_libs: [
+ "libandroid_headers_private",
+ ],
+}
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
new file mode 100644
index 0000000..284e9ee
--- /dev/null
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PerformanceHintNativeTest"
+
+#include <android/os/IHintManager.h>
+#include <android/os/IHintSession.h>
+#include <binder/IBinder.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <performance_hint_private.h>
+#include <memory>
+#include <vector>
+
+using android::binder::Status;
+using android::os::IHintManager;
+using android::os::IHintSession;
+
+using namespace android;
+using namespace testing;
+
+class MockIHintManager : public IHintManager {
+public:
+ MOCK_METHOD(Status, createHintSession,
+ (const ::android::sp<::android::IBinder>& token, const ::std::vector<int32_t>& tids,
+ int64_t durationNanos, ::android::sp<::android::os::IHintSession>* _aidl_return),
+ (override));
+ MOCK_METHOD(Status, getHintSessionPreferredRate, (int64_t * _aidl_return), (override));
+ MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+};
+
+class MockIHintSession : public IHintSession {
+public:
+ MOCK_METHOD(Status, updateTargetWorkDuration, (int64_t targetDurationNanos), (override));
+ MOCK_METHOD(Status, reportActualWorkDuration,
+ (const ::std::vector<int64_t>& actualDurationNanos,
+ const ::std::vector<int64_t>& timeStampNanos),
+ (override));
+ MOCK_METHOD(Status, close, (), (override));
+ MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+};
+
+class PerformanceHintTest : public Test {
+public:
+ void SetUp() override {
+ mMockIHintManager = new StrictMock<MockIHintManager>();
+ APerformanceHint_setIHintManagerForTesting(mMockIHintManager);
+ }
+
+ void TearDown() override {
+ mMockIHintManager = nullptr;
+ // Destroys MockIHintManager.
+ APerformanceHint_setIHintManagerForTesting(nullptr);
+ }
+
+ APerformanceHintManager* createManager() {
+ EXPECT_CALL(*mMockIHintManager, getHintSessionPreferredRate(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(123L), Return(Status())));
+ return APerformanceHint_getManager();
+ }
+
+ StrictMock<MockIHintManager>* mMockIHintManager = nullptr;
+};
+
+TEST_F(PerformanceHintTest, TestGetPreferredUpdateRateNanos) {
+ APerformanceHintManager* manager = createManager();
+ int64_t preferredUpdateRateNanos = APerformanceHint_getPreferredUpdateRateNanos(manager);
+ EXPECT_EQ(123L, preferredUpdateRateNanos);
+}
+
+TEST_F(PerformanceHintTest, TestSession) {
+ APerformanceHintManager* manager = createManager();
+
+ std::vector<int32_t> tids;
+ tids.push_back(1);
+ tids.push_back(2);
+ int64_t targetDuration = 56789L;
+
+ StrictMock<MockIHintSession>* iSession = new StrictMock<MockIHintSession>();
+ sp<IHintSession> session_sp(iSession);
+
+ EXPECT_CALL(*mMockIHintManager, createHintSession(_, Eq(tids), Eq(targetDuration), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(std::move(session_sp)), Return(Status())));
+
+ APerformanceHintSession* session =
+ APerformanceHint_createSession(manager, tids.data(), tids.size(), targetDuration);
+ ASSERT_TRUE(session);
+
+ int64_t targetDurationNanos = 10;
+ EXPECT_CALL(*iSession, updateTargetWorkDuration(Eq(targetDurationNanos))).Times(Exactly(1));
+ int result = APerformanceHint_updateTargetWorkDuration(session, targetDurationNanos);
+ EXPECT_EQ(0, result);
+
+ usleep(2); // Sleep for longer than preferredUpdateRateNanos.
+ int64_t actualDurationNanos = 20;
+ std::vector<int64_t> actualDurations;
+ actualDurations.push_back(20);
+ EXPECT_CALL(*iSession, reportActualWorkDuration(Eq(actualDurations), _)).Times(Exactly(1));
+ result = APerformanceHint_reportActualWorkDuration(session, actualDurationNanos);
+ EXPECT_EQ(0, result);
+
+ result = APerformanceHint_updateTargetWorkDuration(session, -1L);
+ EXPECT_EQ(EINVAL, result);
+ result = APerformanceHint_reportActualWorkDuration(session, -1L);
+ EXPECT_EQ(EINVAL, result);
+
+ EXPECT_CALL(*iSession, close()).Times(Exactly(1));
+ APerformanceHint_closeSession(session);
+}
diff --git a/packages/Shell/res/values-mr/strings.xml b/packages/Shell/res/values-mr/strings.xml
index 3842733..8297488 100644
--- a/packages/Shell/res/values-mr/strings.xml
+++ b/packages/Shell/res/values-mr/strings.xml
@@ -31,7 +31,7 @@
<string name="bugreport_confirm" msgid="5917407234515812495">"बग रीपोर्टांमध्ये तुम्ही संवेदनशील (अॅप-वापर आणि स्थान डेटा यासारखा) डेटा म्हणून विचार करता त्या डेटाच्या समावेशासह सिस्टीमच्या विविध लॉग फायलींमधील डेटा असतो. ज्या लोकांवर आणि अॅपवर तुमचा विश्वास आहे केवळ त्यांच्यासह हा बग रीपोर्ट शेअर करा."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"पुन्हा दर्शवू नका"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"बग रीपोर्ट"</string>
- <string name="bugreport_unreadable_text" msgid="586517851044535486">"बग रिपोर्ट फाइल वाचणे शक्य झाले नाही"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"बग अहवाल फाइल वाचणे शक्य झाले नाही"</string>
<string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"झिप फाइल मध्ये बग रिपोर्ट तपशील जोडणे शक्य झाले नाही"</string>
<string name="bugreport_unnamed" msgid="2800582406842092709">"अनामित"</string>
<string name="bugreport_info_action" msgid="2158204228510576227">"तपशील"</string>
diff --git a/packages/SystemUI/res/layout/qs_carrier.xml b/packages/SystemUI/res/layout/qs_carrier.xml
index d4594d1..a854660 100644
--- a/packages/SystemUI/res/layout/qs_carrier.xml
+++ b/packages/SystemUI/res/layout/qs_carrier.xml
@@ -39,6 +39,13 @@
android:singleLine="true"
android:maxEms="7"/>
+ <View
+ android:id="@+id/spacer"
+ android:layout_width="@dimen/qs_carrier_margin_width"
+ android:layout_height="match_parent"
+ android:visibility="gone"
+ />
+
<include
layout="@layout/mobile_signal_group"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7485ef8..ae09f26 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -921,6 +921,7 @@
<dimen name="keyguard_lock_padding">20dp</dimen>
<dimen name="keyguard_indication_margin_bottom">32dp</dimen>
+ <dimen name="lock_icon_margin_bottom">98dp</dimen>
<!-- The text size for battery level -->
<dimen name="battery_level_text_size">12sp</dimen>
@@ -1266,6 +1267,8 @@
<!-- Three privacy items. This value must not be exceeded -->
<dimen name="ongoing_appops_chip_max_width">76dp</dimen>
<dimen name="ongoing_appops_dot_diameter">6dp</dimen>
+ <!-- Total minimum padding to enforce to ensure that the dot can always show -->
+ <dimen name="ongoing_appops_dot_min_padding">20dp</dimen>
<dimen name="ongoing_appops_dialog_side_margins">@dimen/notification_shade_content_margin_horizontal</dimen>
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 8b974b4..9c8582fa 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -72,7 +72,10 @@
*/
@StatusBarComponent.StatusBarScope
public class LockIconViewController extends ViewController<LockIconView> implements Dumpable {
-
+ private static final float sDefaultDensity =
+ (float) DisplayMetrics.DENSITY_DEVICE_STABLE / (float) DisplayMetrics.DENSITY_DEFAULT;
+ private static final int sLockIconRadiusPx = (int) (sDefaultDensity * 36);
+ private static final float sDistAboveKgBottomAreaPx = sDefaultDensity * 12;
private static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES =
new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
@@ -111,9 +114,7 @@
private boolean mHasUdfps;
private float mHeightPixels;
private float mWidthPixels;
- private float mDensity;
- private int mAmbientIndicationHeight; // in pixels
- private int mKgIndicationHeight; // in pixels
+ private int mBottomPadding; // in pixels
private boolean mShowUnlockIcon;
private boolean mShowLockIcon;
@@ -318,11 +319,8 @@
final DisplayMetrics metrics = mView.getContext().getResources().getDisplayMetrics();
mWidthPixels = metrics.widthPixels;
mHeightPixels = metrics.heightPixels;
- mDensity = metrics.density;
- mKgIndicationHeight = mView.getContext().getResources().getDimensionPixelSize(
- R.dimen.keyguard_indication_margin_bottom)
- + mView.getContext().getResources().getDimensionPixelSize(
- R.dimen.keyguard_indication_bottom_padding);
+ mBottomPadding = mView.getContext().getResources().getDimensionPixelSize(
+ R.dimen.lock_icon_margin_bottom);
updateLockIconLocation();
}
@@ -332,26 +330,15 @@
mView.setCenterLocation(new PointF(props.sensorLocationX, props.sensorLocationY),
props.sensorRadius);
} else {
- final float distAboveKgBottomArea = 12 * mDensity;
- final float radius = 36 * mDensity;
- final int kgBottomAreaHeight = Math.max(mKgIndicationHeight, mAmbientIndicationHeight);
mView.setCenterLocation(
new PointF(mWidthPixels / 2,
- mHeightPixels - kgBottomAreaHeight - distAboveKgBottomArea
- - radius / 2), (int) radius);
+ mHeightPixels - mBottomPadding - sDistAboveKgBottomAreaPx
+ - sLockIconRadiusPx), sLockIconRadiusPx);
}
mView.getHitRect(mSensorTouchLocation);
}
- /**
- * Set the location of ambient indication if showing (ie: now playing)
- */
- public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) {
- mAmbientIndicationHeight = ambientIndicationBottomPadding;
- updateLockIconLocation();
- }
-
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("mUdfpsEnrolled: " + mUdfpsEnrolled);
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 9c87fc4..76f30a8 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -100,6 +100,7 @@
import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.statusbar.policy.AccessibilityController;
@@ -362,6 +363,7 @@
@Inject Lazy<EdgeBackGestureHandler.Factory> mEdgeBackGestureHandlerFactoryLazy;
@Inject Lazy<UiEventLogger> mUiEventLogger;
@Inject Lazy<FeatureFlags> mFeatureFlagsLazy;
+ @Inject Lazy<StatusBarContentInsetsProvider> mContentInsetsProviderLazy;
@Inject
public Dependency() {
@@ -578,6 +580,7 @@
mEdgeBackGestureHandlerFactoryLazy::get);
mProviders.put(UiEventLogger.class, mUiEventLogger::get);
mProviders.put(FeatureFlags.class, mFeatureFlagsLazy::get);
+ mProviders.put(StatusBarContentInsetsProvider.class, mContentInsetsProviderLazy::get);
Dependency.setInstance(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
index c241c08..4104e31 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
@@ -64,16 +64,12 @@
}
}
- override fun onStart() {
- super.onStart()
+ override fun onResume() {
+ super.onResume()
parent = requireViewById<ViewGroup>(R.id.global_actions_controls)
parent.alpha = 0f
uiController.show(parent, { finish() }, this)
- }
-
- override fun onResume() {
- super.onResume()
ControlsAnimations.enterAnimation(parent).start()
}
@@ -82,8 +78,8 @@
finish()
}
- override fun onStop() {
- super.onStop()
+ override fun onPause() {
+ super.onPause()
uiController.hide()
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 03a2c84..cd9db61 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -32,6 +32,8 @@
import android.widget.LinearLayout;
import android.widget.Space;
+import androidx.annotation.NonNull;
+
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.R;
@@ -41,6 +43,8 @@
import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.policy.Clock;
+import java.util.List;
+
/**
* View that contains the top-most bits of the QS panel (primarily the status bar with date, time,
* battery, carrier info and privacy icons) and also contains the {@link QuickQSPanel}.
@@ -86,18 +90,13 @@
private float mKeyguardExpansionFraction;
private int mTextColorPrimary = Color.TRANSPARENT;
private int mTopViewMeasureHeight;
- private boolean mProviderModel;
- private final String mMobileSlotName;
- private final String mNoCallingSlotName;
- private final String mCallStrengthSlotName;
+ @NonNull
+ private List<String> mRssiIgnoredSlots;
+ private boolean mIsSingleCarrier;
public QuickStatusBarHeader(Context context, AttributeSet attrs) {
super(context, attrs);
- mMobileSlotName = context.getString(com.android.internal.R.string.status_bar_mobile);
- mNoCallingSlotName = context.getString(com.android.internal.R.string.status_bar_no_calling);
- mCallStrengthSlotName =
- context.getString(com.android.internal.R.string.status_bar_call_strength);
}
/**
@@ -148,9 +147,9 @@
void onAttach(TintedIconManager iconManager,
QSExpansionPathInterpolator qsExpansionPathInterpolator,
- boolean providerModel) {
- mProviderModel = providerModel;
+ List<String> rssiIgnoredSlots) {
mTintedIconManager = iconManager;
+ mRssiIgnoredSlots = rssiIgnoredSlots;
int fillColor = Utils.getColorAttrDefaultColor(getContext(),
android.R.attr.textColorPrimary);
@@ -161,6 +160,11 @@
updateAnimators();
}
+ void setIsSingleCarrier(boolean isSingleCarrier) {
+ mIsSingleCarrier = isSingleCarrier;
+ updateAlphaAnimator();
+ }
+
public QuickQSPanel getHeaderQsPanel() {
return mHeaderQsPanel;
}
@@ -267,39 +271,26 @@
.setListener(new TouchAnimator.ListenerAdapter() {
@Override
public void onAnimationAtEnd() {
- // TODO(b/185580157): Remove the mProviderModel if the mobile slot can be
- // hidden in Provider model.
- if (mProviderModel) {
- mIconContainer.addIgnoredSlot(mNoCallingSlotName);
- mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
- } else {
- mIconContainer.addIgnoredSlot(mMobileSlotName);
+ super.onAnimationAtEnd();
+ if (!mIsSingleCarrier) {
+ mIconContainer.addIgnoredSlots(mRssiIgnoredSlots);
}
}
@Override
public void onAnimationStarted() {
- if (mProviderModel) {
- mIconContainer.addIgnoredSlot(mNoCallingSlotName);
- mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
- } else {
- mIconContainer.addIgnoredSlot(mMobileSlotName);
- }
-
setSeparatorVisibility(false);
+ if (!mIsSingleCarrier) {
+ mIconContainer.addIgnoredSlots(mRssiIgnoredSlots);
+ }
}
@Override
public void onAnimationAtStart() {
super.onAnimationAtStart();
- if (mProviderModel) {
- mIconContainer.removeIgnoredSlot(mNoCallingSlotName);
- mIconContainer.removeIgnoredSlot(mCallStrengthSlotName);
- } else {
- mIconContainer.removeIgnoredSlot(mMobileSlotName);
- }
-
setSeparatorVisibility(mShowClockIconsSeparator);
+ // In QQS we never ignore RSSI.
+ mIconContainer.removeIgnoredSlots(mRssiIgnoredSlots);
}
});
mAlphaAnimator = builder.build();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index fcf1302..b8b7f42 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -43,7 +43,6 @@
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.util.ViewController;
-import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
@@ -76,6 +75,9 @@
private boolean mMicCameraIndicatorsEnabled;
private boolean mLocationIndicatorsEnabled;
private boolean mPrivacyChipLogged;
+ private final String mCameraSlot;
+ private final String mMicSlot;
+ private final String mLocationSlot;
private SysuiColorExtractor mColorExtractor;
private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
@@ -104,8 +106,7 @@
}
private void update() {
- StatusIconContainer iconContainer = mView.requireViewById(R.id.statusIcons);
- iconContainer.setIgnoredSlots(getIgnoredIconSlots());
+ updatePrivacyIconSlots();
setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty());
}
};
@@ -154,7 +155,7 @@
mClockView = mView.findViewById(R.id.clock);
mIconContainer = mView.findViewById(R.id.statusIcons);
- mIconManager = new StatusBarIconController.TintedIconManager(mIconContainer, mFeatureFlags);
+ mIconManager = new StatusBarIconController.TintedIconManager(mIconContainer, featureFlags);
mDemoModeReceiver = new ClockDemoModeReceiver(mClockView);
mColorExtractor = colorExtractor;
mOnColorsChangedListener = (extractor, which) -> {
@@ -162,6 +163,10 @@
mClockView.onColorsChanged(lightTheme);
};
mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener);
+
+ mCameraSlot = getResources().getString(com.android.internal.R.string.status_bar_camera);
+ mMicSlot = getResources().getString(com.android.internal.R.string.status_bar_microphone);
+ mLocationSlot = getResources().getString(com.android.internal.R.string.status_bar_location);
}
@Override
@@ -172,14 +177,30 @@
mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
// Ignore privacy icons because they show in the space above QQS
- mIconContainer.setIgnoredSlots(getIgnoredIconSlots());
+ updatePrivacyIconSlots();
mIconContainer.setShouldRestrictIcons(false);
mStatusBarIconController.addIconGroup(mIconManager);
setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
- mView.onAttach(mIconManager, mQSExpansionPathInterpolator,
- mFeatureFlags.isCombinedStatusBarSignalIconsEnabled());
+ mView.setIsSingleCarrier(mQSCarrierGroupController.isSingleCarrier());
+ mQSCarrierGroupController
+ .setOnSingleCarrierChangedListener(mView::setIsSingleCarrier);
+
+ List<String> rssiIgnoredSlots;
+
+ if (mFeatureFlags.isCombinedStatusBarSignalIconsEnabled()) {
+ rssiIgnoredSlots = List.of(
+ getResources().getString(com.android.internal.R.string.status_bar_no_calling),
+ getResources().getString(com.android.internal.R.string.status_bar_call_strength)
+ );
+ } else {
+ rssiIgnoredSlots = List.of(
+ getResources().getString(com.android.internal.R.string.status_bar_mobile)
+ );
+ }
+
+ mView.onAttach(mIconManager, mQSExpansionPathInterpolator, rssiIgnoredSlots);
mDemoModeController.addCallback(mDemoModeReceiver);
}
@@ -189,6 +210,7 @@
mColorExtractor.removeOnColorsChangedListener(mOnColorsChangedListener);
mPrivacyChip.setOnClickListener(null);
mStatusBarIconController.removeIconGroup(mIconManager);
+ mQSCarrierGroupController.setOnSingleCarrierChangedListener(null);
mDemoModeController.removeCallback(mDemoModeReceiver);
setListening(false);
}
@@ -236,21 +258,25 @@
mView.setChipVisibility(chipVisible);
}
- private List<String> getIgnoredIconSlots() {
- ArrayList<String> ignored = new ArrayList<>();
+ private void updatePrivacyIconSlots() {
if (getChipEnabled()) {
if (mMicCameraIndicatorsEnabled) {
- ignored.add(mView.getResources().getString(
- com.android.internal.R.string.status_bar_camera));
- ignored.add(mView.getResources().getString(
- com.android.internal.R.string.status_bar_microphone));
+ mIconContainer.addIgnoredSlot(mCameraSlot);
+ mIconContainer.addIgnoredSlot(mMicSlot);
+ } else {
+ mIconContainer.removeIgnoredSlot(mCameraSlot);
+ mIconContainer.removeIgnoredSlot(mMicSlot);
}
if (mLocationIndicatorsEnabled) {
- ignored.add(mView.getResources().getString(
- com.android.internal.R.string.status_bar_location));
+ mIconContainer.addIgnoredSlot(mLocationSlot);
+ } else {
+ mIconContainer.removeIgnoredSlot(mLocationSlot);
}
+ } else {
+ mIconContainer.removeIgnoredSlot(mCameraSlot);
+ mIconContainer.removeIgnoredSlot(mMicSlot);
+ mIconContainer.removeIgnoredSlot(mLocationSlot);
}
- return ignored;
}
private boolean getChipEnabled() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
index d6fa216..32ac733 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
@@ -25,6 +25,8 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.VisibleForTesting;
+
import com.android.settingslib.Utils;
import com.android.settingslib.graph.SignalDrawable;
import com.android.systemui.R;
@@ -37,8 +39,10 @@
private TextView mCarrierText;
private ImageView mMobileSignal;
private ImageView mMobileRoaming;
+ private View mSpacer;
private CellSignalState mLastSignalState;
private boolean mProviderModelInitialized = false;
+ private boolean mIsSingleCarrier;
public QSCarrier(Context context) {
super(context);
@@ -63,18 +67,25 @@
mMobileRoaming = findViewById(R.id.mobile_roaming);
mMobileSignal = findViewById(R.id.mobile_signal);
mCarrierText = findViewById(R.id.qs_carrier_text);
+ mSpacer = findViewById(R.id.spacer);
}
/**
* Update the state of this view
* @param state the current state of the signal for this view
+ * @param isSingleCarrier whether there is a single carrier being shown in the container
* @return true if the state was actually changed
*/
- public boolean updateState(CellSignalState state) {
- if (Objects.equals(state, mLastSignalState)) return false;
+ public boolean updateState(CellSignalState state, boolean isSingleCarrier) {
+ if (Objects.equals(state, mLastSignalState) && isSingleCarrier == mIsSingleCarrier) {
+ return false;
+ }
mLastSignalState = state;
- mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE);
- if (state.visible) {
+ mIsSingleCarrier = isSingleCarrier;
+ final boolean visible = state.visible && !isSingleCarrier;
+ mMobileGroup.setVisibility(visible ? View.VISIBLE : View.GONE);
+ mSpacer.setVisibility(isSingleCarrier ? View.VISIBLE : View.GONE);
+ if (visible) {
mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
ColorStateList colorStateList = Utils.getColorAttr(mContext,
android.R.attr.textColorPrimary);
@@ -125,6 +136,11 @@
com.android.settingslib.R.string.not_default_data_content_description));
}
+ @VisibleForTesting
+ View getRSSIView() {
+ return mMobileGroup;
+ }
+
public void setCarrierText(CharSequence text) {
mCarrierText.setText(text);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
index f23c058..67c4d33 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -37,6 +37,7 @@
import com.android.settingslib.AccessibilityContentDescriptions;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -62,7 +63,8 @@
private final NetworkController mNetworkController;
private final CarrierTextManager mCarrierTextManager;
private final TextView mNoSimTextView;
- private final H mMainHandler;
+ // Non final for testing
+ private H mMainHandler;
private final Callback mCallback;
private boolean mListening;
private final CellSignalState[] mInfos =
@@ -74,6 +76,11 @@
private final boolean mProviderModel;
private final CarrierConfigTracker mCarrierConfigTracker;
+ private boolean mIsSingleCarrier;
+ private OnSingleCarrierChangedListener mOnSingleCarrierChangedListener;
+
+ private final SlotIndexResolver mSlotIndexResolver;
+
private final NetworkController.SignalCallback mSignalCallback =
new NetworkController.SignalCallback() {
@Override
@@ -207,7 +214,8 @@
@Background Handler bgHandler, @Main Looper mainLooper,
NetworkController networkController,
CarrierTextManager.Builder carrierTextManagerBuilder, Context context,
- CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags) {
+ CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags,
+ SlotIndexResolver slotIndexResolver) {
if (featureFlags.isCombinedStatusBarSignalIconsEnabled()) {
mProviderModel = true;
@@ -222,6 +230,7 @@
.setShowMissingSim(false)
.build();
mCarrierConfigTracker = carrierConfigTracker;
+ mSlotIndexResolver = slotIndexResolver;
View.OnClickListener onClickListener = v -> {
if (!v.isVisibleToUser()) {
return;
@@ -256,6 +265,7 @@
.toString();
mCarrierGroups[i].setOnClickListener(onClickListener);
}
+ mIsSingleCarrier = computeIsSingleCarrier();
view.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@@ -272,10 +282,24 @@
@VisibleForTesting
protected int getSlotIndex(int subscriptionId) {
- return SubscriptionManager.getSlotIndex(subscriptionId);
+ return mSlotIndexResolver.getSlotIndex(subscriptionId);
}
- private boolean isSingleCarrier() {
+ /**
+ * Sets a {@link OnSingleCarrierChangedListener}.
+ *
+ * This will get notified when the number of carriers changes between 1 and "not one".
+ * @param listener
+ */
+ public void setOnSingleCarrierChangedListener(OnSingleCarrierChangedListener listener) {
+ mOnSingleCarrierChangedListener = listener;
+ }
+
+ public boolean isSingleCarrier() {
+ return mIsSingleCarrier;
+ }
+
+ private boolean computeIsSingleCarrier() {
int carrierCount = 0;
for (int i = 0; i < SIM_SLOTS; i++) {
@@ -315,7 +339,9 @@
return;
}
- if (isSingleCarrier()) {
+ boolean singleCarrier = computeIsSingleCarrier();
+
+ if (singleCarrier) {
for (int i = 0; i < SIM_SLOTS; i++) {
if (mInfos[i].visible
&& mInfos[i].mobileSignalIconId == R.drawable.ic_qs_sim_card) {
@@ -326,7 +352,7 @@
}
for (int i = 0; i < SIM_SLOTS; i++) {
- mCarrierGroups[i].updateState(mInfos[i]);
+ mCarrierGroups[i].updateState(mInfos[i], singleCarrier);
}
mCarrierDividers[0].setVisibility(
@@ -337,6 +363,12 @@
mCarrierDividers[1].setVisibility(
(mInfos[1].visible && mInfos[2].visible)
|| (mInfos[0].visible && mInfos[2].visible) ? View.VISIBLE : View.GONE);
+ if (mIsSingleCarrier != singleCarrier) {
+ mIsSingleCarrier = singleCarrier;
+ if (mOnSingleCarrierChangedListener != null) {
+ mOnSingleCarrierChangedListener.onSingleCarrierChanged(singleCarrier);
+ }
+ }
}
@MainThread
@@ -433,12 +465,14 @@
private final Context mContext;
private final CarrierConfigTracker mCarrierConfigTracker;
private final FeatureFlags mFeatureFlags;
+ private final SlotIndexResolver mSlotIndexResolver;
@Inject
public Builder(ActivityStarter activityStarter, @Background Handler handler,
@Main Looper looper, NetworkController networkController,
CarrierTextManager.Builder carrierTextControllerBuilder, Context context,
- CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags) {
+ CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags,
+ SlotIndexResolver slotIndexResolver) {
mActivityStarter = activityStarter;
mHandler = handler;
mLooper = looper;
@@ -447,6 +481,7 @@
mContext = context;
mCarrierConfigTracker = carrierConfigTracker;
mFeatureFlags = featureFlags;
+ mSlotIndexResolver = slotIndexResolver;
}
public Builder setQSCarrierGroup(QSCarrierGroup view) {
@@ -457,7 +492,43 @@
public QSCarrierGroupController build() {
return new QSCarrierGroupController(mView, mActivityStarter, mHandler, mLooper,
mNetworkController, mCarrierTextControllerBuilder, mContext,
- mCarrierConfigTracker, mFeatureFlags);
+ mCarrierConfigTracker, mFeatureFlags, mSlotIndexResolver);
+ }
+ }
+
+ /**
+ * Notify when the state changes from 1 carrier to "not one" and viceversa
+ */
+ @FunctionalInterface
+ public interface OnSingleCarrierChangedListener {
+ void onSingleCarrierChanged(boolean isSingleCarrier);
+ }
+
+ /**
+ * Interface for resolving slot index from subscription ID.
+ */
+ @FunctionalInterface
+ public interface SlotIndexResolver {
+ /**
+ * Get slot index for given sub id.
+ */
+ int getSlotIndex(int subscriptionId);
+ }
+
+ /**
+ * Default implementation for {@link SlotIndexResolver}.
+ *
+ * It retrieves the slot index using {@link SubscriptionManager#getSlotIndex}.
+ */
+ @SysUISingleton
+ public static class SubscriptionManagerSlotIndexResolver implements SlotIndexResolver {
+
+ @Inject
+ public SubscriptionManagerSlotIndexResolver() {}
+
+ @Override
+ public int getSlotIndex(int subscriptionId) {
+ return SubscriptionManager.getSlotIndex(subscriptionId);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index 34aac49..4d63349 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -33,6 +33,7 @@
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QuickQSPanel;
import com.android.systemui.qs.QuickStatusBarHeader;
+import com.android.systemui.qs.carrier.QSCarrierGroupController;
import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.statusbar.phone.MultiUserSwitch;
@@ -146,4 +147,8 @@
return useQsMediaPlayer(context);
}
+ /** */
+ @Binds
+ QSCarrierGroupController.SlotIndexResolver provideSlotIndexResolver(
+ QSCarrierGroupController.SubscriptionManagerSlotIndexResolver impl);
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index de3be78..6d1bbee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -89,5 +89,4 @@
/** */
@Binds
QSHost provideQsHost(QSTileHost controllerImpl);
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 4d15a0a..120121c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -885,7 +885,10 @@
mStatusBarKeyguardViewManager.showBouncerMessage(helpString,
mInitialTextColorState);
} else if (mKeyguardUpdateMonitor.isScreenOn()) {
- if (biometricSourceType == BiometricSourceType.FACE && shouldSuppressFaceMsg()) {
+ if (biometricSourceType == BiometricSourceType.FACE
+ && shouldSuppressFaceMsgAndShowTryFingerprintMsg()) {
+ // suggest trying fingerprint
+ showTransientIndication(R.string.keyguard_try_fingerprint);
return;
}
showTransientIndication(helpString, false /* isError */, showSwipeToUnlock);
@@ -903,9 +906,11 @@
return;
}
if (biometricSourceType == BiometricSourceType.FACE
- && shouldSuppressFaceMsg()
+ && shouldSuppressFaceMsgAndShowTryFingerprintMsg()
&& !mStatusBarKeyguardViewManager.isBouncerShowing()
&& mKeyguardUpdateMonitor.isScreenOn()) {
+ // suggest trying fingerprint
+ showTransientIndication(R.string.keyguard_try_fingerprint);
return;
}
if (msgId == FaceManager.FACE_ERROR_TIMEOUT) {
@@ -956,7 +961,7 @@
|| msgId == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED);
}
- private boolean shouldSuppressFaceMsg() {
+ private boolean shouldSuppressFaceMsgAndShowTryFingerprintMsg() {
// For dual biometric, don't show face auth messages
return mKeyguardUpdateMonitor.isFingerprintDetectionRunning()
&& mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index 5f10e55..29cfb07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -18,6 +18,8 @@
import android.animation.Animator
import android.annotation.UiThread
+import android.graphics.Point
+import android.graphics.Rect
import android.util.Log
import android.view.Gravity
import android.view.View
@@ -31,9 +33,16 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState.SHADE
import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
-import com.android.systemui.statusbar.phone.StatusBarLocationPublisher
-import com.android.systemui.statusbar.phone.StatusBarMarginUpdatedListener
+import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener
+import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
+import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.leak.RotationUtils
+import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
+import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE
+import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE
+import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
+import com.android.systemui.util.leak.RotationUtils.Rotation
import java.lang.IllegalStateException
import java.util.concurrent.Executor
@@ -58,7 +67,8 @@
class PrivacyDotViewController @Inject constructor(
@Main private val mainExecutor: Executor,
private val stateController: StatusBarStateController,
- private val locationPublisher: StatusBarLocationPublisher,
+ private val configurationController: ConfigurationController,
+ private val contentInsetsProvider: StatusBarContentInsetsProvider,
private val animationScheduler: SystemStatusAnimationScheduler
) {
private var sbHeightPortrait = 0
@@ -84,18 +94,27 @@
// Privacy dots are created in ScreenDecoration's UiThread, which is not the main thread
private var uiExecutor: DelayableExecutor? = null
- private val marginListener: StatusBarMarginUpdatedListener =
- object : StatusBarMarginUpdatedListener {
- override fun onStatusBarMarginUpdated(marginLeft: Int, marginRight: Int) {
- setStatusBarMargins(marginLeft, marginRight)
- }
- }
-
private val views: Sequence<View>
get() = if (!this::tl.isInitialized) sequenceOf() else sequenceOf(tl, tr, br, bl)
init {
- locationPublisher.addCallback(marginListener)
+ contentInsetsProvider.addCallback(object : StatusBarContentInsetsChangedListener {
+ override fun onStatusBarContentInsetsChanged() {
+ dlog("onStatusBarContentInsetsChanged: ")
+ setNewLayoutRects()
+ }
+ })
+ configurationController.addCallback(object : ConfigurationController.ConfigurationListener {
+ override fun onLayoutDirectionChanged(isRtl: Boolean) {
+ synchronized(this) {
+ val corner = selectDesignatedCorner(nextViewState.rotation, isRtl)
+ nextViewState = nextViewState.copy(
+ layoutRtl = isRtl,
+ designatedCorner = corner
+ )
+ }
+ }
+ })
stateController.addCallback(object : StatusBarStateController.StateListener {
override fun onExpandedChanged(isExpanded: Boolean) {
@@ -123,16 +142,19 @@
fun setNewRotation(rot: Int) {
dlog("updateRotation: $rot")
+ val isRtl: Boolean
synchronized(lock) {
if (rot == nextViewState.rotation) {
return
}
+
+ isRtl = nextViewState.layoutRtl
}
// If we rotated, hide all dotes until the next state resolves
setCornerVisibilities(View.INVISIBLE)
- val newCorner = selectDesignatedCorner(rot)
+ val newCorner = selectDesignatedCorner(rot, isRtl)
val index = newCorner.cornerIndex()
val h = when (rot) {
@@ -222,15 +244,77 @@
}
}
+ @UiThread
+ private fun setCornerSizes(state: ViewState) {
+ // StatusBarContentInsetsProvider can tell us the location of the privacy indicator dot
+ // in every rotation. The only thing we need to check is rtl
+ val rtl = state.layoutRtl
+ val size = Point()
+ tl.context.display.getRealSize(size)
+ val currentRotation = RotationUtils.getExactRotation(tl.context)
+
+ val displayWidth: Int
+ val displayHeight: Int
+ if (currentRotation == ROTATION_LANDSCAPE || currentRotation == ROTATION_SEASCAPE) {
+ displayWidth = size.y
+ displayHeight = size.x
+ } else {
+ displayWidth = size.x
+ displayHeight = size.y
+ }
+
+ var rot = activeRotationForCorner(tl, rtl)
+ var contentInsets = state.contentRectForRotation(rot)
+ (tl.layoutParams as FrameLayout.LayoutParams).apply {
+ height = contentInsets.height()
+ if (rtl) {
+ width = contentInsets.left
+ } else {
+ width = displayHeight - contentInsets.right
+ }
+ }
+
+ rot = activeRotationForCorner(tr, rtl)
+ contentInsets = state.contentRectForRotation(rot)
+ (tr.layoutParams as FrameLayout.LayoutParams).apply {
+ height = contentInsets.height()
+ if (rtl) {
+ width = contentInsets.left
+ } else {
+ width = displayWidth - contentInsets.right
+ }
+ }
+
+ rot = activeRotationForCorner(br, rtl)
+ contentInsets = state.contentRectForRotation(rot)
+ (br.layoutParams as FrameLayout.LayoutParams).apply {
+ height = contentInsets.height()
+ if (rtl) {
+ width = contentInsets.left
+ } else {
+ width = displayHeight - contentInsets.right
+ }
+ }
+
+ rot = activeRotationForCorner(bl, rtl)
+ contentInsets = state.contentRectForRotation(rot)
+ (bl.layoutParams as FrameLayout.LayoutParams).apply {
+ height = contentInsets.height()
+ if (rtl) {
+ width = contentInsets.left
+ } else {
+ width = displayWidth - contentInsets.right
+ }
+ }
+ }
+
// Designated view will be the one at statusbar's view.END
@UiThread
- private fun selectDesignatedCorner(r: Int): View? {
+ private fun selectDesignatedCorner(r: Int, isRtl: Boolean): View? {
if (!this::tl.isInitialized) {
return null
}
- val isRtl = tl.isLayoutRtl
-
return when (r) {
0 -> if (isRtl) tl else tr
1 -> if (isRtl) tr else br
@@ -282,6 +366,17 @@
return modded
}
+ @Rotation
+ private fun activeRotationForCorner(corner: View, rtl: Boolean): Int {
+ // Each corner will only be visible in a single rotation, based on rtl
+ return when (corner) {
+ tr -> if (rtl) ROTATION_LANDSCAPE else ROTATION_NONE
+ tl -> if (rtl) ROTATION_NONE else ROTATION_SEASCAPE
+ br -> if (rtl) ROTATION_UPSIDE_DOWN else ROTATION_LANDSCAPE
+ else /* bl */ -> if (rtl) ROTATION_SEASCAPE else ROTATION_UPSIDE_DOWN
+ }
+ }
+
private fun widthForCorner(corner: Int, left: Int, right: Int): Int {
return when (corner) {
TOP_LEFT, BOTTOM_LEFT -> left
@@ -303,15 +398,32 @@
bl = bottomLeft
br = bottomRight
- val dc = selectDesignatedCorner(0)
+ val rtl = configurationController.isLayoutRtl
+ val dc = selectDesignatedCorner(0, rtl)
+
val index = dc.cornerIndex()
mainExecutor.execute {
animationScheduler.addCallback(systemStatusAnimationCallback)
}
+ val left = contentInsetsProvider.getStatusBarContentInsetsForRotation(ROTATION_SEASCAPE)
+ val top = contentInsetsProvider.getStatusBarContentInsetsForRotation(ROTATION_NONE)
+ val right = contentInsetsProvider.getStatusBarContentInsetsForRotation(ROTATION_LANDSCAPE)
+ val bottom = contentInsetsProvider
+ .getStatusBarContentInsetsForRotation(ROTATION_UPSIDE_DOWN)
+
synchronized(lock) {
- nextViewState = nextViewState.copy(designatedCorner = dc, cornerIndex = index)
+ nextViewState = nextViewState.copy(
+ viewInitialized = true,
+ designatedCorner = dc,
+ cornerIndex = index,
+ seascapeRect = left,
+ portraitRect = top,
+ landscapeRect = right,
+ upsideDownRect = bottom,
+ layoutRtl = rtl
+ )
}
}
@@ -324,19 +436,6 @@
sbHeightLandscape = landscape
}
- /**
- * The dot view containers will fill the margin in order to position the dots correctly
- *
- * @param left the space between the status bar contents and the left side of the screen
- * @param right space between the status bar contents and the right side of the screen
- */
- private fun setStatusBarMargins(left: Int, right: Int) {
- dlog("setStatusBarMargins l=$left r=$right")
- synchronized(lock) {
- nextViewState = nextViewState.copy(marginLeft = left, marginRight = right)
- }
- }
-
private fun updateStatusBarState() {
synchronized(lock) {
nextViewState = nextViewState.copy(shadeExpanded = isShadeInQs())
@@ -377,6 +476,11 @@
@UiThread
private fun resolveState(state: ViewState) {
dlog("resolveState $state")
+ if (!state.viewInitialized) {
+ dlog("resolveState: view is not initialized. skipping.")
+ return
+ }
+
if (state == currentViewState) {
dlog("resolveState: skipping")
return
@@ -387,23 +491,15 @@
updateRotations(state.rotation)
}
- if (state.height != currentViewState.height) {
- updateHeights(state.rotation)
- }
-
- if (state.marginLeft != currentViewState.marginLeft ||
- state.marginRight != currentViewState.marginRight) {
- updateCornerSizes(state.marginLeft, state.marginRight, state.rotation)
+ if (state.needsLayout(currentViewState)) {
+ setCornerSizes(state)
+ views.forEach { it.requestLayout() }
}
if (state.designatedCorner != currentViewState.designatedCorner) {
updateDesignatedCorner(state.designatedCorner, state.shouldShowDot())
}
- if (state.needsLayout(currentViewState)) {
- views.forEach { it.requestLayout() }
- }
-
val shouldShow = state.shouldShowDot()
if (shouldShow != currentViewState.shouldShowDot()) {
if (shouldShow && state.designatedCorner != null) {
@@ -441,6 +537,30 @@
}
return -1
}
+
+ // Returns [left, top, right, bottom] aka [seascape, none, landscape, upside-down]
+ private fun getLayoutRects(): List<Rect> {
+ val left = contentInsetsProvider.getStatusBarContentInsetsForRotation(ROTATION_SEASCAPE)
+ val top = contentInsetsProvider.getStatusBarContentInsetsForRotation(ROTATION_NONE)
+ val right = contentInsetsProvider.getStatusBarContentInsetsForRotation(ROTATION_LANDSCAPE)
+ val bottom = contentInsetsProvider
+ .getStatusBarContentInsetsForRotation(ROTATION_UPSIDE_DOWN)
+
+ return listOf(left, top, right, bottom)
+ }
+
+ private fun setNewLayoutRects() {
+ val rects = getLayoutRects()
+
+ synchronized(lock) {
+ nextViewState = nextViewState.copy(
+ seascapeRect = rects[0],
+ portraitRect = rects[1],
+ landscapeRect = rects[2],
+ upsideDownRect = rects[3]
+ )
+ }
+ }
}
private fun dlog(s: String) {
@@ -461,7 +581,7 @@
const val BOTTOM_LEFT = 3
private const val DURATION = 160L
private const val TAG = "PrivacyDotViewController"
-private const val DEBUG = true
+private const val DEBUG = false
private const val DEBUG_VERBOSE = false
private fun Int.toGravity(): Int {
@@ -485,14 +605,20 @@
}
private data class ViewState(
+ val viewInitialized: Boolean = false,
+
val systemPrivacyEventIsActive: Boolean = false,
val shadeExpanded: Boolean = false,
val qsExpanded: Boolean = false,
+ val portraitRect: Rect? = null,
+ val landscapeRect: Rect? = null,
+ val upsideDownRect: Rect? = null,
+ val seascapeRect: Rect? = null,
+ val layoutRtl: Boolean = false,
+
val rotation: Int = 0,
val height: Int = 0,
- val marginLeft: Int = 0,
- val marginRight: Int = 0,
val cornerIndex: Int = -1,
val designatedCorner: View? = null
) {
@@ -502,7 +628,20 @@
fun needsLayout(other: ViewState): Boolean {
return rotation != other.rotation ||
- marginRight != other.marginRight ||
- height != other.height
+ layoutRtl != other.layoutRtl ||
+ portraitRect != other.portraitRect ||
+ landscapeRect != other.landscapeRect ||
+ upsideDownRect != other.upsideDownRect ||
+ seascapeRect != other.seascapeRect
+ }
+
+ fun contentRectForRotation(@Rotation rot: Int): Rect {
+ return when (rot) {
+ ROTATION_NONE -> portraitRect!!
+ ROTATION_LANDSCAPE -> landscapeRect!!
+ ROTATION_UPSIDE_DOWN -> upsideDownRect!!
+ ROTATION_SEASCAPE -> seascapeRect!!
+ else -> throw IllegalArgumentException("not a rotation ($rot)")
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 93166f3..73bb6cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -287,7 +287,7 @@
mGroupExpansionChanging = true;
final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry);
boolean nowExpanded = mGroupExpansionManager.toggleGroupExpansion(mEntry);
- mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
+ mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER,
nowExpanded);
onExpansionChanged(true /* userAction */, wasExpanded);
@@ -310,7 +310,7 @@
setUserExpanded(nowExpanded);
}
notifyHeightChanged(true);
- mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
+ mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded);
MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_EXPANDER,
nowExpanded);
}
@@ -3064,7 +3064,7 @@
}
public interface OnExpandClickListener {
- void onExpandClicked(NotificationEntry clickedEntry, boolean nowExpanded);
+ void onExpandClicked(NotificationEntry clickedEntry, View clickedView, boolean nowExpanded);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 23e3742..d0507e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -394,7 +394,8 @@
ambientState.getExpansionFraction(), true /* notification */);
}
- if (view.mustStayOnScreen() && viewState.yTranslation >= 0) {
+ if (ambientState.isShadeExpanded() && view.mustStayOnScreen()
+ && viewState.yTranslation >= 0) {
// Even if we're not scrolled away we're in view and we're also not in the
// shelf. We can relax the constraints and let us scroll off the top!
float end = viewState.yTranslation + viewState.height + ambientState.getStackY();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 1361acb..596fce5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -37,7 +37,6 @@
import android.view.ViewStub;
import android.widget.LinearLayout;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -76,9 +75,9 @@
public static final int FADE_IN_DURATION = 320;
public static final int FADE_IN_DELAY = 50;
private PhoneStatusBarView mStatusBar;
- private StatusBarStateController mStatusBarStateController;
- private KeyguardStateController mKeyguardStateController;
- private NetworkController mNetworkController;
+ private final StatusBarStateController mStatusBarStateController;
+ private final KeyguardStateController mKeyguardStateController;
+ private final NetworkController mNetworkController;
private LinearLayout mSystemIconArea;
private View mClockView;
private View mOngoingCallChip;
@@ -86,15 +85,16 @@
private View mCenteredIconArea;
private int mDisabled1;
private int mDisabled2;
- private StatusBar mStatusBarComponent;
+ private final StatusBar mStatusBarComponent;
private DarkIconManager mDarkIconManager;
private View mOperatorNameFrame;
- private CommandQueue mCommandQueue;
- private OngoingCallController mOngoingCallController;
+ private final CommandQueue mCommandQueue;
+ private final OngoingCallController mOngoingCallController;
private final SystemStatusAnimationScheduler mAnimationScheduler;
private final StatusBarLocationPublisher mLocationPublisher;
- private NotificationIconAreaController mNotificationIconAreaController;
private final FeatureFlags mFeatureFlags;
+ private final NotificationIconAreaController mNotificationIconAreaController;
+ private final StatusBarIconController mStatusBarIconController;
private List<String> mBlockedIcons = new ArrayList<>();
@@ -118,23 +118,25 @@
SystemStatusAnimationScheduler animationScheduler,
StatusBarLocationPublisher locationPublisher,
NotificationIconAreaController notificationIconAreaController,
- FeatureFlags featureFlags
+ FeatureFlags featureFlags,
+ StatusBarIconController statusBarIconController,
+ KeyguardStateController keyguardStateController,
+ NetworkController networkController,
+ StatusBarStateController statusBarStateController,
+ StatusBar statusBarComponent,
+ CommandQueue commandQueue
) {
mOngoingCallController = ongoingCallController;
mAnimationScheduler = animationScheduler;
mLocationPublisher = locationPublisher;
mNotificationIconAreaController = notificationIconAreaController;
mFeatureFlags = featureFlags;
- }
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mKeyguardStateController = Dependency.get(KeyguardStateController.class);
- mNetworkController = Dependency.get(NetworkController.class);
- mStatusBarStateController = Dependency.get(StatusBarStateController.class);
- mStatusBarComponent = Dependency.get(StatusBar.class);
- mCommandQueue = Dependency.get(CommandQueue.class);
+ mStatusBarIconController = statusBarIconController;
+ mKeyguardStateController = keyguardStateController;
+ mNetworkController = networkController;
+ mStatusBarStateController = statusBarStateController;
+ mStatusBarComponent = statusBarComponent;
+ mCommandQueue = commandQueue;
}
@Override
@@ -160,7 +162,7 @@
mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_alarm_clock));
mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_call_strength));
mDarkIconManager.setBlockList(mBlockedIcons);
- Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
+ mStatusBarIconController.addIconGroup(mDarkIconManager);
mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
mClockView = mStatusBar.findViewById(R.id.clock);
mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip);
@@ -199,7 +201,7 @@
@Override
public void onDestroyView() {
super.onDestroyView();
- Dependency.get(StatusBarIconController.class).removeIconGroup(mDarkIconManager);
+ mStatusBarIconController.removeIconGroup(mDarkIconManager);
if (mNetworkController.hasEmergencyCryptKeeperText()) {
mNetworkController.removeCallback(mSignalCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
index b148eeb..07618da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
@@ -18,6 +18,7 @@
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.os.LocaleList
+import android.view.View.LAYOUT_DIRECTION_RTL
import com.android.systemui.statusbar.policy.ConfigurationController
import java.util.ArrayList
@@ -33,6 +34,7 @@
private var uiMode: Int = 0
private var localeList: LocaleList? = null
private val context: Context
+ private var layoutDirection: Int
init {
val currentConfig = context.resources.configuration
@@ -44,6 +46,7 @@
Configuration.UI_MODE_TYPE_CAR
uiMode = currentConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
localeList = currentConfig.locales
+ layoutDirection = currentConfig.layoutDirection
}
override fun notifyThemeChanged() {
@@ -101,6 +104,13 @@
}
}
+ if (layoutDirection != newConfig.layoutDirection) {
+ layoutDirection = newConfig.layoutDirection
+ listeners.filterForEach({ this.listeners.contains(it) }) {
+ it.onLayoutDirectionChanged(layoutDirection == LAYOUT_DIRECTION_RTL)
+ }
+ }
+
if (lastConfig.updateFrom(newConfig) and ActivityInfo.CONFIG_ASSETS_PATHS != 0) {
listeners.filterForEach({ this.listeners.contains(it) }) {
it.onOverlayChanged()
@@ -116,6 +126,10 @@
override fun removeCallback(listener: ConfigurationController.ConfigurationListener) {
listeners.remove(listener)
}
+
+ override fun isLayoutRtl(): Boolean {
+ return layoutDirection == LAYOUT_DIRECTION_RTL
+ }
}
// This could be done with a Collection.filter and Collection.forEach, but Collection.filter
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 3c1892d..7958301 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -3635,8 +3635,6 @@
public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) {
if (mAmbientIndicationBottomPadding != ambientIndicationBottomPadding) {
mAmbientIndicationBottomPadding = ambientIndicationBottomPadding;
- mLockIconViewController.setAmbientIndicationBottomPadding(
- mAmbientIndicationBottomPadding);
updateMaxDisplayedNotifications(true);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 31a432e..c300b11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -23,6 +23,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Point;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.EventLog;
@@ -52,6 +53,7 @@
private static final boolean DEBUG = StatusBar.DEBUG;
private static final boolean DEBUG_GESTURES = false;
private final CommandQueue mCommandQueue;
+ private final StatusBarContentInsetsProvider mContentInsetsProvider;
StatusBar mBar;
@@ -85,11 +87,10 @@
private int mCutoutSideNudge = 0;
private boolean mHeadsUpVisible;
- private int mRoundedCornerPadding = 0;
-
public PhoneStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
mCommandQueue = Dependency.get(CommandQueue.class);
+ mContentInsetsProvider = Dependency.get(StatusBarContentInsetsProvider.class);
}
public void setBar(StatusBar bar) {
@@ -305,8 +306,6 @@
public void updateResources() {
mCutoutSideNudge = getResources().getDimensionPixelSize(
R.dimen.display_cutout_margin_consumption);
- mRoundedCornerPadding = getResources().getDimensionPixelSize(
- R.dimen.rounded_corner_content_padding);
updateStatusBarHeight();
}
@@ -341,8 +340,7 @@
private void updateLayoutForCutout() {
updateStatusBarHeight();
updateCutoutLocation(StatusBarWindowView.cornerCutoutMargins(mDisplayCutout, getDisplay()));
- updateSafeInsets(StatusBarWindowView.statusBarCornerCutoutMargins(mDisplayCutout,
- getDisplay(), mRotationOrientation, mStatusBarHeight));
+ updateSafeInsets();
}
private void updateCutoutLocation(Pair<Integer, Integer> cornerCutoutMargins) {
@@ -370,15 +368,18 @@
lp.height = bounds.height();
}
- private void updateSafeInsets(Pair<Integer, Integer> cornerCutoutMargins) {
- // Depending on our rotation, we may have to work around a cutout in the middle of the view,
- // or letterboxing from the right or left sides.
+ private void updateSafeInsets() {
+ Rect contentRect = mContentInsetsProvider
+ .getStatusBarContentInsetsForRotation(RotationUtils.getExactRotation(getContext()));
- Pair<Integer, Integer> padding =
- StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
- mDisplayCutout, cornerCutoutMargins, mRoundedCornerPadding);
+ Point size = new Point();
+ getDisplay().getRealSize(size);
- setPadding(padding.first, getPaddingTop(), padding.second, getPaddingBottom());
+ setPadding(
+ contentRect.left,
+ getPaddingTop(),
+ size.x - contentRect.right,
+ getPaddingBottom());
}
public void setHeadsUpVisible(boolean headsUpVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 63390f9..742eae3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -441,6 +441,7 @@
private final OngoingCallController mOngoingCallController;
private final SystemStatusAnimationScheduler mAnimationScheduler;
private final StatusBarLocationPublisher mStatusBarLocationPublisher;
+ private final StatusBarIconController mStatusBarIconController;
// expanded notifications
// the sliding/resizing panel within the notification window
@@ -803,6 +804,7 @@
OngoingCallController ongoingCallController,
SystemStatusAnimationScheduler animationScheduler,
StatusBarLocationPublisher locationPublisher,
+ StatusBarIconController statusBarIconController,
LockscreenShadeTransitionController lockscreenShadeTransitionController,
FeatureFlags featureFlags,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
@@ -889,6 +891,7 @@
mOngoingCallController = ongoingCallController;
mAnimationScheduler = animationScheduler;
mStatusBarLocationPublisher = locationPublisher;
+ mStatusBarIconController = statusBarIconController;
mFeatureFlags = featureFlags;
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
@@ -1190,7 +1193,14 @@
mAnimationScheduler,
mStatusBarLocationPublisher,
mNotificationIconAreaController,
- mFeatureFlags),
+ mFeatureFlags,
+ mStatusBarIconController,
+ mKeyguardStateController,
+ mNetworkController,
+ mStatusBarStateController,
+ this,
+ mCommandQueue
+ ),
CollapsedStatusBarFragment.TAG)
.commit();
@@ -2737,6 +2747,11 @@
mScrimController.dump(fd, pw, args);
}
+ if (mLightRevealScrim != null) {
+ pw.println(
+ "mLightRevealScrim.getRevealAmount(): " + mLightRevealScrim.getRevealAmount());
+ }
+
if (mStatusBarKeyguardViewManager != null) {
mStatusBarKeyguardViewManager.dump(pw);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index edcf261..fe1f63a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.content.res.Resources
import android.graphics.Rect
+import android.util.Log
import android.util.Pair
import android.view.DisplayCutout
import android.view.View.LAYOUT_DIRECTION_RTL
@@ -155,13 +156,30 @@
val dc = context.display.cutout
val currentRotation = RotationUtils.getExactRotation(context)
+ val isRtl = rotatedResources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL
+ val roundedCornerPadding = rotatedResources
+ .getDimensionPixelSize(R.dimen.rounded_corner_content_padding)
+ val minDotWidth = rotatedResources
+ .getDimensionPixelSize(R.dimen.ongoing_appops_dot_min_padding)
+
+ val minLeft: Int
+ val minRight: Int
+ if (isRtl) {
+ minLeft = max(minDotWidth, roundedCornerPadding)
+ minRight = roundedCornerPadding
+ } else {
+ minLeft = roundedCornerPadding
+ minRight = max(minDotWidth, roundedCornerPadding)
+ }
+
return calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
dc,
windowManager.maximumWindowMetrics,
rotatedResources.getDimensionPixelSize(R.dimen.status_bar_height),
- rotatedResources.getDimensionPixelSize(R.dimen.rounded_corner_content_padding))
+ minLeft,
+ minRight)
}
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
@@ -212,9 +230,12 @@
* Calculates the exact left and right positions for the status bar contents for the given
* rotation
*
- * @param rot rotation for which to query the margins
- * @param context systemui context
- * @param rotatedResources resources constructed with the proper orientation set
+ * @param currentRotation current device rotation
+ * @param targetRotation rotation for which to calculate the status bar content rect
+ * @param displayCutout [DisplayCutout] for the curren display. possibly null
+ * @param windowMetrics [WindowMetrics] for the current window
+ * @param statusBarHeight height of the status bar for the target rotation
+ * @param roundedCornerPadding from rounded_corner_content_padding
*
* @see [RotationUtils#getResourcesForRotation]
*/
@@ -224,7 +245,8 @@
displayCutout: DisplayCutout?,
windowMetrics: WindowMetrics,
statusBarHeight: Int,
- roundedCornerPadding: Int
+ minLeft: Int,
+ minRight: Int
): Rect {
/*
TODO: Check if this is ever used for devices with no rounded corners
@@ -242,7 +264,8 @@
rotZeroBounds.bottom,
currentBounds.width(),
currentBounds.height(),
- roundedCornerPadding,
+ minLeft,
+ minRight,
targetRotation,
currentRotation)
@@ -256,7 +279,10 @@
* @param sbHeight appropriate status bar height for this rotation
* @param width display width calculated for ROTATION_NONE
* @param height display height calculated for ROTATION_NONE
- * @param roundedCornerPadding rounded_corner_content_padding dimension
+ * @param cWidth display width in our current rotation
+ * @param cHeight display height in our current rotation
+ * @param minLeft the minimum padding to enforce on the left
+ * @param minRight the minimum padding to enforce on the right
* @param targetRotation the rotation for which to calculate margins
* @param currentRotation the rotation from which the display cutout was generated
*
@@ -270,7 +296,8 @@
height: Int,
cWidth: Int,
cHeight: Int,
- roundedCornerPadding: Int,
+ minLeft: Int,
+ minRight: Int,
@Rotation targetRotation: Int,
@Rotation currentRotation: Int
): Rect {
@@ -279,9 +306,9 @@
val cutoutRects = dc?.boundingRects
if (cutoutRects == null || cutoutRects.isEmpty()) {
- return Rect(roundedCornerPadding,
+ return Rect(minLeft,
0,
- logicalDisplayWidth - roundedCornerPadding,
+ logicalDisplayWidth - minRight,
sbHeight)
}
@@ -294,8 +321,8 @@
// Size of the status bar window for the given rotation relative to our exact rotation
val sbRect = sbRect(relativeRotation, sbHeight, Pair(cWidth, cHeight))
- var leftMargin = roundedCornerPadding
- var rightMargin = roundedCornerPadding
+ var leftMargin = minLeft
+ var rightMargin = minRight
for (cutoutRect in cutoutRects) {
// There is at most one non-functional area per short edge of the device. So if the status
// bar doesn't share a short edge with the cutout, we can ignore its insets because there
@@ -306,11 +333,11 @@
if (cutoutRect.touchesLeftEdge(relativeRotation, cWidth, cHeight)) {
- val l = max(roundedCornerPadding, cutoutRect.logicalWidth(relativeRotation))
+ val l = max(minLeft, cutoutRect.logicalWidth(relativeRotation))
leftMargin = max(l, leftMargin)
} else if (cutoutRect.touchesRightEdge(relativeRotation, cWidth, cHeight)) {
val logicalWidth = cutoutRect.logicalWidth(relativeRotation)
- rightMargin = max(roundedCornerPadding, logicalWidth)
+ rightMargin = max(minRight, logicalWidth)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index aa58527..47deb1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -24,12 +24,14 @@
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemClock;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
import android.util.Log;
import android.util.Slog;
+import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.widget.TextView;
@@ -394,8 +396,10 @@
}
@Override
- public void onExpandClicked(NotificationEntry clickedEntry, boolean nowExpanded) {
+ public void onExpandClicked(NotificationEntry clickedEntry, View clickedView,
+ boolean nowExpanded) {
mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
+ mStatusBar.wakeUpIfDozing(SystemClock.uptimeMillis(), clickedView, "NOTIFICATION_CLICK");
if (nowExpanded) {
if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
mShadeTransitionController.goToLockedShade(clickedEntry.getRow());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index 64a497d..3292934 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -245,8 +245,19 @@
* @param slotName name of the icon slot to remove from the ignored list
*/
public void removeIgnoredSlot(String slotName) {
- if (mIgnoredSlots.contains(slotName)) {
- mIgnoredSlots.remove(slotName);
+ mIgnoredSlots.remove(slotName);
+
+ requestLayout();
+ }
+
+ /**
+ * Remove a list of slots from the list of ignored icon slots.
+ * It will then be shown when set to visible by the {@link StatusBarIconController}.
+ * @param slots name of the icon slots to remove from the ignored list
+ */
+ public void removeIgnoredSlots(List<String> slots) {
+ for (String slot : slots) {
+ mIgnoredSlots.remove(slot);
}
requestLayout();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 716d1db..b6e8bd8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -89,6 +89,7 @@
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
@@ -216,6 +217,7 @@
OngoingCallController ongoingCallController,
SystemStatusAnimationScheduler animationScheduler,
StatusBarLocationPublisher locationPublisher,
+ StatusBarIconController statusBarIconController,
LockscreenShadeTransitionController transitionController,
FeatureFlags featureFlags,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
@@ -305,6 +307,7 @@
ongoingCallController,
animationScheduler,
locationPublisher,
+ statusBarIconController,
transitionController,
featureFlags,
keyguardUnlockAnimationController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
index c2bd87c..3a05ec7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
@@ -30,6 +30,9 @@
/** Alert controller of a change in between light and dark themes. */
void notifyThemeChanged();
+ /** Query the current configuration's layout direction */
+ boolean isLayoutRtl();
+
interface ConfigurationListener {
default void onConfigChanged(Configuration newConfig) {}
default void onDensityOrFontScaleChanged() {}
@@ -38,5 +41,6 @@
default void onUiModeChanged() {}
default void onThemeChanged() {}
default void onLocaleListChanged() {}
+ default void onLayoutDirectionChanged(boolean isLayoutRtl) {}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index f140eb8..35360bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -48,6 +48,7 @@
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.`when`
+import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -98,6 +99,10 @@
private lateinit var controller: QuickStatusBarHeaderController
+ private lateinit var cameraSlotName: String
+ private lateinit var microphoneSlotName: String
+ private lateinit var locationSlotName: String
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -108,6 +113,13 @@
`when`(view.isAttachedToWindow).thenReturn(true)
`when`(view.context).thenReturn(context)
+ cameraSlotName = mContext.resources.getString(
+ com.android.internal.R.string.status_bar_camera)
+ microphoneSlotName = mContext.resources.getString(
+ com.android.internal.R.string.status_bar_microphone)
+ locationSlotName = mContext.resources.getString(
+ com.android.internal.R.string.status_bar_location)
+
controller = QuickStatusBarHeaderController(
view,
privacyItemController,
@@ -141,10 +153,9 @@
controller.init()
- val captor = argumentCaptor<List<String>>()
- verify(iconContainer).setIgnoredSlots(capture(captor))
-
- assertThat(captor.value).isEmpty()
+ verify(iconContainer).removeIgnoredSlot(cameraSlotName)
+ verify(iconContainer).removeIgnoredSlot(microphoneSlotName)
+ verify(iconContainer).removeIgnoredSlot(locationSlotName)
}
@Test
@@ -153,15 +164,9 @@
controller.init()
- val captor = argumentCaptor<List<String>>()
- verify(iconContainer).setIgnoredSlots(capture(captor))
-
- val cameraString = mContext.resources.getString(
- com.android.internal.R.string.status_bar_camera)
- val micString = mContext.resources.getString(
- com.android.internal.R.string.status_bar_microphone)
-
- assertThat(captor.value).containsExactly(cameraString, micString)
+ verify(iconContainer).addIgnoredSlot(cameraSlotName)
+ verify(iconContainer).addIgnoredSlot(microphoneSlotName)
+ verify(iconContainer).removeIgnoredSlot(locationSlotName)
}
@Test
@@ -170,13 +175,9 @@
controller.init()
- val captor = argumentCaptor<List<String>>()
- verify(iconContainer).setIgnoredSlots(capture(captor))
-
- val locationString = mContext.resources.getString(
- com.android.internal.R.string.status_bar_location)
-
- assertThat(captor.value).containsExactly(locationString)
+ verify(iconContainer).removeIgnoredSlot(cameraSlotName)
+ verify(iconContainer).removeIgnoredSlot(microphoneSlotName)
+ verify(iconContainer).addIgnoredSlot(locationSlotName)
}
@Test
@@ -185,17 +186,9 @@
controller.init()
- val captor = argumentCaptor<List<String>>()
- verify(iconContainer).setIgnoredSlots(capture(captor))
-
- val cameraString = mContext.resources.getString(
- com.android.internal.R.string.status_bar_camera)
- val micString = mContext.resources.getString(
- com.android.internal.R.string.status_bar_microphone)
- val locationString = mContext.resources.getString(
- com.android.internal.R.string.status_bar_location)
-
- assertThat(captor.value).containsExactly(cameraString, micString, locationString)
+ verify(iconContainer).addIgnoredSlot(cameraSlotName)
+ verify(iconContainer).addIgnoredSlot(microphoneSlotName)
+ verify(iconContainer).addIgnoredSlot(locationSlotName)
}
@Test
@@ -210,6 +203,71 @@
verify(privacyDialogController).showDialog(any(Context::class.java))
}
+ @Test
+ fun testSingleCarrierListenerAttachedOnInit() {
+ controller.init()
+
+ verify(qsCarrierGroupController).setOnSingleCarrierChangedListener(any())
+ }
+
+ @Test
+ fun testSingleCarrierSetOnViewOnInit_false() {
+ `when`(qsCarrierGroupController.isSingleCarrier).thenReturn(false)
+ controller.init()
+
+ verify(view).setIsSingleCarrier(false)
+ }
+
+ @Test
+ fun testSingleCarrierSetOnViewOnInit_true() {
+ `when`(qsCarrierGroupController.isSingleCarrier).thenReturn(true)
+ controller.init()
+
+ verify(view).setIsSingleCarrier(true)
+ }
+
+ @Test
+ fun testRSSISlot_notCombined() {
+ `when`(featureFlags.isCombinedStatusBarSignalIconsEnabled).thenReturn(false)
+ controller.init()
+
+ val captor = argumentCaptor<List<String>>()
+ verify(view).onAttach(any(), any(), capture(captor))
+
+ assertThat(captor.value).containsExactly(
+ mContext.getString(com.android.internal.R.string.status_bar_mobile)
+ )
+ }
+
+ @Test
+ fun testRSSISlot_combined() {
+ `when`(featureFlags.isCombinedStatusBarSignalIconsEnabled).thenReturn(true)
+ controller.init()
+
+ val captor = argumentCaptor<List<String>>()
+ verify(view).onAttach(any(), any(), capture(captor))
+
+ assertThat(captor.value).containsExactly(
+ mContext.getString(com.android.internal.R.string.status_bar_no_calling),
+ mContext.getString(com.android.internal.R.string.status_bar_call_strength)
+ )
+ }
+
+ @Test
+ fun testSingleCarrierCallback() {
+ controller.init()
+ reset(view)
+
+ val captor = argumentCaptor<QSCarrierGroupController.OnSingleCarrierChangedListener>()
+ verify(qsCarrierGroupController).setOnSingleCarrierChangedListener(capture(captor))
+
+ captor.value.onSingleCarrierChanged(true)
+ verify(view).setIsSingleCarrier(true)
+
+ captor.value.onSingleCarrierChanged(false)
+ verify(view).setIsSingleCarrier(false)
+ }
+
private fun stubViews() {
`when`(view.findViewById<View>(anyInt())).thenReturn(mockView)
`when`(view.findViewById<QSCarrierGroup>(R.id.carrier_group)).thenReturn(qsCarrierGroup)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index 9ae6069..72c7ddd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -17,15 +17,18 @@
package com.android.systemui.qs.carrier;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
+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.reset;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.os.Handler;
-import android.telephony.SubscriptionManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
@@ -46,9 +49,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -70,8 +71,18 @@
private CarrierTextManager mCarrierTextManager;
@Mock
private CarrierConfigTracker mCarrierConfigTracker;
+ @Mock
+ private QSCarrier mQSCarrier1;
+ @Mock
+ private QSCarrier mQSCarrier2;
+ @Mock
+ private QSCarrier mQSCarrier3;
private TestableLooper mTestableLooper;
@Mock private FeatureFlags mFeatureFlags;
+ @Mock
+ private QSCarrierGroupController.OnSingleCarrierChangedListener mOnSingleCarrierChangedListener;
+
+ private FakeSlotIndexResolver mSlotIndexResolver;
@Before
public void setup() throws Exception {
@@ -96,29 +107,31 @@
.setListening(any(CarrierTextManager.CarrierTextCallback.class));
when(mQSCarrierGroup.getNoSimTextView()).thenReturn(new TextView(mContext));
- when(mQSCarrierGroup.getCarrier1View()).thenReturn(mock(QSCarrier.class));
- when(mQSCarrierGroup.getCarrier2View()).thenReturn(mock(QSCarrier.class));
- when(mQSCarrierGroup.getCarrier3View()).thenReturn(mock(QSCarrier.class));
+ when(mQSCarrierGroup.getCarrier1View()).thenReturn(mQSCarrier1);
+ when(mQSCarrierGroup.getCarrier2View()).thenReturn(mQSCarrier2);
+ when(mQSCarrierGroup.getCarrier3View()).thenReturn(mQSCarrier3);
when(mQSCarrierGroup.getCarrierDivider1()).thenReturn(new View(mContext));
when(mQSCarrierGroup.getCarrierDivider2()).thenReturn(new View(mContext));
+ mSlotIndexResolver = new FakeSlotIndexResolver();
+
mQSCarrierGroupController = new QSCarrierGroupController.Builder(
mActivityStarter, handler, TestableLooper.get(this).getLooper(),
mNetworkController, mCarrierTextControllerBuilder, mContext, mCarrierConfigTracker,
- mFeatureFlags)
+ mFeatureFlags, mSlotIndexResolver)
.setQSCarrierGroup(mQSCarrierGroup)
.build();
mQSCarrierGroupController.setListening(true);
}
+ @Test
+ public void testInitiallyMultiCarrier() {
+ assertFalse(mQSCarrierGroupController.isSingleCarrier());
+ }
+
@Test // throws no Exception
public void testUpdateCarrierText_sameLengths() {
- QSCarrierGroupController spiedCarrierGroupController =
- Mockito.spy(mQSCarrierGroupController);
- when(spiedCarrierGroupController.getSlotIndex(anyInt())).thenAnswer(
- (Answer<Integer>) invocationOnMock -> invocationOnMock.getArgument(0));
-
// listOfCarriers length 1, subscriptionIds length 1, anySims false
CarrierTextManager.CarrierTextCallbackInfo
c1 = new CarrierTextManager.CarrierTextCallbackInfo(
@@ -160,11 +173,6 @@
@Test // throws no Exception
public void testUpdateCarrierText_differentLength() {
- QSCarrierGroupController spiedCarrierGroupController =
- Mockito.spy(mQSCarrierGroupController);
- when(spiedCarrierGroupController.getSlotIndex(anyInt())).thenAnswer(
- (Answer<Integer>) invocationOnMock -> invocationOnMock.getArgument(0));
-
// listOfCarriers length 2, subscriptionIds length 1, anySims false
CarrierTextManager.CarrierTextCallbackInfo
c1 = new CarrierTextManager.CarrierTextCallbackInfo(
@@ -205,10 +213,7 @@
@Test // throws no Exception
public void testUpdateCarrierText_invalidSim() {
- QSCarrierGroupController spiedCarrierGroupController =
- Mockito.spy(mQSCarrierGroupController);
- when(spiedCarrierGroupController.getSlotIndex(anyInt())).thenReturn(
- SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+ mSlotIndexResolver.overrideInvalid = true;
CarrierTextManager.CarrierTextCallbackInfo
c4 = new CarrierTextManager.CarrierTextCallbackInfo(
@@ -222,6 +227,8 @@
@Test // throws no Exception
public void testSetMobileDataIndicators_invalidSim() {
+ mSlotIndexResolver.overrideInvalid = true;
+
MobileDataIndicators indicators = new MobileDataIndicators(
mock(NetworkController.IconState.class),
mock(NetworkController.IconState.class),
@@ -242,4 +249,137 @@
mTestableLooper.processAllMessages();
assertEquals(View.GONE, mQSCarrierGroup.getNoSimTextView().getVisibility());
}
+
+ @Test
+ public void testListenerNotCalledOnRegistreation() {
+ mQSCarrierGroupController
+ .setOnSingleCarrierChangedListener(mOnSingleCarrierChangedListener);
+
+ verify(mOnSingleCarrierChangedListener, never()).onSingleCarrierChanged(anyBoolean());
+ }
+
+ @Test
+ public void testSingleCarrier() {
+ // Only one element in the info
+ CarrierTextManager.CarrierTextCallbackInfo
+ info = new CarrierTextManager.CarrierTextCallbackInfo(
+ "",
+ new CharSequence[]{""},
+ true,
+ new int[]{0},
+ false /* airplaneMode */);
+
+ mCallback.updateCarrierInfo(info);
+ mTestableLooper.processAllMessages();
+
+ verify(mQSCarrier1).updateState(any(), eq(true));
+ verify(mQSCarrier2).updateState(any(), eq(true));
+ verify(mQSCarrier3).updateState(any(), eq(true));
+ }
+
+ @Test
+ public void testMultiCarrier() {
+ // More than one element in the info
+ CarrierTextManager.CarrierTextCallbackInfo
+ info = new CarrierTextManager.CarrierTextCallbackInfo(
+ "",
+ new CharSequence[]{"", ""},
+ true,
+ new int[]{0, 1},
+ false /* airplaneMode */);
+
+ mCallback.updateCarrierInfo(info);
+ mTestableLooper.processAllMessages();
+
+ verify(mQSCarrier1).updateState(any(), eq(false));
+ verify(mQSCarrier2).updateState(any(), eq(false));
+ verify(mQSCarrier3).updateState(any(), eq(false));
+ }
+
+ @Test
+ public void testSingleMultiCarrierSwitch() {
+ CarrierTextManager.CarrierTextCallbackInfo
+ singleCarrierInfo = new CarrierTextManager.CarrierTextCallbackInfo(
+ "",
+ new CharSequence[]{""},
+ true,
+ new int[]{0},
+ false /* airplaneMode */);
+
+ CarrierTextManager.CarrierTextCallbackInfo
+ multiCarrierInfo = new CarrierTextManager.CarrierTextCallbackInfo(
+ "",
+ new CharSequence[]{"", ""},
+ true,
+ new int[]{0, 1},
+ false /* airplaneMode */);
+
+ mCallback.updateCarrierInfo(singleCarrierInfo);
+ mTestableLooper.processAllMessages();
+
+ mQSCarrierGroupController
+ .setOnSingleCarrierChangedListener(mOnSingleCarrierChangedListener);
+ reset(mOnSingleCarrierChangedListener);
+
+ mCallback.updateCarrierInfo(multiCarrierInfo);
+ mTestableLooper.processAllMessages();
+ verify(mOnSingleCarrierChangedListener).onSingleCarrierChanged(false);
+
+ mCallback.updateCarrierInfo(singleCarrierInfo);
+ mTestableLooper.processAllMessages();
+ verify(mOnSingleCarrierChangedListener).onSingleCarrierChanged(true);
+ }
+
+ @Test
+ public void testNoCallbackIfSingleCarrierDoesntChange() {
+ CarrierTextManager.CarrierTextCallbackInfo
+ singleCarrierInfo = new CarrierTextManager.CarrierTextCallbackInfo(
+ "",
+ new CharSequence[]{""},
+ true,
+ new int[]{0},
+ false /* airplaneMode */);
+
+ mCallback.updateCarrierInfo(singleCarrierInfo);
+ mTestableLooper.processAllMessages();
+
+ mQSCarrierGroupController
+ .setOnSingleCarrierChangedListener(mOnSingleCarrierChangedListener);
+
+ mCallback.updateCarrierInfo(singleCarrierInfo);
+ mTestableLooper.processAllMessages();
+
+ verify(mOnSingleCarrierChangedListener, never()).onSingleCarrierChanged(anyBoolean());
+ }
+
+ @Test
+ public void testNoCallbackIfMultiCarrierDoesntChange() {
+ CarrierTextManager.CarrierTextCallbackInfo
+ multiCarrierInfo = new CarrierTextManager.CarrierTextCallbackInfo(
+ "",
+ new CharSequence[]{"", ""},
+ true,
+ new int[]{0, 1},
+ false /* airplaneMode */);
+
+ mCallback.updateCarrierInfo(multiCarrierInfo);
+ mTestableLooper.processAllMessages();
+
+ mQSCarrierGroupController
+ .setOnSingleCarrierChangedListener(mOnSingleCarrierChangedListener);
+
+ mCallback.updateCarrierInfo(multiCarrierInfo);
+ mTestableLooper.processAllMessages();
+
+ verify(mOnSingleCarrierChangedListener, never()).onSingleCarrierChanged(anyBoolean());
+ }
+
+ private class FakeSlotIndexResolver implements QSCarrierGroupController.SlotIndexResolver {
+ public boolean overrideInvalid;
+
+ @Override
+ public int getSlotIndex(int subscriptionId) {
+ return overrideInvalid ? -1 : subscriptionId;
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
index 9bee47d..93c75ad8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.qs.carrier;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -23,6 +24,7 @@
import android.testing.TestableLooper;
import android.util.FeatureFlagUtils;
import android.view.LayoutInflater;
+import android.view.View;
import androidx.test.filters.SmallTest;
@@ -64,25 +66,64 @@
public void testUpdateState_first() {
CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
- assertTrue(mQSCarrier.updateState(c));
+ assertTrue(mQSCarrier.updateState(c, false));
}
@Test
public void testUpdateState_same() {
CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
- assertTrue(mQSCarrier.updateState(c));
- assertFalse(mQSCarrier.updateState(c));
+ assertTrue(mQSCarrier.updateState(c, false));
+ assertFalse(mQSCarrier.updateState(c, false));
}
@Test
public void testUpdateState_changed() {
CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
- assertTrue(mQSCarrier.updateState(c));
+ assertTrue(mQSCarrier.updateState(c, false));
CellSignalState other = c.changeVisibility(false);
- assertTrue(mQSCarrier.updateState(other));
+ assertTrue(mQSCarrier.updateState(other, false));
+ }
+
+ @Test
+ public void testUpdateState_singleCarrier_first() {
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+
+ assertTrue(mQSCarrier.updateState(c, true));
+ }
+
+ @Test
+ public void testUpdateState_singleCarrier_noShowIcon() {
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+
+ mQSCarrier.updateState(c, true);
+
+ assertEquals(View.GONE, mQSCarrier.getRSSIView().getVisibility());
+ }
+
+ @Test
+ public void testUpdateState_multiCarrier_showIcon() {
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+
+ mQSCarrier.updateState(c, false);
+
+ assertEquals(View.VISIBLE, mQSCarrier.getRSSIView().getVisibility());
+ }
+
+ @Test
+ public void testUpdateState_changeSingleMultiSingle() {
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+
+ mQSCarrier.updateState(c, true);
+ assertEquals(View.GONE, mQSCarrier.getRSSIView().getVisibility());
+
+ mQSCarrier.updateState(c, false);
+ assertEquals(View.VISIBLE, mQSCarrier.getRSSIView().getVisibility());
+
+ mQSCarrier.updateState(c, true);
+ assertEquals(View.GONE, mQSCarrier.getRSSIView().getVisibility());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 9640423..bc53074 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -39,10 +39,14 @@
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.NetworkController;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
@@ -54,10 +58,17 @@
private NotificationIconAreaController mMockNotificationAreaController;
private View mNotificationAreaInner;
- private StatusBarStateController mStatusBarStateController;
private OngoingCallController mOngoingCallController;
private SystemStatusAnimationScheduler mAnimationScheduler;
private StatusBarLocationPublisher mLocationPublisher;
+ // Set in instantiate()
+ private StatusBarIconController mStatusBarIconController;
+ private NetworkController mNetworkController;
+ private StatusBarStateController mStatusBarStateController;
+ private KeyguardStateController mKeyguardStateController;
+
+ private final StatusBar mStatusBar = mock(StatusBar.class);
+ private final CommandQueue mCommandQueue = mock(CommandQueue.class);
public CollapsedStatusBarFragmentTest() {
super(CollapsedStatusBarFragment.class);
@@ -65,12 +76,8 @@
@Before
public void setup() {
- StatusBar statusBar = mock(StatusBar.class);
- mDependency.injectTestDependency(StatusBar.class, statusBar);
- mStatusBarStateController = mDependency
- .injectMockDependency(StatusBarStateController.class);
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
- when(statusBar.getPanelController()).thenReturn(
+ when(mStatusBar.getPanelController()).thenReturn(
mock(NotificationPanelViewController.class));
}
@@ -202,6 +209,7 @@
mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
}
+ @Ignore("b/192618546")
@Test
public void testOnDozingChanged() throws Exception {
mFragments.dispatchResume();
@@ -225,13 +233,23 @@
mOngoingCallController = mock(OngoingCallController.class);
mAnimationScheduler = mock(SystemStatusAnimationScheduler.class);
mLocationPublisher = mock(StatusBarLocationPublisher.class);
+ mStatusBarIconController = mock(StatusBarIconController.class);
+ mNetworkController = mock(NetworkController.class);
+ mStatusBarStateController = mock(StatusBarStateController.class);
+ mKeyguardStateController = mock(KeyguardStateController.class);
setUpNotificationIconAreaController();
return new CollapsedStatusBarFragment(
mOngoingCallController,
mAnimationScheduler,
mLocationPublisher,
mMockNotificationAreaController,
- mock(FeatureFlags.class));
+ mock(FeatureFlags.class),
+ mStatusBarIconController,
+ mKeyguardStateController,
+ mNetworkController,
+ mStatusBarStateController,
+ mStatusBar,
+ mCommandQueue);
}
private void setUpNotificationIconAreaController() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
index 4796cd7..10eb71f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
@@ -47,7 +47,8 @@
@Test
fun testGetBoundingRectForPrivacyChipForRotation_noCutout() {
val screenBounds = Rect(0, 0, 1080, 2160)
- val roundedCornerPadding = 20
+ val minLeftPadding = 20
+ val minRightPadding = 20
val sbHeightPortrait = 100
val sbHeightLandscape = 60
val currentRotation = ROTATION_NONE
@@ -64,7 +65,8 @@
null,
windowMetrics,
sbHeightPortrait,
- roundedCornerPadding)
+ minLeftPadding,
+ minRightPadding)
var chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
/* 1080 - 20 (rounded corner) - 30 (chip),
@@ -92,7 +94,8 @@
dc,
windowMetrics,
sbHeightLandscape,
- roundedCornerPadding)
+ minLeftPadding,
+ minRightPadding)
chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
/* 2160 - 20 (rounded corner) - 30 (chip),
@@ -118,7 +121,8 @@
// GIVEN a device in portrait mode with width < height and a display cutout in the top-left
val screenBounds = Rect(0, 0, 1080, 2160)
val dcBounds = Rect(0, 0, 100, 100)
- val roundedCornerPadding = 20
+ val minLeftPadding = 20
+ val minRightPadding = 20
val sbHeightPortrait = 100
val sbHeightLandscape = 60
val currentRotation = ROTATION_NONE
@@ -131,7 +135,7 @@
var targetRotation = ROTATION_NONE
var expectedBounds = Rect(dcBounds.right,
0,
- screenBounds.right - roundedCornerPadding,
+ screenBounds.right - minRightPadding,
sbHeightPortrait)
var bounds = calculateInsetsForRotationWithRotatedResources(
@@ -140,14 +144,15 @@
dc,
windowMetrics,
sbHeightPortrait,
- roundedCornerPadding)
+ minLeftPadding,
+ minRightPadding)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_LANDSCAPE
expectedBounds = Rect(dcBounds.height(),
0,
- screenBounds.height() - roundedCornerPadding,
+ screenBounds.height() - minRightPadding,
sbHeightLandscape)
bounds = calculateInsetsForRotationWithRotatedResources(
@@ -156,16 +161,17 @@
dc,
windowMetrics,
sbHeightLandscape,
- roundedCornerPadding)
+ minLeftPadding,
+ minRightPadding)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
// THEN the side that does NOT share a short side with the display cutout ignores the
// display cutout bounds
targetRotation = ROTATION_UPSIDE_DOWN
- expectedBounds = Rect(roundedCornerPadding,
+ expectedBounds = Rect(minLeftPadding,
0,
- screenBounds.width() - roundedCornerPadding,
+ screenBounds.width() - minRightPadding,
sbHeightPortrait)
bounds = calculateInsetsForRotationWithRotatedResources(
@@ -174,13 +180,14 @@
dc,
windowMetrics,
sbHeightPortrait,
- roundedCornerPadding)
+ minLeftPadding,
+ minRightPadding)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
// Phone in portrait, seascape (rot_270) bounds
targetRotation = ROTATION_SEASCAPE
- expectedBounds = Rect(roundedCornerPadding,
+ expectedBounds = Rect(minLeftPadding,
0,
screenBounds.height() - dcBounds.height(),
sbHeightLandscape)
@@ -191,7 +198,8 @@
dc,
windowMetrics,
sbHeightLandscape,
- roundedCornerPadding)
+ minLeftPadding,
+ minRightPadding)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -205,7 +213,8 @@
val screenBounds = Rect(0, 0, 1080, 2160)
// cutout centered at the top
val dcBounds = Rect(490, 0, 590, 100)
- val roundedCornerPadding = 20
+ val minLeftPadding = 20
+ val minRightPadding = 20
val sbHeightPortrait = 100
val sbHeightLandscape = 60
val currentRotation = ROTATION_NONE
@@ -216,9 +225,9 @@
// THEN only the landscape/seascape rotations should avoid the cutout area because of the
// potential letterboxing
var targetRotation = ROTATION_NONE
- var expectedBounds = Rect(roundedCornerPadding,
+ var expectedBounds = Rect(minLeftPadding,
0,
- screenBounds.right - roundedCornerPadding,
+ screenBounds.right - minRightPadding,
sbHeightPortrait)
var bounds = calculateInsetsForRotationWithRotatedResources(
@@ -227,14 +236,15 @@
dc,
windowMetrics,
sbHeightPortrait,
- roundedCornerPadding)
+ minLeftPadding,
+ minRightPadding)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_LANDSCAPE
expectedBounds = Rect(dcBounds.height(),
0,
- screenBounds.height() - roundedCornerPadding,
+ screenBounds.height() - minRightPadding,
sbHeightLandscape)
bounds = calculateInsetsForRotationWithRotatedResources(
@@ -243,14 +253,15 @@
dc,
windowMetrics,
sbHeightLandscape,
- roundedCornerPadding)
+ minLeftPadding,
+ minRightPadding)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_UPSIDE_DOWN
- expectedBounds = Rect(roundedCornerPadding,
+ expectedBounds = Rect(minLeftPadding,
0,
- screenBounds.right - roundedCornerPadding,
+ screenBounds.right - minRightPadding,
sbHeightPortrait)
bounds = calculateInsetsForRotationWithRotatedResources(
@@ -259,12 +270,13 @@
dc,
windowMetrics,
sbHeightPortrait,
- roundedCornerPadding)
+ minLeftPadding,
+ minRightPadding)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_SEASCAPE
- expectedBounds = Rect(roundedCornerPadding,
+ expectedBounds = Rect(minLeftPadding,
0,
screenBounds.height() - dcBounds.height(),
sbHeightLandscape)
@@ -275,7 +287,8 @@
dc,
windowMetrics,
sbHeightLandscape,
- roundedCornerPadding)
+ minLeftPadding,
+ minRightPadding)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -285,7 +298,8 @@
// GIVEN device in portrait mode, where width < height and no cutout
val currentRotation = ROTATION_NONE
val screenBounds = Rect(0, 0, 1080, 2160)
- val roundedCornerPadding = 20
+ val minLeftPadding = 20
+ val minRightPadding = 20
val sbHeightPortrait = 100
val sbHeightLandscape = 60
@@ -293,9 +307,9 @@
// THEN content insets should only use rounded corner padding
var targetRotation = ROTATION_NONE
- var expectedBounds = Rect(roundedCornerPadding,
+ var expectedBounds = Rect(minLeftPadding,
0,
- screenBounds.right - roundedCornerPadding,
+ screenBounds.right - minRightPadding,
sbHeightPortrait)
var bounds = calculateInsetsForRotationWithRotatedResources(
@@ -304,13 +318,14 @@
null, /* no cutout */
windowMetrics,
sbHeightPortrait,
- roundedCornerPadding)
+ minLeftPadding,
+ minRightPadding)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_LANDSCAPE
- expectedBounds = Rect(roundedCornerPadding,
+ expectedBounds = Rect(minLeftPadding,
0,
- screenBounds.height() - roundedCornerPadding,
+ screenBounds.height() - minRightPadding,
sbHeightLandscape)
bounds = calculateInsetsForRotationWithRotatedResources(
@@ -319,13 +334,14 @@
null, /* no cutout */
windowMetrics,
sbHeightLandscape,
- roundedCornerPadding)
+ minLeftPadding,
+ minRightPadding)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_UPSIDE_DOWN
- expectedBounds = Rect(roundedCornerPadding,
+ expectedBounds = Rect(minLeftPadding,
0,
- screenBounds.width() - roundedCornerPadding,
+ screenBounds.width() - minRightPadding,
sbHeightPortrait)
bounds = calculateInsetsForRotationWithRotatedResources(
@@ -334,13 +350,14 @@
null, /* no cutout */
windowMetrics,
sbHeightPortrait,
- roundedCornerPadding)
+ minLeftPadding,
+ minRightPadding)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_LANDSCAPE
- expectedBounds = Rect(roundedCornerPadding,
+ expectedBounds = Rect(minLeftPadding,
0,
- screenBounds.height() - roundedCornerPadding,
+ screenBounds.height() - minRightPadding,
sbHeightLandscape)
bounds = calculateInsetsForRotationWithRotatedResources(
@@ -349,7 +366,41 @@
null, /* no cutout */
windowMetrics,
sbHeightLandscape,
- roundedCornerPadding)
+ minLeftPadding,
+ minRightPadding)
+ assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+ }
+
+ @Test
+ fun testMinLeftRight_accountsForDisplayCutout() {
+ // GIVEN a device in portrait mode with width < height and a display cutout in the top-left
+ val screenBounds = Rect(0, 0, 1080, 2160)
+ val dcBounds = Rect(0, 0, 100, 100)
+ val minLeftPadding = 80
+ val minRightPadding = 150
+ val sbHeightPortrait = 100
+ val sbHeightLandscape = 60
+ val currentRotation = ROTATION_NONE
+
+ `when`(windowMetrics.bounds).thenReturn(screenBounds)
+ `when`(dc.boundingRects).thenReturn(listOf(dcBounds))
+
+ // THEN left should be set to the display cutout width, and right should use the minRight
+ var targetRotation = ROTATION_NONE
+ var expectedBounds = Rect(dcBounds.right,
+ 0,
+ screenBounds.right - minRightPadding,
+ sbHeightPortrait)
+
+ var bounds = calculateInsetsForRotationWithRotatedResources(
+ currentRotation,
+ targetRotation,
+ dc,
+ windowMetrics,
+ sbHeightPortrait,
+ minLeftPadding,
+ minRightPadding)
+
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index f126ed0..2c2833a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -269,6 +269,7 @@
@Mock private OngoingCallController mOngoingCallController;
@Mock private SystemStatusAnimationScheduler mAnimationScheduler;
@Mock private StatusBarLocationPublisher mLocationPublisher;
+ @Mock private StatusBarIconController mIconController;
@Mock private LockscreenShadeTransitionController mLockscreenTransitionController;
@Mock private FeatureFlags mFeatureFlags;
@Mock private IWallpaperManager mWallpaperManager;
@@ -442,6 +443,7 @@
mOngoingCallController,
mAnimationScheduler,
mLocationPublisher,
+ mIconController,
mLockscreenTransitionController,
mFeatureFlags,
mKeyguardUnlockAnimationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeConfigurationController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeConfigurationController.java
index f5ccac3..516eb6e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeConfigurationController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeConfigurationController.java
@@ -33,4 +33,9 @@
@Override
public void notifyThemeChanged() {
}
+
+ @Override
+ public boolean isLayoutRtl() {
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java b/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java
index f97cb8a..f732a14 100644
--- a/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java
@@ -268,11 +268,18 @@
AuthenticationClient<?> udfps = mClientMap.getOrDefault(SENSOR_TYPE_UDFPS, null);
AuthenticationClient<?> face = mClientMap.getOrDefault(SENSOR_TYPE_FACE, null);
if (isCurrentFaceAuth(client)) {
- // UDFPS should still be running in this case, do not vibrate. However, we
- // should notify the callback and finish the client, so that Keyguard and
- // BiometricScheduler do not get stuck.
- Slog.d(TAG, "Face rejected in multi-sensor auth, udfps: " + udfps);
- callback.handleLifecycleAfterAuth();
+ if (isUdfpsActivelyAuthing(udfps)) {
+ // UDFPS should still be running in this case, do not vibrate. However, we
+ // should notify the callback and finish the client, so that Keyguard and
+ // BiometricScheduler do not get stuck.
+ Slog.d(TAG, "Face rejected in multi-sensor auth, udfps: " + udfps);
+ callback.handleLifecycleAfterAuth();
+ } else {
+ // UDFPS is not actively authenticating (finger not touching, already
+ // rejected, etc).
+ callback.sendHapticFeedback();
+ callback.handleLifecycleAfterAuth();
+ }
} else if (isCurrentUdfps(client)) {
// Face should either be running, or have already finished
SuccessfulAuth auth = popSuccessfulFaceAuthIfExists(currentTimeMillis);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 1edede5..1108937 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -778,7 +778,7 @@
setUpAutoBrightness(mContext.getResources(), mHandler);
reloadReduceBrightColours();
mHbmController.resetHbmData(info.width, info.height, token,
- mDisplayDeviceConfig.getHighBrightnessModeData(), mBrightnessSetting);
+ mDisplayDeviceConfig.getHighBrightnessModeData());
}
private void sendUpdatePowerState() {
@@ -968,7 +968,6 @@
final boolean mustNotify;
final int previousPolicy;
boolean mustInitialize = false;
- boolean shouldSaveBrightnessInfo = true;
int brightnessAdjustmentFlags = 0;
mBrightnessReasonTemp.set(null);
synchronized (mLock) {
@@ -1099,7 +1098,6 @@
if (state == Display.STATE_OFF) {
brightnessState = PowerManager.BRIGHTNESS_OFF_FLOAT;
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_SCREEN_OFF);
- shouldSaveBrightnessInfo = false;
}
// Always use the VR brightness when in the VR state.
@@ -1217,6 +1215,10 @@
brightnessAdjustmentFlags = 0;
}
} else {
+ // Any non-auto-brightness values such as override or temporary should still be subject
+ // to clamping so that they don't go beyond the current max as specified by HBM
+ // Controller.
+ brightnessState = clampScreenBrightness(brightnessState);
mAppliedAutoBrightness = false;
brightnessAdjustmentFlags = 0;
}
@@ -1224,9 +1226,8 @@
// Use default brightness when dozing unless overridden.
if ((Float.isNaN(brightnessState))
&& Display.isDozeState(state)) {
- brightnessState = mScreenBrightnessDozeConfig;
+ brightnessState = clampScreenBrightness(mScreenBrightnessDozeConfig);
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
- shouldSaveBrightnessInfo = false;
}
// Apply manual brightness.
@@ -1241,12 +1242,13 @@
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL);
}
- // Save out the brightness info now that the brightness state for this iteration has been
- // finalized and before we send out notifications about the brightness changing.
- if (shouldSaveBrightnessInfo) {
- saveBrightnessInfo(brightnessState);
-
- }
+ // The current brightness to use has been calculated at this point (minus the adjustments
+ // like low-power and dim), and HbmController should be notified so that it can accurately
+ // calculate HDR or HBM levels. We specifically do it here instead of having HbmController
+ // listen to the brightness setting because certain brightness sources (just as an app
+ // override) are not saved to the setting, but should be reflected in HBM
+ // calculations.
+ mHbmController.onBrightnessChanged(brightnessState);
if (updateScreenBrightnessSetting) {
// Tell the rest of the system about the new brightness in case we had to change it
@@ -1257,6 +1259,10 @@
putScreenBrightnessSetting(brightnessState, /* updateCurrent */ true);
}
+ // We save the brightness info *after* the brightness setting has been changed so that
+ // the brightness info reflects the latest value.
+ saveBrightnessInfo(getScreenBrightnessSetting());
+
// Apply dimming by at least some minimum amount when user activity
// timeout is about to expire.
if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
@@ -1533,7 +1539,7 @@
mHandler.post(mOnBrightnessChangeRunnable);
// TODO(b/192258832): Switch the HBMChangeCallback to a listener pattern.
mAutomaticBrightnessController.update();
- }, mContext, mBrightnessSetting);
+ }, mContext);
}
private void blockScreenOn() {
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index 645131c..2791f6a 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -37,7 +37,6 @@
import android.view.SurfaceControlHdrLayerInfoListener;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.display.BrightnessSetting.BrightnessSettingListener;
import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData;
import com.android.server.display.DisplayManagerService.Clock;
@@ -70,7 +69,6 @@
private final Context mContext;
private final SettingsObserver mSettingsObserver;
private final Injector mInjector;
- private final BrightnessSettingListener mBrightnessSettingListener = this::onBrightnessChanged;
private HdrListener mHdrListener;
private HighBrightnessModeData mHbmData;
@@ -86,7 +84,6 @@
private boolean mIsBlockedByLowPowerMode = false;
private int mWidth;
private int mHeight;
- private BrightnessSetting mBrightnessSetting;
private float mAmbientLux;
/**
@@ -103,30 +100,30 @@
HighBrightnessModeController(Handler handler, int width, int height, IBinder displayToken,
float brightnessMin, float brightnessMax, HighBrightnessModeData hbmData,
- Runnable hbmChangeCallback, Context context, BrightnessSetting brightnessSetting) {
+ Runnable hbmChangeCallback, Context context) {
this(new Injector(), handler, width, height, displayToken, brightnessMin, brightnessMax,
- hbmData, hbmChangeCallback, context, brightnessSetting);
+ hbmData, hbmChangeCallback, context);
}
@VisibleForTesting
HighBrightnessModeController(Injector injector, Handler handler, int width, int height,
IBinder displayToken, float brightnessMin, float brightnessMax,
HighBrightnessModeData hbmData, Runnable hbmChangeCallback,
- Context context, BrightnessSetting brightnessSetting) {
+ Context context) {
mInjector = injector;
mContext = context;
mClock = injector.getClock();
mHandler = handler;
+ mBrightness = brightnessMin;
mBrightnessMin = brightnessMin;
mBrightnessMax = brightnessMax;
- mBrightness = brightnessSetting.getBrightness();
mHbmChangeCallback = hbmChangeCallback;
mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
mRecalcRunnable = this::recalculateTimeAllowance;
mHdrListener = new HdrListener();
- resetHbmData(width, height, displayToken, hbmData, brightnessSetting);
+ resetHbmData(width, height, displayToken, hbmData);
}
void setAutoBrightnessEnabled(boolean isEnabled) {
@@ -185,7 +182,6 @@
}
}
- @VisibleForTesting
void onBrightnessChanged(float brightness) {
if (!deviceSupportsHbm()) {
return;
@@ -224,12 +220,11 @@
mSettingsObserver.stopObserving();
}
- void resetHbmData(int width, int height, IBinder displayToken, HighBrightnessModeData hbmData,
- BrightnessSetting brightnessSetting) {
+ void resetHbmData(int width, int height, IBinder displayToken, HighBrightnessModeData hbmData) {
mWidth = width;
mHeight = height;
mHbmData = hbmData;
- resetBrightnessSetting(brightnessSetting);
+
unregisterHdrListener();
mSkinThermalStatusObserver.stopObserving();
mSettingsObserver.stopObserving();
@@ -261,9 +256,12 @@
pw.println(" mBrightness=" + mBrightness);
pw.println(" mCurrentMin=" + getCurrentBrightnessMin());
pw.println(" mCurrentMax=" + getCurrentBrightnessMax());
- pw.println(" mHbmMode=" + BrightnessInfo.hbmToString(mHbmMode));
+ pw.println(" mHbmMode=" + BrightnessInfo.hbmToString(mHbmMode)
+ + (mHbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
+ ? "(" + getHdrBrightnessValue() + ")" : ""));
pw.println(" mHbmData=" + mHbmData);
- pw.println(" mAmbientLux=" + mAmbientLux);
+ pw.println(" mAmbientLux=" + mAmbientLux
+ + (mIsAutoBrightnessEnabled ? "" : " (old/invalid)"));
pw.println(" mIsInAllowedAmbientRange=" + mIsInAllowedAmbientRange);
pw.println(" mIsAutoBrightnessEnabled=" + mIsAutoBrightnessEnabled);
pw.println(" mIsHdrLayerPresent=" + mIsHdrLayerPresent);
@@ -301,16 +299,6 @@
return event.startTimeMillis;
}
- private void resetBrightnessSetting(BrightnessSetting brightnessSetting) {
- if (mBrightnessSetting != null) {
- mBrightnessSetting.unregisterListener(mBrightnessSettingListener);
- }
- mBrightnessSetting = brightnessSetting;
- if (mBrightnessSetting != null) {
- mBrightnessSetting.registerListener(mBrightnessSettingListener);
- }
- }
-
private boolean isCurrentlyAllowed() {
// Returns true if HBM is allowed (above the ambient lux threshold) and there's still
// time within the current window for additional HBM usage. We return false if there is an
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 00ef97d..5429484 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2252,12 +2252,29 @@
(params.installFlags & PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK)
== 0;
synchronized (mLock) {
- if (checkApexUpdateAllowed && !isApexUpdateAllowed(mPackageName)) {
+ if (checkApexUpdateAllowed && !isApexUpdateAllowed(mPackageName,
+ mInstallSource.installerPackageName)) {
onSessionValidationFailure(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
- "Update of APEX package " + mPackageName + " is not allowed");
+ "Update of APEX package " + mPackageName + " is not allowed for "
+ + mInstallSource.installerPackageName);
return;
}
}
+
+ if (!params.isStaged) {
+ // For non-staged APEX installs also check if there is a staged session that
+ // contains the same APEX. If that's the case, we should fail this session.
+ synchronized (mLock) {
+ int sessionId = mStagingManager.getSessionIdByPackageName(mPackageName);
+ if (sessionId != -1) {
+ onSessionValidationFailure(
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
+ "Staged session " + sessionId + " already contains "
+ + mPackageName);
+ return;
+ }
+ }
+ }
}
if (params.isStaged) {
@@ -2798,9 +2815,23 @@
return sessionContains((s) -> !s.isApexSession());
}
- private boolean isApexUpdateAllowed(String apexPackageName) {
- return mPm.getModuleInfo(apexPackageName, 0) != null
- || SystemConfig.getInstance().getAllowedVendorApexes().contains(apexPackageName);
+ private boolean isApexUpdateAllowed(String apexPackageName, String installerPackageName) {
+ if (mPm.getModuleInfo(apexPackageName, 0) != null) {
+ final String modulesInstaller =
+ SystemConfig.getInstance().getModulesInstallerPackageName();
+ if (modulesInstaller == null) {
+ Slog.w(TAG, "No modules installer defined");
+ return false;
+ }
+ return modulesInstaller.equals(installerPackageName);
+ }
+ final String vendorApexInstaller =
+ SystemConfig.getInstance().getAllowedVendorApexes().get(apexPackageName);
+ if (vendorApexInstaller == null) {
+ Slog.w(TAG, apexPackageName + " is not allowed to be updated");
+ return false;
+ }
+ return vendorApexInstaller.equals(installerPackageName);
}
/**
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 4ac5be2..bdbcb27 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -777,6 +777,26 @@
}
}
+ /**
+ * Returns id of a committed and non-finalized stated session that contains same
+ * {@code packageName}, or {@code -1} if no sessions have this {@code packageName} staged.
+ */
+ int getSessionIdByPackageName(@NonNull String packageName) {
+ synchronized (mStagedSessions) {
+ for (int i = 0; i < mStagedSessions.size(); i++) {
+ StagedSession stagedSession = mStagedSessions.valueAt(i);
+ if (!stagedSession.isCommitted() || stagedSession.isDestroyed()
+ || stagedSession.isInTerminalState()) {
+ continue;
+ }
+ if (stagedSession.getPackageName().equals(packageName)) {
+ return stagedSession.sessionId();
+ }
+ }
+ }
+ return -1;
+ }
+
@VisibleForTesting
void createSession(@NonNull StagedSession sessionInfo) {
synchronized (mStagedSessions) {
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
index c1f8240..9999aff 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
@@ -133,13 +133,8 @@
* Throws a {@link SecurityException} iff the originator has permission to receive data.
*/
void enforcePermissionsForDataDelivery(@NonNull Identity identity, @NonNull String reason) {
- // TODO(b/186164881): remove
- // START TEMP HACK
- enforcePermissionForPreflight(mContext, identity, RECORD_AUDIO);
- int hotwordOp = AppOpsManager.strOpToOp(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD);
- mContext.getSystemService(AppOpsManager.class).noteOpNoThrow(hotwordOp, identity.uid,
- identity.packageName, identity.attributionTag, reason);
- // END TEMP HACK
+ enforcePermissionForDataDelivery(mContext, identity, RECORD_AUDIO,
+ reason);
enforcePermissionForDataDelivery(mContext, identity, CAPTURE_AUDIO_HOTWORD,
reason);
}
@@ -167,8 +162,8 @@
/**
* Throws a {@link SecurityException} if originator permanently doesn't have the given
- * permission, or a {@link ServiceSpecificException} with a {@link
- * Status#TEMPORARY_PERMISSION_DENIED} if caller originator doesn't have the given permission.
+ * permission.
+ * Soft (temporary) denials are considered OK for preflight purposes.
*
* @param context A {@link Context}, used for permission checks.
* @param identity The identity to check.
@@ -180,15 +175,12 @@
permission);
switch (status) {
case PermissionChecker.PERMISSION_GRANTED:
+ case PermissionChecker.PERMISSION_SOFT_DENIED:
return;
case PermissionChecker.PERMISSION_HARD_DENIED:
throw new SecurityException(
String.format("Failed to obtain permission %s for identity %s", permission,
ObjectPrinter.print(identity, true, 16)));
- case PermissionChecker.PERMISSION_SOFT_DENIED:
- throw new ServiceSpecificException(Status.TEMPORARY_PERMISSION_DENIED,
- String.format("Failed to obtain permission %s for identity %s", permission,
- ObjectPrinter.print(identity, true, 16)));
default:
throw new RuntimeException("Unexpected perimission check result.");
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
index 95a30c7..458eae9 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -318,6 +318,8 @@
*/
private Map<Integer, ModelParameterRange> parameterSupport = new HashMap<>();
+ private RecognitionConfig mConfig;
+
/**
* Check that the given parameter is known to be supported for this model.
*
@@ -369,6 +371,14 @@
void setActivityState(Activity activity) {
mActivityState.set(activity.ordinal());
}
+
+ void setRecognitionConfig(@NonNull RecognitionConfig config) {
+ mConfig = config;
+ }
+
+ RecognitionConfig getRecognitionConfig() {
+ return mConfig;
+ }
}
/**
@@ -502,6 +512,7 @@
// Normally, we would set the state after the operation succeeds. However, since
// the activity state may be reset outside of the lock, we set it here first,
// and reset it in case of exception.
+ modelState.setRecognitionConfig(config);
modelState.setActivityState(ModelState.Activity.ACTIVE);
mDelegate.startRecognition(modelHandle, config);
} catch (Exception e) {
@@ -542,6 +553,27 @@
}
}
+ private void restartIfIntercepted(int modelHandle) {
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // State validation.
+ if (mState == ModuleStatus.DETACHED) {
+ return;
+ }
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ if (modelState == null
+ || modelState.getActivityState() != ModelState.Activity.INTERCEPTED) {
+ return;
+ }
+ try {
+ mDelegate.startRecognition(modelHandle, modelState.getRecognitionConfig());
+ modelState.setActivityState(ModelState.Activity.ACTIVE);
+ Log.i(TAG, "Restarted intercepted model " + modelHandle);
+ } catch (Exception e) {
+ Log.i(TAG, "Failed to restart intercepted model " + modelHandle, e);
+ }
+ }
+ }
+
@Override
public void forceRecognitionEvent(int modelHandle) {
// Input validation (always valid).
@@ -753,6 +785,10 @@
Log.e(TAG, "Client callback exception.", e);
if (event.status != RecognitionStatus.FORCED) {
modelState.setActivityState(ModelState.Activity.INTERCEPTED);
+ // If we failed to deliver an actual event to the client, they would never
+ // know to restart it whenever circumstances change. Thus, we restart it
+ // here. We do this from a separate thread to avoid any race conditions.
+ new Thread(() -> restartIfIntercepted(modelHandle)).start();
}
}
}
@@ -780,6 +816,10 @@
Log.e(TAG, "Client callback exception.", e);
if (event.common.status != RecognitionStatus.FORCED) {
modelState.setActivityState(ModelState.Activity.INTERCEPTED);
+ // If we failed to deliver an actual event to the client, they would never
+ // know to restart it whenever circumstances change. Thus, we restart it
+ // here. We do this from a separate thread to avoid any race conditions.
+ new Thread(() -> restartIfIntercepted(modelHandle)).start();
}
}
}
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index e3e2708..fdf23d3 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -59,9 +59,7 @@
private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG);
- private static final long BG_PROCESS_PERIOD = DEBUG
- ? TimeUnit.MINUTES.toMillis(1)
- : TimeUnit.DAYS.toMillis(1);
+ private static final long BG_PROCESS_PERIOD = TimeUnit.DAYS.toMillis(1); // every 1 day.
private IProfCollectd mIProfcollect;
private static ProfcollectForwardingService sSelfService;
@@ -286,6 +284,11 @@
updateEngine.bind(new UpdateEngineCallback() {
@Override
public void onStatusUpdate(int status, float percent) {
+ if (DEBUG) {
+ Log.d(LOG_TAG, "Received OTA status update, status: " + status + ", percent: "
+ + percent);
+ }
+
if (status == UpdateEngine.UpdateStatusConstants.UPDATED_NEED_REBOOT) {
packProfileReport();
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java
index 8336663..9926953 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java
@@ -50,6 +50,14 @@
AppSearchConfig.DEFAULT_SAMPLING_INTERVAL);
assertThat(appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()).isEqualTo(
AppSearchConfig.DEFAULT_SAMPLING_INTERVAL);
+ assertThat(appSearchConfig.getCachedSamplingIntervalForInitializeStats()).isEqualTo(
+ AppSearchConfig.DEFAULT_SAMPLING_INTERVAL);
+ assertThat(appSearchConfig.getCachedSamplingIntervalForSearchStats()).isEqualTo(
+ AppSearchConfig.DEFAULT_SAMPLING_INTERVAL);
+ assertThat(appSearchConfig.getCachedSamplingIntervalForGlobalSearchStats()).isEqualTo(
+ AppSearchConfig.DEFAULT_SAMPLING_INTERVAL);
+ assertThat(appSearchConfig.getCachedSamplingIntervalForOptimizeStats()).isEqualTo(
+ AppSearchConfig.DEFAULT_SAMPLING_INTERVAL);
assertThat(appSearchConfig.getCachedLimitConfigMaxDocumentSizeBytes()).isEqualTo(
AppSearchConfig.DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES);
assertThat(appSearchConfig.getCachedLimitConfigMaxDocumentCount()).isEqualTo(
@@ -100,6 +108,11 @@
final int samplingIntervalDefault = -1;
final int samplingIntervalPutDocumentStats = -2;
final int samplingIntervalBatchCallStats = -3;
+ final int samplingIntervalInitializeStats = -4;
+ final int samplingIntervalSearchStats = -5;
+ final int samplingIntervalGlobalSearchStats = -6;
+ final int samplingIntervalOptimizeStats = -7;
+
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT,
Integer.toString(samplingIntervalDefault),
@@ -112,6 +125,22 @@
AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS,
Integer.toString(samplingIntervalBatchCallStats),
false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS,
+ Integer.toString(samplingIntervalInitializeStats),
+ false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS,
+ Integer.toString(samplingIntervalSearchStats),
+ false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS,
+ Integer.toString(samplingIntervalGlobalSearchStats),
+ false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS,
+ Integer.toString(samplingIntervalOptimizeStats),
+ false);
AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
@@ -121,6 +150,14 @@
samplingIntervalPutDocumentStats);
assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo(
samplingIntervalBatchCallStats);
+ assertThat(appSearchConfig.getCachedSamplingIntervalForInitializeStats()).isEqualTo(
+ samplingIntervalInitializeStats);
+ assertThat(appSearchConfig.getCachedSamplingIntervalForSearchStats()).isEqualTo(
+ samplingIntervalSearchStats);
+ assertThat(appSearchConfig.getCachedSamplingIntervalForGlobalSearchStats()).isEqualTo(
+ samplingIntervalGlobalSearchStats);
+ assertThat(appSearchConfig.getCachedSamplingIntervalForOptimizeStats()).isEqualTo(
+ samplingIntervalOptimizeStats);
}
@Test
@@ -128,6 +165,10 @@
int samplingIntervalDefault = -1;
int samplingIntervalPutDocumentStats = -2;
int samplingIntervalBatchCallStats = -3;
+ int samplingIntervalInitializeStats = -4;
+ int samplingIntervalSearchStats = -5;
+ int samplingIntervalGlobalSearchStats = -6;
+ int samplingIntervalOptimizeStats = -7;
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT,
Integer.toString(samplingIntervalDefault),
@@ -140,12 +181,32 @@
AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS,
Integer.toString(samplingIntervalBatchCallStats),
false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS,
+ Integer.toString(samplingIntervalInitializeStats),
+ false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS,
+ Integer.toString(samplingIntervalSearchStats),
+ false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS,
+ Integer.toString(samplingIntervalGlobalSearchStats),
+ false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS,
+ Integer.toString(samplingIntervalOptimizeStats),
+ false);
AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
// Overrides
samplingIntervalDefault = -4;
samplingIntervalPutDocumentStats = -5;
samplingIntervalBatchCallStats = -6;
+ samplingIntervalInitializeStats = -7;
+ samplingIntervalSearchStats = -8;
+ samplingIntervalGlobalSearchStats = -9;
+ samplingIntervalOptimizeStats = -10;
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT,
Integer.toString(samplingIntervalDefault),
@@ -158,6 +219,22 @@
AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS,
Integer.toString(samplingIntervalBatchCallStats),
false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS,
+ Integer.toString(samplingIntervalInitializeStats),
+ false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS,
+ Integer.toString(samplingIntervalSearchStats),
+ false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS,
+ Integer.toString(samplingIntervalGlobalSearchStats),
+ false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS,
+ Integer.toString(samplingIntervalOptimizeStats),
+ false);
assertThat(appSearchConfig.getCachedSamplingIntervalDefault()).isEqualTo(
samplingIntervalDefault);
@@ -165,6 +242,14 @@
samplingIntervalPutDocumentStats);
assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo(
samplingIntervalBatchCallStats);
+ assertThat(appSearchConfig.getCachedSamplingIntervalForInitializeStats()).isEqualTo(
+ samplingIntervalInitializeStats);
+ assertThat(appSearchConfig.getCachedSamplingIntervalForSearchStats()).isEqualTo(
+ samplingIntervalSearchStats);
+ assertThat(appSearchConfig.getCachedSamplingIntervalForGlobalSearchStats()).isEqualTo(
+ samplingIntervalGlobalSearchStats);
+ assertThat(appSearchConfig.getCachedSamplingIntervalForOptimizeStats()).isEqualTo(
+ samplingIntervalOptimizeStats);
}
/**
@@ -366,6 +451,18 @@
() -> appSearchConfig.getCachedSamplingIntervalForPutDocumentStats());
Assert.assertThrows("Trying to use a closed AppSearchConfig instance.",
IllegalStateException.class,
+ () -> appSearchConfig.getCachedSamplingIntervalForInitializeStats());
+ Assert.assertThrows("Trying to use a closed AppSearchConfig instance.",
+ IllegalStateException.class,
+ () -> appSearchConfig.getCachedSamplingIntervalForSearchStats());
+ Assert.assertThrows("Trying to use a closed AppSearchConfig instance.",
+ IllegalStateException.class,
+ () -> appSearchConfig.getCachedSamplingIntervalForGlobalSearchStats());
+ Assert.assertThrows("Trying to use a closed AppSearchConfig instance.",
+ IllegalStateException.class,
+ () -> appSearchConfig.getCachedSamplingIntervalForOptimizeStats());
+ Assert.assertThrows("Trying to use a closed AppSearchConfig instance.",
+ IllegalStateException.class,
() -> appSearchConfig.getCachedBytesOptimizeThreshold());
Assert.assertThrows("Trying to use a closed AppSearchConfig instance.",
IllegalStateException.class,
diff --git a/services/tests/mockingservicestests/src/com/android/server/appsearch/OWNERS b/services/tests/mockingservicestests/src/com/android/server/appsearch/OWNERS
new file mode 100644
index 0000000..24f6b0b6
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/appsearch/OWNERS
@@ -0,0 +1 @@
+include /apex/appsearch/OWNERS
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index f91cb28..521be70 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -459,6 +459,66 @@
assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed");
}
+ @Test
+ public void getSessionIdByPackageName() throws Exception {
+ FakeStagedSession session = new FakeStagedSession(239);
+ session.setCommitted(true);
+ session.setSessionReady();
+ session.setPackageName("com.foo");
+
+ mStagingManager.createSession(session);
+ assertThat(mStagingManager.getSessionIdByPackageName("com.foo")).isEqualTo(239);
+ }
+
+ @Test
+ public void getSessionIdByPackageName_appliedSession_ignores() throws Exception {
+ FakeStagedSession session = new FakeStagedSession(37);
+ session.setCommitted(true);
+ session.setSessionApplied();
+ session.setPackageName("com.foo");
+
+ mStagingManager.createSession(session);
+ assertThat(mStagingManager.getSessionIdByPackageName("com.foo")).isEqualTo(-1);
+ }
+
+ @Test
+ public void getSessionIdByPackageName_failedSession_ignores() throws Exception {
+ FakeStagedSession session = new FakeStagedSession(73);
+ session.setCommitted(true);
+ session.setSessionFailed(1, "whatevs");
+ session.setPackageName("com.foo");
+
+ mStagingManager.createSession(session);
+ assertThat(mStagingManager.getSessionIdByPackageName("com.foo")).isEqualTo(-1);
+ }
+
+ @Test
+ public void getSessionIdByPackageName_destroyedSession_ignores() throws Exception {
+ FakeStagedSession session = new FakeStagedSession(23);
+ session.setCommitted(true);
+ session.setDestroyed(true);
+ session.setPackageName("com.foo");
+
+ mStagingManager.createSession(session);
+ assertThat(mStagingManager.getSessionIdByPackageName("com.foo")).isEqualTo(-1);
+ }
+
+ @Test
+ public void getSessionIdByPackageName_noSessions() throws Exception {
+ assertThat(mStagingManager.getSessionIdByPackageName("com.foo")).isEqualTo(-1);
+ }
+
+ @Test
+ public void getSessionIdByPackageName_noSessionHasThisPackage() throws Exception {
+ FakeStagedSession session = new FakeStagedSession(37);
+ session.setCommitted(true);
+ session.setSessionApplied();
+ session.setPackageName("com.foo");
+
+ mStagingManager.createSession(session);
+ assertThat(mStagingManager.getSessionIdByPackageName("com.bar")).isEqualTo(-1);
+ }
+
private StagingManager.StagedSession createSession(int sessionId, String packageName,
long committedMillis) {
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
index 5b067bc..f40a5ad 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
@@ -42,6 +42,7 @@
import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter;
import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
+import com.android.server.appsearch.external.localstorage.stats.OptimizeStats;
import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
import com.android.server.appsearch.icing.proto.DocumentProto;
import com.android.server.appsearch.icing.proto.GetOptimizeInfoResultProto;
@@ -451,19 +452,26 @@
assertThat(optimizeInfo.getOptimizableDocs()).isEqualTo(1);
// Increase mutation counter and stop before reach the threshold
- mAppSearchImpl.checkForOptimize(AppSearchImpl.CHECK_OPTIMIZE_INTERVAL - 1);
+ mAppSearchImpl.checkForOptimize(
+ AppSearchImpl.CHECK_OPTIMIZE_INTERVAL - 1, /*builder=*/ null);
// Verify the optimize() isn't triggered.
optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked();
assertThat(optimizeInfo.getOptimizableDocs()).isEqualTo(1);
// Increase the counter and reach the threshold, optimize() should be triggered.
- mAppSearchImpl.checkForOptimize(/*mutateBatchSize=*/ 1);
+ OptimizeStats.Builder builder = new OptimizeStats.Builder();
+ mAppSearchImpl.checkForOptimize(/*mutateBatchSize=*/ 1, builder);
// Verify optimize() is triggered.
optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked();
assertThat(optimizeInfo.getOptimizableDocs()).isEqualTo(0);
assertThat(optimizeInfo.getEstimatedOptimizableBytes()).isEqualTo(0);
+
+ // Verify the stats have been set.
+ OptimizeStats oStats = builder.build();
+ assertThat(oStats.getOriginalDocumentCount()).isEqualTo(1);
+ assertThat(oStats.getDeletedDocumentCount()).isEqualTo(1);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
index 7bacbb6..7c97687 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
@@ -29,12 +29,14 @@
import com.android.server.appsearch.external.localstorage.stats.CallStats;
import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
+import com.android.server.appsearch.external.localstorage.stats.OptimizeStats;
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
import com.android.server.appsearch.external.localstorage.stats.SearchStats;
import com.android.server.appsearch.icing.proto.DeleteStatsProto;
import com.android.server.appsearch.icing.proto.DocumentProto;
import com.android.server.appsearch.icing.proto.InitializeStatsProto;
+import com.android.server.appsearch.icing.proto.OptimizeStatsProto;
import com.android.server.appsearch.icing.proto.PutDocumentStatsProto;
import com.android.server.appsearch.icing.proto.PutResultProto;
import com.android.server.appsearch.icing.proto.QueryStatsProto;
@@ -81,6 +83,7 @@
@Nullable InitializeStats mInitializeStats;
@Nullable SearchStats mSearchStats;
@Nullable RemoveStats mRemoveStats;
+ @Nullable OptimizeStats mOptimizeStats;
@Override
public void logStats(@NonNull CallStats stats) {
@@ -106,6 +109,11 @@
public void logStats(@NonNull RemoveStats stats) {
mRemoveStats = stats;
}
+
+ @Override
+ public void logStats(@NonNull OptimizeStats stats) {
+ mOptimizeStats = stats;
+ }
}
@Test
@@ -286,6 +294,48 @@
assertThat(rStats.getDeletedDocumentCount()).isEqualTo(nativeNumDocumentDeleted);
}
+ @Test
+ public void testAppSearchLoggerHelper_testCopyNativeStats_optimize() {
+ int nativeLatencyMillis = 1;
+ int nativeDocumentStoreOptimizeLatencyMillis = 2;
+ int nativeIndexRestorationLatencyMillis = 3;
+ int nativeNumOriginalDocuments = 4;
+ int nativeNumDeletedDocuments = 5;
+ int nativeNumExpiredDocuments = 6;
+ long nativeStorageSizeBeforeBytes = Integer.MAX_VALUE + 1;
+ long nativeStorageSizeAfterBytes = Integer.MAX_VALUE + 2;
+ long nativeTimeSinceLastOptimizeMillis = Integer.MAX_VALUE + 3;
+ OptimizeStatsProto optimizeStatsProto =
+ OptimizeStatsProto.newBuilder()
+ .setLatencyMs(nativeLatencyMillis)
+ .setDocumentStoreOptimizeLatencyMs(nativeDocumentStoreOptimizeLatencyMillis)
+ .setIndexRestorationLatencyMs(nativeIndexRestorationLatencyMillis)
+ .setNumOriginalDocuments(nativeNumOriginalDocuments)
+ .setNumDeletedDocuments(nativeNumDeletedDocuments)
+ .setNumExpiredDocuments(nativeNumExpiredDocuments)
+ .setStorageSizeBefore(nativeStorageSizeBeforeBytes)
+ .setStorageSizeAfter(nativeStorageSizeAfterBytes)
+ .setTimeSinceLastOptimizeMs(nativeTimeSinceLastOptimizeMillis)
+ .build();
+ OptimizeStats.Builder oBuilder = new OptimizeStats.Builder();
+
+ AppSearchLoggerHelper.copyNativeStats(optimizeStatsProto, oBuilder);
+
+ OptimizeStats oStats = oBuilder.build();
+ assertThat(oStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
+ assertThat(oStats.getDocumentStoreOptimizeLatencyMillis())
+ .isEqualTo(nativeDocumentStoreOptimizeLatencyMillis);
+ assertThat(oStats.getIndexRestorationLatencyMillis())
+ .isEqualTo(nativeIndexRestorationLatencyMillis);
+ assertThat(oStats.getOriginalDocumentCount()).isEqualTo(nativeNumOriginalDocuments);
+ assertThat(oStats.getDeletedDocumentCount()).isEqualTo(nativeNumDeletedDocuments);
+ assertThat(oStats.getExpiredDocumentCount()).isEqualTo(nativeNumExpiredDocuments);
+ assertThat(oStats.getStorageSizeBeforeBytes()).isEqualTo(nativeStorageSizeBeforeBytes);
+ assertThat(oStats.getStorageSizeAfterBytes()).isEqualTo(nativeStorageSizeAfterBytes);
+ assertThat(oStats.getTimeSinceLastOptimizeMillis())
+ .isEqualTo(nativeTimeSinceLastOptimizeMillis);
+ }
+
//
// Testing actual logging
//
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
index 57d9941..c1dc0e4 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
@@ -348,4 +348,49 @@
assertThat(rStats.getDeleteType()).isEqualTo(deleteType);
assertThat(rStats.getDeletedDocumentCount()).isEqualTo(documentDeletedCount);
}
+
+ @Test
+ public void testAppSearchStats_OptimizeStats() {
+ int nativeLatencyMillis = 1;
+ int nativeDocumentStoreOptimizeLatencyMillis = 2;
+ int nativeIndexRestorationLatencyMillis = 3;
+ int nativeNumOriginalDocuments = 4;
+ int nativeNumDeletedDocuments = 5;
+ int nativeNumExpiredDocuments = 6;
+ long nativeStorageSizeBeforeBytes = Integer.MAX_VALUE + 1;
+ long nativeStorageSizeAfterBytes = Integer.MAX_VALUE + 2;
+ long nativeTimeSinceLastOptimizeMillis = Integer.MAX_VALUE + 3;
+
+ final OptimizeStats oStats =
+ new OptimizeStats.Builder()
+ .setStatusCode(TEST_STATUS_CODE)
+ .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
+ .setNativeLatencyMillis(nativeLatencyMillis)
+ .setDocumentStoreOptimizeLatencyMillis(
+ nativeDocumentStoreOptimizeLatencyMillis)
+ .setIndexRestorationLatencyMillis(nativeIndexRestorationLatencyMillis)
+ .setOriginalDocumentCount(nativeNumOriginalDocuments)
+ .setDeletedDocumentCount(nativeNumDeletedDocuments)
+ .setExpiredDocumentCount(nativeNumExpiredDocuments)
+ .setStorageSizeBeforeBytes(nativeStorageSizeBeforeBytes)
+ .setStorageSizeAfterBytes(nativeStorageSizeAfterBytes)
+ .setTimeSinceLastOptimizeMillis(nativeTimeSinceLastOptimizeMillis)
+ .build();
+
+ assertThat(oStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE);
+ assertThat(oStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
+ assertThat(oStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
+ assertThat(oStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
+ assertThat(oStats.getDocumentStoreOptimizeLatencyMillis())
+ .isEqualTo(nativeDocumentStoreOptimizeLatencyMillis);
+ assertThat(oStats.getIndexRestorationLatencyMillis())
+ .isEqualTo(nativeIndexRestorationLatencyMillis);
+ assertThat(oStats.getOriginalDocumentCount()).isEqualTo(nativeNumOriginalDocuments);
+ assertThat(oStats.getDeletedDocumentCount()).isEqualTo(nativeNumDeletedDocuments);
+ assertThat(oStats.getExpiredDocumentCount()).isEqualTo(nativeNumExpiredDocuments);
+ assertThat(oStats.getStorageSizeBeforeBytes()).isEqualTo(nativeStorageSizeBeforeBytes);
+ assertThat(oStats.getStorageSizeAfterBytes()).isEqualTo(nativeStorageSizeAfterBytes);
+ assertThat(oStats.getTimeSinceLastOptimizeMillis())
+ .isEqualTo(nativeTimeSinceLastOptimizeMillis);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java
index fb05825..a169ebd 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java
@@ -259,6 +259,74 @@
}
@Test
+ public void testKeyguard_faceRejectedWhenUdfpsTouching_thenUdfpsRejected() {
+ mCoexCoordinator.reset();
+
+ AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
+ when(faceClient.isKeyguard()).thenReturn(true);
+ when(faceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
+
+ AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class,
+ withSettings().extraInterfaces(Udfps.class));
+ when(udfpsClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
+ when(udfpsClient.isKeyguard()).thenReturn(true);
+ when(((Udfps) udfpsClient).isPointerDown()).thenReturn(true);
+
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient);
+
+ mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, faceClient,
+ LockoutTracker.LOCKOUT_NONE, mCallback);
+ verify(mCallback, never()).sendHapticFeedback();
+ verify(mCallback).handleLifecycleAfterAuth();
+
+ // BiometricScheduler removes the face authentication client after rejection
+ mCoexCoordinator.removeAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
+
+ // Then UDFPS rejected
+ CoexCoordinator.Callback udfpsCallback = mock(CoexCoordinator.Callback.class);
+ mCoexCoordinator.onAuthenticationRejected(1 /* currentTimeMillis */, udfpsClient,
+ LockoutTracker.LOCKOUT_NONE, udfpsCallback);
+ verify(udfpsCallback).sendHapticFeedback();
+ verify(udfpsCallback).sendAuthenticationResult(eq(false) /* addAuthTokenIfStrong */);
+ verify(mCallback, never()).sendHapticFeedback();
+ }
+
+ @Test
+ public void testKeyguard_udfpsRejected_thenFaceRejected() {
+ mCoexCoordinator.reset();
+
+ AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
+ when(faceClient.isKeyguard()).thenReturn(true);
+ when(faceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
+
+ AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class,
+ withSettings().extraInterfaces(Udfps.class));
+ when(udfpsClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
+ when(udfpsClient.isKeyguard()).thenReturn(true);
+ when(((Udfps) udfpsClient).isPointerDown()).thenReturn(true);
+
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
+ mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient);
+
+ mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, udfpsClient,
+ LockoutTracker.LOCKOUT_NONE, mCallback);
+ // Client becomes paused, but finger does not necessarily lift, since we suppress the haptic
+ when(udfpsClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED_PAUSED);
+ verify(mCallback, never()).sendHapticFeedback();
+ verify(mCallback).handleLifecycleAfterAuth();
+
+ // Then face rejected. Note that scheduler leaves UDFPS in the CoexCoordinator since
+ // unlike face, its lifecycle becomes "paused" instead of "finished".
+ CoexCoordinator.Callback faceCallback = mock(CoexCoordinator.Callback.class);
+ mCoexCoordinator.onAuthenticationRejected(1 /* currentTimeMillis */, faceClient,
+ LockoutTracker.LOCKOUT_NONE, faceCallback);
+ verify(faceCallback).sendHapticFeedback();
+ verify(faceCallback).sendAuthenticationResult(eq(false) /* addAuthTokenIfStrong */);
+ verify(mCallback, never()).sendHapticFeedback();
+ }
+
+ @Test
public void testNonKeyguard_rejectAndNotLockedOut() {
mCoexCoordinator.reset();
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index 1ad8850..cc3591c8 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -92,7 +92,6 @@
@Mock IThermalService mThermalServiceMock;
@Mock Injector mInjectorMock;
- @Mock BrightnessSetting mBrightnessSetting;
@Captor ArgumentCaptor<IThermalEventListener> mThermalEventListenerCaptor;
@@ -123,7 +122,7 @@
initHandler(null);
final HighBrightnessModeController hbmc = new HighBrightnessModeController(
mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN,
- DEFAULT_MAX, null, () -> {}, mContextSpy, mBrightnessSetting);
+ DEFAULT_MAX, null, () -> {}, mContextSpy);
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
}
@@ -132,7 +131,7 @@
initHandler(null);
final HighBrightnessModeController hbmc = new HighBrightnessModeController(
mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN,
- DEFAULT_MAX, null, () -> {}, mContextSpy, mBrightnessSetting);
+ DEFAULT_MAX, null, () -> {}, mContextSpy);
hbmc.setAutoBrightnessEnabled(true);
hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
@@ -464,7 +463,7 @@
initHandler(clock);
return new HighBrightnessModeController(mInjectorMock, mHandler, DISPLAY_WIDTH,
DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN, DEFAULT_MAX, DEFAULT_HBM_DATA, () -> {},
- mContextSpy, mBrightnessSetting);
+ mContextSpy);
}
private void initHandler(OffsettableClock clock) {
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index 9044b27..5eb21a5 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.testng.Assert.expectThrows;
import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
@@ -200,6 +201,46 @@
assertThat(mSysConfig.getWhitelistedStagedInstallers())
.containsExactly("com.android.package1");
+ assertThat(mSysConfig.getModulesInstallerPackageName()).isNull();
+ }
+
+ @Test
+ public void readPermissions_parsesStagedInstallerWhitelist_modulesInstaller()
+ throws IOException {
+ final String contents =
+ "<config>\n"
+ + " <whitelisted-staged-installer package=\"com.android.package1\" "
+ + " isModulesInstaller=\"true\" />\n"
+ + "</config>";
+ final File folder = createTempSubfolder("folder");
+ createTempFile(folder, "staged-installer-whitelist.xml", contents);
+
+ mSysConfig.readPermissions(folder, /* Grant all permission flags */ ~0);
+
+ assertThat(mSysConfig.getWhitelistedStagedInstallers())
+ .containsExactly("com.android.package1");
+ assertThat(mSysConfig.getModulesInstallerPackageName())
+ .isEqualTo("com.android.package1");
+ }
+
+ @Test
+ public void readPermissions_parsesStagedInstallerWhitelist_multipleModulesInstallers()
+ throws IOException {
+ final String contents =
+ "<config>\n"
+ + " <whitelisted-staged-installer package=\"com.android.package1\" "
+ + " isModulesInstaller=\"true\" />\n"
+ + " <whitelisted-staged-installer package=\"com.android.package2\" "
+ + " isModulesInstaller=\"true\" />\n"
+ + "</config>";
+ final File folder = createTempSubfolder("folder");
+ createTempFile(folder, "staged-installer-whitelist.xml", contents);
+
+ IllegalStateException e = expectThrows(
+ IllegalStateException.class,
+ () -> mSysConfig.readPermissions(folder, /* Grant all permission flags */ ~0));
+
+ assertThat(e).hasMessageThat().contains("Multiple modules installers");
}
/**
@@ -230,14 +271,16 @@
throws IOException {
final String contents =
"<config>\n"
- + " <allowed-vendor-apex package=\"com.android.apex1\" />\n"
+ + " <allowed-vendor-apex package=\"com.android.apex1\" "
+ + "installerPackage=\"com.installer\" />\n"
+ "</config>";
final File folder = createTempSubfolder("folder");
createTempFile(folder, "vendor-apex-allowlist.xml", contents);
mSysConfig.readPermissions(folder, /* Grant all permission flags */ ~0);
- assertThat(mSysConfig.getAllowedVendorApexes()).containsExactly("com.android.apex1");
+ assertThat(mSysConfig.getAllowedVendorApexes())
+ .containsExactly("com.android.apex1", "com.installer");
}
/**
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index 1aa0499..cac14a7 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -32,6 +32,7 @@
test_suites: ["general-tests"],
java_resources: [
":com.android.apex.apkrollback.test_v2",
+ ":StagedInstallTestApexV2",
":StagedInstallTestApexV2_WrongSha",
":test.rebootless_apex_v1",
":test.rebootless_apex_v2",
diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
index 738e68e..4684f018 100644
--- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
@@ -55,8 +55,11 @@
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 APEX_WRONG_SHA_V2 = new TestApp(
- "ApexWrongSha2", SHIM_APEX_PACKAGE_NAME, 2, /*isApex*/true,
+ "ApexWrongSha2", SHIM_APEX_PACKAGE_NAME, 2, /* isApex= */ true,
"com.android.apex.cts.shim.v2_wrong_sha.apex");
+ private static final TestApp APEX_V2 = new TestApp(
+ "ApexV2", SHIM_APEX_PACKAGE_NAME, 2, /* isApex= */ true,
+ "com.android.apex.cts.shim.v2.apex");
private File mTestStateFile = new File(
InstrumentationRegistry.getInstrumentation().getContext().getFilesDir(),
@@ -237,6 +240,96 @@
}
@Test
+ public void testApexInstallerNotInAllowListCanNotInstall_staged() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
+ // We don't really care which APEX we are trying to install here, since the session creation
+ // should fail immediately.
+ InstallUtils.commitExpectingFailure(
+ SecurityException.class,
+ "Installer not allowed to commit staged install",
+ Install.single(APEX_WRONG_SHA_V2).setBypassStangedInstallerCheck(false)
+ .setStaged());
+ }
+
+ @Test
+ public void testApexInstallerNotInAllowListCanNotInstall_nonStaged() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
+ // We don't really care which APEX we are trying to install here, since the session creation
+ // should fail immediately.
+ InstallUtils.commitExpectingFailure(
+ SecurityException.class,
+ "Installer not allowed to commit non-staged APEX install",
+ Install.single(APEX_WRONG_SHA_V2).setBypassStangedInstallerCheck(false));
+ }
+
+ @Test
+ public void testApexNotInAllowListCanNotInstall_staged() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
+ TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
+ /* isApex= */ true, "test.rebootless_apex_v2.apex");
+ InstallUtils.commitExpectingFailure(
+ AssertionError.class,
+ "Update of APEX package test.apex.rebootless is not allowed "
+ + "for com.android.tests.stagedinstallinternal",
+ Install.single(apex).setBypassAllowedApexUpdateCheck(false).setStaged());
+ }
+
+ @Test
+ public void testApexNotInAllowListCanNotInstall_nonStaged() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
+ TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
+ /* isApex= */ true, "test.rebootless_apex_v2.apex");
+ InstallUtils.commitExpectingFailure(
+ AssertionError.class,
+ "Update of APEX package test.apex.rebootless is not allowed "
+ + "for com.android.tests.stagedinstallinternal",
+ Install.single(apex).setBypassAllowedApexUpdateCheck(false));
+ }
+
+ @Test
+ public void testVendorApexWrongInstaller_staged() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
+ TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
+ /* isApex= */ true, "test.rebootless_apex_v2.apex");
+ InstallUtils.commitExpectingFailure(
+ AssertionError.class,
+ "Update of APEX package test.apex.rebootless is not allowed "
+ + "for com.android.tests.stagedinstallinternal",
+ Install.single(apex).setBypassAllowedApexUpdateCheck(false).setStaged());
+ }
+
+ @Test
+ public void testVendorApexWrongInstaller_nonStaged() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
+ TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
+ /* isApex= */ true, "test.rebootless_apex_v2.apex");
+ InstallUtils.commitExpectingFailure(
+ AssertionError.class,
+ "Update of APEX package test.apex.rebootless is not allowed "
+ + "for com.android.tests.stagedinstallinternal",
+ Install.single(apex).setBypassAllowedApexUpdateCheck(false));
+ }
+
+ @Test
+ public void testVendorApexCorrectInstaller_staged() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
+ TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
+ /* isApex= */ true, "test.rebootless_apex_v2.apex");
+ int sessionId =
+ Install.single(apex).setBypassAllowedApexUpdateCheck(false).setStaged().commit();
+ InstallUtils.getPackageInstaller().abandonSession(sessionId);
+ }
+
+ @Test
+ public void testVendorApexCorrectInstaller_nonStaged() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
+ TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
+ /* isApex= */ true, "test.rebootless_apex_v2.apex");
+ Install.single(apex).setBypassAllowedApexUpdateCheck(false).commit();
+ assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(2);
+ }
+
+ @Test
public void testRebootlessUpdates() throws Exception {
InstallUtils.dropShellPermissionIdentity();
InstallUtils.adoptShellPermissionIdentity(Manifest.permission.INSTALL_PACKAGE_UPDATES);
@@ -298,6 +391,19 @@
}
}
+ @Test
+ public void testRebootlessUpdate_hasStagedSessionWithSameApex_fails() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
+
+ int sessionId = Install.single(APEX_V2).setStaged().commit();
+ assertSessionReady(sessionId);
+ InstallUtils.commitExpectingFailure(
+ AssertionError.class,
+ "Staged session " + sessionId + " already contains " + SHIM_APEX_PACKAGE_NAME,
+ Install.single(APEX_V2));
+
+ }
+
private static void assertSessionApplied(int sessionId) {
assertSessionState(sessionId, (session) -> {
assertThat(session.isStagedSessionApplied()).isTrue();
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index 3bd3767..5021009 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -43,7 +43,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.BufferedWriter;
import java.io.File;
+import java.io.FileWriter;
import java.util.List;
import java.util.stream.Collectors;
@@ -60,6 +62,9 @@
private static final String APK_A = "TestAppAv1.apk";
private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test";
+ private static final String TEST_VENDOR_APEX_ALLOW_LIST =
+ "/vendor/etc/sysconfig/test-vendor-apex-allow-list.xml";
+
private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this);
/**
@@ -87,7 +92,8 @@
"/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex",
"/data/apex/active/" + SHIM_APEX_PACKAGE_NAME + "*.apex",
"/system/apex/test.rebootless_apex_v1.apex",
- "/data/apex/active/test.apex.rebootless*.apex");
+ "/data/apex/active/test.apex.rebootless*.apex",
+ TEST_VENDOR_APEX_ALLOW_LIST);
}
@Before
@@ -134,7 +140,23 @@
}
getDevice().remountSystemWritable();
assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName));
- getDevice().reboot();
+ }
+
+ private void pushTestVendorApexAllowList(String installerPackageName) throws Exception {
+ if (!getDevice().isAdbRoot()) {
+ getDevice().enableAdbRoot();
+ }
+ getDevice().remountSystemWritable();
+ File file = File.createTempFile("test-vendor-apex-allow-list", ".xml");
+ try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
+ final String fmt =
+ "<config>\n"
+ + " <allowed-vendor-apex package=\"test.apex.rebootless\" "
+ + " installerPackage=\"%s\" />\n"
+ + "</config>";
+ writer.write(String.format(fmt, installerPackageName));
+ }
+ getDevice().pushFile(file, TEST_VENDOR_APEX_ALLOW_LIST);
}
/**
@@ -144,6 +166,8 @@
@LargeTest
public void testDuplicateApkInApexShouldFail() throws Exception {
pushTestApex(APK_IN_APEX_TESTAPEX_NAME + "_v1.apex");
+ getDevice().reboot();
+
runPhase("testDuplicateApkInApexShouldFail_Commit");
getDevice().reboot();
runPhase("testDuplicateApkInApexShouldFail_Verify");
@@ -389,11 +413,71 @@
}
@Test
+ public void testApexInstallerNotInAllowListCanNotInstall() throws Exception {
+ assumeTrue("Device does not support updating APEX",
+ mHostUtils.isApexUpdateSupported());
+
+ runPhase("testApexInstallerNotInAllowListCanNotInstall_staged");
+ runPhase("testApexInstallerNotInAllowListCanNotInstall_nonStaged");
+ }
+
+ @Test
+ @LargeTest
+ public void testApexNotInAllowListCanNotInstall() throws Exception {
+ assumeTrue("Device does not support updating APEX",
+ mHostUtils.isApexUpdateSupported());
+
+ pushTestApex("test.rebootless_apex_v1.apex");
+ getDevice().reboot();
+
+ runPhase("testApexNotInAllowListCanNotInstall_staged");
+ runPhase("testApexNotInAllowListCanNotInstall_nonStaged");
+ }
+
+ @Test
+ @LargeTest
+ public void testVendorApexWrongInstaller() throws Exception {
+ assumeTrue("Device does not support updating APEX",
+ mHostUtils.isApexUpdateSupported());
+
+ pushTestVendorApexAllowList("com.wrong.installer");
+ pushTestApex("test.rebootless_apex_v1.apex");
+ getDevice().reboot();
+
+ runPhase("testVendorApexWrongInstaller_staged");
+ runPhase("testVendorApexWrongInstaller_nonStaged");
+ }
+
+ @Test
+ @LargeTest
+ public void testVendorApexCorrectInstaller() throws Exception {
+ assumeTrue("Device does not support updating APEX",
+ mHostUtils.isApexUpdateSupported());
+
+ pushTestVendorApexAllowList("com.android.tests.stagedinstallinternal");
+ pushTestApex("test.rebootless_apex_v1.apex");
+ getDevice().reboot();
+
+ runPhase("testVendorApexCorrectInstaller_staged");
+ runPhase("testVendorApexCorrectInstaller_nonStaged");
+ }
+
+ @Test
public void testRebootlessUpdates() throws Exception {
pushTestApex("test.rebootless_apex_v1.apex");
+ getDevice().reboot();
+
runPhase("testRebootlessUpdates");
}
+ @Test
+ public void testRebootlessUpdate_hasStagedSessionWithSameApex_fails() throws Exception {
+ assumeTrue("Device does not support updating APEX",
+ mHostUtils.isApexUpdateSupported());
+
+ runPhase("testRebootlessUpdate_hasStagedSessionWithSameApex_fails");
+ }
+
private List<String> getStagingDirectories() throws DeviceNotAvailableException {
String baseDir = "/data/app-staging";
try {