Merge "Refine dump info of One Handed Mode components" into sc-dev
diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp
index 5def55f..5bf0b84 100644
--- a/apex/appsearch/framework/Android.bp
+++ b/apex/appsearch/framework/Android.bp
@@ -33,9 +33,10 @@
java_sdk_library {
name: "framework-appsearch",
- srcs: [ ":framework-appsearch-sources" ],
+ srcs: [":framework-appsearch-sources"],
sdk_version: "core_platform", // TODO(b/146218515) should be module_current
impl_only_libs: ["framework-minus-apex"], // TODO(b/146218515) should be removed
+ libs: ["unsupportedappusage"], // TODO(b/181887768) should be removed
defaults: ["framework-module-defaults"],
permitted_packages: ["android.app.appsearch"],
aidl: {
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index 64ac63c..c112d0e 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -21,6 +21,7 @@
import android.annotation.UserIdInt;
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;
@@ -295,6 +296,19 @@
}
/**
+ * @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.
*
@@ -489,6 +503,19 @@
}
/**
+ * @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 2a941fb..4378a98 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
@@ -21,6 +21,7 @@
import android.annotation.Nullable;
import android.app.appsearch.exceptions.IllegalSchemaException;
import android.app.appsearch.util.BundleUtil;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.util.ArraySet;
@@ -747,6 +748,31 @@
}
/**
+ * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
+ * @hide
+ */
+ @Deprecated
+ @UnsupportedAppUsage
+ public Builder(@NonNull String propertyName) {
+ mBundle.putString(NAME_FIELD, propertyName);
+ mBundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_DOCUMENT);
+ mBundle.putInt(CARDINALITY_FIELD, CARDINALITY_OPTIONAL);
+ mBundle.putBoolean(INDEX_NESTED_PROPERTIES_FIELD, false);
+ }
+
+ /**
+ * @deprecated TODO(b/181887768): Exists for dogfood transition; must be removed.
+ * @hide
+ */
+ @Deprecated
+ @UnsupportedAppUsage
+ @NonNull
+ public Builder setSchemaType(@NonNull String schemaType) {
+ mBundle.putString(SCHEMA_TYPE_FIELD, 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
@@ -778,6 +804,18 @@
}
/**
+ * @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.
*
* <p>After calling this method, the builder must no longer be used.
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 2e42749..39a4884 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.appsearch.util.BundleUtil;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
@@ -137,6 +138,17 @@
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
new file mode 100644
index 0000000..7b05eac
--- /dev/null
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
@@ -0,0 +1,200 @@
+/*
+ * 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
new file mode 100644
index 0000000..9c74966
--- /dev/null
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
@@ -0,0 +1,125 @@
+/*
+ * 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 8c8ade8..5cb59b3 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
@@ -18,6 +18,7 @@
import android.annotation.CurrentTimeMillisLong;
import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
import com.android.internal.util.Preconditions;
@@ -68,7 +69,8 @@
/** Builder for {@link ReportUsageRequest} objects. */
public static final class Builder {
private final String mNamespace;
- private final String mDocumentId;
+ // TODO(b/181887768): Make this final
+ private String mDocumentId;
private Long mUsageTimestampMillis;
private boolean mBuilt = false;
@@ -79,6 +81,40 @@
}
/**
+ * @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 b648071..9a1796c 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
@@ -192,7 +192,7 @@
return this;
}
- /** @deprecated this method exists only for dogfooder transition and must be removed */
+ /** @deprecated This method exists only for dogfooder transition and must be removed. */
@Deprecated
@NonNull
public Builder addMatch(@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 b7bd387..7ad5fe8 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.util.ArraySet;
@@ -324,6 +325,17 @@
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/core/api/current.txt b/core/api/current.txt
index f23d24a..b257f5e 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -586,7 +586,6 @@
field public static final int dropDownWidth = 16843362; // 0x1010262
field public static final int duplicateParentState = 16842985; // 0x10100e9
field public static final int duration = 16843160; // 0x1010198
- field public static final int edgeEffectType;
field public static final int editTextBackground = 16843602; // 0x1010352
field public static final int editTextColor = 16843601; // 0x1010351
field public static final int editTextPreferenceStyle = 16842898; // 0x1010092
@@ -12582,7 +12581,7 @@
method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentActivityOptions(@Nullable android.content.ComponentName, @Nullable android.content.Intent[], @NonNull android.content.Intent, int);
method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentContentProviders(@NonNull android.content.Intent, int);
method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryIntentServices(@NonNull android.content.Intent, int);
- method @NonNull public abstract java.util.List<android.content.pm.PermissionInfo> queryPermissionsByGroup(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public abstract java.util.List<android.content.pm.PermissionInfo> queryPermissionsByGroup(@Nullable String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryProviderProperty(@NonNull String);
method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryReceiverProperty(@NonNull String);
method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryServiceProperty(@NonNull String);
@@ -53791,7 +53790,6 @@
method public int getCheckedItemPosition();
method public android.util.SparseBooleanArray getCheckedItemPositions();
method public int getChoiceMode();
- method public int getEdgeEffectType();
method public int getListPaddingBottom();
method public int getListPaddingLeft();
method public int getListPaddingRight();
@@ -53833,7 +53831,6 @@
method public void setChoiceMode(int);
method public void setDrawSelectorOnTop(boolean);
method public void setEdgeEffectColor(@ColorInt int);
- method public void setEdgeEffectType(int);
method public void setFastScrollAlwaysVisible(boolean);
method public void setFastScrollEnabled(boolean);
method public void setFastScrollStyle(int);
@@ -54524,7 +54521,6 @@
method @ColorInt public int getColor();
method public float getDistance();
method public int getMaxHeight();
- method public int getType();
method public boolean isFinished();
method public void onAbsorb(int);
method public void onPull(float);
@@ -54534,10 +54530,7 @@
method public void setBlendMode(@Nullable android.graphics.BlendMode);
method public void setColor(@ColorInt int);
method public void setSize(int, int);
- method public void setType(int);
field public static final android.graphics.BlendMode DEFAULT_BLEND_MODE;
- field public static final int TYPE_GLOW = 0; // 0x0
- field public static final int TYPE_STRETCH = 1; // 0x1
}
public class EditText extends android.widget.TextView {
@@ -54842,7 +54835,6 @@
method public boolean executeKeyEvent(android.view.KeyEvent);
method public void fling(int);
method public boolean fullScroll(int);
- method public int getEdgeEffectType();
method @ColorInt public int getLeftEdgeEffectColor();
method public int getMaxScrollAmount();
method @ColorInt public int getRightEdgeEffectColor();
@@ -54850,7 +54842,6 @@
method public boolean isSmoothScrollingEnabled();
method public boolean pageScroll(int);
method public void setEdgeEffectColor(@ColorInt int);
- method public void setEdgeEffectType(int);
method public void setFillViewport(boolean);
method public void setLeftEdgeEffectColor(@ColorInt int);
method public void setRightEdgeEffectColor(@ColorInt int);
@@ -55736,7 +55727,6 @@
method public void fling(int);
method public boolean fullScroll(int);
method @ColorInt public int getBottomEdgeEffectColor();
- method public int getEdgeEffectType();
method public int getMaxScrollAmount();
method @ColorInt public int getTopEdgeEffectColor();
method public boolean isFillViewport();
@@ -55745,7 +55735,6 @@
method public void scrollToDescendant(@NonNull android.view.View);
method public void setBottomEdgeEffectColor(@ColorInt int);
method public void setEdgeEffectColor(@ColorInt int);
- method public void setEdgeEffectType(int);
method public void setFillViewport(boolean);
method public void setSmoothScrollingEnabled(boolean);
method public void setTopEdgeEffectColor(@ColorInt int);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b3e656d..ff210e1 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5613,7 +5613,7 @@
}
/** Performs the activity relaunch locally vs. requesting from system-server. */
- private void handleRelaunchActivityLocally(IBinder token) {
+ public void handleRelaunchActivityLocally(IBinder token) {
final ActivityClientRecord r = mActivities.get(token);
if (r == null) {
Log.w(TAG, "Activity to relaunch no longer exists");
@@ -5977,20 +5977,6 @@
// Update all affected Resources objects to use new ResourcesImpl
mResourcesManager.applyNewResourceDirsLocked(ai, oldResDirs);
}
-
- ApplicationPackageManager.configurationChanged();
-
- // Trigger a regular Configuration change event, only with a different assetsSeq number
- // so that we actually call through to all components.
- // TODO(adamlesinski): Change this to make use of ActivityManager's upcoming ability to
- // store configurations per-process.
- final Configuration config = mConfigurationController.getConfiguration();
- Configuration newConfig = new Configuration();
- newConfig.assetsSeq = (config != null ? config.assetsSeq : 0) + 1;
- mConfigurationController.handleConfigurationChanged(newConfig, null /* compat */);
-
- // Preserve windows to avoid black flickers when overlays change.
- relaunchAllActivities(true /* preserveWindows */, "handleApplicationInfoChanged");
}
/**
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index d77ce4a..efeef1b 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1894,7 +1894,7 @@
OP_MANAGE_MEDIA, // MANAGE_MEDIA
OP_BLUETOOTH_CONNECT, // OP_BLUETOOTH_CONNECT
OP_UWB_RANGING, // OP_UWB_RANGING
- OP_ACTIVITY_RECOGNITION_SOURCE, // OP_ACTIVITY_RECOGNITION_SOURCE
+ OP_ACTIVITY_RECOGNITION, // OP_ACTIVITY_RECOGNITION_SOURCE
OP_BLUETOOTH_ADVERTISE, // OP_BLUETOOTH_ADVERTISE
OP_RECORD_INCOMING_PHONE_AUDIO, // OP_RECORD_INCOMING_PHONE_AUDIO
};
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index f07f453..7dc662a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1847,6 +1847,8 @@
* Delegation of certificate installation and management. This scope grants access to the
* {@link #getInstalledCaCerts}, {@link #hasCaCertInstalled}, {@link #installCaCert},
* {@link #uninstallCaCert}, {@link #uninstallAllUserCaCerts} and {@link #installKeyPair} APIs.
+ * This scope also grants the ability to read identifiers that the delegating device owner or
+ * profile owner can obtain. See {@link #getEnrollmentSpecificId()}.
*/
public static final String DELEGATION_CERT_INSTALL = "delegation-cert-install";
@@ -13620,12 +13622,20 @@
* It is available both in a work profile and on a fully-managed device.
* The identifier would be consistent even if the work profile is removed and enrolled again
* (to the same organization), or the device is factory reset and re-enrolled.
-
+ *
* Can only be called by the Profile Owner or Device Owner, if the
* {@link #setOrganizationId(String)} was previously called.
* If {@link #setOrganizationId(String)} was not called, then the returned value will be an
* empty string.
*
+ * <p>Note about access to device identifiers: a device owner, a profile owner of an
+ * organization-owned device or the delegated certificate installer (holding the
+ * {@link #DELEGATION_CERT_INSTALL} delegation) on such a device can still obtain hardware
+ * identifiers by calling e.g. {@link android.os.Build#getSerial()}, in addition to using
+ * this method. However, a profile owner on a personal (non organization-owned) device, or the
+ * delegated certificate installer on such a device, cannot obtain hardware identifiers anymore
+ * and must switch to using this method.
+ *
* @return A stable, enrollment-specific identifier.
* @throws SecurityException if the caller is not a profile owner or device owner.
*/
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 99bbcde..07d8478 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4605,7 +4605,7 @@
* Query for all of the permissions associated with a particular group.
*
* @param permissionGroup The fully qualified name (i.e. com.google.permission.LOGIN)
- * of the permission group you are interested in. Use null to
+ * of the permission group you are interested in. Use {@code null} to
* find all of the permissions not associated with a group.
* @param flags Additional option flags to modify the data returned.
* @return Returns a list of {@link PermissionInfo} containing information
@@ -4615,7 +4615,7 @@
*/
//@Deprecated
@NonNull
- public abstract List<PermissionInfo> queryPermissionsByGroup(@NonNull String permissionGroup,
+ public abstract List<PermissionInfo> queryPermissionsByGroup(@Nullable String permissionGroup,
@PermissionInfoFlags int flags) throws NameNotFoundException;
/**
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 22d75ef..725576f 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -3069,6 +3069,13 @@
/**
* @hide
*/
+ public static void setCompatibilityModeEnabled(boolean compatibilityModeEnabled) {
+ sCompatibilityModeEnabled = compatibilityModeEnabled;
+ }
+
+ /**
+ * @hide
+ */
public static void readConfigUseRoundIcon(Resources r) {
if (r != null) {
sUseRoundIcon = r.getBoolean(com.android.internal.R.bool.config_useRoundIcon);
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index 250145e..52dad3e 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -24,7 +24,10 @@
import android.annotation.TestApi;
import android.content.Context;
+import com.android.internal.util.ArrayUtils;
+
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* Manages the state of the system for devices with user-configurable hardware like a foldable
@@ -170,4 +173,34 @@
*/
void onStateChanged(int state);
}
+
+ /**
+ * Listens to changes in device state and reports the state as folded if the device state
+ * matches the value in the {@link com.android.internal.R.integer.config_foldedDeviceState}
+ * resource.
+ * @hide
+ */
+ public static class FoldStateListener implements DeviceStateCallback {
+ private final int[] mFoldedDeviceStates;
+ private final Consumer<Boolean> mDelegate;
+
+ @Nullable
+ private Boolean lastResult;
+
+ public FoldStateListener(Context context, Consumer<Boolean> listener) {
+ mFoldedDeviceStates = context.getResources().getIntArray(
+ com.android.internal.R.array.config_foldedDeviceStates);
+ mDelegate = listener;
+ }
+
+ @Override
+ public final void onStateChanged(int state) {
+ final boolean folded = ArrayUtils.contains(mFoldedDeviceStates, state);
+
+ if (lastResult == null || !lastResult.equals(folded)) {
+ lastResult = folded;
+ mDelegate.accept(folded);
+ }
+ }
+ }
}
diff --git a/core/java/android/os/AggregateBatteryConsumer.java b/core/java/android/os/AggregateBatteryConsumer.java
new file mode 100644
index 0000000..449e3ae
--- /dev/null
+++ b/core/java/android/os/AggregateBatteryConsumer.java
@@ -0,0 +1,94 @@
+/*
+ * 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 android.os;
+
+import android.annotation.NonNull;
+
+/**
+ * Contains power consumption data across the entire device.
+ *
+ * {@hide}
+ */
+public final class AggregateBatteryConsumer extends BatteryConsumer implements Parcelable {
+
+ private final double mConsumedPowerMah;
+
+ public AggregateBatteryConsumer(@NonNull Builder builder) {
+ super(builder.mPowerComponentsBuilder.build());
+ mConsumedPowerMah = builder.mConsumedPowerMah;
+ }
+
+ private AggregateBatteryConsumer(@NonNull Parcel source) {
+ super(new PowerComponents(source));
+ mConsumedPowerMah = source.readDouble();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeDouble(mConsumedPowerMah);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Creator<AggregateBatteryConsumer> CREATOR =
+ new Creator<AggregateBatteryConsumer>() {
+ public AggregateBatteryConsumer createFromParcel(@NonNull Parcel source) {
+ return new AggregateBatteryConsumer(source);
+ }
+
+ public AggregateBatteryConsumer[] newArray(int size) {
+ return new AggregateBatteryConsumer[size];
+ }
+ };
+
+ @Override
+ public double getConsumedPower() {
+ return mConsumedPowerMah;
+ }
+
+ /**
+ * Builder for DeviceBatteryConsumer.
+ */
+ public static final class Builder extends BaseBuilder<AggregateBatteryConsumer.Builder> {
+ private double mConsumedPowerMah;
+
+ public Builder(@NonNull String[] customPowerComponentNames, boolean includePowerModels) {
+ super(customPowerComponentNames, includePowerModels);
+ }
+
+ /**
+ * Sets the total power included in this aggregate.
+ */
+ public Builder setConsumedPower(double consumedPowerMah) {
+ mConsumedPowerMah = consumedPowerMah;
+ return this;
+ }
+
+ /**
+ * Creates a read-only object out of the Builder values.
+ */
+ @NonNull
+ public AggregateBatteryConsumer build() {
+ return new AggregateBatteryConsumer(this);
+ }
+ }
+}
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index 32ca92c..6b628b0 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -27,7 +27,7 @@
*
* @hide
*/
-public abstract class BatteryConsumer {
+public class BatteryConsumer {
/**
* Power usage component, describing the particular part of the system
@@ -72,14 +72,15 @@
public static final int POWER_COMPONENT_WIFI = 11;
public static final int POWER_COMPONENT_WAKELOCK = 12;
public static final int POWER_COMPONENT_MEMORY = 13;
- public static final int POWER_COMPONENT_PHONE = 13;
- public static final int POWER_COMPONENT_IDLE = 15;
+ public static final int POWER_COMPONENT_PHONE = 14;
+ public static final int POWER_COMPONENT_AMBIENT_DISPLAY = 15;
+ public static final int POWER_COMPONENT_IDLE = 16;
// Power that is re-attributed to other battery consumers. For example, for System Server
// this represents the power attributed to apps requesting system services.
// The value should be negative or zero.
- public static final int POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS = 16;
+ public static final int POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS = 17;
- public static final int POWER_COMPONENT_COUNT = 17;
+ public static final int POWER_COMPONENT_COUNT = 18;
public static final int FIRST_CUSTOM_POWER_COMPONENT_ID = 1000;
public static final int LAST_CUSTOM_POWER_COMPONENT_ID = 9999;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 9ec6938..eec6810 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -3234,6 +3234,11 @@
public abstract int getMaxLearnedBatteryCapacity() ;
/**
+ * @return The latest learned battery capacity in uAh.
+ */
+ public abstract int getLearnedBatteryCapacity();
+
+ /**
* Return the array of discharge step durations.
*/
public abstract LevelStepTracker getDischargeLevelStepTracker();
@@ -3925,7 +3930,9 @@
getStartClockTime(),
whichBatteryScreenOffRealtime / 1000, whichBatteryScreenOffUptime / 1000,
getEstimatedBatteryCapacity(),
- getMinLearnedBatteryCapacity(), getMaxLearnedBatteryCapacity(),
+ getLearnedBatteryCapacity(),
+ getMinLearnedBatteryCapacity(),
+ getMaxLearnedBatteryCapacity(),
screenDozeTime / 1000);
@@ -4688,22 +4695,31 @@
pw.println(sb.toString());
}
+ final int lastLearnedBatteryCapacity = getLearnedBatteryCapacity();
+ if (lastLearnedBatteryCapacity > 0) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Last learned battery capacity: ");
+ sb.append(BatteryStatsHelper.makemAh(lastLearnedBatteryCapacity / 1000));
+ sb.append(" mAh");
+ pw.println(sb.toString());
+ }
final int minLearnedBatteryCapacity = getMinLearnedBatteryCapacity();
if (minLearnedBatteryCapacity > 0) {
sb.setLength(0);
sb.append(prefix);
- sb.append(" Min learned battery capacity: ");
- sb.append(BatteryStatsHelper.makemAh(minLearnedBatteryCapacity / 1000));
- sb.append(" mAh");
+ sb.append(" Min learned battery capacity: ");
+ sb.append(BatteryStatsHelper.makemAh(minLearnedBatteryCapacity / 1000));
+ sb.append(" mAh");
pw.println(sb.toString());
}
final int maxLearnedBatteryCapacity = getMaxLearnedBatteryCapacity();
if (maxLearnedBatteryCapacity > 0) {
sb.setLength(0);
sb.append(prefix);
- sb.append(" Max learned battery capacity: ");
- sb.append(BatteryStatsHelper.makemAh(maxLearnedBatteryCapacity / 1000));
- sb.append(" mAh");
+ sb.append(" Max learned battery capacity: ");
+ sb.append(BatteryStatsHelper.makemAh(maxLearnedBatteryCapacity / 1000));
+ sb.append(" mAh");
pw.println(sb.toString());
}
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index efdef62..6bc861f 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.util.Range;
import android.util.SparseArray;
@@ -23,16 +24,53 @@
import com.android.internal.os.BatteryStatsHistory;
import com.android.internal.os.BatteryStatsHistoryIterator;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
/**
* Contains a snapshot of battery attribution data, on a per-subsystem and per-UID basis.
+ * <p>
+ * The totals for the entire device are returned as AggregateBatteryConsumers, which can be
+ * obtained by calling {@link #getAggregateBatteryConsumer(int)}.
+ * <p>
+ * Power attributed to individual apps is returned as UidBatteryConsumers, see
+ * {@link #getUidBatteryConsumers()}.
*
* @hide
*/
public final class BatteryUsageStats implements Parcelable {
- private final double mConsumedPower;
+
+ /**
+ * Scope of battery stats included in a BatteryConsumer: the entire device, just
+ * the apps, etc.
+ *
+ * @hide
+ */
+ @IntDef(prefix = {"AGGREGATE_BATTERY_CONSUMER_SCOPE_"}, value = {
+ AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
+ AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public static @interface AggregateBatteryConsumerScope {
+ }
+
+ /**
+ * Power consumption by the entire device, since last charge. The power usage in this
+ * scope includes both the power attributed to apps and the power unattributed to any
+ * apps.
+ */
+ public static final int AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE = 0;
+
+ /**
+ * Aggregated power consumed by all applications, combined, since last charge. This is
+ * the sum of power reported in UidBatteryConsumers.
+ */
+ public static final int AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS = 1;
+
+ public static final int AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT = 2;
+
private final int mDischargePercentage;
private final long mStatsStartTimestampMs;
private final double mDischargedPowerLowerBound;
@@ -41,8 +79,8 @@
private final long mChargeTimeRemainingMs;
private final String[] mCustomPowerComponentNames;
private final List<UidBatteryConsumer> mUidBatteryConsumers;
- private final List<SystemBatteryConsumer> mSystemBatteryConsumers;
private final List<UserBatteryConsumer> mUserBatteryConsumers;
+ private final AggregateBatteryConsumer[] mAggregateBatteryConsumers;
private final Parcel mHistoryBuffer;
private final List<BatteryStats.HistoryTag> mHistoryTagPool;
@@ -57,8 +95,7 @@
mChargeTimeRemainingMs = builder.mChargeTimeRemainingMs;
mCustomPowerComponentNames = builder.mCustomPowerComponentNames;
- double totalPower = 0;
-
+ double totalPowerMah = 0;
final int uidBatteryConsumerCount = builder.mUidBatteryConsumerBuilders.size();
mUidBatteryConsumers = new ArrayList<>(uidBatteryConsumerCount);
for (int i = 0; i < uidBatteryConsumerCount; i++) {
@@ -66,30 +103,28 @@
builder.mUidBatteryConsumerBuilders.valueAt(i);
if (!uidBatteryConsumerBuilder.isExcludedFromBatteryUsageStats()) {
final UidBatteryConsumer consumer = uidBatteryConsumerBuilder.build();
- totalPower += consumer.getConsumedPower();
+ totalPowerMah += consumer.getConsumedPower();
mUidBatteryConsumers.add(consumer);
}
}
- final int systemBatteryConsumerCount = builder.mSystemBatteryConsumerBuilders.size();
- mSystemBatteryConsumers = new ArrayList<>(systemBatteryConsumerCount);
- for (int i = 0; i < systemBatteryConsumerCount; i++) {
- final SystemBatteryConsumer consumer =
- builder.mSystemBatteryConsumerBuilders.valueAt(i).build();
- totalPower += consumer.getConsumedPower() - consumer.getPowerConsumedByApps();
- mSystemBatteryConsumers.add(consumer);
- }
-
final int userBatteryConsumerCount = builder.mUserBatteryConsumerBuilders.size();
mUserBatteryConsumers = new ArrayList<>(userBatteryConsumerCount);
for (int i = 0; i < userBatteryConsumerCount; i++) {
final UserBatteryConsumer consumer =
builder.mUserBatteryConsumerBuilders.valueAt(i).build();
- totalPower += consumer.getConsumedPower();
+ totalPowerMah += consumer.getConsumedPower();
mUserBatteryConsumers.add(consumer);
}
- mConsumedPower = totalPower;
+ builder.getAggregateBatteryConsumerBuilder(AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+ .setConsumedPower(totalPowerMah);
+
+ mAggregateBatteryConsumers =
+ new AggregateBatteryConsumer[AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT];
+ for (int i = 0; i < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; i++) {
+ mAggregateBatteryConsumers[i] = builder.mAggregateBatteryConsumersBuilders[i].build();
+ }
}
/**
@@ -101,6 +136,15 @@
}
/**
+ * Total amount of battery charge drained since BatteryStats reset (e.g. due to being fully
+ * charged), in mAh
+ */
+ public double getConsumedPower() {
+ return mAggregateBatteryConsumers[AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE]
+ .getConsumedPower();
+ }
+
+ /**
* Portion of battery charge drained since BatteryStats reset (e.g. due to being fully
* charged), as percentage of the full charge in the range [0:100]
*/
@@ -136,11 +180,11 @@
}
/**
- * Total amount of battery charge drained since BatteryStats reset (e.g. due to being fully
- * charged), in mAh
+ * Returns a battery consumer for the specified battery consumer type.
*/
- public double getConsumedPower() {
- return mConsumedPower;
+ public BatteryConsumer getAggregateBatteryConsumer(
+ @AggregateBatteryConsumerScope int scope) {
+ return mAggregateBatteryConsumers[scope];
}
@NonNull
@@ -149,11 +193,6 @@
}
@NonNull
- public List<SystemBatteryConsumer> getSystemBatteryConsumers() {
- return mSystemBatteryConsumers;
- }
-
- @NonNull
public List<UserBatteryConsumer> getUserBatteryConsumers() {
return mUserBatteryConsumers;
}
@@ -178,13 +217,19 @@
private BatteryUsageStats(@NonNull Parcel source) {
mStatsStartTimestampMs = source.readLong();
- mConsumedPower = source.readDouble();
mDischargePercentage = source.readInt();
mDischargedPowerLowerBound = source.readDouble();
mDischargedPowerUpperBound = source.readDouble();
mBatteryTimeRemainingMs = source.readLong();
mChargeTimeRemainingMs = source.readLong();
mCustomPowerComponentNames = source.readStringArray();
+ mAggregateBatteryConsumers =
+ new AggregateBatteryConsumer[AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT];
+ for (int i = 0; i < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; i++) {
+ mAggregateBatteryConsumers[i] =
+ AggregateBatteryConsumer.CREATOR.createFromParcel(source);
+ mAggregateBatteryConsumers[i].setCustomPowerComponentNames(mCustomPowerComponentNames);
+ }
int uidCount = source.readInt();
mUidBatteryConsumers = new ArrayList<>(uidCount);
for (int i = 0; i < uidCount; i++) {
@@ -193,14 +238,6 @@
consumer.setCustomPowerComponentNames(mCustomPowerComponentNames);
mUidBatteryConsumers.add(consumer);
}
- int sysCount = source.readInt();
- mSystemBatteryConsumers = new ArrayList<>(sysCount);
- for (int i = 0; i < sysCount; i++) {
- final SystemBatteryConsumer consumer =
- SystemBatteryConsumer.CREATOR.createFromParcel(source);
- consumer.setCustomPowerComponentNames(mCustomPowerComponentNames);
- mSystemBatteryConsumers.add(consumer);
- }
int userCount = source.readInt();
mUserBatteryConsumers = new ArrayList<>(userCount);
for (int i = 0; i < userCount; i++) {
@@ -237,21 +274,19 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeLong(mStatsStartTimestampMs);
- dest.writeDouble(mConsumedPower);
dest.writeInt(mDischargePercentage);
dest.writeDouble(mDischargedPowerLowerBound);
dest.writeDouble(mDischargedPowerUpperBound);
dest.writeLong(mBatteryTimeRemainingMs);
dest.writeLong(mChargeTimeRemainingMs);
dest.writeStringArray(mCustomPowerComponentNames);
+ for (int i = 0; i < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; i++) {
+ mAggregateBatteryConsumers[i].writeToParcel(dest, flags);
+ }
dest.writeInt(mUidBatteryConsumers.size());
for (int i = mUidBatteryConsumers.size() - 1; i >= 0; i--) {
mUidBatteryConsumers.get(i).writeToParcel(dest, flags);
}
- dest.writeInt(mSystemBatteryConsumers.size());
- for (int i = mSystemBatteryConsumers.size() - 1; i >= 0; i--) {
- mSystemBatteryConsumers.get(i).writeToParcel(dest, flags);
- }
dest.writeInt(mUserBatteryConsumers.size());
for (int i = mUserBatteryConsumers.size() - 1; i >= 0; i--) {
mUserBatteryConsumers.get(i).writeToParcel(dest, flags);
@@ -299,10 +334,10 @@
private double mDischargedPowerUpperBoundMah;
private long mBatteryTimeRemainingMs = -1;
private long mChargeTimeRemainingMs = -1;
+ private final AggregateBatteryConsumer.Builder[] mAggregateBatteryConsumersBuilders =
+ new AggregateBatteryConsumer.Builder[AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT];
private final SparseArray<UidBatteryConsumer.Builder> mUidBatteryConsumerBuilders =
new SparseArray<>();
- private final SparseArray<SystemBatteryConsumer.Builder> mSystemBatteryConsumerBuilders =
- new SparseArray<>();
private final SparseArray<UserBatteryConsumer.Builder> mUserBatteryConsumerBuilders =
new SparseArray<>();
private Parcel mHistoryBuffer;
@@ -315,6 +350,10 @@
public Builder(@NonNull String[] customPowerComponentNames, boolean includePowerModels) {
mCustomPowerComponentNames = customPowerComponentNames;
mIncludePowerModels = includePowerModels;
+ for (int i = 0; i < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; i++) {
+ mAggregateBatteryConsumersBuilders[i] = new AggregateBatteryConsumer.Builder(
+ customPowerComponentNames, includePowerModels);
+ }
}
/**
@@ -386,7 +425,17 @@
}
/**
- * Creates or returns a exiting UidBatteryConsumer, which represents battery attribution
+ * Creates or returns an AggregateBatteryConsumer builder, which represents aggregate
+ * battery consumption data for the specified scope.
+ */
+ @NonNull
+ public AggregateBatteryConsumer.Builder getAggregateBatteryConsumerBuilder(
+ @AggregateBatteryConsumerScope int scope) {
+ return mAggregateBatteryConsumersBuilders[scope];
+ }
+
+ /**
+ * Creates or returns a UidBatteryConsumer, which represents battery attribution
* data for an individual UID.
*/
@NonNull
@@ -403,23 +452,7 @@
}
/**
- * Creates or returns a exiting SystemBatteryConsumer, which represents battery attribution
- * data for a specific drain type.
- */
- @NonNull
- public SystemBatteryConsumer.Builder getOrCreateSystemBatteryConsumerBuilder(
- @SystemBatteryConsumer.DrainType int drainType) {
- SystemBatteryConsumer.Builder builder = mSystemBatteryConsumerBuilders.get(drainType);
- if (builder == null) {
- builder = new SystemBatteryConsumer.Builder(mCustomPowerComponentNames,
- mIncludePowerModels, drainType);
- mSystemBatteryConsumerBuilders.put(drainType, builder);
- }
- return builder;
- }
-
- /**
- * Creates or returns a exiting UserBatteryConsumer, which represents battery attribution
+ * Creates or returns a UserBatteryConsumer, which represents battery attribution
* data for an individual {@link UserHandle}.
*/
@NonNull
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 7b8fdd7..5b3bc26 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -174,15 +174,13 @@
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
- * <li>If the calling app is the device or profile owner and has been granted the
- * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
- * owns a managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
- * Profile owner access is deprecated and will be removed in a future release.
* <li>If the calling app has carrier privileges (see {@link
* android.telephony.TelephonyManager#hasCarrierPrivileges}) on any active subscription.
* <li>If the calling app is the default SMS role holder (see {@link
* android.app.role.RoleManager#isRoleHeld(String)}).
+ * <li>If the calling app is the device owner of a fully-managed device, a profile
+ * owner of an organization-owned device, or their delegates (see {@link
+ * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
* </ul>
*
* <p>If the calling app does not meet one of these requirements then this method will behave
diff --git a/core/java/android/os/SystemBatteryConsumer.java b/core/java/android/os/SystemBatteryConsumer.java
deleted file mode 100644
index 7618339..0000000
--- a/core/java/android/os/SystemBatteryConsumer.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.util.Slog;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-
-
-/**
- * Contains power consumption data attributed to a system-wide drain type.
- *
- * {@hide}
- */
-public class SystemBatteryConsumer extends BatteryConsumer implements Parcelable {
- private static final String TAG = "SystemBatteryConsumer";
-
- // ****************
- // This list must be kept current with atoms.proto (frameworks/base/cmds/statsd/src/atoms.proto)
- // so the constant values must never change.
- // ****************
- @IntDef(prefix = {"DRAIN_TYPE_"}, value = {
- DRAIN_TYPE_AMBIENT_DISPLAY,
- // Reserved: APP
- DRAIN_TYPE_BLUETOOTH,
- DRAIN_TYPE_CAMERA,
- DRAIN_TYPE_MOBILE_RADIO,
- DRAIN_TYPE_FLASHLIGHT,
- DRAIN_TYPE_IDLE,
- DRAIN_TYPE_MEMORY,
- // Reserved: OVERCOUNTED,
- DRAIN_TYPE_PHONE,
- DRAIN_TYPE_SCREEN,
- // Reserved: UNACCOUNTED,
- // Reserved: USER,
- DRAIN_TYPE_WIFI,
- DRAIN_TYPE_CUSTOM,
- })
- @Retention(RetentionPolicy.SOURCE)
- public static @interface DrainType {
- }
-
- public static final int DRAIN_TYPE_AMBIENT_DISPLAY = 0;
- public static final int DRAIN_TYPE_BLUETOOTH = 2;
- public static final int DRAIN_TYPE_CAMERA = 3;
- public static final int DRAIN_TYPE_MOBILE_RADIO = 4;
- public static final int DRAIN_TYPE_FLASHLIGHT = 5;
- public static final int DRAIN_TYPE_IDLE = 6;
- public static final int DRAIN_TYPE_MEMORY = 7;
- public static final int DRAIN_TYPE_PHONE = 9;
- public static final int DRAIN_TYPE_SCREEN = 10;
- public static final int DRAIN_TYPE_WIFI = 13;
- public static final int DRAIN_TYPE_CUSTOM = 14;
-
- @DrainType
- private final int mDrainType;
-
- private final double mPowerConsumedByAppsMah;
-
- @DrainType
- public int getDrainType() {
- return mDrainType;
- }
-
- private SystemBatteryConsumer(@NonNull SystemBatteryConsumer.Builder builder) {
- super(builder.mPowerComponentsBuilder.build());
- mDrainType = builder.mDrainType;
- mPowerConsumedByAppsMah = builder.mPowerConsumedByAppsMah;
- if (mPowerConsumedByAppsMah > getConsumedPower()) {
- Slog.wtf(TAG,
- "Power attributed to apps exceeds total: drain type = " + mDrainType
- + " total consumed power = " + getConsumedPower()
- + " power consumed by apps = " + mPowerConsumedByAppsMah);
- }
- }
-
- private SystemBatteryConsumer(Parcel in) {
- super(new PowerComponents(in));
- mDrainType = in.readInt();
- mPowerConsumedByAppsMah = in.readDouble();
- }
-
- public double getPowerConsumedByApps() {
- return mPowerConsumedByAppsMah;
- }
-
- /**
- * Returns the amount of time this consumer was operating.
- */
- public long getUsageDurationMillis() {
- return mPowerComponents.getMaxComponentUsageDurationMillis();
- }
-
- /**
- * Writes the contents into a Parcel.
- */
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeInt(mDrainType);
- dest.writeDouble(mPowerConsumedByAppsMah);
- }
-
- public static final Creator<SystemBatteryConsumer> CREATOR =
- new Creator<SystemBatteryConsumer>() {
- @Override
- public SystemBatteryConsumer createFromParcel(Parcel in) {
- return new SystemBatteryConsumer(in);
- }
-
- @Override
- public SystemBatteryConsumer[] newArray(int size) {
- return new SystemBatteryConsumer[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- /**
- * Builder for SystemBatteryConsumer.
- */
- public static final class Builder extends BaseBuilder<Builder> {
- @DrainType
- private final int mDrainType;
- private double mPowerConsumedByAppsMah;
- private List<UidBatteryConsumer.Builder> mUidBatteryConsumers;
-
- Builder(@NonNull String[] customPowerComponentNames,
- boolean includePowerModels, @DrainType int drainType) {
- super(customPowerComponentNames, includePowerModels);
- mDrainType = drainType;
- }
-
- /**
- * Sets the amount of power used by this system component that is attributed to apps.
- * It should not exceed the total consumed power.
- */
- public Builder setPowerConsumedByApps(double powerConsumedByAppsMah) {
- mPowerConsumedByAppsMah = powerConsumedByAppsMah;
- return this;
- }
-
- /**
- * Add a UidBatteryConsumer to this SystemBatteryConsumer. For example,
- * the UidBatteryConsumer with the UID == {@link Process#BLUETOOTH_UID} should
- * be added to the SystemBatteryConsumer with the drain type == {@link
- * #DRAIN_TYPE_BLUETOOTH}.
- * <p>
- * Calculated power and duration components of the added battery consumers
- * are aggregated at the time the SystemBatteryConsumer is built by the {@link #build()}
- * method.
- * </p>
- */
- public void addUidBatteryConsumer(UidBatteryConsumer.Builder uidBatteryConsumerBuilder) {
- if (mUidBatteryConsumers == null) {
- mUidBatteryConsumers = new ArrayList<>();
- }
- mUidBatteryConsumers.add(uidBatteryConsumerBuilder);
- }
-
- /**
- * Creates a read-only object out of the Builder values.
- */
- @NonNull
- public SystemBatteryConsumer build() {
- if (mUidBatteryConsumers != null) {
- for (int i = mUidBatteryConsumers.size() - 1; i >= 0; i--) {
- UidBatteryConsumer.Builder uidBatteryConsumer = mUidBatteryConsumers.get(i);
- mPowerComponentsBuilder.addPowerAndDuration(
- uidBatteryConsumer.mPowerComponentsBuilder);
- }
- }
- return new SystemBatteryConsumer(this);
- }
- }
-}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 17c90d6..d490e7a 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -195,7 +195,7 @@
* @hide Pending API
*/
@Nullable
- public List<PermissionInfo> queryPermissionsByGroup(@NonNull String groupName,
+ public List<PermissionInfo> queryPermissionsByGroup(@Nullable String groupName,
@PackageManager.PermissionInfoFlags int flags) {
try {
final ParceledListSlice<PermissionInfo> parceledList =
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index 7e0117d..5f41174 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -52,8 +52,20 @@
import java.util.function.IntConsumer;
/**
- * Implemented by an application that wants to offer detection for hotword. The system will
- * start the service after calling {@link VoiceInteractionService#setHotwordDetectionConfig}.
+ * Implemented by an application that wants to offer detection for hotword. The service can be used
+ * for both DSP and non-DSP detectors.
+ *
+ * The system will bind an application's {@link VoiceInteractionService} first. When {@link
+ * VoiceInteractionService#createHotwordDetector(PersistableBundle, SharedMemory,
+ * HotwordDetector.Callback)} or {@link VoiceInteractionService#createAlwaysOnHotwordDetector(
+ * String, Locale, PersistableBundle, SharedMemory, AlwaysOnHotwordDetector.Callback)} is called,
+ * the system will bind application's {@link HotwordDetectionService}. Either on a hardware
+ * trigger or on request from the {@link VoiceInteractionService}, the system calls into the
+ * {@link HotwordDetectionService} to request detection. The {@link HotwordDetectionService} then
+ * uses {@link Callback#onDetected(HotwordDetectedResult)} to inform the system that a relevant
+ * keyphrase was detected, or if applicable uses {@link Callback#onRejected(HotwordRejectedResult)}
+ * to inform the system that a keyphrase was not detected. The system then relays this result to
+ * the {@link VoiceInteractionService} through {@link HotwordDetector.Callback}.
*
* @hide
*/
diff --git a/core/java/android/view/DragAndDropPermissions.java b/core/java/android/view/DragAndDropPermissions.java
index 16204d8..973836a 100644
--- a/core/java/android/view/DragAndDropPermissions.java
+++ b/core/java/android/view/DragAndDropPermissions.java
@@ -19,7 +19,6 @@
import static java.lang.Integer.toHexString;
import android.app.Activity;
-import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -62,24 +61,6 @@
private static final String TAG = "DragAndDrop";
private static final boolean DEBUG = false;
- /**
- * Permissions for a drop can be granted in one of two ways:
- * <ol>
- * <li>An app can explicitly request permissions using
- * {@link Activity#requestDragAndDropPermissions(DragEvent)}. In this case permissions are
- * revoked automatically when then activity is destroyed. See {@link #take(IBinder)}.
- * <li>The platform can request permissions on behalf of the app (e.g. in
- * {@link android.widget.Editor}). In this case permissions are revoked automatically when
- * the app process terminates. See {@link #takeTransient()}.
- * </ol>
- *
- * <p>In order to implement the second case above, we create a static token object here. This
- * ensures that the token stays alive for the lifetime of the app process, allowing us to
- * revoke permissions when the app process terminates using {@link IBinder#linkToDeath} in
- * {@code DragAndDropPermissionsHandler}.
- */
- private static IBinder sAppToken;
-
private final IDragAndDropPermissions mDragAndDropPermissions;
/**
@@ -128,7 +109,9 @@
}
/**
- * Take permissions transiently. Permissions will be revoked when the app process terminates.
+ * Take permissions transiently. Permissions will be tied to this object's lifecycle; if not
+ * released explicitly, they will be released automatically when there are no more references
+ * to this object and it's garbage collected.
*
* <p>Note: This API is not exposed to apps.
*
@@ -138,14 +121,10 @@
*/
public boolean takeTransient() {
try {
- if (sAppToken == null) {
- sAppToken = new Binder();
- }
if (DEBUG) {
- Log.d(TAG, this + ": calling takeTransient() with process-bound token: "
- + toHexString(sAppToken.hashCode()));
+ Log.d(TAG, this + ": calling takeTransient()");
}
- mDragAndDropPermissions.takeTransient(sAppToken);
+ mDragAndDropPermissions.takeTransient();
} catch (RemoteException e) {
Log.w(TAG, this + ": takeTransient() failed with a RemoteException", e);
return false;
diff --git a/core/java/android/view/OnReceiveContentListener.java b/core/java/android/view/OnReceiveContentListener.java
index 1e930e6..f90e01c 100644
--- a/core/java/android/view/OnReceiveContentListener.java
+++ b/core/java/android/view/OnReceiveContentListener.java
@@ -74,7 +74,7 @@
* preserve the common behavior for inserting text. See the class javadoc for a sample
* implementation.
*
- * <p>Handling different content
+ * <h3>Handling different content</h3>
* <ul>
* <li>Text. If the {@link ContentInfo#getSource() source} is
* {@link ContentInfo#SOURCE_AUTOFILL autofill}, the view's content should be fully
@@ -86,6 +86,25 @@
* completely separate view).
* </ul>
*
+ * <h3>URI permissions</h3>
+ * <p>{@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION Read permissions} are
+ * granted automatically by the platform for any
+ * {@link android.content.ContentResolver#SCHEME_CONTENT content URIs} in the payload passed
+ * to this listener. Permissions are transient and will be released automatically by the
+ * platform.
+ * <ul>
+ * <li>If the {@link ContentInfo#getSource() source} is the
+ * {@link ContentInfo#SOURCE_CLIPBOARD clipboard}, permissions are released whenever the
+ * next copy action is performed by the user.
+ * <li>If the source is {@link ContentInfo#SOURCE_AUTOFILL autofill}, permissions are tied
+ * to the target {@link android.app.Activity} lifecycle (released when the activity
+ * finishes).
+ * <li>For other sources, permissions are tied to the passed-in {@code payload} object
+ * (released automatically when there are no more references to it). To ensure that
+ * permissions are not released prematurely, implementations of this listener should pass
+ * along the {@code payload} object if processing is done on a background thread.
+ * </ul>
+ *
* @param view The view where the content insertion was requested.
* @param payload The content to insert and related metadata.
*
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index ac70dff..1ae7ab0 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -2468,7 +2468,7 @@
if (Float.isNaN(sdrBrightness) || sdrBrightness > 1.0f
|| (sdrBrightness < 0.0f && sdrBrightness != -1.0f)) {
throw new IllegalArgumentException("sdrBrightness must be a number between 0.0f "
- + "and 1.0f, or -1 to turn the backlight off: " + displayBrightness);
+ + "and 1.0f, or -1 to turn the backlight off: " + sdrBrightness);
}
return nativeSetDisplayBrightness(displayToken, sdrBrightness, sdrBrightnessNits,
displayBrightness, displayBrightnessNits);
diff --git a/core/java/android/view/inputmethod/InputContentInfo.java b/core/java/android/view/inputmethod/InputContentInfo.java
index 9aa410f..5ebd9c1 100644
--- a/core/java/android/view/inputmethod/InputContentInfo.java
+++ b/core/java/android/view/inputmethod/InputContentInfo.java
@@ -199,7 +199,12 @@
}
/**
- * Requests a temporary read-only access permission for content URI associated with this object.
+ * Requests a temporary {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION read-only}
+ * access permission for the content URI associated with this object.
+ *
+ * <p>The lifecycle of the permission granted here is tied to this object instance. If the
+ * permission is not released explicitly via {@link #releasePermission()}, it will be
+ * released automatically when there are no more references to this object.</p>
*
* <p>Does nothing if the temporary permission is already granted.</p>
*/
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index eb16cef..b3f848b 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -6614,27 +6614,6 @@
}
/**
- * Returns the {@link EdgeEffect#getType()} for the edge effects.
- * @return the {@link EdgeEffect#getType()} for the edge effects.
- * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
- */
- @EdgeEffect.EdgeEffectType
- public int getEdgeEffectType() {
- return mEdgeGlowTop.getType();
- }
-
- /**
- * Sets the {@link EdgeEffect#setType(int)} for the edge effects.
- * @param type The edge effect type to use for the edge effects.
- * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
- */
- public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) {
- mEdgeGlowTop.setType(type);
- mEdgeGlowBottom.setType(type);
- invalidate();
- }
-
- /**
* Sets the recycler listener to be notified whenever a View is set aside in
* the recycler for later reuse. This listener can be used to free resources
* associated to the View.
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index 4d2d9e8..c11344e 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -62,9 +62,7 @@
*/
public class EdgeEffect {
/**
- * This sets the default value for {@link #setType(int)} to {@link #TYPE_STRETCH} instead
- * of {@link #TYPE_GLOW}. The type can still be overridden by the theme, view attribute,
- * or by calling {@link #setType(int)}.
+ * This sets the edge effect to use stretch instead of glow.
*
* @hide
*/
@@ -73,34 +71,19 @@
public static final long USE_STRETCH_EDGE_EFFECT_BY_DEFAULT = 171228096L;
/**
- * This sets the default value for {@link #setType(int)} to {@link #TYPE_STRETCH} instead
- * of {@link #TYPE_GLOW} for views that instantiate with
- * {@link #EdgeEffect(Context, AttributeSet)}, indicating use of S+ EdgeEffect support. The
- * type can still be overridden by the theme, view attribute, or by calling
- * {@link #setType(int)}.
- *
- * @hide
- */
- @ChangeId
- @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
- public static final long USE_STRETCH_EDGE_EFFECT_FOR_SUPPORTED = 178807038L;
-
- /**
* The default blend mode used by {@link EdgeEffect}.
*/
public static final BlendMode DEFAULT_BLEND_MODE = BlendMode.SRC_ATOP;
/**
- * Use a color edge glow for the edge effect. From XML, use
- * <code>android:edgeEffectType="glow"</code>.
+ * Use a color edge glow for the edge effect.
*/
- public static final int TYPE_GLOW = 0;
+ private static final int TYPE_GLOW = 0;
/**
- * Use a stretch for the edge effect. From XML, use
- * <code>android:edgeEffectType="stretch"</code>.
+ * Use a stretch for the edge effect.
*/
- public static final int TYPE_STRETCH = 1;
+ private static final int TYPE_STRETCH = 1;
/**
* The velocity threshold before the spring animation is considered settled.
@@ -221,7 +204,7 @@
* @param context Context used to provide theming and resource information for the EdgeEffect
*/
public EdgeEffect(Context context) {
- this(context, null, Compatibility.isChangeEnabled(USE_STRETCH_EDGE_EFFECT_BY_DEFAULT));
+ this(context, null);
}
/**
@@ -230,20 +213,12 @@
* @param attrs The attributes of the XML tag that is inflating the view
*/
public EdgeEffect(@NonNull Context context, @Nullable AttributeSet attrs) {
- this(context, attrs,
- Compatibility.isChangeEnabled(USE_STRETCH_EDGE_EFFECT_BY_DEFAULT)
- || Compatibility.isChangeEnabled(USE_STRETCH_EDGE_EFFECT_FOR_SUPPORTED));
- }
-
- private EdgeEffect(@NonNull Context context, @Nullable AttributeSet attrs,
- boolean defaultStretch) {
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.EdgeEffect);
final int themeColor = a.getColor(
com.android.internal.R.styleable.EdgeEffect_colorEdgeEffect, 0xff666666);
- mEdgeEffectType = a.getInt(
- com.android.internal.R.styleable.EdgeEffect_edgeEffectType,
- defaultStretch ? TYPE_STRETCH : TYPE_GLOW);
+ mEdgeEffectType = Compatibility.isChangeEnabled(USE_STRETCH_EDGE_EFFECT_BY_DEFAULT)
+ ? TYPE_STRETCH : TYPE_GLOW;
a.recycle();
mPaint.setAntiAlias(true);
@@ -467,7 +442,6 @@
if (mEdgeEffectType == TYPE_STRETCH) {
mState = STATE_RECEDE;
mVelocity = velocity * ON_ABSORB_VELOCITY_ADJUSTMENT;
- mDistance = 0;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
} else {
mState = STATE_ABSORB;
@@ -506,17 +480,6 @@
}
/**
- * Sets the edge effect type to use. The default without a theme attribute set is
- * {@link EdgeEffect#TYPE_GLOW}.
- *
- * @param type The edge effect type to use.
- * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
- */
- public void setType(@EdgeEffectType int type) {
- mEdgeEffectType = type;
- }
-
- /**
* Set or clear the blend mode. A blend mode defines how source pixels
* (generated by a drawing command) are composited with the destination pixels
* (content of the render target).
@@ -542,16 +505,6 @@
}
/**
- * Return the edge effect type to use.
- *
- * @return The edge effect type to use.
- * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
- */
- public @EdgeEffectType int getType() {
- return mEdgeEffectType;
- }
-
- /**
* Returns the blend mode. A blend mode defines how source pixels
* (generated by a drawing command) are composited with the destination pixels
* (content of the render target).
@@ -568,7 +521,7 @@
* Draw into the provided canvas. Assumes that the canvas has been rotated
* accordingly and the size has been set. The effect will be drawn the full
* width of X=0 to X=width, beginning from Y=0 and extending to some factor <
- * 1.f of height. The {@link #TYPE_STRETCH} effect will only be visible on a
+ * 1.f of height. The effect will only be visible on a
* hardware canvas, e.g. {@link RenderNode#beginRecording()}.
*
* @param canvas Canvas to draw into
@@ -686,7 +639,7 @@
* @return The maximum height of the edge effect
*/
public int getMaxHeight() {
- return (int) (mBounds.height() * MAX_GLOW_SCALE + 0.5f);
+ return (int) mHeight;
}
private void update() {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index e06d5f0..4447361 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -6684,13 +6684,13 @@
if (TextView.DEBUG_CURSOR) {
logCursor("SelectionModifierCursorController: onTouchEvent", "ACTION_UP");
}
+ if (mEndHandle != null) {
+ mEndHandle.dismissMagnifier();
+ }
if (!isDragAcceleratorActive()) {
break;
}
updateSelection(event);
- if (mEndHandle != null) {
- mEndHandle.dismissMagnifier();
- }
// No longer dragging to select text, let the parent intercept events.
mTextView.getParent().requestDisallowInterceptTouchEvent(false);
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index e41893e..018cba7 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -308,27 +308,6 @@
}
/**
- * Returns the {@link EdgeEffect#getType()} for the edge effects.
- * @return the {@link EdgeEffect#getType()} for the edge effects.
- * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
- */
- @EdgeEffect.EdgeEffectType
- public int getEdgeEffectType() {
- return mEdgeGlowLeft.getType();
- }
-
- /**
- * Sets the {@link EdgeEffect#setType(int)} for the edge effects.
- * @param type The edge effect type to use for the edge effects.
- * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
- */
- public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) {
- mEdgeGlowRight.setType(type);
- mEdgeGlowLeft.setType(type);
- invalidate();
- }
-
- /**
* @return The maximum amount this scroll view will scroll in response to
* an arrow event.
*/
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 3610eb4..693b13b 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -340,27 +340,6 @@
}
/**
- * Returns the {@link EdgeEffect#getType()} for the edge effects.
- * @return the {@link EdgeEffect#getType()} for the edge effects.
- * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
- */
- @EdgeEffect.EdgeEffectType
- public int getEdgeEffectType() {
- return mEdgeGlowTop.getType();
- }
-
- /**
- * Sets the {@link EdgeEffect#setType(int)} for the edge effects.
- * @param type The edge effect type to use for the edge effects.
- * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
- */
- public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) {
- mEdgeGlowTop.setType(type);
- mEdgeGlowBottom.setType(type);
- invalidate();
- }
-
- /**
* @return The maximum amount this scroll view will scroll in response to
* an arrow event.
*/
@@ -368,7 +347,6 @@
return (int) (MAX_SCROLL_FACTOR * (mBottom - mTop));
}
-
private void initScrollView() {
mScroller = new OverScroller(getContext());
setFocusable(true);
diff --git a/core/java/com/android/internal/graphics/ColorUtils.java b/core/java/com/android/internal/graphics/ColorUtils.java
index 8b2a2dc..537e797 100644
--- a/core/java/com/android/internal/graphics/ColorUtils.java
+++ b/core/java/com/android/internal/graphics/ColorUtils.java
@@ -22,6 +22,8 @@
import android.annotation.NonNull;
import android.graphics.Color;
+import com.android.internal.graphics.cam.Cam;
+
/**
* Copied from: frameworks/support/core-utils/java/android/support/v4/graphics/ColorUtils.java
*
@@ -333,6 +335,35 @@
}
/**
+ * Convert the ARGB color to a color appearance model.
+ *
+ * The color appearance model is based on CAM16 hue and chroma, using L*a*b*'s L* as the
+ * third dimension.
+ *
+ * @param color the ARGB color to convert. The alpha component is ignored.
+ */
+ public static Cam colorToCAM(@ColorInt int color) {
+ return Cam.fromInt(color);
+ }
+
+ /**
+ * Convert a color appearance model representation to an ARGB color.
+ *
+ * Note: the returned color may have a lower chroma than requested. Whether a chroma is
+ * available depends on luminance. For example, there's no such thing as a high chroma light
+ * red, due to the limitations of our eyes and/or physics. If the requested chroma is
+ * unavailable, the highest possible chroma at the requested luminance is returned.
+ *
+ * @param hue hue, in degrees, in CAM coordinates
+ * @param chroma chroma in CAM coordinates.
+ * @param lstar perceptual luminance, L* in L*a*b*
+ */
+ @ColorInt
+ public static int CAMToColor(float hue, float chroma, float lstar) {
+ return Cam.getInt(hue, chroma, lstar);
+ }
+
+ /**
* Set the alpha component of {@code color} to be {@code alpha}.
*/
@ColorInt
diff --git a/core/java/com/android/internal/graphics/cam/Cam.java b/core/java/com/android/internal/graphics/cam/Cam.java
new file mode 100644
index 0000000..1ac5e50
--- /dev/null
+++ b/core/java/com/android/internal/graphics/cam/Cam.java
@@ -0,0 +1,509 @@
+/*
+ * 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 com.android.internal.graphics.cam;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.graphics.ColorUtils;
+
+/**
+ * A color appearance model, based on CAM16, extended to use L* as the lightness dimension, and
+ * coupled to a gamut mapping algorithm. Creates a color system, enables a digital design system.
+ */
+public class Cam {
+ // The maximum difference between the requested L* and the L* returned.
+ private static final float DL_MAX = 0.2f;
+ // The maximum color distance, in CAM16-UCS, between a requested color and the color returned.
+ private static final float DE_MAX = 1.0f;
+ // When the delta between the floor & ceiling of a binary search for chroma is less than this,
+ // the binary search terminates.
+ private static final float CHROMA_SEARCH_ENDPOINT = 0.4f;
+ // When the delta between the floor & ceiling of a binary search for J, lightness in CAM16,
+ // is less than this, the binary search terminates.
+ private static final float LIGHTNESS_SEARCH_ENDPOINT = 0.01f;
+
+ // CAM16 color dimensions, see getters for documentation.
+ private final float mHue;
+ private final float mChroma;
+ private final float mJ;
+ private final float mQ;
+ private final float mM;
+ private final float mS;
+
+ // Coordinates in UCS space. Used to determine color distance, like delta E equations in L*a*b*.
+ private final float mJstar;
+ private final float mAstar;
+ private final float mBstar;
+
+ /** Hue in CAM16 */
+ public float getHue() {
+ return mHue;
+ }
+
+ /** Chroma in CAM16 */
+ public float getChroma() {
+ return mChroma;
+ }
+
+ /** Lightness in CAM16 */
+ public float getJ() {
+ return mJ;
+ }
+
+ /**
+ * Brightness in CAM16.
+ *
+ * <p>Prefer lightness, brightness is an absolute quantity. For example, a sheet of white paper
+ * is much brighter viewed in sunlight than in indoor light, but it is the lightest object under
+ * any lighting.
+ */
+ public float getQ() {
+ return mQ;
+ }
+
+ /**
+ * Colorfulness in CAM16.
+ *
+ * <p>Prefer chroma, colorfulness is an absolute quantity. For example, a yellow toy car is much
+ * more colorful outside than inside, but it has the same chroma in both environments.
+ */
+ public float getM() {
+ return mM;
+ }
+
+ /**
+ * Saturation in CAM16.
+ *
+ * <p>Colorfulness in proportion to brightness. Prefer chroma, saturation measures colorfulness
+ * relative to the color's own brightness, where chroma is colorfulness relative to white.
+ */
+ public float getS() {
+ return mS;
+ }
+
+ /** Lightness coordinate in CAM16-UCS */
+ public float getJstar() {
+ return mJstar;
+ }
+
+ /** a* coordinate in CAM16-UCS */
+ public float getAstar() {
+ return mAstar;
+ }
+
+ /** b* coordinate in CAM16-UCS */
+ public float getBstar() {
+ return mBstar;
+ }
+
+ /** Construct a CAM16 color */
+ Cam(float hue, float chroma, float j, float q, float m, float s, float jstar, float astar,
+ float bstar) {
+ mHue = hue;
+ mChroma = chroma;
+ mJ = j;
+ mQ = q;
+ mM = m;
+ mS = s;
+ mJstar = jstar;
+ mAstar = astar;
+ mBstar = bstar;
+ }
+
+ /**
+ * Given a hue & chroma in CAM16, L* in L*a*b*, return an ARGB integer. The chroma of the color
+ * returned may, and frequently will, be lower than requested. Assumes the color is viewed in
+ * the
+ * frame defined by the sRGB standard.
+ */
+ public static int getInt(float hue, float chroma, float lstar) {
+ return getInt(hue, chroma, lstar, Frame.DEFAULT);
+ }
+
+ /**
+ * Create a color appearance model from a ARGB integer representing a color. It is assumed the
+ * color was viewed in the frame defined in the sRGB standard.
+ */
+ @NonNull
+ public static Cam fromInt(int argb) {
+ return fromIntInFrame(argb, Frame.DEFAULT);
+ }
+
+ /**
+ * Create a color appearance model from a ARGB integer representing a color, specifying the
+ * frame in which the color was viewed. Prefer Cam.fromInt.
+ */
+ @NonNull
+ public static Cam fromIntInFrame(int argb, @NonNull Frame frame) {
+ // Transform ARGB int to XYZ
+ float[] xyz = CamUtils.xyzFromInt(argb);
+
+ // Transform XYZ to 'cone'/'rgb' responses
+ float[][] matrix = CamUtils.XYZ_TO_CAM16RGB;
+ float rT = (xyz[0] * matrix[0][0]) + (xyz[1] * matrix[0][1]) + (xyz[2] * matrix[0][2]);
+ float gT = (xyz[0] * matrix[1][0]) + (xyz[1] * matrix[1][1]) + (xyz[2] * matrix[1][2]);
+ float bT = (xyz[0] * matrix[2][0]) + (xyz[1] * matrix[2][1]) + (xyz[2] * matrix[2][2]);
+
+ // Discount illuminant
+ float rD = frame.getRgbD()[0] * rT;
+ float gD = frame.getRgbD()[1] * gT;
+ float bD = frame.getRgbD()[2] * bT;
+
+ // Chromatic adaptation
+ float rAF = (float) Math.pow(frame.getFl() * Math.abs(rD) / 100.0, 0.42);
+ float gAF = (float) Math.pow(frame.getFl() * Math.abs(gD) / 100.0, 0.42);
+ float bAF = (float) Math.pow(frame.getFl() * Math.abs(bD) / 100.0, 0.42);
+ float rA = Math.signum(rD) * 400.0f * rAF / (rAF + 27.13f);
+ float gA = Math.signum(gD) * 400.0f * gAF / (gAF + 27.13f);
+ float bA = Math.signum(bD) * 400.0f * bAF / (bAF + 27.13f);
+
+ // redness-greenness
+ float a = (float) (11.0 * rA + -12.0 * gA + bA) / 11.0f;
+ // yellowness-blueness
+ float b = (float) (rA + gA - 2.0 * bA) / 9.0f;
+
+ // auxiliary components
+ float u = (20.0f * rA + 20.0f * gA + 21.0f * bA) / 20.0f;
+ float p2 = (40.0f * rA + 20.0f * gA + bA) / 20.0f;
+
+ // hue
+ float atan2 = (float) Math.atan2(b, a);
+ float atanDegrees = atan2 * 180.0f / (float) Math.PI;
+ float hue =
+ atanDegrees < 0
+ ? atanDegrees + 360.0f
+ : atanDegrees >= 360 ? atanDegrees - 360.0f : atanDegrees;
+ float hueRadians = hue * (float) Math.PI / 180.0f;
+
+ // achromatic response to color
+ float ac = p2 * frame.getNbb();
+
+ // CAM16 lightness and brightness
+ float j = 100.0f * (float) Math.pow(ac / frame.getAw(), frame.getC() * frame.getZ());
+ float q =
+ 4.0f
+ / frame.getC()
+ * (float) Math.sqrt(j / 100.0f)
+ * (frame.getAw() + 4.0f)
+ * frame.getFlRoot();
+
+ // CAM16 chroma, colorfulness, and saturation.
+ float huePrime = (hue < 20.14) ? hue + 360 : hue;
+ float eHue = 0.25f * (float) (Math.cos(huePrime * Math.PI / 180.0 + 2.0) + 3.8);
+ float p1 = 50000.0f / 13.0f * eHue * frame.getNc() * frame.getNcb();
+ float t = p1 * (float) Math.sqrt(a * a + b * b) / (u + 0.305f);
+ float alpha =
+ (float) Math.pow(t, 0.9) * (float) Math.pow(1.64 - Math.pow(0.29, frame.getN()),
+ 0.73);
+ // CAM16 chroma, colorfulness, saturation
+ float c = alpha * (float) Math.sqrt(j / 100.0);
+ float m = c * frame.getFlRoot();
+ float s = 50.0f * (float) Math.sqrt((alpha * frame.getC()) / (frame.getAw() + 4.0f));
+
+ // CAM16-UCS components
+ float jstar = (1.0f + 100.0f * 0.007f) * j / (1.0f + 0.007f * j);
+ float mstar = 1.0f / 0.0228f * (float) Math.log(1.0f + 0.0228f * m);
+ float astar = mstar * (float) Math.cos(hueRadians);
+ float bstar = mstar * (float) Math.sin(hueRadians);
+
+ return new Cam(hue, c, j, q, m, s, jstar, astar, bstar);
+ }
+
+ /**
+ * Create a CAM from lightness, chroma, and hue coordinates. It is assumed those coordinates
+ * were measured in the sRGB standard frame.
+ */
+ @NonNull
+ private static Cam fromJch(float j, float c, float h) {
+ return fromJchInFrame(j, c, h, Frame.DEFAULT);
+ }
+
+ /**
+ * Create a CAM from lightness, chroma, and hue coordinates, and also specify the frame in which
+ * the color is being viewed.
+ */
+ @NonNull
+ private static Cam fromJchInFrame(float j, float c, float h, Frame frame) {
+ float q =
+ 4.0f
+ / frame.getC()
+ * (float) Math.sqrt(j / 100.0)
+ * (frame.getAw() + 4.0f)
+ * frame.getFlRoot();
+ float m = c * frame.getFlRoot();
+ float alpha = c / (float) Math.sqrt(j / 100.0);
+ float s = 50.0f * (float) Math.sqrt((alpha * frame.getC()) / (frame.getAw() + 4.0f));
+
+ float hueRadians = h * (float) Math.PI / 180.0f;
+ float jstar = (1.0f + 100.0f * 0.007f) * j / (1.0f + 0.007f * j);
+ float mstar = 1.0f / 0.0228f * (float) Math.log(1.0 + 0.0228 * m);
+ float astar = mstar * (float) Math.cos(hueRadians);
+ float bstar = mstar * (float) Math.sin(hueRadians);
+ return new Cam(h, c, j, q, m, s, jstar, astar, bstar);
+ }
+
+ /**
+ * Distance in CAM16-UCS space between two colors.
+ *
+ * <p>Much like L*a*b* was designed to measure distance between colors, the CAM16 standard
+ * defined a color space called CAM16-UCS to measure distance between CAM16 colors.
+ */
+ public float distance(@NonNull Cam other) {
+ float dJ = getJstar() - other.getJstar();
+ float dA = getAstar() - other.getAstar();
+ float dB = getBstar() - other.getBstar();
+ double dEPrime = Math.sqrt(dJ * dJ + dA * dA + dB * dB);
+ double dE = 1.41 * Math.pow(dEPrime, 0.63);
+ return (float) dE;
+ }
+
+ /** Returns perceived color as an ARGB integer, as viewed in standard sRGB frame. */
+ public int viewedInSrgb() {
+ return viewed(Frame.DEFAULT);
+ }
+
+ /** Returns color perceived in a frame as an ARGB integer. */
+ public int viewed(@NonNull Frame frame) {
+ float alpha =
+ (getChroma() == 0.0 || getJ() == 0.0)
+ ? 0.0f
+ : getChroma() / (float) Math.sqrt(getJ() / 100.0);
+
+ float t =
+ (float) Math.pow(alpha / Math.pow(1.64 - Math.pow(0.29, frame.getN()), 0.73),
+ 1.0 / 0.9);
+ float hRad = getHue() * (float) Math.PI / 180.0f;
+
+ float eHue = 0.25f * (float) (Math.cos(hRad + 2.0) + 3.8);
+ float ac = frame.getAw() * (float) Math.pow(getJ() / 100.0,
+ 1.0 / frame.getC() / frame.getZ());
+ float p1 = eHue * (50000.0f / 13.0f) * frame.getNc() * frame.getNcb();
+ float p2 = (ac / frame.getNbb());
+
+ float hSin = (float) Math.sin(hRad);
+ float hCos = (float) Math.cos(hRad);
+
+ float gamma =
+ 23.0f * (p2 + 0.305f) * t / (23.0f * p1 + 11.0f * t * hCos + 108.0f * t * hSin);
+ float a = gamma * hCos;
+ float b = gamma * hSin;
+ float rA = (460.0f * p2 + 451.0f * a + 288.0f * b) / 1403.0f;
+ float gA = (460.0f * p2 - 891.0f * a - 261.0f * b) / 1403.0f;
+ float bA = (460.0f * p2 - 220.0f * a - 6300.0f * b) / 1403.0f;
+
+ float rCBase = (float) Math.max(0, (27.13 * Math.abs(rA)) / (400.0 - Math.abs(rA)));
+ float rC = Math.signum(rA) * (100.0f / frame.getFl()) * (float) Math.pow(rCBase,
+ 1.0 / 0.42);
+ float gCBase = (float) Math.max(0, (27.13 * Math.abs(gA)) / (400.0 - Math.abs(gA)));
+ float gC = Math.signum(gA) * (100.0f / frame.getFl()) * (float) Math.pow(gCBase,
+ 1.0 / 0.42);
+ float bCBase = (float) Math.max(0, (27.13 * Math.abs(bA)) / (400.0 - Math.abs(bA)));
+ float bC = Math.signum(bA) * (100.0f / frame.getFl()) * (float) Math.pow(bCBase,
+ 1.0 / 0.42);
+ float rF = rC / frame.getRgbD()[0];
+ float gF = gC / frame.getRgbD()[1];
+ float bF = bC / frame.getRgbD()[2];
+
+
+ float[][] matrix = CamUtils.CAM16RGB_TO_XYZ;
+ float x = (rF * matrix[0][0]) + (gF * matrix[0][1]) + (bF * matrix[0][2]);
+ float y = (rF * matrix[1][0]) + (gF * matrix[1][1]) + (bF * matrix[1][2]);
+ float z = (rF * matrix[2][0]) + (gF * matrix[2][1]) + (bF * matrix[2][2]);
+
+ int argb = ColorUtils.XYZToColor(x, y, z);
+ return argb;
+ }
+
+ /**
+ * Given a hue & chroma in CAM16, L* in L*a*b*, and the frame in which the color will be
+ * viewed,
+ * return an ARGB integer.
+ *
+ * <p>The chroma of the color returned may, and frequently will, be lower than requested. This
+ * is
+ * a fundamental property of color that cannot be worked around by engineering. For example, a
+ * red
+ * hue, with high chroma, and high L* does not exist: red hues have a maximum chroma below 10
+ * in
+ * light shades, creating pink.
+ */
+ public static int getInt(float hue, float chroma, float lstar, @NonNull Frame frame) {
+ // This is a crucial routine for building a color system, CAM16 itself is not sufficient.
+ //
+ // * Why these dimensions?
+ // Hue and chroma from CAM16 are used because they're the most accurate measures of those
+ // quantities. L* from L*a*b* is used because it correlates with luminance, luminance is
+ // used to measure contrast for a11y purposes, thus providing a key constraint on what
+ // colors
+ // can be used.
+ //
+ // * Why is this routine required to build a color system?
+ // In all perceptually accurate color spaces (i.e. L*a*b* and later), `chroma` may be
+ // impossible for a given `hue` and `lstar`.
+ // For example, a high chroma light red does not exist - chroma is limited to below 10 at
+ // light red shades, we call that pink. High chroma light green does exist, but not dark
+ // Also, when converting from another color space to RGB, the color may not be able to be
+ // represented in RGB. In those cases, the conversion process ends with RGB values
+ // outside 0-255
+ // The vast majority of color libraries surveyed simply round to 0 to 255. That is not an
+ // option for this library, as it distorts the expected luminance, and thus the expected
+ // contrast needed for a11y
+ //
+ // * What does this routine do?
+ // Dealing with colors in one color space not fitting inside RGB is, loosely referred to as
+ // gamut mapping or tone mapping. These algorithms are traditionally idiosyncratic, there is
+ // no universal answer. However, because the intent of this library is to build a system for
+ // digital design, and digital design uses luminance to measure contrast/a11y, we have one
+ // very important constraint that leads to an objective algorithm: the L* of the returned
+ // color _must_ match the requested L*.
+ //
+ // Intuitively, if the color must be distorted to fit into the RGB gamut, and the L*
+ // requested *must* be fulfilled, than the hue or chroma of the returned color will need
+ // to be different from the requested hue/chroma.
+ //
+ // After exploring both options, it was more intuitive that if the requested chroma could
+ // not be reached, it used the highest possible chroma. The alternative was finding the
+ // closest hue where the requested chroma could be reached, but that is not nearly as
+ // intuitive, as the requested hue is so fundamental to the color description.
+
+ // If the color doesn't have meaningful chroma, return a gray with the requested Lstar.
+ //
+ // Yellows are very chromatic at L = 100, and blues are very chromatic at L = 0. All the
+ // other hues are white at L = 100, and black at L = 0. To preserve consistency for users of
+ // this system, it is better to simply return white at L* > 99, and black and L* < 0.
+ if (chroma < 1.0 || Math.round(lstar) <= 0.0 || Math.round(lstar) >= 100.0) {
+ return CamUtils.intFromLstar(lstar);
+ }
+
+ hue = hue < 0 ? 0 : Math.min(360, hue);
+
+ // The highest chroma possible. Updated as binary search proceeds.
+ float high = chroma;
+
+ // The guess for the current binary search iteration. Starts off at the highest chroma,
+ // thus, if a color is possible at the requested chroma, the search can stop after one try.
+ float mid = chroma;
+ float low = 0.0f;
+ boolean isFirstLoop = true;
+
+ Cam answer = null;
+
+ while (Math.abs(low - high) >= CHROMA_SEARCH_ENDPOINT) {
+ // Given the current chroma guess, mid, and the desired hue, find J, lightness in
+ // CAM16 color space, that creates a color with L* = `lstar` in the L*a*b* color space.
+ Cam possibleAnswer = findCamByJ(hue, mid, lstar);
+
+ if (isFirstLoop) {
+ if (possibleAnswer != null) {
+ return possibleAnswer.viewed(frame);
+ } else {
+ // If this binary search iteration was the first iteration, and this point
+ // has been reached, it means the requested chroma was not available at the
+ // requested hue and L*.
+ // Proceed to a traditional binary search that starts at the midpoint between
+ // the requested chroma and 0.
+ isFirstLoop = false;
+ mid = low + (high - low) / 2.0f;
+ continue;
+ }
+ }
+
+ if (possibleAnswer == null) {
+ // There isn't a CAM16 J that creates a color with L* `lstar`. Try a lower chroma.
+ high = mid;
+ } else {
+ answer = possibleAnswer;
+ // It is possible to create a color. Try higher chroma.
+ low = mid;
+ }
+
+ mid = low + (high - low) / 2.0f;
+ }
+
+ // There was no answer: meaning, for the desired hue, there was no chroma low enough to
+ // generate a color with the desired L*.
+ // All values of L* are possible when there is 0 chroma. Return a color with 0 chroma, i.e.
+ // a shade of gray, with the desired L*.
+ if (answer == null) {
+ return CamUtils.intFromLstar(lstar);
+ }
+
+ return answer.viewed(frame);
+ }
+
+ // Find J, lightness in CAM16 color space, that creates a color with L* = `lstar` in the L*a*b*
+ // color space.
+ //
+ // Returns null if no J could be found that generated a color with L* `lstar`.
+ @Nullable
+ private static Cam findCamByJ(float hue, float chroma, float lstar) {
+ float low = 0.0f;
+ float high = 100.0f;
+ float mid = 0.0f;
+ float bestdL = 1000.0f;
+ float bestdE = 1000.0f;
+
+ Cam bestCam = null;
+ while (Math.abs(low - high) > LIGHTNESS_SEARCH_ENDPOINT) {
+ mid = low + (high - low) / 2;
+ // Create the intended CAM color
+ Cam camBeforeClip = Cam.fromJch(mid, chroma, hue);
+ // Convert the CAM color to RGB. If the color didn't fit in RGB, during the conversion,
+ // the initial RGB values will be outside 0 to 255. The final RGB values are clipped to
+ // 0 to 255, distorting the intended color.
+ int clipped = camBeforeClip.viewedInSrgb();
+ float clippedLstar = CamUtils.lstarFromInt(clipped);
+ float dL = Math.abs(lstar - clippedLstar);
+
+ // If the clipped color's L* is within error margin...
+ if (dL < DL_MAX) {
+ // ...check if the CAM equivalent of the clipped color is far away from intended CAM
+ // color. For the intended color, use lightness and chroma from the clipped color,
+ // and the intended hue. Callers are wondering what the lightness is, they know
+ // chroma may be distorted, so the only concern here is if the hue slipped too far.
+ Cam camClipped = Cam.fromInt(clipped);
+ float dE = camClipped.distance(
+ Cam.fromJch(camClipped.getJ(), camClipped.getChroma(), hue));
+ if (dE <= DE_MAX) {
+ bestdL = dL;
+ bestdE = dE;
+ bestCam = camClipped;
+ }
+ }
+
+ // If there's no error at all, there's no need to search more.
+ //
+ // Note: this happens much more frequently than expected, but this is a very delicate
+ // property which relies on extremely precise sRGB <=> XYZ calculations, as well as fine
+ // tuning of the constants that determine error margins and when the binary search can
+ // terminate.
+ if (bestdL == 0 && bestdE == 0) {
+ break;
+ }
+
+ if (clippedLstar < lstar) {
+ low = mid;
+ } else {
+ high = mid;
+ }
+ }
+
+ return bestCam;
+ }
+
+}
diff --git a/core/java/com/android/internal/graphics/cam/CamUtils.java b/core/java/com/android/internal/graphics/cam/CamUtils.java
new file mode 100644
index 0000000..13dafdb
--- /dev/null
+++ b/core/java/com/android/internal/graphics/cam/CamUtils.java
@@ -0,0 +1,165 @@
+/*
+ * 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 com.android.internal.graphics.cam;
+
+
+import android.annotation.NonNull;
+import android.graphics.Color;
+
+import com.android.internal.graphics.ColorUtils;
+
+/**
+ * Collection of methods for transforming between color spaces.
+ *
+ * <p>Methods are named $xFrom$Y. For example, lstarFromInt() returns L* from an ARGB integer.
+ *
+ * <p>These methods, generally, convert colors between the L*a*b*, XYZ, and sRGB spaces.
+ *
+ * <p>L*a*b* is a perceptually accurate color space. This is particularly important in the L*
+ * dimension: it measures luminance and unlike lightness measures traditionally used in UI work via
+ * RGB or HSL, this luminance transitions smoothly, permitting creation of pleasing shades of a
+ * color, and more pleasing transitions between colors.
+ *
+ * <p>XYZ is commonly used as an intermediate color space for converting between one color space to
+ * another. For example, to convert RGB to L*a*b*, first RGB is converted to XYZ, then XYZ is
+ * convered to L*a*b*.
+ *
+ * <p>sRGB is a "specification originated from work in 1990s through cooperation by Hewlett-Packard
+ * and Microsoft, and it was designed to be a standard definition of RGB for the internet, which it
+ * indeed became...The standard is based on a sampling of computer monitors at the time...The whole
+ * idea of sRGB is that if everyone assumed that RGB meant the same thing, then the results would be
+ * consistent, and reasonably good. It worked." - Fairchild, Color Models and Systems: Handbook of
+ * Color Psychology, 2015
+ */
+public final class CamUtils {
+ private CamUtils() {
+ }
+
+ // Transforms XYZ color space coordinates to 'cone'/'RGB' responses in CAM16.
+ static final float[][] XYZ_TO_CAM16RGB = {
+ {0.401288f, 0.650173f, -0.051461f},
+ {-0.250268f, 1.204414f, 0.045854f},
+ {-0.002079f, 0.048952f, 0.953127f}
+ };
+
+ // Transforms 'cone'/'RGB' responses in CAM16 to XYZ color space coordinates.
+ static final float[][] CAM16RGB_TO_XYZ = {
+ {1.86206786f, -1.01125463f, 0.14918677f},
+ {0.38752654f, 0.62144744f, -0.00897398f},
+ {-0.01584150f, -0.03412294f, 1.04996444f}
+ };
+
+ // Need this, XYZ coordinates in internal ColorUtils are private
+
+ // sRGB specification has D65 whitepoint - Stokes, Anderson, Chandrasekar, Motta - A Standard
+ // Default Color Space for the Internet: sRGB, 1996
+ static final float[] WHITE_POINT_D65 = {95.047f, 100.0f, 108.883f};
+
+ // This is a more precise sRGB to XYZ transformation matrix than traditionally
+ // used. It was derived using Schlomer's technique of transforming the xyY
+ // primaries to XYZ, then applying a correction to ensure mapping from sRGB
+ // 1, 1, 1 to the reference white point, D65.
+ static final float[][] SRGB_TO_XYZ = {
+ {0.41233895f, 0.35762064f, 0.18051042f},
+ {0.2126f, 0.7152f, 0.0722f},
+ {0.01932141f, 0.11916382f, 0.95034478f}
+ };
+
+ static int intFromLstar(float lstar) {
+ if (lstar < 1) {
+ return 0xff000000;
+ } else if (lstar > 99) {
+ return 0xffffffff;
+ }
+
+ // XYZ to LAB conversion routine, assume a and b are 0.
+ float fy = (lstar + 16.0f) / 116.0f;
+
+ // fz = fx = fy because a and b are 0
+ float fz = fy;
+ float fx = fy;
+
+ float kappa = 24389f / 27f;
+ float epsilon = 216f / 24389f;
+ boolean lExceedsEpsilonKappa = (lstar > 8.0f);
+ float yT = lExceedsEpsilonKappa ? fy * fy * fy : lstar / kappa;
+ boolean cubeExceedEpsilon = (fy * fy * fy) > epsilon;
+ float xT = cubeExceedEpsilon ? fx * fx * fx : (116f * fx - 16f) / kappa;
+ float zT = cubeExceedEpsilon ? fz * fz * fz : (116f * fx - 16f) / kappa;
+
+ return ColorUtils.XYZToColor(xT * CamUtils.WHITE_POINT_D65[0],
+ yT * CamUtils.WHITE_POINT_D65[1], zT * CamUtils.WHITE_POINT_D65[2]);
+ }
+
+ /** Returns L* from L*a*b*, perceptual luminance, from an ARGB integer (ColorInt). */
+ public static float lstarFromInt(int argb) {
+ return lstarFromY(yFromInt(argb));
+ }
+
+ static float lstarFromY(float y) {
+ y = y / 100.0f;
+ final float e = 216.f / 24389.f;
+ float yIntermediate;
+ if (y <= e) {
+ return ((24389.f / 27.f) * y);
+ } else {
+ yIntermediate = (float) Math.cbrt(y);
+ }
+ return 116.f * yIntermediate - 16.f;
+ }
+
+ static float yFromInt(int argb) {
+ final float r = linearized(Color.red(argb));
+ final float g = linearized(Color.green(argb));
+ final float b = linearized(Color.blue(argb));
+ float[][] matrix = SRGB_TO_XYZ;
+ float y = (r * matrix[1][0]) + (g * matrix[1][1]) + (b * matrix[1][2]);
+ return y;
+ }
+
+ @NonNull
+ static float[] xyzFromInt(int argb) {
+ final float r = linearized(Color.red(argb));
+ final float g = linearized(Color.green(argb));
+ final float b = linearized(Color.blue(argb));
+
+ float[][] matrix = SRGB_TO_XYZ;
+ float x = (r * matrix[0][0]) + (g * matrix[0][1]) + (b * matrix[0][2]);
+ float y = (r * matrix[1][0]) + (g * matrix[1][1]) + (b * matrix[1][2]);
+ float z = (r * matrix[2][0]) + (g * matrix[2][1]) + (b * matrix[2][2]);
+ return new float[]{x, y, z};
+ }
+
+ static float yFromLstar(float lstar) {
+ float ke = 8.0f;
+ if (lstar > ke) {
+ return (float) Math.pow(((lstar + 16.0) / 116.0), 3) * 100f;
+ } else {
+ return lstar / (24389f / 27f) * 100f;
+ }
+ }
+
+ static float linearized(int rgbComponent) {
+ float normalized = (float) rgbComponent / 255.0f;
+
+ if (normalized <= 0.04045f) {
+ return (normalized / 12.92f) * 100.0f;
+ } else {
+ return (float) Math.pow(((normalized + 0.055f) / 1.055f), 2.4f) * 100.0f;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/graphics/cam/Frame.java b/core/java/com/android/internal/graphics/cam/Frame.java
new file mode 100644
index 0000000..c422ad1
--- /dev/null
+++ b/core/java/com/android/internal/graphics/cam/Frame.java
@@ -0,0 +1,194 @@
+/*
+ * 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 com.android.internal.graphics.cam;
+
+import android.annotation.NonNull;
+import android.util.MathUtils;
+
+/**
+ * The frame, or viewing conditions, where a color was seen. Used, along with a color, to create a
+ * color appearance model representing the color.
+ *
+ * <p>To convert a traditional color to a color appearance model, it requires knowing what
+ * conditions the color was observed in. Our perception of color depends on, for example, the tone
+ * of the light illuminating the color, how bright that light was, etc.
+ *
+ * <p>This class is modelled separately from the color appearance model itself because there are a
+ * number of calculations during the color => CAM conversion process that depend only on the viewing
+ * conditions. Caching those calculations in a Frame instance saves a significant amount of time.
+ */
+public final class Frame {
+ // Standard viewing conditions assumed in RGB specification - Stokes, Anderson, Chandrasekar,
+ // Motta - A Standard Default Color Space for the Internet: sRGB, 1996.
+ //
+ // White point = D65
+ // Luminance of adapting field: 200 / Pi / 5, units are cd/m^2.
+ // sRGB ambient illuminance = 64 lux (per sRGB spec). However, the spec notes this is
+ // artificially low and based on monitors in 1990s. Use 200, the sRGB spec says this is the
+ // real average, and a survey of lux values on Wikipedia confirms this is a comfortable
+ // default: somewhere between a very dark overcast day and office lighting.
+ // Per CAM16 introduction paper (Li et al, 2017) Ew = pi * lw, and La = lw * Yb/Yw
+ // Ew = ambient environment luminance, in lux.
+ // Yb/Yw is taken to be midgray, ~20% relative luminance (XYZ Y 18.4, CIELAB L* 50).
+ // Therefore La = (Ew / pi) * .184
+ // La = 200 / pi * .184
+ // Image surround to 10 degrees = ~20% relative luminance = CIELAB L* 50
+ //
+ // Not from sRGB standard:
+ // Surround = average, 2.0.
+ // Discounting illuminant = false, doesn't occur for self-luminous displays
+ public static final Frame DEFAULT =
+ Frame.make(
+ CamUtils.WHITE_POINT_D65,
+ (float) (200.0f / Math.PI * CamUtils.yFromLstar(50.0f) / 100.f), 50.0f, 2.0f,
+ false);
+
+ private final float mAw;
+ private final float mNbb;
+ private final float mNcb;
+ private final float mC;
+ private final float mNc;
+ private final float mN;
+ private final float[] mRgbD;
+ private final float mFl;
+ private final float mFlRoot;
+ private final float mZ;
+
+ float getAw() {
+ return mAw;
+ }
+
+ float getN() {
+ return mN;
+ }
+
+ float getNbb() {
+ return mNbb;
+ }
+
+ float getNcb() {
+ return mNcb;
+ }
+
+ float getC() {
+ return mC;
+ }
+
+ float getNc() {
+ return mNc;
+ }
+
+ @NonNull
+ float[] getRgbD() {
+ return mRgbD;
+ }
+
+ float getFl() {
+ return mFl;
+ }
+
+ float getFlRoot() {
+ return mFlRoot;
+ }
+
+ float getZ() {
+ return mZ;
+ }
+
+ private Frame(float n, float aw, float nbb, float ncb, float c, float nc, float[] rgbD,
+ float fl, float fLRoot, float z) {
+ mN = n;
+ mAw = aw;
+ mNbb = nbb;
+ mNcb = ncb;
+ mC = c;
+ mNc = nc;
+ mRgbD = rgbD;
+ mFl = fl;
+ mFlRoot = fLRoot;
+ mZ = z;
+ }
+
+ /** Create a custom frame. */
+ @NonNull
+ public static Frame make(@NonNull float[] whitepoint, float adaptingLuminance,
+ float backgroundLstar, float surround, boolean discountingIlluminant) {
+ // Transform white point XYZ to 'cone'/'rgb' responses
+ float[][] matrix = CamUtils.XYZ_TO_CAM16RGB;
+ float[] xyz = whitepoint;
+ float rW = (xyz[0] * matrix[0][0]) + (xyz[1] * matrix[0][1]) + (xyz[2] * matrix[0][2]);
+ float gW = (xyz[0] * matrix[1][0]) + (xyz[1] * matrix[1][1]) + (xyz[2] * matrix[1][2]);
+ float bW = (xyz[0] * matrix[2][0]) + (xyz[1] * matrix[2][1]) + (xyz[2] * matrix[2][2]);
+
+ // Scale input surround, domain (0, 2), to CAM16 surround, domain (0.8, 1.0)
+ float f = 0.8f + (surround / 10.0f);
+ // "Exponential non-linearity"
+ float c = (f >= 0.9) ? MathUtils.lerp(0.59f, 0.69f, ((f - 0.9f) * 10.0f)) : MathUtils.lerp(
+ 0.525f, 0.59f, ((f - 0.8f) * 10.0f));
+ // Calculate degree of adaptation to illuminant
+ float d = discountingIlluminant ? 1.0f : f * (1.0f - ((1.0f / 3.6f) * (float) Math.exp(
+ (-adaptingLuminance - 42.0f) / 92.0f)));
+ // Per Li et al, if D is greater than 1 or less than 0, set it to 1 or 0.
+ d = (d > 1.0) ? 1.0f : (d < 0.0) ? 0.0f : d;
+ // Chromatic induction factor
+ float nc = f;
+
+ // Cone responses to the whitepoint, adjusted for illuminant discounting.
+ //
+ // Why use 100.0 instead of the white point's relative luminance?
+ //
+ // Some papers and implementations, for both CAM02 and CAM16, use the Y
+ // value of the reference white instead of 100. Fairchild's Color Appearance
+ // Models (3rd edition) notes that this is in error: it was included in the
+ // CIE 2004a report on CIECAM02, but, later parts of the conversion process
+ // account for scaling of appearance relative to the white point relative
+ // luminance. This part should simply use 100 as luminance.
+ float[] rgbD = new float[]{d * (100.0f / rW) + 1.0f - d, d * (100.0f / gW) + 1.0f - d,
+ d * (100.0f / bW) + 1.0f - d, };
+ // Luminance-level adaptation factor
+ float k = 1.0f / (5.0f * adaptingLuminance + 1.0f);
+ float k4 = k * k * k * k;
+ float k4F = 1.0f - k4;
+ float fl = (k4 * adaptingLuminance) + (0.1f * k4F * k4F * (float) Math.cbrt(
+ 5.0 * adaptingLuminance));
+
+ // Intermediate factor, ratio of background relative luminance to white relative luminance
+ float n = CamUtils.yFromLstar(backgroundLstar) / whitepoint[1];
+
+ // Base exponential nonlinearity
+ // note Schlomer 2018 has a typo and uses 1.58, the correct factor is 1.48
+ float z = 1.48f + (float) Math.sqrt(n);
+
+ // Luminance-level induction factors
+ float nbb = 0.725f / (float) Math.pow(n, 0.2);
+ float ncb = nbb;
+
+ // Discounted cone responses to the white point, adjusted for post-chromatic
+ // adaptation perceptual nonlinearities.
+ float[] rgbAFactors = new float[]{(float) Math.pow(fl * rgbD[0] * rW / 100.0, 0.42),
+ (float) Math.pow(fl * rgbD[1] * gW / 100.0, 0.42), (float) Math.pow(
+ fl * rgbD[2] * bW / 100.0, 0.42)};
+
+ float[] rgbA = new float[]{(400.0f * rgbAFactors[0]) / (rgbAFactors[0] + 27.13f),
+ (400.0f * rgbAFactors[1]) / (rgbAFactors[1] + 27.13f),
+ (400.0f * rgbAFactors[2]) / (rgbAFactors[2] + 27.13f), };
+
+ float aw = ((2.0f * rgbA[0]) + rgbA[1] + (0.05f * rgbA[2])) * nbb;
+
+ return new Frame(n, aw, nbb, ncb, c, nc, rgbD, fl, (float) Math.pow(fl, 0.25), z);
+ }
+}
diff --git a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
index 73d962e..0307268 100644
--- a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
+++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
@@ -20,7 +20,6 @@
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
-import android.os.SystemBatteryConsumer;
import android.os.UserHandle;
import android.util.SparseArray;
@@ -50,10 +49,11 @@
BatteryStats.STATS_SINCE_CHARGED);
final double powerMah = getMeasuredOrEstimatedPower(powerModel,
measuredEnergyUC, mPowerEstimator, durationMs);
- builder.getOrCreateSystemBatteryConsumerBuilder(
- SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY)
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, powerMah, powerModel)
- .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN, durationMs);
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY, durationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY,
+ powerMah, powerModel);
}
/**
diff --git a/core/java/com/android/internal/os/AudioPowerCalculator.java b/core/java/com/android/internal/os/AudioPowerCalculator.java
index 9da8191..2eab506 100644
--- a/core/java/com/android/internal/os/AudioPowerCalculator.java
+++ b/core/java/com/android/internal/os/AudioPowerCalculator.java
@@ -17,8 +17,10 @@
import android.os.BatteryConsumer;
import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.UidBatteryConsumer;
+import android.util.SparseArray;
/**
* A {@link PowerCalculator} to calculate power consumed by audio hardware.
@@ -31,18 +33,47 @@
// TODO(b/175344313): improve the model by taking into account different audio routes
private final UsageBasedPowerEstimator mPowerEstimator;
+ private static class PowerAndDuration {
+ public long durationMs;
+ public double powerMah;
+ }
+
public AudioPowerCalculator(PowerProfile powerProfile) {
mPowerEstimator = new UsageBasedPowerEstimator(
powerProfile.getAveragePower(PowerProfile.POWER_AUDIO));
}
@Override
- protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+ public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+ final PowerAndDuration total = new PowerAndDuration();
+
+ final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
+ builder.getUidBatteryConsumerBuilders();
+ for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
+ final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
+ calculateApp(app, total, app.getBatteryStatsUid(), rawRealtimeUs);
+ }
+
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_AUDIO, total.durationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_AUDIO, total.powerMah);
+
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+ .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_AUDIO, total.durationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_AUDIO, total.powerMah);
+ }
+
+ private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total,
+ BatteryStats.Uid u, long rawRealtimeUs) {
final long durationMs = mPowerEstimator.calculateDuration(u.getAudioTurnedOnTimer(),
rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
final double powerMah = mPowerEstimator.calculatePower(durationMs);
app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_AUDIO, durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_AUDIO, powerMah);
+ total.durationMs += durationMs;
+ total.powerMah += powerMah;
}
}
diff --git a/core/java/com/android/internal/os/BatteryChargeCalculator.java b/core/java/com/android/internal/os/BatteryChargeCalculator.java
index dc72f32..16f92ef 100644
--- a/core/java/com/android/internal/os/BatteryChargeCalculator.java
+++ b/core/java/com/android/internal/os/BatteryChargeCalculator.java
@@ -28,20 +28,28 @@
* Estimates the battery discharge amounts.
*/
public class BatteryChargeCalculator extends PowerCalculator {
- private final double mBatteryCapacity;
-
- public BatteryChargeCalculator(PowerProfile powerProfile) {
- mBatteryCapacity = powerProfile.getBatteryCapacity();
- }
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
builder.setDischargePercentage(
- batteryStats.getDischargeAmount(BatteryStats.STATS_SINCE_CHARGED))
- .setDischargedPowerRange(
- batteryStats.getLowDischargeAmountSinceCharge() * mBatteryCapacity / 100,
- batteryStats.getHighDischargeAmountSinceCharge() * mBatteryCapacity / 100);
+ batteryStats.getDischargeAmount(BatteryStats.STATS_SINCE_CHARGED));
+
+ int batteryCapacityMah = batteryStats.getLearnedBatteryCapacity() / 1000;
+ if (batteryCapacityMah <= 0) {
+ batteryCapacityMah = batteryStats.getMinLearnedBatteryCapacity() / 1000;
+ if (batteryCapacityMah <= 0) {
+ batteryCapacityMah = batteryStats.getEstimatedBatteryCapacity();
+ }
+ }
+ final double dischargedPowerLowerBoundMah =
+ batteryStats.getLowDischargeAmountSinceCharge() * batteryCapacityMah / 100.0;
+ final double dischargedPowerUpperBoundMah =
+ batteryStats.getHighDischargeAmountSinceCharge() * batteryCapacityMah / 100.0;
+ builder.setDischargePercentage(
+ batteryStats.getDischargeAmount(BatteryStats.STATS_SINCE_CHARGED))
+ .setDischargedPowerRange(dischargedPowerLowerBoundMah,
+ dischargedPowerUpperBoundMah);
final long batteryTimeRemainingMs = batteryStats.computeBatteryTimeRemaining(rawRealtimeUs);
if (batteryTimeRemainingMs != -1) {
@@ -52,6 +60,11 @@
if (chargeTimeRemainingMs != -1) {
builder.setChargeTimeRemainingMs(chargeTimeRemainingMs / 1000);
}
+
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .setConsumedPower(
+ (dischargedPowerLowerBoundMah + dischargedPowerUpperBoundMah) / 2);
}
@Override
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 02a29085..db67bab 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -161,7 +161,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- static final int VERSION = 198;
+ static final int VERSION = 199;
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
@@ -1056,6 +1056,7 @@
private int mBatteryVoltageMv = -1;
private int mEstimatedBatteryCapacityMah = -1;
+ private int mLastLearnedBatteryCapacityUah = -1;
private int mMinLearnedBatteryCapacityUah = -1;
private int mMaxLearnedBatteryCapacityUah = -1;
@@ -1142,6 +1143,11 @@
}
@Override
+ public int getLearnedBatteryCapacity() {
+ return mLastLearnedBatteryCapacityUah;
+ }
+
+ @Override
public int getMinLearnedBatteryCapacity() {
return mMinLearnedBatteryCapacityUah;
}
@@ -11199,6 +11205,7 @@
} else {
mEstimatedBatteryCapacityMah = -1;
}
+ mLastLearnedBatteryCapacityUah = -1;
mMinLearnedBatteryCapacityUah = -1;
mMaxLearnedBatteryCapacityUah = -1;
mInteractiveTimer.reset(false, elapsedRealtimeUs);
@@ -13799,6 +13806,7 @@
mRecordingHistory = DEBUG;
}
+ mLastLearnedBatteryCapacityUah = chargeFullUah;
if (mMinLearnedBatteryCapacityUah == -1) {
mMinLearnedBatteryCapacityUah = chargeFullUah;
} else {
@@ -15066,6 +15074,7 @@
mDischargeCurrentLevel = in.readInt();
mCurrentBatteryLevel = in.readInt();
mEstimatedBatteryCapacityMah = in.readInt();
+ mLastLearnedBatteryCapacityUah = in.readInt();
mMinLearnedBatteryCapacityUah = in.readInt();
mMaxLearnedBatteryCapacityUah = in.readInt();
mLowDischargeAmountSinceCharge = in.readInt();
@@ -15570,6 +15579,7 @@
out.writeInt(mDischargeCurrentLevel);
out.writeInt(mCurrentBatteryLevel);
out.writeInt(mEstimatedBatteryCapacityMah);
+ out.writeInt(mLastLearnedBatteryCapacityUah);
out.writeInt(mMinLearnedBatteryCapacityUah);
out.writeInt(mMaxLearnedBatteryCapacityUah);
out.writeInt(getLowDischargeAmountSinceCharge());
@@ -16071,6 +16081,7 @@
mRealtimeStartUs = in.readLong();
mOnBattery = in.readInt() != 0;
mEstimatedBatteryCapacityMah = in.readInt();
+ mLastLearnedBatteryCapacityUah = in.readInt();
mMinLearnedBatteryCapacityUah = in.readInt();
mMaxLearnedBatteryCapacityUah = in.readInt();
mOnBatteryInternal = false; // we are no longer really running.
@@ -16310,6 +16321,7 @@
out.writeLong(mRealtimeStartUs);
out.writeInt(mOnBattery ? 1 : 0);
out.writeInt(mEstimatedBatteryCapacityMah);
+ out.writeInt(mLastLearnedBatteryCapacityUah);
out.writeInt(mMinLearnedBatteryCapacityUah);
out.writeInt(mMaxLearnedBatteryCapacityUah);
mOnBatteryTimeBase.writeToParcel(out, uSecUptime, uSecRealtime);
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index babcea1..4989559 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -54,7 +54,7 @@
mPowerCalculators = new ArrayList<>();
// Power calculators are applied in the order of registration
- mPowerCalculators.add(new BatteryChargeCalculator(mPowerProfile));
+ mPowerCalculators.add(new BatteryChargeCalculator());
mPowerCalculators.add(new CpuPowerCalculator(mPowerProfile));
mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile));
mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile));
diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
index 2c32e48..6e99bbb 100644
--- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java
+++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
@@ -21,7 +21,6 @@
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Process;
-import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
import android.os.UserHandle;
import android.util.Log;
@@ -58,19 +57,11 @@
final PowerAndDuration total = new PowerAndDuration();
- SystemBatteryConsumer.Builder systemBatteryConsumerBuilder =
- builder.getOrCreateSystemBatteryConsumerBuilder(
- SystemBatteryConsumer.DRAIN_TYPE_BLUETOOTH);
-
final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
builder.getUidBatteryConsumerBuilders();
for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
calculateApp(app, total, query);
- if (app.getUid() == Process.BLUETOOTH_UID) {
- app.excludeFromBatteryUsageStats();
- systemBatteryConsumerBuilder.addUidBatteryConsumer(app);
- }
}
final long measuredChargeUC = batteryStats.getBluetoothMeasuredBatteryConsumptionUC();
@@ -87,12 +78,18 @@
Log.d(TAG, "Bluetooth active: time=" + (systemComponentDurationMs)
+ " power=" + formatCharge(systemPowerMah));
}
- systemBatteryConsumerBuilder
- .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
- systemComponentDurationMs)
+
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, systemDurationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
- Math.max(systemPowerMah, total.powerMah), powerModel)
- .setPowerConsumedByApps(total.powerMah);
+ Math.max(systemPowerMah, total.powerMah), powerModel);
+
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+ .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, total.durationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, total.powerMah,
+ powerModel);
}
private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total,
diff --git a/core/java/com/android/internal/os/CameraPowerCalculator.java b/core/java/com/android/internal/os/CameraPowerCalculator.java
index e56e7be..ddcabe8 100644
--- a/core/java/com/android/internal/os/CameraPowerCalculator.java
+++ b/core/java/com/android/internal/os/CameraPowerCalculator.java
@@ -17,6 +17,7 @@
import android.os.BatteryConsumer;
import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.UidBatteryConsumer;
@@ -36,6 +37,24 @@
}
@Override
+ public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+ super.calculate(builder, batteryStats, rawRealtimeUs, rawUptimeUs, query);
+
+ final long durationMs = batteryStats.getCameraOnTime(rawRealtimeUs,
+ BatteryStats.STATS_SINCE_CHARGED) / 1000;
+ final double powerMah = mPowerEstimator.calculatePower(durationMs);
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA, durationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA, powerMah);
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+ .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA, durationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA, powerMah);
+ }
+
+ @Override
protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
final long durationMs =
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index 2a55aa9..0d041c4 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -80,13 +80,28 @@
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+ double totalPowerMah = 0;
+
Result result = new Result();
final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
builder.getUidBatteryConsumerBuilders();
for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
calculateApp(app, app.getBatteryStatsUid(), query, result);
+ totalPowerMah += result.powerMah;
}
+
+ final long consumptionUC = batteryStats.getCpuMeasuredBatteryConsumptionUC();
+ final int powerModel = getPowerModel(consumptionUC, query);
+
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, totalPowerMah);
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU,
+ powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY
+ ? uCtoMah(consumptionUC) : totalPowerMah, powerModel);
}
private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
diff --git a/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java b/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
index 9941e30..9b51a8e 100644
--- a/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
+++ b/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java
@@ -15,12 +15,13 @@
*/
package com.android.internal.os;
+import android.os.AggregateBatteryConsumer;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
-import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
+import android.util.SparseArray;
/**
* Calculates the amount of power consumed by custom energy consumers (i.e. consumers of type
@@ -33,33 +34,62 @@
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
- super.calculate(builder, batteryStats, rawRealtimeUs, rawUptimeUs, query);
+ double[] totalAppPowerMah = null;
+
+ final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
+ builder.getUidBatteryConsumerBuilders();
+ for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
+ final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
+ totalAppPowerMah = calculateApp(app, app.getBatteryStatsUid(), totalAppPowerMah);
+ }
+
final double[] customMeasuredPowerMah = calculateMeasuredEnergiesMah(
batteryStats.getCustomConsumerMeasuredBatteryConsumptionUC());
if (customMeasuredPowerMah != null) {
- final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder =
- builder.getOrCreateSystemBatteryConsumerBuilder(
- SystemBatteryConsumer.DRAIN_TYPE_CUSTOM);
+ final AggregateBatteryConsumer.Builder deviceBatteryConsumerBuilder =
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
for (int i = 0; i < customMeasuredPowerMah.length; i++) {
- systemBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
+ deviceBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + i,
customMeasuredPowerMah[i]);
}
}
+ if (totalAppPowerMah != null) {
+ final AggregateBatteryConsumer.Builder appsBatteryConsumerBuilder =
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
+ for (int i = 0; i < totalAppPowerMah.length; i++) {
+ appsBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + i,
+ totalAppPowerMah[i]);
+ }
+ }
}
- @Override
- protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
- long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+ private double[] calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+ double[] totalPowerMah) {
+ double[] newTotalPowerMah = null;
final double[] customMeasuredPowerMah = calculateMeasuredEnergiesMah(
u.getCustomConsumerMeasuredBatteryConsumptionUC());
if (customMeasuredPowerMah != null) {
+ if (totalPowerMah == null) {
+ newTotalPowerMah = new double[customMeasuredPowerMah.length];
+ } else if (totalPowerMah.length != customMeasuredPowerMah.length) {
+ newTotalPowerMah = new double[customMeasuredPowerMah.length];
+ System.arraycopy(totalPowerMah, 0, newTotalPowerMah, 0,
+ customMeasuredPowerMah.length);
+ } else {
+ newTotalPowerMah = totalPowerMah;
+ }
for (int i = 0; i < customMeasuredPowerMah.length; i++) {
app.setConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + i,
customMeasuredPowerMah[i]);
+ newTotalPowerMah[i] += customMeasuredPowerMah[i];
}
}
+ return newTotalPowerMah;
}
@Override
diff --git a/core/java/com/android/internal/os/FlashlightPowerCalculator.java b/core/java/com/android/internal/os/FlashlightPowerCalculator.java
index cbe0cde..32df17c 100644
--- a/core/java/com/android/internal/os/FlashlightPowerCalculator.java
+++ b/core/java/com/android/internal/os/FlashlightPowerCalculator.java
@@ -17,6 +17,7 @@
import android.os.BatteryConsumer;
import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.UidBatteryConsumer;
@@ -34,6 +35,24 @@
}
@Override
+ public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+ super.calculate(builder, batteryStats, rawRealtimeUs, rawUptimeUs, query);
+
+ final long durationMs = batteryStats.getFlashlightOnTime(rawRealtimeUs,
+ BatteryStats.STATS_SINCE_CHARGED) / 1000;
+ final double powerMah = mPowerEstimator.calculatePower(durationMs);
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, durationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, powerMah);
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+ .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, durationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, powerMah);
+ }
+
+ @Override
protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
final long durationMs = mPowerEstimator.calculateDuration(u.getFlashlightTurnedOnTimer(),
diff --git a/core/java/com/android/internal/os/GnssPowerCalculator.java b/core/java/com/android/internal/os/GnssPowerCalculator.java
index 7eb4b4a..a508e03 100644
--- a/core/java/com/android/internal/os/GnssPowerCalculator.java
+++ b/core/java/com/android/internal/os/GnssPowerCalculator.java
@@ -46,6 +46,7 @@
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+ double appsPowerMah = 0;
final double averageGnssPowerMa = getAverageGnssPower(batteryStats, rawRealtimeUs,
BatteryStats.STATS_SINCE_CHARGED);
final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
@@ -55,12 +56,27 @@
final long consumptionUC =
app.getBatteryStatsUid().getGnssMeasuredBatteryConsumptionUC();
final int powerModel = getPowerModel(consumptionUC, query);
- calculateApp(app, app.getBatteryStatsUid(), powerModel, rawRealtimeUs,
- averageGnssPowerMa, consumptionUC);
+ appsPowerMah += calculateApp(app, app.getBatteryStatsUid(), powerModel,
+ rawRealtimeUs, averageGnssPowerMa, consumptionUC);
}
+
+ final long consumptionUC = batteryStats.getGnssMeasuredBatteryConsumptionUC();
+ final int powerModel = getPowerModel(consumptionUC, query);
+ double powerMah;
+ if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
+ powerMah = uCtoMah(consumptionUC);
+ } else {
+ powerMah = appsPowerMah;
+ }
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS, powerMah, powerModel);
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS, appsPowerMah, powerModel);
}
- private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+ private double calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
@BatteryConsumer.PowerModel int powerModel, long rawRealtimeUs,
double averageGnssPowerMa, long measuredChargeUC) {
final long durationMs = computeDuration(u, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
@@ -76,6 +92,7 @@
app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_GNSS, durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS, powerMah, powerModel);
+ return powerMah;
}
@Override
diff --git a/core/java/com/android/internal/os/IdlePowerCalculator.java b/core/java/com/android/internal/os/IdlePowerCalculator.java
index 0c80deb..d33a88d 100644
--- a/core/java/com/android/internal/os/IdlePowerCalculator.java
+++ b/core/java/com/android/internal/os/IdlePowerCalculator.java
@@ -20,7 +20,6 @@
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
-import android.os.SystemBatteryConsumer;
import android.os.UserHandle;
import android.util.Log;
import android.util.SparseArray;
@@ -53,7 +52,8 @@
calculatePowerAndDuration(batteryStats, rawRealtimeUs, rawUptimeUs,
BatteryStats.STATS_SINCE_CHARGED);
if (mPowerMah != 0) {
- builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_IDLE)
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_IDLE, mPowerMah)
.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_IDLE, mDurationMs);
}
diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
index 64c68ce..e670178 100644
--- a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
@@ -482,7 +482,8 @@
// Unit is 10ms.
mDeltaTimes[i] = mCurTimes[i] - lastTimes[i];
if (mDeltaTimes[i] < 0) {
- Slog.e(mTag, "Negative delta from freq time proc: " + mDeltaTimes[i]);
+ Slog.e(mTag, "Negative delta from freq time for uid: " + uid
+ + ", delta: " + mDeltaTimes[i]);
valid = false;
}
notify |= mDeltaTimes[i] > 0;
@@ -648,7 +649,8 @@
cb.onUidCpuTime(uid, delta);
}
} else if (delta < 0) {
- Slog.e(mTag, "Negative delta from active time proc: " + delta);
+ Slog.e(mTag, "Negative delta from active time for uid: " + uid
+ + ", delta: " + delta);
}
}
}
@@ -822,7 +824,8 @@
for (int i = 0; i < mNumClusters; i++) {
mDeltaTime[i] = mCurTime[i] - lastTimes[i];
if (mDeltaTime[i] < 0) {
- Slog.e(mTag, "Negative delta from cluster time proc: " + mDeltaTime[i]);
+ Slog.e(mTag, "Negative delta from cluster time for uid: " + uid
+ + ", delta: " + mDeltaTime[i]);
valid = false;
}
notify |= mDeltaTime[i] > 0;
diff --git a/core/java/com/android/internal/os/MemoryPowerCalculator.java b/core/java/com/android/internal/os/MemoryPowerCalculator.java
index 5d5c155..09fd85e 100644
--- a/core/java/com/android/internal/os/MemoryPowerCalculator.java
+++ b/core/java/com/android/internal/os/MemoryPowerCalculator.java
@@ -4,7 +4,6 @@
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
-import android.os.SystemBatteryConsumer;
import android.os.UserHandle;
import android.util.LongSparseArray;
import android.util.SparseArray;
@@ -31,7 +30,8 @@
BatteryStats.STATS_SINCE_CHARGED);
final double powerMah = calculatePower(batteryStats, rawRealtimeUs,
BatteryStats.STATS_SINCE_CHARGED);
- builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_MEMORY)
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_MEMORY, durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_MEMORY, powerMah);
}
diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
index 4db15a4..eb5993d 100644
--- a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
+++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
@@ -19,7 +19,6 @@
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
-import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
import android.os.UserHandle;
import android.telephony.CellSignalStrength;
@@ -105,14 +104,19 @@
calculateRemaining(total, powerModel, batteryStats, rawRealtimeUs, consumptionUC);
if (total.remainingPowerMah != 0 || total.totalAppPowerMah != 0) {
- builder.getOrCreateSystemBatteryConsumerBuilder(
- SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO)
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
total.durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
- total.remainingPowerMah + total.totalAppPowerMah,
- powerModel)
- .setPowerConsumedByApps(total.totalAppPowerMah);
+ total.remainingPowerMah + total.totalAppPowerMah, powerModel);
+
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+ .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ total.durationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ total.totalAppPowerMah, powerModel);
}
}
diff --git a/core/java/com/android/internal/os/PhonePowerCalculator.java b/core/java/com/android/internal/os/PhonePowerCalculator.java
index 2e3bff3..8dd463c 100644
--- a/core/java/com/android/internal/os/PhonePowerCalculator.java
+++ b/core/java/com/android/internal/os/PhonePowerCalculator.java
@@ -20,7 +20,6 @@
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
-import android.os.SystemBatteryConsumer;
import android.os.UserHandle;
import android.util.SparseArray;
@@ -44,7 +43,8 @@
BatteryStats.STATS_SINCE_CHARGED) / 1000;
final double phoneOnPower = mPowerEstimator.calculatePower(phoneOnTimeMs);
if (phoneOnPower != 0) {
- builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_PHONE)
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_PHONE, phoneOnPower)
.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_PHONE, phoneOnTimeMs);
}
diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java
index dc0f719..1b3bc23 100644
--- a/core/java/com/android/internal/os/ScreenPowerCalculator.java
+++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java
@@ -20,7 +20,6 @@
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
-import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
import android.os.UserHandle;
import android.text.format.DateUtils;
@@ -68,6 +67,7 @@
rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED, consumptionUC);
double totalAppPower = 0;
+ long totalAppDuration = 0;
// Now deal with each app's UidBatteryConsumer. The results are stored in the
// BatteryConsumer.POWER_COMPONENT_SCREEN power component, which is considered smeared,
@@ -86,6 +86,7 @@
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN,
appPowerAndDuration.powerMah, powerModel);
totalAppPower += appPowerAndDuration.powerMah;
+ totalAppDuration += appPowerAndDuration.durationMs;
}
break;
case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
@@ -93,14 +94,20 @@
smearScreenBatteryDrain(uidBatteryConsumerBuilders, totalPowerAndDuration,
rawRealtimeUs);
totalAppPower = totalPowerAndDuration.powerMah;
+ totalAppDuration = totalPowerAndDuration.durationMs;
}
- builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_SCREEN)
- .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN,
- totalPowerAndDuration.durationMs)
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN,
Math.max(totalPowerAndDuration.powerMah, totalAppPower), powerModel)
- .setPowerConsumedByApps(totalAppPower);
+ .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN,
+ totalPowerAndDuration.durationMs);
+
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, totalAppPower, powerModel)
+ .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN, totalAppDuration);
}
/**
diff --git a/core/java/com/android/internal/os/SensorPowerCalculator.java b/core/java/com/android/internal/os/SensorPowerCalculator.java
index d18b7b1..83e5b57 100644
--- a/core/java/com/android/internal/os/SensorPowerCalculator.java
+++ b/core/java/com/android/internal/os/SensorPowerCalculator.java
@@ -19,6 +19,7 @@
import android.hardware.SensorManager;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.UidBatteryConsumer;
import android.util.SparseArray;
@@ -38,12 +39,32 @@
}
@Override
- protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+ public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+ double appsPowerMah = 0;
+ final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
+ builder.getUidBatteryConsumerBuilders();
+ for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
+ final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
+ appsPowerMah += calculateApp(app, app.getBatteryStatsUid(), rawRealtimeUs);
+ }
+
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SENSORS, appsPowerMah);
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SENSORS, appsPowerMah);
+ }
+
+ private double calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+ long rawRealtimeUs) {
+ final double powerMah = calculatePowerMah(u, rawRealtimeUs,
+ BatteryStats.STATS_SINCE_CHARGED);
app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SENSORS,
calculateDuration(u, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED))
- .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SENSORS,
- calculatePowerMah(u, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED));
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SENSORS, powerMah);
+ return powerMah;
}
@Override
diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
index b4d5f97..a26abc2 100644
--- a/core/java/com/android/internal/os/SystemServicePowerCalculator.java
+++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
@@ -87,6 +87,15 @@
systemServicePowerMah * uid.getProportionalSystemServiceUsage());
}
}
+
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES,
+ systemServicePowerMah);
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES,
+ systemServicePowerMah);
}
@Override
diff --git a/core/java/com/android/internal/os/VideoPowerCalculator.java b/core/java/com/android/internal/os/VideoPowerCalculator.java
index 0cad9a7..47916a6 100644
--- a/core/java/com/android/internal/os/VideoPowerCalculator.java
+++ b/core/java/com/android/internal/os/VideoPowerCalculator.java
@@ -17,8 +17,10 @@
import android.os.BatteryConsumer;
import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.UidBatteryConsumer;
+import android.util.SparseArray;
/**
* A {@link PowerCalculator} to calculate power consumed by video hardware.
@@ -28,18 +30,47 @@
public class VideoPowerCalculator extends PowerCalculator {
private final UsageBasedPowerEstimator mPowerEstimator;
+ private static class PowerAndDuration {
+ public long durationMs;
+ public double powerMah;
+ }
+
public VideoPowerCalculator(PowerProfile powerProfile) {
mPowerEstimator = new UsageBasedPowerEstimator(
powerProfile.getAveragePower(PowerProfile.POWER_VIDEO));
}
@Override
- protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+ public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
+ final PowerAndDuration total = new PowerAndDuration();
+
+ final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
+ builder.getUidBatteryConsumerBuilders();
+ for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
+ final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
+ calculateApp(app, total, app.getBatteryStatsUid(), rawRealtimeUs);
+ }
+
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_VIDEO, total.durationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_VIDEO, total.powerMah);
+
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+ .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_VIDEO, total.durationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_VIDEO, total.powerMah);
+ }
+
+ private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total,
+ BatteryStats.Uid u, long rawRealtimeUs) {
final long durationMs = mPowerEstimator.calculateDuration(u.getVideoTurnedOnTimer(),
rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
final double powerMah = mPowerEstimator.calculatePower(durationMs);
app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_VIDEO, durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_VIDEO, powerMah);
+ total.durationMs += durationMs;
+ total.powerMah += powerMah;
}
}
diff --git a/core/java/com/android/internal/os/WakelockPowerCalculator.java b/core/java/com/android/internal/os/WakelockPowerCalculator.java
index 194b6b8..d594107 100644
--- a/core/java/com/android/internal/os/WakelockPowerCalculator.java
+++ b/core/java/com/android/internal/os/WakelockPowerCalculator.java
@@ -51,6 +51,7 @@
double osPowerMah = 0;
long osDurationMs = 0;
long totalAppDurationMs = 0;
+ double appPowerMah = 0;
final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
builder.getUidBatteryConsumerBuilders();
for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
@@ -60,6 +61,7 @@
app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.powerMah);
totalAppDurationMs += result.durationMs;
+ appPowerMah += result.powerMah;
if (app.getUid() == Process.ROOT_UID) {
osBatteryConsumer = app;
@@ -71,13 +73,24 @@
// The device has probably been awake for longer than the screen on
// time and application wake lock time would account for. Assign
// this remainder to the OS, if possible.
+ calculateRemaining(result, batteryStats, rawRealtimeUs, rawUptimeUs,
+ BatteryStats.STATS_SINCE_CHARGED, osPowerMah, osDurationMs, totalAppDurationMs);
if (osBatteryConsumer != null) {
- calculateRemaining(result, batteryStats, rawRealtimeUs, rawUptimeUs,
- BatteryStats.STATS_SINCE_CHARGED, osPowerMah, osDurationMs, totalAppDurationMs);
osBatteryConsumer.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK,
result.durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.powerMah);
}
+
+ final long wakeTimeMillis =
+ calculateWakeTimeMillis(batteryStats, rawRealtimeUs, rawUptimeUs);
+ final double powerMah = mPowerEstimator.calculatePower(wakeTimeMillis);
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK, wakeTimeMillis)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, powerMah);
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, appPowerMah);
}
@Override
@@ -145,8 +158,7 @@
private void calculateRemaining(PowerAndDuration result, BatteryStats stats, long rawRealtimeUs,
long rawUptimeUs, int statsType, double osPowerMah, long osDurationMs,
long totalAppDurationMs) {
- final long wakeTimeMillis = stats.getBatteryUptime(rawUptimeUs) / 1000
- - stats.getScreenOnTime(rawRealtimeUs, statsType) / 1000
+ final long wakeTimeMillis = calculateWakeTimeMillis(stats, rawRealtimeUs, rawUptimeUs)
- totalAppDurationMs;
if (wakeTimeMillis > 0) {
final double power = mPowerEstimator.calculatePower(wakeTimeMillis);
@@ -157,4 +169,12 @@
result.powerMah = osPowerMah + power;
}
}
+
+ private long calculateWakeTimeMillis(BatteryStats batteryStats, long rawRealtimeUs,
+ long rawUptimeUs) {
+ final long batteryUptimeUs = batteryStats.getBatteryUptime(rawUptimeUs);
+ final long screenOnTimeUs =
+ batteryStats.getScreenOnTime(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
+ return (batteryUptimeUs - screenOnTimeUs) / 1000;
+ }
}
diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java
index ef5b147..776a705 100644
--- a/core/java/com/android/internal/os/WifiPowerCalculator.java
+++ b/core/java/com/android/internal/os/WifiPowerCalculator.java
@@ -20,7 +20,6 @@
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Process;
-import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
import android.os.UserHandle;
import android.util.Log;
@@ -79,10 +78,6 @@
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
- final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder =
- builder.getOrCreateSystemBatteryConsumerBuilder(
- SystemBatteryConsumer.DRAIN_TYPE_WIFI);
-
long totalAppDurationMs = 0;
double totalAppPowerMah = 0;
final PowerDurationAndTraffic powerDurationAndTraffic = new PowerDurationAndTraffic();
@@ -104,11 +99,6 @@
powerDurationAndTraffic.durationMs);
app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI,
powerDurationAndTraffic.powerMah, powerModel);
-
- if (app.getUid() == Process.WIFI_UID) {
- systemBatteryConsumerBuilder.addUidBatteryConsumer(app);
- app.excludeFromBatteryUsageStats();
- }
}
final long consumptionUC = batteryStats.getWifiMeasuredBatteryConsumptionUC();
@@ -117,12 +107,16 @@
BatteryStats.STATS_SINCE_CHARGED, batteryStats.hasWifiActivityReporting(),
totalAppDurationMs, totalAppPowerMah, consumptionUC);
- systemBatteryConsumerBuilder
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI,
powerDurationAndTraffic.durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI,
- totalAppPowerMah + powerDurationAndTraffic.powerMah, powerModel)
- .setPowerConsumedByApps(totalAppPowerMah);
+ totalAppPowerMah + powerDurationAndTraffic.powerMah, powerModel);
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI,
+ totalAppPowerMah, powerModel);
}
/**
diff --git a/core/java/com/android/internal/view/IDragAndDropPermissions.aidl b/core/java/com/android/internal/view/IDragAndDropPermissions.aidl
index edb759a..4834d22 100644
--- a/core/java/com/android/internal/view/IDragAndDropPermissions.aidl
+++ b/core/java/com/android/internal/view/IDragAndDropPermissions.aidl
@@ -24,6 +24,6 @@
*/
interface IDragAndDropPermissions {
void take(IBinder activityToken);
- void takeTransient(IBinder transientToken);
+ void takeTransient();
void release();
}
diff --git a/core/java/com/android/internal/widget/RecyclerView.java b/core/java/com/android/internal/widget/RecyclerView.java
index 9ed5eb1..be15a9b 100644
--- a/core/java/com/android/internal/widget/RecyclerView.java
+++ b/core/java/com/android/internal/widget/RecyclerView.java
@@ -16,16 +16,10 @@
package com.android.internal.widget;
-import static android.widget.EdgeEffect.TYPE_GLOW;
-import static android.widget.EdgeEffect.TYPE_STRETCH;
-import static android.widget.EdgeEffect.USE_STRETCH_EDGE_EFFECT_BY_DEFAULT;
-import static android.widget.EdgeEffect.USE_STRETCH_EDGE_EFFECT_FOR_SUPPORTED;
-
import android.annotation.CallSuper;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.compat.Compatibility;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
@@ -466,8 +460,6 @@
private final int[] mScrollConsumed = new int[2];
private final int[] mNestedOffsets = new int[2];
- private int mEdgeEffectType;
-
/**
* These are views that had their a11y importance changed during a layout. We defer these events
* until the end of the layout because a11y service may make sync calls back to the RV while
@@ -595,12 +587,8 @@
setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
}
- boolean defaultToStretch = Compatibility.isChangeEnabled(USE_STRETCH_EDGE_EFFECT_BY_DEFAULT)
- || Compatibility.isChangeEnabled(USE_STRETCH_EDGE_EFFECT_FOR_SUPPORTED);
TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.EdgeEffect);
- mEdgeEffectType = a.getInt(com.android.internal.R.styleable.EdgeEffect_edgeEffectType,
- defaultToStretch ? TYPE_STRETCH : TYPE_GLOW);
a.recycle();
// Re-set whether nested scrolling is enabled so that it is set on all API levels
@@ -626,28 +614,6 @@
}
/**
- * Returns the {@link EdgeEffect#getType()} used for all EdgeEffects.
- *
- * @return @link EdgeEffect#getType()} used for all EdgeEffects.
- */
- @EdgeEffect.EdgeEffectType
- public int getEdgeEffectType() {
- return mEdgeEffectType;
- }
-
- /**
- * Sets the {@link EdgeEffect#getType()} used in all EdgeEffects.
- * Any existing over-scroll effects are cleared and new effects are created as needed.
- *
- * @param type the {@link EdgeEffect#getType()} used in all EdgeEffects.
- */
- public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) {
- mEdgeEffectType = type;
- invalidateGlows();
- invalidate();
- }
-
- /**
* Instantiate and set a LayoutManager, if specified in the attributes.
*/
private void createLayoutManager(Context context, String className, AttributeSet attrs,
@@ -2223,7 +2189,6 @@
return;
}
mLeftGlow = new EdgeEffect(getContext());
- mLeftGlow.setType(mEdgeEffectType);
if (mClipToPadding) {
mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
@@ -2237,7 +2202,6 @@
return;
}
mRightGlow = new EdgeEffect(getContext());
- mRightGlow.setType(mEdgeEffectType);
if (mClipToPadding) {
mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
@@ -2251,7 +2215,6 @@
return;
}
mTopGlow = new EdgeEffect(getContext());
- mTopGlow.setType(mEdgeEffectType);
if (mClipToPadding) {
mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
@@ -2266,7 +2229,6 @@
return;
}
mBottomGlow = new EdgeEffect(getContext());
- mBottomGlow.setType(mEdgeEffectType);
if (mClipToPadding) {
mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index 93cde3d..1174db5 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -387,28 +387,6 @@
}
/**
- * Returns the {@link EdgeEffect#getType()} for the edge effects.
- * @return the {@link EdgeEffect#getType()} for the edge effects.
- * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
- */
- @EdgeEffect.EdgeEffectType
- public int getEdgeEffectType() {
- // Both left and right edge have the same edge effect type
- return mLeftEdge.getType();
- }
-
- /**
- * Sets the {@link EdgeEffect#setType(int)} for the edge effects.
- * @param type The edge effect type to use for the edge effects.
- * @attr ref android.R.styleable#EdgeEffect_edgeEffectType
- */
- public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) {
- mLeftEdge.setType(type);
- mRightEdge.setType(type);
- invalidate();
- }
-
- /**
* Set a PagerAdapter that will supply views for this pager as needed.
*
* @param adapter Adapter to use
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 480b478..f0c43ff 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1179,15 +1179,6 @@
<!-- Color applied to effects. -->
<attr name="effectColor" format="color" />
- <!-- The type of the edge effect. The default is glow. -->
- <attr name="edgeEffectType">
- <!-- Use a colored glow at the edge. -->
- <enum name="glow" value="0" />
-
- <!-- Stretch the content. -->
- <enum name="stretch" value="1" />
- </attr>
-
<!-- =================== -->
<!-- Lighting properties -->
<!-- =================== -->
@@ -9257,7 +9248,6 @@
<!-- Used as a filter array on the theme to pull out only the EdgeEffect-relevant bits. -->
<declare-styleable name="EdgeEffect">
<attr name="colorEdgeEffect" />
- <attr name="edgeEffectType" />
</declare-styleable>
<!-- Use <code>tv-input</code> as the root tag of the XML resource that describes a
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 0e9526a..482b112 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3064,7 +3064,6 @@
<public name="hotwordDetectionService" />
<public name="previewLayout" />
<public name="clipToOutline" />
- <public name="edgeEffectType" />
<public name="knownCerts" />
<public name="windowBackgroundBlurRadius"/>
<public name="windowSplashScreenBackground"/>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_device_24.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_device_24.xml
new file mode 100644
index 0000000..223cdf4
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_device_24.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="#3ddc84">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M17,1L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-2 -2,-2zM7,6h10v10L7,16L7,6zM17,21L7,21v-3h10v3zM7,4L7,3h10v1L7,4zM10,19h4v1h-4z"/>
+</vector>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
index a15a8d8..24b164b 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
@@ -19,7 +19,6 @@
import android.content.Context;
import android.os.BatteryConsumer;
import android.os.BatteryUsageStats;
-import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
import android.os.UserHandle;
import android.util.DebugUtils;
@@ -63,7 +62,7 @@
}
mBatteryConsumerInfo = BatteryConsumerInfoHelper.makeBatteryConsumerInfo(
- context.getPackageManager(), requestedBatteryConsumer);
+ requestedBatteryConsumer, batteryConsumerId, context.getPackageManager());
double[] totalPowerByComponentMah = new double[BatteryConsumer.POWER_COMPONENT_COUNT];
double[] totalModeledPowerByComponentMah =
@@ -119,16 +118,20 @@
private BatteryConsumer getRequestedBatteryConsumer(BatteryUsageStats batteryUsageStats,
String batteryConsumerId) {
+ for (int scope = 0;
+ scope < BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT;
+ scope++) {
+ if (batteryConsumerId(scope).equals(batteryConsumerId)) {
+ return batteryUsageStats.getAggregateBatteryConsumer(scope);
+ }
+ }
+
for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
if (batteryConsumerId(consumer).equals(batteryConsumerId)) {
return consumer;
}
}
- for (BatteryConsumer consumer : batteryUsageStats.getSystemBatteryConsumers()) {
- if (batteryConsumerId(consumer).equals(batteryConsumerId)) {
- return consumer;
- }
- }
+
return null;
}
@@ -148,11 +151,25 @@
private void computeTotalPower(BatteryUsageStats batteryUsageStats,
double[] powerByComponentMah) {
- for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
- for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT;
- component++) {
- powerByComponentMah[component] += consumer.getConsumedPower(component);
- }
+ final BatteryConsumer consumer =
+ batteryUsageStats.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
+ for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) {
+ powerByComponentMah[component] += consumer.getConsumedPower(component);
+ }
+ }
+
+ private void computeTotalPowerForCustomComponent(
+ BatteryUsageStats batteryUsageStats, double[] powerByComponentMah) {
+ final BatteryConsumer consumer =
+ batteryUsageStats.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
+ final int customComponentCount = consumer.getCustomPowerComponentCount();
+ for (int component = 0;
+ component < Math.min(customComponentCount, powerByComponentMah.length);
+ component++) {
+ powerByComponentMah[component] += consumer.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component);
}
}
@@ -166,19 +183,6 @@
}
}
- private void computeTotalPowerForCustomComponent(
- BatteryUsageStats batteryUsageStats, double[] powerByComponentMah) {
- for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
- final int customComponentCount = consumer.getCustomPowerComponentCount();
- for (int component = 0;
- component < Math.min(customComponentCount, powerByComponentMah.length);
- component++) {
- powerByComponentMah[component] += consumer.getConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component);
- }
- }
- }
-
private void addEntry(String title, EntryType entryType, double amount, double totalAmount,
boolean isSystemBatteryConsumer) {
Entry entry = new Entry();
@@ -203,10 +207,13 @@
return "APP|"
+ UserHandle.getUserId(((UidBatteryConsumer) consumer).getUid()) + "|"
+ ((UidBatteryConsumer) consumer).getUid();
- } else if (consumer instanceof SystemBatteryConsumer) {
- return ((SystemBatteryConsumer) consumer).getDrainType() + "|0|0";
} else {
return "";
}
}
-}
+
+ public static String batteryConsumerId(
+ @BatteryUsageStats.AggregateBatteryConsumerScope int scope) {
+ return "SYS|" + scope;
+ }
+}
\ No newline at end of file
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java
index 6288e0b..f2d6bca 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java
@@ -16,11 +16,13 @@
package com.android.frameworks.core.batterystatsviewer;
+import static com.android.frameworks.core.batterystatsviewer.BatteryConsumerData.batteryConsumerId;
+
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.BatteryConsumer;
+import android.os.BatteryUsageStats;
import android.os.Process;
-import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
import android.util.DebugUtils;
@@ -41,10 +43,11 @@
}
@NonNull
- public static BatteryConsumerInfo makeBatteryConsumerInfo(PackageManager packageManager,
- @NonNull BatteryConsumer batteryConsumer) {
+ public static BatteryConsumerInfo makeBatteryConsumerInfo(
+ @NonNull BatteryConsumer batteryConsumer, String batteryConsumerId,
+ PackageManager packageManager) {
BatteryConsumerInfo info = new BatteryConsumerInfo();
- info.id = BatteryConsumerData.batteryConsumerId(batteryConsumer);
+ info.id = batteryConsumerId;
info.powerMah = batteryConsumer.getConsumedPower();
if (batteryConsumer instanceof UidBatteryConsumer) {
@@ -100,25 +103,29 @@
info.packages = sb;
}
}
- } else if (batteryConsumer instanceof SystemBatteryConsumer) {
- final SystemBatteryConsumer systemBatteryConsumer =
- (SystemBatteryConsumer) batteryConsumer;
- final int drainType = systemBatteryConsumer.getDrainType();
- String name = DebugUtils.constantToString(SystemBatteryConsumer.class, "DRAIN_TYPE_",
- drainType);
- info.label = name.charAt(0) + name.substring(1).toLowerCase().replace('_', ' ');
- info.isSystemBatteryConsumer = true;
- }
-
- // Default the app icon to System Server. This includes root, dex2oat and other UIDs.
- if (info.iconInfo == null) {
- try {
- info.iconInfo =
- packageManager.getApplicationInfo(SYSTEM_SERVER_PACKAGE_NAME, 0);
- } catch (PackageManager.NameNotFoundException nameNotFoundException) {
- // Won't happen
+ // Default the app icon to System Server. This includes root, dex2oat and other UIDs.
+ if (info.iconInfo == null) {
+ try {
+ info.iconInfo =
+ packageManager.getApplicationInfo(SYSTEM_SERVER_PACKAGE_NAME, 0);
+ } catch (PackageManager.NameNotFoundException nameNotFoundException) {
+ // Won't happen
+ }
+ }
+ } else {
+ for (int scope = 0;
+ scope < BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT;
+ scope++) {
+ if (batteryConsumerId(scope).equals(batteryConsumerId)) {
+ final String name = DebugUtils.constantToString(BatteryUsageStats.class,
+ "AGGREGATE_BATTERY_CONSUMER_SCOPE_", scope)
+ .replace('_', ' ');
+ info.label = name;
+ break;
+ }
}
}
+
return info;
}
}
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java
index 63a15d6..9e63a35 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java
@@ -18,68 +18,60 @@
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.BatteryStatsManager;
+import android.os.BatteryUsageStats;
import android.os.Bundle;
+import android.os.UidBatteryConsumer;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+import androidx.activity.ComponentActivity;
import androidx.annotation.NonNull;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentActivity;
-import androidx.fragment.app.FragmentStatePagerAdapter;
-import androidx.viewpager.widget.ViewPager;
+import androidx.loader.app.LoaderManager;
+import androidx.loader.content.Loader;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
-import com.google.android.material.tabs.TabLayout;
+import com.android.settingslib.utils.AsyncLoaderCompat;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Locale;
/**
* Picker, showing a sorted lists of applications and other types of entities consuming power.
* Opens BatteryStatsViewerActivity upon item selection.
*/
-public class BatteryConsumerPickerActivity extends FragmentActivity {
+public class BatteryConsumerPickerActivity extends ComponentActivity {
private static final String PREF_SELECTED_BATTERY_CONSUMER = "batteryConsumerId";
+ private static final int BATTERY_STATS_REFRESH_RATE_MILLIS = 60 * 1000;
+ private BatteryConsumerListAdapter mBatteryConsumerListAdapter;
+ private RecyclerView mAppList;
+ private View mLoadingView;
+ private final Runnable mBatteryStatsRefresh = this::loadBatteryStats;
+
+ private interface OnBatteryConsumerSelectedListener {
+ void onBatteryConsumerSelected(String batteryConsumerId);
+ }
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
- setContentView(R.layout.battery_consumer_picker_activity_layout);
+ setContentView(R.layout.battery_consumer_picker_layout);
+ mLoadingView = findViewById(R.id.loading_view);
- ViewPager viewPager = findViewById(R.id.pager);
+ mAppList = findViewById(R.id.list_view);
+ mAppList.setLayoutManager(new LinearLayoutManager(this));
+ mBatteryConsumerListAdapter =
+ new BatteryConsumerListAdapter((this::setSelectedBatteryConsumer));
+ mAppList.setAdapter(mBatteryConsumerListAdapter);
- FragmentStatePagerAdapter adapter = new FragmentStatePagerAdapter(
- getSupportFragmentManager()) {
-
- @Override
- public int getCount() {
- return 2;
- }
-
- @NonNull
- @Override
- public Fragment getItem(int position) {
- switch (position) {
- case 0:
- return new BatteryConsumerPickerFragment(
- BatteryConsumerPickerFragment.PICKER_TYPE_APP);
- case 1:
- default:
- return new BatteryConsumerPickerFragment(
- BatteryConsumerPickerFragment.PICKER_TYPE_DRAIN);
- }
- }
-
- @Override
- public CharSequence getPageTitle(int position) {
- switch (position) {
- case 0:
- return "Apps";
- case 1:
- return "Drains";
- }
- return null;
- }
- };
-
- viewPager.setAdapter(adapter);
- TabLayout tabLayout = findViewById(R.id.tab_layout);
- tabLayout.setupWithViewPager(viewPager);
if (icicle == null) {
final String batteryConsumerId = getPreferences(Context.MODE_PRIVATE)
.getString(PREF_SELECTED_BATTERY_CONSUMER, null);
@@ -101,4 +93,183 @@
.putExtra(BatteryStatsViewerActivity.EXTRA_BATTERY_CONSUMER, batteryConsumerId);
startActivity(intent);
}
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ loadBatteryStats();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ getMainThreadHandler().removeCallbacks(mBatteryStatsRefresh);
+ }
+
+ private void loadBatteryStats() {
+ LoaderManager.getInstance(this).restartLoader(0, null,
+ new BatteryConsumerListLoaderCallbacks());
+ getMainThreadHandler().postDelayed(mBatteryStatsRefresh, BATTERY_STATS_REFRESH_RATE_MILLIS);
+ }
+
+ private static class BatteryConsumerListLoader extends
+ AsyncLoaderCompat<List<BatteryConsumerInfoHelper.BatteryConsumerInfo>> {
+ private final BatteryStatsManager mBatteryStatsManager;
+ private final PackageManager mPackageManager;
+
+ BatteryConsumerListLoader(Context context) {
+ super(context);
+ mBatteryStatsManager = context.getSystemService(BatteryStatsManager.class);
+ mPackageManager = context.getPackageManager();
+ }
+
+ @Override
+ public List<BatteryConsumerInfoHelper.BatteryConsumerInfo> loadInBackground() {
+ final BatteryUsageStats batteryUsageStats = mBatteryStatsManager.getBatteryUsageStats();
+ List<BatteryConsumerInfoHelper.BatteryConsumerInfo> batteryConsumerList =
+ new ArrayList<>();
+
+ for (int scope = 0;
+ scope < BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT;
+ scope++) {
+ batteryConsumerList.add(
+ BatteryConsumerInfoHelper.makeBatteryConsumerInfo(
+ batteryUsageStats.getAggregateBatteryConsumer(scope),
+ BatteryConsumerData.batteryConsumerId(scope),
+ mPackageManager));
+ }
+
+ for (UidBatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
+ batteryConsumerList.add(
+ BatteryConsumerInfoHelper.makeBatteryConsumerInfo(consumer,
+ BatteryConsumerData.batteryConsumerId(consumer),
+ mPackageManager));
+ }
+
+ batteryConsumerList.sort(
+ Comparator.comparing(
+ (BatteryConsumerInfoHelper.BatteryConsumerInfo a) -> a.powerMah)
+ .reversed());
+
+ return batteryConsumerList;
+ }
+
+ @Override
+ protected void onDiscardResult(List<BatteryConsumerInfoHelper.BatteryConsumerInfo> result) {
+ }
+ }
+
+ private class BatteryConsumerListLoaderCallbacks implements
+ LoaderManager.LoaderCallbacks<List<BatteryConsumerInfoHelper.BatteryConsumerInfo>> {
+
+ @NonNull
+ @Override
+ public Loader<List<BatteryConsumerInfoHelper.BatteryConsumerInfo>> onCreateLoader(int id,
+ Bundle args) {
+ return new BatteryConsumerListLoader(BatteryConsumerPickerActivity.this);
+ }
+
+ @Override
+ public void onLoadFinished(
+ @NonNull Loader<List<BatteryConsumerInfoHelper.BatteryConsumerInfo>> loader,
+ List<BatteryConsumerInfoHelper.BatteryConsumerInfo> batteryConsumerList) {
+ mBatteryConsumerListAdapter.setBatteryConsumerList(batteryConsumerList);
+ mAppList.setVisibility(View.VISIBLE);
+ mLoadingView.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onLoaderReset(
+ @NonNull Loader<List<BatteryConsumerInfoHelper.BatteryConsumerInfo>> loader) {
+ }
+ }
+
+ public class BatteryConsumerListAdapter
+ extends RecyclerView.Adapter<BatteryConsumerViewHolder> {
+ private final OnBatteryConsumerSelectedListener mListener;
+ private List<BatteryConsumerInfoHelper.BatteryConsumerInfo> mBatteryConsumerList;
+
+ public BatteryConsumerListAdapter(OnBatteryConsumerSelectedListener listener) {
+ mListener = listener;
+ }
+
+ void setBatteryConsumerList(
+ List<BatteryConsumerInfoHelper.BatteryConsumerInfo> batteryConsumerList) {
+ mBatteryConsumerList = batteryConsumerList;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getItemCount() {
+ return mBatteryConsumerList.size();
+ }
+
+ @NonNull
+ @Override
+ public BatteryConsumerViewHolder onCreateViewHolder(
+ @NonNull ViewGroup viewGroup,
+ int position) {
+ LayoutInflater layoutInflater = LayoutInflater.from(viewGroup.getContext());
+ View view = layoutInflater.inflate(R.layout.battery_consumer_info_layout, viewGroup,
+ false);
+ return new BatteryConsumerViewHolder(view, mListener);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull BatteryConsumerViewHolder viewHolder, int position) {
+ BatteryConsumerInfoHelper.BatteryConsumerInfo item = mBatteryConsumerList.get(position);
+ viewHolder.id = item.id;
+ viewHolder.titleView.setText(item.label);
+ if (item.details != null) {
+ viewHolder.detailsView.setText(item.details);
+ viewHolder.detailsView.setVisibility(View.VISIBLE);
+ } else {
+ viewHolder.detailsView.setVisibility(View.GONE);
+ }
+ viewHolder.powerView.setText(
+ String.format(Locale.getDefault(), "%.1f mAh", item.powerMah));
+ if (item.iconInfo != null) {
+ viewHolder.iconView.setImageDrawable(
+ item.iconInfo.loadIcon(getPackageManager()));
+ } else {
+ viewHolder.iconView.setImageResource(R.drawable.gm_device_24);
+ }
+ if (item.packages != null) {
+ viewHolder.packagesView.setText(item.packages);
+ viewHolder.packagesView.setVisibility(View.VISIBLE);
+ } else {
+ viewHolder.packagesView.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ // View Holder used when displaying apps
+ public static class BatteryConsumerViewHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener {
+ private final OnBatteryConsumerSelectedListener mListener;
+
+ public String id;
+ public TextView titleView;
+ public TextView detailsView;
+ public ImageView iconView;
+ public TextView packagesView;
+ public TextView powerView;
+
+ BatteryConsumerViewHolder(View view, OnBatteryConsumerSelectedListener listener) {
+ super(view);
+ mListener = listener;
+ view.setOnClickListener(this);
+ titleView = view.findViewById(android.R.id.title);
+ detailsView = view.findViewById(R.id.details);
+ iconView = view.findViewById(android.R.id.icon);
+ packagesView = view.findViewById(R.id.packages);
+ powerView = view.findViewById(R.id.power_mah);
+ powerView.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onClick(View v) {
+ mListener.onBatteryConsumerSelected(id);
+ }
+ }
}
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerFragment.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerFragment.java
deleted file mode 100644
index 4922087..0000000
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerFragment.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright (C) 2008 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.frameworks.core.batterystatsviewer;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.BatteryStatsManager;
-import android.os.BatteryUsageStats;
-import android.os.Bundle;
-import android.os.SystemBatteryConsumer;
-import android.os.UidBatteryConsumer;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-import androidx.loader.app.LoaderManager;
-import androidx.loader.content.Loader;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.frameworks.core.batterystatsviewer.BatteryConsumerInfoHelper.BatteryConsumerInfo;
-import com.android.settingslib.utils.AsyncLoaderCompat;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * Picker, showing a sorted lists of applications or other types of entities consuming power.
- * Returns the selected entity ID or null.
- */
-public class BatteryConsumerPickerFragment extends Fragment {
- private static final String TAG = "AppPicker";
-
- public static final String PICKER_TYPE = "pickertype";
-
- public static final int PICKER_TYPE_APP = 0;
- public static final int PICKER_TYPE_DRAIN = 1;
-
- private BatteryConsumerListAdapter mBatteryConsumerListAdapter;
- private RecyclerView mAppList;
- private View mLoadingView;
-
- private interface OnBatteryConsumerSelectedListener {
- void onBatteryConsumerSelected(String batteryConsumerId);
- }
-
- public BatteryConsumerPickerFragment(int pickerType) {
- Bundle args = new Bundle();
- args.putInt(PICKER_TYPE, pickerType);
- setArguments(args);
- }
-
- public BatteryConsumerPickerFragment() {
- }
-
- @Nullable
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.battery_consumer_picker_layout, container, false);
- mLoadingView = view.findViewById(R.id.loading_view);
-
- mAppList = view.findViewById(R.id.list_view);
- mAppList.setLayoutManager(new LinearLayoutManager(getContext()));
- mBatteryConsumerListAdapter = new BatteryConsumerListAdapter(
- BatteryConsumerPickerFragment.this::setSelectedBatteryConsumer);
- mAppList.setAdapter(mBatteryConsumerListAdapter);
-
- LoaderManager.getInstance(this).initLoader(0, getArguments(),
- new BatteryConsumerListLoaderCallbacks());
- return view;
- }
-
- public void setSelectedBatteryConsumer(String id) {
- ((BatteryConsumerPickerActivity) getActivity()).setSelectedBatteryConsumer(id);
- }
-
- private static class BatteryConsumerListLoader extends
- AsyncLoaderCompat<List<BatteryConsumerInfo>> {
- private final int mPickerType;
- private final BatteryStatsManager mBatteryStatsManager;
- private final PackageManager mPackageManager;
-
- BatteryConsumerListLoader(Context context, int pickerType) {
- super(context);
- mBatteryStatsManager = context.getSystemService(BatteryStatsManager.class);
- mPickerType = pickerType;
- mPackageManager = context.getPackageManager();
- }
-
- @Override
- public List<BatteryConsumerInfo> loadInBackground() {
- final BatteryUsageStats batteryUsageStats = mBatteryStatsManager.getBatteryUsageStats();
-
- List<BatteryConsumerInfo> batteryConsumerList = new ArrayList<>();
- switch (mPickerType) {
- case PICKER_TYPE_APP:
- for (UidBatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
- batteryConsumerList.add(
- BatteryConsumerInfoHelper.makeBatteryConsumerInfo(mPackageManager,
- consumer));
- }
- break;
- case PICKER_TYPE_DRAIN:
- default:
- for (SystemBatteryConsumer consumer :
- batteryUsageStats.getSystemBatteryConsumers()) {
- batteryConsumerList.add(
- BatteryConsumerInfoHelper.makeBatteryConsumerInfo(mPackageManager,
- consumer));
- }
- break;
- }
-
- batteryConsumerList.sort(
- Comparator.comparing((BatteryConsumerInfo a) -> a.powerMah).reversed());
- return batteryConsumerList;
- }
-
- @Override
- protected void onDiscardResult(List<BatteryConsumerInfo> result) {
- }
- }
-
- private class BatteryConsumerListLoaderCallbacks implements
- LoaderManager.LoaderCallbacks<List<BatteryConsumerInfo>> {
-
- @NonNull
- @Override
- public Loader<List<BatteryConsumerInfo>> onCreateLoader(int id, Bundle args) {
- return new BatteryConsumerListLoader(getContext(), args.getInt(PICKER_TYPE));
- }
-
- @Override
- public void onLoadFinished(@NonNull Loader<List<BatteryConsumerInfo>> loader,
- List<BatteryConsumerInfo> batteryConsumerList) {
- mBatteryConsumerListAdapter.setBatteryConsumerList(batteryConsumerList);
- mAppList.setVisibility(View.VISIBLE);
- mLoadingView.setVisibility(View.GONE);
- }
-
- @Override
- public void onLoaderReset(
- @NonNull Loader<List<BatteryConsumerInfo>> loader) {
- }
- }
-
- public class BatteryConsumerListAdapter extends
- RecyclerView.Adapter<BatteryConsumerViewHolder> {
- private final OnBatteryConsumerSelectedListener mListener;
- private List<BatteryConsumerInfo> mBatteryConsumerList;
-
- public BatteryConsumerListAdapter(OnBatteryConsumerSelectedListener listener) {
- mListener = listener;
- }
-
- void setBatteryConsumerList(List<BatteryConsumerInfo> batteryConsumerList) {
- mBatteryConsumerList = batteryConsumerList;
- notifyDataSetChanged();
- }
-
- @Override
- public int getItemCount() {
- return mBatteryConsumerList.size();
- }
-
- @NonNull
- @Override
- public BatteryConsumerViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
- int position) {
- LayoutInflater layoutInflater = LayoutInflater.from(viewGroup.getContext());
- View view = layoutInflater.inflate(R.layout.battery_consumer_info_layout, viewGroup,
- false);
- return new BatteryConsumerViewHolder(view, mListener);
- }
-
- @Override
- public void onBindViewHolder(@NonNull BatteryConsumerViewHolder viewHolder, int position) {
- BatteryConsumerInfo item = mBatteryConsumerList.get(position);
- viewHolder.id = item.id;
- viewHolder.titleView.setText(item.label);
- if (item.details != null) {
- viewHolder.detailsView.setText(item.details);
- viewHolder.detailsView.setVisibility(View.VISIBLE);
- } else {
- viewHolder.detailsView.setVisibility(View.GONE);
- }
- viewHolder.powerView.setText(
- String.format(Locale.getDefault(), "%.1f mAh", item.powerMah));
- viewHolder.iconView.setImageDrawable(
- item.iconInfo.loadIcon(getContext().getPackageManager()));
- if (item.packages != null) {
- viewHolder.packagesView.setText(item.packages);
- viewHolder.packagesView.setVisibility(View.VISIBLE);
- } else {
- viewHolder.packagesView.setVisibility(View.GONE);
- }
- }
- }
-
- // View Holder used when displaying apps
- public static class BatteryConsumerViewHolder extends RecyclerView.ViewHolder
- implements View.OnClickListener {
- private final OnBatteryConsumerSelectedListener mListener;
-
- public String id;
- public TextView titleView;
- public TextView detailsView;
- public ImageView iconView;
- public TextView packagesView;
- public TextView powerView;
-
- BatteryConsumerViewHolder(View view, OnBatteryConsumerSelectedListener listener) {
- super(view);
- mListener = listener;
- view.setOnClickListener(this);
- titleView = view.findViewById(android.R.id.title);
- detailsView = view.findViewById(R.id.details);
- iconView = view.findViewById(android.R.id.icon);
- packagesView = view.findViewById(R.id.packages);
- powerView = view.findViewById(R.id.power_mah);
- powerView.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void onClick(View v) {
- mListener.onBatteryConsumerSelected(id);
- }
- }
-}
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
index 03dde04..bb75be4 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
@@ -51,7 +51,7 @@
private static final int LOADER_BATTERY_USAGE_STATS = 1;
private BatteryStatsDataAdapter mBatteryStatsDataAdapter;
- private final Runnable mBatteryStatsRefresh = this::periodicBatteryStatsRefresh;
+ private final Runnable mBatteryStatsRefresh = this::loadBatteryStats;
private String mBatteryConsumerId;
private TextView mTitleView;
private TextView mDetailsView;
@@ -85,13 +85,15 @@
mLoadingView = findViewById(R.id.loading_view);
mEmptyView = findViewById(R.id.empty_view);
- loadBatteryStats();
+ LoaderManager loaderManager = LoaderManager.getInstance(this);
+ loaderManager.restartLoader(LOADER_BATTERY_USAGE_STATS, null,
+ new BatteryUsageStatsLoaderCallbacks());
}
@Override
protected void onResume() {
super.onResume();
- periodicBatteryStatsRefresh();
+ loadBatteryStats();
}
@Override
@@ -100,15 +102,11 @@
getMainThreadHandler().removeCallbacks(mBatteryStatsRefresh);
}
- private void periodicBatteryStatsRefresh() {
- loadBatteryStats();
- getMainThreadHandler().postDelayed(mBatteryStatsRefresh, BATTERY_STATS_REFRESH_RATE_MILLIS);
- }
-
private void loadBatteryStats() {
LoaderManager loaderManager = LoaderManager.getInstance(this);
loaderManager.restartLoader(LOADER_BATTERY_USAGE_STATS, null,
new BatteryUsageStatsLoaderCallbacks());
+ getMainThreadHandler().postDelayed(mBatteryStatsRefresh, BATTERY_STATS_REFRESH_RATE_MILLIS);
}
private static class BatteryUsageStatsLoader extends
@@ -183,9 +181,12 @@
} else {
mDetailsView.setVisibility(View.GONE);
}
- mIconView.setImageDrawable(
- batteryConsumerInfo.iconInfo.loadIcon(getPackageManager()));
-
+ if (batteryConsumerInfo.iconInfo != null) {
+ mIconView.setImageDrawable(
+ batteryConsumerInfo.iconInfo.loadIcon(getPackageManager()));
+ } else {
+ mIconView.setImageResource(R.drawable.gm_device_24);
+ }
if (batteryConsumerInfo.packages != null) {
mPackagesView.setText(batteryConsumerInfo.packages);
mPackagesView.setVisibility(View.VISIBLE);
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 2e2e6bd..6f17ea9 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -185,15 +185,6 @@
}
@Test
- public void testHandleActivity_assetsChanged() {
- relaunchActivityAndAssertPreserveWindow(activity -> {
- // Relaunches all activities.
- activity.getActivityThread().handleApplicationInfoChanged(
- activity.getApplicationInfo());
- });
- }
-
- @Test
public void testRecreateActivity() {
relaunchActivityAndAssertPreserveWindow(Activity::recreate);
}
diff --git a/core/tests/coretests/src/android/colormodel/CamTest.java b/core/tests/coretests/src/android/colormodel/CamTest.java
new file mode 100644
index 0000000..a70ecd72
--- /dev/null
+++ b/core/tests/coretests/src/android/colormodel/CamTest.java
@@ -0,0 +1,196 @@
+/*
+ * 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 com.android.internal.graphics.cam;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class CamTest {
+ static final int BLACK = 0xff000000;
+ static final int WHITE = 0xffffffff;
+ static final int MIDGRAY = 0xff777777;
+
+ static final int RED = 0xffff0000;
+ static final int GREEN = 0xff00ff00;
+ static final int BLUE = 0xff0000ff;
+
+ @Test
+ public void camFromIntToInt() {
+ Cam cam = Cam.fromInt(RED);
+ int color = cam.viewed(Frame.DEFAULT);
+ assertEquals(color, RED);
+ }
+
+ @Test
+ public void yFromMidgray() {
+ assertEquals(18.418f, CamUtils.yFromLstar(50.0f), 0.001);
+ }
+
+ @Test
+ public void yFromBlack() {
+ assertEquals(0.0f, CamUtils.yFromLstar(0.0f), 0.001);
+ }
+
+ @Test
+ public void yFromWhite() {
+ assertEquals(100.0f, CamUtils.yFromLstar(100.0f), 0.001);
+ }
+
+ @Test
+ public void camFromRed() {
+ Cam cam = Cam.fromInt(RED);
+ assertEquals(46.445f, cam.getJ(), 0.001f);
+ assertEquals(113.357f, cam.getChroma(), 0.001f);
+ assertEquals(27.408f, cam.getHue(), 0.001f);
+ assertEquals(89.494f, cam.getM(), 0.001f);
+ assertEquals(91.889f, cam.getS(), 0.001f);
+ assertEquals(105.988f, cam.getQ(), 0.001f);
+ }
+
+ @Test
+ public void camFromGreen() {
+ Cam cam = Cam.fromInt(GREEN);
+ assertEquals(79.331f, cam.getJ(), 0.001f);
+ assertEquals(108.409f, cam.getChroma(), 0.001f);
+ assertEquals(142.139f, cam.getHue(), 0.001f);
+ assertEquals(85.587f, cam.getM(), 0.001f);
+ assertEquals(78.604f, cam.getS(), 0.001f);
+ assertEquals(138.520, cam.getQ(), 0.001f);
+ }
+
+ @Test
+ public void camFromBlue() {
+ Cam cam = Cam.fromInt(BLUE);
+ assertEquals(25.465f, cam.getJ(), 0.001f);
+ assertEquals(87.230f, cam.getChroma(), 0.001f);
+ assertEquals(282.788f, cam.getHue(), 0.001f);
+ assertEquals(68.867f, cam.getM(), 0.001f);
+ assertEquals(93.674f, cam.getS(), 0.001f);
+ assertEquals(78.481f, cam.getQ(), 0.001f);
+ }
+
+ @Test
+ public void camFromBlack() {
+ Cam cam = Cam.fromInt(BLACK);
+ assertEquals(0.0f, cam.getJ(), 0.001f);
+ assertEquals(0.0f, cam.getChroma(), 0.001f);
+ assertEquals(0.0f, cam.getHue(), 0.001f);
+ assertEquals(0.0f, cam.getM(), 0.001f);
+ assertEquals(0.0f, cam.getS(), 0.001f);
+ assertEquals(0.0f, cam.getQ(), 0.001f);
+ }
+
+ @Test
+ public void camFromWhite() {
+ Cam cam = Cam.fromInt(WHITE);
+ assertEquals(100.0f, cam.getJ(), 0.001f);
+ assertEquals(2.869f, cam.getChroma(), 0.001f);
+ assertEquals(209.492f, cam.getHue(), 0.001f);
+ assertEquals(2.265f, cam.getM(), 0.001f);
+ assertEquals(12.068f, cam.getS(), 0.001f);
+ assertEquals(155.521, cam.getQ(), 0.001f);
+ }
+
+ @Test
+ public void getRedFromGamutMap() {
+ int colorToTest = RED;
+ Cam cam = Cam.fromInt(colorToTest);
+ int color = Cam.getInt(cam.getHue(), cam.getChroma(), CamUtils.lstarFromInt(colorToTest));
+ assertEquals(colorToTest, color);
+ }
+
+ @Test
+ public void getGreenFromGamutMap() {
+ int colorToTest = GREEN;
+ Cam cam = Cam.fromInt(colorToTest);
+ int color = Cam.getInt(cam.getHue(), cam.getChroma(), CamUtils.lstarFromInt(colorToTest));
+ assertEquals(colorToTest, color);
+ }
+
+ @Test
+ public void getBlueFromGamutMap() {
+ int colorToTest = BLUE;
+ Cam cam = Cam.fromInt(colorToTest);
+ int color = Cam.getInt(cam.getHue(), cam.getChroma(), CamUtils.lstarFromInt(colorToTest));
+ assertEquals(colorToTest, color);
+ }
+
+ @Test
+ public void getWhiteFromGamutMap() {
+ int colorToTest = WHITE;
+ Cam cam = Cam.fromInt(colorToTest);
+ int color = Cam.getInt(cam.getHue(), cam.getChroma(), CamUtils.lstarFromInt(colorToTest));
+ assertEquals(colorToTest, color);
+ }
+
+ @Test
+ public void getBlackFromGamutMap() {
+ int colorToTest = BLACK;
+ Cam cam = Cam.fromInt(colorToTest);
+ int color = Cam.getInt(cam.getHue(), cam.getChroma(), CamUtils.lstarFromInt(colorToTest));
+ assertEquals(colorToTest, color);
+ }
+
+ @Test
+ public void getMidgrayFromGamutMap() {
+ int colorToTest = MIDGRAY;
+ Cam cam = Cam.fromInt(colorToTest);
+ int color = Cam.getInt(cam.getHue(), cam.getChroma(), CamUtils.lstarFromInt(colorToTest));
+ assertEquals(colorToTest, color);
+ }
+
+ @Test
+ public void getRandomGreenFromGamutMap() {
+ int colorToTest = 0xff009200;
+ Cam cam = Cam.fromInt(colorToTest);
+ int color = Cam.getInt(cam.getHue(), cam.getChroma(), CamUtils.lstarFromInt(colorToTest));
+ assertEquals(colorToTest, color);
+ }
+
+ @Test
+ public void gamutMapArbitraryHCL() {
+ int color = Cam.getInt(309.0f, 40.0f, 70.0f);
+ Cam cam = Cam.fromInt(color);
+
+ assertEquals(308.759f, cam.getHue(), 0.001);
+ assertEquals(40.148f, cam.getChroma(), 0.001);
+ assertEquals(70.029f, CamUtils.lstarFromInt(color), 0.001f);
+ }
+
+ @Test
+ public void ucsCoordinates() {
+ Cam cam = Cam.fromInt(RED);
+
+ assertEquals(59.584f, cam.getJstar(), 0.001f);
+ assertEquals(43.297f, cam.getAstar(), 0.001f);
+ assertEquals(22.451f, cam.getBstar(), 0.001f);
+ }
+
+ @Test
+ public void deltaEWhiteToBlack() {
+ assertEquals(25.661f, Cam.fromInt(WHITE).distance(Cam.fromInt(BLACK)), 0.001f);
+ }
+
+ @Test
+ public void deltaERedToBlue() {
+ assertEquals(21.415f, Cam.fromInt(RED).distance(Cam.fromInt(BLUE)), 0.001f);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java
index 236c3da..4a5528d 100644
--- a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java
@@ -19,7 +19,6 @@
import static com.google.common.truth.Truth.assertThat;
import android.os.BatteryConsumer;
-import android.os.SystemBatteryConsumer;
import android.view.Display;
import androidx.test.filters.SmallTest;
@@ -62,15 +61,13 @@
mStatsRule.apply(calculator);
- SystemBatteryConsumer consumer =
- mStatsRule.getSystemBatteryConsumer(
- SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY);
- assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ BatteryConsumer consumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
.isEqualTo(90 * MINUTE_IN_MS);
// 100,000,00 uC / 1000 (micro-/milli-) / 360 (seconds/hour) = 27.777778 mAh
- assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
.isWithin(PRECISION).of(27.777778);
- assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
.isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
}
@@ -88,14 +85,12 @@
mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
- SystemBatteryConsumer consumer =
- mStatsRule.getSystemBatteryConsumer(
- SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY);
- assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ BatteryConsumer consumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
.isEqualTo(90 * MINUTE_IN_MS);
- assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
.isWithin(PRECISION).of(15.0);
- assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
.isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/AudioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/AudioPowerCalculatorTest.java
index c694d67..81940ed 100644
--- a/core/tests/coretests/src/com/android/internal/os/AudioPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/AudioPowerCalculatorTest.java
@@ -56,5 +56,19 @@
.isEqualTo(1000);
assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AUDIO))
.isWithin(PRECISION).of(0.1);
+
+ final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(deviceBatteryConsumer
+ .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_AUDIO))
+ .isEqualTo(1000);
+ assertThat(deviceBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AUDIO))
+ .isWithin(PRECISION).of(0.1);
+
+ final BatteryConsumer appsBatteryConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(appsBatteryConsumer
+ .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_AUDIO))
+ .isEqualTo(1000);
+ assertThat(appsBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AUDIO))
+ .isWithin(PRECISION).of(0.1);
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryChargeCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryChargeCalculatorTest.java
index cf126c6..23fc35d 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryChargeCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryChargeCalculatorTest.java
@@ -36,18 +36,12 @@
@Rule
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
- .setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 4000.0);
+ .setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 1234.0); // Should be ignored
@Test
public void testDischargeTotals() {
- BatteryChargeCalculator calculator =
- new BatteryChargeCalculator(mStatsRule.getPowerProfile());
-
final BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
- mStatsRule.setTime(1000, 1000);
- batteryStats.resetAllStatsCmdLocked();
- batteryStats.setNoAutoReset(true);
batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
/* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0,
1_000_000, 1_000_000, 1_000_000);
@@ -58,8 +52,11 @@
/* plugType */ 0, 80, 72, 3700, 2_400_000, 4_000_000, 0,
2_000_000, 2_000_000, 2_000_000);
+ BatteryChargeCalculator calculator = new BatteryChargeCalculator();
BatteryUsageStats batteryUsageStats = mStatsRule.apply(calculator);
+ assertThat(batteryUsageStats.getConsumedPower())
+ .isWithin(PRECISION).of(380.0);
assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(10);
assertThat(batteryUsageStats.getDischargedPowerRange().getLower())
.isWithin(PRECISION).of(360.0);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
index 41fe372..1a6408f 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
@@ -23,10 +23,10 @@
import android.content.Context;
import android.net.NetworkStats;
+import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
-import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
import android.os.UserBatteryConsumer;
import android.util.SparseArray;
@@ -186,6 +186,16 @@
return mBatteryUsageStats;
}
+ public BatteryConsumer getDeviceBatteryConsumer() {
+ return mBatteryUsageStats.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
+ }
+
+ public BatteryConsumer getAppsBatteryConsumer() {
+ return mBatteryUsageStats.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
+ }
+
public UidBatteryConsumer getUidBatteryConsumer(int uid) {
for (UidBatteryConsumer ubc : mBatteryUsageStats.getUidBatteryConsumers()) {
if (ubc.getUid() == uid) {
@@ -195,16 +205,6 @@
return null;
}
- public SystemBatteryConsumer getSystemBatteryConsumer(
- @SystemBatteryConsumer.DrainType int drainType) {
- for (SystemBatteryConsumer sbc : mBatteryUsageStats.getSystemBatteryConsumers()) {
- if (sbc.getDrainType() == drainType) {
- return sbc;
- }
- }
- return null;
- }
-
public UserBatteryConsumer getUserBatteryConsumer(int userId) {
for (UserBatteryConsumer ubc : mBatteryUsageStats.getUserBatteryConsumers()) {
if (ubc.getUserId() == userId) {
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index 55302bc..127cea8 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -23,7 +23,6 @@
import android.os.BatteryConsumer;
import android.os.BatteryUsageStats;
import android.os.Parcel;
-import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
import androidx.test.filters.SmallTest;
@@ -71,7 +70,6 @@
.setDischargePercentage(20)
.setDischargedPowerRange(1000, 2000)
.setStatsStartTimestamp(1000);
-
builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid)
.setPackageWithHighestDrain("foo")
.setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, 1000)
@@ -87,7 +85,8 @@
.setUsageDurationForCustomComponentMillis(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 800);
- builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_CAMERA)
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
.setConsumedPower(
BatteryConsumer.POWER_COMPONENT_CPU, 10100)
.setConsumedPowerForCustomComponent(
@@ -95,17 +94,25 @@
.setUsageDurationMillis(
BatteryConsumer.POWER_COMPONENT_CPU, 10300)
.setUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10400)
- .setPowerConsumedByApps(20000);
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10400);
+
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .setConsumedPower(30000)
+ .setConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU, 20100)
+ .setConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20200)
+ .setUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU, 20300)
+ .setUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20400);
return builder.build();
}
public void validateBatteryUsageStats(BatteryUsageStats batteryUsageStats) {
- // Camera: (10100 + 10200) - 20000 (consumed by apps) = 300
- // App: 300 + 400 + 500 = 1200
- // Total: 1500
- assertThat(batteryUsageStats.getConsumedPower()).isEqualTo(1500);
+ assertThat(batteryUsageStats.getConsumedPower()).isEqualTo(30000);
assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(20);
assertThat(batteryUsageStats.getDischargedPowerRange().getLower()).isEqualTo(1000);
assertThat(batteryUsageStats.getDischargedPowerRange().getUpper()).isEqualTo(2000);
@@ -139,28 +146,32 @@
}
}
- final List<SystemBatteryConsumer> systemBatteryConsumers =
- batteryUsageStats.getSystemBatteryConsumers();
- for (SystemBatteryConsumer systemBatteryConsumer : systemBatteryConsumers) {
- if (systemBatteryConsumer.getDrainType() == SystemBatteryConsumer.DRAIN_TYPE_CAMERA) {
- assertThat(systemBatteryConsumer.getConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10100);
- assertThat(systemBatteryConsumer.getConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(10200);
- assertThat(systemBatteryConsumer.getUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10300);
- assertThat(systemBatteryConsumer.getUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(10400);
- assertThat(systemBatteryConsumer.getConsumedPower()).isEqualTo(20300);
- assertThat(systemBatteryConsumer.getPowerConsumedByApps()).isEqualTo(20000);
- assertThat(systemBatteryConsumer.getUsageDurationMillis())
- .isEqualTo(10400); // max
- assertThat(systemBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
- assertThat(systemBatteryConsumer.getCustomPowerComponentName(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
- } else {
- fail("Unexpected drain type " + systemBatteryConsumer.getDrainType());
- }
- }
+ final BatteryConsumer appsBatteryConsumer = batteryUsageStats.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
+ assertThat(appsBatteryConsumer.getConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10100);
+ assertThat(appsBatteryConsumer.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(10200);
+ assertThat(appsBatteryConsumer.getUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10300);
+ assertThat(appsBatteryConsumer.getUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(10400);
+ assertThat(appsBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
+ assertThat(appsBatteryConsumer.getCustomPowerComponentName(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
+
+ final BatteryConsumer deviceBatteryConsumer = batteryUsageStats.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
+ assertThat(deviceBatteryConsumer.getConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(20100);
+ assertThat(deviceBatteryConsumer.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(20200);
+ assertThat(deviceBatteryConsumer.getUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(20300);
+ assertThat(deviceBatteryConsumer.getUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(20400);
+ assertThat(deviceBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
+ assertThat(deviceBatteryConsumer.getCustomPowerComponentName(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
index 8723195..2de621c 100644
--- a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
@@ -24,7 +24,6 @@
import android.os.BatteryConsumer;
import android.os.BatteryUsageStatsQuery;
import android.os.Process;
-import android.os.SystemBatteryConsumer;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -65,13 +64,18 @@
mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
- assertThat(mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID)).isNull();
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
+ 0.11388, 6000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
assertBluetoothPowerAndDuration(
mStatsRule.getUidBatteryConsumer(APP_UID),
0.24722, 15000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
assertBluetoothPowerAndDuration(
- mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_BLUETOOTH),
- 0.51944, 9000, 0.51944, 0.36111, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ mStatsRule.getDeviceBatteryConsumer(),
+ 0.40555, 24000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getAppsBatteryConsumer(),
+ 0.36111, 21000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
@Test
@@ -93,13 +97,18 @@
mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
- assertThat(mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID)).isNull();
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
+ 0.1, 6000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
assertBluetoothPowerAndDuration(
mStatsRule.getUidBatteryConsumer(APP_UID),
0.2, 15000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
assertBluetoothPowerAndDuration(
- mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_BLUETOOTH),
- 0.45, 9000, 0.45, 0.3, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ mStatsRule.getDeviceBatteryConsumer(),
+ 0.35, 24000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getAppsBatteryConsumer(),
+ 0.3, 21000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
@Test
@@ -118,13 +127,18 @@
mStatsRule.apply(new BatteryUsageStatsQuery.Builder().includePowerModels().build(),
calculator);
- assertThat(mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID)).isNull();
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
+ 0.10378, 3583, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
assertBluetoothPowerAndDuration(
mStatsRule.getUidBatteryConsumer(APP_UID),
0.22950, 8416, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
assertBluetoothPowerAndDuration(
- mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_BLUETOOTH),
- 0.43712, 3584, 0.43712, 0.33329, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+ mStatsRule.getDeviceBatteryConsumer(),
+ 0.33333, 12000, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+ assertBluetoothPowerAndDuration(
+ mStatsRule.getAppsBatteryConsumer(),
+ 0.33329, 11999, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
}
private void setDurationsAndPower(
@@ -151,16 +165,4 @@
assertThat(usageDurationMillis).isEqualTo(durationMs);
}
-
- private void assertBluetoothPowerAndDuration(@Nullable SystemBatteryConsumer batteryConsumer,
- double powerMah, int durationMs, double consumedPower, double attributedPower,
- @BatteryConsumer.PowerModel int powerModel) {
- assertBluetoothPowerAndDuration(batteryConsumer, powerMah, durationMs, powerModel);
-
- assertThat(batteryConsumer.getConsumedPower())
- .isWithin(PRECISION).of(consumedPower);
-
- assertThat(batteryConsumer.getPowerConsumedByApps())
- .isWithin(PRECISION).of(attributedPower);
- }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/CameraPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CameraPowerCalculatorTest.java
index 61eb173..c40d8a0 100644
--- a/core/tests/coretests/src/com/android/internal/os/CameraPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/CameraPowerCalculatorTest.java
@@ -42,9 +42,9 @@
@Test
public void testTimerBasedModel() {
- BatteryStatsImpl.Uid uidStats = mStatsRule.getUidStats(APP_UID);
- uidStats.noteCameraTurnedOnLocked(1000);
- uidStats.noteCameraTurnedOffLocked(2000);
+ BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ stats.noteCameraOnLocked(APP_UID, 1000, 1000);
+ stats.noteCameraOffLocked(APP_UID, 2000, 2000);
CameraPowerCalculator calculator =
new CameraPowerCalculator(mStatsRule.getPowerProfile());
@@ -56,5 +56,19 @@
.isEqualTo(1000);
assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA))
.isWithin(PRECISION).of(0.1);
+
+ final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(deviceBatteryConsumer
+ .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA))
+ .isEqualTo(1000);
+ assertThat(deviceBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA))
+ .isWithin(PRECISION).of(0.1);
+
+ final BatteryConsumer appsBatteryConsumer = mStatsRule.getAppsBatteryConsumer();
+ assertThat(appsBatteryConsumer
+ .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA))
+ .isEqualTo(1000);
+ assertThat(appsBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA))
+ .isWithin(PRECISION).of(0.1);
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
index 1a99fb0f..152d246 100644
--- a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
@@ -160,6 +160,18 @@
assertThat(uidConsumer2.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU))
.isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull();
+
+ final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(deviceBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
+ .isWithin(PRECISION).of(3.76455);
+ assertThat(deviceBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU))
+ .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+
+ final BatteryConsumer appsBatteryConsumer = mStatsRule.getAppsBatteryConsumer();
+ assertThat(appsBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
+ .isWithin(PRECISION).of(3.76455);
+ assertThat(appsBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU))
+ .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
@Test
@@ -224,5 +236,17 @@
assertThat(uidConsumer2.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU))
.isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull();
+
+ final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(deviceBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
+ .isWithin(PRECISION).of(10.62949);
+ assertThat(deviceBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU))
+ .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+
+ final BatteryConsumer appsBatteryConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(appsBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
+ .isWithin(PRECISION).of(10.62949);
+ assertThat(appsBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU))
+ .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java
index f011117..f8c2bc6 100644
--- a/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java
@@ -20,7 +20,6 @@
import android.os.BatteryConsumer;
import android.os.Process;
-import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
import android.util.SparseLongArray;
@@ -68,12 +67,19 @@
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1))
.isWithin(PRECISION).of(33.33333);
- SystemBatteryConsumer systemConsumer = mStatsRule.getSystemBatteryConsumer(
- SystemBatteryConsumer.DRAIN_TYPE_CUSTOM);
- assertThat(systemConsumer.getConsumedPowerForCustomComponent(
+ final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(deviceBatteryConsumer.getConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID))
.isWithin(PRECISION).of(27.77777);
- assertThat(systemConsumer.getConsumedPowerForCustomComponent(
+ assertThat(deviceBatteryConsumer.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1))
+ .isWithin(PRECISION).of(55.55555);
+
+ final BatteryConsumer appsBatteryConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(appsBatteryConsumer.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID))
+ .isWithin(PRECISION).of(27.77777);
+ assertThat(appsBatteryConsumer.getConsumedPowerForCustomComponent(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1))
.isWithin(PRECISION).of(55.55555);
}
diff --git a/core/tests/coretests/src/com/android/internal/os/GnssPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/GnssPowerCalculatorTest.java
index 7ea799f..1964430 100644
--- a/core/tests/coretests/src/com/android/internal/os/GnssPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/GnssPowerCalculatorTest.java
@@ -62,6 +62,18 @@
.isWithin(PRECISION).of(0.1);
assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS))
.isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+
+ BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
+ .isWithin(PRECISION).of(0.1);
+ assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS))
+ .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+
+ BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
+ assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
+ .isWithin(PRECISION).of(0.1);
+ assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS))
+ .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
@Test
@@ -97,5 +109,17 @@
.isWithin(PRECISION).of(5.55555);
assertThat(consumer2.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS))
.isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+
+ BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
+ .isWithin(PRECISION).of(8.333333);
+ assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS))
+ .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+
+ BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
+ assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
+ .isWithin(PRECISION).of(8.333333);
+ assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS))
+ .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/IdlePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/IdlePowerCalculatorTest.java
index 2331eeb..67b1e51 100644
--- a/core/tests/coretests/src/com/android/internal/os/IdlePowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/IdlePowerCalculatorTest.java
@@ -19,7 +19,6 @@
import static com.google.common.truth.Truth.assertThat;
import android.os.BatteryConsumer;
-import android.os.SystemBatteryConsumer;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -46,11 +45,16 @@
mStatsRule.apply(calculator);
- SystemBatteryConsumer consumer =
- mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_IDLE);
- assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_IDLE))
+ BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_IDLE))
.isEqualTo(3000);
- assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_IDLE))
+ assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_IDLE))
.isWithin(PRECISION).of(0.7);
+
+ BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
+ assertThat(appsConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_IDLE))
+ .isEqualTo(0);
+ assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_IDLE))
+ .isWithin(PRECISION).of(0);
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/MemoryPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MemoryPowerCalculatorTest.java
index 94e760a..4868d6a 100644
--- a/core/tests/coretests/src/com/android/internal/os/MemoryPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/MemoryPowerCalculatorTest.java
@@ -19,7 +19,6 @@
import static com.google.common.truth.Truth.assertThat;
import android.os.BatteryConsumer;
-import android.os.SystemBatteryConsumer;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -53,8 +52,7 @@
mStatsRule.apply(calculator);
- SystemBatteryConsumer consumer =
- mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_MEMORY);
+ BatteryConsumer consumer = mStatsRule.getDeviceBatteryConsumer();
assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_MEMORY))
.isEqualTo(3000);
assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MEMORY))
diff --git a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
index 5b84a1b..48a1da1 100644
--- a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
@@ -27,7 +27,6 @@
import android.net.NetworkStats;
import android.os.BatteryConsumer;
import android.os.Process;
-import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ModemActivityInfo;
@@ -100,22 +99,23 @@
mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
- SystemBatteryConsumer consumer =
- mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO);
- assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isWithin(PRECISION).of(2.2444);
- assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- assertThat(consumer.getConsumedPower())
- .isWithin(PRECISION).of(2.2444);
- assertThat(consumer.getPowerConsumedByApps())
- .isWithin(PRECISION).of(0.8);
-
UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(0.8);
assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+
+ BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
+ .isWithin(PRECISION).of(2.2444);
+ assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
+ .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+
+ BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
+ assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
+ .isWithin(PRECISION).of(0.8);
+ assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
+ .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
@Test
@@ -159,21 +159,22 @@
mStatsRule.apply(calculator);
- SystemBatteryConsumer consumer =
- mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO);
-
- // 100000000 uAs * (1 mA / 1000 uA) * (1 h / 3600 s) + 1.53934 (apps)= 4.31711 mAh
- assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isWithin(PRECISION).of(4.31711);
- assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
- .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
- assertThat(consumer.getPowerConsumedByApps())
- .isWithin(PRECISION).of(1.53934);
-
UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isWithin(PRECISION).of(1.53934);
assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+
+ BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
+ .isWithin(PRECISION).of(4.31711);
+ assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
+ .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+
+ BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
+ assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
+ .isWithin(PRECISION).of(1.53934);
+ assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
+ .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
index 93c7106..4c29c20 100644
--- a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
@@ -21,7 +21,6 @@
import android.app.ActivityManager;
import android.os.BatteryConsumer;
import android.os.Process;
-import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
import android.view.Display;
@@ -82,21 +81,6 @@
mStatsRule.apply(calculator);
- SystemBatteryConsumer consumer =
- mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_SCREEN);
- assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isEqualTo(80 * MINUTE_IN_MS);
-
- // 600000000 uAs * (1 mA / 1000 uA) * (1 h / 3600 s) = 166.66666 mAh
- assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isWithin(PRECISION).of(166.66666);
- assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
- assertThat(consumer.getConsumedPower())
- .isWithin(PRECISION).of(166.66666);
- assertThat(consumer.getPowerConsumedByApps())
- .isWithin(PRECISION).of(166.66666);
-
UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
assertThat(uid1.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isEqualTo(20 * MINUTE_IN_MS);
@@ -120,6 +104,25 @@
.isWithin(PRECISION).of(101.85185);
assertThat(uid2.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+
+ BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ .isEqualTo(80 * MINUTE_IN_MS);
+
+ // 600000000 uAs * (1 mA / 1000 uA) * (1 h / 3600 s) = 166.66666 mAh
+ assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ .isWithin(PRECISION).of(166.66666);
+ assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+
+ BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
+ assertThat(appsConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ .isEqualTo(80 * MINUTE_IN_MS);
+
+ assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ .isWithin(PRECISION).of(166.66666);
+ assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
}
@Test
@@ -151,19 +154,6 @@
mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
- SystemBatteryConsumer consumer =
- mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_SCREEN);
- assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isEqualTo(80 * MINUTE_IN_MS);
- assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isWithin(PRECISION).of(92.0);
- assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- assertThat(consumer.getConsumedPower())
- .isWithin(PRECISION).of(92.0);
- assertThat(consumer.getPowerConsumedByApps())
- .isWithin(PRECISION).of(92.0);
-
UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
assertThat(uid1.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isEqualTo(20 * MINUTE_IN_MS);
@@ -185,6 +175,22 @@
.isWithin(PRECISION).of(69.0);
assertThat(uid2.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
.isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+
+ BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ .isEqualTo(80 * MINUTE_IN_MS);
+ assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ .isWithin(PRECISION).of(92);
+ assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+
+ BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
+ assertThat(appsConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ .isEqualTo(80 * MINUTE_IN_MS);
+ assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ .isWithin(PRECISION).of(92);
+ assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
+ .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
private void setProcState(int uid, int procState, boolean resumed, long realtimeMs,
diff --git a/core/tests/coretests/src/com/android/internal/os/SensorPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SensorPowerCalculatorTest.java
index 74235b2..7563e39 100644
--- a/core/tests/coretests/src/com/android/internal/os/SensorPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/SensorPowerCalculatorTest.java
@@ -59,11 +59,11 @@
when(sensorManager.getSensorList(Sensor.TYPE_ALL))
.thenReturn(List.of(sensor1, sensor2));
- BatteryStatsImpl.Uid uidStats = mStatsRule.getUidStats(APP_UID);
- uidStats.noteStartSensor(SENSOR_HANDLE_1, 1000);
- uidStats.noteStopSensor(SENSOR_HANDLE_1, 2000);
- uidStats.noteStartSensor(SENSOR_HANDLE_2, 3000);
- uidStats.noteStopSensor(SENSOR_HANDLE_2, 5000);
+ final BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ stats.noteStartSensorLocked(APP_UID, SENSOR_HANDLE_1, 1000, 1000);
+ stats.noteStopSensorLocked(APP_UID, SENSOR_HANDLE_1, 2000, 2000);
+ stats.noteStartSensorLocked(APP_UID, SENSOR_HANDLE_2, 3000, 3000);
+ stats.noteStopSensorLocked(APP_UID, SENSOR_HANDLE_2, 5000, 5000);
SensorPowerCalculator calculator = new SensorPowerCalculator(sensorManager);
@@ -74,6 +74,14 @@
.isEqualTo(3000);
assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SENSORS))
.isWithin(PRECISION).of(0.5);
+
+ BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SENSORS))
+ .isWithin(PRECISION).of(0.5);
+
+ BatteryConsumer appsConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SENSORS))
+ .isWithin(PRECISION).of(0.5);
}
private Sensor createSensor(int handle, int type, double power) {
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
index 58e2513..cd45060 100644
--- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
@@ -133,6 +133,12 @@
assertThat(mStatsRule.getUidBatteryConsumer(Process.SYSTEM_UID)
.getConsumedPower(BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS))
.isWithin(PRECISION).of(-18.888888);
+ assertThat(mStatsRule.getDeviceBatteryConsumer()
+ .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
+ .isWithin(PRECISION).of(18.888888);
+ assertThat(mStatsRule.getAppsBatteryConsumer()
+ .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
+ .isWithin(PRECISION).of(18.888888);
}
private static class MockKernelCpuUidFreqTimeReader extends
diff --git a/core/tests/coretests/src/com/android/internal/os/VideoPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/VideoPowerCalculatorTest.java
index fa0dbc7..ae61d31 100644
--- a/core/tests/coretests/src/com/android/internal/os/VideoPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/VideoPowerCalculatorTest.java
@@ -56,5 +56,19 @@
.isEqualTo(1000);
assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_VIDEO))
.isWithin(PRECISION).of(0.1);
+
+ final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(deviceBatteryConsumer
+ .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_VIDEO))
+ .isEqualTo(1000);
+ assertThat(deviceBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_VIDEO))
+ .isWithin(PRECISION).of(0.1);
+
+ final BatteryConsumer appsBatteryConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(appsBatteryConsumer
+ .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_VIDEO))
+ .isEqualTo(1000);
+ assertThat(appsBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_VIDEO))
+ .isWithin(PRECISION).of(0.1);
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java
index 9d3ed55..82830f2 100644
--- a/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java
@@ -72,5 +72,15 @@
.isEqualTo(5000);
assertThat(osConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK))
.isWithin(PRECISION).of(0.5);
+
+ BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK))
+ .isEqualTo(6000);
+ assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK))
+ .isWithin(PRECISION).of(0.6);
+
+ BatteryConsumer appConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(appConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK))
+ .isWithin(PRECISION).of(0.6);
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
index 4a7cf1e..fc44ddc 100644
--- a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
@@ -25,7 +25,6 @@
import android.net.NetworkStats;
import android.os.BatteryConsumer;
import android.os.Process;
-import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
import android.os.WorkSource;
import android.os.connectivity.WifiActivityEnergyInfo;
@@ -94,16 +93,19 @@
assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
.isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- SystemBatteryConsumer systemConsumer =
- mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI);
- assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isEqualTo(5577);
- assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isWithin(PRECISION).of(1.11153);
- assertThat(systemConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
+ BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI))
+ .isEqualTo(4002);
+ assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+ .isWithin(PRECISION).of(0.86666);
+ assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
.isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- assertThat(systemConsumer.getPowerConsumedByApps())
- .isWithin(PRECISION).of(0.466333);
+
+ BatteryConsumer appsConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+ .isWithin(PRECISION).of(0.866666);
+ assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
+ .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
}
@Test
@@ -125,17 +127,19 @@
assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
.isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
- SystemBatteryConsumer systemConsumer =
- mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI);
- assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isEqualTo(5577);
- /* Same ratio as in testPowerControllerBasedModel_nonMeasured but scaled by 1_000_000uC. */
- assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isWithin(PRECISION).of(1.11153 / (0.2214666 + 0.645200) * 1_000_000 / 3600000);
- assertThat(systemConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
+ BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI))
+ .isEqualTo(4002);
+ assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+ .isWithin(PRECISION).of(0.27777);
+ assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
.isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
- assertThat(systemConsumer.getPowerConsumedByApps())
- .isWithin(PRECISION).of(0.14946);
+
+ BatteryConsumer appsConsumer = mStatsRule.getDeviceBatteryConsumer();
+ assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
+ .isWithin(PRECISION).of(0.277777);
+ assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
+ .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
}
/** Sets up batterystats object with prepopulated network & timer data for Timer-model tests. */
@@ -168,17 +172,6 @@
.isWithin(PRECISION).of(0.8231573);
assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
.isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
-
- SystemBatteryConsumer systemConsumer =
- mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI);
- assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isEqualTo(2222);
- assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isWithin(PRECISION).of(2.575000);
- assertThat(systemConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
- assertThat(systemConsumer.getPowerConsumedByApps())
- .isWithin(PRECISION).of(1.69907);
}
@Test
@@ -200,17 +193,5 @@
.isWithin(PRECISION).of(0.8231573 / (0.8231573 + 0.8759216) * 1_000_000 / 3600000);
assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
.isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
-
- SystemBatteryConsumer systemConsumer =
- mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI);
- assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isEqualTo(2222);
- /* Same ratio as in testTimerBasedModel_nonMeasured but scaled by 1_000_000uC. */
- assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isWithin(PRECISION).of(2.575000 / (0.8231573 + 0.8759216) * 1_000_000 / 3600000);
- assertThat(systemConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
- .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
- assertThat(systemConsumer.getPowerConsumedByApps())
- .isWithin(PRECISION).of(0.277777);
}
}
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 895cd3e..24d7780 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -864,7 +864,6 @@
boolean shouldExit = mExitingAnimation;
mRippleActive = false;
mExitingAnimation = false;
- getRipplePaint();
drawContent(canvas);
drawPatternedBackground(canvas, cx, cy);
if (shouldAnimate && mRunningAnimations.size() <= MAX_RIPPLES) {
@@ -928,7 +927,7 @@
startBackgroundAnimation();
}
if (mBackgroundOpacity == 0) return;
- Paint p = mRipplePaint;
+ Paint p = getRipplePaint();
float newOpacity = mBackgroundOpacity;
final int origAlpha = p.getAlpha();
final int alpha = Math.min((int) (origAlpha * newOpacity + 0.5f), 255);
@@ -957,7 +956,7 @@
@NonNull
private RippleAnimationSession.AnimationProperties<Float, Paint> createAnimationProperties(
float x, float y, float cx, float cy, float w, float h) {
- Paint p = new Paint(mRipplePaint);
+ Paint p = new Paint(getRipplePaint());
float radius = getComputedRadius();
RippleAnimationSession.AnimationProperties<Float, Paint> properties;
RippleShader shader = new RippleShader();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationCallback.java
index 6749f7e..24e5111 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationCallback.java
@@ -44,8 +44,9 @@
}
/**
- * Called when OneHanded animator is updating offset
+ * Called when OneHanded animator is updating position
*/
- default void onTutorialAnimationUpdate(int offset) {}
+ default void onAnimationUpdate(float xPos, float yPos) {
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
index 25dd3ca..180cceb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
@@ -25,8 +25,10 @@
import android.view.animation.BaseInterpolator;
import android.window.WindowContainerToken;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -37,6 +39,7 @@
* Controller class of OneHanded animations (both from and to OneHanded mode).
*/
public class OneHandedAnimationController {
+ private static final String TAG = "OneHandedAnimationController";
private static final float FRACTION_START = 0f;
private static final float FRACTION_END = 1f;
@@ -68,17 +71,19 @@
@SuppressWarnings("unchecked")
OneHandedTransitionAnimator getAnimator(WindowContainerToken token, SurfaceControl leash,
- Rect startBounds, Rect endBounds) {
+ float startPos, float endPos, Rect displayBounds) {
final OneHandedTransitionAnimator animator = mAnimatorMap.get(token);
if (animator == null) {
mAnimatorMap.put(token, setupOneHandedTransitionAnimator(
- OneHandedTransitionAnimator.ofBounds(token, leash, startBounds, endBounds)));
+ OneHandedTransitionAnimator.ofYOffset(token, leash, startPos, endPos,
+ displayBounds)));
} else if (animator.isRunning()) {
- animator.updateEndValue(endBounds);
+ animator.updateEndValue(endPos);
} else {
animator.cancel();
mAnimatorMap.put(token, setupOneHandedTransitionAnimator(
- OneHandedTransitionAnimator.ofBounds(token, leash, startBounds, endBounds)));
+ OneHandedTransitionAnimator.ofYOffset(token, leash, startPos, endPos,
+ displayBounds)));
}
return mAnimatorMap.get(token);
}
@@ -147,9 +152,7 @@
public void onAnimationStart(Animator animation) {
mCurrentValue = mStartValue;
mOneHandedAnimationCallbacks.forEach(
- (callback) -> {
- callback.onOneHandedAnimationStart(this);
- }
+ (callback) -> callback.onOneHandedAnimationStart(this)
);
}
@@ -159,9 +162,7 @@
final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
onEndTransaction(mLeash, tx);
mOneHandedAnimationCallbacks.forEach(
- (callback) -> {
- callback.onOneHandedAnimationEnd(tx, this);
- }
+ (callback) -> callback.onOneHandedAnimationEnd(tx, this)
);
}
@@ -169,9 +170,7 @@
public void onAnimationCancel(Animator animation) {
mCurrentValue = mEndValue;
mOneHandedAnimationCallbacks.forEach(
- (callback) -> {
- callback.onOneHandedAnimationCancel(this);
- }
+ (callback) -> callback.onOneHandedAnimationCancel(this)
);
}
@@ -181,12 +180,10 @@
@Override
public void onAnimationUpdate(ValueAnimator animation) {
- applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(),
- animation.getAnimatedFraction());
+ final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
+ applySurfaceControlTransaction(mLeash, tx, animation.getAnimatedFraction());
mOneHandedAnimationCallbacks.forEach(
- (callback) -> {
- callback.onTutorialAnimationUpdate(((Rect) mCurrentValue).top);
- }
+ (callback) -> callback.onAnimationUpdate(0f, (float) mCurrentValue)
);
}
@@ -217,12 +214,8 @@
return mToken;
}
- Rect getDestinationBounds() {
- return (Rect) mEndValue;
- }
-
- int getDestinationOffset() {
- return ((Rect) mEndValue).top - ((Rect) mStartValue).top;
+ float getDestinationOffset() {
+ return ((float) mEndValue - (float) mStartValue);
}
@TransitionDirection
@@ -259,40 +252,42 @@
}
@VisibleForTesting
- static OneHandedTransitionAnimator<Rect> ofBounds(WindowContainerToken token,
- SurfaceControl leash, Rect startValue, Rect endValue) {
+ static OneHandedTransitionAnimator<Float> ofYOffset(WindowContainerToken token,
+ SurfaceControl leash, float startValue, float endValue, Rect displayBounds) {
- return new OneHandedTransitionAnimator<Rect>(token, leash, new Rect(startValue),
- new Rect(endValue)) {
+ return new OneHandedTransitionAnimator<Float>(token, leash, startValue, endValue) {
- private final Rect mTmpRect = new Rect();
+ private final Rect mTmpRect = new Rect(displayBounds);
- private int getCastedFractionValue(float start, float end, float fraction) {
- return (int) (start * (1 - fraction) + end * fraction + .5f);
+ private float getCastedFractionValue(float start, float end, float fraction) {
+ return (start * (1 - fraction) + end * fraction + .5f);
}
@Override
void applySurfaceControlTransaction(SurfaceControl leash,
SurfaceControl.Transaction tx, float fraction) {
- final Rect start = getStartValue();
- final Rect end = getEndValue();
+ final float start = getStartValue();
+ final float end = getEndValue();
+ final float currentValue = getCastedFractionValue(start, end, fraction);
mTmpRect.set(
- getCastedFractionValue(start.left, end.left, fraction),
- getCastedFractionValue(start.top, end.top, fraction),
- getCastedFractionValue(start.right, end.right, fraction),
- getCastedFractionValue(start.bottom, end.bottom, fraction));
- setCurrentValue(mTmpRect);
- getSurfaceTransactionHelper().crop(tx, leash, mTmpRect)
- .round(tx, leash);
+ mTmpRect.left,
+ mTmpRect.top + Math.round(currentValue),
+ mTmpRect.right,
+ mTmpRect.bottom + Math.round(currentValue));
+ setCurrentValue(currentValue);
+ getSurfaceTransactionHelper()
+ .crop(tx, leash, mTmpRect)
+ .round(tx, leash)
+ .translate(tx, leash, currentValue);
tx.apply();
}
@Override
void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
getSurfaceTransactionHelper()
- .alpha(tx, leash, 1f)
- .translate(tx, leash, getEndValue().top - getStartValue().top)
- .round(tx, leash);
+ .crop(tx, leash, mTmpRect)
+ .round(tx, leash)
+ .translate(tx, leash, getStartValue());
tx.apply();
}
};
@@ -309,4 +304,15 @@
* (2.0f * Math.PI) / 4.0f) + 1);
}
}
+
+ void dump(@NonNull PrintWriter pw) {
+ final String innerPrefix = " ";
+ pw.println(TAG + "states: ");
+ pw.print(innerPrefix + "mAnimatorMap=");
+ pw.println(mAnimatorMap);
+
+ if (mSurfaceTransactionHelper != null) {
+ mSurfaceTransactionHelper.dump(pw);
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 7844c11..b8da37f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -61,6 +61,7 @@
private DisplayLayout mDisplayLayout = new DisplayLayout();
+ private float mLastVisualOffset = 0;
private final Rect mLastVisualDisplayBounds = new Rect();
private final Rect mDefaultDisplayBounds = new Rect();
private final OneHandedSettingsUtil mOneHandedSettingsUtil;
@@ -96,8 +97,7 @@
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
mAnimationController.removeAnimator(animator.getToken());
if (mAnimationController.isAnimatorsConsumed()) {
- resetWindowsOffsetInternal(animator.getTransitionDirection());
- finishOffset(animator.getDestinationOffset(),
+ finishOffset((int) animator.getDestinationOffset(),
animator.getTransitionDirection());
}
}
@@ -107,8 +107,7 @@
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
mAnimationController.removeAnimator(animator.getToken());
if (mAnimationController.isAnimatorsConsumed()) {
- resetWindowsOffsetInternal(animator.getTransitionDirection());
- finishOffset(animator.getDestinationOffset(),
+ finishOffset((int) animator.getDestinationOffset(),
animator.getTransitionDirection());
}
}
@@ -165,16 +164,16 @@
@Override
public void unregisterOrganizer() {
super.unregisterOrganizer();
- resetWindowsOffset(null);
+ resetWindowsOffset();
}
/**
* Handler for display rotation changes by {@link DisplayLayout}
*
- * @param context Any context
- * @param toRotation target rotation of the display (after rotating).
- * @param wct A task transaction {@link WindowContainerTransaction} from
- * {@link DisplayChangeController} to populate.
+ * @param context Any context
+ * @param toRotation target rotation of the display (after rotating).
+ * @param wct A task transaction {@link WindowContainerTransaction} from
+ * {@link DisplayChangeController} to populate.
*/
public void onRotateDisplay(Context context, int toRotation, WindowContainerTransaction wct) {
if (mDisplayLayout.rotation() == toRotation) {
@@ -186,7 +185,6 @@
return;
}
mDisplayLayout.rotateTo(context.getResources(), toRotation);
- resetWindowsOffset(wct);
updateDisplayBounds();
finishOffset(0, TRANSITION_DIRECTION_EXIT);
}
@@ -196,38 +194,20 @@
* Directly perform manipulation/offset on the leash.
*/
public void scheduleOffset(int xOffset, int yOffset) {
- final Rect toBounds = new Rect(mDefaultDisplayBounds.left,
- mDefaultDisplayBounds.top + yOffset,
- mDefaultDisplayBounds.right,
- mDefaultDisplayBounds.bottom + yOffset);
- final Rect fromBounds = getLastVisualDisplayBounds();
+ final float fromPos = mLastVisualOffset;
final int direction = yOffset > 0
? TRANSITION_DIRECTION_TRIGGER
: TRANSITION_DIRECTION_EXIT;
-
- final WindowContainerTransaction wct = new WindowContainerTransaction();
mDisplayAreaTokenMap.forEach(
(token, leash) -> {
- animateWindows(token, leash, fromBounds, toBounds, direction,
+ animateWindows(token, leash, fromPos, yOffset, direction,
mEnterExitAnimationDurationMs);
- wct.setBounds(token, toBounds);
- wct.setAppBounds(token, toBounds);
});
- applyTransaction(wct);
- }
-
- private void resetWindowsOffsetInternal(
- @OneHandedAnimationController.TransitionDirection int td) {
- if (td == TRANSITION_DIRECTION_TRIGGER) {
- return;
- }
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- resetWindowsOffset(wct);
- applyTransaction(wct);
+ mLastVisualOffset = yOffset;
}
@VisibleForTesting
- void resetWindowsOffset(WindowContainerTransaction wct) {
+ void resetWindowsOffset() {
final SurfaceControl.Transaction tx =
mSurfaceControlTransactionFactory.getTransaction();
mDisplayAreaTokenMap.forEach(
@@ -238,21 +218,20 @@
animator.cancel();
}
tx.setPosition(leash, 0, 0)
- .setWindowCrop(leash, -1/* reset */, -1/* reset */);
- // DisplayRotationController will applyTransaction() after finish rotating
- if (wct != null) {
- wct.setBounds(token, null/* reset */);
- wct.setAppBounds(token, null/* reset */);
- }
+ .setWindowCrop(leash, -1, -1)
+ .setCornerRadius(leash, -1);
});
tx.apply();
+ mLastVisualOffset = 0;
+ mLastVisualDisplayBounds.offsetTo(0, 0);
}
- private void animateWindows(WindowContainerToken token, SurfaceControl leash, Rect fromBounds,
- Rect toBounds, @OneHandedAnimationController.TransitionDirection int direction,
+ private void animateWindows(WindowContainerToken token, SurfaceControl leash, float fromPos,
+ float toPos, @OneHandedAnimationController.TransitionDirection int direction,
int durationMs) {
final OneHandedAnimationController.OneHandedTransitionAnimator animator =
- mAnimationController.getAnimator(token, leash, fromBounds, toBounds);
+ mAnimationController.getAnimator(token, leash, fromPos, toPos,
+ mLastVisualDisplayBounds);
if (animator != null) {
animator.setTransitionDirection(direction)
.addOneHandedAnimationCallback(mOneHandedAnimationCallback)
@@ -265,10 +244,13 @@
}
@VisibleForTesting
- void finishOffset(int offset,
- @OneHandedAnimationController.TransitionDirection int direction) {
- mLastVisualDisplayBounds.offsetTo(0,
- direction == TRANSITION_DIRECTION_TRIGGER ? offset : 0);
+ void finishOffset(int offset, @OneHandedAnimationController.TransitionDirection int direction) {
+ if (direction == TRANSITION_DIRECTION_EXIT) {
+ // We must do this to ensure reset property for leash when exit one handed mode
+ resetWindowsOffset();
+ }
+ mLastVisualOffset = direction == TRANSITION_DIRECTION_TRIGGER ? offset : 0;
+ mLastVisualDisplayBounds.offsetTo(0, Math.round(mLastVisualOffset));
for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) {
final OneHandedTransitionCallback cb = mTransitionCallbacks.get(i);
cb.onStartTransition(false /* isTransitioning */);
@@ -285,7 +267,7 @@
*
* @return Rect latest finish_offset
*/
- public Rect getLastVisualDisplayBounds() {
+ private Rect getLastVisualDisplayBounds() {
return mLastVisualDisplayBounds;
}
@@ -332,5 +314,11 @@
pw.println(mDefaultDisplayBounds);
pw.print(innerPrefix + "mLastVisualDisplayBounds=");
pw.println(mLastVisualDisplayBounds);
+ pw.print(innerPrefix + "mLastVisualOffset=");
+ pw.println(mLastVisualOffset);
+
+ if (mAnimationController != null) {
+ mAnimationController.dump(pw);
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSurfaceTransactionHelper.java
index e7010db..e9048c6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSurfaceTransactionHelper.java
@@ -21,18 +21,28 @@
import android.graphics.Rect;
import android.view.SurfaceControl;
+import androidx.annotation.NonNull;
+
import com.android.wm.shell.R;
+import java.io.PrintWriter;
+
/**
* Abstracts the common operations on {@link SurfaceControl.Transaction} for OneHanded transition.
*/
public class OneHandedSurfaceTransactionHelper {
+ private static final String TAG = "OneHandedSurfaceTransactionHelper";
+
private final boolean mEnableCornerRadius;
private final float mCornerRadius;
+ private final float mCornerRadiusAdjustment;
public OneHandedSurfaceTransactionHelper(Context context) {
final Resources res = context.getResources();
- mCornerRadius = res.getDimension(com.android.internal.R.dimen.rounded_corner_radius);
+ mCornerRadiusAdjustment = res.getDimension(
+ com.android.internal.R.dimen.rounded_corner_radius_adjustment);
+ mCornerRadius = res.getDimension(com.android.internal.R.dimen.rounded_corner_radius)
+ - mCornerRadiusAdjustment;
mEnableCornerRadius = res.getBoolean(R.bool.config_one_handed_enable_round_corner);
}
@@ -48,25 +58,13 @@
}
/**
- * Operates the alpha on a given transaction and leash
- *
- * @return same {@link OneHandedSurfaceTransactionHelper} instance for method chaining
- */
- OneHandedSurfaceTransactionHelper alpha(SurfaceControl.Transaction tx, SurfaceControl leash,
- float alpha) {
- tx.setAlpha(leash, alpha);
- return this;
- }
-
- /**
* Operates the crop (setMatrix) on a given transaction and leash
*
* @return same {@link OneHandedSurfaceTransactionHelper} instance for method chaining
*/
OneHandedSurfaceTransactionHelper crop(SurfaceControl.Transaction tx, SurfaceControl leash,
Rect destinationBounds) {
- tx.setWindowCrop(leash, destinationBounds.width(), destinationBounds.height())
- .setPosition(leash, destinationBounds.left, destinationBounds.top);
+ tx.setWindowCrop(leash, destinationBounds.width(), destinationBounds.height());
return this;
}
@@ -85,4 +83,15 @@
interface SurfaceControlTransactionFactory {
SurfaceControl.Transaction getTransaction();
}
+
+ void dump(@NonNull PrintWriter pw) {
+ final String innerPrefix = " ";
+ pw.println(TAG + "states: ");
+ pw.print(innerPrefix + "mEnableCornerRadius=");
+ pw.println(mEnableCornerRadius);
+ pw.print(innerPrefix + "mCornerRadiusAdjustment=");
+ pw.println(mCornerRadiusAdjustment);
+ pw.print(innerPrefix + "mCornerRadius=");
+ pw.println(mCornerRadius);
+ }
}
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 d55359c..e8cee8a 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
@@ -78,16 +78,21 @@
private final OneHandedAnimationCallback mAnimationCallback = new OneHandedAnimationCallback() {
@Override
- public void onTutorialAnimationUpdate(int offset) {
- onAnimationUpdate(offset);
+ public void onAnimationUpdate(float xPos, float yPos) {
+ if (!canShowTutorial()) {
+ return;
+ }
+ mTargetViewContainer.setVisibility(View.VISIBLE);
+ mTargetViewContainer.setTransitionGroup(true);
+ mTargetViewContainer.setTranslationY(yPos - mTargetViewContainer.getHeight());
}
@Override
public void onOneHandedAnimationStart(
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
- final Rect startValue = (Rect) animator.getStartValue();
+ final float startValue = (float) animator.getStartValue();
if (mTriggerState == ONE_HANDED_TRIGGER_STATE.UNSET) {
- mTriggerState = (startValue.top == 0)
+ mTriggerState = (startValue == 0f)
? ONE_HANDED_TRIGGER_STATE.ENTERING : ONE_HANDED_TRIGGER_STATE.EXITING;
if (mCanShowTutorial && mTriggerState == ONE_HANDED_TRIGGER_STATE.ENTERING) {
attachTurtorialTarget();
@@ -239,15 +244,6 @@
return true;
}
- private void onAnimationUpdate(float value) {
- if (!canShowTutorial()) {
- return;
- }
- mTargetViewContainer.setVisibility(View.VISIBLE);
- mTargetViewContainer.setTransitionGroup(true);
- mTargetViewContainer.setTranslationY(value - mTargetViewContainer.getHeight());
- }
-
/**
* onConfigurationChanged events for updating tutorial text.
* @param newConfig
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index efaa269..f7160e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -452,6 +452,10 @@
// Make sure the main stage is active.
mMainStage.activate(getMainStageBounds(), wct);
mSideStage.setBounds(getSideStageBounds(), wct);
+ // Reorder side stage to the top whenever there's a new child task appeared in side
+ // stage. This is needed to prevent main stage occludes side stage and makes main stage
+ // flipping between fullscreen and multi-window windowing mode.
+ wct.reorder(mSideStage.mRootTaskInfo.token, true);
mTaskOrganizer.applyTransaction(wct);
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java
index a8feb04..af11b7e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java
@@ -26,8 +26,6 @@
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.common.ShellExecutor;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -43,8 +41,6 @@
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class OneHandedAnimationControllerTest extends OneHandedTestCase {
- private static final int TEST_BOUNDS_WIDTH = 1000;
- private static final int TEST_BOUNDS_HEIGHT = 1000;
OneHandedAnimationController mOneHandedAnimationController;
@@ -52,9 +48,7 @@
private SurfaceControl mMockLeash;
@Mock
private WindowContainerToken mMockToken;
-
- @Mock
- private ShellExecutor mMainExecutor;
+ private Rect mDisplayBounds = new Rect();
@Before
public void setUp() throws Exception {
@@ -64,12 +58,10 @@
@Test
public void testGetAnimator_withSameBounds_returnAnimator() {
- final Rect originalBounds = new Rect(0, 0, TEST_BOUNDS_WIDTH, TEST_BOUNDS_HEIGHT);
- final Rect destinationBounds = originalBounds;
- destinationBounds.offset(0, 300);
+ final float yOffset = 300;
final OneHandedAnimationController.OneHandedTransitionAnimator animator =
mOneHandedAnimationController
- .getAnimator(mMockToken, mMockLeash, originalBounds, destinationBounds);
+ .getAnimator(mMockToken, mMockLeash, 0, yOffset, mDisplayBounds);
assertNotNull(animator);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index eb731d2..a27ed11 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -116,7 +116,9 @@
mDisplayLayout = new DisplayLayout(mContext, mDisplay);
mDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY, FEATURE_ONE_HANDED);
mDisplayAreaInfo.configuration.orientation = Configuration.ORIENTATION_PORTRAIT;
- when(mMockAnimationController.getAnimator(any(), any(), any(), any())).thenReturn(null);
+ when(mMockAnimationController.getAnimator(any(), any(), anyFloat(), anyFloat(),
+ any())).thenReturn(
+ null);
when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
when(mMockSurfaceTransactionHelper.translate(any(), any(), anyFloat())).thenReturn(
mMockSurfaceTransactionHelper);
@@ -164,7 +166,8 @@
info.getDisplayAreaInfo(),
info.getLeash()));
- verify(mMockAnimationController, never()).getAnimator(any(), any(), any(), any());
+ verify(mMockAnimationController, never()).getAnimator(any(), any(), anyFloat(), anyFloat(),
+ any());
}
@Test
@@ -189,7 +192,7 @@
mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_90,
mMockWindowContainerTransaction);
- verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset();
verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@@ -200,7 +203,7 @@
mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_270,
mMockWindowContainerTransaction);
- verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset();
verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@@ -213,7 +216,7 @@
mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_90,
mMockWindowContainerTransaction);
- verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset();
verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@@ -226,7 +229,7 @@
mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_270,
mMockWindowContainerTransaction);
- verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset();
verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@@ -239,7 +242,7 @@
mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_0,
mMockWindowContainerTransaction);
- verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset();
verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@@ -252,7 +255,7 @@
mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_180,
mMockWindowContainerTransaction);
- verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset();
verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@@ -265,7 +268,7 @@
mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_0,
mMockWindowContainerTransaction);
- verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset();
verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@@ -278,7 +281,7 @@
mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_180,
mMockWindowContainerTransaction);
- verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset();
verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@@ -289,8 +292,7 @@
mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_0,
mMockWindowContainerTransaction);
- verify(mSpiedDisplayAreaOrganizer, never()).resetWindowsOffset(
- mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer, never()).resetWindowsOffset();
verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@@ -301,7 +303,7 @@
mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_180,
mMockWindowContainerTransaction);
- verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset();
verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@@ -314,8 +316,7 @@
mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_180,
mMockWindowContainerTransaction);
- verify(mSpiedDisplayAreaOrganizer, never()).resetWindowsOffset(
- mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer, never()).resetWindowsOffset();
verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@@ -328,7 +329,7 @@
mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_0,
mMockWindowContainerTransaction);
- verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset();
verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@@ -341,8 +342,7 @@
mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_90,
mMockWindowContainerTransaction);
- verify(mSpiedDisplayAreaOrganizer, never()).resetWindowsOffset(
- mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer, never()).resetWindowsOffset();
verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@@ -355,7 +355,7 @@
mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_270,
mMockWindowContainerTransaction);
- verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset();
verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@@ -368,8 +368,7 @@
mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_270,
mMockWindowContainerTransaction);
- verify(mSpiedDisplayAreaOrganizer, never()).resetWindowsOffset(
- mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer, never()).resetWindowsOffset();
verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@@ -382,8 +381,7 @@
mSpiedDisplayAreaOrganizer.onRotateDisplay(mContext, Surface.ROTATION_90,
mMockWindowContainerTransaction);
- verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset(
- mMockWindowContainerTransaction);
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset();
verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@@ -406,4 +404,18 @@
assertThat(mSpiedDisplayAreaOrganizer.getLastDisplayBounds()).isEqualTo(testBounds);
}
+
+ @Test
+ public void testExit_must_resetWindowsOffset() {
+ mSpiedDisplayAreaOrganizer.finishOffset(0, TRANSITION_DIRECTION_EXIT);
+
+ verify(mSpiedDisplayAreaOrganizer).resetWindowsOffset();
+ }
+
+ @Test
+ public void testTrigger_not_resetWindowsOffset() {
+ mSpiedDisplayAreaOrganizer.finishOffset(0, TRANSITION_DIRECTION_TRIGGER);
+
+ verify(mSpiedDisplayAreaOrganizer, never()).resetWindowsOffset();
+ }
}
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 1e90b7c..17c1404 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -816,6 +816,11 @@
uint32_t mSourceResourceId;
};
+static inline bool operator==(const android::ResXMLParser::ResXMLPosition& lhs,
+ const android::ResXMLParser::ResXMLPosition& rhs) {
+ return lhs.curNode == rhs.curNode;
+}
+
class DynamicRefTable;
/**
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index c1d6725..5952479 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -360,6 +360,9 @@
}
}
+ private static volatile LocationEnabledCache sLocationEnabledCache =
+ new LocationEnabledCache(4);
+
@GuardedBy("sLocationListeners")
private static final WeakHashMap<LocationListener, WeakReference<LocationListenerTransport>>
sLocationListeners = new WeakHashMap<>();
@@ -386,20 +389,6 @@
final Context mContext;
final ILocationManager mService;
- private volatile PropertyInvalidatedCache<Integer, Boolean> mLocationEnabledCache =
- new PropertyInvalidatedCache<Integer, Boolean>(
- 4,
- CACHE_KEY_LOCATION_ENABLED_PROPERTY) {
- @Override
- protected Boolean recompute(Integer userHandle) {
- try {
- return mService.isLocationEnabledForUser(userHandle);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- };
-
/**
* @hide
*/
@@ -533,7 +522,7 @@
* @return true if location is enabled and false if location is disabled.
*/
public boolean isLocationEnabled() {
- return isLocationEnabledForUser(Process.myUserHandle());
+ return isLocationEnabledForUser(mContext.getUser());
}
/**
@@ -546,12 +535,17 @@
*/
@SystemApi
public boolean isLocationEnabledForUser(@NonNull UserHandle userHandle) {
- PropertyInvalidatedCache<Integer, Boolean> cache = mLocationEnabledCache;
- if (cache != null) {
- return cache.query(userHandle.getIdentifier());
+ // skip the cache for any "special" user ids - special ids like CURRENT_USER may change
+ // their meaning over time and should never be in the cache. we could resolve the special
+ // user ids here, but that would require an x-process call anyways, and the whole point of
+ // the cache is to avoid x-process calls.
+ if (userHandle.getIdentifier() >= 0) {
+ PropertyInvalidatedCache<Integer, Boolean> cache = sLocationEnabledCache;
+ if (cache != null) {
+ return cache.query(userHandle.getIdentifier());
+ }
}
- // fallback if cache is disabled
try {
return mService.isLocationEnabledForUser(userHandle.getIdentifier());
} catch (RemoteException e) {
@@ -3004,7 +2998,7 @@
ListenerExecutor, CancellationSignal.OnCancelListener {
private final Executor mExecutor;
- private volatile @Nullable Consumer<Location> mConsumer;
+ volatile @Nullable Consumer<Location> mConsumer;
GetCurrentLocationTransport(Executor executor, Consumer<Location> consumer,
@Nullable CancellationSignal cancellationSignal) {
@@ -3465,6 +3459,37 @@
}
}
+ private static class LocationEnabledCache extends PropertyInvalidatedCache<Integer, Boolean> {
+
+ // this is not loaded immediately because this class is created as soon as LocationManager
+ // is referenced for the first time, and within the system server, the ILocationManager
+ // service may not have been loaded yet at that time.
+ private @Nullable ILocationManager mManager;
+
+ LocationEnabledCache(int numEntries) {
+ super(numEntries, CACHE_KEY_LOCATION_ENABLED_PROPERTY);
+ }
+
+ @Override
+ protected Boolean recompute(Integer userId) {
+ Preconditions.checkArgument(userId >= 0);
+
+ if (mManager == null) {
+ try {
+ mManager = getService();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ try {
+ return mManager.isLocationEnabledForUser(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
/**
* @hide
*/
@@ -3475,7 +3500,7 @@
/**
* @hide
*/
- public void disableLocalLocationEnabledCaches() {
- mLocationEnabledCache = null;
+ public static void disableLocalLocationEnabledCaches() {
+ sLocationEnabledCache = null;
}
}
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 0c5eaa7..df81603 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -1525,7 +1525,7 @@
/**
* Get the {@link NetworkCapabilities} for the given {@link Network}. This
- * will return {@code null} if the network is unknown.
+ * will return {@code null} if the network is unknown or if the |network| argument is null.
*
* This will remove any location sensitive data in {@link TransportInfo} embedded in
* {@link NetworkCapabilities#getTransportInfo()}. Some transport info instances like
diff --git a/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.java b/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.java
index 9c82907..778537b 100644
--- a/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.java
+++ b/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.java
@@ -39,11 +39,4 @@
setLayoutResource(R.layout.top_intro_preference);
setSelectable(false);
}
-
- @Override
- public void onBindViewHolder(PreferenceViewHolder holder) {
- super.onBindViewHolder(holder);
- holder.setDividerAllowedAbove(true);
- holder.setDividerAllowedBelow(true);
- }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index f180776..f046f06 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -2027,6 +2027,7 @@
}
};
+ /* For the Storage Settings which shows category of app types. */
public static final AppFilter FILTER_OTHER_APPS =
new AppFilter() {
@Override
@@ -2046,4 +2047,21 @@
return !isCategorized;
}
};
+
+ /* For the Storage Settings which shows category of file types. */
+ public static final AppFilter FILTER_APPS_EXCEPT_GAMES =
+ new AppFilter() {
+ @Override
+ public void init() {
+ }
+
+ @Override
+ public boolean filterApp(AppEntry entry) {
+ boolean isCategorized;
+ synchronized (entry) {
+ isCategorized = FILTER_GAMES.filterApp(entry);
+ }
+ return !isCategorized;
+ }
+ };
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
index d9ac262..f1e1e7d 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
@@ -138,6 +138,34 @@
}
@Test
+ public void testAppsExceptGamesFilterRejectsGame() {
+ mEntry.info.category = ApplicationInfo.CATEGORY_GAME;
+
+ assertThat(ApplicationsState.FILTER_APPS_EXCEPT_GAMES.filterApp(mEntry)).isFalse();
+ }
+
+ @Test
+ public void testAppsExceptGamesFilterAcceptsImage() {
+ mEntry.info.category = ApplicationInfo.CATEGORY_IMAGE;
+
+ assertThat(ApplicationsState.FILTER_APPS_EXCEPT_GAMES.filterApp(mEntry)).isTrue();
+ }
+
+ @Test
+ public void testAppsExceptGamesFilterAcceptsVideo() {
+ mEntry.info.category = ApplicationInfo.CATEGORY_VIDEO;
+
+ assertThat(ApplicationsState.FILTER_APPS_EXCEPT_GAMES.filterApp(mEntry)).isTrue();
+ }
+
+ @Test
+ public void testAppsExceptGamesFilterAcceptsAudio() {
+ mEntry.info.category = ApplicationInfo.CATEGORY_AUDIO;
+
+ assertThat(ApplicationsState.FILTER_APPS_EXCEPT_GAMES.filterApp(mEntry)).isTrue();
+ }
+
+ @Test
public void testDownloadAndLauncherAndInstantAcceptsCorrectApps() {
// should include instant apps
mEntry.isHomeApp = false;
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 20273d0..1cf0c5f 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -17,6 +17,7 @@
import android.view.RemoteAnimationTarget
import android.view.SyncRtSurfaceTransactionApplier
import android.view.View
+import android.view.ViewGroup
import android.view.WindowManager
import android.view.animation.AnimationUtils
import android.view.animation.PathInterpolator
@@ -112,7 +113,7 @@
@PublishedApi
internal fun Controller.callOnIntentStartedOnMainThread(willAnimate: Boolean) {
if (Looper.myLooper() != Looper.getMainLooper()) {
- this.getRootView().context.mainExecutor.execute {
+ this.launchContainer.context.mainExecutor.execute {
this.onIntentStarted(willAnimate)
}
} else {
@@ -166,15 +167,19 @@
}
/**
- * Return the root [View] that contains the view that started the intent and will be
- * animating together with the window.
+ * The container in which the view that started the intent will be animating together with
+ * the opening window.
*
- * This view will be used to:
+ * This will be used to:
* - Get the associated [Context].
* - Compute whether we are expanding fully above the current window.
* - Apply surface transactions in sync with RenderThread.
+ *
+ * This container can be changed to force this [Controller] to animate the expanding view
+ * inside a different location, for instance to ensure correct layering during the
+ * animation.
*/
- fun getRootView(): View
+ var launchContainer: ViewGroup
/**
* Return the [State] of the view that will be animated. We will animate from this state to
@@ -272,9 +277,9 @@
@VisibleForTesting
inner class Runner(private val controller: Controller) : IRemoteAnimationRunner.Stub() {
- private val rootView = controller.getRootView()
- @PublishedApi internal val context = rootView.context
- private val transactionApplier = SyncRtSurfaceTransactionApplier(rootView)
+ private val launchContainer = controller.launchContainer
+ @PublishedApi internal val context = launchContainer.context
+ private val transactionApplier = SyncRtSurfaceTransactionApplier(launchContainer)
private var animator: ValueAnimator? = null
private var windowCrop = Rect()
@@ -291,11 +296,11 @@
@PublishedApi
internal fun postTimeout() {
- rootView.postDelayed(onTimeout, LAUNCH_TIMEOUT)
+ launchContainer.postDelayed(onTimeout, LAUNCH_TIMEOUT)
}
private fun removeTimeout() {
- rootView.removeCallbacks(onTimeout)
+ launchContainer.removeCallbacks(onTimeout)
}
override fun onAnimationStart(
@@ -369,11 +374,11 @@
val endWidth = endRight - endLeft
// TODO(b/184121838): Ensure that we are launching on the same screen.
- val rootViewLocation = rootView.locationOnScreen
+ val rootViewLocation = launchContainer.locationOnScreen
val isExpandingFullyAbove = endTop <= rootViewLocation[1] &&
- endBottom >= rootViewLocation[1] + rootView.height &&
+ endBottom >= rootViewLocation[1] + launchContainer.height &&
endLeft <= rootViewLocation[0] &&
- endRight >= rootViewLocation[0] + rootView.width
+ endRight >= rootViewLocation[0] + launchContainer.width
// TODO(b/184121838): We should somehow get the top and bottom radius of the window.
val endRadius = if (isExpandingFullyAbove) {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt
new file mode 100644
index 0000000..d4be253
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt
@@ -0,0 +1,10 @@
+package com.android.systemui.animation
+
+/**
+ * A base class to easily create an implementation of [ActivityLaunchAnimator.Controller] which
+ * delegates most of its call to [delegate]. This is mostly useful for Java code which can't easily
+ * create such a delegated class.
+ */
+open class DelegateLaunchAnimatorController(
+ protected val delegate: ActivityLaunchAnimator.Controller
+) : ActivityLaunchAnimator.Controller by delegate
\ No newline at end of file
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index 3da4521..ce9feed 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -14,6 +14,7 @@
import android.view.GhostView
import android.view.View
import android.view.ViewGroup
+import android.view.ViewGroupOverlay
import android.widget.FrameLayout
import kotlin.math.min
@@ -32,9 +33,10 @@
/** The view that will be ghosted and from which the background will be extracted. */
private val ghostedView: View
) : ActivityLaunchAnimator.Controller {
- /** The root view to which we will add the ghost view and expanding background. */
- private val rootView = ghostedView.rootView as ViewGroup
- private val rootViewOverlay = rootView.overlay
+ /** The container to which we will add the ghost view and expanding background. */
+ override var launchContainer = ghostedView.rootView as ViewGroup
+ private val launchContainerOverlay: ViewGroupOverlay
+ get() = launchContainer.overlay
/** The ghost view that is drawn and animated instead of the ghosted view. */
private var ghostView: GhostView? = null
@@ -42,7 +44,7 @@
private val ghostViewMatrix = Matrix()
/**
- * The expanding background view that will be added to [rootView] (below [ghostView]) and
+ * The expanding background view that will be added to [launchContainer] (below [ghostView]) and
* animate.
*/
private var backgroundView: FrameLayout? = null
@@ -96,10 +98,6 @@
return gradient.cornerRadii?.get(CORNER_RADIUS_BOTTOM_INDEX) ?: gradient.cornerRadius
}
- override fun getRootView(): View {
- return rootView
- }
-
override fun createAnimatorState(): ActivityLaunchAnimator.State {
val location = ghostedView.locationOnScreen
return ActivityLaunchAnimator.State(
@@ -113,10 +111,10 @@
}
override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
- backgroundView = FrameLayout(rootView.context).apply {
+ backgroundView = FrameLayout(launchContainer.context).apply {
forceHasOverlappingRendering(false)
}
- rootViewOverlay.add(backgroundView)
+ launchContainerOverlay.add(backgroundView)
// We wrap the ghosted view background and use it to draw the expandable background. Its
// alpha will be set to 0 as soon as we start drawing the expanding background.
@@ -127,7 +125,7 @@
// Create a ghost of the view that will be moving and fading out. This allows to fade out
// the content before fading out the background.
- ghostView = GhostView.addGhost(ghostedView, rootView).apply {
+ ghostView = GhostView.addGhost(ghostedView, launchContainer).apply {
setLayerType(View.LAYER_TYPE_HARDWARE, null)
}
@@ -169,7 +167,7 @@
backgroundDrawable?.wrapped?.alpha = startBackgroundAlpha
GhostView.removeGhost(ghostedView)
- rootViewOverlay.remove(backgroundView)
+ launchContainerOverlay.remove(backgroundView)
ghostedView.invalidate()
}
diff --git a/packages/SystemUI/res/drawable/notification_material_bg.xml b/packages/SystemUI/res/drawable/notification_material_bg.xml
index 085263a..61a8e8e 100644
--- a/packages/SystemUI/res/drawable/notification_material_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_material_bg.xml
@@ -17,7 +17,7 @@
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:color="@color/notification_ripple_untinted_color">
+ android:color="?android:attr/colorControlHighlight">
<item>
<shape>
<solid android:color="?androidprv:attr/colorSurface" />
diff --git a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
index 1c4c89f..8535426 100644
--- a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
+++ b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
@@ -37,7 +37,14 @@
android:orientation="vertical"
app:layout_constraintGuide_percent="0.25" />
- <ImageView
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/media_horizontal_center_guideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ app:layout_constraintGuide_percent="0.5" />
+
+ <com.android.internal.widget.CachingIconView
android:id="@+id/recommendation_card_icon"
android:layout_width="@dimen/qs_aa_media_rec_header_icon_size"
android:layout_height="@dimen/qs_aa_media_rec_header_icon_size"
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 7142929..5176d96 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -26,6 +26,11 @@
android:fitsSystemWindows="true">
<FrameLayout
+ android:id="@+id/status_bar_launch_animation_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <FrameLayout
android:id="@+id/status_bar_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 13c285f..edd8486 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1279,9 +1279,10 @@
<dimen name="qs_media_disabled_seekbar_vertical_padding">36dp</dimen>
<!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
- <dimen name="qs_aa_media_rec_header_icon_padding">10dp</dimen>
+ <dimen name="qs_aa_media_rec_header_icon_start_margin">10dp</dimen>
<dimen name="qs_aa_media_rec_header_icon_size">18dp</dimen>
<dimen name="qs_aa_media_rec_album_size">72dp</dimen>
+ <dimen name="qs_aa_media_rec_album_vertical_margin">8dp</dimen>
<dimen name="qq_aa_media_rec_header_text_size">16sp</dimen>
<!-- Window magnification -->
diff --git a/packages/SystemUI/res/xml/media_recommendation_collapsed.xml b/packages/SystemUI/res/xml/media_recommendation_collapsed.xml
index afd800b..31a924c 100644
--- a/packages/SystemUI/res/xml/media_recommendation_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_recommendation_collapsed.xml
@@ -22,8 +22,8 @@
android:id="@+id/recommendation_card_icon"
android:layout_width="@dimen/qs_aa_media_rec_header_icon_size"
android:layout_height="@dimen/qs_aa_media_rec_header_icon_size"
- android:layout_marginTop="@dimen/qs_aa_media_rec_header_icon_padding"
- android:layout_marginStart="@dimen/qs_aa_media_rec_header_icon_padding"
+ android:layout_marginTop="@dimen/qs_media_padding"
+ android:layout_marginStart="@dimen/qs_aa_media_rec_header_icon_start_margin"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/media_vertical_start_guideline"
@@ -33,7 +33,7 @@
android:id="@+id/recommendation_card_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_aa_media_rec_header_icon_padding"
+ android:layout_marginStart="@dimen/qs_aa_media_rec_header_icon_start_margin"
app:layout_constraintTop_toBottomOf="@id/recommendation_card_icon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/media_vertical_start_guideline"
@@ -45,9 +45,12 @@
android:layout_height="@dimen/qs_aa_media_rec_album_size"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
+ android:layout_marginTop="@dimen/qs_media_padding"
+ android:layout_marginBottom="@dimen/qs_media_padding"
app:layout_constraintStart_toEndOf="@id/media_vertical_start_guideline"
app:layout_constraintEnd_toStartOf="@id/media_cover2"
app:layout_constraintHorizontal_chainStyle="spread"
+ app:layout_constraintVertical_chainStyle="spread"
android:visibility="gone" />
<Constraint
@@ -64,6 +67,8 @@
android:id="@+id/media_cover2"
android:layout_width="@dimen/qs_aa_media_rec_album_size"
android:layout_height="@dimen/qs_aa_media_rec_album_size"
+ android:layout_marginTop="@dimen/qs_media_padding"
+ android:layout_marginBottom="@dimen/qs_media_padding"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/media_cover1"
@@ -85,6 +90,8 @@
android:id="@+id/media_cover3"
android:layout_width="@dimen/qs_aa_media_rec_album_size"
android:layout_height="@dimen/qs_aa_media_rec_album_size"
+ android:layout_marginTop="@dimen/qs_media_padding"
+ android:layout_marginBottom="@dimen/qs_media_padding"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/media_cover2"
@@ -112,7 +119,6 @@
app:layout_constraintStart_toEndOf="@id/media_vertical_start_guideline"
app:layout_constraintEnd_toStartOf="@id/media_cover5"
app:layout_constraintHorizontal_chainStyle="spread"
- app:layout_constraintHorizontal_bias="1"
android:visibility="gone" />
<Constraint
@@ -134,7 +140,6 @@
app:layout_constraintStart_toEndOf="@+id/media_cover4"
app:layout_constraintEnd_toStartOf="@+id/media_cover6"
app:layout_constraintHorizontal_chainStyle="spread"
- app:layout_constraintHorizontal_bias="1"
android:visibility="gone" />
<Constraint
diff --git a/packages/SystemUI/res/xml/media_recommendation_expanded.xml b/packages/SystemUI/res/xml/media_recommendation_expanded.xml
index 04a4877..1411030 100644
--- a/packages/SystemUI/res/xml/media_recommendation_expanded.xml
+++ b/packages/SystemUI/res/xml/media_recommendation_expanded.xml
@@ -23,7 +23,7 @@
android:layout_width="@dimen/qs_aa_media_rec_header_icon_size"
android:layout_height="@dimen/qs_aa_media_rec_header_icon_size"
android:layout_marginTop="@dimen/qs_media_padding"
- android:layout_marginStart="@dimen/qs_aa_media_rec_header_icon_padding"
+ android:layout_marginStart="@dimen/qs_aa_media_rec_header_icon_start_margin"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/media_vertical_start_guideline"
@@ -33,7 +33,7 @@
android:id="@+id/recommendation_card_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_aa_media_rec_header_icon_padding"
+ android:layout_marginStart="@dimen/qs_aa_media_rec_header_icon_start_margin"
app:layout_constraintTop_toBottomOf="@id/recommendation_card_icon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/media_vertical_start_guideline"
@@ -44,11 +44,14 @@
android:layout_width="@dimen/qs_aa_media_rec_album_size"
android:layout_height="@dimen/qs_aa_media_rec_album_size"
android:layout_marginTop="@dimen/qs_media_padding"
+ android:layout_marginBottom="@dimen/qs_aa_media_rec_album_vertical_margin"
app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintBottom_toTopOf="@+id/media_cover4"
+ app:layout_constraintBottom_toTopOf="@+id/media_horizontal_center_guideline"
app:layout_constraintStart_toEndOf="@id/media_vertical_start_guideline"
app:layout_constraintEnd_toStartOf="@id/media_cover2"
app:layout_constraintHorizontal_chainStyle="spread"
+ app:layout_constraintVertical_chainStyle="spread"
+ app:layout_constraintVertical_bias="0"
android:visibility="gone" />
<Constraint
@@ -66,11 +69,14 @@
android:layout_width="@dimen/qs_aa_media_rec_album_size"
android:layout_height="@dimen/qs_aa_media_rec_album_size"
android:layout_marginTop="@dimen/qs_media_padding"
+ android:layout_marginBottom="@dimen/qs_aa_media_rec_album_vertical_margin"
app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintBottom_toTopOf="@+id/media_cover5"
+ app:layout_constraintBottom_toTopOf="@+id/media_horizontal_center_guideline"
app:layout_constraintStart_toEndOf="@id/media_cover1"
app:layout_constraintEnd_toStartOf="@id/media_cover3"
app:layout_constraintHorizontal_chainStyle="spread"
+ app:layout_constraintVertical_chainStyle="spread"
+ app:layout_constraintVertical_bias="0"
android:visibility="gone" />
<Constraint
@@ -88,12 +94,15 @@
android:layout_width="@dimen/qs_aa_media_rec_album_size"
android:layout_height="@dimen/qs_aa_media_rec_album_size"
android:layout_marginTop="@dimen/qs_media_padding"
+ android:layout_marginBottom="@dimen/qs_aa_media_rec_album_vertical_margin"
app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintBottom_toTopOf="@+id/media_cover6"
+ app:layout_constraintBottom_toTopOf="@+id/media_horizontal_center_guideline"
app:layout_constraintStart_toEndOf="@id/media_cover2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintHorizontal_bias="1"
+ app:layout_constraintVertical_chainStyle="spread"
+ app:layout_constraintVertical_bias="0"
android:visibility="gone" />
<Constraint
@@ -110,14 +119,15 @@
android:id="@+id/media_cover4"
android:layout_width="@dimen/qs_aa_media_rec_album_size"
android:layout_height="@dimen/qs_aa_media_rec_album_size"
- android:layout_marginTop="@dimen/qs_media_padding"
+ android:layout_marginTop="@dimen/qs_aa_media_rec_album_vertical_margin"
android:layout_marginBottom="@dimen/qs_media_padding"
- app:layout_constraintTop_toBottomOf="@+id/media_cover1"
+ app:layout_constraintTop_toBottomOf="@+id/media_horizontal_center_guideline"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/media_vertical_start_guideline"
app:layout_constraintEnd_toStartOf="@id/media_cover5"
app:layout_constraintHorizontal_chainStyle="spread"
- app:layout_constraintHorizontal_bias="1"
+ app:layout_constraintVertical_chainStyle="spread"
+ app:layout_constraintVertical_bias="1"
android:visibility="gone" />
<Constraint
@@ -134,14 +144,15 @@
android:id="@+id/media_cover5"
android:layout_width="@dimen/qs_aa_media_rec_album_size"
android:layout_height="@dimen/qs_aa_media_rec_album_size"
- android:layout_marginTop="@dimen/qs_media_padding"
+ android:layout_marginTop="@dimen/qs_aa_media_rec_album_vertical_margin"
android:layout_marginBottom="@dimen/qs_media_padding"
- app:layout_constraintTop_toBottomOf="@+id/media_cover2"
+ app:layout_constraintTop_toBottomOf="@+id/media_horizontal_center_guideline"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/media_cover4"
app:layout_constraintEnd_toStartOf="@+id/media_cover6"
app:layout_constraintHorizontal_chainStyle="spread"
- app:layout_constraintHorizontal_bias="1"
+ app:layout_constraintVertical_chainStyle="spread"
+ app:layout_constraintVertical_bias="1"
android:visibility="gone" />
<Constraint
@@ -158,14 +169,16 @@
android:id="@+id/media_cover6"
android:layout_width="@dimen/qs_aa_media_rec_album_size"
android:layout_height="@dimen/qs_aa_media_rec_album_size"
- android:layout_marginTop="@dimen/qs_media_padding"
+ android:layout_marginTop="@dimen/qs_aa_media_rec_album_vertical_margin"
android:layout_marginBottom="@dimen/qs_media_padding"
- app:layout_constraintTop_toBottomOf="@id/media_cover3"
+ app:layout_constraintTop_toBottomOf="@id/media_horizontal_center_guideline"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/media_cover5"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintHorizontal_bias="1"
+ app:layout_constraintVertical_chainStyle="spread"
+ app:layout_constraintVertical_bias="1"
android:visibility="gone" />
<Constraint
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index f1431f5..666afed 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -51,7 +51,6 @@
import com.android.internal.policy.IKeyguardService;
import com.android.internal.policy.IKeyguardStateCallback;
import com.android.systemui.SystemUIApplication;
-import com.android.wm.shell.transition.Transitions;
import javax.inject.Inject;
@@ -63,29 +62,16 @@
* Run Keyguard animation as remote animation in System UI instead of local animation in
* the server process.
*
- * 0: Runs all keyguard animation as local animation
- * 1: Only runs keyguard going away animation as remote animation
- * 2: Runs all keyguard animation as remote animation
- *
* Note: Must be consistent with WindowManagerService.
*/
private static final String ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY =
"persist.wm.enable_remote_keyguard_animation";
- private static final int sEnableRemoteKeyguardAnimation =
- SystemProperties.getInt(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, 1);
-
/**
* @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
*/
- public static boolean sEnableRemoteKeyguardGoingAwayAnimation =
- !Transitions.ENABLE_SHELL_TRANSITIONS && sEnableRemoteKeyguardAnimation >= 1;
-
- /**
- * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
- */
- public static boolean sEnableRemoteKeyguardOccludeAnimation =
- !Transitions.ENABLE_SHELL_TRANSITIONS && sEnableRemoteKeyguardAnimation >= 2;
+ static boolean sEnableRemoteKeyguardAnimation =
+ SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
private final KeyguardViewMediator mKeyguardViewMediator;
private final KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher;
@@ -97,22 +83,20 @@
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
- RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
- if (sEnableRemoteKeyguardGoingAwayAnimation) {
+ if (sEnableRemoteKeyguardAnimation) {
+ RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
final RemoteAnimationAdapter exitAnimationAdapter =
new RemoteAnimationAdapter(mExitAnimationRunner, 0, 0);
definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY, exitAnimationAdapter);
definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
exitAnimationAdapter);
- }
- if (sEnableRemoteKeyguardOccludeAnimation) {
final RemoteAnimationAdapter occludeAnimationAdapter =
new RemoteAnimationAdapter(mOccludeAnimationRunner, 0, 0);
definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE, occludeAnimationAdapter);
definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_UNOCCLUDE, occludeAnimationAdapter);
+ ActivityTaskManager.getInstance().registerRemoteAnimationsForDisplay(
+ DEFAULT_DISPLAY, definition);
}
- ActivityTaskManager.getInstance().registerRemoteAnimationsForDisplay(
- DEFAULT_DISPLAY, definition);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 85ee0dc..411c328 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -280,7 +280,7 @@
}
override fun onKeyguardDismissAmountChanged() {
- if (!KeyguardService.sEnableRemoteKeyguardGoingAwayAnimation) {
+ if (!KeyguardService.sEnableRemoteKeyguardAnimation) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index b7da7ad..48f9a58 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2100,7 +2100,7 @@
playSounds(false);
}
- if (KeyguardService.sEnableRemoteKeyguardGoingAwayAnimation) {
+ if (KeyguardService.sEnableRemoteKeyguardAnimation) {
mSurfaceBehindRemoteAnimationFinishedCallback = finishedCallback;
mSurfaceBehindRemoteAnimationRunning = true;
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 8d9a4be..6a2d0af 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -20,6 +20,7 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.PageIndicator
+import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.Utils
@@ -156,6 +157,13 @@
}
}
+ var visibleToUser: Boolean = false
+ set(value) {
+ if (field != value) {
+ field = value
+ }
+ }
+
init {
mediaFrame = inflateMediaCarousel()
mediaCarousel = mediaFrame.requireViewById(R.id.media_carousel_scroller)
@@ -203,6 +211,9 @@
override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {
Log.d(TAG, "My Smartspace media update is here")
addSmartspaceMediaRecommendations(key, data)
+ if (visibleToUser) {
+ logSmartspaceImpression()
+ }
}
override fun onMediaDataRemoved(key: String) {
@@ -567,6 +578,29 @@
mediaCarouselScrollHandler.playerWidthPlusPadding = playerWidthPlusPadding
}
}
+
+ /**
+ * Log the user impression for media card.
+ */
+ fun logSmartspaceImpression() {
+ MediaPlayerData.players().forEach {
+ // Log every impression of media recommendation card since it will only be shown
+ // for 1 minute after each connection.
+ if (it.recommendationViewHolder?.recommendations?.visibility == View.VISIBLE) {
+ /* ktlint-disable max-line-length */
+ SysUiStatsLog.write(SysUiStatsLog.SMARTSPACE_CARD_REPORTED,
+ 800, // SMARTSPACE_CARD_SEEN
+ it.getInstanceId(),
+ SysUiStatsLog.SMART_SPACE_CARD_REPORTED__CARD_TYPE__HEADPHONE_MEDIA_RECOMMENDATIONS,
+ it.getSurfaceForSmartspaceLogging(),
+ /* rank */ 0,
+ /* cardinality */ 1)
+ /* ktlint-disable max-line-length */
+ }
+
+ // TODO(shijieru): add logging for media control card
+ }
+ }
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index c713b22..ed16777 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -55,6 +55,7 @@
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.util.animation.TransitionLayout;
+import java.net.URISyntaxException;
import java.util.List;
import java.util.concurrent.Executor;
@@ -69,6 +70,9 @@
private static final String TAG = "MediaControlPanel";
private static final float DISABLED_ALPHA = 0.38f;
private static final String EXTRAS_MEDIA_SOURCE_PACKAGE_NAME = "package_name";
+ private static final String EXTRAS_SMARTSPACE_INTENT =
+ "com.google.android.apps.gsa.smartspace.extra.SMARTSPACE_INTENT";
+ private static final String KEY_SMARTSPACE_OPEN_IN_FOREGROUND = "KEY_OPEN_IN_FOREGROUND";
private static final int MEDIA_RECOMMENDATION_ITEMS_PER_ROW = 3;
private static final int MEDIA_RECOMMENDATION_MAX_NUM = 6;
@@ -100,6 +104,8 @@
private int mBackgroundColor;
private int mDevicePadding;
private int mAlbumArtSize;
+ // Instance id for logging purpose.
+ private int mInstanceId;
private final MediaOutputDialogFactory mMediaOutputDialogFactory;
/**
@@ -468,6 +474,7 @@
mRecommendationViewHolder.getCardIcon().setColorFilter(primaryColor);
mRecommendationViewHolder.getCardText().setTextColor(primaryColor);
+ mInstanceId = target.getSmartspaceTargetId().hashCode();
mRecommendationViewHolder.getRecommendations()
.setBackgroundTintList(ColorStateList.valueOf(backgroundColor));
mBackgroundColor = backgroundColor;
@@ -486,8 +493,10 @@
ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout();
int mediaRecommendationNum = Math.min(mediaRecommendationList.size(),
MEDIA_RECOMMENDATION_MAX_NUM);
- for (int i = 0; i < mediaRecommendationNum; i++) {
- SmartspaceAction recommendation = mediaRecommendationList.get(i);
+ for (int itemIndex = 0, uiComponentIndex = 0;
+ itemIndex < mediaRecommendationNum && uiComponentIndex < mediaRecommendationNum;
+ itemIndex++) {
+ SmartspaceAction recommendation = mediaRecommendationList.get(itemIndex);
if (recommendation.getIcon() == null) {
Log.w(TAG, "No media cover is provided. Skipping this item...");
continue;
@@ -511,31 +520,38 @@
}
// Set up media source app's logo.
- ImageView mediaSourceLogoImageView = mediaLogoItems.get(i);
+ ImageView mediaSourceLogoImageView = mediaLogoItems.get(uiComponentIndex);
mediaSourceLogoImageView.setImageDrawable(icon);
// TODO(b/186699032): Tint the app logo using the accent color.
mediaSourceLogoImageView.setColorFilter(backgroundColor, PorterDuff.Mode.XOR);
// Set up media item cover.
- ImageView mediaCoverImageView = mediaCoverItems.get(i);
+ ImageView mediaCoverImageView = mediaCoverItems.get(uiComponentIndex);
mediaCoverImageView.setImageIcon(recommendation.getIcon());
// Set up the click listener if applicable.
setSmartspaceRecItemOnClickListener(
mediaCoverImageView,
recommendation,
- target.getSmartspaceTargetId(),
null);
- if (i < MEDIA_RECOMMENDATION_ITEMS_PER_ROW) {
- setVisibleAndAlpha(collapsedSet, mediaCoverItemsResIds.get(i), true);
- setVisibleAndAlpha(collapsedSet, mediaLogoItemsResIds.get(i), true);
+ if (uiComponentIndex < MEDIA_RECOMMENDATION_ITEMS_PER_ROW) {
+ setVisibleAndAlpha(collapsedSet,
+ mediaCoverItemsResIds.get(uiComponentIndex), true);
+ setVisibleAndAlpha(collapsedSet,
+ mediaLogoItemsResIds.get(uiComponentIndex), true);
} else {
- setVisibleAndAlpha(collapsedSet, mediaCoverItemsResIds.get(i), false);
- setVisibleAndAlpha(collapsedSet, mediaLogoItemsResIds.get(i), false);
+ setVisibleAndAlpha(collapsedSet,
+ mediaCoverItemsResIds.get(uiComponentIndex), false);
+ setVisibleAndAlpha(collapsedSet,
+ mediaLogoItemsResIds.get(uiComponentIndex), false);
}
- setVisibleAndAlpha(expandedSet, mediaCoverItemsResIds.get(i), true);
- setVisibleAndAlpha(expandedSet, mediaLogoItemsResIds.get(i), true);
+ setVisibleAndAlpha(expandedSet,
+ mediaCoverItemsResIds.get(uiComponentIndex), true);
+ setVisibleAndAlpha(expandedSet,
+ mediaLogoItemsResIds.get(uiComponentIndex), true);
+
+ uiComponentIndex++;
}
// Set up long press to show guts setting panel.
@@ -635,7 +651,6 @@
private void setSmartspaceRecItemOnClickListener(
@NonNull View view,
@NonNull SmartspaceAction action,
- @NonNull String targetId,
@Nullable View.OnClickListener callback) {
if (view == null || action == null || action.getIntent() == null) {
Log.e(TAG, "No tap action can be set up");
@@ -646,24 +661,60 @@
// When media recommendation card is shown, there could be only one card.
SysUiStatsLog.write(SysUiStatsLog.SMARTSPACE_CARD_REPORTED,
760, // SMARTSPACE_CARD_CLICK
- targetId.hashCode(),
+ mInstanceId,
SysUiStatsLog
.SMART_SPACE_CARD_REPORTED__CARD_TYPE__HEADPHONE_MEDIA_RECOMMENDATIONS,
- getSurfaceForSmartspaceLogging(mMediaViewController.getCurrentEndLocation()),
- /* rank */ 1,
+ getSurfaceForSmartspaceLogging(),
+ /* rank */ 0,
/* cardinality */ 1);
- mActivityStarter.postStartActivityDismissingKeyguard(
- action.getIntent(),
- 0 /* delay */,
- buildLaunchAnimatorController(mRecommendationViewHolder.getRecommendations()));
+ if (shouldSmartspaceRecItemOpenInForeground(action)) {
+ // Request to unlock the device if the activity needs to be opened in foreground.
+ mActivityStarter.postStartActivityDismissingKeyguard(
+ action.getIntent(),
+ 0 /* delay */,
+ buildLaunchAnimatorController(
+ mRecommendationViewHolder.getRecommendations()));
+ } else {
+ // Otherwise, open the activity in background directly.
+ view.getContext().startActivity(action.getIntent());
+ }
+
if (callback != null) {
callback.onClick(v);
}
});
}
- private int getSurfaceForSmartspaceLogging(int currentEndLocation) {
+ /** Returns if the Smartspace action will open the activity in foreground. */
+ private boolean shouldSmartspaceRecItemOpenInForeground(SmartspaceAction action) {
+ if (action == null || action.getIntent() == null
+ || action.getIntent().getExtras() == null) {
+ return false;
+ }
+
+ String intentString = action.getIntent().getExtras().getString(EXTRAS_SMARTSPACE_INTENT);
+ if (intentString == null) {
+ return false;
+ }
+
+ try {
+ Intent wrapperIntent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME);
+ return wrapperIntent.getBooleanExtra(KEY_SMARTSPACE_OPEN_IN_FOREGROUND, false);
+ } catch (URISyntaxException e) {
+ Log.wtf(TAG, "Failed to create intent from URI: " + intentString);
+ e.printStackTrace();
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the surface given the current end location for MediaViewController
+ * @return surface used for Smartspace logging
+ */
+ protected int getSurfaceForSmartspaceLogging() {
+ int currentEndLocation = mMediaViewController.getCurrentEndLocation();
if (currentEndLocation == MediaHierarchyManager.LOCATION_QQS
|| currentEndLocation == MediaHierarchyManager.LOCATION_QS) {
return SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE;
@@ -672,4 +723,8 @@
}
return SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__DEFAULT_SURFACE;
}
+
+ protected int getInstanceId() {
+ return mInstanceId;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index 30bc8c1..a80a410 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -38,6 +38,7 @@
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.animation.UniqueObjectHostView
import javax.inject.Inject
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
/**
* Similarly to isShown but also excludes views that have 0 alpha
@@ -73,7 +74,8 @@
private val bypassController: KeyguardBypassController,
private val mediaCarouselController: MediaCarouselController,
private val notifLockscreenUserManager: NotificationLockscreenUserManager,
- wakefulnessLifecycle: WakefulnessLifecycle
+ wakefulnessLifecycle: WakefulnessLifecycle,
+ private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager
) {
/**
* The root overlay of the hierarchy. This is where the media notification is attached to
@@ -162,6 +164,26 @@
}
/**
+ * Is quick setting expanded?
+ */
+ var qsExpanded: Boolean = false
+ set(value) {
+ if (field != value) {
+ field = value
+ }
+ // Pull down shade from lock screen (exclude the case when shade is brought out by
+ // tapping twice on lock screen)
+ if (value && isLockScreenShadeVisibleToUser()) {
+ mediaCarouselController.logSmartspaceImpression()
+ }
+ // Release shade and back to lock screen
+ if (isLockScreenVisibleToUser()) {
+ mediaCarouselController.logSmartspaceImpression()
+ }
+ mediaCarouselController.visibleToUser = isVisibleToUser()
+ }
+
+ /**
* Is the shade currently collapsing from the expanded qs? If we're on the lockscreen and in qs,
* we wouldn't want to transition in that case.
*/
@@ -231,6 +253,11 @@
override fun onStateChanged(newState: Int) {
updateTargetState()
+ // Enters shade from lock screen
+ if (newState == StatusBarState.SHADE_LOCKED && isLockScreenShadeVisibleToUser()) {
+ mediaCarouselController.logSmartspaceImpression()
+ }
+ mediaCarouselController.visibleToUser = isVisibleToUser()
}
override fun onDozeAmountChanged(linear: Float, eased: Float) {
@@ -240,9 +267,27 @@
override fun onDozingChanged(isDozing: Boolean) {
if (!isDozing) {
dozeAnimationRunning = false
+ // Enters lock screen from screen off
+ if (isLockScreenVisibleToUser()) {
+ mediaCarouselController.logSmartspaceImpression()
+ }
} else {
updateDesiredLocation()
+ qsExpanded = false
}
+ mediaCarouselController.visibleToUser = isVisibleToUser()
+ }
+
+ override fun onExpandedChanged(isExpanded: Boolean) {
+ // Enters shade from home screen
+ if (isHomeScreenShadeVisibleToUser()) {
+ mediaCarouselController.logSmartspaceImpression()
+ }
+ // Back to lock screen from bouncer
+ if (isLockScreenVisibleToUser()) {
+ mediaCarouselController.logSmartspaceImpression()
+ }
+ mediaCarouselController.visibleToUser = isVisibleToUser()
}
})
@@ -622,6 +667,36 @@
return location
}
+ /**
+ * Returns true when the media card could be visible to the user if existed.
+ */
+ private fun isVisibleToUser(): Boolean {
+ return isLockScreenVisibleToUser() || isLockScreenShadeVisibleToUser() ||
+ isHomeScreenShadeVisibleToUser()
+ }
+
+ private fun isLockScreenVisibleToUser(): Boolean {
+ return !statusBarStateController.isDozing &&
+ !statusBarKeyguardViewManager.isBouncerShowing &&
+ statusBarStateController.state == StatusBarState.KEYGUARD &&
+ notifLockscreenUserManager.shouldShowLockscreenNotifications() &&
+ statusBarStateController.isExpanded &&
+ !qsExpanded
+ }
+
+ private fun isLockScreenShadeVisibleToUser(): Boolean {
+ return !statusBarStateController.isDozing &&
+ !statusBarKeyguardViewManager.isBouncerShowing &&
+ (statusBarStateController.state == StatusBarState.SHADE_LOCKED ||
+ (statusBarStateController.state == StatusBarState.KEYGUARD && qsExpanded))
+ }
+
+ private fun isHomeScreenShadeVisibleToUser(): Boolean {
+ return !statusBarStateController.isDozing &&
+ statusBarStateController.state == StatusBarState.SHADE &&
+ statusBarStateController.isExpanded
+ }
+
companion object {
/**
* Attached in expanded quick settings
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 4e41d75..56375ad 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -101,7 +101,6 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
-import android.view.inputmethod.InputMethodManager;
import androidx.annotation.VisibleForTesting;
@@ -1176,9 +1175,6 @@
accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
updateAccessibilityServicesState(mAccessibilityManager);
- ButtonDispatcher imeSwitcherButton = mNavigationBarView.getImeSwitchButton();
- imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick);
-
updateScreenPinningGestures();
}
@@ -1278,11 +1274,6 @@
mCommandQueue.toggleRecentApps();
}
- private void onImeSwitcherClick(View v) {
- mContext.getSystemService(InputMethodManager.class).showInputMethodPickerFromSystem(
- true /* showAuxiliarySubtypes */, mDisplayId);
- };
-
private boolean onLongPressBackHome(View v) {
return onLongPressNavigationButtons(v, R.id.back, R.id.home);
}
@@ -1291,6 +1282,7 @@
return onLongPressNavigationButtons(v, R.id.back, R.id.recent_apps);
}
+
/**
* This handles long-press of both back and recents/home. Back is the common button with
* combination of recents if it is visible or home if recents is invisible.
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 66cfae4..3544f60 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -217,9 +217,6 @@
@Override
public void onNavigationModeChanged(int mode) {
- if (mNavMode == mode) {
- return;
- }
final int oldMode = mNavMode;
mNavMode = mode;
mHandler.post(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java
index 4d9175b..7342f91 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java
@@ -158,6 +158,7 @@
}
public void onLikelyDefaultLayoutChange() {
+
// Reevaluate new layout
final String newValue = getDefaultLayout();
if (!Objects.equals(mCurrentLayout, newValue)) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index bdd2735..0ed4d86 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -166,7 +166,6 @@
private NavigationBarInflaterView mNavigationInflaterView;
private RecentsOnboarding mRecentsOnboarding;
private NotificationPanelViewController mPanelView;
- private RotationContextButton mRotationContextButton;
private FloatingRotationButton mFloatingRotationButton;
private RotationButtonController mRotationButtonController;
private NavigationBarOverlayController mNavBarOverlayController;
@@ -234,6 +233,14 @@
}
}
+ private final OnClickListener mImeSwitcherClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mContext.getSystemService(InputMethodManager.class).showInputMethodPickerFromSystem(
+ true /* showAuxiliarySubtypes */, getContext().getDisplayId());
+ }
+ };
+
private final AccessibilityDelegate mQuickStepAccessibilityDelegate =
new AccessibilityDelegate() {
private AccessibilityAction mToggleOverviewAction;
@@ -304,26 +311,32 @@
mIsVertical = false;
mLongClickableAccessibilityButton = false;
mNavBarMode = Dependency.get(NavigationModeController.class).addListener(this);
+ boolean isGesturalMode = isGesturalMode(mNavBarMode);
mSysUiFlagContainer = Dependency.get(SysUiState.class);
// Set up the context group of buttons
mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container);
final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher,
mLightContext, R.drawable.ic_ime_switcher_default);
+ final RotationContextButton rotateSuggestionButton = new RotationContextButton(
+ R.id.rotate_suggestion, mLightContext,
+ R.drawable.ic_sysbar_rotate_button_ccw_start_0);
final ContextualButton accessibilityButton =
new ContextualButton(R.id.accessibility_button, mLightContext,
R.drawable.ic_sysbar_accessibility_button);
mContextualButtonGroup.addButton(imeSwitcherButton);
+ if (!isGesturalMode) {
+ mContextualButtonGroup.addButton(rotateSuggestionButton);
+ }
mContextualButtonGroup.addButton(accessibilityButton);
mOverviewProxyService = Dependency.get(OverviewProxyService.class);
- mRotationContextButton = new RotationContextButton(R.id.rotate_suggestion,
- mLightContext, R.drawable.ic_sysbar_rotate_button_ccw_start_0);
mFloatingRotationButton = new FloatingRotationButton(context);
mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
mRotationButtonController = new RotationButtonController(mLightContext,
- mLightIconColor, mDarkIconColor);
- updateRotationButton();
+ mLightIconColor, mDarkIconColor,
+ isGesturalMode ? mFloatingRotationButton : rotateSuggestionButton,
+ mRotationButtonListener);
mNavBarOverlayController = Dependency.get(NavigationBarOverlayController.class);
if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
@@ -344,6 +357,7 @@
mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps));
mButtonDispatchers.put(R.id.ime_switcher, imeSwitcherButton);
mButtonDispatchers.put(R.id.accessibility_button, accessibilityButton);
+ mButtonDispatchers.put(R.id.rotate_suggestion, rotateSuggestionButton);
mButtonDispatchers.put(R.id.menu_container, mContextualButtonGroup);
mDeadZone = new DeadZone(this);
@@ -541,23 +555,6 @@
}
}
- /**
- * Updates the rotation button based on the current navigation mode.
- */
- private void updateRotationButton() {
- if (isGesturalMode(mNavBarMode)) {
- mContextualButtonGroup.removeButton(R.id.rotate_suggestion);
- mButtonDispatchers.remove(R.id.rotate_suggestion);
- mRotationButtonController.setRotationButton(mFloatingRotationButton,
- mRotationButtonListener);
- } else if (mContextualButtonGroup.getContextButton(R.id.rotate_suggestion) == null) {
- mContextualButtonGroup.addButton(mRotationContextButton);
- mButtonDispatchers.put(R.id.rotate_suggestion, mRotationContextButton);
- mRotationButtonController.setRotationButton(mRotationContextButton,
- mRotationButtonListener);
- }
- }
-
public KeyButtonDrawable getBackDrawable() {
KeyButtonDrawable drawable = getDrawable(getBackDrawableRes());
orientBackButton(drawable);
@@ -911,7 +908,6 @@
mBarTransitions.onNavigationModeChanged(mNavBarMode);
mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode);
mRecentsOnboarding.onNavigationModeChanged(mNavBarMode);
- updateRotationButton();
if (isGesturalMode(mNavBarMode)) {
mRegionSamplingHelper.start(mSamplingBounds);
@@ -936,6 +932,7 @@
mNavigationInflaterView = findViewById(R.id.navigation_inflater);
mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers);
+ getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
updateOrientationViews();
reloadNavIcons();
}
@@ -1030,9 +1027,6 @@
private void updateButtonLocation(ButtonDispatcher button, boolean inScreenSpace,
boolean useNearestRegion) {
- if (button == null) {
- return;
- }
View view = button.getCurrentView();
if (view == null || !button.isVisible()) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
index ddf089b..4bcb019 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
@@ -66,10 +66,10 @@
private static final int NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION = 3;
private final Context mContext;
+ private final RotationButton mRotationButton;
private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
private final UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
private final ViewRippler mViewRippler = new ViewRippler();
- private RotationButton mRotationButton;
private int mLastRotationSuggestion;
private boolean mPendingRotationSuggestion;
@@ -125,21 +125,20 @@
}
RotationButtonController(Context context, @ColorInt int lightIconColor,
- @ColorInt int darkIconColor) {
+ @ColorInt int darkIconColor, RotationButton rotationButton,
+ Consumer<Boolean> visibilityChangedCallback) {
mContext = context;
mLightIconColor = lightIconColor;
mDarkIconColor = darkIconColor;
+ mRotationButton = rotationButton;
+ mRotationButton.setRotationButtonController(this);
mIsNavigationBarShowing = true;
mRotationLockController = Dependency.get(RotationLockController.class);
mAccessibilityManagerWrapper = Dependency.get(AccessibilityManagerWrapper.class);
- mTaskStackListener = new TaskStackListenerImpl();
- }
- void setRotationButton(RotationButton rotationButton,
- Consumer<Boolean> visibilityChangedCallback) {
- mRotationButton = rotationButton;
- mRotationButton.setRotationButtonController(this);
+ // Register the task stack listener
+ mTaskStackListener = new TaskStackListenerImpl();
mRotationButton.setOnClickListener(this::onRotateSuggestionClick);
mRotationButton.setOnHoverListener(this::onRotateSuggestionHover);
mRotationButton.setVisibilityChangedCallback(visibilityChangedCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButtonGroup.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButtonGroup.java
index 2ace303..50b638b 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButtonGroup.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButtonGroup.java
@@ -41,23 +41,10 @@
* @param button the button added to the group
*/
public void addButton(@NonNull ContextualButton button) {
- // By default buttons in the context group are not visible until
- // {@link #setButtonVisibility()) is called to show one of the buttons
- button.setVisibility(View.INVISIBLE);
button.attachToGroup(this);
mButtonData.add(new ButtonData(button));
}
- /**
- * Removes a contextual button from the group.
- */
- public void removeButton(@IdRes int buttonResId) {
- int index = getContextButtonIndex(buttonResId);
- if (index != INVALID_INDEX) {
- mButtonData.remove(index);
- }
- }
-
public ContextualButton getContextButton(@IdRes int buttonResId) {
int index = getContextButtonIndex(buttonResId);
if (index != INVALID_INDEX) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index f078ccd..1ec785d47 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -213,7 +213,9 @@
// TODO move this logic to message queue
mStatusBarOptionalLazy.ifPresent(statusBarLazy -> {
StatusBar statusBar = statusBarLazy.get();
- statusBar.getPanelController().startExpandLatencyTracking();
+ if (event.getActionMasked() == ACTION_DOWN) {
+ statusBar.getPanelController().startExpandLatencyTracking();
+ }
mHandler.post(()-> {
int action = event.getActionMasked();
if (action == ACTION_DOWN) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 6b68fc6..a072de8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -924,7 +924,8 @@
// check of whether non-strong biometric is allowed
return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
&& msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
- || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED);
+ || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
+ || msgId == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED);
}
private boolean shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 5437ce6..7f31fdd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -18,6 +18,7 @@
import static com.android.systemui.statusbar.RemoteInputController.processForRemoteInput;
import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
+import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
@@ -34,7 +35,6 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins;
-import com.android.systemui.statusbar.phone.StatusBar;
import java.util.ArrayList;
import java.util.List;
@@ -46,7 +46,6 @@
@SuppressLint("OverrideAbstract")
public class NotificationListener extends NotificationListenerWithPlugins {
private static final String TAG = "NotificationListener";
- private static final boolean DEBUG = StatusBar.DEBUG;
private final Context mContext;
private final NotificationManager mNotificationManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
index 8194220..c4e2279 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -1,6 +1,6 @@
package com.android.systemui.statusbar.notification
-import android.view.View
+import android.view.ViewGroup
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.statusbar.NotificationShadeDepthController
@@ -45,7 +45,11 @@
) : ActivityLaunchAnimator.Controller {
private val notificationKey = notification.entry.sbn.key
- override fun getRootView(): View = notification.rootView
+ override var launchContainer: ViewGroup
+ get() = notification.rootView as ViewGroup
+ set(ignored) {
+ // Do nothing. Notifications are always animated inside their rootView.
+ }
override fun createAnimatorState(): ActivityLaunchAnimator.State {
// If the notification panel is collapsed, the clip may be larger than the height.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
index d95c265..d6356de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
@@ -16,9 +16,7 @@
package com.android.systemui.statusbar.notification.collection.legacy;
-import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.Notification;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.Log;
@@ -33,7 +31,6 @@
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.wm.shell.bubbles.Bubbles;
@@ -42,12 +39,10 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
-import java.util.TreeSet;
import javax.inject.Inject;
@@ -63,21 +58,13 @@
public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener, StateListener,
GroupMembershipManager, GroupExpansionManager, Dumpable {
- private static final String TAG = "NotifGroupManager";
- private static final boolean DEBUG = StatusBar.DEBUG;
- private static final boolean SPEW = StatusBar.SPEW;
- /**
- * The maximum amount of time (in ms) between the posting of notifications that can be
- * considered part of the same update batch.
- */
- private static final long POST_BATCH_MAX_AGE = 5000;
+ private static final String TAG = "NotificationGroupManager";
private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>();
private final ArraySet<OnGroupExpansionChangeListener> mExpansionChangeListeners =
new ArraySet<>();
private final ArraySet<OnGroupChangeListener> mGroupChangeListeners = new ArraySet<>();
private final Lazy<PeopleNotificationIdentifier> mPeopleNotificationIdentifier;
private final Optional<Bubbles> mBubblesOptional;
- private final EventBuffer mEventBuffer = new EventBuffer();
private int mBarState = -1;
private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
private HeadsUpManager mHeadsUpManager;
@@ -147,14 +134,8 @@
* When we want to remove an entry from being tracked for grouping
*/
public void onEntryRemoved(NotificationEntry removed) {
- if (SPEW) {
- Log.d(TAG, "onEntryRemoved: entry=" + removed);
- }
onEntryRemovedInternal(removed, removed.getSbn());
- StatusBarNotification oldSbn = mIsolatedEntries.remove(removed.getKey());
- if (oldSbn != null) {
- updateSuppression(mGroupMap.get(oldSbn.getGroupKey()));
- }
+ mIsolatedEntries.remove(removed.getKey());
}
/**
@@ -181,9 +162,6 @@
// the close future. See b/23676310 for reference.
return;
}
- if (SPEW) {
- Log.d(TAG, "onEntryRemovedInternal: entry=" + removed + " group=" + group.groupKey);
- }
if (isGroupChild(removed.getKey(), isGroup, isGroupSummary)) {
group.children.remove(removed.getKey());
} else {
@@ -204,9 +182,6 @@
* Notify the group manager that a new entry was added
*/
public void onEntryAdded(final NotificationEntry added) {
- if (SPEW) {
- Log.d(TAG, "onEntryAdded: entry=" + added);
- }
updateIsolation(added);
onEntryAddedInternal(added);
}
@@ -220,16 +195,13 @@
String groupKey = getGroupKey(sbn);
NotificationGroup group = mGroupMap.get(groupKey);
if (group == null) {
- group = new NotificationGroup(groupKey);
+ group = new NotificationGroup();
mGroupMap.put(groupKey, group);
for (OnGroupChangeListener listener : mGroupChangeListeners) {
listener.onGroupCreated(group, groupKey);
}
}
- if (SPEW) {
- Log.d(TAG, "onEntryAddedInternal: entry=" + added + " group=" + group.groupKey);
- }
if (isGroupChild) {
NotificationEntry existing = group.children.get(added.getKey());
if (existing != null && existing != added) {
@@ -241,11 +213,9 @@
+ " added removed" + added.isRowRemoved(), new Throwable());
}
group.children.put(added.getKey(), added);
- addToPostBatchHistory(group, added);
updateSuppression(group);
} else {
group.summary = added;
- addToPostBatchHistory(group, added);
group.expanded = added.areChildrenExpanded();
updateSuppression(group);
if (!group.children.isEmpty()) {
@@ -261,27 +231,6 @@
}
}
- private void addToPostBatchHistory(NotificationGroup group, @Nullable NotificationEntry entry) {
- if (entry == null) {
- return;
- }
- boolean didAdd = group.postBatchHistory.add(new PostRecord(entry));
- if (didAdd) {
- trimPostBatchHistory(group.postBatchHistory);
- }
- }
-
- /** remove all history that's too old to be in the batch. */
- private void trimPostBatchHistory(@NonNull TreeSet<PostRecord> postBatchHistory) {
- if (postBatchHistory.size() <= 1) {
- return;
- }
- long batchStartTime = postBatchHistory.last().postTime - POST_BATCH_MAX_AGE;
- while (!postBatchHistory.isEmpty() && postBatchHistory.first().postTime < batchStartTime) {
- postBatchHistory.pollFirst();
- }
- }
-
private void onEntryBecomingChild(NotificationEntry entry) {
updateIsolation(entry);
}
@@ -290,9 +239,6 @@
if (group == null) {
return;
}
- NotificationEntry prevAlertOverride = group.alertOverride;
- group.alertOverride = getPriorityConversationAlertOverride(group);
-
int childCount = 0;
boolean hasBubbles = false;
for (NotificationEntry entry : group.children.values()) {
@@ -309,148 +255,18 @@
group.suppressed = group.summary != null && !group.expanded
&& (childCount == 1
|| (childCount == 0
- && group.summary.getSbn().getNotification().isGroupSummary()
- && (hasIsolatedChildren(group) || hasBubbles)));
-
- boolean alertOverrideChanged = prevAlertOverride != group.alertOverride;
- boolean suppressionChanged = prevSuppressed != group.suppressed;
- if (alertOverrideChanged || suppressionChanged) {
- if (DEBUG && alertOverrideChanged) {
- Log.d(TAG, group + " alertOverride was=" + prevAlertOverride + " now="
- + group.alertOverride);
- }
- if (DEBUG && suppressionChanged) {
- Log.d(TAG, group + " suppressed changed to " + group.suppressed);
- }
- if (!mIsUpdatingUnchangedGroup) {
- if (alertOverrideChanged) {
- mEventBuffer.notifyAlertOverrideChanged(group, prevAlertOverride);
- }
- if (suppressionChanged) {
- for (OnGroupChangeListener listener : mGroupChangeListeners) {
- listener.onGroupSuppressionChanged(group, group.suppressed);
- }
- }
- mEventBuffer.notifyGroupsChanged();
- } else {
- if (DEBUG) {
- Log.d(TAG, group + " did not notify listeners of above change(s)");
+ && group.summary.getSbn().getNotification().isGroupSummary()
+ && (hasIsolatedChildren(group) || hasBubbles)));
+ if (prevSuppressed != group.suppressed) {
+ for (OnGroupChangeListener listener : mGroupChangeListeners) {
+ if (!mIsUpdatingUnchangedGroup) {
+ listener.onGroupSuppressionChanged(group, group.suppressed);
+ listener.onGroupsChanged();
}
}
}
}
- /**
- * Finds the isolated logical child of this group which is should be alerted instead.
- *
- * Notifications from priority conversations are isolated from their groups to make them more
- * prominent, however apps may post these with a GroupAlertBehavior that has the group receiving
- * the alert. This would lead to the group alerting even though the conversation that was
- * updated was not actually a part of that group. This method finds the best priority
- * conversation in this situation, if there is one, so they can be set as the alertOverride of
- * the group.
- *
- * @param group the group to check
- * @return the entry which should receive the alert instead of the group, if any.
- */
- @Nullable
- private NotificationEntry getPriorityConversationAlertOverride(NotificationGroup group) {
- // GOAL: if there is a priority child which wouldn't alert based on its groupAlertBehavior,
- // but which should be alerting (because priority conversations are isolated), find it.
- if (group == null || group.summary == null) {
- if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride: null group or summary");
- }
- return null;
- }
- if (isIsolated(group.summary.getKey())) {
- if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride: isolated group");
- }
- return null;
- }
-
- // Precondiions:
- // * Only necessary when all notifications in the group use GROUP_ALERT_SUMMARY
- // * Only necessary when at least one notification in the group is on a priority channel
- if (group.summary.getSbn().getNotification().getGroupAlertBehavior()
- != Notification.GROUP_ALERT_SUMMARY) {
- if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride: summary != GROUP_ALERT_SUMMARY");
- }
- return null;
- }
-
- // Get the important children first, copy the keys for the final importance check,
- // then add the non-isolated children to the map for unified lookup.
- HashMap<String, NotificationEntry> children = getImportantConversations(group);
- if (children == null || children.isEmpty()) {
- if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride: no important conversations");
- }
- return null;
- }
- HashSet<String> importantChildKeys = new HashSet<>(children.keySet());
- children.putAll(group.children);
-
- // Ensure all children have GROUP_ALERT_SUMMARY
- for (NotificationEntry child : children.values()) {
- if (child.getSbn().getNotification().getGroupAlertBehavior()
- != Notification.GROUP_ALERT_SUMMARY) {
- if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride: "
- + "child != GROUP_ALERT_SUMMARY");
- }
- return null;
- }
- }
-
- // Create a merged post history from all the children
- TreeSet<PostRecord> combinedHistory = new TreeSet<>(group.postBatchHistory);
- for (String importantChildKey : importantChildKeys) {
- NotificationGroup importantChildGroup = mGroupMap.get(importantChildKey);
- combinedHistory.addAll(importantChildGroup.postBatchHistory);
- }
- trimPostBatchHistory(combinedHistory);
-
- // This is a streamlined implementation of the following idea:
- // * From the subset of notifications in the latest 'batch' of updates. A batch is:
- // * Notifs posted less than POST_BATCH_MAX_AGE before the most recently posted.
- // * Only including notifs newer than the second-to-last post of any notification.
- // * Find the newest child in the batch -- the with the largest 'when' value.
- // * If the newest child is a priority conversation, set that as the override.
- HashSet<String> batchKeys = new HashSet<>();
- long newestChildWhen = -1;
- NotificationEntry newestChild = null;
- // Iterate backwards through the post history, tracking the child with the smallest sort key
- for (PostRecord record : combinedHistory.descendingSet()) {
- if (batchKeys.contains(record.key)) {
- // Once you see a notification again, the batch has ended
- break;
- }
- batchKeys.add(record.key);
- NotificationEntry child = children.get(record.key);
- if (child != null) {
- long childWhen = child.getSbn().getNotification().when;
- if (newestChild == null || childWhen > newestChildWhen) {
- newestChildWhen = childWhen;
- newestChild = child;
- }
- }
- }
- if (newestChild != null && importantChildKeys.contains(newestChild.getKey())) {
- if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride: result=" + newestChild);
- }
- return newestChild;
- }
- if (SPEW) {
- Log.d(TAG, "getPriorityConversationAlertOverride: result=null, newestChild="
- + newestChild);
- }
- return null;
- }
-
private boolean hasIsolatedChildren(NotificationGroup group) {
return getNumberOfIsolatedChildren(group.summary.getSbn().getGroupKey()) != 0;
}
@@ -465,33 +281,12 @@
return count;
}
- @Nullable
- private HashMap<String, NotificationEntry> getImportantConversations(NotificationGroup group) {
- String groupKey = group.summary.getSbn().getGroupKey();
- HashMap<String, NotificationEntry> result = null;
- for (StatusBarNotification sbn : mIsolatedEntries.values()) {
- if (sbn.getGroupKey().equals(groupKey)) {
- NotificationEntry entry = mGroupMap.get(sbn.getKey()).summary;
- if (isImportantConversation(entry)) {
- if (result == null) {
- result = new HashMap<>();
- }
- result.put(sbn.getKey(), entry);
- }
- }
- }
- return result;
- }
-
/**
* Update an entry's group information
* @param entry notification entry to update
* @param oldNotification previous notification info before this update
*/
public void onEntryUpdated(NotificationEntry entry, StatusBarNotification oldNotification) {
- if (SPEW) {
- Log.d(TAG, "onEntryUpdated: entry=" + entry);
- }
onEntryUpdated(entry, oldNotification.getGroupKey(), oldNotification.isGroup(),
oldNotification.getNotification().isGroupSummary());
}
@@ -530,17 +325,7 @@
* Whether the given notification is the summary of a group that is being suppressed
*/
public boolean isSummaryOfSuppressedGroup(StatusBarNotification sbn) {
- return sbn.getNotification().isGroupSummary() && isGroupSuppressed(getGroupKey(sbn));
- }
-
- /**
- * If the given notification is a summary, get the group for it.
- */
- public NotificationGroup getGroupForSummary(StatusBarNotification sbn) {
- if (sbn.getNotification().isGroupSummary()) {
- return mGroupMap.get(getGroupKey(sbn));
- }
- return null;
+ return isGroupSuppressed(getGroupKey(sbn)) && sbn.getNotification().isGroupSummary();
}
private boolean isOnlyChild(StatusBarNotification sbn) {
@@ -760,7 +545,9 @@
if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) {
return false;
}
- if (isImportantConversation(entry)) {
+ int peopleNotificationType =
+ mPeopleNotificationIdentifier.get().getPeopleNotificationType(entry);
+ if (peopleNotificationType == PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON) {
return true;
}
if (mHeadsUpManager != null && !mHeadsUpManager.isAlerting(entry.getKey())) {
@@ -773,25 +560,18 @@
|| isGroupNotFullyVisible(notificationGroup));
}
- private boolean isImportantConversation(NotificationEntry entry) {
- int peopleNotificationType =
- mPeopleNotificationIdentifier.get().getPeopleNotificationType(entry);
- return peopleNotificationType == PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON;
- }
-
/**
* Isolate a notification from its group so that it visually shows as its own group.
*
* @param entry the notification to isolate
*/
private void isolateNotification(NotificationEntry entry) {
- if (SPEW) {
- Log.d(TAG, "isolateNotification: entry=" + entry);
- }
+ StatusBarNotification sbn = entry.getSbn();
+
// We will be isolated now, so lets update the groups
onEntryRemovedInternal(entry, entry.getSbn());
- mIsolatedEntries.put(entry.getKey(), entry.getSbn());
+ mIsolatedEntries.put(sbn.getKey(), sbn);
onEntryAddedInternal(entry);
// We also need to update the suppression of the old group, because this call comes
@@ -808,14 +588,6 @@
* Update the isolation of an entry, splitting it from the group.
*/
public void updateIsolation(NotificationEntry entry) {
- // We need to buffer a few events because we do isolation changes in 3 steps:
- // removeInternal, update mIsolatedEntries, addInternal. This means that often the
- // alertOverride will update on the removal, however processing the event in that case can
- // cause problems because the mIsolatedEntries map is not in its final state, so the event
- // listener may be unable to correctly determine the true state of the group. By delaying
- // the alertOverride change until after the add phase, we can ensure that listeners only
- // have to handle a consistent state.
- mEventBuffer.startBuffering();
boolean isIsolated = isIsolated(entry.getSbn().getKey());
if (shouldIsolate(entry)) {
if (!isIsolated) {
@@ -824,7 +596,6 @@
} else if (isIsolated) {
stopIsolatingNotification(entry);
}
- mEventBuffer.flushAndStopBuffering();
}
/**
@@ -833,15 +604,15 @@
* @param entry the notification to un-isolate
*/
private void stopIsolatingNotification(NotificationEntry entry) {
- if (SPEW) {
- Log.d(TAG, "stopIsolatingNotification: entry=" + entry);
- }
- // not isolated anymore, we need to update the groups
- onEntryRemovedInternal(entry, entry.getSbn());
- mIsolatedEntries.remove(entry.getKey());
- onEntryAddedInternal(entry);
- for (OnGroupChangeListener listener : mGroupChangeListeners) {
- listener.onGroupsChanged();
+ StatusBarNotification sbn = entry.getSbn();
+ if (isIsolated(sbn.getKey())) {
+ // not isolated anymore, we need to update the groups
+ onEntryRemovedInternal(entry, entry.getSbn());
+ mIsolatedEntries.remove(sbn.getKey());
+ onEntryAddedInternal(entry);
+ for (OnGroupChangeListener listener : mGroupChangeListeners) {
+ listener.onGroupsChanged();
+ }
}
}
@@ -877,154 +648,33 @@
}
/**
- * A record of a notification being posted, containing the time of the post and the key of the
- * notification entry. These are stored in a TreeSet by the NotificationGroup and used to
- * calculate a batch of notifications.
- */
- public static class PostRecord implements Comparable<PostRecord> {
- public final long postTime;
- public final String key;
-
- /** constructs a record containing the post time and key from the notification entry */
- public PostRecord(@NonNull NotificationEntry entry) {
- this.postTime = entry.getSbn().getPostTime();
- this.key = entry.getKey();
- }
-
- @Override
- public int compareTo(PostRecord o) {
- int postTimeComparison = Long.compare(this.postTime, o.postTime);
- return postTimeComparison == 0
- ? String.CASE_INSENSITIVE_ORDER.compare(this.key, o.key)
- : postTimeComparison;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- PostRecord that = (PostRecord) o;
- return postTime == that.postTime && key.equals(that.key);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(postTime, key);
- }
- }
-
- /**
* Represents a notification group in the notification shade.
*/
public static class NotificationGroup {
- public final String groupKey;
public final HashMap<String, NotificationEntry> children = new HashMap<>();
- public final TreeSet<PostRecord> postBatchHistory = new TreeSet<>();
public NotificationEntry summary;
public boolean expanded;
/**
* Is this notification group suppressed, i.e its summary is hidden
*/
public boolean suppressed;
- /**
- * The child (which is isolated from this group) to which the alert should be transferred,
- * due to priority conversations.
- */
- public NotificationEntry alertOverride;
-
- NotificationGroup(String groupKey) {
- this.groupKey = groupKey;
- }
@Override
public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append(" groupKey: ").append(groupKey);
- sb.append("\n summary:");
- appendEntry(sb, summary);
- sb.append("\n children size: ").append(children.size());
+ String result = " summary:\n "
+ + (summary != null ? summary.getSbn() : "null")
+ + (summary != null && summary.getDebugThrowable() != null
+ ? Log.getStackTraceString(summary.getDebugThrowable())
+ : "");
+ result += "\n children size: " + children.size();
for (NotificationEntry child : children.values()) {
- appendEntry(sb, child);
+ result += "\n " + child.getSbn()
+ + (child.getDebugThrowable() != null
+ ? Log.getStackTraceString(child.getDebugThrowable())
+ : "");
}
- sb.append("\n alertOverride:");
- appendEntry(sb, alertOverride);
- sb.append("\n summary suppressed: ").append(suppressed);
- return sb.toString();
- }
-
- private void appendEntry(StringBuilder sb, NotificationEntry entry) {
- sb.append("\n ").append(entry != null ? entry.getSbn() : "null");
- if (entry != null && entry.getDebugThrowable() != null) {
- sb.append(Log.getStackTraceString(entry.getDebugThrowable()));
- }
- }
- }
-
- /**
- * This class is a toggleable buffer for a subset of events of {@link OnGroupChangeListener}.
- * When buffering, instead of notifying the listeners it will set internal state that will allow
- * it to notify listeners of those events later
- */
- private class EventBuffer {
- private final HashMap<String, NotificationEntry> mOldAlertOverrideByGroup = new HashMap<>();
- private boolean mIsBuffering = false;
- private boolean mDidGroupsChange = false;
-
- void notifyAlertOverrideChanged(NotificationGroup group,
- NotificationEntry oldAlertOverride) {
- if (mIsBuffering) {
- // The value in this map is the override before the event. If there is an entry
- // already in the map, then we are effectively coalescing two events, which means
- // we need to preserve the original initial value.
- mOldAlertOverrideByGroup.putIfAbsent(group.groupKey, oldAlertOverride);
- } else {
- for (OnGroupChangeListener listener : mGroupChangeListeners) {
- listener.onGroupAlertOverrideChanged(group, oldAlertOverride,
- group.alertOverride);
- }
- }
- }
-
- void notifyGroupsChanged() {
- if (mIsBuffering) {
- mDidGroupsChange = true;
- } else {
- for (OnGroupChangeListener listener : mGroupChangeListeners) {
- listener.onGroupsChanged();
- }
- }
- }
-
- void startBuffering() {
- mIsBuffering = true;
- }
-
- void flushAndStopBuffering() {
- // stop buffering so that we can call our own helpers
- mIsBuffering = false;
- // alert all group alert override changes for groups that were not removed
- for (Map.Entry<String, NotificationEntry> entry : mOldAlertOverrideByGroup.entrySet()) {
- NotificationGroup group = mGroupMap.get(entry.getKey());
- if (group == null) {
- // The group can be null if this alertOverride changed before the group was
- // permanently removed, meaning that there's no guarantee that listeners will
- // that field clear.
- continue;
- }
- NotificationEntry oldAlertOverride = entry.getValue();
- if (group.alertOverride == oldAlertOverride) {
- // If the final alertOverride equals the initial, it means we coalesced two
- // events which undid the change, so we can drop it entirely.
- continue;
- }
- notifyAlertOverrideChanged(group, oldAlertOverride);
- }
- mOldAlertOverrideByGroup.clear();
- // alert that groups changed
- if (mDidGroupsChange) {
- notifyGroupsChanged();
- mDidGroupsChange = false;
- }
+ result += "\n summary suppressed: " + suppressed;
+ return result;
}
}
@@ -1064,18 +714,6 @@
boolean suppressed) {}
/**
- * The alert override of a group has changed.
- *
- * @param group the group that has changed
- * @param oldAlertOverride the previous notification to which the group's alerts were sent
- * @param newAlertOverride the notification to which the group's alerts should now be sent
- */
- default void onGroupAlertOverrideChanged(
- NotificationGroup group,
- @Nullable NotificationEntry oldAlertOverride,
- @Nullable NotificationEntry newAlertOverride) {}
-
- /**
* A group of children just received a summary notification and should therefore become
* children of it.
*
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 7d586ba..c1a5f14 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
@@ -400,10 +400,14 @@
}
if (view instanceof FooterView) {
- viewState.yTranslation = Math.min(viewState.yTranslation,
- ambientState.getStackHeight());
- // Hide footer if shelf is showing
- viewState.hidden = algorithmState.firstViewInShelf != null;
+ final boolean isShelfShowing = algorithmState.firstViewInShelf != null;
+
+ final float footerEnd = viewState.yTranslation + view.getIntrinsicHeight();
+ final boolean noSpaceForFooter = footerEnd > ambientState.getStackHeight();
+
+ viewState.hidden = isShelfShowing
+ || (!ambientState.isExpansionChanging() && noSpaceForFooter);
+
} else if (view != ambientState.getTrackedHeadsUpRow()) {
if (ambientState.isExpansionChanging()) {
// Show all views. Views below the shelf will later be clipped (essentially hidden)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index 9787a944..3181f52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -22,12 +22,12 @@
import android.os.SystemClock;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
-import android.util.Log;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -41,21 +41,17 @@
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import java.util.ArrayList;
-import java.util.List;
import java.util.Objects;
/**
* A helper class dealing with the alert interactions between {@link NotificationGroupManagerLegacy}
* and {@link HeadsUpManager}. In particular, this class deals with keeping
- * the correct notification in a group alerting based off the group suppression and alertOverride.
+ * the correct notification in a group alerting based off the group suppression.
*/
public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedListener,
StateListener {
private static final long ALERT_TRANSFER_TIMEOUT = 300;
- private static final String TAG = "NotifGroupAlertTransfer";
- private static final boolean DEBUG = StatusBar.DEBUG;
- private static final boolean SPEW = StatusBar.SPEW;
/**
* The list of entries containing group alert metadata for each group. Keyed by group key.
@@ -146,98 +142,41 @@
@Override
public void onGroupSuppressionChanged(NotificationGroup group, boolean suppressed) {
- if (DEBUG) {
- Log.d(TAG, "!! onGroupSuppressionChanged: group.summary=" + group.summary
- + " suppressed=" + suppressed);
+ if (suppressed) {
+ if (mHeadsUpManager.isAlerting(group.summary.getKey())) {
+ handleSuppressedSummaryAlerted(group.summary, mHeadsUpManager);
+ }
+ } else {
+ // Group summary can be null if we are no longer suppressed because the summary was
+ // removed. In that case, we don't need to alert the summary.
+ if (group.summary == null) {
+ return;
+ }
+ GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(mGroupManager.getGroupKey(
+ group.summary.getSbn()));
+ // Group is no longer suppressed. We should check if we need to transfer the alert
+ // back to the summary now that it's no longer suppressed.
+ if (groupAlertEntry.mAlertSummaryOnNextAddition) {
+ if (!mHeadsUpManager.isAlerting(group.summary.getKey())) {
+ alertNotificationWhenPossible(group.summary, mHeadsUpManager);
+ }
+ groupAlertEntry.mAlertSummaryOnNextAddition = false;
+ } else {
+ checkShouldTransferBack(groupAlertEntry);
+ }
}
- NotificationEntry oldAlertOverride = group.alertOverride;
- onGroupChanged(group, oldAlertOverride);
- }
-
- @Override
- public void onGroupAlertOverrideChanged(NotificationGroup group,
- @Nullable NotificationEntry oldAlertOverride,
- @Nullable NotificationEntry newAlertOverride) {
- if (DEBUG) {
- Log.d(TAG, "!! onGroupAlertOverrideChanged: group.summary=" + group.summary
- + " oldAlertOverride=" + oldAlertOverride
- + " newAlertOverride=" + newAlertOverride);
- }
- onGroupChanged(group, oldAlertOverride);
}
};
- /**
- * Called when either the suppressed or alertOverride fields of the group changed
- *
- * @param group the group which changed
- * @param oldAlertOverride the previous value of group.alertOverride
- */
- private void onGroupChanged(NotificationGroup group,
- NotificationEntry oldAlertOverride) {
- // Group summary can be null if we are no longer suppressed because the summary was
- // removed. In that case, we don't need to alert the summary.
- if (group.summary == null) {
- if (DEBUG) {
- Log.d(TAG, "onGroupChanged: summary is null");
- }
- return;
- }
- if (group.suppressed || group.alertOverride != null) {
- checkForForwardAlertTransfer(group.summary, oldAlertOverride);
- } else {
- if (DEBUG) {
- Log.d(TAG, "onGroupChanged: maybe transfer back");
- }
- GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(mGroupManager.getGroupKey(
- group.summary.getSbn()));
- // Group is no longer suppressed or overridden.
- // We should check if we need to transfer the alert back to the summary.
- if (groupAlertEntry.mAlertSummaryOnNextAddition) {
- if (!mHeadsUpManager.isAlerting(group.summary.getKey())) {
- alertNotificationWhenPossible(group.summary);
- }
- groupAlertEntry.mAlertSummaryOnNextAddition = false;
- } else {
- checkShouldTransferBack(groupAlertEntry);
- }
- }
- }
-
@Override
public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
- if (DEBUG) {
- Log.d(TAG, "!! onHeadsUpStateChanged: entry=" + entry + " isHeadsUp=" + isHeadsUp);
- }
- if (isHeadsUp && entry.getSbn().getNotification().isGroupSummary()) {
- // a group summary is alerting; trigger the forward transfer checks
- checkForForwardAlertTransfer(entry, /* oldAlertOverride */ null);
- }
+ onAlertStateChanged(entry, isHeadsUp, mHeadsUpManager);
}
- /**
- * Handles changes in a group's suppression or alertOverride, but where at least one of those
- * conditions is still true (either the group is suppressed, the group has an alertOverride,
- * or both). The method determined which kind of child needs to receive the alert, finds the
- * entry currently alerting, and makes the transfer.
- *
- * Internally, this is handled with two main cases: the override needs the alert, or there is
- * no override but the summary is suppressed (so an isolated child needs the alert).
- *
- * @param summary the notification entry of the summary of the logical group.
- * @param oldAlertOverride the former value of group.alertOverride, before whatever event
- * required us to check for for a transfer condition.
- */
- private void checkForForwardAlertTransfer(NotificationEntry summary,
- NotificationEntry oldAlertOverride) {
- if (DEBUG) {
- Log.d(TAG, "checkForForwardAlertTransfer: enter");
- }
- NotificationGroup group = mGroupManager.getGroupForSummary(summary.getSbn());
- if (group != null && group.alertOverride != null) {
- handleOverriddenSummaryAlerted(summary);
- } else if (mGroupManager.isSummaryOfSuppressedGroup(summary.getSbn())) {
- handleSuppressedSummaryAlerted(summary, oldAlertOverride);
+ private void onAlertStateChanged(NotificationEntry entry, boolean isAlerting,
+ AlertingNotificationManager alertManager) {
+ if (isAlerting && mGroupManager.isSummaryOfSuppressedGroup(entry.getSbn())) {
+ handleSuppressedSummaryAlerted(entry, alertManager);
}
}
@@ -247,16 +186,9 @@
// see as early as we can if we need to abort a transfer.
@Override
public void onPendingEntryAdded(NotificationEntry entry) {
- if (DEBUG) {
- Log.d(TAG, "!! onPendingEntryAdded: entry=" + entry);
- }
String groupKey = mGroupManager.getGroupKey(entry.getSbn());
GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(groupKey);
- if (groupAlertEntry != null && groupAlertEntry.mGroup.alertOverride == null) {
- // new pending group entries require us to transfer back from the child to the
- // group, but alertOverrides are only present in very limited circumstances, so
- // while it's possible the group should ALSO alert, the previous detection which set
- // this alertOverride won't be invalidated by this notification added to this group.
+ if (groupAlertEntry != null) {
checkShouldTransferBack(groupAlertEntry);
}
}
@@ -330,128 +262,43 @@
}
/**
- * Handles the scenario where a summary that has been suppressed is itself, or has a former
- * alertOverride (in the form of an isolated logical child) which was alerted. A suppressed
+ * Handles the scenario where a summary that has been suppressed is alerted. A suppressed
* summary should for all intents and purposes be invisible to the user and as a result should
* not alert. When this is the case, it is our responsibility to pass the alert to the
* appropriate child which will be the representative notification alerting for the group.
*
- * @param summary the summary that is suppressed and (potentially) alerting
- * @param oldAlertOverride the alertOverride before whatever event triggered this method. If
- * the alert override was removed, this will be the entry that should
- * be transferred back from.
+ * @param summary the summary that is suppressed and alerting
+ * @param alertManager the alert manager that manages the alerting summary
*/
private void handleSuppressedSummaryAlerted(@NonNull NotificationEntry summary,
- NotificationEntry oldAlertOverride) {
- if (DEBUG) {
- Log.d(TAG, "handleSuppressedSummaryAlerted: summary=" + summary);
- }
+ @NonNull AlertingNotificationManager alertManager) {
+ StatusBarNotification sbn = summary.getSbn();
GroupAlertEntry groupAlertEntry =
- mGroupAlertEntries.get(mGroupManager.getGroupKey(summary.getSbn()));
-
+ mGroupAlertEntries.get(mGroupManager.getGroupKey(sbn));
if (!mGroupManager.isSummaryOfSuppressedGroup(summary.getSbn())
+ || !alertManager.isAlerting(sbn.getKey())
|| groupAlertEntry == null) {
- if (DEBUG) {
- Log.d(TAG, "handleSuppressedSummaryAlerted: invalid state");
- }
- return;
- }
- boolean summaryIsAlerting = mHeadsUpManager.isAlerting(summary.getKey());
- boolean priorityIsAlerting = oldAlertOverride != null
- && mHeadsUpManager.isAlerting(oldAlertOverride.getKey());
- if (!summaryIsAlerting && !priorityIsAlerting) {
- if (DEBUG) {
- Log.d(TAG, "handleSuppressedSummaryAlerted: no summary or override alerting");
- }
return;
}
if (pendingInflationsWillAddChildren(groupAlertEntry.mGroup)) {
// New children will actually be added to this group, let's not transfer the alert.
- if (DEBUG) {
- Log.d(TAG, "handleSuppressedSummaryAlerted: pending inflations");
- }
return;
}
NotificationEntry child =
mGroupManager.getLogicalChildren(summary.getSbn()).iterator().next();
- if (summaryIsAlerting) {
- if (DEBUG) {
- Log.d(TAG, "handleSuppressedSummaryAlerted: transfer summary -> child");
+ if (child != null) {
+ if (child.getRow().keepInParent()
+ || child.isRowRemoved()
+ || child.isRowDismissed()) {
+ // The notification is actually already removed. No need to alert it.
+ return;
}
- tryTransferAlertState(summary, /*from*/ summary, /*to*/ child, groupAlertEntry);
- return;
- }
- // Summary didn't have the alert, so we're in "transfer back" territory. First, make sure
- // it's not too late to transfer back, then transfer the alert from the oldAlertOverride to
- // the isolated child which should receive the alert.
- if (!canStillTransferBack(groupAlertEntry)) {
- if (DEBUG) {
- Log.d(TAG, "handleSuppressedSummaryAlerted: transfer from override: too late");
+ if (!alertManager.isAlerting(child.getKey()) && onlySummaryAlerts(summary)) {
+ groupAlertEntry.mLastAlertTransferTime = SystemClock.elapsedRealtime();
}
- return;
- }
-
- if (DEBUG) {
- Log.d(TAG, "handleSuppressedSummaryAlerted: transfer override -> child");
- }
- tryTransferAlertState(summary, /*from*/ oldAlertOverride, /*to*/ child, groupAlertEntry);
- }
-
- /**
- * Checks for and handles the scenario where the given entry is the summary of a group which
- * has an alertOverride, and either the summary itself or one of its logical isolated children
- * is currently alerting (which happens if the summary is suppressed).
- */
- private void handleOverriddenSummaryAlerted(NotificationEntry summary) {
- if (DEBUG) {
- Log.d(TAG, "handleOverriddenSummaryAlerted: summary=" + summary);
- }
- GroupAlertEntry groupAlertEntry =
- mGroupAlertEntries.get(mGroupManager.getGroupKey(summary.getSbn()));
- NotificationGroup group = mGroupManager.getGroupForSummary(summary.getSbn());
- if (group == null || group.alertOverride == null || groupAlertEntry == null) {
- if (DEBUG) {
- Log.d(TAG, "handleOverriddenSummaryAlerted: invalid state");
- }
- return;
- }
- boolean summaryIsAlerting = mHeadsUpManager.isAlerting(summary.getKey());
- if (summaryIsAlerting) {
- if (DEBUG) {
- Log.d(TAG, "handleOverriddenSummaryAlerted: transfer summary -> override");
- }
- tryTransferAlertState(summary, /*from*/ summary, group.alertOverride, groupAlertEntry);
- return;
- }
- // Summary didn't have the alert, so we're in "transfer back" territory. First, make sure
- // it's not too late to transfer back, then remove the alert from any of the logical
- // children, and if one of them was alerting, we can alert the override.
- if (!canStillTransferBack(groupAlertEntry)) {
- if (DEBUG) {
- Log.d(TAG, "handleOverriddenSummaryAlerted: transfer from child: too late");
- }
- return;
- }
- List<NotificationEntry> children = mGroupManager.getLogicalChildren(summary.getSbn());
- if (children == null) {
- if (DEBUG) {
- Log.d(TAG, "handleOverriddenSummaryAlerted: no children");
- }
- return;
- }
- children.remove(group.alertOverride); // do not release the alert on our desired destination
- boolean releasedChild = releaseChildAlerts(children);
- if (releasedChild) {
- if (DEBUG) {
- Log.d(TAG, "handleOverriddenSummaryAlerted: transfer child -> override");
- }
- tryTransferAlertState(summary, /*from*/ null, group.alertOverride, groupAlertEntry);
- } else {
- if (DEBUG) {
- Log.d(TAG, "handleOverriddenSummaryAlerted: no child alert released");
- }
+ transferAlertState(summary, child, alertManager);
}
}
@@ -460,37 +307,14 @@
* immediately to have the incorrect one up as short as possible. The second should alert
* when possible.
*
- * @param summary entry of the summary
* @param fromEntry entry to transfer alert from
* @param toEntry entry to transfer to
+ * @param alertManager alert manager for the alert type
*/
- private void tryTransferAlertState(
- NotificationEntry summary,
- NotificationEntry fromEntry,
- NotificationEntry toEntry,
- GroupAlertEntry groupAlertEntry) {
- if (toEntry != null) {
- if (toEntry.getRow().keepInParent()
- || toEntry.isRowRemoved()
- || toEntry.isRowDismissed()) {
- // The notification is actually already removed. No need to alert it.
- return;
- }
- if (!mHeadsUpManager.isAlerting(toEntry.getKey()) && onlySummaryAlerts(summary)) {
- groupAlertEntry.mLastAlertTransferTime = SystemClock.elapsedRealtime();
- }
- if (DEBUG) {
- Log.d(TAG, "transferAlertState: fromEntry=" + fromEntry + " toEntry=" + toEntry);
- }
- transferAlertState(fromEntry, toEntry);
- }
- }
- private void transferAlertState(@Nullable NotificationEntry fromEntry,
- @NonNull NotificationEntry toEntry) {
- if (fromEntry != null) {
- mHeadsUpManager.removeNotification(fromEntry.getKey(), true /* releaseImmediately */);
- }
- alertNotificationWhenPossible(toEntry);
+ private void transferAlertState(@NonNull NotificationEntry fromEntry, @NonNull NotificationEntry toEntry,
+ @NonNull AlertingNotificationManager alertManager) {
+ alertManager.removeNotification(fromEntry.getKey(), true /* releaseImmediately */);
+ alertNotificationWhenPossible(toEntry, alertManager);
}
/**
@@ -502,13 +326,11 @@
* more children are coming. Thus, if a child is added within a certain timeframe after we
* transfer, we back out and alert the summary again.
*
- * An alert can only transfer back within a small window of time after a transfer away from the
- * summary to a child happened.
- *
* @param groupAlertEntry group alert entry to check
*/
private void checkShouldTransferBack(@NonNull GroupAlertEntry groupAlertEntry) {
- if (canStillTransferBack(groupAlertEntry)) {
+ if (SystemClock.elapsedRealtime() - groupAlertEntry.mLastAlertTransferTime
+ < ALERT_TRANSFER_TIMEOUT) {
NotificationEntry summary = groupAlertEntry.mGroup.summary;
if (!onlySummaryAlerts(summary)) {
@@ -516,17 +338,30 @@
}
ArrayList<NotificationEntry> children = mGroupManager.getLogicalChildren(
summary.getSbn());
- int numActiveChildren = children.size();
+ int numChildren = children.size();
int numPendingChildren = getPendingChildrenNotAlerting(groupAlertEntry.mGroup);
- int numChildren = numActiveChildren + numPendingChildren;
+ numChildren += numPendingChildren;
if (numChildren <= 1) {
return;
}
- boolean releasedChild = releaseChildAlerts(children);
+ boolean releasedChild = false;
+ for (int i = 0; i < children.size(); i++) {
+ NotificationEntry entry = children.get(i);
+ if (onlySummaryAlerts(entry) && mHeadsUpManager.isAlerting(entry.getKey())) {
+ releasedChild = true;
+ mHeadsUpManager.removeNotification(
+ entry.getKey(), true /* releaseImmediately */);
+ }
+ if (mPendingAlerts.containsKey(entry.getKey())) {
+ // This is the child that would've been removed if it was inflated.
+ releasedChild = true;
+ mPendingAlerts.get(entry.getKey()).mAbortOnInflation = true;
+ }
+ }
if (releasedChild && !mHeadsUpManager.isAlerting(summary.getKey())) {
- boolean notifyImmediately = numActiveChildren > 1;
+ boolean notifyImmediately = (numChildren - numPendingChildren) > 1;
if (notifyImmediately) {
- alertNotificationWhenPossible(summary);
+ alertNotificationWhenPossible(summary, mHeadsUpManager);
} else {
// Should wait until the pending child inflates before alerting.
groupAlertEntry.mAlertSummaryOnNextAddition = true;
@@ -536,61 +371,25 @@
}
}
- private boolean canStillTransferBack(@NonNull GroupAlertEntry groupAlertEntry) {
- return SystemClock.elapsedRealtime() - groupAlertEntry.mLastAlertTransferTime
- < ALERT_TRANSFER_TIMEOUT;
- }
-
- private boolean releaseChildAlerts(List<NotificationEntry> children) {
- boolean releasedChild = false;
- if (SPEW) {
- Log.d(TAG, "releaseChildAlerts: numChildren=" + children.size());
- }
- for (int i = 0; i < children.size(); i++) {
- NotificationEntry entry = children.get(i);
- if (SPEW) {
- Log.d(TAG, "releaseChildAlerts: checking i=" + i + " entry=" + entry
- + " onlySummaryAlerts=" + onlySummaryAlerts(entry)
- + " isAlerting=" + mHeadsUpManager.isAlerting(entry.getKey())
- + " isPendingAlert=" + mPendingAlerts.containsKey(entry.getKey()));
- }
- if (onlySummaryAlerts(entry) && mHeadsUpManager.isAlerting(entry.getKey())) {
- releasedChild = true;
- mHeadsUpManager.removeNotification(
- entry.getKey(), true /* releaseImmediately */);
- }
- if (mPendingAlerts.containsKey(entry.getKey())) {
- // This is the child that would've been removed if it was inflated.
- releasedChild = true;
- mPendingAlerts.get(entry.getKey()).mAbortOnInflation = true;
- }
- }
- if (SPEW) {
- Log.d(TAG, "releaseChildAlerts: didRelease=" + releasedChild);
- }
- return releasedChild;
- }
-
/**
* Tries to alert the notification. If its content view is not inflated, we inflate and continue
* when the entry finishes inflating the view.
*
* @param entry entry to show
+ * @param alertManager alert manager for the alert type
*/
- private void alertNotificationWhenPossible(@NonNull NotificationEntry entry) {
- @InflationFlag int contentFlag = mHeadsUpManager.getContentFlag();
+ private void alertNotificationWhenPossible(@NonNull NotificationEntry entry,
+ @NonNull AlertingNotificationManager alertManager) {
+ @InflationFlag int contentFlag = alertManager.getContentFlag();
final RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
if ((params.getContentViews() & contentFlag) == 0) {
- if (DEBUG) {
- Log.d(TAG, "alertNotificationWhenPossible: async requestRebind entry=" + entry);
- }
mPendingAlerts.put(entry.getKey(), new PendingAlertInfo(entry));
params.requireContentViews(contentFlag);
mRowContentBindStage.requestRebind(entry, en -> {
PendingAlertInfo alertInfo = mPendingAlerts.remove(entry.getKey());
if (alertInfo != null) {
if (alertInfo.isStillValid()) {
- alertNotificationWhenPossible(entry);
+ alertNotificationWhenPossible(entry, mHeadsUpManager);
} else {
// The transfer is no longer valid. Free the content.
mRowContentBindStage.getStageParams(entry).markContentViewsFreeable(
@@ -601,16 +400,10 @@
});
return;
}
- if (mHeadsUpManager.isAlerting(entry.getKey())) {
- if (DEBUG) {
- Log.d(TAG, "alertNotificationWhenPossible: continue alerting entry=" + entry);
- }
- mHeadsUpManager.updateNotification(entry.getKey(), true /* alert */);
+ if (alertManager.isAlerting(entry.getKey())) {
+ alertManager.updateNotification(entry.getKey(), true /* alert */);
} else {
- if (DEBUG) {
- Log.d(TAG, "alertNotificationWhenPossible: start alerting entry=" + entry);
- }
- mHeadsUpManager.showNotification(entry);
+ alertManager.showNotification(entry);
}
}
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 c79e503..9c08b89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -22,7 +22,6 @@
import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
import static androidx.constraintlayout.widget.ConstraintSet.START;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
@@ -1730,7 +1729,6 @@
return;
}
mExpectingSynthesizedDown = true;
- InteractionJankMonitor.getInstance().begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
onTrackingStarted();
updatePanelExpanded();
}
@@ -2698,6 +2696,7 @@
mConversationNotificationManager.onNotificationPanelExpandStateChanged(isFullyCollapsed());
mIsExpanding = false;
mMediaHierarchyManager.setCollapsingShadeFromQS(false);
+ mMediaHierarchyManager.setQsExpanded(mQsExpanded);
if (isFullyCollapsed()) {
DejankUtils.postAfterTraversal(new Runnable() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 4714c4b..2649309 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -42,6 +42,7 @@
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -139,6 +140,7 @@
*/
private boolean mInstantExpanding;
private boolean mAnimateAfterExpanding;
+ private boolean mIsFlinging;
PanelBar mBar;
@@ -596,6 +598,7 @@
notifyExpandingFinished();
return;
}
+ mIsFlinging = true;
mOverExpandedBeforeFling = getOverExpansionAmount() > 0f;
ValueAnimator animator = createHeightAnimator(target);
mFlingTarget = target;
@@ -635,6 +638,12 @@
private boolean mCancelled;
@Override
+ public void onAnimationStart(Animator animation) {
+ InteractionJankMonitor.getInstance()
+ .begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ }
+
+ @Override
public void onAnimationCancel(Animator animation) {
mCancelled = true;
}
@@ -679,6 +688,7 @@
}
private void onFlingEnd(boolean cancelled) {
+ mIsFlinging = false;
setAnimator(null);
mKeyguardStateController.notifyPanelFlingEnd();
if (!cancelled) {
@@ -751,6 +761,16 @@
if (isNaN(h)) {
Log.wtf(TAG, "ExpandedHeight set to NaN");
}
+ if (mAmbientState.isExpansionChanging()
+ && !mIsFlinging // Fling already uses interpolated height from end of swipe
+ && !mAmbientState.isOnKeyguard()
+ && !mAmbientState.isDozing()
+ && !mAmbientState.isPulsing()) {
+ final float fraction = h / mView.getHeight();
+ final float interpolatedFraction = new PathInterpolator(0.2f, 0.8f, 0.8f, 1f)
+ .getInterpolation(fraction);
+ h = interpolatedFraction * mView.getHeight();
+ }
maybeOverScrollForShadeFlingOpen(h);
if (mExpandLatencyTracking && h != 0f) {
DejankUtils.postAfterTraversal(
@@ -1398,11 +1418,14 @@
case MotionEvent.ACTION_CANCEL:
addMovement(event);
endMotionEvent(event, x, y, false /* forceCancel */);
- InteractionJankMonitor monitor = InteractionJankMonitor.getInstance();
- if (event.getActionMasked() == MotionEvent.ACTION_UP) {
- monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
- } else {
- monitor.cancel(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ // mHeightAnimator is null, there is no remaining frame, ends instrumenting.
+ if (mHeightAnimator == null) {
+ InteractionJankMonitor monitor = InteractionJankMonitor.getInstance();
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ } else {
+ monitor.cancel(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ }
}
break;
}
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 6ef4663..aaef739 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -148,6 +148,7 @@
import com.android.systemui.SystemUI;
import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.DelegateLaunchAnimatorController;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.camera.CameraIntents;
@@ -2785,13 +2786,8 @@
final boolean afterKeyguardGone = mActivityIntentHelper.wouldLaunchResolverActivity(
intent, mLockscreenUserManager.getCurrentUserId());
- ActivityLaunchAnimator.Controller animController = null;
- if (animationController != null) {
- animController = dismissShade ? new StatusBarLaunchAnimatorController(
- animationController, this, true /* isLaunchForActivity */)
- : animationController;
- }
- final ActivityLaunchAnimator.Controller animCallbackForLambda = animController;
+ ActivityLaunchAnimator.Controller animController = wrapAnimationController(
+ animationController, dismissShade);
// If we animate, we will dismiss the shade only once the animation is done. This is taken
// care of by the StatusBarLaunchAnimationController.
@@ -2804,7 +2800,7 @@
intent.addFlags(flags);
int[] result = new int[]{ActivityManager.START_CANCELED};
- mActivityLaunchAnimator.startIntentWithAnimation(animCallbackForLambda,
+ mActivityLaunchAnimator.startIntentWithAnimation(animController,
areLaunchAnimationsEnabled(), (adapter) -> {
ActivityOptions options = new ActivityOptions(
getActivityOptions(mDisplayId, adapter));
@@ -2858,6 +2854,46 @@
afterKeyguardGone, true /* deferred */);
}
+ @Nullable
+ private ActivityLaunchAnimator.Controller wrapAnimationController(
+ @Nullable ActivityLaunchAnimator.Controller animationController, boolean dismissShade) {
+ if (animationController == null) {
+ return null;
+ }
+
+ View rootView = animationController.getLaunchContainer().getRootView();
+ if (rootView == mSuperStatusBarViewFactory.getStatusBarWindowView()) {
+ // We are animating a view in the status bar. We have to make sure that the status bar
+ // window matches the full screen during the animation and that we are expanding the
+ // view below the other status bar text.
+ animationController.setLaunchContainer(
+ mStatusBarWindowController.getLaunchAnimationContainer());
+
+ return new DelegateLaunchAnimatorController(animationController) {
+ @Override
+ public void onLaunchAnimationStart(boolean isExpandingFullyAbove) {
+ getDelegate().onLaunchAnimationStart(isExpandingFullyAbove);
+ mStatusBarWindowController.setLaunchAnimationRunning(true);
+ }
+
+ @Override
+ public void onLaunchAnimationEnd(boolean isExpandingFullyAbove) {
+ getDelegate().onLaunchAnimationEnd(isExpandingFullyAbove);
+ mStatusBarWindowController.setLaunchAnimationRunning(false);
+ }
+ };
+ }
+
+ if (dismissShade && rootView == mNotificationShadeWindowView) {
+ // We are animating a view in the shade. We have to make sure that we collapse it when
+ // the animation ends or is cancelled.
+ return new StatusBarLaunchAnimatorController(animationController, this,
+ true /* isLaunchForActivity */);
+ }
+
+ return animationController;
+ }
+
public void readyForKeyguardDone() {
mStatusBarKeyguardViewManager.readyForKeyguardDone();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index f403cc94..1ef84701 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -591,6 +591,8 @@
if (mStatusBar.isInLaunchTransition()
|| mKeyguardStateController.isFlingingToDismissKeyguard()) {
+ final boolean wasFlingingToDismissKeyguard =
+ mKeyguardStateController.isFlingingToDismissKeyguard();
mStatusBar.fadeKeyguardAfterLaunchTransition(new Runnable() {
@Override
public void run() {
@@ -604,6 +606,11 @@
public void run() {
mStatusBar.hideKeyguard();
mNotificationShadeWindowController.setKeyguardFadingAway(false);
+
+ if (wasFlingingToDismissKeyguard) {
+ mStatusBar.finishKeyguardFadingAway();
+ }
+
mViewMediatorCallback.keyguardGone();
executeAfterKeyguardGoneAction();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 2f7278b..30b8c5c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -29,6 +29,7 @@
import android.view.ViewGroup;
import android.view.WindowManager;
+import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
@@ -51,6 +52,7 @@
private final State mCurrentState = new State();
private ViewGroup mStatusBarView;
+ private ViewGroup mLaunchAnimationContainer;
private WindowManager.LayoutParams mLp;
private final WindowManager.LayoutParams mLpChanged;
@@ -62,6 +64,8 @@
mWindowManager = windowManager;
mSuperStatusBarViewFactory = superStatusBarViewFactory;
mStatusBarView = mSuperStatusBarViewFactory.getStatusBarWindowView();
+ mLaunchAnimationContainer = mStatusBarView.findViewById(
+ R.id.status_bar_launch_animation_container);
mLpChanged = new WindowManager.LayoutParams();
mResources = resources;
@@ -124,13 +128,38 @@
apply(mCurrentState);
}
- private void applyHeight() {
- mLpChanged.height = mBarHeight;
+ /**
+ * Return the container in which we should run launch animations started from the status bar and
+ * expanding into the opening window.
+ *
+ * @see #setLaunchAnimationRunning
+ */
+ public ViewGroup getLaunchAnimationContainer() {
+ return mLaunchAnimationContainer;
+ }
+
+ /**
+ * Set whether a launch animation is currently running. If true, this will ensure that the
+ * window matches its parent height so that the animation is not clipped by the normal status
+ * bar height.
+ */
+ public void setLaunchAnimationRunning(boolean isLaunchAnimationRunning) {
+ if (isLaunchAnimationRunning == mCurrentState.mIsLaunchAnimationRunning) {
+ return;
+ }
+
+ mCurrentState.mIsLaunchAnimationRunning = isLaunchAnimationRunning;
+ apply(mCurrentState);
+ }
+
+ private void applyHeight(State state) {
+ mLpChanged.height =
+ state.mIsLaunchAnimationRunning ? ViewGroup.LayoutParams.MATCH_PARENT : mBarHeight;
}
private void apply(State state) {
applyForceStatusBarVisibleFlag(state);
- applyHeight();
+ applyHeight(state);
if (mLp != null && mLp.copyFrom(mLpChanged) != 0) {
mWindowManager.updateViewLayout(mStatusBarView, mLp);
}
@@ -138,10 +167,11 @@
private static class State {
boolean mForceStatusBarVisible;
+ boolean mIsLaunchAnimationRunning;
}
private void applyForceStatusBarVisibleFlag(State state) {
- if (state.mForceStatusBarVisible) {
+ if (state.mForceStatusBarVisible || state.mIsLaunchAnimationRunning) {
mLpChanged.privateFlags |= PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
} else {
mLpChanged.privateFlags &= ~PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index fbba09a..897d78b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -11,7 +11,8 @@
import android.view.RemoteAnimationAdapter
import android.view.RemoteAnimationTarget
import android.view.SurfaceControl
-import android.view.View
+import android.view.ViewGroup
+import android.widget.LinearLayout
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import junit.framework.Assert.assertFalse
@@ -36,8 +37,8 @@
@RunWithLooper
class ActivityLaunchAnimatorTest : SysuiTestCase() {
private val activityLaunchAnimator = ActivityLaunchAnimator(mContext)
- private val rootView = View(mContext)
- @Spy private val controller = TestLaunchAnimatorController(rootView)
+ private val launchContainer = LinearLayout(mContext)
+ @Spy private val controller = TestLaunchAnimatorController(launchContainer)
@Mock lateinit var iCallback: IRemoteAnimationFinishedCallback
@get:Rule val rule = MockitoJUnit.rule()
@@ -146,10 +147,8 @@
* outside of the main thread.
*/
private class TestLaunchAnimatorController(
- private val rootView: View
+ override var launchContainer: ViewGroup
) : ActivityLaunchAnimator.Controller {
- override fun getRootView(): View = rootView
-
override fun createAnimatorState() = ActivityLaunchAnimator.State(
top = 100,
bottom = 200,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index d86dfa5..406f40c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.animation.UniqueObjectHostView
import org.junit.Assert.assertNotNull
@@ -73,6 +74,8 @@
private lateinit var mediaCarouselController: MediaCarouselController
@Mock
private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
+ @Mock
+ private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Captor
private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)>
@JvmField
@@ -90,7 +93,8 @@
bypassController,
mediaCarouselController,
notificationLockscreenUserManager,
- wakefulnessLifecycle)
+ wakefulnessLifecycle,
+ statusBarKeyguardViewManager)
verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture())
setupHost(lockHost, MediaHierarchyManager.LOCATION_LOCKSCREEN)
setupHost(qsHost, MediaHierarchyManager.LOCATION_QS)
@@ -98,7 +102,6 @@
`when`(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
// We'll use the viewmanager to verify a few calls below, let's reset this.
clearInvocations(mediaCarouselController)
-
}
private fun setupHost(host: MediaHost, location: Int) {
@@ -125,7 +128,8 @@
observer.onStartedGoingToSleep()
clearInvocations(mediaCarouselController)
mediaHiearchyManager.qsExpansion = 0.0f
- verify(mediaCarouselController, times(0)).onDesiredLocationChanged(ArgumentMatchers.anyInt(),
+ verify(mediaCarouselController, times(0))
+ .onDesiredLocationChanged(ArgumentMatchers.anyInt(),
any(MediaHostState::class.java), anyBoolean(), anyLong(), anyLong())
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
index b85af48..47f4183 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
@@ -62,8 +62,8 @@
final View view = new View(mContext);
mRotationButton = mock(RotationButton.class);
- mRotationButtonController = spy(new RotationButtonController(mContext, 0, 0));
- mRotationButtonController.setRotationButton(mRotationButton, (visibility) -> {});
+ mRotationButtonController = spy(new RotationButtonController(mContext, 0, 0,
+ mRotationButton, (visibility) -> {}));
final KeyButtonDrawable kbd = mock(KeyButtonDrawable.class);
doReturn(view).when(mRotationButton).getCurrentView();
doReturn(true).when(mRotationButton).acceptRotationProposal();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 68ed2a5..6f0ae22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -474,6 +474,22 @@
}
@Test
+ public void transientIndication_visibleWhenDozing_ignoresFingerprintCancellation() {
+ createController();
+
+ mController.setVisible(true);
+ reset(mRotateTextViewController);
+ mController.getKeyguardCallback().onBiometricError(
+ FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED, "foo",
+ BiometricSourceType.FINGERPRINT);
+ mController.getKeyguardCallback().onBiometricError(
+ FingerprintManager.FINGERPRINT_ERROR_CANCELED, "bar",
+ BiometricSourceType.FINGERPRINT);
+
+ verifyNoTransientMessage();
+ }
+
+ @Test
public void transientIndication_swipeUpToRetry() {
createController();
String message = mContext.getString(R.string.keyguard_retry);
@@ -668,4 +684,8 @@
private void verifyTransientMessage(String message) {
verify(mRotateTextViewController).showTransient(eq(message), anyBoolean());
}
+
+ private void verifyNoTransientMessage() {
+ verify(mRotateTextViewController, never()).showTransient(any(), anyBoolean());
+ }
}
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/Android.bp b/packages/overlays/AvoidAppsInCutoutOverlay/Android.bp
new file mode 100644
index 0000000..4352c04
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/Android.bp
@@ -0,0 +1,29 @@
+//
+// 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 {
+ // 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"],
+}
+
+runtime_resource_overlay {
+ name: "AvoidAppsInCutoutOverlay",
+ theme: "DisplayCutoutAvoidAppsInCutout",
+ product_specific: true,
+}
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/AndroidManifest.xml b/packages/overlays/AvoidAppsInCutoutOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..e63c5c3
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.internal.display.cutout.emulation.avoidAppsInCutout"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="android"
+ android:category="com.android.internal.display_cutout_emulation"
+ android:priority="0"/>
+
+ <application android:label="@string/display_cutout_emulation_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values/config.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values/config.xml
new file mode 100644
index 0000000..22eabf2
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values/config.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether the display cutout region of the main built-in display should be forced to
+ black in software (to avoid aliasing or emulate a cutout that is not physically existent).
+ -->
+ <bool name="config_fillMainBuiltInDisplayCutout">false</bool>
+
+ <!-- If true, hide the display cutout with display area -->
+ <bool name="config_hideDisplayCutoutWithDisplayArea">true</bool>
+
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values/strings.xml
new file mode 100644
index 0000000..a65fd43
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values/strings.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- [CHAR_LIMIT=NONE] Developer Settings: Label for the option that masks the display cutout, i.e. avoid apps in cutout region.-->
+ <string name="display_cutout_emulation_overlay">Hide (avoid apps in cutout region)</string>
+
+</resources>
+
diff --git a/packages/overlays/NoCutoutOverlay/Android.bp b/packages/overlays/NoCutoutOverlay/Android.bp
new file mode 100644
index 0000000..78f5627
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/Android.bp
@@ -0,0 +1,30 @@
+//
+// 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 {
+ // 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"],
+}
+
+runtime_resource_overlay {
+ name: "NoCutoutOverlay",
+ theme: "DisplayCutoutNoCutout",
+ product_specific: true,
+}
diff --git a/packages/overlays/NoCutoutOverlay/AndroidManifest.xml b/packages/overlays/NoCutoutOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..c622496
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.internal.display.cutout.emulation.noCutout"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="android"
+ android:category="com.android.internal.display_cutout_emulation"
+ android:priority="0"/>
+
+ <application android:label="@string/display_cutout_emulation_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/NoCutoutOverlay/res/values/config.xml b/packages/overlays/NoCutoutOverlay/res/values/config.xml
new file mode 100644
index 0000000..9157699
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values/config.xml
@@ -0,0 +1,36 @@
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether the display cutout region of the main built-in display should be forced to
+ black in software (to avoid aliasing or emulate a cutout that is not physically existent).
+ -->
+ <bool name="config_fillMainBuiltInDisplayCutout">false</bool>
+
+ <!-- If true, and there is a cutout on the main built in display, the cutout will be masked
+ by shrinking the display such that it does not overlap the cutout area. -->
+ <bool name="config_maskMainBuiltInDisplayCutout">true</bool>
+
+ <!-- Height of the status bar -->
+ <dimen name="status_bar_height_portrait">28dp</dimen>
+ <dimen name="status_bar_height_landscape">28dp</dimen>
+
+ <!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) -->
+ <dimen name="quick_qs_offset_height">48dp</dimen>
+ <!-- Total height of QQS (quick_qs_offset_height + 128) -->
+ <dimen name="quick_qs_total_height">176dp</dimen>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values/strings.xml b/packages/overlays/NoCutoutOverlay/res/values/strings.xml
new file mode 100644
index 0000000..dd01ada
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?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.
+ -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- [CHAR_LIMIT=NONE] Developer Settings: Label for the option that masks the display cutout,
+ i.e. it shrinks the display such that the display cutout is no longer visible.-->
+ <string name="display_cutout_emulation_overlay">Hide</string>
+
+</resources>
+
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index ceb12c8..9e8b9c6 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -36,6 +36,15 @@
}
],
"file_patterns": ["ClipboardService\\.java"]
+ },
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.sensorprivacy"
+ }
+ ],
+ "file_patterns": ["SensorPrivacyService\\.java"]
}
],
"presubmit-large": [
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 206f135..5700bb36 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -5934,7 +5934,7 @@
*/
private void logForegroundServiceStateChanged(ServiceRecord r, int state, int durationMs) {
if (!ActivityManagerUtils.shouldSamplePackageForAtom(
- r.packageName, mAm.mConstants.mDefaultFgsAtomSampleRate)) {
+ r.packageName, mAm.mConstants.mFgsAtomSampleRate)) {
return;
}
FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index d8eccef..f7ce6dd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -73,10 +73,16 @@
private static final String KEY_POWER_CHECK_MAX_CPU_2 = "power_check_max_cpu_2";
private static final String KEY_POWER_CHECK_MAX_CPU_3 = "power_check_max_cpu_3";
private static final String KEY_POWER_CHECK_MAX_CPU_4 = "power_check_max_cpu_4";
- private static final String KEY_SERVICE_USAGE_INTERACTION_TIME
- = "service_usage_interaction_time";
- private static final String KEY_USAGE_STATS_INTERACTION_INTERVAL
- = "usage_stats_interaction_interval";
+ /** Used for all apps on R and earlier versions. */
+ private static final String KEY_SERVICE_USAGE_INTERACTION_TIME_PRE_S =
+ "service_usage_interaction_time";
+ private static final String KEY_SERVICE_USAGE_INTERACTION_TIME_POST_S =
+ "service_usage_interaction_time_post_s";
+ /** Used for all apps on R and earlier versions. */
+ private static final String KEY_USAGE_STATS_INTERACTION_INTERVAL_PRE_S =
+ "usage_stats_interaction_interval";
+ private static final String KEY_USAGE_STATS_INTERACTION_INTERVAL_POST_S =
+ "usage_stats_interaction_interval_post_s";
private static final String KEY_IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES =
"imperceptible_kill_exempt_packages";
private static final String KEY_IMPERCEPTIBLE_KILL_EXEMPT_PROC_STATES =
@@ -120,8 +126,10 @@
private static final int DEFAULT_POWER_CHECK_MAX_CPU_2 = 25;
private static final int DEFAULT_POWER_CHECK_MAX_CPU_3 = 10;
private static final int DEFAULT_POWER_CHECK_MAX_CPU_4 = 2;
- private static final long DEFAULT_SERVICE_USAGE_INTERACTION_TIME = 30*60*1000;
- private static final long DEFAULT_USAGE_STATS_INTERACTION_INTERVAL = 2*60*60*1000L;
+ private static final long DEFAULT_SERVICE_USAGE_INTERACTION_TIME_PRE_S = 30 * 60 * 1000;
+ private static final long DEFAULT_SERVICE_USAGE_INTERACTION_TIME_POST_S = 60 * 1000;
+ private static final long DEFAULT_USAGE_STATS_INTERACTION_INTERVAL_PRE_S = 2 * 60 * 60 * 1000;
+ private static final long DEFAULT_USAGE_STATS_INTERACTION_INTERVAL_POST_S = 10 * 60 * 1000;
private static final long DEFAULT_SERVICE_RESTART_DURATION = 1*1000;
private static final long DEFAULT_SERVICE_RESET_RUN_DURATION = 60*1000;
private static final int DEFAULT_SERVICE_RESTART_DURATION_FACTOR = 4;
@@ -303,11 +311,23 @@
// This is the amount of time an app needs to be running a foreground service before
// we will consider it to be doing interaction for usage stats.
- long SERVICE_USAGE_INTERACTION_TIME = DEFAULT_SERVICE_USAGE_INTERACTION_TIME;
+ // Only used for apps targeting pre-S versions.
+ long SERVICE_USAGE_INTERACTION_TIME_PRE_S = DEFAULT_SERVICE_USAGE_INTERACTION_TIME_PRE_S;
+
+ // This is the amount of time an app needs to be running a foreground service before
+ // we will consider it to be doing interaction for usage stats.
+ // Only used for apps targeting versions S and above.
+ long SERVICE_USAGE_INTERACTION_TIME_POST_S = DEFAULT_SERVICE_USAGE_INTERACTION_TIME_POST_S;
// Maximum amount of time we will allow to elapse before re-reporting usage stats
// interaction with foreground processes.
- long USAGE_STATS_INTERACTION_INTERVAL = DEFAULT_USAGE_STATS_INTERACTION_INTERVAL;
+ // Only used for apps targeting pre-S versions.
+ long USAGE_STATS_INTERACTION_INTERVAL_PRE_S = DEFAULT_USAGE_STATS_INTERACTION_INTERVAL_PRE_S;
+
+ // Maximum amount of time we will allow to elapse before re-reporting usage stats
+ // interaction with foreground processes.
+ // Only used for apps targeting versions S and above.
+ long USAGE_STATS_INTERACTION_INTERVAL_POST_S = DEFAULT_USAGE_STATS_INTERACTION_INTERVAL_POST_S;
// How long a service needs to be running until restarting its process
// is no longer considered to be a relaunch of the service.
@@ -460,7 +480,7 @@
*
* If the value is 0.1, 10% of the installed packages would be sampled.
*/
- volatile float mDefaultFgsAtomSampleRate = DEFAULT_FGS_ATOM_SAMPLE_RATE;
+ volatile float mFgsAtomSampleRate = DEFAULT_FGS_ATOM_SAMPLE_RATE;
private final ActivityManagerService mService;
private ContentResolver mResolver;
@@ -817,10 +837,18 @@
DEFAULT_POWER_CHECK_MAX_CPU_3);
POWER_CHECK_MAX_CPU_4 = mParser.getInt(KEY_POWER_CHECK_MAX_CPU_4,
DEFAULT_POWER_CHECK_MAX_CPU_4);
- SERVICE_USAGE_INTERACTION_TIME = mParser.getLong(KEY_SERVICE_USAGE_INTERACTION_TIME,
- DEFAULT_SERVICE_USAGE_INTERACTION_TIME);
- USAGE_STATS_INTERACTION_INTERVAL = mParser.getLong(KEY_USAGE_STATS_INTERACTION_INTERVAL,
- DEFAULT_USAGE_STATS_INTERACTION_INTERVAL);
+ SERVICE_USAGE_INTERACTION_TIME_PRE_S = mParser.getLong(
+ KEY_SERVICE_USAGE_INTERACTION_TIME_PRE_S,
+ DEFAULT_SERVICE_USAGE_INTERACTION_TIME_PRE_S);
+ SERVICE_USAGE_INTERACTION_TIME_POST_S = mParser.getLong(
+ KEY_SERVICE_USAGE_INTERACTION_TIME_POST_S,
+ DEFAULT_SERVICE_USAGE_INTERACTION_TIME_POST_S);
+ USAGE_STATS_INTERACTION_INTERVAL_PRE_S = mParser.getLong(
+ KEY_USAGE_STATS_INTERACTION_INTERVAL_PRE_S,
+ DEFAULT_USAGE_STATS_INTERACTION_INTERVAL_PRE_S);
+ USAGE_STATS_INTERACTION_INTERVAL_POST_S = mParser.getLong(
+ KEY_USAGE_STATS_INTERACTION_INTERVAL_POST_S,
+ DEFAULT_USAGE_STATS_INTERACTION_INTERVAL_POST_S);
SERVICE_RESTART_DURATION = mParser.getLong(KEY_SERVICE_RESTART_DURATION,
DEFAULT_SERVICE_RESTART_DURATION);
SERVICE_RESET_RUN_DURATION = mParser.getLong(KEY_SERVICE_RESET_RUN_DURATION,
@@ -985,7 +1013,7 @@
}
private void updateFgsAtomSamplePercent() {
- mDefaultFgsAtomSampleRate = DeviceConfig.getFloat(
+ mFgsAtomSampleRate = DeviceConfig.getFloat(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_FGS_ATOM_SAMPLE_RATE,
DEFAULT_FGS_ATOM_SAMPLE_RATE);
@@ -1135,10 +1163,14 @@
pw.println(POWER_CHECK_MAX_CPU_3);
pw.print(" "); pw.print(KEY_POWER_CHECK_MAX_CPU_4); pw.print("=");
pw.println(POWER_CHECK_MAX_CPU_4);
- pw.print(" "); pw.print(KEY_SERVICE_USAGE_INTERACTION_TIME); pw.print("=");
- pw.println(SERVICE_USAGE_INTERACTION_TIME);
- pw.print(" "); pw.print(KEY_USAGE_STATS_INTERACTION_INTERVAL); pw.print("=");
- pw.println(USAGE_STATS_INTERACTION_INTERVAL);
+ pw.print(" "); pw.print(KEY_SERVICE_USAGE_INTERACTION_TIME_PRE_S); pw.print("=");
+ pw.println(SERVICE_USAGE_INTERACTION_TIME_PRE_S);
+ pw.print(" "); pw.print(KEY_SERVICE_USAGE_INTERACTION_TIME_POST_S); pw.print("=");
+ pw.println(SERVICE_USAGE_INTERACTION_TIME_POST_S);
+ pw.print(" "); pw.print(KEY_USAGE_STATS_INTERACTION_INTERVAL_PRE_S); pw.print("=");
+ pw.println(USAGE_STATS_INTERACTION_INTERVAL_PRE_S);
+ pw.print(" "); pw.print(KEY_USAGE_STATS_INTERACTION_INTERVAL_POST_S); pw.print("=");
+ pw.println(USAGE_STATS_INTERACTION_INTERVAL_POST_S);
pw.print(" "); pw.print(KEY_SERVICE_RESTART_DURATION); pw.print("=");
pw.println(SERVICE_RESTART_DURATION);
pw.print(" "); pw.print(KEY_SERVICE_RESET_RUN_DURATION); pw.print("=");
@@ -1204,7 +1236,7 @@
pw.print(" "); pw.print(KEY_DEFAULT_FGS_STARTS_RESTRICTION_CHECK_CALLER_TARGET_SDK);
pw.print("="); pw.println(mFgsStartRestrictionCheckCallerTargetSdk);
pw.print(" "); pw.print(KEY_FGS_ATOM_SAMPLE_RATE);
- pw.print("="); pw.println(mDefaultFgsAtomSampleRate);
+ pw.print("="); pw.println(mFgsAtomSampleRate);
pw.print(" "); pw.print(KEY_PUSH_MESSAGING_OVER_QUOTA_BEHAVIOR);
pw.print("="); pw.println(mPushMessagingOverQuotaBehavior);
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 649d050..b413010 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -80,6 +80,7 @@
import android.app.usage.UsageEvents;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.EnabledSince;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -122,7 +123,7 @@
/**
* All of the code required to compute proc states and oom_adj values.
*/
-public final class OomAdjuster {
+public class OomAdjuster {
static final String TAG = "OomAdjuster";
static final String OOM_ADJ_REASON_METHOD = "updateOomAdj";
static final String OOM_ADJ_REASON_NONE = OOM_ADJ_REASON_METHOD + "_meh";
@@ -164,6 +165,14 @@
static final long CAMERA_MICROPHONE_CAPABILITY_CHANGE_ID = 136219221L;
/**
+ * For apps targeting S+, this determines whether to use a shorter timeout before elevating the
+ * standby bucket to ACTIVE when apps start a foreground service.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S)
+ static final long USE_SHORT_FGS_USAGE_INTERACTION_TIME = 183972877L;
+
+ /**
* For some direct access we need to power manager.
*/
PowerManagerInternal mLocalPowerManager;
@@ -249,7 +258,9 @@
private final PlatformCompatCache mPlatformCompatCache;
- private static class PlatformCompatCache {
+ /** Overrideable by a test */
+ @VisibleForTesting
+ static class PlatformCompatCache {
private final PlatformCompat mPlatformCompat;
private final IPlatformCompat mIPlatformCompatProxy;
private final LongSparseArray<CacheItem> mCaches = new LongSparseArray<>();
@@ -278,6 +289,20 @@
: mIPlatformCompatProxy.isChangeEnabled(changeId, app);
}
+ /**
+ * Same as {@link #isChangeEnabled(long, ApplicationInfo)} but instead of throwing a
+ * RemoteException from platform compat, it returns the default value provided.
+ */
+ boolean isChangeEnabled(long changeId, ApplicationInfo app, boolean defaultValue) {
+ try {
+ return mCacheEnabled ? mCaches.get(changeId).isChangeEnabled(app)
+ : mIPlatformCompatProxy.isChangeEnabled(changeId, app);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error reading platform compat change " + changeId, e);
+ return defaultValue;
+ }
+ }
+
void invalidate(ApplicationInfo app) {
for (int i = mCaches.size() - 1; i >= 0; i--) {
mCaches.valueAt(i).invalidate(app);
@@ -335,6 +360,12 @@
}
}
+ /** Overrideable by a test */
+ @VisibleForTesting
+ protected PlatformCompatCache getPlatformCompatCache() {
+ return mPlatformCompatCache;
+ }
+
OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids) {
this(service, processList, activeUids, createAdjusterThread());
}
@@ -383,7 +414,8 @@
mNumSlots = ((ProcessList.CACHED_APP_MAX_ADJ - ProcessList.CACHED_APP_MIN_ADJ + 1) >> 1)
/ ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
mPlatformCompatCache = new PlatformCompatCache(new long[] {
- PROCESS_CAPABILITY_CHANGE_ID, CAMERA_MICROPHONE_CAPABILITY_CHANGE_ID
+ PROCESS_CAPABILITY_CHANGE_ID, CAMERA_MICROPHONE_CAPABILITY_CHANGE_ID,
+ USE_SHORT_FGS_USAGE_INTERACTION_TIME
});
}
@@ -755,7 +787,7 @@
if (app != null) {
mPendingProcessSet.remove(app);
if (procDied) {
- mPlatformCompatCache.invalidate(app.info);
+ getPlatformCompatCache().invalidate(app.info);
}
}
}
@@ -1924,7 +1956,7 @@
boolean enabled = false;
try {
- enabled = mPlatformCompatCache.isChangeEnabled(
+ enabled = getPlatformCompatCache().isChangeEnabled(
CAMERA_MICROPHONE_CAPABILITY_CHANGE_ID, s.appInfo);
} catch (RemoteException e) {
}
@@ -2147,7 +2179,7 @@
state.bumpAllowStartFgsState(PROCESS_STATE_BOUND_TOP);
boolean enabled = false;
try {
- enabled = mPlatformCompatCache.isChangeEnabled(
+ enabled = getPlatformCompatCache().isChangeEnabled(
PROCESS_CAPABILITY_CHANGE_ID, client.info);
} catch (RemoteException e) {
}
@@ -2787,15 +2819,27 @@
} else {
state.setProcStateChanged(true);
}
- } else if (state.hasReportedInteraction() && (nowElapsed - state.getInteractionEventTime())
- > mConstants.USAGE_STATS_INTERACTION_INTERVAL) {
+ } else if (state.hasReportedInteraction()) {
+ final boolean fgsInteractionChangeEnabled = getPlatformCompatCache().isChangeEnabled(
+ USE_SHORT_FGS_USAGE_INTERACTION_TIME, app.info, false);
+ final long interactionThreshold = fgsInteractionChangeEnabled
+ ? mConstants.USAGE_STATS_INTERACTION_INTERVAL_POST_S
+ : mConstants.USAGE_STATS_INTERACTION_INTERVAL_PRE_S;
// For apps that sit around for a long time in the interactive state, we need
// to report this at least once a day so they don't go idle.
- maybeUpdateUsageStatsLSP(app, nowElapsed);
- } else if (!state.hasReportedInteraction() && (nowElapsed - state.getFgInteractionTime())
- > mConstants.SERVICE_USAGE_INTERACTION_TIME) {
+ if ((nowElapsed - state.getInteractionEventTime()) > interactionThreshold) {
+ maybeUpdateUsageStatsLSP(app, nowElapsed);
+ }
+ } else {
+ final boolean fgsInteractionChangeEnabled = getPlatformCompatCache().isChangeEnabled(
+ USE_SHORT_FGS_USAGE_INTERACTION_TIME, app.info, false);
+ final long interactionThreshold = fgsInteractionChangeEnabled
+ ? mConstants.SERVICE_USAGE_INTERACTION_TIME_POST_S
+ : mConstants.SERVICE_USAGE_INTERACTION_TIME_PRE_S;
// For foreground services that sit around for a long time but are not interacted with.
- maybeUpdateUsageStatsLSP(app, nowElapsed);
+ if ((nowElapsed - state.getFgInteractionTime()) > interactionThreshold) {
+ maybeUpdateUsageStatsLSP(app, nowElapsed);
+ }
}
if (state.getCurCapability() != state.getSetCapability()) {
@@ -2877,6 +2921,8 @@
if (mService.mUsageStatsService == null) {
return;
}
+ final boolean fgsInteractionChangeEnabled = getPlatformCompatCache().isChangeEnabled(
+ USE_SHORT_FGS_USAGE_INTERACTION_TIME, app.info, false);
boolean isInteraction;
// To avoid some abuse patterns, we are going to be careful about what we consider
// to be an app interaction. Being the top activity doesn't count while the display
@@ -2890,18 +2936,22 @@
state.setFgInteractionTime(nowElapsed);
isInteraction = false;
} else {
- isInteraction = nowElapsed > state.getFgInteractionTime()
- + mConstants.SERVICE_USAGE_INTERACTION_TIME;
+ final long interactionTime = fgsInteractionChangeEnabled
+ ? mConstants.SERVICE_USAGE_INTERACTION_TIME_POST_S
+ : mConstants.SERVICE_USAGE_INTERACTION_TIME_PRE_S;
+ isInteraction = nowElapsed > state.getFgInteractionTime() + interactionTime;
}
} else {
isInteraction =
state.getCurProcState() <= PROCESS_STATE_IMPORTANT_FOREGROUND;
state.setFgInteractionTime(0);
}
+ final long interactionThreshold = fgsInteractionChangeEnabled
+ ? mConstants.USAGE_STATS_INTERACTION_INTERVAL_POST_S
+ : mConstants.USAGE_STATS_INTERACTION_INTERVAL_PRE_S;
if (isInteraction
&& (!state.hasReportedInteraction()
- || (nowElapsed - state.getInteractionEventTime())
- > mConstants.USAGE_STATS_INTERACTION_INTERVAL)) {
+ || (nowElapsed - state.getInteractionEventTime()) > interactionThreshold)) {
state.setInteractionEventTime(nowElapsed);
String[] packages = app.getPackageList();
if (packages != null) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 6765ad0..cc98abf 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -135,6 +135,7 @@
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.wm.ActivityServiceConnectionsHolder;
import com.android.server.wm.WindowManagerService;
+import com.android.server.wm.WindowProcessController;
import dalvik.system.VMRuntime;
@@ -4626,6 +4627,7 @@
@GuardedBy(anyOf = {"mService", "mProcLock"})
void updateApplicationInfoLOSP(List<String> packagesToUpdate, int userId,
boolean updateFrameworkRes) {
+ final ArrayList<WindowProcessController> targetProcesses = new ArrayList<>();
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
final ProcessRecord app = mLruProcesses.get(i);
if (app.getThread() == null) {
@@ -4646,6 +4648,7 @@
if (ai.packageName.equals(app.info.packageName)) {
app.info = ai;
}
+ targetProcesses.add(app.getWindowProcessController());
}
} catch (RemoteException e) {
Slog.w(TAG, String.format("Failed to update %s ApplicationInfo for %s",
@@ -4654,6 +4657,9 @@
}
});
}
+
+ mService.mActivityTaskManager.updateAssetConfiguration(
+ updateFrameworkRes ? null : targetProcesses);
}
@GuardedBy("mService")
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index f566080..a139589 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -257,8 +257,8 @@
return false;
}
- // don't allow attention check in screen off state
- if (!mPowerManager.isInteractive()) {
+ // don't allow attention check in screen off state or power save mode
+ if (!mPowerManager.isInteractive() || mPowerManager.isPowerSaveMode()) {
return false;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 39b7a74..ce06d06 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -811,7 +811,7 @@
for (String instance : instances) {
final String fqName = IFingerprint.DESCRIPTOR + "/" + instance;
final IFingerprint fp = IFingerprint.Stub.asInterface(
- ServiceManager.waitForDeclaredService(fqName));
+ Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName)));
if (fp == null) {
Slog.e(TAG, "Unable to get declared service: " + fqName);
continue;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index c23c113..083df19 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -37,6 +37,7 @@
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -186,8 +187,9 @@
Slog.d(getTag(), "Daemon was null, reconnecting");
mDaemon = IFingerprint.Stub.asInterface(
- ServiceManager.waitForDeclaredService(IFingerprint.DESCRIPTOR
- + "/" + mHalInstanceName));
+ Binder.allowBlocking(
+ ServiceManager.waitForDeclaredService(
+ IFingerprint.DESCRIPTOR + "/" + mHalInstanceName)));
if (mDaemon == null) {
Slog.e(getTag(), "Unable to get daemon");
return null;
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 62de369..e604a05 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -167,6 +167,28 @@
return enabled;
}
+ /**
+ * Called by the package manager to check if a given change is enabled for a given package name
+ * and the target sdk version while the package is in the parsing state.
+ *
+ * <p>Does not perform costly permission check.
+ *
+ * @param changeId the ID of the change in question
+ * @param packageName package name to check for
+ * @param targetSdkVersion target sdk version to check for
+ * @return {@code true} if the change would be enabled for this package name.
+ */
+ public boolean isChangeEnabledInternal(long changeId, String packageName,
+ int targetSdkVersion) {
+ if (mCompatConfig.willChangeBeEnabled(changeId, packageName)) {
+ final ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = packageName;
+ appInfo.targetSdkVersion = targetSdkVersion;
+ return isChangeEnabledInternalNoLogging(changeId, appInfo);
+ }
+ return false;
+ }
+
@Override
public void setOverrides(CompatibilityChangeConfig overrides, String packageName) {
checkCompatChangeOverridePermission();
diff --git a/services/core/java/com/android/server/inputmethod/InputContentUriTokenHandler.java b/services/core/java/com/android/server/inputmethod/InputContentUriTokenHandler.java
index 78c4144..5a0069a 100644
--- a/services/core/java/com/android/server/inputmethod/InputContentUriTokenHandler.java
+++ b/services/core/java/com/android/server/inputmethod/InputContentUriTokenHandler.java
@@ -104,7 +104,8 @@
}
/**
- * {@inheritDoc}
+ * If permissions are not released explicitly via {@link #release()}, release automatically
+ * whenever there are no more references to this object.
*/
@Override
protected void finalize() throws Throwable {
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 864aa33..172a68a 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -171,8 +171,7 @@
// client caching behavior is only enabled after seeing the first invalidate
LocationManager.invalidateLocalLocationEnabledCaches();
// disable caching for our own process
- Objects.requireNonNull(getContext().getSystemService(LocationManager.class))
- .disableLocalLocationEnabledCaches();
+ LocationManager.disableLocalLocationEnabledCaches();
}
@Override
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 0be325f..4b772f2 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -2271,22 +2271,28 @@
}
if (mOnLocationTagsChangeListener != null) {
- if (!oldState.extraAttributionTags.equals(newState.extraAttributionTags)) {
+ if (!oldState.extraAttributionTags.equals(newState.extraAttributionTags)
+ || !Objects.equals(oldState.identity, newState.identity)) {
if (oldState.identity != null) {
FgThread.getHandler().sendMessage(PooledLambda.obtainMessage(
OnProviderLocationTagsChangeListener::onLocationTagsChanged,
mOnLocationTagsChangeListener, new LocationTagInfo(
oldState.identity.getUid(), oldState.identity.getPackageName(),
Collections.emptySet())
- ));
+ ));
}
if (newState.identity != null) {
+ ArraySet<String> attributionTags = new ArraySet<>(
+ newState.extraAttributionTags.size() + 1);
+ attributionTags.addAll(newState.extraAttributionTags);
+ attributionTags.add(newState.identity.getAttributionTag());
+
FgThread.getHandler().sendMessage(PooledLambda.obtainMessage(
OnProviderLocationTagsChangeListener::onLocationTagsChanged,
mOnLocationTagsChangeListener, new LocationTagInfo(
newState.identity.getUid(), newState.identity.getPackageName(),
- newState.extraAttributionTags)
- ));
+ attributionTags)
+ ));
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 656f347..b6f5e99 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -20,6 +20,7 @@
import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.Manifest;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -88,6 +89,7 @@
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
import com.android.server.pm.parsing.PackageParser2;
+import com.android.server.pm.utils.RequestThrottle;
import libcore.io.IoUtils;
@@ -220,6 +222,14 @@
}
}
+ @NonNull
+ private final RequestThrottle mSettingsWriteRequest = new RequestThrottle(IoThread.getHandler(),
+ () -> {
+ synchronized (mSessions) {
+ return writeSessionsLocked();
+ }
+ });
+
public PackageInstallerService(Context context, PackageManagerService pm,
Supplier<PackageParser2> apexParserSupplier) {
mContext = context;
@@ -275,7 +285,7 @@
// Invalid sessions might have been marked while parsing. Re-write the database with
// the updated information.
- writeSessionsLocked();
+ mSettingsWriteRequest.runNow();
}
}
@@ -464,7 +474,7 @@
}
@GuardedBy("mSessions")
- private void writeSessionsLocked() {
+ private boolean writeSessionsLocked() {
if (LOGD) Slog.v(TAG, "writeSessionsLocked()");
FileOutputStream fos = null;
@@ -483,28 +493,20 @@
out.endDocument();
mSessionsFile.finishWrite(fos);
+ return true;
} catch (IOException e) {
if (fos != null) {
mSessionsFile.failWrite(fos);
}
}
+
+ return false;
}
private File buildAppIconFile(int sessionId) {
return new File(mSessionsDir, "app_icon." + sessionId + ".png");
}
- private void writeSessionsAsync() {
- IoThread.getHandler().post(new Runnable() {
- @Override
- public void run() {
- synchronized (mSessions) {
- writeSessionsLocked();
- }
- }
- });
- }
-
@Override
public int createSession(SessionParams params, String installerPackageName,
String callingAttributionTag, int userId) {
@@ -764,7 +766,7 @@
mCallbacks.notifySessionCreated(session.sessionId, session.userId);
- writeSessionsAsync();
+ mSettingsWriteRequest.schedule();
return sessionId;
}
@@ -1374,7 +1376,7 @@
class InternalCallback {
public void onSessionBadgingChanged(PackageInstallerSession session) {
mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId);
- writeSessionsAsync();
+ mSettingsWriteRequest.schedule();
}
public void onSessionActiveChanged(PackageInstallerSession session, boolean active) {
@@ -1389,7 +1391,7 @@
public void onStagedSessionChanged(PackageInstallerSession session) {
session.markUpdated();
- writeSessionsAsync();
+ mSettingsWriteRequest.schedule();
if (mOkToSendBroadcasts && !session.isDestroyed()) {
// we don't scrub the data here as this is sent only to the installer several
// privileged system packages
@@ -1419,7 +1421,7 @@
appIconFile.delete();
}
- writeSessionsLocked();
+ mSettingsWriteRequest.runNow();
}
}
});
@@ -1428,16 +1430,14 @@
public void onSessionPrepared(PackageInstallerSession session) {
// We prepared the destination to write into; we want to persist
// this, but it's not critical enough to block for.
- writeSessionsAsync();
+ mSettingsWriteRequest.schedule();
}
public void onSessionSealedBlocking(PackageInstallerSession session) {
// It's very important that we block until we've recorded the
// session as being sealed, since we never want to allow mutation
// after sealing.
- synchronized (mSessions) {
- writeSessionsLocked();
- }
+ mSettingsWriteRequest.runNow();
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 30aa8fd..51288de 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -134,6 +134,7 @@
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
import static com.android.server.pm.PackageManagerServiceUtils.makeDirRecursive;
import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
+import static com.android.server.pm.parsing.PackageInfoUtils.checkUseInstalledOrHidden;
import android.Manifest;
import android.annotation.AppIdInt;
@@ -2510,8 +2511,8 @@
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
AndroidPackage pkg = a == null ? null : mPackages.get(a.getPackageName());
+ PackageSetting ps = a == null ? null : mSettings.getPackageLPr(a.getPackageName());
if (pkg != null && mSettings.isEnabledAndMatchLPr(pkg, a, flags, userId)) {
- PackageSetting ps = mSettings.getPackageLPr(component.getPackageName());
if (ps == null) return null;
if (shouldFilterApplicationLocked(
ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {
@@ -2521,8 +2522,8 @@
a, flags, ps.readUserState(userId), userId, ps);
}
if (resolveComponentName().equals(component)) {
- return PackageParser.generateActivityInfo(
- mResolveActivity, flags, new PackageUserState(), userId);
+ return generateDelegateActivityInfo(pkg, ps, new PackageUserState(),
+ mResolveActivity, flags, userId);
}
return null;
}
@@ -3168,8 +3169,8 @@
return result;
}
final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo);
- ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo(
- instantAppInstallerActivity(), 0, ps.readUserState(userId), userId);
+ ephemeralInstaller.activityInfo = generateDelegateActivityInfo(ps.getPkg(), ps,
+ ps.readUserState(userId), instantAppInstallerActivity(), 0 /*flags*/, userId);
ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
| IntentFilter.MATCH_ADJUSTMENT_NORMAL;
// add a non-generic filter
@@ -3253,7 +3254,7 @@
ai.flags = ps.pkgFlags;
ai.privateFlags = ps.pkgPrivateFlags;
pi.applicationInfo =
- PackageParser.generateApplicationInfo(ai, flags, state, userId);
+ PackageInfoUtils.generateApplicationInfo(p, flags, state, userId, ps);
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "ps.pkg is n/a for ["
+ ps.name + "]. Provides a minimum info.");
@@ -3369,6 +3370,19 @@
return getInstalledPackagesBody(flags, userId, callingUid);
}
+ private static ActivityInfo generateDelegateActivityInfo(@Nullable AndroidPackage pkg,
+ @Nullable PackageSetting ps, @NonNull PackageUserState state,
+ @Nullable ActivityInfo activity, int flags, int userId) {
+ if (activity == null || pkg == null
+ || !checkUseInstalledOrHidden(pkg, ps, state, flags)) {
+ return null;
+ }
+ final ActivityInfo info = new ActivityInfo(activity);
+ info.applicationInfo =
+ PackageInfoUtils.generateApplicationInfo(pkg, flags, state, userId, ps);
+ return info;
+ }
+
public ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId,
int callingUid) {
// writer
@@ -12711,7 +12725,8 @@
Map<String, AndroidPackage> availablePackages)
throws PackageManagerException {
final ArrayList<SharedLibraryInfo> sharedLibraryInfos = collectSharedLibraryInfos(
- pkgSetting.pkg, availablePackages, mSharedLibraries, null);
+ pkgSetting.pkg, availablePackages, mSharedLibraries, null /* newLibraries */,
+ mInjector.getCompatibility());
executeSharedLibrariesUpdateLPr(pkg, pkgSetting, changingLib, changingLibSetting,
sharedLibraryInfos, mUserManager.getUserIds());
}
@@ -12719,8 +12734,8 @@
private static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(AndroidPackage pkg,
Map<String, AndroidPackage> availablePackages,
@NonNull final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
- @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries)
- throws PackageManagerException {
+ @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries,
+ PlatformCompat platformCompat) throws PackageManagerException {
if (pkg == null) {
return null;
}
@@ -12744,9 +12759,9 @@
null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
}
- // TODO(b/160928779) gate this behavior using ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES
- if (pkg.getTargetSdkVersion() > 30) {
- if (!pkg.getUsesNativeLibraries().isEmpty() && pkg.getTargetSdkVersion() > 30) {
+ if (platformCompat.isChangeEnabledInternal(ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES,
+ pkg.getPackageName(), pkg.getTargetSdkVersion())) {
+ if (!pkg.getUsesNativeLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null,
null, pkg.getPackageName(), true, pkg.getTargetSdkVersion(),
usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
@@ -18875,7 +18890,7 @@
result.get(installPackageName).collectedSharedLibraryInfos =
collectSharedLibraryInfos(scanResult.request.parsedPackage,
combinedPackages, request.sharedLibrarySource,
- incomingSharedLibraries);
+ incomingSharedLibraries, injector.getCompatibility());
} catch (PackageManagerException e) {
throw new ReconcileFailure(e.error, e.getMessage());
@@ -23603,7 +23618,7 @@
boolean compatibilityModeEnabled = android.provider.Settings.Global.getInt(
mContext.getContentResolver(),
android.provider.Settings.Global.COMPATIBILITY_MODE, 1) == 1;
- PackageParser.setCompatibilityModeEnabled(compatibilityModeEnabled);
+ ParsingPackageUtils.setCompatibilityModeEnabled(compatibilityModeEnabled);
if (DEBUG_SETTINGS) {
Log.d(TAG, "compatibility mode:" + compatibilityModeEnabled);
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 61f51e3..b89dbdc 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -417,7 +417,7 @@
* Returns true if the package is installed and not hidden, or if the caller
* explicitly wanted all uninstalled and hidden packages as well.
*/
- private static boolean checkUseInstalledOrHidden(AndroidPackage pkg,
+ public static boolean checkUseInstalledOrHidden(AndroidPackage pkg,
PackageSetting pkgSetting, PackageUserState state,
@PackageManager.PackageInfoFlags int flags) {
// Returns false if the package is hidden system app until installed.
diff --git a/services/core/java/com/android/server/pm/utils/RequestThrottle.java b/services/core/java/com/android/server/pm/utils/RequestThrottle.java
new file mode 100644
index 0000000..f1dd402
--- /dev/null
+++ b/services/core/java/com/android/server/pm/utils/RequestThrottle.java
@@ -0,0 +1,154 @@
+/*
+ * 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 com.android.server.pm.utils;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+
+import com.android.server.IoThread;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
+
+/**
+ * Loose throttle latest behavior for success/fail requests, with options to schedule or force a
+ * request through. Throttling is implicit and not configurable. This means requests are dispatched
+ * to the {@link Handler} immediately when received, and only batched while waiting on the next
+ * message execution or running request.
+ *
+ * This also means there is no explicit debouncing. Implicit debouncing is available through the
+ * same runtime delays in the {@link Handler} instance and the request execution, where multiple
+ * requests prior to the execution point are collapsed.
+ *
+ * Callers provide a {@link Handler} with which to schedule tasks on. This may be a highly
+ * contentious thread like {@link IoThread#getHandler()}, but note that there are no guarantees
+ * that the request will be handled before the system server dies. Ideally callers should handle
+ * re-initialization from stale state with no consequences to the user.
+ *
+ * This class will retry requests if they don't succeed, as provided by a true/false response from
+ * the block provided to run the request. This uses an exponential backoff mechanism, assuming that
+ * state write should be attempted immediately, but not retried so heavily as to potentially block
+ * other system server callers. Exceptions are not considered and will not result in a retry if
+ * thrown from inside the block. Caller should wrap with try-catch and rollback and transaction
+ * state before returning false to signal a retry.
+ *
+ * The caller is strictly responsible for data synchronization, as this class will not synchronize
+ * the request block, potentially running it multiple times or on multiple threads simultaneously
+ * if requests come in asynchronously.
+ */
+public class RequestThrottle {
+
+ private static final int DEFAULT_RETRY_MAX_ATTEMPTS = 5;
+ private static final int DEFAULT_DELAY_MS = 1000;
+ private static final int DEFAULT_BACKOFF_BASE = 2;
+
+ private final AtomicInteger mLastRequest = new AtomicInteger(0);
+ private final AtomicInteger mLastCommitted = new AtomicInteger(-1);
+
+ private final int mMaxAttempts;
+ private final int mFirstDelay;
+ private final int mBackoffBase;
+
+ private final AtomicInteger mCurrentRetry = new AtomicInteger(0);
+
+ @NonNull
+ private final Handler mHandler;
+
+ @NonNull
+ private final Supplier<Boolean> mBlock;
+
+ @NonNull
+ private final Runnable mRunnable;
+
+ /**
+ * @see #RequestThrottle(Handler, int, int, int, Supplier)
+ */
+ public RequestThrottle(@NonNull Handler handler, @NonNull Supplier<Boolean> block) {
+ this(handler, DEFAULT_RETRY_MAX_ATTEMPTS, DEFAULT_DELAY_MS, DEFAULT_BACKOFF_BASE,
+ block);
+ }
+
+ /**
+ * Backoff timing is calculated as firstDelay * (backoffBase ^ retryAttempt).
+ *
+ * @param handler Representing the thread to run the provided block.
+ * @param block The action to run when scheduled, returning whether or not the request was
+ * successful. Note that any thrown exceptions will be ignored and not
+ * retried, since it's not easy to tell how destructive or retry-able an
+ * exception is.
+ * @param maxAttempts Number of times to re-attempt any single request.
+ * @param firstDelay The first delay used after the initial attempt.
+ * @param backoffBase The base of the backoff calculation, where retry attempt count is the
+ * exponent.
+ */
+ public RequestThrottle(@NonNull Handler handler, int maxAttempts, int firstDelay,
+ int backoffBase, @NonNull Supplier<Boolean> block) {
+ mHandler = handler;
+ mBlock = block;
+ mMaxAttempts = maxAttempts;
+ mFirstDelay = firstDelay;
+ mBackoffBase = backoffBase;
+ mRunnable = this::runInternal;
+ }
+
+ /**
+ * Schedule the intended action on the provided {@link Handler}.
+ */
+ public void schedule() {
+ // To avoid locking the Handler twice by pre-checking hasCallbacks, instead just queue
+ // the Runnable again. It will no-op if the request has already been written to disk.
+ mLastRequest.incrementAndGet();
+ mHandler.post(mRunnable);
+ }
+
+ /**
+ * Run the intended action immediately on the calling thread. Note that synchronization and
+ * deadlock between threads is not handled. This will immediately call the request block, and
+ * also potentially schedule a retry. The caller must not block itself.
+ *
+ * @return true if the write succeeded or the last request was already written
+ */
+ public boolean runNow() {
+ mLastRequest.incrementAndGet();
+ return runInternal();
+ }
+
+ private boolean runInternal() {
+ int lastRequest = mLastRequest.get();
+ int lastCommitted = mLastCommitted.get();
+ if (lastRequest == lastCommitted) {
+ return true;
+ }
+
+ if (mBlock.get()) {
+ mCurrentRetry.set(0);
+ mLastCommitted.set(lastRequest);
+ return true;
+ } else {
+ int currentRetry = mCurrentRetry.getAndIncrement();
+ if (currentRetry < mMaxAttempts) {
+ long nextDelay =
+ (long) (mFirstDelay * Math.pow(mBackoffBase, currentRetry));
+ mHandler.postDelayed(mRunnable, nextDelay);
+ } else {
+ mCurrentRetry.set(0);
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java
index 0e12584..3c9b106 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldController.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldController.java
@@ -21,6 +21,7 @@
import android.graphics.Rect;
import android.hardware.ICameraService;
import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
import android.hardware.display.DisplayManagerInternal;
import android.os.Handler;
import android.os.HandlerExecutor;
@@ -75,7 +76,7 @@
DeviceStateManager deviceStateManager = context.getSystemService(DeviceStateManager.class);
deviceStateManager.registerCallback(new HandlerExecutor(handler),
- new DeviceStateListener(context));
+ new FoldStateListener(context, folded -> setDeviceFolded(folded)));
}
void finishedGoingToSleep() {
@@ -202,30 +203,4 @@
return new DisplayFoldController(context, windowManagerService, displayService,
cameraServiceProxy, displayId, foldedArea, DisplayThread.getHandler());
}
-
- /**
- * Listens to changes in device state and reports the state as folded if the device state
- * matches the value in the {@link com.android.internal.R.integer.config_foldedDeviceState}
- * resource.
- */
- private class DeviceStateListener implements DeviceStateManager.DeviceStateCallback {
- private final int[] mFoldedDeviceStates;
-
- DeviceStateListener(Context context) {
- mFoldedDeviceStates = context.getResources().getIntArray(
- com.android.internal.R.array.config_foldedDeviceStates);
- }
-
- @Override
- public void onStateChanged(int deviceState) {
- boolean folded = false;
- for (int i = 0; i < mFoldedDeviceStates.length; i++) {
- if (deviceState == mFoldedDeviceStates[i]) {
- folded = true;
- break;
- }
- }
- setDeviceFolded(folded);
- }
- }
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7f325f1..27f5350 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3014,7 +3014,7 @@
private int handleStartTransitionForKeyguardLw(boolean keyguardGoingAway, long duration) {
final int res = applyKeyguardOcclusionChange();
if (res != 0) return res;
- if (!WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation && keyguardGoingAway) {
+ if (!WindowManagerService.sEnableRemoteKeyguardAnimation && keyguardGoingAway) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation");
startKeyguardExitAnimation(SystemClock.uptimeMillis(), duration);
}
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 6e478ee7..44f14b4 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -263,8 +263,7 @@
*/
@Deprecated
public void setOccluded(boolean isOccluded, boolean animate) {
- if (!WindowManagerService.sEnableRemoteKeyguardOccludeAnimation
- && mKeyguardService != null) {
+ if (!WindowManagerService.sEnableRemoteKeyguardAnimation && mKeyguardService != null) {
if (DEBUG) Log.v(TAG, "setOccluded(" + isOccluded + ") animate=" + animate);
mKeyguardService.setOccluded(isOccluded, animate);
}
@@ -404,8 +403,7 @@
}
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
- if (!WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation
- && mKeyguardService != null) {
+ if (!WindowManagerService.sEnableRemoteKeyguardAnimation && mKeyguardService != null) {
mKeyguardService.startKeyguardExitAnimation(startTime, fadeoutDuration);
}
}
diff --git a/services/core/java/com/android/server/telecom/TelecomLoaderService.java b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
index f0c96e1..32ad702 100644
--- a/services/core/java/com/android/server/telecom/TelecomLoaderService.java
+++ b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
@@ -31,6 +31,7 @@
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
import android.util.IntArray;
import android.util.Slog;
@@ -44,6 +45,9 @@
import com.android.server.pm.UserManagerService;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Starts the telecom component by binding to its ITelecomService implementation. Telecom is setup
* to run in the system-server process so once it is loaded into memory it will stay running.
@@ -208,13 +212,23 @@
return null;
}
}
+ SubscriptionManager subscriptionManager =
+ mContext.getSystemService(SubscriptionManager.class);
+ if (subscriptionManager == null) {
+ return null;
+ }
TelecomManager telecomManager =
(TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
- PhoneAccountHandle phoneAccount = telecomManager.getSimCallManager(userId);
- if (phoneAccount != null) {
- return new String[]{phoneAccount.getComponentName().getPackageName()};
+ List<String> packages = new ArrayList<>();
+ int[] subIds = subscriptionManager.getActiveSubscriptionIdList();
+ for (int subId : subIds) {
+ PhoneAccountHandle phoneAccount =
+ telecomManager.getSimCallManagerForSubscription(subId);
+ if (phoneAccount != null) {
+ packages.add(phoneAccount.getComponentName().getPackageName());
+ }
}
- return null;
+ return packages.toArray(new String[] {});
});
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 6a1b106..f568b66 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -91,6 +91,7 @@
import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.res.Configuration.ASSETS_SEQ_UNDEFINED;
import static android.content.res.Configuration.EMPTY;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -6858,6 +6859,11 @@
@Override
void resolveOverrideConfiguration(Configuration newParentConfiguration) {
+ final Configuration requestedOverrideConfig = getRequestedOverrideConfiguration();
+ if (requestedOverrideConfig.assetsSeq != ASSETS_SEQ_UNDEFINED
+ && newParentConfiguration.assetsSeq > requestedOverrideConfig.assetsSeq) {
+ requestedOverrideConfig.assetsSeq = ASSETS_SEQ_UNDEFINED;
+ }
super.resolveOverrideConfiguration(newParentConfiguration);
final Configuration resolvedConfig = getResolvedOverrideConfiguration();
if (isFixedRotationTransforming()) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index e249e67..c6a66c5 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -472,6 +472,10 @@
/** Current sequencing integer of the configuration, for skipping old configurations. */
private int mConfigurationSeq;
+
+ /** Current sequencing integer of the asset changes, for skipping old resources overlays. */
+ private int mGlobalAssetsSeq;
+
// To cache the list of supported system locales
private String[] mSupportedSystemLocales = null;
@@ -4129,6 +4133,35 @@
return changes;
}
+ private int increaseAssetConfigurationSeq() {
+ mGlobalAssetsSeq = Math.max(++mGlobalAssetsSeq, 1);
+ return mGlobalAssetsSeq;
+ }
+
+ /**
+ * Update the asset configuration and increase the assets sequence number.
+ * @param processes the processes that needs to update the asset configuration, if none
+ * updates the global configuration for all processes.
+ */
+ public void updateAssetConfiguration(List<WindowProcessController> processes) {
+ synchronized (mGlobalLock) {
+ final int assetSeq = increaseAssetConfigurationSeq();
+
+ // Update the global configuration if the no target processes
+ if (processes == null) {
+ Configuration newConfig = new Configuration();
+ newConfig.assetsSeq = assetSeq;
+ updateConfiguration(newConfig);
+ return;
+ }
+
+ for (int i = processes.size() - 1; i >= 0; i--) {
+ final WindowProcessController wpc = processes.get(i);
+ wpc.updateAssetConfiguration(assetSeq);
+ }
+ }
+ }
+
void startLaunchPowerMode(@PowerModeReason int reason) {
if (mPowerManagerInternal == null) return;
mPowerManagerInternal.setPowerMode(Mode.LAUNCH, true);
diff --git a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
index 62e4a85..87670d2 100644
--- a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
+++ b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
@@ -32,8 +32,7 @@
import java.util.ArrayList;
-class DragAndDropPermissionsHandler extends IDragAndDropPermissions.Stub
- implements IBinder.DeathRecipient {
+class DragAndDropPermissionsHandler extends IDragAndDropPermissions.Stub {
private static final String TAG = "DragAndDrop";
private static final boolean DEBUG = false;
@@ -49,7 +48,6 @@
private IBinder mActivityToken = null;
private IBinder mPermissionOwnerToken = null;
- private IBinder mAppToken = null;
DragAndDropPermissionsHandler(WindowManagerGlobalLock lock, ClipData clipData, int sourceUid,
String targetPackage, int mode, int sourceUserId, int targetUserId) {
@@ -94,18 +92,15 @@
}
@Override
- public void takeTransient(IBinder appToken) throws RemoteException {
+ public void takeTransient() throws RemoteException {
if (mActivityToken != null || mPermissionOwnerToken != null) {
return;
}
if (DEBUG) {
- Log.d(TAG, this + ": taking permissions bound to app process: "
- + toHexString(appToken.hashCode()));
+ Log.d(TAG, this + ": taking transient permissions");
}
mPermissionOwnerToken = LocalServices.getService(UriGrantsManagerInternal.class)
.newUriPermissionOwner("drop");
- mAppToken = appToken;
- mAppToken.linkToDeath(this, 0);
doTake(mPermissionOwnerToken);
}
@@ -132,10 +127,8 @@
} else {
permissionOwner = mPermissionOwnerToken;
mPermissionOwnerToken = null;
- mAppToken.unlinkToDeath(this, 0);
- mAppToken = null;
if (DEBUG) {
- Log.d(TAG, this + ": releasing process-bound permissions");
+ Log.d(TAG, this + ": releasing transient permissions");
}
}
@@ -157,15 +150,18 @@
}
}
+ /**
+ * If permissions are not tied to an activity, release whenever there are no more references
+ * to this object (if not already released).
+ */
@Override
- public void binderDied() {
+ protected void finalize() throws Throwable {
if (DEBUG) {
- Log.d(TAG, this + ": app process died: " + toHexString(mAppToken.hashCode()));
+ Log.d(TAG, this + ": running finalizer");
}
- try {
- release();
- } catch (RemoteException e) {
- // Cannot happen, local call.
+ if (mActivityToken != null || mPermissionOwnerToken == null) {
+ return;
}
+ release();
}
}
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index fb66c04..d67a0d3 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -163,14 +163,8 @@
originalWidth = displayInfo.logicalWidth;
originalHeight = displayInfo.logicalHeight;
}
- if (realOriginalRotation == Surface.ROTATION_90
- || realOriginalRotation == Surface.ROTATION_270) {
- mWidth = originalHeight;
- mHeight = originalWidth;
- } else {
- mWidth = originalWidth;
- mHeight = originalHeight;
- }
+ mWidth = originalWidth;
+ mHeight = originalHeight;
mOriginalRotation = originalRotation;
// If the delta is not zero, the rotation of display may not change, but we still want to
@@ -189,8 +183,14 @@
final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
try {
+ SurfaceControl.LayerCaptureArgs args =
+ new SurfaceControl.LayerCaptureArgs.Builder(displayContent.getSurfaceControl())
+ .setCaptureSecureLayers(true)
+ .setAllowProtected(true)
+ .setSourceCrop(new Rect(0, 0, mWidth, mHeight))
+ .build();
SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
- mService.mDisplayManagerInternal.systemScreenshot(displayId);
+ SurfaceControl.captureLayers(args);
if (screenshotBuffer == null) {
Slog.w(TAG, "Unable to take screenshot of display " + displayId);
return;
@@ -236,9 +236,6 @@
GraphicBuffer buffer = GraphicBuffer.createFromHardwareBuffer(
screenshotBuffer.getHardwareBuffer());
- // Scale the layer to the display size.
- float dsdx = (float) mWidth / hardwareBuffer.getWidth();
- float dsdy = (float) mHeight / hardwareBuffer.getHeight();
t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE);
t.reparent(mBackColorSurface, displayContent.getSurfaceControl());
@@ -247,7 +244,6 @@
t.setAlpha(mBackColorSurface, 1);
t.setBuffer(mScreenshotLayer, buffer);
t.setColorSpace(mScreenshotLayer, screenshotBuffer.getColorSpace());
- t.setMatrix(mScreenshotLayer, dsdx, 0, 0, dsdy);
t.show(mScreenshotLayer);
t.show(mBackColorSurface);
@@ -330,9 +326,8 @@
// Compute the transformation matrix that must be applied
// to the snapshot to make it stay in the same original position
// with the current screen rotation.
- int delta = deltaRotation(rotation, Surface.ROTATION_0);
+ int delta = deltaRotation(rotation, mOriginalRotation);
RotationAnimationUtils.createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
-
setRotationTransform(t, mSnapshotInitialMatrix);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7c95edd..2c592d0 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2258,7 +2258,6 @@
mTmpPrevBounds.set(getBounds());
final boolean wasInMultiWindowMode = inMultiWindowMode();
final boolean wasInPictureInPicture = inPinnedWindowingMode();
- final int oldOrientation = getOrientation();
super.onConfigurationChanged(newParentConfig);
// Only need to update surface size here since the super method will handle updating
// surface position.
@@ -2301,11 +2300,6 @@
mForceNotOrganized = false;
}
- // Report orientation change such as changing from freeform to fullscreen.
- if (oldOrientation != getOrientation()) {
- onDescendantOrientationChanged(this);
- }
-
saveLaunchingStateIfNeeded();
final boolean taskOrgChanged = updateTaskOrganizerState(false /* forceUpdate */);
if (taskOrgChanged) {
@@ -4315,11 +4309,21 @@
// the screen are opaque.
return TASK_VISIBILITY_INVISIBLE;
}
- if (isAssistantType && gotRootSplitScreenTask) {
- // Assistant stack can't be visible behind split-screen. In addition to this not
- // making sense, it also works around an issue here we boost the z-order of the
- // assistant window surfaces in window manager whenever it is visible.
- return TASK_VISIBILITY_INVISIBLE;
+ if (gotRootSplitScreenTask) {
+ if (isAssistantType) {
+ // Assistant stack can't be visible behind split-screen. In addition to this not
+ // making sense, it also works around an issue here we boost the z-order of the
+ // assistant window surfaces in window manager whenever it is visible.
+ return TASK_VISIBILITY_INVISIBLE;
+ }
+ if (other.isHomeOrRecentsRootTask()) {
+ // While in split mode, home task will be reparented to the secondary split and
+ // leaving tasks not supporting split below. Due to
+ // TaskDisplayArea#assignRootTaskOrdering always adjusts home surface layer to
+ // the bottom, this makes sure those tasks below home is invisible and won't
+ // occlude home task unexpectedly.
+ return TASK_VISIBILITY_INVISIBLE;
+ }
}
if (other.mAdjacentTask != null) {
if (adjacentTasks.contains(other.mAdjacentTask)) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index fbeb968..1657a13 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -421,41 +421,18 @@
SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true);
/**
- * Use WMShell for app transition.
- */
- public static final String ENABLE_SHELL_TRANSITIONS = "persist.debug.shell_transit";
-
- /**
- * @see #ENABLE_SHELL_TRANSITIONS
- */
- public static final boolean sEnableShellTransitions =
- SystemProperties.getBoolean(ENABLE_SHELL_TRANSITIONS, false);
-
- /**
* Run Keyguard animation as remote animation in System UI instead of local animation in
* the server process.
- *
- * 0: Runs all keyguard animation as local animation
- * 1: Only runs keyguard going away animation as remote animation
- * 2: Runs all keyguard animation as remote animation
*/
private static final String ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY =
"persist.wm.enable_remote_keyguard_animation";
- private static final int sEnableRemoteKeyguardAnimation =
- SystemProperties.getInt(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, 1);
-
/**
* @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
*/
- public static final boolean sEnableRemoteKeyguardGoingAwayAnimation = !sEnableShellTransitions
- && sEnableRemoteKeyguardAnimation >= 1;
+ public static boolean sEnableRemoteKeyguardAnimation =
+ SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
- /**
- * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
- */
- public static final boolean sEnableRemoteKeyguardOccludeAnimation = !sEnableShellTransitions
- && sEnableRemoteKeyguardAnimation >= 2;
/**
* Allows a fullscreen windowing mode activity to launch in its desired orientation directly
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 4dc6007..d5965494 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -130,6 +130,12 @@
return runResetLetterboxStyle(pw);
case "set-sandbox-display-apis":
return runSandboxDisplayApis(pw);
+ case "set-multi-window-config":
+ return runSetMultiWindowConfig();
+ case "get-multi-window-config":
+ return runGetMultiWindowConfig(pw);
+ case "reset-multi-window-config":
+ return runResetMultiWindowConfig();
case "reset":
return runReset(pw);
case "disable-blur":
@@ -815,6 +821,80 @@
return 0;
}
+ private int runSetMultiWindowConfig() {
+ if (peekNextArg() == null) {
+ getErrPrintWriter().println("Error: No arguments provided.");
+ }
+ int result = 0;
+ while (peekNextArg() != null) {
+ String arg = getNextArg();
+ switch (arg) {
+ case "--supportsNonResizable":
+ result += runSetSupportsNonResizableMultiWindow();
+ break;
+ case "--respectsActivityMinWidthHeight":
+ result += runSetRespectsActivityMinWidthHeightMultiWindow();
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: Unrecognized multi window option: " + arg);
+ return -1;
+ }
+ }
+ return result == 0 ? 0 : -1;
+ }
+
+ private int runSetSupportsNonResizableMultiWindow() {
+ final String arg = getNextArg();
+ if (!arg.equals("-1") && !arg.equals("0") && !arg.equals("1")) {
+ getErrPrintWriter().println("Error: a config value of [-1, 0, 1] must be provided as"
+ + " an argument for supportsNonResizableMultiWindow");
+ return -1;
+ }
+ final int configValue = Integer.parseInt(arg);
+ synchronized (mInternal.mAtmService.mGlobalLock) {
+ mInternal.mAtmService.mSupportsNonResizableMultiWindow = configValue;
+ }
+ return 0;
+ }
+
+ private int runSetRespectsActivityMinWidthHeightMultiWindow() {
+ final String arg = getNextArg();
+ if (!arg.equals("-1") && !arg.equals("0") && !arg.equals("1")) {
+ getErrPrintWriter().println("Error: a config value of [-1, 0, 1] must be provided as"
+ + " an argument for respectsActivityMinWidthHeightMultiWindow");
+ return -1;
+ }
+ final int configValue = Integer.parseInt(arg);
+ synchronized (mInternal.mAtmService.mGlobalLock) {
+ mInternal.mAtmService.mRespectsActivityMinWidthHeightMultiWindow = configValue;
+ }
+ return 0;
+ }
+
+ private int runGetMultiWindowConfig(PrintWriter pw) {
+ synchronized (mInternal.mAtmService.mGlobalLock) {
+ pw.println("Supports non-resizable in multi window: "
+ + mInternal.mAtmService.mSupportsNonResizableMultiWindow);
+ pw.println("Respects activity min width/height in multi window: "
+ + mInternal.mAtmService.mRespectsActivityMinWidthHeightMultiWindow);
+ }
+ return 0;
+ }
+
+ private int runResetMultiWindowConfig() {
+ final int supportsNonResizable = mInternal.mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_supportsNonResizableMultiWindow);
+ final int respectsActivityMinWidthHeight = mInternal.mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_respectsActivityMinWidthHeightMultiWindow);
+ synchronized (mInternal.mAtmService.mGlobalLock) {
+ mInternal.mAtmService.mSupportsNonResizableMultiWindow = supportsNonResizable;
+ mInternal.mAtmService.mRespectsActivityMinWidthHeightMultiWindow =
+ respectsActivityMinWidthHeight;
+ }
+ return 0;
+ }
+
private void resetLetterboxStyle() {
synchronized (mInternal.mGlobalLock) {
mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
@@ -879,6 +959,9 @@
// set-sandbox-display-apis
mInternal.setSandboxDisplayApis(displayId, /* sandboxDisplayApis= */ true);
+ // set-multi-window-config
+ runResetMultiWindowConfig();
+
pw.println("Reset all settings for displayId=" + displayId);
return 0;
}
@@ -916,6 +999,7 @@
pw.println(" Size Compat Mode.");
printLetterboxHelp(pw);
+ printMultiWindowConfigHelp(pw);
pw.println(" reset [-d DISPLAY_ID]");
pw.println(" Reset all override settings.");
@@ -969,4 +1053,31 @@
pw.println(" get-letterbox-style");
pw.println(" Prints letterbox style configuration.");
}
+
+ private void printMultiWindowConfigHelp(PrintWriter pw) {
+ pw.println(" set-multi-window-config");
+ pw.println(" Sets options to determine if activity should be shown in multi window:");
+ pw.println(" --supportsNonResizable [configValue]");
+ pw.println(" Whether the device supports non-resizable activity in multi window.");
+ pw.println(" -1: The device doesn't support non-resizable in multi window.");
+ pw.println(" 0: The device supports non-resizable in multi window only if");
+ pw.println(" this is a large screen device.");
+ pw.println(" 1: The device always supports non-resizable in multi window.");
+ pw.println(" --respectsActivityMinWidthHeight [configValue]");
+ pw.println(" Whether the device checks the activity min width/height to determine ");
+ pw.println(" if it can be shown in multi window.");
+ pw.println(" -1: The device ignores the activity min width/height when determining");
+ pw.println(" if it can be shown in multi window.");
+ pw.println(" 0: If this is a small screen, the device compares the activity min");
+ pw.println(" width/height with the min multi window modes dimensions");
+ pw.println(" the device supports to determine if the activity can be shown in");
+ pw.println(" multi window.");
+ pw.println(" 1: The device always compare the activity min width/height with the");
+ pw.println(" min multi window dimensions the device supports to determine if");
+ pw.println(" the activity can be shown in multi window.");
+ pw.println(" get-multi-window-config");
+ pw.println(" Prints values of the multi window config options.");
+ pw.println(" reset-multi-window-config");
+ pw.println(" Resets overrides to default values of the multi window config options.");
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index bac1ab1..26cfbdf 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -18,6 +18,7 @@
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.content.res.Configuration.ASSETS_SEQ_UNDEFINED;
import static android.os.Build.VERSION_CODES.Q;
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
@@ -1318,6 +1319,11 @@
@Override
void resolveOverrideConfiguration(Configuration newParentConfig) {
+ final Configuration requestedOverrideConfig = getRequestedOverrideConfiguration();
+ if (requestedOverrideConfig.assetsSeq != ASSETS_SEQ_UNDEFINED
+ && newParentConfig.assetsSeq > requestedOverrideConfig.assetsSeq) {
+ requestedOverrideConfig.assetsSeq = ASSETS_SEQ_UNDEFINED;
+ }
super.resolveOverrideConfiguration(newParentConfig);
final Configuration resolvedConfig = getResolvedOverrideConfiguration();
// Make sure that we don't accidentally override the activity type.
@@ -1396,6 +1402,28 @@
return mHasPendingConfigurationChange;
}
+ void updateAssetConfiguration(int assetSeq) {
+ // Update the process override configuration directly if the process configuration will
+ // not be override from its activities.
+ if (!mHasActivities || !mIsActivityConfigOverrideAllowed) {
+ Configuration overrideConfig = new Configuration(getRequestedOverrideConfiguration());
+ overrideConfig.assetsSeq = assetSeq;
+ onRequestedOverrideConfigurationChanged(overrideConfig);
+ return;
+ }
+
+ // Otherwise, we can just update the activity override configuration.
+ for (int i = mActivities.size() - 1; i >= 0; i--) {
+ ActivityRecord r = mActivities.get(i);
+ Configuration overrideConfig = new Configuration(r.getRequestedOverrideConfiguration());
+ overrideConfig.assetsSeq = assetSeq;
+ r.onRequestedOverrideConfigurationChanged(overrideConfig);
+ if (r.mVisibleRequested) {
+ r.ensureActivityConfiguration(0, true);
+ }
+ }
+ }
+
/**
* This is called for sending {@link android.app.servertransaction.LaunchActivityItem}.
* The caller must call {@link #setLastReportedConfiguration} if the delivered configuration
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index f3e7d67..36c0a67 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -269,7 +269,10 @@
template <class Func>
static auto makeCleanup(Func&& f) requires(!std::is_lvalue_reference_v<Func>) {
- auto deleter = [f = std::move(f)](auto) { f(); };
+ // ok to move a 'forwarding' reference here as lvalues are disabled anyway
+ auto deleter = [f = std::move(f)](auto) { // NOLINT
+ f();
+ };
// &f is a dangling pointer here, but we actually never use it as deleter moves it in.
return std::unique_ptr<Func, decltype(deleter)>(&f, std::move(deleter));
}
@@ -395,8 +398,18 @@
return std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
}
+static uint64_t elapsedUsSinceMonoTs(uint64_t monoTsUs) {
+ timespec now;
+ if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) {
+ return 0;
+ }
+ uint64_t nowUs = now.tv_sec * 1000000LL + now.tv_nsec / 1000;
+ return nowUs - monoTsUs;
+}
+
void IncrementalService::onDump(int fd) {
dprintf(fd, "Incremental is %s\n", incfs::enabled() ? "ENABLED" : "DISABLED");
+ dprintf(fd, "IncFs features: 0x%x\n", int(mIncFs->features()));
dprintf(fd, "Incremental dir: %s\n", mIncrementalDir.c_str());
std::unique_lock l(mLock);
@@ -411,6 +424,8 @@
} else {
dprintf(fd, " mountId: %d\n", mnt.mountId);
dprintf(fd, " root: %s\n", mnt.root.c_str());
+ const auto metricsInstanceName = path::basename(ifs->root);
+ dprintf(fd, " metrics instance name: %s\n", path::c_str(metricsInstanceName).get());
dprintf(fd, " nextStorageDirNo: %d\n", mnt.nextStorageDirNo.load());
dprintf(fd, " flags: %d\n", int(mnt.flags));
if (mnt.startLoadingTs.time_since_epoch() == Clock::duration::zero()) {
@@ -440,6 +455,45 @@
dprintf(fd, " kind: %s\n", toString(bind.kind));
}
dprintf(fd, " }\n");
+
+ dprintf(fd, " incfsMetrics: {\n");
+ const auto incfsMetrics = mIncFs->getMetrics(metricsInstanceName);
+ if (incfsMetrics) {
+ dprintf(fd, " readsDelayedMin: %d\n", incfsMetrics.value().readsDelayedMin);
+ dprintf(fd, " readsDelayedMinUs: %lld\n",
+ (long long)incfsMetrics.value().readsDelayedMinUs);
+ dprintf(fd, " readsDelayedPending: %d\n",
+ incfsMetrics.value().readsDelayedPending);
+ dprintf(fd, " readsDelayedPendingUs: %lld\n",
+ (long long)incfsMetrics.value().readsDelayedPendingUs);
+ dprintf(fd, " readsFailedHashVerification: %d\n",
+ incfsMetrics.value().readsFailedHashVerification);
+ dprintf(fd, " readsFailedOther: %d\n", incfsMetrics.value().readsFailedOther);
+ dprintf(fd, " readsFailedTimedOut: %d\n",
+ incfsMetrics.value().readsFailedTimedOut);
+ } else {
+ dprintf(fd, " Metrics not available. Errno: %d\n", errno);
+ }
+ dprintf(fd, " }\n");
+
+ const auto lastReadError = mIncFs->getLastReadError(ifs->control);
+ const auto errorNo = errno;
+ dprintf(fd, " lastReadError: {\n");
+ if (lastReadError) {
+ if (lastReadError->timestampUs == 0) {
+ dprintf(fd, " No read errors.\n");
+ } else {
+ dprintf(fd, " fileId: %s\n",
+ IncFsWrapper::toString(lastReadError->id).c_str());
+ dprintf(fd, " time: %llu microseconds ago\n",
+ (unsigned long long)elapsedUsSinceMonoTs(lastReadError->timestampUs));
+ dprintf(fd, " blockIndex: %d\n", lastReadError->block);
+ dprintf(fd, " errno: %d\n", lastReadError->errorNo);
+ }
+ } else {
+ dprintf(fd, " Info not available. Errno: %d\n", errorNo);
+ }
+ dprintf(fd, " }\n");
}
dprintf(fd, " }\n");
}
@@ -578,7 +632,7 @@
if (!mkdirOrLog(path::join(backing, ".incomplete"), 0777)) {
return kInvalidStorageId;
}
- auto status = mVold->mountIncFs(backing, mountTarget, 0, &controlParcel);
+ auto status = mVold->mountIncFs(backing, mountTarget, 0, mountKey, &controlParcel);
if (!status.isOk()) {
LOG(ERROR) << "Vold::mountIncFs() failed: " << status.toString8();
return kInvalidStorageId;
@@ -1586,9 +1640,10 @@
bool IncrementalService::mountExistingImage(std::string_view root) {
auto mountTarget = path::join(root, constants().mount);
const auto backing = path::join(root, constants().backing);
+ std::string mountKey(path::basename(path::dirname(mountTarget)));
IncrementalFileSystemControlParcel controlParcel;
- auto status = mVold->mountIncFs(backing, mountTarget, 0, &controlParcel);
+ auto status = mVold->mountIncFs(backing, mountTarget, 0, mountKey, &controlParcel);
if (!status.isOk()) {
LOG(ERROR) << "Vold::mountIncFs() failed: " << status.toString8();
return false;
@@ -2562,7 +2617,9 @@
maxBindDelayMs)
.count();
const auto bindDelayJitterRangeMs = bindDelayMs / Constants::bindDelayJitterDivider;
- const auto bindDelayJitterMs = rand() % (bindDelayJitterRangeMs * 2) - bindDelayJitterRangeMs;
+ // rand() is enough, not worth maintaining a full-blown <rand> object for delay jitter
+ const auto bindDelayJitterMs = rand() % (bindDelayJitterRangeMs * 2) - // NOLINT
+ bindDelayJitterRangeMs;
mPreviousBindDelay = std::chrono::milliseconds(bindDelayMs + bindDelayJitterMs);
return mPreviousBindDelay;
}
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 0755a22..68a28b2 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -43,8 +43,9 @@
~RealVoldService() = default;
binder::Status mountIncFs(
const std::string& backingPath, const std::string& targetDir, int32_t flags,
+ const std::string& sysfsName,
os::incremental::IncrementalFileSystemControlParcel* _aidl_return) const final {
- return mInterface->mountIncFs(backingPath, targetDir, flags, _aidl_return);
+ return mInterface->mountIncFs(backingPath, targetDir, flags, sysfsName, _aidl_return);
}
binder::Status unmountIncFs(const std::string& dir) const final {
return mInterface->unmountIncFs(dir);
@@ -261,6 +262,12 @@
return cb(control, id);
});
}
+ std::optional<Metrics> getMetrics(std::string_view sysfsName) const final {
+ return incfs::getMetrics(sysfsName);
+ }
+ std::optional<LastReadError> getLastReadError(const Control& control) const final {
+ return incfs::getLastReadError(control);
+ }
};
static JNIEnv* getOrAttachJniEnv(JavaVM* jvm);
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index 78e9589..c0ef7ba 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -51,6 +51,7 @@
virtual ~VoldServiceWrapper() = default;
virtual binder::Status mountIncFs(
const std::string& backingPath, const std::string& targetDir, int32_t flags,
+ const std::string& sysfsName,
os::incremental::IncrementalFileSystemControlParcel* result) const = 0;
virtual binder::Status unmountIncFs(const std::string& dir) const = 0;
virtual binder::Status bindMount(const std::string& sourceDir,
@@ -79,6 +80,8 @@
using UniqueFd = incfs::UniqueFd;
using WaitResult = incfs::WaitResult;
using Features = incfs::Features;
+ using Metrics = incfs::Metrics;
+ using LastReadError = incfs::LastReadError;
using ExistingMountCallback = android::base::function_ref<
void(std::string_view root, std::string_view backingDir,
@@ -124,6 +127,8 @@
const = 0;
virtual ErrorCode forEachFile(const Control& control, FileCallback cb) const = 0;
virtual ErrorCode forEachIncompleteFile(const Control& control, FileCallback cb) const = 0;
+ virtual std::optional<Metrics> getMetrics(std::string_view sysfsName) const = 0;
+ virtual std::optional<LastReadError> getLastReadError(const Control& control) const = 0;
};
class AppOpsManagerWrapper {
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 68586a8..da7f0db 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -49,9 +49,9 @@
class MockVoldService : public VoldServiceWrapper {
public:
- MOCK_CONST_METHOD4(mountIncFs,
+ MOCK_CONST_METHOD5(mountIncFs,
binder::Status(const std::string& backingPath, const std::string& targetDir,
- int32_t flags,
+ int32_t flags, const std::string& sysfsName,
IncrementalFileSystemControlParcel* _aidl_return));
MOCK_CONST_METHOD1(unmountIncFs, binder::Status(const std::string& dir));
MOCK_CONST_METHOD2(bindMount,
@@ -62,16 +62,16 @@
bool, bool));
void mountIncFsFails() {
- ON_CALL(*this, mountIncFs(_, _, _, _))
+ ON_CALL(*this, mountIncFs(_, _, _, _, _))
.WillByDefault(
Return(binder::Status::fromExceptionCode(1, String8("failed to mount"))));
}
void mountIncFsInvalidControlParcel() {
- ON_CALL(*this, mountIncFs(_, _, _, _))
+ ON_CALL(*this, mountIncFs(_, _, _, _, _))
.WillByDefault(Invoke(this, &MockVoldService::getInvalidControlParcel));
}
void mountIncFsSuccess() {
- ON_CALL(*this, mountIncFs(_, _, _, _))
+ ON_CALL(*this, mountIncFs(_, _, _, _, _))
.WillByDefault(Invoke(this, &MockVoldService::incFsSuccess));
}
void bindMountFails() {
@@ -93,12 +93,14 @@
}
binder::Status getInvalidControlParcel(const std::string& imagePath,
const std::string& targetDir, int32_t flags,
+ const std::string& sysfsName,
IncrementalFileSystemControlParcel* _aidl_return) {
_aidl_return = {};
return binder::Status::ok();
}
binder::Status incFsSuccess(const std::string& imagePath, const std::string& targetDir,
- int32_t flags, IncrementalFileSystemControlParcel* _aidl_return) {
+ int32_t flags, const std::string& sysfsName,
+ IncrementalFileSystemControlParcel* _aidl_return) {
_aidl_return->pendingReads.reset(base::unique_fd(dup(STDIN_FILENO)));
_aidl_return->cmd.reset(base::unique_fd(dup(STDIN_FILENO)));
_aidl_return->log.reset(base::unique_fd(dup(STDIN_FILENO)));
@@ -414,6 +416,8 @@
const std::vector<PerUidReadTimeouts>& perUidReadTimeouts));
MOCK_CONST_METHOD2(forEachFile, ErrorCode(const Control& control, FileCallback cb));
MOCK_CONST_METHOD2(forEachIncompleteFile, ErrorCode(const Control& control, FileCallback cb));
+ MOCK_CONST_METHOD1(getMetrics, std::optional<Metrics>(std::string_view path));
+ MOCK_CONST_METHOD1(getLastReadError, std::optional<LastReadError>(const Control& control));
MockIncFs() {
ON_CALL(*this, listExistingMounts(_)).WillByDefault(Return());
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/install/RequestThrottleTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/install/RequestThrottleTest.kt
new file mode 100644
index 0000000..2196ef7
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/install/RequestThrottleTest.kt
@@ -0,0 +1,219 @@
+/*
+ * 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 com.android.server.pm.test.install
+
+import com.android.server.pm.utils.RequestThrottle
+import com.android.server.testutils.TestHandler
+import com.google.common.collect.Range
+import com.google.common.truth.LongSubject
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.CyclicBarrier
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.atomic.AtomicBoolean
+import java.util.concurrent.atomic.AtomicInteger
+import java.util.concurrent.atomic.AtomicLong
+
+class RequestThrottleTest {
+
+ private val counter = AtomicInteger(0)
+
+ private val handler = TestHandler(null)
+
+ @Before
+ fun resetValues() {
+ handler.flush()
+ counter.set(0)
+ assertThat(counter.get()).isEqualTo(0)
+ }
+
+ @Test
+ fun simpleThrottle() {
+ val request = RequestThrottle(handler) {
+ counter.incrementAndGet()
+ true
+ }
+
+ fun sendRequests() {
+ request.schedule()
+ val thread = startThread { request.schedule() }
+ request.schedule()
+ thread.joinForTest()
+ }
+
+ sendRequests()
+ handler.flush()
+ assertThat(counter.get()).isEqualTo(1)
+
+ sendRequests()
+ handler.flush()
+ assertThat(counter.get()).isEqualTo(2)
+ }
+
+ @Test
+ fun exceptionInRequest() {
+ val shouldThrow = AtomicBoolean(true)
+ val request = RequestThrottle(handler) {
+ if (shouldThrow.get()) {
+ throw RuntimeException()
+ }
+ counter.incrementAndGet()
+ true
+ }
+
+ fun sendRequests() {
+ request.schedule()
+ val thread = startThread { request.schedule() }
+ request.schedule()
+ thread.joinForTest()
+ }
+
+ sendRequests()
+ try {
+ handler.flush()
+ } catch (ignored: Exception) {
+ }
+ assertThat(counter.get()).isEqualTo(0)
+
+ shouldThrow.set(false)
+
+ sendRequests()
+ handler.flush()
+ assertThat(counter.get()).isEqualTo(1)
+ }
+
+ @Test
+ fun scheduleWhileRunning() {
+ val latchForStartRequest = CountDownLatch(1)
+ val latchForEndRequest = CountDownLatch(1)
+ val request = RequestThrottle(handler) {
+ latchForStartRequest.countDown()
+ counter.incrementAndGet()
+ latchForEndRequest.awaitForTest()
+ true
+ }
+
+ // Schedule and block a request
+ request.schedule()
+ val handlerThread = startThread { handler.timeAdvance() }
+ latchForStartRequest.awaitForTest()
+
+ // Hit it with other requests
+ request.schedule()
+ (0..5).map { startThread { request.schedule() } }
+ .forEach { it.joinForTest() }
+
+ // Release everything
+ latchForEndRequest.countDown()
+ handlerThread.join()
+ handler.flush()
+
+ // Ensure another request was run after initial blocking request ends
+ assertThat(counter.get()).isEqualTo(2)
+ }
+
+ @Test
+ fun backoffRetry() {
+ val time = AtomicLong(0)
+ val handler = TestHandler(null) { time.get() }
+ val returnValue = AtomicBoolean(false)
+ val request = RequestThrottle(handler, 3, 1000, 2) {
+ counter.incrementAndGet()
+ returnValue.get()
+ }
+
+ request.schedule()
+
+ handler.timeAdvance()
+ handler.pendingMessages.apply {
+ assertThat(size).isEqualTo(1)
+ assertThat(single().sendTime).isAround(1000)
+ }
+
+ time.set(1000)
+ handler.timeAdvance()
+ handler.pendingMessages.apply {
+ assertThat(size).isEqualTo(1)
+ assertThat(single().sendTime).isAround(3000)
+ }
+
+ time.set(3000)
+ handler.timeAdvance()
+ handler.pendingMessages.apply {
+ assertThat(size).isEqualTo(1)
+ assertThat(single().sendTime).isAround(7000)
+ }
+
+ returnValue.set(true)
+ time.set(7000)
+ handler.timeAdvance()
+ assertThat(handler.pendingMessages).isEmpty()
+
+ // Ensure another request was run after initial blocking request ends
+ assertThat(counter.get()).isEqualTo(4)
+ }
+
+ @Test
+ fun forceWriteMultiple() {
+ val request = RequestThrottle(handler) {
+ counter.incrementAndGet()
+ true
+ }
+
+ request.runNow()
+ request.runNow()
+ request.runNow()
+
+ assertThat(counter.get()).isEqualTo(3)
+ }
+
+ @Test
+ fun forceWriteNowWithoutSync() {
+ // When forcing a write without synchronizing the request block, 2 instances will be run.
+ // There is no test for "with sync" because any logic to avoid multiple runs is left
+ // entirely up to the caller.
+
+ val barrierForEndRequest = CyclicBarrier(2)
+ val request = RequestThrottle(handler) {
+ counter.incrementAndGet()
+ barrierForEndRequest.awaitForTest()
+ true
+ }
+
+ // Schedule and block a request
+ request.schedule()
+ val thread = startThread { handler.timeAdvance() }
+
+ request.runNow()
+
+ thread.joinForTest()
+
+ assertThat(counter.get()).isEqualTo(2)
+ }
+
+ private fun CountDownLatch.awaitForTest() = assertThat(await(5, TimeUnit.SECONDS)).isTrue()
+ private fun CyclicBarrier.awaitForTest() = await(5, TimeUnit.SECONDS)
+ private fun Thread.joinForTest() = join(5000)
+
+ private fun startThread(block: () -> Unit) = Thread { block() }.apply { start() }
+
+ // Float math means time calculations are not exact, so use a loose range
+ private fun LongSubject.isAround(value: Long, threshold: Long = 10) =
+ isIn(Range.closed(value - threshold, value + threshold))
+}
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/OWNERS b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/OWNERS
new file mode 100644
index 0000000..c74393b
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 137825
+
+file:platform/frameworks/native:/libs/sensorprivacy/OWNERS
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file1.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file1.xml
new file mode 100644
index 0000000..a4de08a
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file1.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<sensor-privacy persistence-version="1" version="1">
+ <user id="0" enabled="false">
+ <individual-sensor-privacy sensor="1" enabled="true" />
+ <individual-sensor-privacy sensor="2" enabled="true" />
+ </user>
+</sensor-privacy>
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file2.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file2.xml
new file mode 100644
index 0000000..361075e
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file2.xml
@@ -0,0 +1,16 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<sensor-privacy persistence-version="1" version="1">
+ <user id="0" enabled="false">
+ <individual-sensor-privacy sensor="1" enabled="true" />
+ <individual-sensor-privacy sensor="2" enabled="true" />
+ </user>
+ <user id="10" enabled="false">
+ <individual-sensor-privacy sensor="1" enabled="true" />
+ <individual-sensor-privacy sensor="2" enabled="false" />
+ </user>
+ <user id="11" enabled="false">
+ </user>
+ <user id="12" enabled="false">
+ <individual-sensor-privacy sensor="1" enabled="true" />
+ </user>
+</sensor-privacy>
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file3.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file3.xml
new file mode 100644
index 0000000..e8f9edf
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file3.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<sensor-privacy persistence-version="1" version="1">
+ <user id="0" enabled="false">
+ <individual-sensor-privacy sensor="1" enabled="false" />
+ <individual-sensor-privacy sensor="2" enabled="false" />
+ </user>
+</sensor-privacy>
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file4.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file4.xml
new file mode 100644
index 0000000..d26c275
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file4.xml
@@ -0,0 +1,10 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<sensor-privacy persistence-version="1" version="1">
+ <user id="0" enabled="false">
+ <individual-sensor-privacy sensor="1" enabled="true" />
+ <individual-sensor-privacy sensor="2" enabled="false" />
+ </user>
+ <user id="10" enabled="false">
+ <individual-sensor-privacy sensor="1" enabled="false" />
+ </user>
+</sensor-privacy>
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file5.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file5.xml
new file mode 100644
index 0000000..5c9d0cd
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file5.xml
@@ -0,0 +1,9 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<sensor-privacy persistence-version="1" version="1">
+ <user id="0" enabled="false">
+ <individual-sensor-privacy sensor="2" enabled="false" />
+ </user>
+ <user id="10" enabled="false">
+ <individual-sensor-privacy sensor="2" enabled="false" />
+ </user>
+</sensor-privacy>
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 24b85f0..92e4ec9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -61,6 +61,8 @@
import android.location.LastLocationRequest;
import android.location.Location;
import android.location.LocationManagerInternal;
+import android.location.LocationManagerInternal.LocationTagInfo;
+import android.location.LocationManagerInternal.OnProviderLocationTagsChangeListener;
import android.location.LocationManagerInternal.ProviderEnabledListener;
import android.location.LocationRequest;
import android.location.LocationResult;
@@ -90,6 +92,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
@@ -216,6 +219,21 @@
}
@Test
+ public void testAttributionTags() {
+ OnProviderLocationTagsChangeListener listener = mock(
+ OnProviderLocationTagsChangeListener.class);
+ mManager.setOnProviderLocationTagsChangeListener(listener);
+
+ mProvider.setExtraAttributionTags(Collections.singleton("extra"));
+
+ ArgumentCaptor<LocationTagInfo> captor = ArgumentCaptor.forClass(LocationTagInfo.class);
+ verify(listener, times(2)).onLocationTagsChanged(captor.capture());
+
+ assertThat(captor.getAllValues().get(0).getTags()).isEmpty();
+ assertThat(captor.getAllValues().get(1).getTags()).containsExactly("extra", "attribution");
+ }
+
+ @Test
public void testRemoveProvider() {
mManager.setRealProvider(null);
assertThat(mManager.hasProvider()).isFalse();
diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/OWNERS b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/OWNERS
new file mode 100644
index 0000000..c74393b
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 137825
+
+file:platform/frameworks/native:/libs/sensorprivacy/OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
new file mode 100644
index 0000000..844687f
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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 com.android.server.sensorprivacy;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.Environment;
+import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.server.LocalServices;
+import com.android.server.SensorPrivacyService;
+import com.android.server.pm.UserManagerInternal;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+
+@RunWith(AndroidTestingRunner.class)
+public class SensorPrivacyServiceMockingTest {
+
+ private static final String PERSISTENCE_FILE_PATHS_TEMPLATE =
+ "SensorPrivacyServiceMockingTest/persisted_file%d.xml";
+ public static final String PERSISTENCE_FILE1 =
+ String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 1);
+ public static final String PERSISTENCE_FILE2 =
+ String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 2);
+ public static final String PERSISTENCE_FILE3 =
+ String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 3);
+ public static final String PERSISTENCE_FILE4 =
+ String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 4);
+ public static final String PERSISTENCE_FILE5 =
+ String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 5);
+
+ private Context mContext;
+ @Mock
+ private AppOpsManager mMockedAppOpsManager;
+ @Mock
+ private UserManagerInternal mMockedUserManagerInternal;
+ @Mock
+ private ActivityManager mMockedActivityManager;
+ @Mock
+ private ActivityTaskManager mMockedActivityTaskManager;
+ @Mock
+ private TelephonyManager mMockedTelephonyManager;
+
+ @Test
+ public void testServiceInit() throws IOException {
+ MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.WARN)
+ .spyStatic(LocalServices.class)
+ .spyStatic(Environment.class)
+ .startMocking();
+
+ try {
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ spyOn(mContext);
+
+ doReturn(mMockedAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
+ doReturn(mMockedUserManagerInternal)
+ .when(() -> LocalServices.getService(UserManagerInternal.class));
+ doReturn(mMockedActivityManager).when(mContext).getSystemService(ActivityManager.class);
+ doReturn(mMockedActivityTaskManager)
+ .when(mContext).getSystemService(ActivityTaskManager.class);
+ doReturn(mMockedTelephonyManager).when(mContext).getSystemService(
+ TelephonyManager.class);
+
+ String dataDir = mContext.getApplicationInfo().dataDir;
+ doReturn(new File(dataDir)).when(() -> Environment.getDataSystemDirectory());
+
+ File onDeviceFile = new File(dataDir, "sensor_privacy.xml");
+ onDeviceFile.delete();
+
+ // Try all files with one known user
+ doReturn(new int[]{0}).when(mMockedUserManagerInternal).getUserIds();
+ doReturn(ExtendedMockito.mock(UserInfo.class)).when(mMockedUserManagerInternal)
+ .getUserInfo(0);
+ initServiceWithPersistenceFile(onDeviceFile, null);
+ initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE1);
+ initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE2);
+ initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE3);
+ initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE4);
+ initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE5);
+
+ // Try all files with two known users
+ doReturn(new int[]{0, 10}).when(mMockedUserManagerInternal).getUserIds();
+ doReturn(ExtendedMockito.mock(UserInfo.class)).when(mMockedUserManagerInternal)
+ .getUserInfo(0);
+ doReturn(ExtendedMockito.mock(UserInfo.class)).when(mMockedUserManagerInternal)
+ .getUserInfo(10);
+ initServiceWithPersistenceFile(onDeviceFile, null);
+ initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE1);
+ initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE2);
+ initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE3);
+ initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE4);
+ initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE5);
+
+ } finally {
+ mockitoSession.finishMocking();
+ }
+ }
+
+ private void initServiceWithPersistenceFile(File onDeviceFile,
+ String persistenceFilePath) throws IOException {
+ if (persistenceFilePath != null) {
+ Files.copy(mContext.getAssets().open(persistenceFilePath),
+ onDeviceFile.toPath());
+ }
+ new SensorPrivacyService(mContext);
+ onDeviceFile.delete();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
index 638b1b4..8344049 100644
--- a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
@@ -30,6 +30,7 @@
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManagerInternal;
import com.android.server.LocalServices;
@@ -57,8 +58,36 @@
private ProcessRecord mProcessRecord;
private static final long ZERO = 0L;
- private static final long USAGE_STATS_INTERACTION = 2 * 60 * 60 * 1000L;
- private static final long SERVICE_USAGE_INTERACTION = 30 * 60 * 1000;
+ private static final long USAGE_STATS_INTERACTION = 10 * 60 * 1000L;
+ private static final long SERVICE_USAGE_INTERACTION = 60 * 1000;
+
+ static class MyOomAdjuster extends OomAdjuster {
+
+ private final PlatformCompatCache mPlatformCompatCache;
+
+ MyOomAdjuster(ActivityManagerService service, ProcessList processList,
+ ActiveUids activeUids) {
+ super(service, processList, activeUids);
+ mPlatformCompatCache = new MyPlatformCompatCache(new long[]{});
+ }
+
+ static class MyPlatformCompatCache extends PlatformCompatCache {
+
+ MyPlatformCompatCache(long[] compatChanges) {
+ super(compatChanges);
+ }
+
+ @Override
+ boolean isChangeEnabled(long changeId, ApplicationInfo app, boolean defaultValue) {
+ return true;
+ }
+ }
+
+ @Override
+ protected OomAdjuster.PlatformCompatCache getPlatformCompatCache() {
+ return mPlatformCompatCache;
+ }
+ }
@BeforeClass
public static void setUpOnce() {
@@ -84,7 +113,7 @@
final AppProfiler profiler = mock(AppProfiler.class);
setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object());
setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler);
- sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList, null);
+ sService.mOomAdjuster = new MyOomAdjuster(sService, sService.mProcessList, null);
LocalServices.addService(UsageStatsManagerInternal.class,
mock(UsageStatsManagerInternal.class));
sService.mUsageStatsService = LocalServices.getService(UsageStatsManagerInternal.class);
@@ -119,8 +148,10 @@
// Ensure certain services and constants are defined properly
assertNotNull(sService.mUsageStatsService);
- assertEquals(USAGE_STATS_INTERACTION, sService.mConstants.USAGE_STATS_INTERACTION_INTERVAL);
- assertEquals(SERVICE_USAGE_INTERACTION, sService.mConstants.SERVICE_USAGE_INTERACTION_TIME);
+ assertEquals(USAGE_STATS_INTERACTION,
+ sService.mConstants.USAGE_STATS_INTERACTION_INTERVAL_POST_S);
+ assertEquals(SERVICE_USAGE_INTERACTION,
+ sService.mConstants.SERVICE_USAGE_INTERACTION_TIME_POST_S);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/utils/OWNERS b/services/tests/servicestests/src/com/android/server/utils/OWNERS
new file mode 100644
index 0000000..1853220
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/utils/OWNERS
@@ -0,0 +1,4 @@
+per-file WatchableTester.java = file:/services/core/java/com/android/server/pm/OWNERS
+per-file WatchableTester.java = shombert@google.com
+per-file WatcherTest.java = file:/services/core/java/com/android/server/pm/OWNERS
+per-file WatcherTest.java = shombert@google.com
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 13ef998..9226c0b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -62,7 +62,6 @@
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
import android.app.ActivityManager;
import android.app.TaskInfo;
@@ -1323,20 +1322,22 @@
}
@Test
- public void testNotifyOrientationChangeCausedByConfigurationChange() {
+ public void testTaskOrientationOnDisplayWindowingModeChange() {
+ // Skip unnecessary operations to speed up the test.
+ mAtm.deferWindowLayout();
final Task task = getTestTask();
final ActivityRecord activity = task.getTopMostActivity();
final DisplayContent display = task.getDisplayContent();
- display.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ mWm.setWindowingMode(display.mDisplayId, WINDOWING_MODE_FREEFORM);
activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation());
- verify(display).onDescendantOrientationChanged(same(task));
- reset(display);
+ assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, display.getLastOrientation());
- display.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ mWm.setWindowingMode(display.mDisplayId, WINDOWING_MODE_FULLSCREEN);
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, task.getOrientation());
- verify(display).onDescendantOrientationChanged(same(task));
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, display.getLastOrientation());
+ assertEquals(Configuration.ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
}
@Test
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index d77ab2b..f0d43fa 100755
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -2543,6 +2543,7 @@
} else if (mRttCall != null && parcelableCall.getParcelableRttCall() == null
&& parcelableCall.getIsRttCallChanged()) {
isRttChanged = true;
+ mRttCall.close();
mRttCall = null;
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2616ec8..114c90b 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -3402,7 +3402,10 @@
* Set uicc applications being enabled or disabled.
* The value will be remembered on the subscription and will be applied whenever it's present.
* If the subscription in currently present, it will also apply the setting to modem
- * immediately.
+ * immediately (the setting in the modem will not change until the modem receives and responds
+ * to the request, but typically this should only take a few seconds. The user visible setting
+ * available from SubscriptionInfo.areUiccApplicationsEnabled() will be updated
+ * immediately.)
*
* Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
*
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 05f5d29..78da86c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1936,11 +1936,9 @@
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
- * <li>If the calling app is the device or profile owner and has been granted the
- * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
- * owns a managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
- * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app is the device owner of a fully-managed device, a profile
+ * owner of an organization-owned device, or their delegates (see {@link
+ * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
* <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any
* active subscription.
* <li>If the calling app is the default SMS role holder (see {@link
@@ -1989,11 +1987,9 @@
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
- * <li>If the calling app is the device or profile owner and has been granted the
- * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
- * owns a managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
- * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app is the device owner of a fully-managed device, a profile
+ * owner of an organization-owned device, or their delegates (see {@link
+ * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
* <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any
* active subscription.
* <li>If the calling app is the default SMS role holder (see {@link
@@ -2058,11 +2054,9 @@
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
- * <li>If the calling app is the device or profile owner and has been granted the
- * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
- * owns a managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
- * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app is the device owner of a fully-managed device, a profile
+ * owner of an organization-owned device, or their delegates (see {@link
+ * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
* <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any
* active subscription.
* <li>If the calling app is the default SMS role holder (see {@link
@@ -2138,11 +2132,9 @@
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
- * <li>If the calling app is the device or profile owner and has been granted the
- * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
- * owns a managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
- * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app is the device owner of a fully-managed device, a profile
+ * owner of an organization-owned device, or their delegates (see {@link
+ * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
* <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any
* active subscription.
* <li>If the calling app is the default SMS role holder (see {@link
@@ -2176,11 +2168,9 @@
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
- * <li>If the calling app is the device or profile owner and has been granted the
- * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
- * owns a managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
- * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app is the device owner of a fully-managed device, a profile
+ * owner of an organization-owned device, or their delegates (see {@link
+ * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
* <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any
* active subscription.
* <li>If the calling app is the default SMS role holder (see {@link
@@ -2260,11 +2250,9 @@
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
- * <li>If the calling app is the device or profile owner and has been granted the
- * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
- * owns a managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
- * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app is the device owner of a fully-managed device, a profile
+ * owner of an organization-owned device, or their delegates (see {@link
+ * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
* <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
* <li>If the calling app is the default SMS role holder (see {@link
* RoleManager#isRoleHeld(String)}).
@@ -2297,11 +2285,9 @@
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
- * <li>If the calling app is the device or profile owner and has been granted the
- * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
- * owns a managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
- * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app is the device owner of a fully-managed device, a profile
+ * owner of an organization-owned device, or their delegates (see {@link
+ * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
* <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
* <li>If the calling app is the default SMS role holder (see {@link
* RoleManager#isRoleHeld(String)}).
@@ -3783,11 +3769,9 @@
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
- * <li>If the calling app is the device or profile owner and has been granted the
- * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
- * owns a managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
- * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app is the device owner of a fully-managed device, a profile
+ * owner of an organization-owned device, or their delegates (see {@link
+ * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
* <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
* <li>If the calling app is the default SMS role holder (see {@link
* RoleManager#isRoleHeld(String)}).
@@ -3821,11 +3805,9 @@
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
- * <li>If the calling app is the device or profile owner and has been granted the
- * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
- * owns a managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
- * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app is the device owner of a fully-managed device, a profile
+ * owner of an organization-owned device, or their delegates (see {@link
+ * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
* <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
* <li>If the calling app is the default SMS role holder (see {@link
* RoleManager#isRoleHeld(String)}).
@@ -4075,11 +4057,9 @@
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
- * <li>If the calling app is the device or profile owner and has been granted the
- * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
- * owns a managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
- * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app is the device owner of a fully-managed device, a profile
+ * owner of an organization-owned device, or their delegates (see {@link
+ * android.app.admin.DevicePolicyManager#getEnrollmentSpecificId()}).
* <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
* <li>If the calling app is the default SMS role holder (see {@link
* RoleManager#isRoleHeld(String)}).
@@ -15036,6 +15016,15 @@
"CAPABILITY_SLICING_CONFIG_SUPPORTED";
/**
+ * Indicates whether PHYSICAL_CHANNEL_CONFIG HAL1.6 is supported. See comments on
+ * respective methods for more information.
+ *
+ * @hide
+ */
+ public static final String CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED =
+ "CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED";
+
+ /**
* A list of the radio interface capability values with public valid constants.
*
* Here is a related list for the systemapi-only valid constants:
diff --git a/tests/HwAccelerationTest/res/layout/stretch_layout.xml b/tests/HwAccelerationTest/res/layout/stretch_layout.xml
index df5f297..81e0c01 100644
--- a/tests/HwAccelerationTest/res/layout/stretch_layout.xml
+++ b/tests/HwAccelerationTest/res/layout/stretch_layout.xml
@@ -16,7 +16,6 @@
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scroll_view"
- android:edgeEffectType="stretch"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
@@ -26,7 +25,6 @@
<HorizontalScrollView
android:id="@+id/horizontal_scroll_view"
- android:edgeEffectType="stretch"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 21386b8..f2c3b86 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -969,6 +969,8 @@
densities.add(dens);
}
+ std::vector<ResXMLParser::ResXMLPosition> tagsToSkip;
+
size_t len;
ResXMLTree::event_code_t code;
int depth = 0;
@@ -1091,6 +1093,42 @@
Vector<FeatureGroup> featureGroups;
KeyedVector<String8, ImpliedFeature> impliedFeatures;
+ {
+ int curDepth = 0;
+ ResXMLParser::ResXMLPosition initialPos;
+ tree.getPosition(&initialPos);
+
+ // Find all of the "uses-sdk" tags within the "manifest" tag.
+ std::vector<ResXMLParser::ResXMLPosition> usesSdkTagPositions;
+ ResXMLParser::ResXMLPosition curPos;
+ while ((code = tree.next()) != ResXMLTree::END_DOCUMENT &&
+ code != ResXMLTree::BAD_DOCUMENT) {
+ if (code == ResXMLTree::END_TAG) {
+ curDepth--;
+ continue;
+ }
+ if (code == ResXMLTree::START_TAG) {
+ curDepth++;
+ }
+ const char16_t* ctag16 = tree.getElementName(&len);
+ if (ctag16 == NULL || String8(ctag16) != "uses-sdk" || curDepth != 2) {
+ continue;
+ }
+
+ tree.getPosition(&curPos);
+ usesSdkTagPositions.emplace_back(curPos);
+ }
+
+ // Skip all "uses-sdk" tags besides the very last tag. The android runtime only uses
+ // the attribute values from the last defined tag.
+ for (size_t i = 0; i < usesSdkTagPositions.size() - 1; i++) {
+ tagsToSkip.emplace_back(usesSdkTagPositions[i]);
+ }
+
+ // Reset the position before parsing.
+ tree.setPosition(initialPos);
+ }
+
while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::END_TAG) {
@@ -1202,8 +1240,25 @@
if (code != ResXMLTree::START_TAG) {
continue;
}
+
depth++;
+ // If this tag should be skipped, skip to the end of this tag.
+ ResXMLParser::ResXMLPosition curPos;
+ tree.getPosition(&curPos);
+ if (std::find(tagsToSkip.begin(), tagsToSkip.end(), curPos) != tagsToSkip.end()) {
+ const int breakDepth = depth - 1;
+ while ((code = tree.next()) != ResXMLTree::END_DOCUMENT &&
+ code != ResXMLTree::BAD_DOCUMENT) {
+ if (code == ResXMLTree::END_TAG && --depth == breakDepth) {
+ break;
+ } else if (code == ResXMLTree::START_TAG) {
+ depth++;
+ }
+ }
+ continue;
+ }
+
const char16_t* ctag16 = tree.getElementName(&len);
if (ctag16 == NULL) {
SourcePos(manifestFile, tree.getLineNumber()).error(