Merge changes I0ac2d766,I198371b6 into main
* changes:
Filter out work profile widgets when not allowed by device policy
Allow disabling keyguard widgets for work profile
diff --git a/core/api/current.txt b/core/api/current.txt
index 13d5e03..c189a24c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -54479,7 +54479,6 @@
field @FlaggedApi("com.android.window.flags.cover_display_opt_in") public static final int COMPAT_SMALL_COVER_SCREEN_OPT_IN = 1; // 0x1
field public static final String PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE = "android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE";
field public static final String PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED = "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED";
- field @FlaggedApi("com.android.window.flags.untrusted_embedding_state_sharing") public static final String PROPERTY_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING_STATE_SHARING = "android.window.PROPERTY_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING_STATE_SHARING";
field public static final String PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION = "android.window.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION";
field public static final String PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH = "android.window.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH";
field public static final String PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE = "android.window.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE";
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 3b988e1..35ab5f0 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -589,6 +589,7 @@
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs();
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void forceRemoveActiveAdmin(@NonNull android.content.ComponentName, int);
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs();
+ method @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_internal_bug_fix_enabled") @RequiresPermission("android.permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT") public void forceSetMaxPolicyStorageLimit(int);
method public void forceUpdateUserSetupComplete(int);
method @NonNull public java.util.Set<java.lang.String> getDefaultCrossProfilePackages();
method @Deprecated public int getDeviceOwnerType(@NonNull android.content.ComponentName);
@@ -599,6 +600,7 @@
method public long getLastSecurityLogRetrievalTime();
method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public java.util.Set<java.lang.String> getPolicyExemptApps();
+ method @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_internal_bug_fix_enabled") @RequiresPermission("android.permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT") public int getPolicySizeForAdmin(@NonNull android.app.admin.EnforcingAdmin);
method public boolean isCurrentInputMethodSetByOwner();
method public boolean isFactoryResetProtectionPolicySupported();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public boolean isNewUserDisclaimerAcknowledged();
@@ -667,6 +669,10 @@
field @NonNull public static final android.app.admin.DpcAuthority DPC_AUTHORITY;
}
+ public final class EnforcingAdmin implements android.os.Parcelable {
+ ctor @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_internal_bug_fix_enabled") public EnforcingAdmin(@NonNull String, @NonNull android.app.admin.Authority, @NonNull android.os.UserHandle, @Nullable android.content.ComponentName);
+ }
+
public final class FlagUnion extends android.app.admin.ResolutionMechanism<java.lang.Integer> {
method public int describeContents();
method public void writeToParcel(@NonNull android.os.Parcel, int);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3575545..f644185 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2608,7 +2608,14 @@
break;
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
- mTransactionExecutor.execute(transaction);
+ final ClientTransactionListenerController controller =
+ ClientTransactionListenerController.getInstance();
+ controller.onClientTransactionStarted();
+ try {
+ mTransactionExecutor.execute(transaction);
+ } finally {
+ controller.onClientTransactionFinished();
+ }
if (isSystem()) {
// Client transactions inside system process are recycled on the client side
// instead of ClientLifecycleManager to avoid being cleared before this
@@ -6747,6 +6754,21 @@
void handleActivityConfigurationChanged(@NonNull ActivityClientRecord r,
@NonNull Configuration overrideConfig, int displayId,
@NonNull ActivityWindowInfo activityWindowInfo, boolean alwaysReportChange) {
+ final ClientTransactionListenerController controller =
+ ClientTransactionListenerController.getInstance();
+ final Context contextToUpdate = r.activity;
+ controller.onContextConfigurationPreChanged(contextToUpdate);
+ try {
+ handleActivityConfigurationChangedInner(r, overrideConfig, displayId,
+ activityWindowInfo, alwaysReportChange);
+ } finally {
+ controller.onContextConfigurationPostChanged(contextToUpdate);
+ }
+ }
+
+ private void handleActivityConfigurationChangedInner(@NonNull ActivityClientRecord r,
+ @NonNull Configuration overrideConfig, int displayId,
+ @NonNull ActivityWindowInfo activityWindowInfo, boolean alwaysReportChange) {
synchronized (mPendingOverrideConfigs) {
final Configuration pendingOverrideConfig = mPendingOverrideConfigs.get(r.token);
if (overrideConfig.isOtherSeqNewer(pendingOverrideConfig)) {
diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java
index 18dc1ce..62a50db 100644
--- a/core/java/android/app/ConfigurationController.java
+++ b/core/java/android/app/ConfigurationController.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.servertransaction.ClientTransactionListenerController;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.res.CompatibilityInfo;
@@ -145,6 +146,24 @@
*/
void handleConfigurationChanged(@Nullable Configuration config,
@Nullable CompatibilityInfo compat) {
+ final ClientTransactionListenerController controller =
+ ClientTransactionListenerController.getInstance();
+ final Context contextToUpdate = ActivityThread.currentApplication();
+ controller.onContextConfigurationPreChanged(contextToUpdate);
+ try {
+ handleConfigurationChangedInner(config, compat);
+ } finally {
+ controller.onContextConfigurationPostChanged(contextToUpdate);
+ }
+ }
+
+ /**
+ * Update the configuration to latest.
+ * @param config The new configuration.
+ * @param compat The new compatibility information.
+ */
+ private void handleConfigurationChangedInner(@Nullable Configuration config,
+ @Nullable CompatibilityInfo compat) {
int configDiff;
boolean equivalent;
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 8f81ae2..cf06416 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -50,8 +50,8 @@
void cancelAllNotifications(String pkg, int userId);
void clearData(String pkg, int uid, boolean fromApp);
- void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration, boolean isUiContext, int displayId, @nullable ITransientNotificationCallback callback);
- void enqueueToast(String pkg, IBinder token, ITransientNotification callback, int duration, boolean isUiContext, int displayId);
+ boolean enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration, boolean isUiContext, int displayId, @nullable ITransientNotificationCallback callback);
+ boolean enqueueToast(String pkg, IBinder token, ITransientNotification callback, int duration, boolean isUiContext, int displayId);
void cancelToast(String pkg, IBinder token);
void finishToken(String pkg, IBinder token);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index fe261be..d9e0413 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -23,6 +23,7 @@
import static android.app.admin.DevicePolicyResources.UNDEFINED;
import static android.graphics.drawable.Icon.TYPE_URI;
import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP;
+import static android.app.Flags.cleanUpSpansAndNewLines;
import static android.app.Flags.evenlyDividedCallStyleActionLayout;
import static android.app.Flags.updateRankingTime;
@@ -3124,9 +3125,29 @@
+ " instance is a custom Parcelable and not allowed in Notification");
return cs.toString();
}
+ if (Flags.cleanUpSpansAndNewLines()) {
+ return stripStyling(cs);
+ }
+
return removeTextSizeSpans(cs);
}
+ private static CharSequence stripStyling(@Nullable CharSequence cs) {
+ if (cs == null) {
+ return cs;
+ }
+
+ return cs.toString();
+ }
+
+ private static CharSequence cleanUpNewLines(@Nullable CharSequence charSequence) {
+ if (charSequence == null) {
+ return charSequence;
+ }
+
+ return charSequence.toString().replaceAll("[\r\n]+", "\n");
+ }
+
private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
if (charSequence instanceof Spanned) {
Spanned ss = (Spanned) charSequence;
@@ -5505,7 +5526,8 @@
boolean hasSecondLine = showProgress;
if (p.hasTitle()) {
contentView.setViewVisibility(p.mTitleViewId, View.VISIBLE);
- contentView.setTextViewText(p.mTitleViewId, ensureColorSpanContrast(p.mTitle, p));
+ contentView.setTextViewText(p.mTitleViewId,
+ ensureColorSpanContrastOrStripStyling(p.mTitle, p));
setTextViewColorPrimary(contentView, p.mTitleViewId, p);
} else if (p.mTitleViewId != R.id.title) {
// This alternate title view ID is not cleared by resetStandardTemplate
@@ -5515,7 +5537,8 @@
if (p.mText != null && p.mText.length() != 0
&& (!showProgress || p.mAllowTextWithProgress)) {
contentView.setViewVisibility(p.mTextViewId, View.VISIBLE);
- contentView.setTextViewText(p.mTextViewId, ensureColorSpanContrast(p.mText, p));
+ contentView.setTextViewText(p.mTextViewId,
+ ensureColorSpanContrastOrStripStyling(p.mText, p));
setTextViewColorSecondary(contentView, p.mTextViewId, p);
hasSecondLine = true;
} else if (p.mTextViewId != R.id.text) {
@@ -5804,7 +5827,7 @@
headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
}
if (!TextUtils.isEmpty(headerText)) {
- contentView.setTextViewText(R.id.header_text, ensureColorSpanContrast(
+ contentView.setTextViewText(R.id.header_text, ensureColorSpanContrastOrStripStyling(
processLegacyText(headerText), p));
setTextViewColorSecondary(contentView, R.id.header_text, p);
contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
@@ -5826,8 +5849,9 @@
return false;
}
if (!TextUtils.isEmpty(p.mHeaderTextSecondary)) {
- contentView.setTextViewText(R.id.header_text_secondary, ensureColorSpanContrast(
- processLegacyText(p.mHeaderTextSecondary), p));
+ contentView.setTextViewText(R.id.header_text_secondary,
+ ensureColorSpanContrastOrStripStyling(
+ processLegacyText(p.mHeaderTextSecondary), p));
setTextViewColorSecondary(contentView, R.id.header_text_secondary, p);
contentView.setViewVisibility(R.id.header_text_secondary, View.VISIBLE);
if (hasTextToLeft) {
@@ -6048,7 +6072,7 @@
big.setViewVisibility(R.id.notification_material_reply_text_1_container,
View.VISIBLE);
big.setTextViewText(R.id.notification_material_reply_text_1,
- ensureColorSpanContrast(replyText[0].getText(), p));
+ ensureColorSpanContrastOrStripStyling(replyText[0].getText(), p));
setTextViewColorSecondary(big, R.id.notification_material_reply_text_1, p);
big.setViewVisibility(R.id.notification_material_reply_progress,
showSpinner ? View.VISIBLE : View.GONE);
@@ -6060,7 +6084,7 @@
&& p.maxRemoteInputHistory > 1) {
big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
big.setTextViewText(R.id.notification_material_reply_text_2,
- ensureColorSpanContrast(replyText[1].getText(), p));
+ ensureColorSpanContrastOrStripStyling(replyText[1].getText(), p));
setTextViewColorSecondary(big, R.id.notification_material_reply_text_2, p);
if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2].getText())
@@ -6068,7 +6092,7 @@
big.setViewVisibility(
R.id.notification_material_reply_text_3, View.VISIBLE);
big.setTextViewText(R.id.notification_material_reply_text_3,
- ensureColorSpanContrast(replyText[2].getText(), p));
+ ensureColorSpanContrastOrStripStyling(replyText[2].getText(), p));
setTextViewColorSecondary(big, R.id.notification_material_reply_text_3, p);
}
}
@@ -6500,21 +6524,37 @@
mContext, getColors(p).getBackgroundColor(), mInNightMode),
R.dimen.notification_action_disabled_container_alpha);
}
- if (isLegacy()) {
- title = ContrastColorUtil.clearColorSpans(title);
- } else {
- // Check for a full-length span color to use as the button fill color.
- Integer fullLengthColor = getFullLengthSpanColor(title);
- if (fullLengthColor != null) {
- // Ensure the custom button fill has 1.3:1 contrast w/ notification bg.
- int notifBackgroundColor = getColors(p).getBackgroundColor();
- buttonFillColor = ensureButtonFillContrast(
- fullLengthColor, notifBackgroundColor);
+ if (Flags.cleanUpSpansAndNewLines()) {
+ if (!isLegacy()) {
+ // Check for a full-length span color to use as the button fill color.
+ Integer fullLengthColor = getFullLengthSpanColor(title);
+ if (fullLengthColor != null) {
+ // Ensure the custom button fill has 1.3:1 contrast w/ notification bg.
+ int notifBackgroundColor = getColors(p).getBackgroundColor();
+ buttonFillColor = ensureButtonFillContrast(
+ fullLengthColor, notifBackgroundColor);
+ }
}
- // Remove full-length color spans and ensure text contrast with the button fill.
- title = ContrastColorUtil.ensureColorSpanContrast(title, buttonFillColor);
+ } else {
+ if (isLegacy()) {
+ title = ContrastColorUtil.clearColorSpans(title);
+ } else {
+ // Check for a full-length span color to use as the button fill color.
+ Integer fullLengthColor = getFullLengthSpanColor(title);
+ if (fullLengthColor != null) {
+ // Ensure the custom button fill has 1.3:1 contrast w/ notification bg.
+ int notifBackgroundColor = getColors(p).getBackgroundColor();
+ buttonFillColor = ensureButtonFillContrast(
+ fullLengthColor, notifBackgroundColor);
+ }
+ // Remove full-length color spans
+ // and ensure text contrast with the button fill.
+ title = ContrastColorUtil.ensureColorSpanContrast(title, buttonFillColor);
+ }
}
- final CharSequence label = ensureColorSpanContrast(title, p);
+
+
+ final CharSequence label = ensureColorSpanContrastOrStripStyling(title, p);
if (p.mCallStyleActions && evenlyDividedCallStyleActionLayout()) {
if (CallStyle.DEBUG_NEW_ACTION_LAYOUT) {
Log.d(TAG, "new action layout enabled, gluing instead of setting text");
@@ -6554,7 +6594,7 @@
button.setIntDimen(R.id.action0, "setMinimumWidth", minWidthDimen);
}
} else {
- button.setTextViewText(R.id.action0, ensureColorSpanContrast(
+ button.setTextViewText(R.id.action0, ensureColorSpanContrastOrStripStyling(
action.title, p));
button.setTextColor(R.id.action0, getStandardActionColor(p));
}
@@ -6629,6 +6669,26 @@
}
/**
+ * @hide
+ */
+ public CharSequence ensureColorSpanContrastOrStripStyling(CharSequence cs,
+ StandardTemplateParams p) {
+ return ensureColorSpanContrastOrStripStyling(cs, getBackgroundColor(p));
+ }
+
+ /**
+ * @hide
+ */
+ public CharSequence ensureColorSpanContrastOrStripStyling(CharSequence cs,
+ int buttonFillColor) {
+ if (Flags.cleanUpSpansAndNewLines()) {
+ return stripStyling(cs);
+ }
+
+ return ContrastColorUtil.ensureColorSpanContrast(cs, buttonFillColor);
+ }
+
+ /**
* Ensures contrast on color spans against a background color.
* Note that any full-length color spans will be removed instead of being contrasted.
*
@@ -7853,8 +7913,9 @@
RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource(),
p, null /* result */);
if (mSummaryTextSet) {
- contentView.setTextViewText(R.id.text, mBuilder.ensureColorSpanContrast(
- mBuilder.processLegacyText(mSummaryText), p));
+ contentView.setTextViewText(R.id.text,
+ mBuilder.ensureColorSpanContrastOrStripStyling(
+ mBuilder.processLegacyText(mSummaryText), p));
mBuilder.setTextViewColorSecondary(contentView, R.id.text, p);
contentView.setViewVisibility(R.id.text, View.VISIBLE);
}
@@ -8017,6 +8078,9 @@
*/
public BigTextStyle bigText(CharSequence cs) {
mBigText = safeCharSequence(cs);
+ if (Flags.cleanUpSpansAndNewLines()) {
+ mBigText = cleanUpNewLines(mBigText);
+ }
return this;
}
@@ -8517,7 +8581,7 @@
for (int i = 0; i < N; i++) {
final Message m = messages.get(i);
if (ensureContrast) {
- m.ensureColorContrast(backgroundColor);
+ m.ensureColorContrastOrStripStyling(backgroundColor);
}
bundles[i] = m.toBundle();
}
@@ -8543,7 +8607,9 @@
} else {
title = sender;
}
-
+ if (Flags.cleanUpSpansAndNewLines()) {
+ title = stripStyling(title);
+ }
if (title != null) {
extras.putCharSequence(EXTRA_TITLE, title);
}
@@ -8995,6 +9061,17 @@
}
/**
+ * Strip styling or updates TextAppearance spans in message text.
+ * @hide
+ */
+ public void ensureColorContrastOrStripStyling(int backgroundColor) {
+ if (Flags.cleanUpSpansAndNewLines()) {
+ mText = stripStyling(mText);
+ } else {
+ ensureColorContrast(backgroundColor);
+ }
+ }
+ /**
* Updates TextAppearance spans in the message text so it has sufficient contrast
* against its background.
* @hide
@@ -9324,7 +9401,8 @@
if (!TextUtils.isEmpty(str)) {
contentView.setViewVisibility(rowIds[i], View.VISIBLE);
contentView.setTextViewText(rowIds[i],
- mBuilder.ensureColorSpanContrast(mBuilder.processLegacyText(str), p));
+ mBuilder.ensureColorSpanContrastOrStripStyling(
+ mBuilder.processLegacyText(str), p));
mBuilder.setTextViewColorSecondary(contentView, rowIds[i], p);
contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
if (first) {
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 02d6944..257aff0 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1603,8 +1603,18 @@
@SetWallpaperFlags int which, boolean originalBitmap) {
checkExactlyOneWallpaperFlagSet(which);
try {
- return sGlobals.mService.getBitmapCrops(displaySizes, which, originalBitmap,
- mContext.getUserId());
+ List<Rect> result = sGlobals.mService.getBitmapCrops(
+ displaySizes, which, originalBitmap, mContext.getUserId());
+ if (result != null) return result;
+ // mService.getBitmapCrops returns null if the requested wallpaper is an ImageWallpaper,
+ // but there are no crop hints and the bitmap size is unknown to the service (this
+ // mostly happens for the default wallpaper). In that case, fetch the bitmap dimensions
+ // and use the other getBitmapCrops API with no cropHints to figure out the crops.
+ Rect bitmapDimensions = peekBitmapDimensions(which, true);
+ if (bitmapDimensions == null) return List.of();
+ Point bitmapSize = new Point(bitmapDimensions.width(), bitmapDimensions.height());
+ return getBitmapCrops(bitmapSize, displaySizes, null);
+
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/AccountTypePolicyKey.java b/core/java/android/app/admin/AccountTypePolicyKey.java
index 51f3137..02e492b 100644
--- a/core/java/android/app/admin/AccountTypePolicyKey.java
+++ b/core/java/android/app/admin/AccountTypePolicyKey.java
@@ -54,7 +54,7 @@
@TestApi
public AccountTypePolicyKey(@NonNull String key, @NonNull String accountType) {
super(key);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
PolicySizeVerifier.enforceMaxStringLength(accountType, "accountType");
}
mAccountType = Objects.requireNonNull((accountType));
diff --git a/core/java/android/app/admin/BundlePolicyValue.java b/core/java/android/app/admin/BundlePolicyValue.java
index cb5e986..c993671 100644
--- a/core/java/android/app/admin/BundlePolicyValue.java
+++ b/core/java/android/app/admin/BundlePolicyValue.java
@@ -31,7 +31,7 @@
public BundlePolicyValue(Bundle value) {
super(value);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
PolicySizeVerifier.enforceMaxBundleFieldsLength(value);
}
}
diff --git a/core/java/android/app/admin/ComponentNamePolicyValue.java b/core/java/android/app/admin/ComponentNamePolicyValue.java
index a957dbf..a7a2f7d 100644
--- a/core/java/android/app/admin/ComponentNamePolicyValue.java
+++ b/core/java/android/app/admin/ComponentNamePolicyValue.java
@@ -31,7 +31,7 @@
public ComponentNamePolicyValue(@NonNull ComponentName value) {
super(value);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
PolicySizeVerifier.enforceMaxComponentNameLength(value);
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ae6ed1c..ba91be9 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -54,6 +54,7 @@
import static android.Manifest.permission.SET_TIME;
import static android.Manifest.permission.SET_TIME_ZONE;
import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
+import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED;
import static android.app.admin.flags.Flags.FLAG_DEVICE_THEFT_API_ENABLED;
import static android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED;
import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED;
@@ -17572,6 +17573,48 @@
}
/**
+ * Force sets the maximum storage size allowed for policies associated with an admin regardless
+ * of the default value set in the system, unlike {@link #setMaxPolicyStorageLimit} which can
+ * only set it to a value higher than the default value set by the system.Setting a limit of -1
+ * effectively removes any storage restrictions.
+ *
+ * @param storageLimit Maximum storage allowed in bytes. Use -1 to disable limits.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT)
+ @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED)
+ public void forceSetMaxPolicyStorageLimit(int storageLimit) {
+ if (mService != null) {
+ try {
+ mService.forceSetMaxPolicyStorageLimit(mContext.getPackageName(), storageLimit);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Retrieves the size of the current policies set by the {@code admin}.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT)
+ @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED)
+ public int getPolicySizeForAdmin(@NonNull EnforcingAdmin admin) {
+ if (mService != null) {
+ try {
+ return mService.getPolicySizeForAdmin(mContext.getPackageName(), admin);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return -1;
+ }
+
+ /**
* @return The headless device owner mode for the current set DO, returns
* {@link DeviceAdminInfo#HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED} if no DO is set.
*
diff --git a/core/java/android/app/admin/EnforcingAdmin.java b/core/java/android/app/admin/EnforcingAdmin.java
index 7c718f6..f70a53f 100644
--- a/core/java/android/app/admin/EnforcingAdmin.java
+++ b/core/java/android/app/admin/EnforcingAdmin.java
@@ -16,9 +16,13 @@
package android.app.admin;
+import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED;
+
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
@@ -60,6 +64,8 @@
*
* @hide
*/
+ @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED)
+ @TestApi
public EnforcingAdmin(
@NonNull String packageName, @NonNull Authority authority,
@NonNull UserHandle userHandle, @Nullable ComponentName componentName) {
@@ -101,6 +107,16 @@
return mUserHandle;
}
+ /**
+ * Returns the {@link ComponentName} of the admin if applicable.
+ *
+ * @hide
+ */
+ @Nullable
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 2002326..d183713 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -623,8 +623,10 @@
int[] getSubscriptionIds(String callerPackageName);
- void setMaxPolicyStorageLimit(String packageName, int storageLimit);
- int getMaxPolicyStorageLimit(String packageName);
+ void setMaxPolicyStorageLimit(String callerPackageName, int storageLimit);
+ void forceSetMaxPolicyStorageLimit(String callerPackageName, int storageLimit);
+ int getMaxPolicyStorageLimit(String callerPackageName);
+ int getPolicySizeForAdmin(String callerPackageName, in EnforcingAdmin admin);
int getHeadlessDeviceOwnerMode(String callerPackageName);
}
diff --git a/core/java/android/app/admin/LockTaskPolicy.java b/core/java/android/app/admin/LockTaskPolicy.java
index a36ea05..68b4ad8 100644
--- a/core/java/android/app/admin/LockTaskPolicy.java
+++ b/core/java/android/app/admin/LockTaskPolicy.java
@@ -135,7 +135,7 @@
}
private void setPackagesInternal(Set<String> packages) {
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
for (String p : packages) {
PolicySizeVerifier.enforceMaxPackageNameLength(p);
}
diff --git a/core/java/android/app/admin/PackagePermissionPolicyKey.java b/core/java/android/app/admin/PackagePermissionPolicyKey.java
index 389585f..1a04f6c 100644
--- a/core/java/android/app/admin/PackagePermissionPolicyKey.java
+++ b/core/java/android/app/admin/PackagePermissionPolicyKey.java
@@ -59,7 +59,7 @@
public PackagePermissionPolicyKey(@NonNull String identifier, @NonNull String packageName,
@NonNull String permissionName) {
super(identifier);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
PolicySizeVerifier.enforceMaxStringLength(permissionName, "permissionName");
}
diff --git a/core/java/android/app/admin/PackagePolicyKey.java b/core/java/android/app/admin/PackagePolicyKey.java
index 68dc797..9e31a23 100644
--- a/core/java/android/app/admin/PackagePolicyKey.java
+++ b/core/java/android/app/admin/PackagePolicyKey.java
@@ -55,7 +55,7 @@
@TestApi
public PackagePolicyKey(@NonNull String key, @NonNull String packageName) {
super(key);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
}
mPackageName = Objects.requireNonNull((packageName));
diff --git a/core/java/android/app/admin/StringPolicyValue.java b/core/java/android/app/admin/StringPolicyValue.java
index 8995c0f..6efe9ad 100644
--- a/core/java/android/app/admin/StringPolicyValue.java
+++ b/core/java/android/app/admin/StringPolicyValue.java
@@ -30,7 +30,7 @@
public StringPolicyValue(@NonNull String value) {
super(value);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
PolicySizeVerifier.enforceMaxStringLength(value, "policyValue");
}
}
diff --git a/core/java/android/app/admin/StringSetPolicyValue.java b/core/java/android/app/admin/StringSetPolicyValue.java
index f37dfee..12b11f4 100644
--- a/core/java/android/app/admin/StringSetPolicyValue.java
+++ b/core/java/android/app/admin/StringSetPolicyValue.java
@@ -32,7 +32,7 @@
public StringSetPolicyValue(@NonNull Set<String> value) {
super(value);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
for (String str : value) {
PolicySizeVerifier.enforceMaxStringLength(str, "policyValue");
}
diff --git a/core/java/android/app/admin/UserRestrictionPolicyKey.java b/core/java/android/app/admin/UserRestrictionPolicyKey.java
index ee90ccd..9054287 100644
--- a/core/java/android/app/admin/UserRestrictionPolicyKey.java
+++ b/core/java/android/app/admin/UserRestrictionPolicyKey.java
@@ -45,7 +45,7 @@
@TestApi
public UserRestrictionPolicyKey(@NonNull String identifier, @NonNull String restriction) {
super(identifier);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
PolicySizeVerifier.enforceMaxStringLength(restriction, "restriction");
}
mRestriction = Objects.requireNonNull(restriction);
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index fe2f764..6a07484 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -27,6 +27,17 @@
}
flag {
+ name: "device_policy_size_tracking_internal_bug_fix_enabled"
+ namespace: "enterprise"
+ description: "Bug fix for tracking the total policy size and have a max threshold"
+ bug: "281543351"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+
+flag {
name: "onboarding_bugreport_v2_enabled"
is_exported: true
namespace: "enterprise"
@@ -232,3 +243,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "headless_single_user_bad_device_admin_state_fix"
+ namespace: "enterprise"
+ description: "Fix the bad state in DPMS caused by an earlier bug related to the headless single user change"
+ bug: "332477138"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 0214d40..1f6ac2e 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -109,4 +109,11 @@
namespace: "systemui"
description: "No notifs can use USAGE_UNKNOWN or USAGE_MEDIA"
bug: "331793339"
+}
+
+flag {
+ name: "clean_up_spans_and_new_lines"
+ namespace: "systemui"
+ description: "Cleans up spans and unnecessary new lines from standard notification templates"
+ bug: "313439845"
}
\ No newline at end of file
diff --git a/core/java/android/app/servertransaction/ClientTransactionItem.java b/core/java/android/app/servertransaction/ClientTransactionItem.java
index a8d61db..6e7e930 100644
--- a/core/java/android/app/servertransaction/ClientTransactionItem.java
+++ b/core/java/android/app/servertransaction/ClientTransactionItem.java
@@ -53,6 +53,7 @@
return true;
}
+ // TODO(b/260873529): cleanup
/**
* If this {@link ClientTransactionItem} is updating configuration, returns the {@link Context}
* it is updating; otherwise, returns {@code null}.
diff --git a/core/java/android/app/servertransaction/ClientTransactionListenerController.java b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
index c55b0f1..722d5f0 100644
--- a/core/java/android/app/servertransaction/ClientTransactionListenerController.java
+++ b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
@@ -16,6 +16,8 @@
package android.app.servertransaction;
+import static android.app.WindowConfiguration.areConfigurationsEqualForDisplay;
+
import static com.android.window.flags.Flags.activityWindowInfoFlag;
import static com.android.window.flags.Flags.bundleClientTransactionFlag;
@@ -24,8 +26,11 @@
import android.annotation.NonNull;
import android.app.Activity;
import android.app.ActivityThread;
+import android.content.Context;
+import android.content.res.Configuration;
import android.hardware.display.DisplayManagerGlobal;
import android.os.IBinder;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.window.ActivityWindowInfo;
@@ -51,6 +56,15 @@
private final ArraySet<BiConsumer<IBinder, ActivityWindowInfo>>
mActivityWindowInfoChangedListeners = new ArraySet<>();
+ /**
+ * Keeps track of the Context whose Configuration will get updated, mapping to the config before
+ * the change.
+ */
+ private final ArrayMap<Context, Configuration> mContextToPreChangedConfigMap = new ArrayMap<>();
+
+ /** Whether there is an {@link ClientTransaction} being executed. */
+ private boolean mIsClientTransactionExecuting;
+
/** Gets the singleton controller. */
@NonNull
public static ClientTransactionListenerController getInstance() {
@@ -126,18 +140,92 @@
}
}
- /**
- * Called when receives a {@link ClientTransaction} that is updating display-related
- * window configuration.
- */
- public void onDisplayChanged(int displayId) {
- if (!bundleClientTransactionFlag()) {
- return;
- }
- if (ActivityThread.isSystem()) {
+ /** Called when starts executing a remote {@link ClientTransaction}. */
+ public void onClientTransactionStarted() {
+ mIsClientTransactionExecuting = true;
+ }
+
+ /** Called when finishes executing a remote {@link ClientTransaction}. */
+ public void onClientTransactionFinished() {
+ notifyDisplayManagerIfNeeded();
+ mIsClientTransactionExecuting = false;
+ }
+
+ /** Called before updating the Configuration of the given {@code context}. */
+ public void onContextConfigurationPreChanged(@NonNull Context context) {
+ if (!bundleClientTransactionFlag() || ActivityThread.isSystem()) {
// Not enable for system server.
return;
}
+ if (mContextToPreChangedConfigMap.containsKey(context)) {
+ // There is an earlier change that hasn't been reported yet.
+ return;
+ }
+ mContextToPreChangedConfigMap.put(context,
+ new Configuration(context.getResources().getConfiguration()));
+ }
+
+ /** Called after updating the Configuration of the given {@code context}. */
+ public void onContextConfigurationPostChanged(@NonNull Context context) {
+ if (!bundleClientTransactionFlag() || ActivityThread.isSystem()) {
+ // Not enable for system server.
+ return;
+ }
+ if (mIsClientTransactionExecuting) {
+ // Wait until #onClientTransactionFinished to prevent it from triggering the same
+ // #onDisplayChanged multiple times within the same ClientTransaction.
+ return;
+ }
+ final Configuration preChangedConfig = mContextToPreChangedConfigMap.remove(context);
+ if (preChangedConfig != null && shouldReportDisplayChange(context, preChangedConfig)) {
+ onDisplayChanged(context.getDisplayId());
+ }
+ }
+
+ /**
+ * When {@link Configuration} is changed, we want to trigger display change callback as well,
+ * because Display reads some fields from {@link Configuration}.
+ */
+ private void notifyDisplayManagerIfNeeded() {
+ if (mContextToPreChangedConfigMap.isEmpty()) {
+ return;
+ }
+ // Whether the configuration change should trigger DisplayListener#onDisplayChanged.
+ try {
+ // Calculate display ids that have config changed.
+ final ArraySet<Integer> configUpdatedDisplayIds = new ArraySet<>();
+ final int contextCount = mContextToPreChangedConfigMap.size();
+ for (int i = 0; i < contextCount; i++) {
+ final Context context = mContextToPreChangedConfigMap.keyAt(i);
+ final Configuration preChangedConfig = mContextToPreChangedConfigMap.valueAt(i);
+ if (shouldReportDisplayChange(context, preChangedConfig)) {
+ configUpdatedDisplayIds.add(context.getDisplayId());
+ }
+ }
+
+ // Dispatch the display changed callbacks.
+ final int displayCount = configUpdatedDisplayIds.size();
+ for (int i = 0; i < displayCount; i++) {
+ final int displayId = configUpdatedDisplayIds.valueAt(i);
+ onDisplayChanged(displayId);
+ }
+ } finally {
+ mContextToPreChangedConfigMap.clear();
+ }
+ }
+
+ private boolean shouldReportDisplayChange(@NonNull Context context,
+ @NonNull Configuration preChangedConfig) {
+ final Configuration postChangedConfig = context.getResources().getConfiguration();
+ return !areConfigurationsEqualForDisplay(postChangedConfig, preChangedConfig);
+ }
+
+ /**
+ * Called when receives a {@link Configuration} changed event that is updating display-related
+ * window configuration.
+ */
+ @VisibleForTesting
+ public void onDisplayChanged(int displayId) {
mDisplayManager.handleDisplayChangeFromWindowManager(displayId);
}
}
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index c837191..480205e 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -16,7 +16,6 @@
package android.app.servertransaction;
-import static android.app.WindowConfiguration.areConfigurationsEqualForDisplay;
import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE;
import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY;
import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
@@ -32,17 +31,12 @@
import static android.app.servertransaction.TransactionExecutorHelper.tId;
import static android.app.servertransaction.TransactionExecutorHelper.transactionToString;
-import static com.android.window.flags.Flags.bundleClientTransactionFlag;
-
import android.annotation.NonNull;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.content.Context;
-import android.content.res.Configuration;
import android.os.IBinder;
import android.os.Trace;
-import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.IntArray;
import android.util.Slog;
@@ -63,12 +57,6 @@
private final PendingTransactionActions mPendingActions = new PendingTransactionActions();
private final TransactionExecutorHelper mHelper = new TransactionExecutorHelper();
- /**
- * Keeps track of the Context whose Configuration got updated within a transaction, mapping to
- * the config before the transaction.
- */
- private final ArrayMap<Context, Configuration> mContextToPreChangedConfigMap = new ArrayMap<>();
-
/** Initialize an instance with transaction handler, that will execute all requested actions. */
public TransactionExecutor(@NonNull ClientTransactionHandler clientTransactionHandler) {
mTransactionHandler = clientTransactionHandler;
@@ -104,37 +92,6 @@
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
- if (!mContextToPreChangedConfigMap.isEmpty()) {
- // Whether this transaction should trigger DisplayListener#onDisplayChanged.
- try {
- // Calculate display ids that have config changed.
- final ArraySet<Integer> configUpdatedDisplayIds = new ArraySet<>();
- final int contextCount = mContextToPreChangedConfigMap.size();
- for (int i = 0; i < contextCount; i++) {
- final Context context = mContextToPreChangedConfigMap.keyAt(i);
- final Configuration preTransactionConfig =
- mContextToPreChangedConfigMap.valueAt(i);
- final Configuration postTransactionConfig = context.getResources()
- .getConfiguration();
- if (!areConfigurationsEqualForDisplay(
- postTransactionConfig, preTransactionConfig)) {
- configUpdatedDisplayIds.add(context.getDisplayId());
- }
- }
-
- // Dispatch the display changed callbacks.
- final ClientTransactionListenerController controller =
- ClientTransactionListenerController.getInstance();
- final int displayCount = configUpdatedDisplayIds.size();
- for (int i = 0; i < displayCount; i++) {
- final int displayId = configUpdatedDisplayIds.valueAt(i);
- controller.onDisplayChanged(displayId);
- }
- } finally {
- mContextToPreChangedConfigMap.clear();
- }
- }
-
mPendingActions.clear();
if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction");
}
@@ -214,20 +171,6 @@
}
}
- final boolean shouldTrackConfigUpdatedContext =
- // No configuration change for local transaction.
- !mTransactionHandler.isExecutingLocalTransaction()
- && bundleClientTransactionFlag();
- final Context configUpdatedContext = shouldTrackConfigUpdatedContext
- ? item.getContextToUpdate(mTransactionHandler)
- : null;
- if (configUpdatedContext != null
- && !mContextToPreChangedConfigMap.containsKey(configUpdatedContext)) {
- // Keep track of the first pre-executed config of each changed Context.
- mContextToPreChangedConfigMap.put(configUpdatedContext,
- new Configuration(configUpdatedContext.getResources().getConfiguration()));
- }
-
item.execute(mTransactionHandler, mPendingActions);
item.postExecute(mTransactionHandler, mPendingActions);
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index ec67212..89ab105 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -364,6 +364,14 @@
public abstract List<RefreshRateLimitation> getRefreshRateLimitations(int displayId);
/**
+ * Returns if vrr support is enabled for specified display
+ *
+ * @param displayId The id of the display.
+ * @return true if associated display supports dvrr
+ */
+ public abstract boolean isVrrSupportEnabled(int displayId);
+
+ /**
* For the given displayId, updates if WindowManager is responsible for mirroring on that
* display. If {@code false}, then SurfaceFlinger performs no layer mirroring to the
* given display.
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index f4dadbb..beb4d95 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -17,6 +17,7 @@
package android.view;
import static com.android.text.flags.Flags.handwritingCursorPosition;
+import static com.android.text.flags.Flags.handwritingUnsupportedMessage;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
@@ -607,7 +608,9 @@
final View candidateView = findBestCandidateView(hoverX, hoverY, /* isHover */ true);
if (candidateView != null) {
- mCachedHoverTarget = new WeakReference<>(candidateView);
+ if (!handwritingUnsupportedMessage()) {
+ mCachedHoverTarget = new WeakReference<>(candidateView);
+ }
return candidateView;
}
}
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
index 253073a..69228ca 100644
--- a/core/java/android/view/HapticFeedbackConstants.java
+++ b/core/java/android/view/HapticFeedbackConstants.java
@@ -83,11 +83,7 @@
*/
public static final int TEXT_HANDLE_MOVE = 9;
- /**
- * The user unlocked the device
- * @hide
- */
- public static final int ENTRY_BUMP = 10;
+ // REMOVED: ENTRY_BUMP = 10
/**
* The user has moved the dragged object within a droppable area.
@@ -230,6 +226,22 @@
public static final int LONG_PRESS_POWER_BUTTON = 10003;
/**
+ * A haptic effect to signal the confirmation of a user biometric authentication
+ * (e.g. fingerprint reading).
+ * This is a private constant to be used only by system apps.
+ * @hide
+ */
+ public static final int BIOMETRIC_CONFIRM = 10004;
+
+ /**
+ * A haptic effect to signal the rejection of a user biometric authentication attempt
+ * (e.g. fingerprint reading).
+ * This is a private constant to be used only by system apps.
+ * @hide
+ */
+ public static final int BIOMETRIC_REJECT = 10005;
+
+ /**
* Flag for {@link View#performHapticFeedback(int, int)
* View.performHapticFeedback(int, int)}: Ignore the setting in the
* view for whether to perform haptic feedback, do it always.
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 59cb450..afe5b7e 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1565,8 +1565,9 @@
* android:value="true|false"/>
* </activity>
* </pre>
+ *
+ * @hide
*/
- @FlaggedApi(Flags.FLAG_UNTRUSTED_EMBEDDING_STATE_SHARING)
String PROPERTY_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING_STATE_SHARING =
"android.window.PROPERTY_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING_STATE_SHARING";
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 65984f5..ead8887 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -45,8 +45,10 @@
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.IAccessibilityManager;
+import android.widget.flags.Flags;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -205,27 +207,41 @@
INotificationManager service = getService();
String pkg = mContext.getOpPackageName();
TN tn = mTN;
- tn.mNextView = new WeakReference<>(mNextView);
+ if (Flags.toastNoWeakref()) {
+ tn.mNextView = mNextView;
+ } else {
+ tn.mNextViewWeakRef = new WeakReference<>(mNextView);
+ }
final boolean isUiContext = mContext.isUiContext();
final int displayId = mContext.getDisplayId();
+ boolean wasEnqueued = false;
try {
if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
if (mNextView != null) {
// It's a custom toast
- service.enqueueToast(pkg, mToken, tn, mDuration, isUiContext, displayId);
+ wasEnqueued = service.enqueueToast(pkg, mToken, tn, mDuration, isUiContext,
+ displayId);
} else {
// It's a text toast
ITransientNotificationCallback callback =
new CallbackBinder(mCallbacks, mHandler);
- service.enqueueTextToast(pkg, mToken, mText, mDuration, isUiContext, displayId,
- callback);
+ wasEnqueued = service.enqueueTextToast(pkg, mToken, mText, mDuration,
+ isUiContext, displayId, callback);
}
} else {
- service.enqueueToast(pkg, mToken, tn, mDuration, isUiContext, displayId);
+ wasEnqueued = service.enqueueToast(pkg, mToken, tn, mDuration, isUiContext,
+ displayId);
}
} catch (RemoteException e) {
// Empty
+ } finally {
+ if (Flags.toastNoWeakref()) {
+ if (!wasEnqueued) {
+ tn.mNextViewWeakRef = null;
+ tn.mNextView = null;
+ }
+ }
}
}
@@ -581,6 +597,16 @@
}
}
+ /**
+ * Get the Toast.TN ITransientNotification object
+ * @return TN
+ * @hide
+ */
+ @VisibleForTesting
+ public TN getTn() {
+ return mTN;
+ }
+
// =======================================================================================
// All the gunk below is the interaction with the Notification Service, which handles
// the proper ordering of these system-wide.
@@ -599,7 +625,11 @@
return sService;
}
- private static class TN extends ITransientNotification.Stub {
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public static class TN extends ITransientNotification.Stub {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private final WindowManager.LayoutParams mParams;
@@ -620,7 +650,9 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
View mView;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
- WeakReference<View> mNextView;
+ WeakReference<View> mNextViewWeakRef;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ View mNextView;
int mDuration;
WindowManager mWM;
@@ -662,14 +694,22 @@
handleHide();
// Don't do this in handleHide() because it is also invoked by
// handleShow()
- mNextView = null;
+ if (Flags.toastNoWeakref()) {
+ mNextView = null;
+ } else {
+ mNextViewWeakRef = null;
+ }
break;
}
case CANCEL: {
handleHide();
// Don't do this in handleHide() because it is also invoked by
// handleShow()
- mNextView = null;
+ if (Flags.toastNoWeakref()) {
+ mNextView = null;
+ } else {
+ mNextViewWeakRef = null;
+ }
try {
getService().cancelToast(mPackageName, mToken);
} catch (RemoteException e) {
@@ -716,21 +756,43 @@
}
public void handleShow(IBinder windowToken) {
- if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
- + " mNextView=" + mNextView);
+ if (Flags.toastNoWeakref()) {
+ if (localLOGV) {
+ Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
+ + " mNextView=" + mNextView);
+ }
+ } else {
+ if (localLOGV) {
+ Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
+ + " mNextView=" + mNextViewWeakRef);
+ }
+ }
// If a cancel/hide is pending - no need to show - at this point
// the window token is already invalid and no need to do any work.
if (mHandler.hasMessages(CANCEL) || mHandler.hasMessages(HIDE)) {
return;
}
- if (mNextView != null && mView != mNextView.get()) {
- // remove the old view if necessary
- handleHide();
- mView = mNextView.get();
- if (mView != null) {
- mPresenter.show(mView, mToken, windowToken, mDuration, mGravity, mX, mY,
- mHorizontalMargin, mVerticalMargin,
- new CallbackBinder(getCallbacks(), mHandler));
+ if (Flags.toastNoWeakref()) {
+ if (mNextView != null && mView != mNextView) {
+ // remove the old view if necessary
+ handleHide();
+ mView = mNextView;
+ if (mView != null) {
+ mPresenter.show(mView, mToken, windowToken, mDuration, mGravity, mX, mY,
+ mHorizontalMargin, mVerticalMargin,
+ new CallbackBinder(getCallbacks(), mHandler));
+ }
+ }
+ } else {
+ if (mNextViewWeakRef != null && mView != mNextViewWeakRef.get()) {
+ // remove the old view if necessary
+ handleHide();
+ mView = mNextViewWeakRef.get();
+ if (mView != null) {
+ mPresenter.show(mView, mToken, windowToken, mDuration, mGravity, mX, mY,
+ mHorizontalMargin, mVerticalMargin,
+ new CallbackBinder(getCallbacks(), mHandler));
+ }
}
}
}
@@ -745,6 +807,23 @@
mView = null;
}
}
+
+ /**
+ * Get the next view to show for enqueued toasts
+ * Custom toast views are deprecated.
+ * @see #setView(View)
+ *
+ * @return next view
+ * @hide
+ */
+ @VisibleForTesting
+ public View getNextView() {
+ if (Flags.toastNoWeakref()) {
+ return mNextView;
+ } else {
+ return (mNextViewWeakRef != null) ? mNextViewWeakRef.get() : null;
+ }
+ }
}
/**
diff --git a/core/java/android/widget/flags/notification_widget_flags.aconfig b/core/java/android/widget/flags/notification_widget_flags.aconfig
index e60fa15..515fa55 100644
--- a/core/java/android/widget/flags/notification_widget_flags.aconfig
+++ b/core/java/android/widget/flags/notification_widget_flags.aconfig
@@ -25,4 +25,14 @@
metadata {
purpose: PURPOSE_BUGFIX
}
-}
\ No newline at end of file
+}
+
+flag {
+ name: "toast_no_weakref"
+ namespace: "systemui"
+ description: "Do not use WeakReference for custom view Toast"
+ bug: "321732224"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 4a3aba1..a868d48 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -25,6 +25,7 @@
import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.ResourcesManager;
+import android.app.servertransaction.ClientTransactionListenerController;
import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -137,12 +138,24 @@
* should be dispatched to listeners.
*/
@AnyThread
- public void onConfigurationChanged(Configuration newConfig, int newDisplayId,
+ public void onConfigurationChanged(@NonNull Configuration newConfig, int newDisplayId,
boolean shouldReportConfigChange) {
final Context context = mContextRef.get();
if (context == null) {
return;
}
+ final ClientTransactionListenerController controller =
+ ClientTransactionListenerController.getInstance();
+ controller.onContextConfigurationPreChanged(context);
+ try {
+ onConfigurationChangedInner(context, newConfig, newDisplayId, shouldReportConfigChange);
+ } finally {
+ controller.onContextConfigurationPostChanged(context);
+ }
+ }
+
+ private void onConfigurationChangedInner(@NonNull Context context,
+ @NonNull Configuration newConfig, int newDisplayId, boolean shouldReportConfigChange) {
CompatibilityInfo.applyOverrideScaleIfNeeded(newConfig);
final boolean displayChanged;
final boolean shouldUpdateResources;
diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
index eef6ce7..07fa679 100644
--- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
@@ -229,6 +229,17 @@
}
/**
+ * Copies time-in-state and timestamps from the supplied counter.
+ */
+ public void copyStatesFrom(LongArrayMultiStateCounter counter) {
+ if (mStateCount != counter.mStateCount) {
+ throw new IllegalArgumentException(
+ "State count is not the same: " + mStateCount + " vs. " + counter.mStateCount);
+ }
+ native_copyStatesFrom(mNativeObject, counter.mNativeObject);
+ }
+
+ /**
* Sets the new values for the given state.
*/
public void setValues(int state, long[] values) {
@@ -376,6 +387,10 @@
private static native void native_setState(long nativeObject, int state, long timestampMs);
@CriticalNative
+ private static native void native_copyStatesFrom(long nativeObjectTarget,
+ long nativeObjectSource);
+
+ @CriticalNative
private static native void native_setValues(long nativeObject, int state,
long longArrayContainerNativeObject);
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index ab982f5..9646ae9 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -18,6 +18,7 @@
import android.annotation.LongDef;
+import android.annotation.Nullable;
import android.annotation.StringDef;
import android.annotation.XmlRes;
import android.compat.annotation.UnsupportedAppUsage;
@@ -352,19 +353,39 @@
* WARNING: use only for testing!
*/
@VisibleForTesting
- public void forceInitForTesting(Context context, @XmlRes int xmlId) {
+ public void initForTesting(XmlPullParser parser) {
+ initForTesting(parser, null);
+ }
+
+ /**
+ * Reinitialize the PowerProfile with the provided XML, using optional Resources for fallback
+ * configuration settings.
+ * WARNING: use only for testing!
+ */
+ @VisibleForTesting
+ public void initForTesting(XmlPullParser parser, @Nullable Resources resources) {
synchronized (sLock) {
sPowerItemMap.clear();
sPowerArrayMap.clear();
sModemPowerProfile.clear();
- initLocked(context, xmlId);
+
+ try {
+ readPowerValuesFromXml(parser, resources);
+ } finally {
+ if (parser instanceof XmlResourceParser) {
+ ((XmlResourceParser) parser).close();
+ }
+ }
+ initLocked();
}
}
@GuardedBy("sLock")
private void initLocked(Context context, @XmlRes int xmlId) {
if (sPowerItemMap.size() == 0 && sPowerArrayMap.size() == 0) {
- readPowerValuesFromXml(context, xmlId);
+ final Resources resources = context.getResources();
+ XmlResourceParser parser = resources.getXml(xmlId);
+ readPowerValuesFromXml(parser, resources);
}
initLocked();
}
@@ -377,9 +398,8 @@
initModem();
}
- private void readPowerValuesFromXml(Context context, @XmlRes int xmlId) {
- final Resources resources = context.getResources();
- XmlResourceParser parser = resources.getXml(xmlId);
+ private static void readPowerValuesFromXml(XmlPullParser parser,
+ @Nullable Resources resources) {
boolean parsingArray = false;
ArrayList<Double> array = new ArrayList<>();
String arrayName = null;
@@ -430,9 +450,17 @@
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
- parser.close();
+ if (parser instanceof XmlResourceParser) {
+ ((XmlResourceParser) parser).close();
+ }
}
+ if (resources != null) {
+ getDefaultValuesFromConfig(resources);
+ }
+ }
+
+ private static void getDefaultValuesFromConfig(Resources resources) {
// Now collect other config variables.
int[] configResIds = new int[]{
com.android.internal.R.integer.config_bluetooth_idle_cur_ma,
diff --git a/core/java/com/android/internal/os/PowerStats.java b/core/java/com/android/internal/os/PowerStats.java
index 56263fb..7c7c7b8 100644
--- a/core/java/com/android/internal/os/PowerStats.java
+++ b/core/java/com/android/internal/os/PowerStats.java
@@ -47,7 +47,7 @@
private static final BatteryStatsHistory.VarintParceler VARINT_PARCELER =
new BatteryStatsHistory.VarintParceler();
- private static final byte PARCEL_FORMAT_VERSION = 1;
+ private static final byte PARCEL_FORMAT_VERSION = 2;
private static final int PARCEL_FORMAT_VERSION_MASK = 0x000000FF;
private static final int PARCEL_FORMAT_VERSION_SHIFT =
@@ -57,7 +57,12 @@
Integer.numberOfTrailingZeros(STATS_ARRAY_LENGTH_MASK);
public static final int MAX_STATS_ARRAY_LENGTH =
(1 << Integer.bitCount(STATS_ARRAY_LENGTH_MASK)) - 1;
- private static final int UID_STATS_ARRAY_LENGTH_MASK = 0x00FF0000;
+ private static final int STATE_STATS_ARRAY_LENGTH_MASK = 0x00FF0000;
+ private static final int STATE_STATS_ARRAY_LENGTH_SHIFT =
+ Integer.numberOfTrailingZeros(STATE_STATS_ARRAY_LENGTH_MASK);
+ public static final int MAX_STATE_STATS_ARRAY_LENGTH =
+ (1 << Integer.bitCount(STATE_STATS_ARRAY_LENGTH_MASK)) - 1;
+ private static final int UID_STATS_ARRAY_LENGTH_MASK = 0xFF000000;
private static final int UID_STATS_ARRAY_LENGTH_SHIFT =
Integer.numberOfTrailingZeros(UID_STATS_ARRAY_LENGTH_MASK);
public static final int MAX_UID_STATS_ARRAY_LENGTH =
@@ -74,6 +79,10 @@
private static final String XML_ATTR_ID = "id";
private static final String XML_ATTR_NAME = "name";
private static final String XML_ATTR_STATS_ARRAY_LENGTH = "stats-array-length";
+ private static final String XML_TAG_STATE = "state";
+ private static final String XML_ATTR_STATE_KEY = "key";
+ private static final String XML_ATTR_STATE_LABEL = "label";
+ private static final String XML_ATTR_STATE_STATS_ARRAY_LENGTH = "state-stats-array-length";
private static final String XML_ATTR_UID_STATS_ARRAY_LENGTH = "uid-stats-array-length";
private static final String XML_TAG_EXTRAS = "extras";
@@ -85,7 +94,24 @@
public final int powerComponentId;
public final String name;
+ /**
+ * Stats for the power component, such as the total usage time.
+ */
public final int statsArrayLength;
+
+ /**
+ * Map of device state codes to their corresponding human-readable labels.
+ */
+ public final SparseArray<String> stateLabels;
+
+ /**
+ * Stats for a specific state of the power component, e.g. "mobile radio in the 5G mode"
+ */
+ public final int stateStatsArrayLength;
+
+ /**
+ * Stats for the usage of this power component by a specific UID (app)
+ */
public final int uidStatsArrayLength;
/**
@@ -95,17 +121,25 @@
public final PersistableBundle extras;
public Descriptor(@BatteryConsumer.PowerComponent int powerComponentId,
- int statsArrayLength, int uidStatsArrayLength, @NonNull PersistableBundle extras) {
+ int statsArrayLength, @Nullable SparseArray<String> stateLabels,
+ int stateStatsArrayLength, int uidStatsArrayLength,
+ @NonNull PersistableBundle extras) {
this(powerComponentId, BatteryConsumer.powerComponentIdToString(powerComponentId),
- statsArrayLength, uidStatsArrayLength, extras);
+ statsArrayLength, stateLabels, stateStatsArrayLength, uidStatsArrayLength,
+ extras);
}
public Descriptor(int customPowerComponentId, String name, int statsArrayLength,
+ @Nullable SparseArray<String> stateLabels, int stateStatsArrayLength,
int uidStatsArrayLength, PersistableBundle extras) {
if (statsArrayLength > MAX_STATS_ARRAY_LENGTH) {
throw new IllegalArgumentException(
"statsArrayLength is too high. Max = " + MAX_STATS_ARRAY_LENGTH);
}
+ if (stateStatsArrayLength > MAX_STATE_STATS_ARRAY_LENGTH) {
+ throw new IllegalArgumentException(
+ "stateStatsArrayLength is too high. Max = " + MAX_STATE_STATS_ARRAY_LENGTH);
+ }
if (uidStatsArrayLength > MAX_UID_STATS_ARRAY_LENGTH) {
throw new IllegalArgumentException(
"uidStatsArrayLength is too high. Max = " + MAX_UID_STATS_ARRAY_LENGTH);
@@ -113,11 +147,25 @@
this.powerComponentId = customPowerComponentId;
this.name = name;
this.statsArrayLength = statsArrayLength;
+ this.stateLabels = stateLabels != null ? stateLabels : new SparseArray<>();
+ this.stateStatsArrayLength = stateStatsArrayLength;
this.uidStatsArrayLength = uidStatsArrayLength;
this.extras = extras;
}
/**
+ * Returns the label associated with the give state key, e.g. "5G-high" for the
+ * state of Mobile Radio representing the 5G mode and high signal power.
+ */
+ public String getStateLabel(int key) {
+ String label = stateLabels.get(key);
+ if (label != null) {
+ return label;
+ }
+ return name + "-" + Integer.toHexString(key);
+ }
+
+ /**
* Writes the Descriptor into the parcel.
*/
public void writeSummaryToParcel(Parcel parcel) {
@@ -125,11 +173,18 @@
& PARCEL_FORMAT_VERSION_MASK)
| ((statsArrayLength << STATS_ARRAY_LENGTH_SHIFT)
& STATS_ARRAY_LENGTH_MASK)
+ | ((stateStatsArrayLength << STATE_STATS_ARRAY_LENGTH_SHIFT)
+ & STATE_STATS_ARRAY_LENGTH_MASK)
| ((uidStatsArrayLength << UID_STATS_ARRAY_LENGTH_SHIFT)
& UID_STATS_ARRAY_LENGTH_MASK);
parcel.writeInt(firstWord);
parcel.writeInt(powerComponentId);
parcel.writeString(name);
+ parcel.writeInt(stateLabels.size());
+ for (int i = 0, size = stateLabels.size(); i < size; i++) {
+ parcel.writeInt(stateLabels.keyAt(i));
+ parcel.writeString(stateLabels.valueAt(i));
+ }
extras.writeToParcel(parcel, 0);
}
@@ -148,13 +203,22 @@
}
int statsArrayLength =
(firstWord & STATS_ARRAY_LENGTH_MASK) >>> STATS_ARRAY_LENGTH_SHIFT;
+ int stateStatsArrayLength =
+ (firstWord & STATE_STATS_ARRAY_LENGTH_MASK) >>> STATE_STATS_ARRAY_LENGTH_SHIFT;
int uidStatsArrayLength =
(firstWord & UID_STATS_ARRAY_LENGTH_MASK) >>> UID_STATS_ARRAY_LENGTH_SHIFT;
int powerComponentId = parcel.readInt();
String name = parcel.readString();
+ int stateLabelCount = parcel.readInt();
+ SparseArray<String> stateLabels = new SparseArray<>(stateLabelCount);
+ for (int i = stateLabelCount; i > 0; i--) {
+ int key = parcel.readInt();
+ String label = parcel.readString();
+ stateLabels.put(key, label);
+ }
PersistableBundle extras = parcel.readPersistableBundle();
- return new Descriptor(powerComponentId, name, statsArrayLength, uidStatsArrayLength,
- extras);
+ return new Descriptor(powerComponentId, name, statsArrayLength, stateLabels,
+ stateStatsArrayLength, uidStatsArrayLength, extras);
}
@Override
@@ -163,11 +227,13 @@
if (!(o instanceof Descriptor)) return false;
Descriptor that = (Descriptor) o;
return powerComponentId == that.powerComponentId
- && statsArrayLength == that.statsArrayLength
- && uidStatsArrayLength == that.uidStatsArrayLength
- && Objects.equals(name, that.name)
- && extras.size() == that.extras.size() // Unparcel the Parcel if not yet
- && Bundle.kindofEquals(extras,
+ && statsArrayLength == that.statsArrayLength
+ && stateLabels.contentEquals(that.stateLabels)
+ && stateStatsArrayLength == that.stateStatsArrayLength
+ && uidStatsArrayLength == that.uidStatsArrayLength
+ && Objects.equals(name, that.name)
+ && extras.size() == that.extras.size() // Unparcel the Parcel if not yet
+ && Bundle.kindofEquals(extras,
that.extras); // Since the Parcel is now unparceled, do a deep comparison
}
@@ -179,7 +245,14 @@
serializer.attributeInt(null, XML_ATTR_ID, powerComponentId);
serializer.attribute(null, XML_ATTR_NAME, name);
serializer.attributeInt(null, XML_ATTR_STATS_ARRAY_LENGTH, statsArrayLength);
+ serializer.attributeInt(null, XML_ATTR_STATE_STATS_ARRAY_LENGTH, stateStatsArrayLength);
serializer.attributeInt(null, XML_ATTR_UID_STATS_ARRAY_LENGTH, uidStatsArrayLength);
+ for (int i = stateLabels.size() - 1; i >= 0; i--) {
+ serializer.startTag(null, XML_TAG_STATE);
+ serializer.attributeInt(null, XML_ATTR_STATE_KEY, stateLabels.keyAt(i));
+ serializer.attribute(null, XML_ATTR_STATE_LABEL, stateLabels.valueAt(i));
+ serializer.endTag(null, XML_TAG_STATE);
+ }
try {
serializer.startTag(null, XML_TAG_EXTRAS);
extras.saveToXml(serializer);
@@ -199,6 +272,8 @@
int powerComponentId = -1;
String name = null;
int statsArrayLength = 0;
+ SparseArray<String> stateLabels = new SparseArray<>();
+ int stateStatsArrayLength = 0;
int uidStatsArrayLength = 0;
PersistableBundle extras = null;
int eventType = parser.getEventType();
@@ -212,9 +287,16 @@
name = parser.getAttributeValue(null, XML_ATTR_NAME);
statsArrayLength = parser.getAttributeInt(null,
XML_ATTR_STATS_ARRAY_LENGTH);
+ stateStatsArrayLength = parser.getAttributeInt(null,
+ XML_ATTR_STATE_STATS_ARRAY_LENGTH);
uidStatsArrayLength = parser.getAttributeInt(null,
XML_ATTR_UID_STATS_ARRAY_LENGTH);
break;
+ case XML_TAG_STATE:
+ int value = parser.getAttributeInt(null, XML_ATTR_STATE_KEY);
+ String label = parser.getAttributeValue(null, XML_ATTR_STATE_LABEL);
+ stateLabels.put(value, label);
+ break;
case XML_TAG_EXTRAS:
extras = PersistableBundle.restoreFromXml(parser);
break;
@@ -225,11 +307,11 @@
if (powerComponentId == -1) {
return null;
} else if (powerComponentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) {
- return new Descriptor(powerComponentId, name, statsArrayLength, uidStatsArrayLength,
- extras);
+ return new Descriptor(powerComponentId, name, statsArrayLength,
+ stateLabels, stateStatsArrayLength, uidStatsArrayLength, extras);
} else if (powerComponentId < BatteryConsumer.POWER_COMPONENT_COUNT) {
- return new Descriptor(powerComponentId, statsArrayLength, uidStatsArrayLength,
- extras);
+ return new Descriptor(powerComponentId, statsArrayLength, stateLabels,
+ stateStatsArrayLength, uidStatsArrayLength, extras);
} else {
Slog.e(TAG, "Unrecognized power component: " + powerComponentId);
return null;
@@ -247,12 +329,14 @@
extras.size(); // Unparcel
}
return "PowerStats.Descriptor{"
- + "powerComponentId=" + powerComponentId
- + ", name='" + name + '\''
- + ", statsArrayLength=" + statsArrayLength
- + ", uidStatsArrayLength=" + uidStatsArrayLength
- + ", extras=" + extras
- + '}';
+ + "powerComponentId=" + powerComponentId
+ + ", name='" + name + '\''
+ + ", statsArrayLength=" + statsArrayLength
+ + ", stateStatsArrayLength=" + stateStatsArrayLength
+ + ", stateLabels=" + stateLabels
+ + ", uidStatsArrayLength=" + uidStatsArrayLength
+ + ", extras=" + extras
+ + '}';
}
}
@@ -293,6 +377,12 @@
public long[] stats;
/**
+ * Device-wide mode stats, used when the power component can operate in different modes,
+ * e.g. RATs such as LTE and 5G.
+ */
+ public final SparseArray<long[]> stateStats = new SparseArray<>();
+
+ /**
* Per-UID CPU stats.
*/
public final SparseArray<long[]> uidStats = new SparseArray<>();
@@ -313,6 +403,15 @@
parcel.writeInt(descriptor.powerComponentId);
parcel.writeLong(durationMs);
VARINT_PARCELER.writeLongArray(parcel, stats);
+
+ if (descriptor.stateStatsArrayLength != 0) {
+ parcel.writeInt(stateStats.size());
+ for (int i = 0; i < stateStats.size(); i++) {
+ parcel.writeInt(stateStats.keyAt(i));
+ VARINT_PARCELER.writeLongArray(parcel, stateStats.valueAt(i));
+ }
+ }
+
parcel.writeInt(uidStats.size());
for (int i = 0; i < uidStats.size(); i++) {
parcel.writeInt(uidStats.keyAt(i));
@@ -347,6 +446,17 @@
stats.durationMs = parcel.readLong();
stats.stats = new long[descriptor.statsArrayLength];
VARINT_PARCELER.readLongArray(parcel, stats.stats);
+
+ if (descriptor.stateStatsArrayLength != 0) {
+ int count = parcel.readInt();
+ for (int i = 0; i < count; i++) {
+ int state = parcel.readInt();
+ long[] stateStats = new long[descriptor.stateStatsArrayLength];
+ VARINT_PARCELER.readLongArray(parcel, stateStats);
+ stats.stateStats.put(state, stateStats);
+ }
+ }
+
int uidCount = parcel.readInt();
for (int i = 0; i < uidCount; i++) {
int uid = parcel.readInt();
@@ -376,6 +486,14 @@
if (stats.length > 0) {
sb.append("=").append(Arrays.toString(stats));
}
+ if (descriptor.stateStatsArrayLength != 0) {
+ for (int i = 0; i < stateStats.size(); i++) {
+ sb.append(" [");
+ sb.append(descriptor.getStateLabel(stateStats.keyAt(i)));
+ sb.append("]=");
+ sb.append(Arrays.toString(stateStats.valueAt(i)));
+ }
+ }
for (int i = 0; i < uidStats.size(); i++) {
sb.append(uidPrefix)
.append(UserHandle.formatUid(uidStats.keyAt(i)))
@@ -391,6 +509,18 @@
pw.println("PowerStats: " + descriptor.name + " (" + descriptor.powerComponentId + ')');
pw.increaseIndent();
pw.print("duration", durationMs).println();
+ if (descriptor.statsArrayLength != 0) {
+ pw.print("stats", Arrays.toString(stats)).println();
+ }
+ if (descriptor.stateStatsArrayLength != 0) {
+ for (int i = 0; i < stateStats.size(); i++) {
+ pw.print("state ");
+ pw.print(descriptor.getStateLabel(stateStats.keyAt(i)));
+ pw.print(": ");
+ pw.print(Arrays.toString(stateStats.valueAt(i)));
+ pw.println();
+ }
+ }
for (int i = 0; i < uidStats.size(); i++) {
pw.print("UID ");
pw.print(uidStats.keyAt(i));
diff --git a/core/java/com/android/internal/power/ModemPowerProfile.java b/core/java/com/android/internal/power/ModemPowerProfile.java
index b15c10e..64d3139 100644
--- a/core/java/com/android/internal/power/ModemPowerProfile.java
+++ b/core/java/com/android/internal/power/ModemPowerProfile.java
@@ -17,14 +17,16 @@
package com.android.internal.power;
import android.annotation.IntDef;
-import android.content.res.XmlResourceParser;
+import android.os.BatteryStats;
import android.telephony.ModemActivityInfo;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseDoubleArray;
+import com.android.internal.os.PowerProfile;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -95,6 +97,8 @@
*/
public static final int MODEM_DRAIN_TYPE_TX = 0x3000_0000;
+ private static final int IGNORE = -1;
+
@IntDef(prefix = {"MODEM_DRAIN_TYPE_"}, value = {
MODEM_DRAIN_TYPE_SLEEP,
MODEM_DRAIN_TYPE_IDLE,
@@ -256,7 +260,7 @@
/**
* Generates a ModemPowerProfile object from the <modem /> element of a power_profile.xml
*/
- public void parseFromXml(XmlResourceParser parser) throws IOException,
+ public void parseFromXml(XmlPullParser parser) throws IOException,
XmlPullParserException {
final int depth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, depth)) {
@@ -286,7 +290,7 @@
}
/** Parse the <active /> XML element */
- private void parseActivePowerConstantsFromXml(XmlResourceParser parser)
+ private void parseActivePowerConstantsFromXml(XmlPullParser parser)
throws IOException, XmlPullParserException {
// Parse attributes to get the type of active modem usage the power constants are for.
final int ratType;
@@ -339,7 +343,7 @@
}
}
- private static int getTypeFromAttribute(XmlResourceParser parser, String attr,
+ private static int getTypeFromAttribute(XmlPullParser parser, String attr,
SparseArray<String> names) {
final String value = XmlUtils.readStringAttribute(parser, attr);
if (value == null) {
@@ -382,6 +386,84 @@
}
}
+ public static long getAverageBatteryDrainKey(@ModemDrainType int drainType,
+ @BatteryStats.RadioAccessTechnology int rat, @ServiceState.FrequencyRange int freqRange,
+ int txLevel) {
+ long key = PowerProfile.SUBSYSTEM_MODEM;
+
+ // Attach Modem drain type to the key if specified.
+ if (drainType != IGNORE) {
+ key |= drainType;
+ }
+
+ // Attach RadioAccessTechnology to the key if specified.
+ switch (rat) {
+ case IGNORE:
+ // do nothing
+ break;
+ case BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER:
+ key |= MODEM_RAT_TYPE_DEFAULT;
+ break;
+ case BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE:
+ key |= MODEM_RAT_TYPE_LTE;
+ break;
+ case BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR:
+ key |= MODEM_RAT_TYPE_NR;
+ break;
+ default:
+ Log.w(TAG, "Unexpected RadioAccessTechnology : " + rat);
+ }
+
+ // Attach NR Frequency Range to the key if specified.
+ switch (freqRange) {
+ case IGNORE:
+ // do nothing
+ break;
+ case ServiceState.FREQUENCY_RANGE_UNKNOWN:
+ key |= MODEM_NR_FREQUENCY_RANGE_DEFAULT;
+ break;
+ case ServiceState.FREQUENCY_RANGE_LOW:
+ key |= MODEM_NR_FREQUENCY_RANGE_LOW;
+ break;
+ case ServiceState.FREQUENCY_RANGE_MID:
+ key |= MODEM_NR_FREQUENCY_RANGE_MID;
+ break;
+ case ServiceState.FREQUENCY_RANGE_HIGH:
+ key |= MODEM_NR_FREQUENCY_RANGE_HIGH;
+ break;
+ case ServiceState.FREQUENCY_RANGE_MMWAVE:
+ key |= MODEM_NR_FREQUENCY_RANGE_MMWAVE;
+ break;
+ default:
+ Log.w(TAG, "Unexpected NR frequency range : " + freqRange);
+ }
+
+ // Attach transmission level to the key if specified.
+ switch (txLevel) {
+ case IGNORE:
+ // do nothing
+ break;
+ case 0:
+ key |= MODEM_TX_LEVEL_0;
+ break;
+ case 1:
+ key |= MODEM_TX_LEVEL_1;
+ break;
+ case 2:
+ key |= MODEM_TX_LEVEL_2;
+ break;
+ case 3:
+ key |= MODEM_TX_LEVEL_3;
+ break;
+ case 4:
+ key |= MODEM_TX_LEVEL_4;
+ break;
+ default:
+ Log.w(TAG, "Unexpected transmission level : " + txLevel);
+ }
+ return key;
+ }
+
/**
* Returns the average battery drain in milli-amps of the modem for a given drain type.
* Returns {@link Double.NaN} if a suitable value is not found for the given key.
@@ -444,6 +526,7 @@
}
return sb.toString();
}
+
private static void appendFieldToString(StringBuilder sb, String fieldName,
SparseArray<String> names, int key) {
sb.append(fieldName);
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 9c63d0d..bd654fa 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -1212,6 +1212,10 @@
}
}
}
+ if (android.app.Flags.cleanUpSpansAndNewLines() && conversationText != null) {
+ // remove formatting from title.
+ conversationText = conversationText.toString();
+ }
if (conversationIcon == null) {
conversationIcon = largeIcon;
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index f8f1049..97a2d3b 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StyleRes;
+import android.app.Flags;
import android.app.Person;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -200,6 +201,10 @@
if (nameOverride == null) {
nameOverride = sender.getName();
}
+ if (Flags.cleanUpSpansAndNewLines() && nameOverride != null) {
+ // remove formatting from sender name
+ nameOverride = nameOverride.toString();
+ }
mSenderName = nameOverride;
if (mSingleLine && !TextUtils.isEmpty(nameOverride)) {
nameOverride = mContext.getResources().getString(
diff --git a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
index 76b05ea..b3c41df 100644
--- a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
+++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
@@ -55,6 +55,14 @@
counter->setState(state, timestamp);
}
+static void native_copyStatesFrom(jlong nativePtrTarget, jlong nativePtrSource) {
+ battery::LongArrayMultiStateCounter *counterTarget =
+ reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtrTarget);
+ battery::LongArrayMultiStateCounter *counterSource =
+ reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtrSource);
+ counterTarget->copyStatesFrom(*counterSource);
+}
+
static void native_setValues(jlong nativePtr, jint state, jlong longArrayContainerNativePtr) {
battery::LongArrayMultiStateCounter *counter =
reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
@@ -219,6 +227,8 @@
// @CriticalNative
{"native_setState", "(JIJ)V", (void *)native_setState},
// @CriticalNative
+ {"native_copyStatesFrom", "(JJ)V", (void *)native_copyStatesFrom},
+ // @CriticalNative
{"native_setValues", "(JIJ)V", (void *)native_setValues},
// @CriticalNative
{"native_updateValues", "(JJJ)V", (void *)native_updateValues},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f743299..c694426 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2593,6 +2593,13 @@
<permission android:name="android.permission.VIBRATE_ALWAYS_ON"
android:protectionLevel="signature" />
+ <!-- Allows access to system-only haptic feedback constants.
+ <p>Protection level: signature
+ @hide
+ -->
+ <permission android:name="android.permission.VIBRATE_SYSTEM_CONSTANTS"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows access to the vibrator state.
<p>Protection level: signature
@hide
@@ -3889,6 +3896,13 @@
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL"
android:protectionLevel="internal|role" />
+ <!-- Allows the holder to manage and retrieve max storage limit for admin policies. This
+ permission is only grantable on rooted devices.
+ @TestAPI
+ @hide -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT"
+ android:protectionLevel="internal" />
+
<!-- Allows an application to access EnhancedConfirmationManager.
@SystemApi
@FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled")
diff --git a/core/res/res/drawable/pointer_spot_anchor_vector.xml b/core/res/res/drawable/pointer_spot_anchor_vector.xml
index 54de2ae..89990b8 100644
--- a/core/res/res/drawable/pointer_spot_anchor_vector.xml
+++ b/core/res/res/drawable/pointer_spot_anchor_vector.xml
@@ -22,4 +22,8 @@
<path android:fillColor="#ADC6E7" android:pathData="M12 3c-4.963 0-9 4.038-9 9 0 4.963 4.037 9 9 9s9-4.037 9-9c0-4.962-4.037-9-9-9m0 17c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8" />
<path android:fillColor="#ADC6E7" android:pathData="M12 5c-3.859 0-7 3.14-7 7s3.141 7 7 7 7-3.141 7-7-3.141-7-7-7m0 13c-3.309 0-6-2.691-6-6s2.691-6 6-6 6 2.691 6 6-2.691 6-6 6" />
</group>
+ <path
+ android:pathData="M12 4a8 8 0 1 0 0 16 8 8 0 0 0 0 -16m0 15a7 7 0 1 1 0 -14 7 7 0 0 1 0 14"
+ android:fillColor="#99FFFFFF"
+ android:fillType="evenOdd"/>
</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_spot_hover_vector.xml b/core/res/res/drawable/pointer_spot_hover_vector.xml
index ef596c4..4bf5fbc 100644
--- a/core/res/res/drawable/pointer_spot_hover_vector.xml
+++ b/core/res/res/drawable/pointer_spot_hover_vector.xml
@@ -22,4 +22,7 @@
<path android:fillColor="#ADC6E7" android:pathData="M12 3c-4.963 0-9 4.038-9 9 0 4.963 4.037 9 9 9s9-4.037 9-9c0-4.962-4.037-9-9-9m0 17c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8" />
<path android:fillColor="#ADC6E7" android:pathData="M12 7c-2.757 0-5 2.243-5 5s2.243 5 5 5 5-2.243 5-5-2.243-5-5-5m0 9c-2.206 0-4-1.794-4-4s1.794-4 4-4 4 1.794 4 4-1.794 4-4 4" />
</group>
+ <path
+ android:pathData="M12 3.998a8.002 8.002 0 1 0 0 16.004 8.002 8.002 0 0 0 0 -16.004m0 13.004a5.002 5.002 0 1 1 0 -10.004 5.002 5.002 0 0 1 0 10.004"
+ android:fillColor="#99FFFFFF"/>
</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_spot_touch_vector.xml b/core/res/res/drawable/pointer_spot_touch_vector.xml
index afd2956..a25ffe0 100644
--- a/core/res/res/drawable/pointer_spot_touch_vector.xml
+++ b/core/res/res/drawable/pointer_spot_touch_vector.xml
@@ -21,4 +21,7 @@
<path
android:fillColor="#ADC6E7"
android:pathData="M21 12c0-4.963-4.038-9-9-9s-9 4.037-9 9 4.038 9 9 9 9-4.037 9-9m-9 8c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8" />
+ <path
+ android:pathData="M12 12m-8 0a8 8 0 1 1 16 0a8 8 0 1 1 -16 0"
+ android:fillColor="#99FFFFFF"/>
</vector>
\ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d4db244..2115f64 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4334,9 +4334,6 @@
-->
<bool name="config_wallpaperTopApp">false</bool>
- <!-- True if the device supports dVRR -->
- <bool name="config_supportsDvrr">false</bool>
-
<!-- True if the device supports at least one form of multi-window.
E.g. freeform, split-screen, picture-in-picture. -->
<bool name="config_supportsMultiWindow">true</bool>
diff --git a/core/res/res/values/config_battery_stats.xml b/core/res/res/values/config_battery_stats.xml
index e42962c..ae47899 100644
--- a/core/res/res/values/config_battery_stats.xml
+++ b/core/res/res/values/config_battery_stats.xml
@@ -32,6 +32,9 @@
devices-->
<integer name="config_defaultPowerStatsThrottlePeriodCpu">60000</integer>
+ <!-- Mobile Radio power stats collection throttle period in milliseconds. -->
+ <integer name="config_defaultPowerStatsThrottlePeriodMobileRadio">3600000</integer>
+
<!-- PowerStats aggregation period in milliseconds. This is the interval at which the power
stats aggregation procedure is performed and the results stored in PowerStatsStore. -->
<integer name="config_powerStatsAggregationPeriod">14400000</integer>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f33e277..9e09540 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -404,7 +404,6 @@
<java-symbol type="bool" name="config_supportAudioSourceUnprocessed" />
<java-symbol type="bool" name="config_freeformWindowManagement" />
<java-symbol type="bool" name="config_supportsBubble" />
- <java-symbol type="bool" name="config_supportsDvrr" />
<java-symbol type="bool" name="config_supportsMultiWindow" />
<java-symbol type="bool" name="config_supportsSplitScreenMultiWindow" />
<java-symbol type="bool" name="config_supportsMultiDisplay" />
@@ -5217,6 +5216,7 @@
<java-symbol type="bool" name="config_batteryStatsResetOnUnplugHighBatteryLevel" />
<java-symbol type="bool" name="config_batteryStatsResetOnUnplugAfterSignificantCharge" />
<java-symbol type="integer" name="config_defaultPowerStatsThrottlePeriodCpu" />
+ <java-symbol type="integer" name="config_defaultPowerStatsThrottlePeriodMobileRadio" />
<java-symbol type="integer" name="config_powerStatsAggregationPeriod" />
<java-symbol type="integer" name="config_aggregatedPowerStatsSpanDuration" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 4808204..404e873 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -267,5 +267,10 @@
generate_get_transaction_name: true,
local_include_dirs: ["aidl"],
},
+ java_resources: [
+ "res/xml/power_profile_test.xml",
+ "res/xml/power_profile_test_cpu_legacy.xml",
+ "res/xml/power_profile_test_modem.xml",
+ ],
auto_gen_config: true,
}
diff --git a/core/tests/coretests/res/xml/power_profile_test.xml b/core/tests/coretests/res/xml/power_profile_test.xml
index 322ae05..7356c9e 100644
--- a/core/tests/coretests/res/xml/power_profile_test.xml
+++ b/core/tests/coretests/res/xml/power_profile_test.xml
@@ -98,4 +98,16 @@
<value>40</value>
<value>50</value>
</array>
-</device>
\ No newline at end of file
+
+ <!-- Idle current for bluetooth in mA.-->
+ <item name="bluetooth.controller.idle">0.02</item>
+
+ <!-- Rx current for bluetooth in mA.-->
+ <item name="bluetooth.controller.rx">3</item>
+
+ <!-- Tx current for bluetooth in mA-->
+ <item name="bluetooth.controller.tx">5</item>
+
+ <!-- Operating voltage for bluetooth in mV.-->
+ <item name="bluetooth.controller.voltage">3300</item>
+</device>
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
index 77d31a5..8506905 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
@@ -23,11 +23,17 @@
import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
@@ -76,6 +82,13 @@
private BiConsumer<IBinder, ActivityWindowInfo> mActivityWindowInfoListener;
@Mock
private IBinder mActivityToken;
+ @Mock
+ private Activity mActivity;
+ @Mock
+ private Resources mResources;
+
+ private Configuration mConfiguration;
+
private DisplayManagerGlobal mDisplayManager;
private Handler mHandler;
@@ -88,7 +101,12 @@
MockitoAnnotations.initMocks(this);
mDisplayManager = new DisplayManagerGlobal(mIDisplayManager);
mHandler = getInstrumentation().getContext().getMainThreadHandler();
- mController = ClientTransactionListenerController.createInstanceForTesting(mDisplayManager);
+ mController = spy(ClientTransactionListenerController
+ .createInstanceForTesting(mDisplayManager));
+
+ mConfiguration = new Configuration();
+ doReturn(mConfiguration).when(mResources).getConfiguration();
+ doReturn(mResources).when(mActivity).getResources();
}
@Test
@@ -107,6 +125,43 @@
}
@Test
+ public void testOnContextConfigurationChanged() {
+ doNothing().when(mController).onDisplayChanged(anyInt());
+ doReturn(123).when(mActivity).getDisplayId();
+
+ // Not trigger onDisplayChanged when there is no change.
+ mController.onContextConfigurationPreChanged(mActivity);
+ mController.onContextConfigurationPostChanged(mActivity);
+
+ verify(mController, never()).onDisplayChanged(anyInt());
+
+ mController.onContextConfigurationPreChanged(mActivity);
+ mConfiguration.windowConfiguration.setMaxBounds(new Rect(0, 0, 100, 200));
+ mController.onContextConfigurationPostChanged(mActivity);
+
+ verify(mController).onDisplayChanged(123);
+ }
+
+ @Test
+ public void testOnContextConfigurationChanged_duringClientTransaction() {
+ doNothing().when(mController).onDisplayChanged(anyInt());
+ doReturn(123).when(mActivity).getDisplayId();
+
+ // Not trigger onDisplayChanged until ClientTransaction finished execution.
+ mController.onClientTransactionStarted();
+
+ mController.onContextConfigurationPreChanged(mActivity);
+ mConfiguration.windowConfiguration.setMaxBounds(new Rect(0, 0, 100, 200));
+ mController.onContextConfigurationPostChanged(mActivity);
+
+ verify(mController, never()).onDisplayChanged(anyInt());
+
+ mController.onClientTransactionFinished();
+
+ verify(mController).onDisplayChanged(123);
+ }
+
+ @Test
public void testActivityWindowInfoChangedListener() {
mSetFlagsRule.enableFlags(Flags.FLAG_ACTIVITY_WINDOW_INFO_FLAG);
diff --git a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
index 533b799..fa5d72a 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
@@ -55,6 +55,21 @@
}
@Test
+ public void copyStatesFrom() {
+ LongArrayMultiStateCounter source = new LongArrayMultiStateCounter(2, 1);
+ updateValue(source, new long[]{0}, 1000);
+ source.setState(0, 1000);
+ source.setState(1, 2000);
+
+ LongArrayMultiStateCounter target = new LongArrayMultiStateCounter(2, 1);
+ target.copyStatesFrom(source);
+ updateValue(target, new long[]{1000}, 5000);
+
+ assertCounts(target, 0, new long[]{250});
+ assertCounts(target, 1, new long[]{750});
+ }
+
+ @Test
public void setValue() {
LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4);
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
index c0f0714..951fa98 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
@@ -21,20 +21,21 @@
import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL;
import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON;
-import static org.junit.Assert.fail;
+import static com.google.common.truth.Truth.assertThat;
-import android.annotation.XmlRes;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import android.content.Context;
import android.content.res.Resources;
-import android.content.res.XmlResourceParser;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.Xml;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.frameworks.coretests.R;
import com.android.internal.power.ModemPowerProfile;
import com.android.internal.util.XmlUtils;
@@ -43,6 +44,11 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.StringReader;
/*
* Keep this file in sync with frameworks/base/core/res/res/xml/power_profile_test.xml and
@@ -53,7 +59,6 @@
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = PowerProfile.class)
public class PowerProfileTest {
@Rule
public final RavenwoodRule mRavenwood = new RavenwoodRule();
@@ -62,17 +67,15 @@
static final String ATTR_NAME = "name";
private PowerProfile mProfile;
- private Context mContext;
@Before
public void setUp() {
- mContext = InstrumentationRegistry.getContext();
- mProfile = new PowerProfile(mContext);
+ mProfile = new PowerProfile();
}
@Test
public void testPowerProfile() {
- mProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
+ mProfile.initForTesting(resolveParser("power_profile_test"));
assertEquals(5.0, mProfile.getAveragePower(PowerProfile.POWER_CPU_SUSPEND));
assertEquals(1.11, mProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE));
@@ -127,11 +130,36 @@
PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT
| ModemPowerProfile.MODEM_DRAIN_TYPE_TX
| ModemPowerProfile.MODEM_TX_LEVEL_4));
+
+ assertEquals(0.02, mProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE));
+ assertEquals(3, mProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX));
+ assertEquals(5, mProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX));
+ assertEquals(3300, mProfile.getAveragePower(
+ PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE));
}
+
+ @DisabledOnRavenwood
+ @Test
+ public void configDefaults() throws XmlPullParserException {
+ Resources mockResources = mock(Resources.class);
+ when(mockResources.getInteger(com.android.internal.R.integer.config_bluetooth_rx_cur_ma))
+ .thenReturn(123);
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new StringReader(
+ "<device name='Android'>"
+ + "<item name='bluetooth.controller.idle'>10</item>"
+ + "</device>"));
+ mProfile.initForTesting(parser, mockResources);
+ assertThat(mProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE))
+ .isEqualTo(10);
+ assertThat(mProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX))
+ .isEqualTo(123);
+ }
+
@Test
public void testPowerProfile_legacyCpuConfig() {
// This power profile has per-cluster data, rather than per-policy
- mProfile.forceInitForTesting(mContext, R.xml.power_profile_test_cpu_legacy);
+ mProfile.initForTesting(resolveParser("power_profile_test_cpu_legacy"));
assertEquals(2.11, mProfile.getAveragePowerForCpuScalingPolicy(0));
assertEquals(2.22, mProfile.getAveragePowerForCpuScalingPolicy(4));
@@ -148,7 +176,7 @@
@Test
public void testModemPowerProfile_defaultRat() throws Exception {
- final XmlResourceParser parser = getTestModemElement(R.xml.power_profile_test_modem,
+ final XmlPullParser parser = getTestModemElement("power_profile_test_modem",
"testModemPowerProfile_defaultRat");
ModemPowerProfile mpp = new ModemPowerProfile();
mpp.parseFromXml(parser);
@@ -216,7 +244,7 @@
@Test
public void testModemPowerProfile_partiallyDefined() throws Exception {
- final XmlResourceParser parser = getTestModemElement(R.xml.power_profile_test_modem,
+ final XmlPullParser parser = getTestModemElement("power_profile_test_modem",
"testModemPowerProfile_partiallyDefined");
ModemPowerProfile mpp = new ModemPowerProfile();
mpp.parseFromXml(parser);
@@ -369,7 +397,7 @@
@Test
public void testModemPowerProfile_fullyDefined() throws Exception {
- final XmlResourceParser parser = getTestModemElement(R.xml.power_profile_test_modem,
+ final XmlPullParser parser = getTestModemElement("power_profile_test_modem",
"testModemPowerProfile_fullyDefined");
ModemPowerProfile mpp = new ModemPowerProfile();
mpp.parseFromXml(parser);
@@ -519,11 +547,10 @@
| ModemPowerProfile.MODEM_DRAIN_TYPE_TX | ModemPowerProfile.MODEM_TX_LEVEL_4));
}
- private XmlResourceParser getTestModemElement(@XmlRes int xmlId, String elementName)
+ private XmlPullParser getTestModemElement(String resourceName, String elementName)
throws Exception {
+ XmlPullParser parser = resolveParser(resourceName);
final String element = TAG_TEST_MODEM;
- final Resources resources = mContext.getResources();
- XmlResourceParser parser = resources.getXml(xmlId);
while (true) {
XmlUtils.nextElement(parser);
final String e = parser.getName();
@@ -535,10 +562,26 @@
return parser;
}
- fail("Unanable to find element " + element + " with name " + elementName);
+ fail("Unable to find element " + element + " with name " + elementName);
return null;
}
+ private XmlPullParser resolveParser(String resourceName) {
+ if (RavenwoodRule.isOnRavenwood()) {
+ try {
+ return Xml.resolvePullParser(getClass().getClassLoader()
+ .getResourceAsStream("res/xml/" + resourceName + ".xml"));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ Context context = androidx.test.InstrumentationRegistry.getContext();
+ Resources resources = context.getResources();
+ int resId = resources.getIdentifier(resourceName, "xml", context.getPackageName());
+ return resources.getXml(resId);
+ }
+ }
+
private void assertEquals(double expected, double actual) {
Assert.assertEquals(expected, actual, 0.1);
}
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java b/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
index b99e202..6402206 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
@@ -21,20 +21,27 @@
import android.os.BatteryConsumer;
import android.os.Parcel;
import android.os.PersistableBundle;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.SparseArray;
+import android.util.Xml;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
-@IgnoreUnderRavenwood(reason = "Needs kernel support")
public class PowerStatsTest {
@Rule
public final RavenwoodRule mRavenwood = new RavenwoodRule();
@@ -47,7 +54,10 @@
mRegistry = new PowerStats.DescriptorRegistry();
PersistableBundle extras = new PersistableBundle();
extras.putBoolean("hasPowerMonitor", true);
- mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, 3, 2, extras);
+ SparseArray<String> stateLabels = new SparseArray<>();
+ stateLabels.put(0x0F, "idle");
+ mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, 3, stateLabels,
+ 1, 2, extras);
mRegistry.register(mDescriptor);
}
@@ -58,6 +68,8 @@
stats.stats[0] = 10;
stats.stats[1] = 20;
stats.stats[2] = 30;
+ stats.stateStats.put(0x0F, new long[]{16});
+ stats.stateStats.put(0xF0, new long[]{17});
stats.uidStats.put(42, new long[]{40, 50});
stats.uidStats.put(99, new long[]{60, 70});
@@ -73,6 +85,7 @@
assertThat(newDescriptor.powerComponentId).isEqualTo(BatteryConsumer.POWER_COMPONENT_CPU);
assertThat(newDescriptor.name).isEqualTo("cpu");
assertThat(newDescriptor.statsArrayLength).isEqualTo(3);
+ assertThat(newDescriptor.stateStatsArrayLength).isEqualTo(1);
assertThat(newDescriptor.uidStatsArrayLength).isEqualTo(2);
assertThat(newDescriptor.extras.getBoolean("hasPowerMonitor")).isTrue();
@@ -81,6 +94,11 @@
PowerStats newStats = PowerStats.readFromParcel(newParcel, mRegistry);
assertThat(newStats.durationMs).isEqualTo(1234);
assertThat(newStats.stats).isEqualTo(new long[]{10, 20, 30});
+ assertThat(newStats.stateStats.size()).isEqualTo(2);
+ assertThat(newStats.stateStats.get(0x0F)).isEqualTo(new long[]{16});
+ assertThat(newStats.descriptor.getStateLabel(0x0F)).isEqualTo("idle");
+ assertThat(newStats.stateStats.get(0xF0)).isEqualTo(new long[]{17});
+ assertThat(newStats.descriptor.getStateLabel(0xF0)).isEqualTo("cpu-f0");
assertThat(newStats.uidStats.size()).isEqualTo(2);
assertThat(newStats.uidStats.get(42)).isEqualTo(new long[]{40, 50});
assertThat(newStats.uidStats.get(99)).isEqualTo(new long[]{60, 70});
@@ -90,9 +108,33 @@
}
@Test
+ public void xmlFormat() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ TypedXmlSerializer serializer = Xml.newBinarySerializer();
+ serializer.setOutput(out, StandardCharsets.UTF_8.name());
+ mDescriptor.writeXml(serializer);
+ serializer.flush();
+
+ byte[] bytes = out.toByteArray();
+
+ TypedXmlPullParser parser = Xml.newBinaryPullParser();
+ parser.setInput(new ByteArrayInputStream(bytes), StandardCharsets.UTF_8.name());
+ PowerStats.Descriptor actual = PowerStats.Descriptor.createFromXml(parser);
+
+ assertThat(actual.powerComponentId).isEqualTo(BatteryConsumer.POWER_COMPONENT_CPU);
+ assertThat(actual.name).isEqualTo("cpu");
+ assertThat(actual.statsArrayLength).isEqualTo(3);
+ assertThat(actual.stateStatsArrayLength).isEqualTo(1);
+ assertThat(actual.getStateLabel(0x0F)).isEqualTo("idle");
+ assertThat(actual.getStateLabel(0xF0)).isEqualTo("cpu-f0");
+ assertThat(actual.uidStatsArrayLength).isEqualTo(2);
+ assertThat(actual.extras.getBoolean("hasPowerMonitor")).isEqualTo(true);
+ }
+
+ @Test
public void parceling_unrecognizedPowerComponent() {
PowerStats stats = new PowerStats(
- new PowerStats.Descriptor(777, "luck", 3, 2, new PersistableBundle()));
+ new PowerStats.Descriptor(777, "luck", 3, null, 1, 2, new PersistableBundle()));
stats.durationMs = 1234;
Parcel parcel = Parcel.obtain();
diff --git a/core/tests/mockingcoretests/Android.bp b/core/tests/mockingcoretests/Android.bp
index 2d778b1..aca52a8 100644
--- a/core/tests/mockingcoretests/Android.bp
+++ b/core/tests/mockingcoretests/Android.bp
@@ -40,6 +40,7 @@
"platform-test-annotations",
"truth",
"testables",
+ "flag-junit",
],
libs: [
diff --git a/core/tests/mockingcoretests/src/android/widget/OWNERS b/core/tests/mockingcoretests/src/android/widget/OWNERS
new file mode 100644
index 0000000..c0cbea9
--- /dev/null
+++ b/core/tests/mockingcoretests/src/android/widget/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/notification/OWNERS
\ No newline at end of file
diff --git a/core/tests/mockingcoretests/src/android/widget/ToastTest.java b/core/tests/mockingcoretests/src/android/widget/ToastTest.java
new file mode 100644
index 0000000..79bc81d
--- /dev/null
+++ b/core/tests/mockingcoretests/src/android/widget/ToastTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2024 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.widget;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.when;
+
+import android.app.INotificationManager;
+import android.content.Context;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.view.View;
+import android.widget.flags.Flags;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+
+/**
+ * ToastTest tests {@link Toast}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ToastTest {
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ private Context mContext;
+ private MockitoSession mMockingSession;
+ private static INotificationManager.Stub sMockNMS;
+
+ @Before
+ public void setup() {
+ mContext = InstrumentationRegistry.getContext();
+ mMockingSession =
+ ExtendedMockito.mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .mockStatic(ServiceManager.class)
+ .startMocking();
+
+ //Toast caches the NotificationManager service as static class member
+ if (sMockNMS == null) {
+ sMockNMS = mock(INotificationManager.Stub.class);
+ }
+ doReturn(sMockNMS).when(sMockNMS).queryLocalInterface("android.app.INotificationManager");
+ doReturn(sMockNMS).when(() -> ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ }
+
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ reset(sMockNMS);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_TOAST_NO_WEAKREF)
+ public void enqueueFail_nullifiesNextView() throws RemoteException {
+ Looper.prepare();
+
+ // allow 1st toast and fail on the 2nd
+ when(sMockNMS.enqueueToast(anyString(), any(), any(), anyInt(), anyBoolean(),
+ anyInt())).thenReturn(true, false);
+
+ // first toast is enqueued
+ Toast t = Toast.makeText(mContext, "Toast1", Toast.LENGTH_SHORT);
+ t.setView(mock(View.class));
+ t.show();
+ Toast.TN tn = t.getTn();
+ assertThat(tn.getNextView()).isNotNull();
+
+ // second toast is not enqueued
+ t = Toast.makeText(mContext, "Toast2", Toast.LENGTH_SHORT);
+ t.setView(mock(View.class));
+ t.show();
+ tn = t.getTn();
+ assertThat(tn.getNextView()).isNull();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_TOAST_NO_WEAKREF)
+ public void enqueueFail_doesNotNullifyNextView() throws RemoteException {
+ Looper.prepare();
+
+ // allow 1st toast and fail on the 2nd
+ when(sMockNMS.enqueueToast(anyString(), any(), any(), anyInt(), anyBoolean(),
+ anyInt())).thenReturn(true, false);
+
+ // first toast is enqueued
+ Toast t = Toast.makeText(mContext, "Toast1", Toast.LENGTH_SHORT);
+ t.setView(mock(View.class));
+ t.show();
+ Toast.TN tn = t.getTn();
+ assertThat(tn.getNextView()).isNotNull();
+
+ // second toast is not enqueued
+ t = Toast.makeText(mContext, "Toast2", Toast.LENGTH_SHORT);
+ t.setView(mock(View.class));
+ t.show();
+ tn = t.getTn();
+ assertThat(tn.getNextView()).isNotNull();
+ }
+}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 6cf12de..483b693 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1045,12 +1045,6 @@
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
},
- "-1459414342866553129": {
- "message": "Current focused window being animated by recents. Overriding back callback to recents controller callback.",
- "level": "DEBUG",
- "group": "WM_DEBUG_BACK_PREVIEW",
- "at": "com\/android\/server\/wm\/BackNavigationController.java"
- },
"2881085074175114605": {
"message": "Focused window didn't have a valid surface drawn.",
"level": "DEBUG",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 74e85f8..9adb67c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -507,6 +507,15 @@
final Point animRelOffset = new Point(
change.getEndAbsBounds().left - animRoot.getOffset().x,
change.getEndAbsBounds().top - animRoot.getOffset().y);
+
+ if (change.getActivityComponent() != null) {
+ // For appcompat letterbox: we intentionally report the task-bounds so that we
+ // can animate as-if letterboxes are "part of" the activity. This means we can't
+ // always rely solely on endAbsBounds and need to also max with endRelOffset.
+ animRelOffset.x = Math.max(animRelOffset.x, change.getEndRelOffset().x);
+ animRelOffset.y = Math.max(animRelOffset.y, change.getEndRelOffset().y);
+ }
+
if (change.getActivityComponent() != null && !isActivityLevel) {
// At this point, this is an independent activity change in a non-activity
// transition. This means that an activity transition got erroneously combined
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index b865d94..963b130 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -29,6 +29,7 @@
import android.app.ActivityManager;
import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
@@ -40,6 +41,7 @@
import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.os.Handler;
+import android.util.Log;
import android.view.Choreographer;
import android.view.MotionEvent;
import android.view.SurfaceControl;
@@ -434,15 +436,20 @@
}
private void loadAppInfo() {
+ final ActivityInfo activityInfo = mTaskInfo.topActivityInfo;
+ if (activityInfo == null) {
+ Log.e(TAG, "Top activity info not found in task");
+ return;
+ }
PackageManager pm = mContext.getApplicationContext().getPackageManager();
final IconProvider provider = new IconProvider(mContext);
- mAppIconDrawable = provider.getIcon(mTaskInfo.topActivityInfo);
+ mAppIconDrawable = provider.getIcon(activityInfo);
final Resources resources = mContext.getResources();
final BaseIconFactory factory = new BaseIconFactory(mContext,
resources.getDisplayMetrics().densityDpi,
resources.getDimensionPixelSize(R.dimen.desktop_mode_caption_icon_radius));
mAppIconBitmap = factory.createScaledBitmap(mAppIconDrawable, MODE_DEFAULT);
- final ApplicationInfo applicationInfo = mTaskInfo.topActivityInfo.applicationInfo;
+ final ApplicationInfo applicationInfo = activityInfo.applicationInfo;
mAppName = pm.getApplicationLabel(applicationInfo);
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 29bb1b9..4706699 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -538,6 +538,7 @@
"pipeline/skia/RenderNodeDrawable.cpp",
"pipeline/skia/ReorderBarrierDrawables.cpp",
"pipeline/skia/TransformCanvas.cpp",
+ "renderstate/RenderState.cpp",
"renderthread/Frame.cpp",
"renderthread/RenderTask.cpp",
"renderthread/TimeLord.cpp",
@@ -615,7 +616,6 @@
"pipeline/skia/SkiaVulkanPipeline.cpp",
"pipeline/skia/VkFunctorDrawable.cpp",
"pipeline/skia/VkInteropFunctorDrawable.cpp",
- "renderstate/RenderState.cpp",
"renderthread/CacheManager.cpp",
"renderthread/CanvasContext.cpp",
"renderthread/DrawFrameTask.cpp",
diff --git a/libs/hwui/platform/host/renderthread/RenderThread.cpp b/libs/hwui/platform/host/renderthread/RenderThread.cpp
index 6f08b59..f9d0f47 100644
--- a/libs/hwui/platform/host/renderthread/RenderThread.cpp
+++ b/libs/hwui/platform/host/renderthread/RenderThread.cpp
@@ -17,6 +17,7 @@
#include "renderthread/RenderThread.h"
#include "Readback.h"
+#include "renderstate/RenderState.h"
#include "renderthread/VulkanManager.h"
namespace android {
@@ -66,6 +67,7 @@
RenderThread::~RenderThread() {}
void RenderThread::initThreadLocals() {
+ mRenderState = new RenderState(*this);
mCacheManager = new CacheManager(*this);
}
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index e08d32a..60657cf 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -16,11 +16,13 @@
#ifndef RENDERSTATE_H
#define RENDERSTATE_H
-#include "utils/Macros.h"
-
+#include <pthread.h>
#include <utils/RefBase.h>
+
#include <set>
+#include "utils/Macros.h"
+
namespace android {
namespace uirenderer {
diff --git a/packages/InputDevices/res/raw/keyboard_layout_thai_kedmanee.kcm b/packages/InputDevices/res/raw/keyboard_layout_thai_kedmanee.kcm
new file mode 100644
index 0000000..2283032
--- /dev/null
+++ b/packages/InputDevices/res/raw/keyboard_layout_thai_kedmanee.kcm
@@ -0,0 +1,321 @@
+# Copyright 2024 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.
+
+#
+# Thai Kedmanee keyboard layout.
+#
+
+type OVERLAY
+
+map key 86 PLUS
+
+### ROW 1
+
+key GRAVE {
+ label: '_'
+ base: '_'
+ shift, capslock: '%'
+}
+
+key 1 {
+ label: '\u0e45'
+ base: '\u0e45'
+ shift, capslock: '+'
+}
+
+key 2 {
+ label: '/'
+ base: '/'
+ shift, capslock: '\u0e51'
+}
+
+key 3 {
+ label: '-'
+ base: '-'
+ shift, capslock: '\u0e52'
+}
+
+key 4 {
+ label: '\u0e20'
+ base: '\u0e20'
+ shift, capslock: '\u0e53'
+}
+
+key 5 {
+ label: '\u0e16'
+ base: '\u0e16'
+ shift, capslock: '\u0e54'
+}
+
+key 6 {
+ label: '\u0e38'
+ base: '\u0e38'
+ shift, capslock: '\u0e39'
+}
+
+key 7 {
+ label: '\u0e36'
+ base: '\u0e36'
+ shift, capslock: '\u0e3f'
+}
+
+key 8 {
+ label: '\u0e04'
+ base: '\u0e04'
+ shift, capslock: '\u0e55'
+}
+
+key 9 {
+ label: '\u0e15'
+ base: '\u0e15'
+ shift, capslock: '\u0e56'
+}
+
+key 0 {
+ label: '\u0e08'
+ base: '\u0e08'
+ shift, capslock: '\u0e57'
+}
+
+key MINUS {
+ label: '\u0e02'
+ base: '\u0e02'
+ shift, capslock: '\u0e58'
+}
+
+key EQUALS {
+ label: '\u0e0a'
+ base: '\u0e0a'
+ shift, capslock: '\u0e59'
+}
+
+### ROW 2
+
+key Q {
+ label: '\u0e46'
+ base: '\u0e46'
+ shift, capslock: '\u0e50'
+}
+
+key W {
+ label: '\u0e44'
+ base: '\u0e44'
+ shift, capslock: '\u0022'
+}
+
+key E {
+ label: '\u0e33'
+ base: '\u0e33'
+ shift, capslock: '\u0e0e'
+}
+
+key R {
+ label: '\u0e1e'
+ base: '\u0e1e'
+ shift, capslock: '\u0e11'
+}
+
+key T {
+ label: '\u0e30'
+ base: '\u0e30'
+ shift, capslock: '\u0e18'
+}
+
+key Y {
+ label: '\u0e31'
+ base: '\u0e31'
+ shift, capslock: '\u0e4d'
+}
+
+key U {
+ label: '\u0e35'
+ base: '\u0e35'
+ shift, capslock: '\u0e4a'
+}
+
+key I {
+ label: '\u0e23'
+ base: '\u0e23'
+ shift, capslock: '\u0e13'
+}
+
+key O {
+ label: '\u0e19'
+ base: '\u0e19'
+ shift, capslock: '\u0e2f'
+}
+
+key P {
+ label: '\u0e22'
+ base: '\u0e22'
+ shift, capslock: '\u0e0d'
+}
+
+key LEFT_BRACKET {
+ label: '\u0e1a'
+ base: '\u0e1a'
+ shift, capslock: '\u0e10'
+ ctrl: '%'
+}
+
+key RIGHT_BRACKET {
+ label: '\u0e25'
+ base: '\u0e25'
+ shift, capslock: ','
+ ctrl: '\u0e51'
+}
+
+### ROW 3
+
+key A {
+ label: '\u0e1f'
+ base: '\u0e1f'
+ shift, capslock: '\u0e24'
+}
+
+key S {
+ label: '\u0e2b'
+ base: '\u0e2b'
+ shift, capslock: '\u0e06'
+}
+
+key D {
+ label: '\u0e01'
+ base: '\u0e01'
+ shift, capslock: '\u0e0f'
+}
+
+key F {
+ label: '\u0e14'
+ base: '\u0e14'
+ shift, capslock: '\u0e42'
+}
+
+key G {
+ label: '\u0e40'
+ base: '\u0e40'
+ shift, capslock: '\u0e0c'
+}
+
+key H {
+ label: '\u0e49'
+ base: '\u0e49'
+ shift, capslock: '\u0e47'
+}
+
+key J {
+ label: '\u0e48'
+ base: '\u0e48'
+ shift, capslock: '\u0e4b'
+}
+
+key K {
+ label: '\u0e32'
+ base: '\u0e32'
+ shift, capslock: '\u0e29'
+}
+
+key L {
+ label: '\u0e2a'
+ base: '\u0e2a'
+ shift, capslock: '\u0e28'
+}
+
+key SEMICOLON {
+ label: '\u0e27'
+ base: '\u0e27'
+ shift, capslock: '\u0e0b'
+}
+
+key APOSTROPHE {
+ label: '\u0e07'
+ base: '\u0e07'
+ shift, capslock: '.'
+}
+
+key BACKSLASH {
+ label: '\u0e03'
+ base: '\u0e03'
+ shift, capslock: '\u0e05'
+ ctrl: '+'
+}
+
+### ROW 4
+
+key PLUS {
+ label: '\u0e03'
+ base: '\u0e03'
+ shift, capslock: '\u0e05'
+ ctrl: '\u0e52'
+}
+
+key Z {
+ label: '\u0e1c'
+ base: '\u0e1c'
+ shift, capslock: '('
+}
+
+key X {
+ label: '\u0e1b'
+ base: '\u0e1b'
+ shift, capslock: ')'
+}
+
+key C {
+ label: '\u0e41'
+ base: '\u0e41'
+ shift, capslock: '\u0e09'
+}
+
+key V {
+ label: '\u0e2d'
+ base: '\u0e2d'
+ shift, capslock: '\u0e2e'
+}
+
+key B {
+ label: '\u0e34'
+ base: '\u0e34'
+ shift, capslock: '\u0e3a'
+}
+
+key N {
+ label: '\u0e37'
+ base: '\u0e37'
+ shift, capslock: '\u0e4c'
+}
+
+key M {
+ label: '\u0e17'
+ base: '\u0e17'
+ shift, capslock: '?'
+}
+
+key COMMA {
+ label: '\u0e21'
+ base: '\u0e21'
+ shift, capslock: '\u0e12'
+}
+
+key PERIOD {
+ label: '\u0e43'
+ base: '\u0e43'
+ shift, capslock: '\u0e2c'
+}
+
+key SLASH {
+ label: '\u0e1d'
+ base: '\u0e1d'
+ shift, capslock: '\u0e26'
+}
\ No newline at end of file
diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml
index 1e13940..33a1d76 100644
--- a/packages/InputDevices/res/values/strings.xml
+++ b/packages/InputDevices/res/values/strings.xml
@@ -146,4 +146,7 @@
<!-- Georgian keyboard layout label. [CHAR LIMIT=35] -->
<string name="keyboard_layout_georgian">Georgian</string>
+
+ <!-- Thai (Kedmanee variant) keyboard layout label. [CHAR LIMIT=35] -->
+ <string name="keyboard_layout_thai_kedmanee">Thai (Kedmanee)</string>
</resources>
diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml
index ee49b23..4b7ea90 100644
--- a/packages/InputDevices/res/xml/keyboard_layouts.xml
+++ b/packages/InputDevices/res/xml/keyboard_layouts.xml
@@ -318,4 +318,11 @@
android:label="@string/keyboard_layout_georgian"
android:keyboardLayout="@raw/keyboard_layout_georgian"
android:keyboardLocale="ka-Geor" />
+
+ <keyboard-layout
+ android:name="keyboard_layout_thai_kedmanee"
+ android:label="@string/keyboard_layout_thai_kedmanee"
+ android:keyboardLayout="@raw/keyboard_layout_thai_kedmanee"
+ android:keyboardLocale="th-Thai"
+ android:keyboardLayoutType="extended" />
</keyboard-layouts>
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 761bb79..91bd791 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -40,7 +40,7 @@
import com.android.settingslib.spa.gallery.page.IllustrationPageProvider
import com.android.settingslib.spa.gallery.page.LoadingBarPageProvider
import com.android.settingslib.spa.gallery.page.ProgressBarPageProvider
-import com.android.settingslib.spa.gallery.page.SettingsPagerPageProvider
+import com.android.settingslib.spa.gallery.scaffold.NonScrollablePagerPageProvider
import com.android.settingslib.spa.gallery.page.SliderPageProvider
import com.android.settingslib.spa.gallery.preference.ListPreferencePageProvider
import com.android.settingslib.spa.gallery.preference.MainSwitchPreferencePageProvider
@@ -48,10 +48,12 @@
import com.android.settingslib.spa.gallery.preference.PreferencePageProvider
import com.android.settingslib.spa.gallery.preference.SwitchPreferencePageProvider
import com.android.settingslib.spa.gallery.preference.TwoTargetSwitchPreferencePageProvider
+import com.android.settingslib.spa.gallery.scaffold.PagerMainPageProvider
import com.android.settingslib.spa.gallery.scaffold.SearchScaffoldPageProvider
import com.android.settingslib.spa.gallery.scaffold.SuwScaffoldPageProvider
import com.android.settingslib.spa.gallery.ui.CategoryPageProvider
import com.android.settingslib.spa.gallery.ui.CopyablePageProvider
+import com.android.settingslib.spa.gallery.scaffold.ScrollablePagerPageProvider
import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
import com.android.settingslib.spa.slice.SpaSliceBroadcastReceiver
@@ -84,7 +86,9 @@
ArgumentPageProvider,
SliderPageProvider,
SpinnerPageProvider,
- SettingsPagerPageProvider,
+ PagerMainPageProvider,
+ NonScrollablePagerPageProvider,
+ ScrollablePagerPageProvider,
FooterPageProvider,
IllustrationPageProvider,
CategoryPageProvider,
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
index 1f028d5..654719d 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
@@ -39,9 +39,9 @@
import com.android.settingslib.spa.gallery.page.IllustrationPageProvider
import com.android.settingslib.spa.gallery.page.LoadingBarPageProvider
import com.android.settingslib.spa.gallery.page.ProgressBarPageProvider
-import com.android.settingslib.spa.gallery.page.SettingsPagerPageProvider
import com.android.settingslib.spa.gallery.page.SliderPageProvider
import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
+import com.android.settingslib.spa.gallery.scaffold.PagerMainPageProvider
import com.android.settingslib.spa.gallery.scaffold.SearchScaffoldPageProvider
import com.android.settingslib.spa.gallery.scaffold.SuwScaffoldPageProvider
import com.android.settingslib.spa.gallery.ui.CategoryPageProvider
@@ -63,7 +63,7 @@
SuwScaffoldPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
SliderPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
SpinnerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
- SettingsPagerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+ PagerMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
FooterPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
IllustrationPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
CategoryPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/NonScrollablePagerPageProvider.kt
similarity index 70%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/NonScrollablePagerPageProvider.kt
index dc45e6d..029773f 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/NonScrollablePagerPageProvider.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.gallery.page
+package com.android.settingslib.spa.gallery.scaffold
import android.os.Bundle
import androidx.compose.foundation.layout.Box
@@ -33,24 +33,19 @@
import com.android.settingslib.spa.widget.scaffold.SettingsScaffold
import com.android.settingslib.spa.widget.ui.PlaceholderTitle
-private const val TITLE = "Sample SettingsPager"
+object NonScrollablePagerPageProvider : SettingsPageProvider {
+ override val name = "NonScrollablePager"
+ private const val TITLE = "Sample Non Scrollable SettingsPager"
-object SettingsPagerPageProvider : SettingsPageProvider {
- override val name = "SettingsPager"
+ fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = createSettingsPage())
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
- fun buildInjectEntry(): SettingsEntryBuilder {
- return SettingsEntryBuilder.createInject(owner = createSettingsPage())
- .setUiLayoutFn {
- Preference(object : PreferenceModel {
- override val title = TITLE
- override val onClick = navigator(name)
- })
- }
- }
-
- override fun getTitle(arguments: Bundle?): String {
- return TITLE
- }
+ override fun getTitle(arguments: Bundle?) = TITLE
@Composable
override fun Page(arguments: Bundle?) {
@@ -66,8 +61,8 @@
@Preview(showBackground = true)
@Composable
-private fun SettingsPagerPagePreview() {
+private fun NonScrollablePagerPageProviderPreview() {
SettingsTheme {
- SettingsPagerPageProvider.Page(null)
+ NonScrollablePagerPageProvider.Page(null)
}
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/PagerMainPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/PagerMainPageProvider.kt
new file mode 100644
index 0000000..66cc38f
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/PagerMainPageProvider.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 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.settingslib.spa.gallery.scaffold
+
+import android.os.Bundle
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+
+object PagerMainPageProvider : SettingsPageProvider {
+ override val name = "PagerMain"
+ private val owner = createSettingsPage()
+ private const val TITLE = "Category: Pager"
+
+ override fun buildEntry(arguments: Bundle?) = listOf(
+ NonScrollablePagerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+ ScrollablePagerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+ )
+
+ fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = owner)
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+
+ override fun getTitle(arguments: Bundle?) = TITLE
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/ScrollablePagerPageProvider.kt
similarity index 62%
copy from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt
copy to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/ScrollablePagerPageProvider.kt
index dc45e6d..689a98a 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SettingsPagerPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/ScrollablePagerPageProvider.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-package com.android.settingslib.spa.gallery.page
+package com.android.settingslib.spa.gallery.scaffold
import android.os.Bundle
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
@@ -31,33 +32,33 @@
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.SettingsPager
import com.android.settingslib.spa.widget.scaffold.SettingsScaffold
-import com.android.settingslib.spa.widget.ui.PlaceholderTitle
-private const val TITLE = "Sample SettingsPager"
+object ScrollablePagerPageProvider : SettingsPageProvider {
+ override val name = "ScrollablePager"
+ private const val TITLE = "Sample Scrollable SettingsPager"
-object SettingsPagerPageProvider : SettingsPageProvider {
- override val name = "SettingsPager"
+ fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = createSettingsPage())
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
- fun buildInjectEntry(): SettingsEntryBuilder {
- return SettingsEntryBuilder.createInject(owner = createSettingsPage())
- .setUiLayoutFn {
- Preference(object : PreferenceModel {
- override val title = TITLE
- override val onClick = navigator(name)
- })
- }
- }
-
- override fun getTitle(arguments: Bundle?): String {
- return TITLE
- }
+ override fun getTitle(arguments: Bundle?) = TITLE
@Composable
override fun Page(arguments: Bundle?) {
SettingsScaffold(title = getTitle(arguments)) { paddingValues ->
Box(Modifier.padding(paddingValues)) {
SettingsPager(listOf("Personal", "Work")) {
- PlaceholderTitle("Page $it")
+ LazyColumn {
+ items(30) {
+ Preference(object : PreferenceModel {
+ override val title = it.toString()
+ })
+ }
+ }
}
}
}
@@ -66,8 +67,8 @@
@Preview(showBackground = true)
@Composable
-private fun SettingsPagerPagePreview() {
+private fun ScrollablePagerPageProviderPreview() {
SettingsTheme {
- SettingsPagerPageProvider.Page(null)
+ ScrollablePagerPageProvider.Page(null)
}
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SuwScaffoldPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SuwScaffoldPageProvider.kt
index 6fc8de3..a0ab2ce 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SuwScaffoldPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/scaffold/SuwScaffoldPageProvider.kt
@@ -22,6 +22,10 @@
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.SignalCellularAlt
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
@@ -37,6 +41,8 @@
import com.android.settingslib.spa.widget.scaffold.BottomAppBarButton
import com.android.settingslib.spa.widget.scaffold.SuwScaffold
import com.android.settingslib.spa.widget.ui.SettingsBody
+import com.android.settingslib.spa.widget.ui.Spinner
+import com.android.settingslib.spa.widget.ui.SpinnerOption
private const val TITLE = "Sample SuwScaffold"
@@ -67,13 +73,12 @@
actionButton = BottomAppBarButton("Next") {},
dismissButton = BottomAppBarButton("Cancel") {},
) {
- Column(Modifier.padding(SettingsDimension.itemPadding)) {
- SettingsBody("To add another SIM, download a new eSIM.")
- }
- Illustration(object : IllustrationModel {
- override val resId = R.drawable.accessibility_captioning_banner
- override val resourceType = ResourceType.IMAGE
- })
+ var selectedId by rememberSaveable { mutableIntStateOf(1) }
+ Spinner(
+ options = (1..3).map { SpinnerOption(id = it, text = "Option $it") },
+ selectedId = selectedId,
+ setId = { selectedId = it },
+ )
Column(Modifier.padding(SettingsDimension.itemPadding)) {
SettingsBody("To add another SIM, download a new eSIM.")
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreference.kt
index 791893b..ef1a137 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreference.kt
@@ -37,6 +37,7 @@
SettingsSwitch(
checked = model.checked(),
changeable = model.changeable,
+ contentDescription = model.title,
onCheckedChange = model.onCheckedChange,
)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt
index f372a45..163766a 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt
@@ -19,7 +19,6 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
@@ -33,6 +32,8 @@
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.movableContentOf
+import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import com.android.settingslib.spa.framework.theme.SettingsDimension
@@ -50,7 +51,7 @@
title: String,
actionButton: BottomAppBarButton? = null,
dismissButton: BottomAppBarButton? = null,
- content: @Composable ColumnScope.() -> Unit,
+ content: @Composable () -> Unit,
) {
ActivityTitle(title)
Scaffold { innerPadding ->
@@ -59,6 +60,7 @@
.padding(innerPadding)
.padding(top = SettingsDimension.itemPaddingAround)
) {
+ val movableContent = remember(content) { movableContentOf { content() } }
// Use single column layout in portrait, two columns in landscape.
val useSingleColumn = maxWidth < maxHeight
if (useSingleColumn) {
@@ -69,7 +71,7 @@
.verticalScroll(rememberScrollState())
) {
Header(imageVector, title)
- content()
+ movableContent()
}
BottomBar(actionButton, dismissButton)
}
@@ -82,8 +84,9 @@
Column(
Modifier
.weight(1f)
- .verticalScroll(rememberScrollState())) {
- content()
+ .verticalScroll(rememberScrollState())
+ ) {
+ movableContent()
}
}
BottomBar(actionButton, dismissButton)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt
index a0da241..5155406 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt
@@ -20,12 +20,16 @@
import androidx.compose.material3.Switch
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
import com.android.settingslib.spa.framework.util.wrapOnSwitchWithLog
@Composable
internal fun SettingsSwitch(
checked: Boolean?,
changeable: () -> Boolean,
+ contentDescription: String? = null,
onCheckedChange: ((newChecked: Boolean) -> Unit)? = null,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) {
@@ -33,6 +37,9 @@
Switch(
checked = checked,
onCheckedChange = wrapOnSwitchWithLog(onCheckedChange),
+ modifier = if (contentDescription != null) Modifier.semantics {
+ this.contentDescription = contentDescription
+ } else Modifier,
enabled = changeable(),
interactionSource = interactionSource,
)
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
index a4089c0..7f4bebc 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
@@ -52,16 +52,12 @@
import java.util.Comparator;
import java.util.HashMap;
-/**
- * Description of a single dashboard tile that the user can select.
- */
+/** Description of a single dashboard tile that the user can select. */
public abstract class Tile implements Parcelable {
private static final String TAG = "Tile";
- /**
- * Optional list of user handles which the intent should be launched on.
- */
+ /** Optional list of user handles which the intent should be launched on. */
public ArrayList<UserHandle> userHandle = new ArrayList<>();
public HashMap<UserHandle, PendingIntent> pendingIntentMap = new HashMap<>();
@@ -76,6 +72,7 @@
private CharSequence mSummaryOverride;
private Bundle mMetaData;
private String mCategory;
+ private String mGroupKey;
public Tile(ComponentInfo info, String category, Bundle metaData) {
mComponentInfo = info;
@@ -83,6 +80,9 @@
mComponentName = mComponentInfo.name;
mCategory = category;
mMetaData = metaData;
+ if (mMetaData != null) {
+ mGroupKey = metaData.getString(META_DATA_PREFERENCE_GROUP_KEY);
+ }
mIntent = new Intent().setClassName(mComponentPackage, mComponentName);
if (isNewTask()) {
mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -102,6 +102,7 @@
if (isNewTask()) {
mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
+ mGroupKey = in.readString();
}
@Override
@@ -121,16 +122,13 @@
}
dest.writeString(mCategory);
dest.writeBundle(mMetaData);
+ dest.writeString(mGroupKey);
}
- /**
- * Unique ID of the tile
- */
+ /** Unique ID of the tile */
public abstract int getId();
- /**
- * Human-readable description of the tile
- */
+ /** Human-readable description of the tile */
public abstract String getDescription();
protected abstract ComponentInfo getComponentInfo(Context context);
@@ -147,16 +145,12 @@
return mComponentName;
}
- /**
- * Intent to launch when the preference is selected.
- */
+ /** Intent to launch when the preference is selected. */
public Intent getIntent() {
return mIntent;
}
- /**
- * Category in which the tile should be placed.
- */
+ /** Category in which the tile should be placed. */
public String getCategory() {
return mCategory;
}
@@ -165,9 +159,7 @@
mCategory = newCategoryKey;
}
- /**
- * Priority of this tile, used for display ordering.
- */
+ /** Priority of this tile, used for display ordering. */
public int getOrder() {
if (hasOrder()) {
return mMetaData.getInt(META_DATA_KEY_ORDER);
@@ -176,32 +168,24 @@
}
}
- /**
- * Check whether tile has order.
- */
+ /** Check whether tile has order. */
public boolean hasOrder() {
return mMetaData != null
&& mMetaData.containsKey(META_DATA_KEY_ORDER)
&& mMetaData.get(META_DATA_KEY_ORDER) instanceof Integer;
}
- /**
- * Check whether tile has a switch.
- */
+ /** Check whether tile has a switch. */
public boolean hasSwitch() {
return mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_SWITCH_URI);
}
- /**
- * Check whether tile has a pending intent.
- */
+ /** Check whether tile has a pending intent. */
public boolean hasPendingIntent() {
return !pendingIntentMap.isEmpty();
}
- /**
- * Title of the tile that is shown to the user.
- */
+ /** Title of the tile that is shown to the user. */
public CharSequence getTitle(Context context) {
CharSequence title = null;
ensureMetadataNotStale(context);
@@ -238,9 +222,7 @@
mSummaryOverride = summaryOverride;
}
- /**
- * Optional summary describing what this tile controls.
- */
+ /** Optional summary describing what this tile controls. */
public CharSequence getSummary(Context context) {
if (mSummaryOverride != null) {
return mSummaryOverride;
@@ -275,16 +257,12 @@
mMetaData = metaData;
}
- /**
- * The metaData from the activity that defines this tile.
- */
+ /** The metaData from the activity that defines this tile. */
public Bundle getMetaData() {
return mMetaData;
}
- /**
- * Optional key to use for this tile.
- */
+ /** Optional key to use for this tile. */
public String getKey(Context context) {
ensureMetadataNotStale(context);
if (!hasKey()) {
@@ -297,9 +275,7 @@
}
}
- /**
- * Check whether title has key.
- */
+ /** Check whether title has key. */
public boolean hasKey() {
return mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_KEYHINT);
}
@@ -325,8 +301,9 @@
if (iconResId != 0 && iconResId != android.R.color.transparent) {
final Icon icon = Icon.createWithResource(componentInfo.packageName, iconResId);
if (isIconTintable(context)) {
- final TypedArray a = context.obtainStyledAttributes(new int[]{
- android.R.attr.colorControlNormal});
+ final TypedArray a =
+ context.obtainStyledAttributes(
+ new int[] {android.R.attr.colorControlNormal});
final int tintColor = a.getColor(0, 0);
a.recycle();
icon.setTint(tintColor);
@@ -349,26 +326,22 @@
return false;
}
- /**
- * Whether the {@link Activity} should be launched in a separate task.
- */
+ /** Whether the {@link Activity} should be launched in a separate task. */
public boolean isNewTask() {
- if (mMetaData != null
- && mMetaData.containsKey(META_DATA_NEW_TASK)) {
+ if (mMetaData != null && mMetaData.containsKey(META_DATA_NEW_TASK)) {
return mMetaData.getBoolean(META_DATA_NEW_TASK);
}
return false;
}
- /**
- * Ensures metadata is not stale for this tile.
- */
+ /** Ensures metadata is not stale for this tile. */
private void ensureMetadataNotStale(Context context) {
final PackageManager pm = context.getApplicationContext().getPackageManager();
try {
- final long lastUpdateTime = pm.getPackageInfo(mComponentPackage,
- PackageManager.GET_META_DATA).lastUpdateTime;
+ final long lastUpdateTime =
+ pm.getPackageInfo(mComponentPackage, PackageManager.GET_META_DATA)
+ .lastUpdateTime;
if (lastUpdateTime == mLastUpdateTime) {
// All good. Do nothing
return;
@@ -382,72 +355,60 @@
}
}
- public static final Creator<Tile> CREATOR = new Creator<Tile>() {
- public Tile createFromParcel(Parcel source) {
- final boolean isProviderTile = source.readBoolean();
- // reset the Parcel pointer before delegating to the real constructor.
- source.setDataPosition(0);
- return isProviderTile ? new ProviderTile(source) : new ActivityTile(source);
- }
+ public static final Creator<Tile> CREATOR =
+ new Creator<Tile>() {
+ public Tile createFromParcel(Parcel source) {
+ final boolean isProviderTile = source.readBoolean();
+ // reset the Parcel pointer before delegating to the real constructor.
+ source.setDataPosition(0);
+ return isProviderTile ? new ProviderTile(source) : new ActivityTile(source);
+ }
- public Tile[] newArray(int size) {
- return new Tile[size];
- }
- };
+ public Tile[] newArray(int size) {
+ return new Tile[size];
+ }
+ };
- /**
- * Check whether tile only has primary profile.
- */
+ /** Check whether tile only has primary profile. */
public boolean isPrimaryProfileOnly() {
return isPrimaryProfileOnly(mMetaData);
}
static boolean isPrimaryProfileOnly(Bundle metaData) {
- String profile = metaData != null
- ? metaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
+ String profile = metaData != null ? metaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
profile = (profile != null ? profile : PROFILE_ALL);
return TextUtils.equals(profile, PROFILE_PRIMARY);
}
- /**
- * Returns whether the tile belongs to another group / category.
- */
+ /** Returns whether the tile belongs to another group / category. */
public boolean hasGroupKey() {
- return mMetaData != null
- && !TextUtils.isEmpty(mMetaData.getString(META_DATA_PREFERENCE_GROUP_KEY));
+ return !TextUtils.isEmpty(mGroupKey);
}
- /**
- * Returns the group / category key this tile belongs to.
- */
+ /** Set the group / PreferenceCategory key this tile belongs to. */
+ public void setGroupKey(String groupKey) {
+ mGroupKey = groupKey;
+ }
+
+ /** Returns the group / category key this tile belongs to. */
public String getGroupKey() {
- return (mMetaData == null) ? null : mMetaData.getString(META_DATA_PREFERENCE_GROUP_KEY);
+ return mGroupKey;
}
- /**
- * Returns if this is searchable.
- */
+ /** Returns if this is searchable. */
public boolean isSearchable() {
return mMetaData == null || mMetaData.getBoolean(META_DATA_PREFERENCE_SEARCHABLE, true);
}
- /**
- * The type of the tile.
- */
+ /** The type of the tile. */
public enum Type {
- /**
- * A preference that can be tapped on to open a new page.
- */
+ /** A preference that can be tapped on to open a new page. */
ACTION,
- /**
- * A preference that can be tapped on to open an external app.
- */
+ /** A preference that can be tapped on to open an external app. */
EXTERNAL_ACTION,
- /**
- * A preference that shows an on / off switch that can be toggled by the user.
- */
+ /** A preference that shows an on / off switch that can be toggled by the user. */
SWITCH,
/**
@@ -460,7 +421,7 @@
* A preference category with a title that can be used to group multiple preferences
* together.
*/
- GROUP;
+ GROUP
}
/**
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
index d0929e1..b949cd5 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
@@ -77,9 +77,7 @@
*/
public static final String IA_SETTINGS_ACTION = "com.android.settings.action.IA_SETTINGS";
- /**
- * Same as #EXTRA_SETTINGS_ACTION but used for the platform Settings activities.
- */
+ /** Same as #EXTRA_SETTINGS_ACTION but used for the platform Settings activities. */
private static final String SETTINGS_ACTION = "com.android.settings.action.SETTINGS";
private static final String OPERATOR_SETTINGS =
@@ -101,9 +99,7 @@
*/
static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";
- /**
- * The key used to get the package name of the icon resource for the preference.
- */
+ /** The key used to get the package name of the icon resource for the preference. */
static final String EXTRA_PREFERENCE_ICON_PACKAGE = "com.android.settings.icon_package";
/**
@@ -145,18 +141,17 @@
"com.android.settings.bg.argb";
/**
- * Name of the meta-data item that should be set in the AndroidManifest.xml
- * to specify the content provider providing the icon that should be displayed for
- * the preference.
+ * Name of the meta-data item that should be set in the AndroidManifest.xml to specify the
+ * content provider providing the icon that should be displayed for the preference.
*
- * Icon provided by the content provider overrides any static icon.
+ * <p>Icon provided by the content provider overrides any static icon.
*/
public static final String META_DATA_PREFERENCE_ICON_URI = "com.android.settings.icon_uri";
/**
- * Name of the meta-data item that should be set in the AndroidManifest.xml
- * to specify whether the icon is tintable. This should be a boolean value {@code true} or
- * {@code false}, set using {@code android:value}
+ * Name of the meta-data item that should be set in the AndroidManifest.xml to specify whether
+ * the icon is tintable. This should be a boolean value {@code true} or {@code false}, set using
+ * {@code android:value}
*/
public static final String META_DATA_PREFERENCE_ICON_TINTABLE =
"com.android.settings.icon_tintable";
@@ -171,41 +166,36 @@
public static final String META_DATA_PREFERENCE_TITLE = "com.android.settings.title";
/**
- * Name of the meta-data item that should be set in the AndroidManifest.xml
- * to specify the content provider providing the title text that should be displayed for the
- * preference.
+ * Name of the meta-data item that should be set in the AndroidManifest.xml to specify the
+ * content provider providing the title text that should be displayed for the preference.
*
- * Title provided by the content provider overrides any static title.
+ * <p>Title provided by the content provider overrides any static title.
*/
- public static final String META_DATA_PREFERENCE_TITLE_URI =
- "com.android.settings.title_uri";
+ public static final String META_DATA_PREFERENCE_TITLE_URI = "com.android.settings.title_uri";
/**
- * Name of the meta-data item that should be set in the AndroidManifest.xml
- * to specify the summary text that should be displayed for the preference.
+ * Name of the meta-data item that should be set in the AndroidManifest.xml to specify the
+ * summary text that should be displayed for the preference.
*/
public static final String META_DATA_PREFERENCE_SUMMARY = "com.android.settings.summary";
/**
- * Name of the meta-data item that should be set in the AndroidManifest.xml
- * to specify the content provider providing the summary text that should be displayed for the
- * preference.
+ * Name of the meta-data item that should be set in the AndroidManifest.xml to specify the
+ * content provider providing the summary text that should be displayed for the preference.
*
- * Summary provided by the content provider overrides any static summary.
+ * <p>Summary provided by the content provider overrides any static summary.
*/
public static final String META_DATA_PREFERENCE_SUMMARY_URI =
"com.android.settings.summary_uri";
/**
- * Name of the meta-data item that should be set in the AndroidManifest.xml
- * to specify the content provider providing the switch that should be displayed for the
- * preference.
+ * Name of the meta-data item that should be set in the AndroidManifest.xml to specify the
+ * content provider providing the switch that should be displayed for the preference.
*
- * This works with {@link #META_DATA_PREFERENCE_KEYHINT} which should also be set in the
+ * <p>This works with {@link #META_DATA_PREFERENCE_KEYHINT} which should also be set in the
* AndroidManifest.xml
*/
- public static final String META_DATA_PREFERENCE_SWITCH_URI =
- "com.android.settings.switch_uri";
+ public static final String META_DATA_PREFERENCE_SWITCH_URI = "com.android.settings.switch_uri";
/**
* Name of the meta-data item that can be set from the content provider providing the intent
@@ -215,8 +205,8 @@
"com.android.settings.pending_intent";
/**
- * Value for {@link #META_DATA_KEY_PROFILE}. When the device has a managed profile,
- * the app will always be run in the primary profile.
+ * Value for {@link #META_DATA_KEY_PROFILE}. When the device has a managed profile, the app will
+ * always be run in the primary profile.
*
* @see #META_DATA_KEY_PROFILE
*/
@@ -231,11 +221,11 @@
public static final String PROFILE_ALL = "all_profiles";
/**
- * Name of the meta-data item that should be set in the AndroidManifest.xml
- * to specify the profile in which the app should be run when the device has a managed profile.
- * The default value is {@link #PROFILE_ALL} which means the user will be presented with a
- * dialog to choose the profile. If set to {@link #PROFILE_PRIMARY} the app will always be
- * run in the primary profile.
+ * Name of the meta-data item that should be set in the AndroidManifest.xml to specify the
+ * profile in which the app should be run when the device has a managed profile. The default
+ * value is {@link #PROFILE_ALL} which means the user will be presented with a dialog to choose
+ * the profile. If set to {@link #PROFILE_PRIMARY} the app will always be run in the primary
+ * profile.
*
* @see #PROFILE_PRIMARY
* @see #PROFILE_ALL
@@ -243,20 +233,16 @@
public static final String META_DATA_KEY_PROFILE = "com.android.settings.profile";
/**
- * Name of the meta-data item that should be set in the AndroidManifest.xml
- * to specify whether the {@link android.app.Activity} should be launched in a separate task.
- * This should be a boolean value {@code true} or {@code false}, set using {@code android:value}
+ * Name of the meta-data item that should be set in the AndroidManifest.xml to specify whether
+ * the {@link android.app.Activity} should be launched in a separate task. This should be a
+ * boolean value {@code true} or {@code false}, set using {@code android:value}
*/
public static final String META_DATA_NEW_TASK = "com.android.settings.new_task";
- /**
- * If the entry should be shown in settings search results. Defaults to true.
- */
+ /** If the entry should be shown in settings search results. Defaults to true. */
public static final String META_DATA_PREFERENCE_SEARCHABLE = "com.android.settings.searchable";
- /**
- * Build a list of DashboardCategory.
- */
+ /** Build a list of DashboardCategory. */
public static List<DashboardCategory> getCategories(Context context,
Map<Pair<String, String>, Tile> cache) {
final long startTime = System.currentTimeMillis();
@@ -341,8 +327,8 @@
UserHandle user, Map<Pair<String, String>, Tile> addedCache,
String defaultCategory, List<Tile> outTiles, Intent intent) {
final PackageManager pm = context.getPackageManager();
- final List<ResolveInfo> results = pm.queryIntentContentProvidersAsUser(intent,
- 0 /* flags */, user.getIdentifier());
+ final List<ResolveInfo> results =
+ pm.queryIntentContentProvidersAsUser(intent, 0 /* flags */, user.getIdentifier());
for (ResolveInfo resolved : results) {
if (!resolved.system) {
// Do not allow any app to add to settings, only system ones.
@@ -403,6 +389,8 @@
tile.setMetaData(metaData);
}
+ tile.setGroupKey(metaData.getString(META_DATA_PREFERENCE_GROUP_KEY));
+
if (!tile.userHandle.contains(user)) {
tile.userHandle.add(user);
}
@@ -448,15 +436,15 @@
/**
* Returns the complete uri from the meta data key of the tile.
*
- * A complete uri should contain at least one path segment and be one of the following types:
- * content://authority/method
- * content://authority/method/key
+ * <p>A complete uri should contain at least one path segment and be one of the following types:
+ * <br>content://authority/method
+ * <br>content://authority/method/key
*
- * If the uri from the tile is not complete, build a uri by the default method and the
+ * <p>If the uri from the tile is not complete, build a uri by the default method and the
* preference key.
*
- * @param tile Tile which contains meta data
- * @param metaDataKey Key mapping to the uri in meta data
+ * @param tile Tile which contains meta data
+ * @param metaDataKey Key mapping to the uri in meta data
* @param defaultMethod Method to be attached to the uri by default if it has no path segment
* @return Uri associated with the key
*/
@@ -501,9 +489,9 @@
/**
* Gets the icon package name and resource id from content provider.
*
- * @param context context
+ * @param context context
* @param packageName package name of the target activity
- * @param uri URI for the content provider
+ * @param uri URI for the content provider
* @param providerMap Maps URI authorities to providers
* @return package name and resource id of the icon specified
*/
@@ -532,10 +520,10 @@
/**
* Gets text associated with the input key from the content provider.
*
- * @param context context
- * @param uri URI for the content provider
+ * @param context context
+ * @param uri URI for the content provider
* @param providerMap Maps URI authorities to providers
- * @param key Key mapping to the text in bundle returned by the content provider
+ * @param key Key mapping to the text in bundle returned by the content provider
* @return Text associated with the key, if returned by the content provider
*/
public static String getTextFromUri(Context context, Uri uri,
@@ -547,10 +535,10 @@
/**
* Gets boolean associated with the input key from the content provider.
*
- * @param context context
- * @param uri URI for the content provider
+ * @param context context
+ * @param uri URI for the content provider
* @param providerMap Maps URI authorities to providers
- * @param key Key mapping to the text in bundle returned by the content provider
+ * @param key Key mapping to the text in bundle returned by the content provider
* @return Boolean associated with the key, if returned by the content provider
*/
public static boolean getBooleanFromUri(Context context, Uri uri,
@@ -562,11 +550,11 @@
/**
* Puts boolean associated with the input key to the content provider.
*
- * @param context context
- * @param uri URI for the content provider
+ * @param context context
+ * @param uri URI for the content provider
* @param providerMap Maps URI authorities to providers
- * @param key Key mapping to the text in bundle returned by the content provider
- * @param value Boolean associated with the key
+ * @param key Key mapping to the text in bundle returned by the content provider
+ * @param value Boolean associated with the key
* @return Bundle associated with the action, if returned by the content provider
*/
public static Bundle putBooleanToUriAndGetResult(Context context, Uri uri,
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 9ec5caa..89f54d9 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -52,3 +52,13 @@
description: "Hide exclusively managed Bluetooth devices in BT settings menu."
bug: "324475542"
}
+
+flag {
+ name: "enable_set_preferred_transport_for_le_audio_device"
+ namespace: "bluetooth"
+ description: "Enable setting preferred transport for Le Audio device"
+ bug: "330581926"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 56118da..06c41cb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -1993,6 +1993,40 @@
};
/**
+ * Displays a combined list with "downloaded" and "visible in launcher" apps which belong to a
+ * user which is either not in quiet mode or allows showing apps even when in quiet mode.
+ */
+ public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER_NOT_QUIET = new AppFilter() {
+ @Override
+ public void init() {
+ }
+
+ @Override
+ public boolean filterApp(@NonNull AppEntry entry) {
+ if (entry.hideInQuietMode) {
+ return false;
+ }
+ if (AppUtils.isInstant(entry.info)) {
+ return false;
+ } else if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) {
+ return true;
+ } else if (!hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM)) {
+ return true;
+ } else if (entry.hasLauncherEntry) {
+ return true;
+ } else if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_SYSTEM) && entry.isHomeApp) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void refreshAppEntryOnRebuild(@NonNull AppEntry appEntry, boolean hideInQuietMode) {
+ appEntry.hideInQuietMode = hideInQuietMode;
+ }
+ };
+
+ /**
* Displays a combined list with "downloaded" and "visible in launcher" apps only.
*/
public static final AppFilter FILTER_DOWNLOADED_AND_LAUNCHER_AND_INSTANT = new AppFilter() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 4777b0d..04516eb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -16,6 +16,8 @@
package com.android.settingslib.bluetooth;
+import static com.android.settingslib.flags.Flags.enableSetPreferredTransportForLeAudioDevice;
+
import android.annotation.CallbackExecutor;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
@@ -288,6 +290,10 @@
mLocalNapRoleConnected = true;
}
}
+ if (enableSetPreferredTransportForLeAudioDevice()
+ && profile instanceof HidProfile) {
+ updatePreferredTransport();
+ }
} else if (profile instanceof MapProfile
&& newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
profile.setEnabled(mDevice, false);
@@ -300,12 +306,34 @@
mLocalNapRoleConnected = false;
}
+ if (enableSetPreferredTransportForLeAudioDevice()
+ && profile instanceof LeAudioProfile) {
+ updatePreferredTransport();
+ }
+
HearingAidStatsLogUtils.updateHistoryIfNeeded(mContext, this, profile, newProfileState);
}
fetchActiveDevices();
}
+ private void updatePreferredTransport() {
+ if (mProfiles.stream().noneMatch(p -> p instanceof LeAudioProfile)
+ || mProfiles.stream().noneMatch(p -> p instanceof HidProfile)) {
+ return;
+ }
+ // Both LeAudioProfile and HidProfile are connectable.
+ if (!mProfileManager
+ .getHidProfile()
+ .setPreferredTransport(
+ mDevice,
+ mProfileManager.getLeAudioProfile().isEnabled(mDevice)
+ ? BluetoothDevice.TRANSPORT_LE
+ : BluetoothDevice.TRANSPORT_BREDR)) {
+ Log.w(TAG, "Fail to set preferred transport");
+ }
+ }
+
@VisibleForTesting
void setProfileConnectedStatus(int profileId, boolean isFailed) {
switch (profileId) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index 5b91ac9..b849d44 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -27,6 +27,8 @@
import android.content.Context;
import android.util.Log;
+import androidx.annotation.NonNull;
+
import com.android.settingslib.R;
import java.util.List;
@@ -187,6 +189,14 @@
}
}
+ /** Set preferred transport for the device */
+ public boolean setPreferredTransport(@NonNull BluetoothDevice device, int transport) {
+ if (mService != null) {
+ mService.setPreferredTransport(device, transport);
+ }
+ return false;
+ }
+
protected void finalize() {
Log.d(TAG, "finalize()");
if (mService != null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 6c12cb7..9df23aa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -42,6 +42,7 @@
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
+import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
@@ -336,7 +337,6 @@
+ ", sourceId = "
+ sourceId);
}
- updateFallbackActiveDeviceIfNeeded();
}
@Override
@@ -468,6 +468,15 @@
mServiceBroadcast.startBroadcast(settings);
}
+ /** Checks if the broadcast is playing. */
+ public boolean isPlaying(int broadcastId) {
+ if (mServiceBroadcast == null) {
+ Log.d(TAG, "check isPlaying failed, the BluetoothLeBroadcast is null.");
+ return false;
+ }
+ return mServiceBroadcast.isPlaying(broadcastId);
+ }
+
private BluetoothLeBroadcastSettings buildBroadcastSettings(
boolean isPublic,
@Nullable String broadcastName,
@@ -1025,6 +1034,16 @@
/** Update fallback active device if needed. */
public void updateFallbackActiveDeviceIfNeeded() {
+ if (mServiceBroadcast == null) {
+ Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded due to broadcast profile is null");
+ return;
+ }
+ List<BluetoothLeBroadcastMetadata> sources = mServiceBroadcast.getAllBroadcastMetadata();
+ if (sources.stream()
+ .noneMatch(source -> mServiceBroadcast.isPlaying(source.getBroadcastId()))) {
+ Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded due to no broadcast ongoing");
+ return;
+ }
if (mServiceBroadcastAssistant == null) {
Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded due to assistant profile is null");
return;
@@ -1117,10 +1136,19 @@
Log.d(TAG, "Skip notifyBroadcastStateChange, not triggered by Settings.");
return;
}
+ if (isWorkProfile(mContext)) {
+ Log.d(TAG, "Skip notifyBroadcastStateChange, not triggered for work profile.");
+ return;
+ }
Intent intent = new Intent(ACTION_LE_AUDIO_SHARING_STATE_CHANGE);
intent.putExtra(EXTRA_LE_AUDIO_SHARING_STATE, state);
intent.setPackage(mContext.getPackageName());
Log.e(TAG, "notifyBroadcastStateChange for state = " + state);
mContext.sendBroadcast(intent);
}
+
+ private boolean isWorkProfile(Context context) {
+ UserManager userManager = context.getSystemService(UserManager.class);
+ return userManager != null && userManager.isManagedProfile();
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 79e4c37..4055986 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -572,8 +572,7 @@
return mSapProfile;
}
- @VisibleForTesting
- HidProfile getHidProfile() {
+ public HidProfile getHidProfile() {
return mHidProfile;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
index b015b2b..46f2290 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
@@ -184,7 +184,7 @@
dialogHelper
.setTitle(R.string.user_info_settings_title)
.addCustomView(content)
- .setPositiveButton(android.R.string.ok, view -> {
+ .setPositiveButton(R.string.okay, view -> {
Drawable newUserIcon = mEditUserPhotoController != null
? mEditUserPhotoController.getNewUserPhotoDrawable()
: null;
@@ -201,7 +201,7 @@
}
dialogHelper.getDialog().dismiss();
})
- .setBackButton(android.R.string.cancel, view -> {
+ .setBackButton(R.string.cancel, view -> {
clear();
if (cancelCallback != null) {
cancelCallback.run();
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 b974888..fef0561 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
@@ -240,6 +240,56 @@
}
@Test
+ public void testDownloadAndLauncherNotInQuietAcceptsCorrectApps() {
+ mEntry.isHomeApp = false;
+ mEntry.hasLauncherEntry = false;
+
+ // should include updated system apps
+ when(mEntry.info.isInstantApp()).thenReturn(false);
+ mEntry.info.flags = ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+ assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_NOT_QUIET.filterApp(mEntry))
+ .isTrue();
+
+ // should not include system apps other than the home app
+ mEntry.info.flags = ApplicationInfo.FLAG_SYSTEM;
+ mEntry.isHomeApp = false;
+ mEntry.hasLauncherEntry = false;
+ assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_NOT_QUIET.filterApp(mEntry))
+ .isFalse();
+
+ // should include the home app
+ mEntry.isHomeApp = true;
+ assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_NOT_QUIET.filterApp(mEntry))
+ .isTrue();
+
+ // should include any System app with a launcher entry
+ mEntry.isHomeApp = false;
+ mEntry.hasLauncherEntry = true;
+ assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_NOT_QUIET.filterApp(mEntry))
+ .isTrue();
+
+ // should not include updated system apps when in quiet mode
+ when(mEntry.info.isInstantApp()).thenReturn(false);
+ mEntry.info.flags = ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+ mEntry.hideInQuietMode = true;
+ assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_NOT_QUIET.filterApp(mEntry))
+ .isFalse();
+
+ // should not include the home app when in quiet mode
+ mEntry.isHomeApp = true;
+ mEntry.hideInQuietMode = true;
+ assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_NOT_QUIET.filterApp(mEntry))
+ .isFalse();
+
+ // should not include any System app with a launcher entry when in quiet mode
+ mEntry.isHomeApp = false;
+ mEntry.hasLauncherEntry = true;
+ mEntry.hideInQuietMode = true;
+ assertThat(ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER_NOT_QUIET.filterApp(mEntry))
+ .isFalse();
+ }
+
+ @Test
public void testOtherAppsRejectsLegacyGame() {
mEntry.info.flags = ApplicationInfo.FLAG_IS_GAME;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 5996dbb..646e9eb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -15,6 +15,8 @@
*/
package com.android.settingslib.bluetooth;
+import static com.android.settingslib.flags.Flags.FLAG_ENABLE_SET_PREFERRED_TRANSPORT_FOR_LE_AUDIO_DEVICE;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -86,6 +88,9 @@
private HapClientProfile mHapClientProfile;
@Mock
private LeAudioProfile mLeAudioProfile;
+
+ @Mock
+ private HidProfile mHidProfile;
@Mock
private BluetoothDevice mDevice;
@Mock
@@ -104,6 +109,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TV_MEDIA_OUTPUT_DIALOG);
+ mSetFlagsRule.enableFlags(FLAG_ENABLE_SET_PREFERRED_TRANSPORT_FOR_LE_AUDIO_DEVICE);
mContext = RuntimeEnvironment.application;
mAudioManager = mContext.getSystemService(AudioManager.class);
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
@@ -118,6 +124,8 @@
when(mHearingAidProfile.getProfileId()).thenReturn(BluetoothProfile.HEARING_AID);
when(mLeAudioProfile.isProfileReady()).thenReturn(true);
when(mLeAudioProfile.getProfileId()).thenReturn(BluetoothProfile.LE_AUDIO);
+ when(mHidProfile.isProfileReady()).thenReturn(true);
+ when(mHidProfile.getProfileId()).thenReturn(BluetoothProfile.HID_HOST);
mCachedDevice = spy(new CachedBluetoothDevice(mContext, mProfileManager, mDevice));
mSubCachedDevice = spy(new CachedBluetoothDevice(mContext, mProfileManager, mSubDevice));
doAnswer((invocation) -> mBatteryLevel).when(mCachedDevice).getBatteryLevel();
@@ -1819,6 +1827,32 @@
assertThat(mCachedDevice.isConnectedHearingAidDevice()).isFalse();
}
+ @Test
+ public void leAudioHidDevice_leAudioEnabled_setPreferredTransportToLE() {
+
+ when(mProfileManager.getHidProfile()).thenReturn(mHidProfile);
+ when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
+ when(mLeAudioProfile.isEnabled(mDevice)).thenReturn(true);
+
+ updateProfileStatus(mHidProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mLeAudioProfile, BluetoothProfile.STATE_CONNECTED);
+
+ verify(mHidProfile).setPreferredTransport(mDevice, BluetoothDevice.TRANSPORT_LE);
+ }
+
+ @Test
+ public void leAudioHidDevice_leAudioDisabled_setPreferredTransportToBredr() {
+ when(mProfileManager.getHidProfile()).thenReturn(mHidProfile);
+ when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
+ when(mLeAudioProfile.isEnabled(mDevice)).thenReturn(false);
+
+ updateProfileStatus(mLeAudioProfile, BluetoothProfile.STATE_CONNECTED);
+ updateProfileStatus(mLeAudioProfile, BluetoothProfile.STATE_DISCONNECTED);
+ updateProfileStatus(mHidProfile, BluetoothProfile.STATE_CONNECTED);
+
+ verify(mHidProfile).setPreferredTransport(mDevice, BluetoothDevice.TRANSPORT_BREDR);
+ }
+
private HearingAidInfo getLeftAshaHearingAidInfo() {
return new HearingAidInfo.Builder()
.setAshaDeviceSide(HearingAidProfile.DeviceSide.SIDE_LEFT)
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt
index 4d327e1..6c982a0 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt
@@ -41,9 +41,9 @@
maxMarginXdp: Float,
maxMarginYdp: Float,
minScale: Float,
- translateXEasing: Interpolator = Interpolators.STANDARD_DECELERATE,
+ translateXEasing: Interpolator = Interpolators.BACK_GESTURE,
translateYEasing: Interpolator = Interpolators.LINEAR,
- scaleEasing: Interpolator = Interpolators.STANDARD_DECELERATE,
+ scaleEasing: Interpolator = Interpolators.BACK_GESTURE,
): BackAnimationSpec {
return BackAnimationSpec { backEvent, progressY, result ->
val displayMetrics = displayMetricsProvider()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
index 0f3d3dc2..d55d4e4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -160,7 +160,9 @@
FoldAware(
modifier =
modifier.padding(
+ start = 32.dp,
top = 92.dp,
+ end = 32.dp,
bottom = 48.dp,
),
viewModel = viewModel,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
index a78c2c0..07c2d3c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
@@ -432,12 +432,12 @@
}
}
-private const val DOT_DIAMETER_DP = 16
-private const val SELECTED_DOT_DIAMETER_DP = 24
+private const val DOT_DIAMETER_DP = 14
+private const val SELECTED_DOT_DIAMETER_DP = (DOT_DIAMETER_DP * 1.5).toInt()
private const val SELECTED_DOT_REACTION_ANIMATION_DURATION_MS = 83
private const val SELECTED_DOT_RETRACT_ANIMATION_DURATION_MS = 750
-private const val LINE_STROKE_WIDTH_DP = 16
-private const val FAILURE_ANIMATION_DOT_DIAMETER_DP = 13
+private const val LINE_STROKE_WIDTH_DP = DOT_DIAMETER_DP
+private const val FAILURE_ANIMATION_DOT_DIAMETER_DP = (DOT_DIAMETER_DP * 0.81f).toInt()
private const val FAILURE_ANIMATION_DOT_SHRINK_ANIMATION_DURATION_MS = 50
private const val FAILURE_ANIMATION_DOT_SHRINK_STAGGER_DELAY_MS = 33
private const val FAILURE_ANIMATION_DOT_REVERT_ANIMATION_DURATION = 617
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
index 55f7f69a..52cbffb 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
@@ -19,8 +19,6 @@
import com.android.systemui.keyguard.ui.composable.blueprint.CommunalBlueprintModule
import com.android.systemui.keyguard.ui.composable.blueprint.DefaultBlueprintModule
import com.android.systemui.keyguard.ui.composable.blueprint.ShortcutsBesideUdfpsBlueprintModule
-import com.android.systemui.keyguard.ui.composable.blueprint.SplitShadeWeatherClockBlueprintModule
-import com.android.systemui.keyguard.ui.composable.blueprint.WeatherClockBlueprintModule
import com.android.systemui.keyguard.ui.composable.section.OptionalSectionModule
import dagger.Module
@@ -31,8 +29,6 @@
DefaultBlueprintModule::class,
OptionalSectionModule::class,
ShortcutsBesideUdfpsBlueprintModule::class,
- SplitShadeWeatherClockBlueprintModule::class,
- WeatherClockBlueprintModule::class,
],
)
interface LockscreenSceneBlueprintModule
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt
index acd9e3d..c6fe81a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt
@@ -25,6 +25,9 @@
import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.largeClockElementKey
import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.smallClockElementKey
import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.smartspaceElementKey
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockTransition.transitioningToLargeClock
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockTransition.transitioningToSmallClock
+import com.android.systemui.keyguard.ui.composable.blueprint.WeatherClockElementKeys.largeWeatherClockElementKeyList
import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.ClockFaceInTransition.Companion.CLOCK_IN_MILLIS
import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.ClockFaceInTransition.Companion.CLOCK_IN_START_DELAY_MILLIS
import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.ClockFaceOutTransition.Companion.CLOCK_OUT_MILLIS
@@ -34,30 +37,45 @@
object ClockTransition {
val defaultClockTransitions = transitions {
from(ClockScenes.smallClockScene, to = ClockScenes.largeClockScene) {
- transitioningToLargeClock()
+ transitioningToLargeClock(largeClockElements = listOf(largeClockElementKey))
}
from(ClockScenes.largeClockScene, to = ClockScenes.smallClockScene) {
- transitioningToSmallClock()
+ transitioningToSmallClock(largeClockElements = listOf(largeClockElementKey))
}
from(ClockScenes.splitShadeLargeClockScene, to = ClockScenes.largeClockScene) {
- spec = tween(1000, easing = LinearEasing)
+ spec = tween(300, easing = LinearEasing)
+ }
+
+ from(WeatherClockScenes.largeClockScene, to = ClockScenes.smallClockScene) {
+ transitioningToSmallClock(largeClockElements = largeWeatherClockElementKeyList)
+ }
+
+ from(ClockScenes.smallClockScene, to = WeatherClockScenes.largeClockScene) {
+ transitioningToLargeClock(largeClockElements = largeWeatherClockElementKeyList)
+ }
+
+ from(
+ WeatherClockScenes.largeClockScene,
+ to = WeatherClockScenes.splitShadeLargeClockScene
+ ) {
+ spec = tween(300, easing = LinearEasing)
}
}
- private fun TransitionBuilder.transitioningToLargeClock() {
+ private fun TransitionBuilder.transitioningToLargeClock(largeClockElements: List<ElementKey>) {
spec = tween(durationMillis = STATUS_AREA_MOVE_UP_MILLIS.toInt())
timestampRange(
startMillis = CLOCK_IN_START_DELAY_MILLIS.toInt(),
endMillis = (CLOCK_IN_START_DELAY_MILLIS + CLOCK_IN_MILLIS).toInt()
) {
- fade(largeClockElementKey)
+ largeClockElements.forEach { fade(it) }
}
timestampRange(endMillis = CLOCK_OUT_MILLIS.toInt()) { fade(smallClockElementKey) }
anchoredTranslate(smallClockElementKey, smartspaceElementKey)
}
- private fun TransitionBuilder.transitioningToSmallClock() {
+ private fun TransitionBuilder.transitioningToSmallClock(largeClockElements: List<ElementKey>) {
spec = tween(durationMillis = STATUS_AREA_MOVE_DOWN_MILLIS.toInt())
timestampRange(
startMillis = CLOCK_IN_START_DELAY_MILLIS.toInt(),
@@ -66,7 +84,9 @@
fade(smallClockElementKey)
}
- timestampRange(endMillis = CLOCK_OUT_MILLIS.toInt()) { fade(largeClockElementKey) }
+ timestampRange(endMillis = CLOCK_OUT_MILLIS.toInt()) {
+ largeClockElements.forEach { fade(it) }
+ }
anchoredTranslate(smallClockElementKey, smartspaceElementKey)
}
}
@@ -81,14 +101,26 @@
object ClockElementKeys {
val largeClockElementKey = ElementKey("large-clock")
val smallClockElementKey = ElementKey("small-clock")
- val weatherSmallClockElementKey = ElementKey("weather-small-clock")
val smartspaceElementKey = ElementKey("smart-space")
}
+object WeatherClockScenes {
+ val largeClockScene = SceneKey("large-weather-clock-scene")
+ val splitShadeLargeClockScene = SceneKey("split-shade-large-weather-clock-scene")
+}
+
object WeatherClockElementKeys {
val timeElementKey = ElementKey("weather-large-clock-time")
val dateElementKey = ElementKey("weather-large-clock-date")
val weatherIconElementKey = ElementKey("weather-large-clock-weather-icon")
val temperatureElementKey = ElementKey("weather-large-clock-temperature")
val dndAlarmElementKey = ElementKey("weather-large-clock-dnd-alarm")
+ val largeWeatherClockElementKeyList =
+ listOf(
+ timeElementKey,
+ dateElementKey,
+ weatherIconElementKey,
+ temperatureElementKey,
+ dndAlarmElementKey
+ )
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
deleted file mode 100644
index cba5453..0000000
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
+++ /dev/null
@@ -1,499 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.ui.composable.blueprint
-
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.IntRect
-import androidx.compose.ui.unit.dp
-import com.android.compose.animation.scene.SceneScope
-import com.android.compose.modifiers.padding
-import com.android.keyguard.KeyguardClockSwitch.LARGE
-import com.android.systemui.Flags
-import com.android.systemui.customization.R as customizationR
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.WEATHER_CLOCK_BLUEPRINT_ID
-import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
-import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
-import com.android.systemui.keyguard.ui.composable.modifier.onTopPlacementChanged
-import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
-import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
-import com.android.systemui.keyguard.ui.composable.section.LockSection
-import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
-import com.android.systemui.keyguard.ui.composable.section.NotificationSection
-import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
-import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
-import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
-import com.android.systemui.keyguard.ui.composable.section.WeatherClockSection
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import com.android.systemui.res.R
-import com.android.systemui.shade.LargeScreenHeaderHelper
-import dagger.Binds
-import dagger.Module
-import dagger.multibindings.IntoSet
-import java.util.Optional
-import javax.inject.Inject
-
-class WeatherClockBlueprint
-@Inject
-constructor(
- private val viewModel: LockscreenContentViewModel,
- private val statusBarSection: StatusBarSection,
- private val weatherClockSection: WeatherClockSection,
- private val smartSpaceSection: SmartSpaceSection,
- private val notificationSection: NotificationSection,
- private val lockSection: LockSection,
- private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
- private val bottomAreaSection: BottomAreaSection,
- private val settingsMenuSection: SettingsMenuSection,
- private val clockInteractor: KeyguardClockInteractor,
- private val mediaCarouselSection: MediaCarouselSection,
- private val clockViewModel: KeyguardClockViewModel,
-) : ComposableLockscreenSceneBlueprint {
-
- override val id: String = WEATHER_CLOCK_BLUEPRINT_ID
- @Composable
- override fun SceneScope.Content(modifier: Modifier) {
- val isUdfpsVisible = viewModel.isUdfpsVisible
- val burnIn = rememberBurnIn(clockInteractor)
- val resources = LocalContext.current.resources
- val currentClockState = clockViewModel.currentClock.collectAsState()
- val areNotificationsVisible by viewModel.areNotificationsVisible.collectAsState()
- LockscreenLongPress(
- viewModel = viewModel.longPress,
- modifier = modifier,
- ) { onSettingsMenuPlaced ->
- Layout(
- content = {
- // Constrained to above the lock icon.
- Column(
- modifier = Modifier.fillMaxWidth(),
- ) {
- with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
- val currentClock = currentClockState.value
- val clockSize by clockViewModel.clockSize.collectAsState()
- with(weatherClockSection) {
- if (currentClock == null) {
- return@with
- }
-
- if (clockSize == LARGE) {
- Time(
- clock = currentClock,
- modifier =
- Modifier.padding(
- start =
- dimensionResource(
- customizationR.dimen.clock_padding_start
- )
- )
- )
- } else {
- SmallClock(
- burnInParams = burnIn.parameters,
- modifier =
- Modifier.align(Alignment.Start)
- .onTopPlacementChanged(burnIn.onSmallClockTopChanged),
- clock = currentClock
- )
- }
- }
- with(smartSpaceSection) {
- SmartSpace(
- burnInParams = burnIn.parameters,
- onTopChanged = burnIn.onSmartspaceTopChanged,
- modifier =
- Modifier.fillMaxWidth()
- .padding(
- top = { viewModel.getSmartSpacePaddingTop(resources) },
- )
- .padding(
- bottom =
- dimensionResource(
- R.dimen.keyguard_status_view_bottom_margin
- ),
- ),
- )
- }
-
- with(mediaCarouselSection) { MediaCarousel() }
-
- if (areNotificationsVisible) {
- with(notificationSection) {
- Notifications(
- burnInParams = burnIn.parameters,
- modifier = Modifier.fillMaxWidth().weight(weight = 1f)
- )
- }
- }
- with(weatherClockSection) {
- if (currentClock == null || clockSize != LARGE) {
- return@with
- }
- LargeClockSectionBelowSmartspace(clock = currentClock)
- }
-
- if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
- with(ambientIndicationSectionOptional.get()) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
- }
-
- with(lockSection) { LockIcon() }
-
- // Aligned to bottom and constrained to below the lock icon.
- Column(modifier = Modifier.fillMaxWidth()) {
- if (isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
- with(ambientIndicationSectionOptional.get()) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
-
- with(bottomAreaSection) {
- IndicationArea(modifier = Modifier.fillMaxWidth())
- }
- }
-
- // Aligned to bottom and NOT constrained by the lock icon.
- with(bottomAreaSection) {
- Shortcut(isStart = true, applyPadding = true)
- Shortcut(isStart = false, applyPadding = true)
- }
- with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) }
- },
- modifier = Modifier.fillMaxSize(),
- ) { measurables, constraints ->
- check(measurables.size == 6)
- val aboveLockIconMeasurable = measurables[0]
- val lockIconMeasurable = measurables[1]
- val belowLockIconMeasurable = measurables[2]
- val startShortcutMeasurable = measurables[3]
- val endShortcutMeasurable = measurables[4]
- val settingsMenuMeasurable = measurables[5]
-
- val noMinConstraints =
- constraints.copy(
- minWidth = 0,
- minHeight = 0,
- )
- val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
- val lockIconBounds =
- IntRect(
- left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
- top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
- right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
- bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
- )
-
- val aboveLockIconPlaceable =
- aboveLockIconMeasurable.measure(
- noMinConstraints.copy(maxHeight = lockIconBounds.top)
- )
- val belowLockIconPlaceable =
- belowLockIconMeasurable.measure(
- noMinConstraints.copy(
- maxHeight =
- (constraints.maxHeight - lockIconBounds.bottom).coerceAtLeast(0)
- )
- )
- val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints)
- val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints)
- val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints)
-
- layout(constraints.maxWidth, constraints.maxHeight) {
- aboveLockIconPlaceable.place(
- x = 0,
- y = 0,
- )
- lockIconPlaceable.place(
- x = lockIconBounds.left,
- y = lockIconBounds.top,
- )
- belowLockIconPlaceable.place(
- x = 0,
- y = constraints.maxHeight - belowLockIconPlaceable.height,
- )
- startShortcutPleaceable.place(
- x = 0,
- y = constraints.maxHeight - startShortcutPleaceable.height,
- )
- endShortcutPleaceable.place(
- x = constraints.maxWidth - endShortcutPleaceable.width,
- y = constraints.maxHeight - endShortcutPleaceable.height,
- )
- settingsMenuPlaceable.place(
- x = (constraints.maxWidth - settingsMenuPlaceable.width) / 2,
- y = constraints.maxHeight - settingsMenuPlaceable.height,
- )
- }
- }
- }
- }
-}
-
-class SplitShadeWeatherClockBlueprint
-@Inject
-constructor(
- private val viewModel: LockscreenContentViewModel,
- private val statusBarSection: StatusBarSection,
- private val smartSpaceSection: SmartSpaceSection,
- private val notificationSection: NotificationSection,
- private val lockSection: LockSection,
- private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
- private val bottomAreaSection: BottomAreaSection,
- private val settingsMenuSection: SettingsMenuSection,
- private val clockInteractor: KeyguardClockInteractor,
- private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
- private val weatherClockSection: WeatherClockSection,
- private val mediaCarouselSection: MediaCarouselSection,
- private val clockViewModel: KeyguardClockViewModel,
-) : ComposableLockscreenSceneBlueprint {
- override val id: String = SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
-
- @Composable
- override fun SceneScope.Content(modifier: Modifier) {
- val isUdfpsVisible = viewModel.isUdfpsVisible
- val burnIn = rememberBurnIn(clockInteractor)
- val resources = LocalContext.current.resources
- val currentClockState = clockViewModel.currentClock.collectAsState()
- LockscreenLongPress(
- viewModel = viewModel.longPress,
- modifier = modifier,
- ) { onSettingsMenuPlaced ->
- Layout(
- content = {
- // Constrained to above the lock icon.
- Column(
- modifier = Modifier.fillMaxSize(),
- ) {
- with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
- Row(
- modifier = Modifier.fillMaxSize(),
- ) {
- Column(
- modifier = Modifier.fillMaxHeight().weight(weight = 1f),
- horizontalAlignment = Alignment.CenterHorizontally,
- ) {
- val currentClock = currentClockState.value
- val clockSize by clockViewModel.clockSize.collectAsState()
- with(weatherClockSection) {
- if (currentClock == null) {
- return@with
- }
-
- if (clockSize == LARGE) {
- Time(
- clock = currentClock,
- modifier =
- Modifier.align(Alignment.Start)
- .padding(
- start =
- dimensionResource(
- customizationR.dimen
- .clock_padding_start
- )
- )
- )
- } else {
- SmallClock(
- burnInParams = burnIn.parameters,
- modifier =
- Modifier.align(Alignment.Start)
- .onTopPlacementChanged(
- burnIn.onSmallClockTopChanged
- ),
- clock = currentClock,
- )
- }
- }
- with(smartSpaceSection) {
- SmartSpace(
- burnInParams = burnIn.parameters,
- onTopChanged = burnIn.onSmartspaceTopChanged,
- modifier =
- Modifier.fillMaxWidth()
- .padding(
- top = {
- viewModel.getSmartSpacePaddingTop(resources)
- },
- )
- .padding(
- bottom =
- dimensionResource(
- R.dimen
- .keyguard_status_view_bottom_margin
- )
- ),
- )
- }
-
- with(mediaCarouselSection) { MediaCarousel() }
-
- with(weatherClockSection) {
- if (currentClock == null || clockSize != LARGE) {
- return@with
- }
-
- LargeClockSectionBelowSmartspace(currentClock)
- }
- }
- with(notificationSection) {
- val splitShadeTopMargin: Dp =
- if (Flags.centralizedStatusBarHeightFix()) {
- largeScreenHeaderHelper.getLargeScreenHeaderHeight().dp
- } else {
- dimensionResource(
- id = R.dimen.large_screen_shade_header_height
- )
- }
- Notifications(
- burnInParams = burnIn.parameters,
- modifier =
- Modifier.fillMaxHeight()
- .weight(weight = 1f)
- .padding(top = splitShadeTopMargin)
- )
- }
- }
-
- if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
- with(ambientIndicationSectionOptional.get()) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
- }
-
- with(lockSection) { LockIcon() }
-
- // Aligned to bottom and constrained to below the lock icon.
- Column(modifier = Modifier.fillMaxWidth()) {
- if (isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
- with(ambientIndicationSectionOptional.get()) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
-
- with(bottomAreaSection) {
- IndicationArea(modifier = Modifier.fillMaxWidth())
- }
- }
-
- // Aligned to bottom and NOT constrained by the lock icon.
- with(bottomAreaSection) {
- Shortcut(isStart = true, applyPadding = true)
- Shortcut(isStart = false, applyPadding = true)
- }
- with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) }
- },
- modifier = Modifier.fillMaxSize(),
- ) { measurables, constraints ->
- check(measurables.size == 6)
- val aboveLockIconMeasurable = measurables[0]
- val lockIconMeasurable = measurables[1]
- val belowLockIconMeasurable = measurables[2]
- val startShortcutMeasurable = measurables[3]
- val endShortcutMeasurable = measurables[4]
- val settingsMenuMeasurable = measurables[5]
-
- val noMinConstraints =
- constraints.copy(
- minWidth = 0,
- minHeight = 0,
- )
- val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
- val lockIconBounds =
- IntRect(
- left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
- top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
- right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
- bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
- )
-
- val aboveLockIconPlaceable =
- aboveLockIconMeasurable.measure(
- noMinConstraints.copy(maxHeight = lockIconBounds.top)
- )
- val belowLockIconPlaceable =
- belowLockIconMeasurable.measure(
- noMinConstraints.copy(
- maxHeight =
- (constraints.maxHeight - lockIconBounds.bottom).coerceAtLeast(0)
- )
- )
- val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints)
- val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints)
- val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints)
-
- layout(constraints.maxWidth, constraints.maxHeight) {
- aboveLockIconPlaceable.place(
- x = 0,
- y = 0,
- )
- lockIconPlaceable.place(
- x = lockIconBounds.left,
- y = lockIconBounds.top,
- )
- belowLockIconPlaceable.place(
- x = 0,
- y = constraints.maxHeight - belowLockIconPlaceable.height,
- )
- startShortcutPleaceable.place(
- x = 0,
- y = constraints.maxHeight - startShortcutPleaceable.height,
- )
- endShortcutPleaceable.place(
- x = constraints.maxWidth - endShortcutPleaceable.width,
- y = constraints.maxHeight - endShortcutPleaceable.height,
- )
- settingsMenuPlaceable.place(
- x = (constraints.maxWidth - settingsMenuPlaceable.width) / 2,
- y = constraints.maxHeight - settingsMenuPlaceable.height,
- )
- }
- }
- }
- }
-}
-
-@Module
-interface WeatherClockBlueprintModule {
- @Binds
- @IntoSet
- fun blueprint(blueprint: WeatherClockBlueprint): ComposableLockscreenSceneBlueprint
-}
-
-@Module
-interface SplitShadeWeatherClockBlueprintModule {
- @Binds
- @IntoSet
- fun blueprint(blueprint: SplitShadeWeatherClockBlueprint): ComposableLockscreenSceneBlueprint
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
index 467dbca..97d5b41 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
@@ -32,7 +32,6 @@
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.animation.view.LaunchableImageView
-import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
@@ -44,7 +43,6 @@
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.flow.Flow
@@ -56,7 +54,6 @@
private val vibratorHelper: VibratorHelper,
private val indicationController: KeyguardIndicationController,
private val indicationAreaViewModel: KeyguardIndicationAreaViewModel,
- @Main private val mainImmediateDispatcher: CoroutineDispatcher,
) {
/**
* Renders a single lockscreen shortcut.
@@ -164,7 +161,6 @@
transitionAlpha,
falsingManager,
vibratorHelper,
- mainImmediateDispatcher,
) {
indicationController.showTransientIndication(it)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
index 48684a0..9f02201 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
@@ -34,7 +34,6 @@
import com.android.keyguard.LockIconViewController
import com.android.systemui.biometrics.AuthController
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
@@ -51,14 +50,12 @@
import com.android.systemui.statusbar.VibratorHelper
import dagger.Lazy
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
class LockSection
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
- @Main private val mainImmediateDispatcher: CoroutineDispatcher,
private val windowManager: WindowManager,
private val authController: AuthController,
private val featureFlags: FeatureFlagsClassic,
@@ -96,7 +93,6 @@
deviceEntryBackgroundViewModel.get(),
falsingManager.get(),
vibratorHelper.get(),
- mainImmediateDispatcher,
)
}
} else {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
index f8e6341..0934b20 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
@@ -16,9 +16,11 @@
package com.android.systemui.keyguard.ui.composable.section
+import android.content.Context
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable
@@ -26,6 +28,10 @@
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.SceneTransitionLayout
@@ -36,6 +42,7 @@
import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.splitShadeLargeClockScene
import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.splitShadeSmallClockScene
import com.android.systemui.keyguard.ui.composable.blueprint.ClockTransition
+import com.android.systemui.keyguard.ui.composable.blueprint.WeatherClockScenes
import com.android.systemui.keyguard.ui.composable.blueprint.rememberBurnIn
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import javax.inject.Inject
@@ -47,6 +54,7 @@
private val smartSpaceSection: SmartSpaceSection,
private val mediaCarouselSection: MediaCarouselSection,
private val clockSection: DefaultClockSection,
+ private val weatherClockSection: WeatherClockSection,
private val clockInteractor: KeyguardClockInteractor,
) {
@Composable
@@ -64,6 +72,10 @@
splitShadeSmallClockScene
KeyguardClockViewModel.ClockLayout.LARGE_CLOCK -> largeClockScene
KeyguardClockViewModel.ClockLayout.SMALL_CLOCK -> smallClockScene
+ KeyguardClockViewModel.ClockLayout.WEATHER_LARGE_CLOCK ->
+ WeatherClockScenes.largeClockScene
+ KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_WEATHER_LARGE_CLOCK ->
+ WeatherClockScenes.splitShadeLargeClockScene
}
SceneTransitionLayout(
@@ -86,6 +98,12 @@
scene(smallClockScene) { SmallClockWithSmartSpace() }
scene(largeClockScene) { LargeClockWithSmartSpace() }
+
+ scene(WeatherClockScenes.largeClockScene) { WeatherLargeClockWithSmartSpace() }
+
+ scene(WeatherClockScenes.splitShadeLargeClockScene) {
+ WeatherLargeClockWithSmartSpace(modifier = Modifier.fillMaxWidth(0.5f))
+ }
}
}
@@ -146,4 +164,50 @@
}
}
}
+
+ @Composable
+ private fun SceneScope.WeatherLargeClockWithSmartSpace(modifier: Modifier = Modifier) {
+ val burnIn = rememberBurnIn(clockInteractor)
+ val isLargeClockVisible by clockViewModel.isLargeClockVisible.collectAsState()
+ val currentClockState = clockViewModel.currentClock.collectAsState()
+
+ LaunchedEffect(isLargeClockVisible) {
+ if (isLargeClockVisible) {
+ burnIn.onSmallClockTopChanged(null)
+ }
+ }
+
+ Column(modifier = modifier) {
+ val currentClock = currentClockState.value ?: return@Column
+ with(weatherClockSection) { Time(clock = currentClock, modifier = Modifier) }
+ val density = LocalDensity.current
+ val context = LocalContext.current
+
+ with(smartSpaceSection) {
+ SmartSpace(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmartspaceTopChanged,
+ modifier =
+ Modifier.heightIn(
+ min = getDimen(context, "enhanced_smartspace_height", density)
+ )
+ )
+ }
+ with(weatherClockSection) { LargeClockSectionBelowSmartspace(clock = currentClock) }
+ }
+ }
+
+ /*
+ * Use this function to access dimen which cannot be access by R.dimen directly
+ * Currently use to access dimen from BcSmartspace
+ * @param name Name of resources
+ * @param density Density required to convert dimen from Int To Dp
+ */
+ private fun getDimen(context: Context, name: String, density: Density): Dp {
+ val res = context.packageManager.getResourcesForApplication(context.packageName)
+ val id = res.getIdentifier(name, "dimen", context.packageName)
+ var dimen: Dp
+ with(density) { dimen = (if (id == 0) 0 else res.getDimensionPixelSize(id)).toDp() }
+ return dimen
+ }
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
index d358453..a7bb308ad 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.composable.section
+import android.view.View
import android.view.ViewGroup
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.IntrinsicSize
@@ -27,18 +28,14 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
import com.android.compose.modifiers.padding
-import com.android.systemui.customization.R
-import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.weatherSmallClockElementKey
+import com.android.systemui.customization.R as customizationR
import com.android.systemui.keyguard.ui.composable.blueprint.WeatherClockElementKeys
-import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
-import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.plugins.clocks.ClockController
import javax.inject.Inject
@@ -55,12 +52,19 @@
clock: ClockController,
modifier: Modifier = Modifier,
) {
- WeatherElement(
- weatherClockElementViewId = R.id.weather_clock_time,
- clock = clock,
- elementKey = WeatherClockElementKeys.timeElementKey,
- modifier = modifier.wrapContentSize(),
- )
+ Row(
+ modifier =
+ Modifier.padding(
+ horizontal = dimensionResource(customizationR.dimen.clock_padding_start)
+ )
+ ) {
+ WeatherElement(
+ weatherClockElementViewId = customizationR.id.weather_clock_time,
+ clock = clock,
+ elementKey = WeatherClockElementKeys.timeElementKey,
+ modifier = modifier,
+ )
+ }
}
@Composable
@@ -69,7 +73,7 @@
modifier: Modifier = Modifier,
) {
WeatherElement(
- weatherClockElementViewId = R.id.weather_clock_date,
+ weatherClockElementViewId = customizationR.id.weather_clock_date,
clock = clock,
elementKey = WeatherClockElementKeys.dateElementKey,
modifier = modifier,
@@ -82,7 +86,7 @@
modifier: Modifier = Modifier,
) {
WeatherElement(
- weatherClockElementViewId = R.id.weather_clock_weather_icon,
+ weatherClockElementViewId = customizationR.id.weather_clock_weather_icon,
clock = clock,
elementKey = WeatherClockElementKeys.weatherIconElementKey,
modifier = modifier.wrapContentSize(),
@@ -95,7 +99,7 @@
modifier: Modifier = Modifier,
) {
WeatherElement(
- weatherClockElementViewId = R.id.weather_clock_alarm_dnd,
+ weatherClockElementViewId = customizationR.id.weather_clock_alarm_dnd,
clock = clock,
elementKey = WeatherClockElementKeys.dndAlarmElementKey,
modifier = modifier.wrapContentSize(),
@@ -108,7 +112,7 @@
modifier: Modifier = Modifier,
) {
WeatherElement(
- weatherClockElementViewId = R.id.weather_clock_temperature,
+ weatherClockElementViewId = customizationR.id.weather_clock_temperature,
clock = clock,
elementKey = WeatherClockElementKeys.temperatureElementKey,
modifier = modifier.wrapContentSize(),
@@ -126,12 +130,16 @@
content {
AndroidView(
factory = {
- val view =
- clock.largeClock.layout.views.first {
- it.id == weatherClockElementViewId
- }
- (view.parent as? ViewGroup)?.removeView(view)
- view
+ try {
+ val view =
+ clock.largeClock.layout.views.first {
+ it.id == weatherClockElementViewId
+ }
+ (view.parent as? ViewGroup)?.removeView(view)
+ view
+ } catch (e: NoSuchElementException) {
+ View(it)
+ }
},
update = {},
modifier = modifier
@@ -147,46 +155,22 @@
Row(
modifier =
Modifier.height(IntrinsicSize.Max)
- .padding(horizontal = dimensionResource(R.dimen.clock_padding_start))
+ .padding(
+ horizontal = dimensionResource(customizationR.dimen.clock_padding_start)
+ )
) {
Date(clock = clock, modifier = Modifier.wrapContentSize())
- Box(modifier = Modifier.fillMaxSize()) {
+ Box(
+ modifier =
+ Modifier.fillMaxSize()
+ .padding(
+ start = dimensionResource(customizationR.dimen.clock_padding_start)
+ )
+ ) {
Weather(clock = clock, modifier = Modifier.align(Alignment.TopStart))
Temperature(clock = clock, modifier = Modifier.align(Alignment.BottomEnd))
DndAlarmStatus(clock = clock, modifier = Modifier.align(Alignment.TopEnd))
}
}
}
-
- @Composable
- fun SceneScope.SmallClock(
- burnInParams: BurnInParameters,
- modifier: Modifier = Modifier,
- clock: ClockController,
- ) {
- val localContext = LocalContext.current
- MovableElement(key = weatherSmallClockElementKey, modifier) {
- content {
- AndroidView(
- factory = {
- val view = clock.smallClock.view
- if (view.parent != null) {
- (view.parent as? ViewGroup)?.removeView(view)
- }
- view
- },
- modifier =
- modifier
- .height(dimensionResource(R.dimen.small_clock_height))
- .padding(start = dimensionResource(R.dimen.clock_padding_start))
- .padding(top = { viewModel.getSmallClockTopMargin(localContext) })
- .burnInAware(
- viewModel = aodBurnInViewModel,
- params = burnInParams,
- ),
- update = {},
- )
- }
- }
- }
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index b1cfdcf..dbec059 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -204,15 +204,16 @@
}
// Handle back events.
- // TODO(b/290184746): Make sure that this works with SystemUI once we use
- // SceneTransitionLayout in Flexiglass.
- scene(state.transitionState.currentScene).userActions[Back]?.let { result ->
- // TODO(b/290184746): Handle predictive back and use result.distance if
- // specified.
- BackHandler {
- val targetScene = result.toScene
- if (state.canChangeScene(targetScene)) {
- with(state) { coroutineScope.onChangeScene(targetScene) }
+ val targetSceneForBackOrNull =
+ scene(state.transitionState.currentScene).userActions[Back]?.toScene
+ BackHandler(
+ enabled = targetSceneForBackOrNull != null,
+ ) {
+ targetSceneForBackOrNull?.let { targetSceneForBack ->
+ // TODO(b/290184746): Handle predictive back and use result.distance if
+ // specified.
+ if (state.canChangeScene(targetSceneForBack)) {
+ with(state) { coroutineScope.onChangeScene(targetSceneForBack) }
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 4950b96..85774c6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -537,4 +537,65 @@
assertThat(lp.height).isEqualTo(overlayParams.sensorBounds.height())
}
}
+
+ @Test
+ fun addViewPending_layoutIsNotUpdated() =
+ testScope.runTest {
+ withReasonSuspend(REASON_AUTH_KEYGUARD) {
+ mSetFlagsRule.enableFlags(Flags.FLAG_UDFPS_VIEW_PERFORMANCE)
+ mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+
+ // GIVEN going to sleep
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.OFF,
+ to = KeyguardState.GONE,
+ testScope = this,
+ )
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.STARTING_TO_SLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ runCurrent()
+
+ // WHEN a request comes to show the view
+ controllerOverlay.show(udfpsController, overlayParams)
+ runCurrent()
+
+ // THEN the view does not get added immediately
+ verify(windowManager, never()).addView(any(), any())
+
+ // WHEN updateOverlayParams gets called when the view is pending to be added
+ controllerOverlay.updateOverlayParams(overlayParams)
+
+ // THEN the view layout is never updated
+ verify(windowManager, never()).updateViewLayout(any(), any())
+
+ // CLEANUPL we hide to end the job that listens for the finishedGoingToSleep signal
+ controllerOverlay.hide()
+ }
+ }
+
+ @Test
+ fun updateOverlayParams_viewLayoutUpdated() =
+ testScope.runTest {
+ withReasonSuspend(REASON_AUTH_KEYGUARD) {
+ mSetFlagsRule.enableFlags(Flags.FLAG_UDFPS_VIEW_PERFORMANCE)
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.AWAKE,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ runCurrent()
+ controllerOverlay.show(udfpsController, overlayParams)
+ runCurrent()
+ verify(windowManager).addView(any(), any())
+
+ // WHEN updateOverlayParams gets called
+ controllerOverlay.updateOverlayParams(overlayParams)
+
+ // THEN the view layout is updated
+ verify(windowManager, never()).updateViewLayout(any(), any())
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 1dd5d07..f78fbd1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -215,16 +215,14 @@
)
repository.setStatusBarState(StatusBarState.KEYGUARD)
- // User begins to swipe up
- shadeRepository.setLegacyShadeExpansion(0.99f)
+ shadeRepository.setLegacyShadeExpansion(1f)
// When not dismissable, no alpha value (null) should emit
repository.setKeyguardDismissible(false)
assertThat(dismissAlpha).isNull()
repository.setKeyguardDismissible(true)
- shadeRepository.setLegacyShadeExpansion(0.98f)
- assertThat(dismissAlpha).isGreaterThan(0.5f)
+ assertThat(dismissAlpha).isGreaterThan(0.95f)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 66f7e01..776f1a5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -34,6 +34,8 @@
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.data.repository.fakePowerRepository
+import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.shadeRepository
@@ -43,6 +45,7 @@
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
+import kotlin.math.pow
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.BeforeClass
@@ -57,22 +60,26 @@
class LockscreenSceneViewModelTest : SysuiTestCase() {
companion object {
+ private const val parameterCount = 6
+
@Parameters(
name =
"canSwipeToEnter={0}, downWithTwoPointers={1}, downFromEdge={2}," +
- " isSingleShade={3}, isCommunalAvailable={4}"
+ " isSingleShade={3}, isCommunalAvailable={4}, isShadeTouchable={5}"
)
@JvmStatic
fun combinations() = buildList {
- repeat(32) { combination ->
+ repeat(2f.pow(parameterCount).toInt()) { combination ->
add(
arrayOf(
- /* canSwipeToEnter= */ combination and 1 != 0,
- /* downWithTwoPointers= */ combination and 2 != 0,
- /* downFromEdge= */ combination and 4 != 0,
- /* isSingleShade= */ combination and 8 != 0,
- /* isCommunalAvailable= */ combination and 16 != 0,
- )
+ /* canSwipeToEnter= */ combination and 1 != 0,
+ /* downWithTwoPointers= */ combination and 2 != 0,
+ /* downFromEdge= */ combination and 4 != 0,
+ /* isSingleShade= */ combination and 8 != 0,
+ /* isCommunalAvailable= */ combination and 16 != 0,
+ /* isShadeTouchable= */ combination and 32 != 0,
+ )
+ .also { check(it.size == parameterCount) }
)
}
}
@@ -82,8 +89,15 @@
fun setUp() {
val combinationStrings =
combinations().map { array ->
- check(array.size == 5)
- "${array[4]},${array[3]},${array[2]},${array[1]},${array[0]}"
+ check(array.size == parameterCount)
+ buildString {
+ ((parameterCount - 1) downTo 0).forEach { index ->
+ append("${array[index]}")
+ if (index > 0) {
+ append(",")
+ }
+ }
+ }
}
val uniqueCombinations = combinationStrings.toSet()
assertThat(combinationStrings).hasSize(uniqueCombinations.size)
@@ -92,8 +106,35 @@
private fun expectedDownDestination(
downFromEdge: Boolean,
isSingleShade: Boolean,
- ): SceneKey {
- return if (downFromEdge && isSingleShade) Scenes.QuickSettings else Scenes.Shade
+ isShadeTouchable: Boolean,
+ ): SceneKey? {
+ return when {
+ !isShadeTouchable -> null
+ downFromEdge && isSingleShade -> Scenes.QuickSettings
+ else -> Scenes.Shade
+ }
+ }
+
+ private fun expectedUpDestination(
+ canSwipeToEnter: Boolean,
+ isShadeTouchable: Boolean,
+ ): SceneKey? {
+ return when {
+ !isShadeTouchable -> null
+ canSwipeToEnter -> Scenes.Gone
+ else -> Scenes.Bouncer
+ }
+ }
+
+ private fun expectedLeftDestination(
+ isCommunalAvailable: Boolean,
+ isShadeTouchable: Boolean,
+ ): SceneKey? {
+ return when {
+ !isShadeTouchable -> null
+ isCommunalAvailable -> Scenes.Communal
+ else -> null
+ }
}
}
@@ -106,6 +147,7 @@
@JvmField @Parameter(2) var downFromEdge: Boolean = false
@JvmField @Parameter(3) var isSingleShade: Boolean = true
@JvmField @Parameter(4) var isCommunalAvailable: Boolean = false
+ @JvmField @Parameter(5) var isShadeTouchable: Boolean = false
private val underTest by lazy { createLockscreenSceneViewModel() }
@@ -130,6 +172,14 @@
}
)
kosmos.setCommunalAvailable(isCommunalAvailable)
+ kosmos.fakePowerRepository.updateWakefulness(
+ rawState =
+ if (isShadeTouchable) {
+ WakefulnessState.AWAKE
+ } else {
+ WakefulnessState.ASLEEP
+ },
+ )
val destinationScenes by collectLastValue(underTest.destinationScenes)
@@ -148,14 +198,25 @@
expectedDownDestination(
downFromEdge = downFromEdge,
isSingleShade = isSingleShade,
+ isShadeTouchable = isShadeTouchable,
)
)
assertThat(destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene)
- .isEqualTo(if (canSwipeToEnter) Scenes.Gone else Scenes.Bouncer)
+ .isEqualTo(
+ expectedUpDestination(
+ canSwipeToEnter = canSwipeToEnter,
+ isShadeTouchable = isShadeTouchable,
+ )
+ )
assertThat(destinationScenes?.get(Swipe(SwipeDirection.Left))?.toScene)
- .isEqualTo(Scenes.Communal.takeIf { isCommunalAvailable })
+ .isEqualTo(
+ expectedLeftDestination(
+ isCommunalAvailable = isCommunalAvailable,
+ isShadeTouchable = isShadeTouchable,
+ )
+ )
}
private fun createLockscreenSceneViewModel(): LockscreenSceneViewModel {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 9856f90..93302e3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -293,6 +293,7 @@
occlusionInteractor = kosmos.sceneContainerOcclusionInteractor,
faceUnlockInteractor = kosmos.deviceEntryFaceAuthInteractor,
deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor,
+ shadeInteractor = kosmos.shadeInteractor,
)
startable.start()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
index ae31058..7f7c24e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
@@ -39,7 +39,6 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
-@android.platform.test.annotations.EnabledOnRavenwood
class SceneContainerRepositoryTest : SysuiTestCase() {
private val kosmos = testKosmos().apply { fakeSceneContainerFlags.enabled = true }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 3fd5306..75e66fb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -48,14 +48,17 @@
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.model.sysUiState
+import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
+import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shared.system.QuickStepContract
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
@@ -140,6 +143,7 @@
occlusionInteractor = kosmos.sceneContainerOcclusionInteractor,
faceUnlockInteractor = kosmos.deviceEntryFaceAuthInteractor,
deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor,
+ shadeInteractor = kosmos.shadeInteractor,
)
}
@@ -1127,6 +1131,33 @@
assertThat(kosmos.fakeDeviceEntryFaceAuthRepository.isAuthRunning.value).isTrue()
}
+ @Test
+ fun switchToLockscreen_whenShadeBecomesNotTouchable() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val isShadeTouchable by collectLastValue(kosmos.shadeInteractor.isShadeTouchable)
+ val transitionStateFlow = prepareState()
+ underTest.start()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ // Flung to bouncer, 90% of the way there:
+ transitionStateFlow.value =
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Lockscreen,
+ toScene = Scenes.Bouncer,
+ progress = flowOf(0.9f),
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+
+ kosmos.fakePowerRepository.updateWakefulness(WakefulnessState.ASLEEP)
+ runCurrent()
+ assertThat(isShadeTouchable).isFalse()
+
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ }
+
private fun TestScope.emulateSceneTransition(
transitionStateFlow: MutableStateFlow<ObservableTransitionState>,
toScene: SceneKey,
@@ -1166,6 +1197,7 @@
isLockscreenEnabled: Boolean = true,
startsAwake: Boolean = true,
isDeviceProvisioned: Boolean = true,
+ isInteractive: Boolean = true,
): MutableStateFlow<ObservableTransitionState> {
if (authenticationMethod?.isSecure == true) {
assert(isLockscreenEnabled) {
@@ -1205,6 +1237,7 @@
} else {
powerInteractor.setAsleepForTest()
}
+ kosmos.fakePowerRepository.setInteractive(isInteractive)
kosmos.fakeDeviceProvisioningRepository.setDeviceProvisioned(isDeviceProvisioned)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
similarity index 88%
rename from packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
index 543f6c9..e590bae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
@@ -22,6 +22,7 @@
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.kosmos.Kosmos
import com.google.common.truth.Truth
import org.junit.Test
import org.junit.runner.RunWith
@@ -35,6 +36,7 @@
fun isNotEnabled_withoutAconfigFlags() {
Truth.assertThat(SceneContainerFlag.isEnabled).isEqualTo(false)
Truth.assertThat(SceneContainerFlagsImpl().isEnabled()).isEqualTo(false)
+ Truth.assertThat(Kosmos().sceneContainerFlags.isEnabled()).isEqualTo(false)
}
@Test
@@ -42,5 +44,6 @@
fun isEnabled_withAconfigFlags() {
Truth.assertThat(SceneContainerFlag.isEnabled).isEqualTo(true)
Truth.assertThat(SceneContainerFlagsImpl().isEnabled()).isEqualTo(true)
+ Truth.assertThat(Kosmos().sceneContainerFlags.isEnabled()).isEqualTo(true)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index aef1d71..8f7a56d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -31,6 +31,7 @@
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.BrokenWithSceneContainer
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.andSceneContainer
@@ -50,6 +51,8 @@
import com.android.systemui.keyguard.ui.viewmodel.keyguardRootViewModel
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.flag.sceneContainerFlags
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.mockLargeScreenHeaderHelper
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
@@ -125,6 +128,7 @@
@Before
fun setUp() {
+ assertThat(kosmos.sceneContainerFlags.isEnabled()).isEqualTo(SceneContainerFlag.isEnabled)
overrideResource(R.bool.config_use_split_notification_shade, false)
movementFlow = MutableStateFlow(BurnInModel())
whenever(aodBurnInViewModel.movement(any())).thenReturn(movementFlow)
@@ -310,6 +314,7 @@
}
@Test
+ @BrokenWithSceneContainer(bugId = 333132830)
fun glanceableHubAlpha_lockscreenToHub() =
testScope.runTest {
val alpha by collectLastValue(underTest.glanceableHubAlpha)
@@ -459,6 +464,7 @@
}
@Test
+ @BrokenWithSceneContainer(bugId = 333132830)
fun isOnLockscreenWithoutShade() =
testScope.runTest {
val isOnLockscreenWithoutShade by collectLastValue(underTest.isOnLockscreenWithoutShade)
@@ -495,6 +501,7 @@
}
@Test
+ @BrokenWithSceneContainer(bugId = 333132830)
fun isOnGlanceableHubWithoutShade() =
testScope.runTest {
val isOnGlanceableHubWithoutShade by
@@ -676,6 +683,7 @@
}
@Test
+ @BrokenWithSceneContainer(bugId = 333132830)
fun maxNotificationsOnLockscreen_DoesNotUpdateWhenUserInteracting() =
testScope.runTest {
var notificationCount = 10
@@ -712,6 +720,7 @@
}
@Test
+ @BrokenWithSceneContainer(bugId = 333132830)
fun maxNotificationsOnShade() =
testScope.runTest {
val calculateSpace = { space: Float, useExtraShelfSpace: Boolean -> 10 }
@@ -798,6 +807,7 @@
}
@Test
+ @BrokenWithSceneContainer(bugId = 333132830)
fun alphaOnFullQsExpansion() =
testScope.runTest {
val viewState = ViewStateAccessor()
@@ -905,6 +915,7 @@
}
@Test
+ @BrokenWithSceneContainer(bugId = 333132830)
fun shadeCollapseFadeIn() =
testScope.runTest {
val fadeIn by collectValues(underTest.shadeCollapseFadeIn)
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 70182c1..3b8a268 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -31,7 +31,6 @@
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.customization.R
import com.android.systemui.dagger.qualifiers.Background
@@ -66,7 +65,6 @@
import java.util.TimeZone
import java.util.concurrent.Executor
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.Job
@@ -74,6 +72,7 @@
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.launch
/**
* Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
@@ -91,7 +90,6 @@
@DisplaySpecific private val resources: Resources,
private val context: Context,
@Main private val mainExecutor: DelayableExecutor,
- @Main private val mainImmediateDispatcher: CoroutineDispatcher,
@Background private val bgExecutor: Executor,
private val clockBuffers: ClockMessageBuffers,
private val featureFlags: FeatureFlagsClassic,
@@ -426,7 +424,7 @@
keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
zenModeController.addCallback(zenModeCallback)
disposableHandle =
- parent.repeatWhenAttached(mainImmediateDispatcher) {
+ parent.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
listenForDozing(this)
if (MigrateClocksToBlueprint.isEnabled) {
@@ -531,14 +529,12 @@
@VisibleForTesting
internal fun listenForDozeAmount(scope: CoroutineScope): Job {
- return scope.launch("$TAG#listenForDozeAmount") {
- keyguardInteractor.dozeAmount.collect { handleDoze(it) }
- }
+ return scope.launch { keyguardInteractor.dozeAmount.collect { handleDoze(it) } }
}
@VisibleForTesting
internal fun listenForDozeAmountTransition(scope: CoroutineScope): Job {
- return scope.launch("$TAG#listenForDozeAmountTransition") {
+ return scope.launch {
merge(
keyguardTransitionInteractor.aodToLockscreenTransition.map { step ->
step.copy(value = 1f - step.value)
@@ -556,7 +552,7 @@
*/
@VisibleForTesting
internal fun listenForAnyStateToAodTransition(scope: CoroutineScope): Job {
- return scope.launch("$TAG#listenForAnyStateToAodTransition") {
+ return scope.launch {
keyguardTransitionInteractor
.transitionStepsToState(AOD)
.filter { it.transitionState == TransitionState.STARTED }
@@ -567,7 +563,7 @@
@VisibleForTesting
internal fun listenForDozing(scope: CoroutineScope): Job {
- return scope.launch("$TAG#listenForDozing") {
+ return scope.launch {
combine(
keyguardInteractor.dozeAmount,
keyguardInteractor.isDozing,
@@ -632,7 +628,7 @@
}
companion object {
- private const val TAG = "ClockEventController"
- private const val DOZE_TICKRATE_THRESHOLD = 0.99f
+ private val TAG = ClockEventController::class.simpleName!!
+ private val DOZE_TICKRATE_THRESHOLD = 0.99f
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 3a45db1..61d1c71 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -323,7 +323,13 @@
overlayParams = updatedOverlayParams
sensorBounds = updatedOverlayParams.sensorBounds
getTouchOverlay()?.let {
- windowManager.updateViewLayout(it, coreLayoutParams.updateDimensions(null))
+ if (addViewRunnable != null) {
+ // Only updateViewLayout if there's no pending view to add to WM.
+ // If there is a pending view, that means the view hasn't been added yet so there's
+ // no need to update any layouts. Instead the correct params will be used when the
+ // view is eventually added.
+ windowManager.updateViewLayout(it, coreLayoutParams.updateDimensions(null))
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 9a9e698..424bd0a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -209,15 +209,6 @@
}
/**
- * Logs cancelation requests for time ticks
- * @param isPending is an unschedule request pending?
- * @param isTimeTickScheduled is a time tick request scheduled
- */
- public void tracePendingUnscheduleTimeTick(boolean isPending, boolean isTimeTickScheduled) {
- mLogger.logPendingUnscheduleTimeTick(isPending, isTimeTickScheduled);
- }
-
- /**
* Appends keyguard visibility change event to the logs
* @param showing whether the keyguard is now showing
*/
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index 9d6693e..75b8e51 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -162,15 +162,6 @@
})
}
- fun logPendingUnscheduleTimeTick(isPending: Boolean, isTimeTickScheduled: Boolean) {
- buffer.log(TAG, INFO, {
- bool1 = isPending
- bool2 = isTimeTickScheduled
- }, {
- "Pending unschedule time tick, isPending=$bool1, isTimeTickScheduled:$bool2"
- })
- }
-
fun logDozeStateChanged(state: DozeMachine.State) {
buffer.log(TAG, INFO, {
str1 = state.name
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 1a855d7..34a80e8 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -26,12 +26,11 @@
import android.text.format.Formatter;
import android.util.Log;
-import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.DejankUtils;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.AlarmTimeout;
-import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.wakelock.WakeLock;
import java.util.Calendar;
@@ -53,19 +52,14 @@
private final boolean mCanAnimateTransition;
private final DozeParameters mDozeParameters;
private final DozeLog mDozeLog;
- private final DelayableExecutor mBgExecutor;
- private Runnable mCancelRunnable = null;
private long mLastTimeTickElapsed = 0;
// If time tick is scheduled and there's not a pending runnable to cancel:
- private volatile boolean mTimeTickScheduled;
+ private boolean mTimeTickScheduled;
private final Runnable mCancelTimeTickerRunnable = new Runnable() {
@Override
public void run() {
- mDozeLog.tracePendingUnscheduleTimeTick(false, mTimeTickScheduled);
- if (!mTimeTickScheduled) {
- mTimeTicker.cancel();
- }
+ mTimeTicker.cancel();
}
};
@@ -73,13 +67,11 @@
public DozeUi(Context context, AlarmManager alarmManager,
WakeLock wakeLock, DozeHost host, @Main Handler handler,
DozeParameters params,
- @Background DelayableExecutor bgExecutor,
DozeLog dozeLog) {
mContext = context;
mWakeLock = wakeLock;
mHost = host;
mHandler = handler;
- mBgExecutor = bgExecutor;
mCanAnimateTransition = !params.getDisplayNeedsBlanking();
mDozeParameters = params;
mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", handler);
@@ -174,6 +166,7 @@
return;
}
mTimeTickScheduled = true;
+ DejankUtils.removeCallbacks(mCancelTimeTickerRunnable);
long time = System.currentTimeMillis();
long delta = roundToNextMinute(time) - System.currentTimeMillis();
@@ -189,8 +182,7 @@
return;
}
mTimeTickScheduled = false;
- mDozeLog.tracePendingUnscheduleTimeTick(true, mTimeTickScheduled);
- mBgExecutor.execute(mCancelTimeTickerRunnable);
+ DejankUtils.postAfterTraversal(mCancelTimeTickerRunnable);
}
private void verifyLastTimeTick() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index e6e6ff6..c32c226 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -41,7 +41,6 @@
import com.android.systemui.CoreStartable
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
@@ -73,7 +72,6 @@
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import dagger.Lazy
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -110,7 +108,6 @@
private val keyguardBlueprintViewBinder: KeyguardBlueprintViewBinder,
private val clockInteractor: KeyguardClockInteractor,
private val keyguardViewMediator: KeyguardViewMediator,
- @Main private val mainImmediateDispatcher: CoroutineDispatcher,
) : CoreStartable {
private var rootViewHandle: DisposableHandle? = null
@@ -214,7 +211,6 @@
vibratorHelper,
falsingManager,
keyguardViewMediator,
- mainImmediateDispatcher,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
index d39bd3d..720baec 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
@@ -83,19 +83,12 @@
private fun updateBlueprint() {
val useSplitShade =
splitShadeStateController.shouldUseSplitNotificationShade(context.resources)
- // TODO(b/326098079): Make ID a constant value.
- val useWeatherClockLayout =
- clockInteractor.currentClock.value?.config?.id == "DIGITAL_CLOCK_WEATHER" &&
- ComposeLockscreen.isEnabled
val blueprintId =
when {
- useWeatherClockLayout && useSplitShade -> SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
- useWeatherClockLayout -> WEATHER_CLOCK_BLUEPRINT_ID
useSplitShade && !ComposeLockscreen.isEnabled -> SplitShadeKeyguardBlueprint.ID
else -> DefaultKeyguardBlueprint.DEFAULT
}
-
transitionToBlueprint(blueprintId)
}
@@ -128,13 +121,4 @@
fun getCurrentBlueprint(): KeyguardBlueprint {
return keyguardBlueprintRepository.blueprint.value
}
-
- companion object {
- /**
- * These values live here because classes in the composable package do not exist in some
- * systems.
- */
- const val WEATHER_CLOCK_BLUEPRINT_ID = "weather-clock"
- const val SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID = "split-shade-weather-clock"
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 2182fe3..e384bfb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -49,7 +49,6 @@
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
-import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import javax.inject.Provider
@@ -280,16 +279,12 @@
* signal should be sent directly to transitions.
*/
val dismissAlpha: Flow<Float?> =
- shadeRepository.legacyShadeExpansion
- .filter { it < 1f }
- .sampleCombine(
+ combine(
+ shadeRepository.legacyShadeExpansion,
statusBarState,
keyguardTransitionInteractor.currentKeyguardState,
isKeyguardDismissible,
- )
- .map {
- (legacyShadeExpansion, statusBarState, currentKeyguardState, isKeyguardDismissible)
- ->
+ ) { legacyShadeExpansion, statusBarState, currentKeyguardState, isKeyguardDismissible ->
if (
statusBarState == StatusBarState.KEYGUARD &&
isKeyguardDismissible &&
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index e423fe0..f46a207 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -25,7 +25,6 @@
import androidx.core.view.isInvisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
import com.android.systemui.common.ui.view.LongPressHandlingView
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
@@ -35,15 +34,13 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.VibratorHelper
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
@ExperimentalCoroutinesApi
object DeviceEntryIconViewBinder {
- private const val TAG = "DeviceEntryIconViewBinder"
-
/**
* Updates UI for:
* - device entry containing view (parent view for the below views)
@@ -61,7 +58,6 @@
bgViewModel: DeviceEntryBackgroundViewModel,
falsingManager: FalsingManager,
vibratorHelper: VibratorHelper,
- mainImmediateDispatcher: CoroutineDispatcher,
) {
DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()
val longPressHandlingView = view.longPressHandlingView
@@ -77,33 +73,31 @@
view,
HapticFeedbackConstants.CONFIRM,
)
- applicationScope.launch("$TAG#viewModel.onLongPress") {
- viewModel.onLongPress()
- }
+ applicationScope.launch { viewModel.onLongPress() }
}
}
- view.repeatWhenAttached(mainImmediateDispatcher) {
+ view.repeatWhenAttached {
// Repeat on CREATED so that the view will always observe the entire
// GONE => AOD transition (even though the view may not be visible until the middle
// of the transition.
repeatOnLifecycle(Lifecycle.State.CREATED) {
- launch("$TAG#viewModel.isVisible") {
+ launch {
viewModel.isVisible.collect { isVisible ->
longPressHandlingView.isInvisible = !isVisible
}
}
- launch("$TAG#viewModel.isLongPressEnabled") {
+ launch {
viewModel.isLongPressEnabled.collect { isEnabled ->
longPressHandlingView.setLongPressHandlingEnabled(isEnabled)
}
}
- launch("$TAG#viewModel.accessibilityDelegateHint") {
+ launch {
viewModel.accessibilityDelegateHint.collect { hint ->
view.accessibilityHintType = hint
}
}
- launch("$TAG#viewModel.useBackgroundProtection") {
+ launch {
viewModel.useBackgroundProtection.collect { useBackgroundProtection ->
if (useBackgroundProtection) {
bgView.visibility = View.VISIBLE
@@ -112,7 +106,7 @@
}
}
}
- launch("$TAG#viewModel.burnInOffsets") {
+ launch {
viewModel.burnInOffsets.collect { burnInOffsets ->
view.translationX = burnInOffsets.x.toFloat()
view.translationY = burnInOffsets.y.toFloat()
@@ -120,17 +114,15 @@
}
}
- launch("$TAG#viewModel.deviceEntryViewAlpha") {
- viewModel.deviceEntryViewAlpha.collect { alpha -> view.alpha = alpha }
- }
+ launch { viewModel.deviceEntryViewAlpha.collect { alpha -> view.alpha = alpha } }
}
}
- fgIconView.repeatWhenAttached(mainImmediateDispatcher) {
+ fgIconView.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
// Start with an empty state
fgIconView.setImageState(StateSet.NOTHING, /* merge */ false)
- launch("$TAG#fgViewModel.viewModel") {
+ launch {
fgViewModel.viewModel.collect { viewModel ->
fgIconView.setImageState(
view.getIconState(viewModel.type, viewModel.useAodVariant),
@@ -150,10 +142,8 @@
bgView.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
- launch("$TAG#bgViewModel.alpha") {
- bgViewModel.alpha.collect { alpha -> bgView.alpha = alpha }
- }
- launch("$TAG#bgViewModel.color") {
+ launch { bgViewModel.alpha.collect { alpha -> bgView.alpha = alpha } }
+ launch {
bgViewModel.color.collect { color ->
bgView.imageTintList = ColorStateList.valueOf(color)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
index 1b06a69..6255f0d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
@@ -26,7 +26,6 @@
import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
import com.android.keyguard.KeyguardClockSwitch.LARGE
import com.android.keyguard.KeyguardClockSwitch.SMALL
import com.android.systemui.keyguard.MigrateClocksToBlueprint
@@ -38,10 +37,10 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.shared.clocks.DEFAULT_CLOCK_ID
-import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.launch
object KeyguardClockViewBinder {
- private const val TAG = "KeyguardClockViewBinder"
+ private val TAG = KeyguardClockViewBinder::class.simpleName!!
// When changing to new clock, we need to remove old clock views from burnInLayer
private var lastClock: ClockController? = null
@JvmStatic
@@ -51,12 +50,15 @@
viewModel: KeyguardClockViewModel,
keyguardClockInteractor: KeyguardClockInteractor,
blueprintInteractor: KeyguardBlueprintInteractor,
- ): DisposableHandle {
- keyguardClockInteractor.clockEventController.registerListeners(keyguardRootView)
-
- return keyguardRootView.repeatWhenAttached {
+ ) {
+ keyguardRootView.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
- launch("$TAG#viewModel.currentClock") {
+ keyguardClockInteractor.clockEventController.registerListeners(keyguardRootView)
+ }
+ }
+ keyguardRootView.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch {
if (!MigrateClocksToBlueprint.isEnabled) return@launch
viewModel.currentClock.collect { currentClock ->
cleanupClockViews(currentClock, keyguardRootView, viewModel.burnInLayer)
@@ -65,14 +67,14 @@
applyConstraints(clockSection, keyguardRootView, true)
}
}
- launch("$TAG#viewModel.clockSize") {
+ launch {
if (!MigrateClocksToBlueprint.isEnabled) return@launch
viewModel.clockSize.collect {
updateBurnInLayer(keyguardRootView, viewModel)
blueprintInteractor.refreshBlueprint(Type.ClockSize)
}
}
- launch("$TAG#viewModel.clockShouldBeCentered") {
+ launch {
if (!MigrateClocksToBlueprint.isEnabled) return@launch
viewModel.clockShouldBeCentered.collect { clockShouldBeCentered ->
viewModel.currentClock.value?.let {
@@ -89,7 +91,7 @@
}
}
}
- launch("$TAG#viewModel.isAodIconsVisible") {
+ launch {
if (!MigrateClocksToBlueprint.isEnabled) return@launch
viewModel.isAodIconsVisible.collect { isAodIconsVisible ->
viewModel.currentClock.value?.let {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
index 486320a..3ff32bf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
@@ -44,6 +44,7 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.res.R
+import com.android.systemui.shared.clocks.ClockRegistry
import com.android.systemui.util.Utils
import kotlin.reflect.KSuspendFunction1
@@ -77,37 +78,42 @@
context: Context,
rootView: ConstraintLayout,
viewModel: KeyguardPreviewClockViewModel,
+ clockRegistry: ClockRegistry,
updateClockAppearance: KSuspendFunction1<ClockController, Unit>,
) {
rootView.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
+ var lastClock: ClockController? = null
launch("$TAG#viewModel.previewClock") {
- var lastClock: ClockController? = null
- viewModel.previewClock.collect { currentClock ->
- lastClock?.let { clock ->
- (clock.largeClock.layout.views + clock.smallClock.layout.views)
- .forEach { rootView.removeView(it) }
- }
- lastClock = currentClock
- updateClockAppearance(currentClock)
+ viewModel.previewClock.collect { currentClock ->
+ lastClock?.let { clock ->
+ (clock.largeClock.layout.views + clock.smallClock.layout.views)
+ .forEach { rootView.removeView(it) }
+ }
+ lastClock = currentClock
+ updateClockAppearance(currentClock)
- if (viewModel.shouldHighlightSelectedAffordance) {
- (currentClock.largeClock.layout.views +
- currentClock.smallClock.layout.views)
- .forEach { it.alpha = KeyguardPreviewRenderer.DIM_ALPHA }
- }
- currentClock.largeClock.layout.views.forEach {
- (it.parent as? ViewGroup)?.removeView(it)
- rootView.addView(it)
- }
+ if (viewModel.shouldHighlightSelectedAffordance) {
+ (currentClock.largeClock.layout.views +
+ currentClock.smallClock.layout.views)
+ .forEach { it.alpha = KeyguardPreviewRenderer.DIM_ALPHA }
+ }
+ currentClock.largeClock.layout.views.forEach {
+ (it.parent as? ViewGroup)?.removeView(it)
+ rootView.addView(it)
+ }
- currentClock.smallClock.layout.views.forEach {
- (it.parent as? ViewGroup)?.removeView(it)
- rootView.addView(it)
+ currentClock.smallClock.layout.views.forEach {
+ (it.parent as? ViewGroup)?.removeView(it)
+ rootView.addView(it)
+ }
+ applyPreviewConstraints(context, rootView, currentClock, viewModel)
}
- applyPreviewConstraints(context, rootView, currentClock, viewModel)
}
- }
+ .invokeOnCompletion {
+ // recover seed color especially for Transit clock
+ lastClock?.events?.onSeedColorChanged(clockRegistry.seedColor)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index 6c21e6c..abd79ab 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -30,7 +30,6 @@
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
import com.android.settingslib.Utils
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.view.LaunchableImageView
@@ -42,11 +41,11 @@
import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.util.doOnEnd
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
/** This is only for a SINGLE Quick affordance */
object KeyguardQuickAffordanceViewBinder {
@@ -54,7 +53,6 @@
private const val EXIT_DOZE_BUTTON_REVEAL_ANIMATION_DURATION_MS = 250L
private const val SCALE_SELECTED_BUTTON = 1.23f
private const val DIM_ALPHA = 0.3f
- private const val TAG = "KeyguardQuickAffordanceViewBinder"
/**
* Defines interface for an object that acts as the binding between the view and its view-model.
@@ -76,15 +74,14 @@
alpha: Flow<Float>,
falsingManager: FalsingManager?,
vibratorHelper: VibratorHelper?,
- mainImmediateDispatcher: CoroutineDispatcher,
messageDisplayer: (Int) -> Unit,
): Binding {
val button = view as ImageView
val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
val disposableHandle =
- view.repeatWhenAttached(mainImmediateDispatcher) {
+ view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
- launch("$TAG#viewModel.collect") {
+ launch {
viewModel.collect { buttonModel ->
updateButton(
view = button,
@@ -96,7 +93,7 @@
}
}
- launch("$TAG#updateButtonAlpha") {
+ launch {
updateButtonAlpha(
view = button,
viewModel = viewModel,
@@ -104,7 +101,7 @@
)
}
- launch("$TAG#configurationBasedDimensions") {
+ launch {
configurationBasedDimensions.collect { dimensions ->
button.updateLayoutParams<ViewGroup.LayoutParams> {
width = dimensions.buttonSizePx.width
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index 44fd582..5ee35e4f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -33,7 +33,6 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launch
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD
import com.android.systemui.Flags.newAodTransition
@@ -73,7 +72,6 @@
import com.android.systemui.util.ui.stopAnimating
import com.android.systemui.util.ui.value
import kotlin.math.min
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.coroutineScope
@@ -81,6 +79,7 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
/** Bind occludingAppDeviceEntryMessageViewModel to run whenever the keyguard view is attached. */
@OptIn(ExperimentalCoroutinesApi::class)
@@ -101,7 +100,6 @@
vibratorHelper: VibratorHelper?,
falsingManager: FalsingManager?,
keyguardViewMediator: KeyguardViewMediator?,
- mainImmediateDispatcher: CoroutineDispatcher,
): DisposableHandle {
val disposables = DisposableHandles()
val childViews = mutableMapOf<Int, View>()
@@ -125,9 +123,9 @@
)
disposables +=
- view.repeatWhenAttached(mainImmediateDispatcher) {
+ view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
- launch("$TAG#occludingAppDeviceEntryMessageViewModel.message") {
+ launch {
occludingAppDeviceEntryMessageViewModel.message.collect { biometricMessage
->
if (biometricMessage?.message != null) {
@@ -146,7 +144,7 @@
if (
KeyguardBottomAreaRefactor.isEnabled || DeviceEntryUdfpsRefactor.isEnabled
) {
- launch("$TAG#viewModel.alpha") {
+ launch {
viewModel.alpha(viewState).collect { alpha ->
view.alpha = alpha
if (KeyguardBottomAreaRefactor.isEnabled) {
@@ -158,21 +156,21 @@
}
if (MigrateClocksToBlueprint.isEnabled) {
- launch("$TAG#viewModel.burnInLayerVisibility") {
+ launch {
viewModel.burnInLayerVisibility.collect { visibility ->
childViews[burnInLayerId]?.visibility = visibility
childViews[aodNotificationIconContainerId]?.visibility = visibility
}
}
- launch("$TAG#viewModel.burnInLayerAlpha") {
+ launch {
viewModel.burnInLayerAlpha.collect { alpha ->
childViews[statusViewId]?.alpha = alpha
childViews[aodNotificationIconContainerId]?.alpha = alpha
}
}
- launch("$TAG#viewModel.topClippingBounds") {
+ launch {
val clipBounds = Rect()
viewModel.topClippingBounds.collect { clipTop ->
if (clipTop == null) {
@@ -189,13 +187,13 @@
}
}
- launch("$TAG#viewModel.lockscreenStateAlpha") {
+ launch {
viewModel.lockscreenStateAlpha(viewState).collect { alpha ->
childViews[statusViewId]?.alpha = alpha
}
}
- launch("$TAG#viewModel.translationY") {
+ launch {
// When translation happens in burnInLayer, it won't be weather clock
// large clock isn't added to burnInLayer due to its scale transition
// so we also need to add translation to it here
@@ -207,7 +205,7 @@
}
}
- launch("$TAG#viewModel.translationX") {
+ launch {
viewModel.translationX.collect { state ->
val px = state.value ?: return@collect
when {
@@ -234,7 +232,7 @@
}
}
- launch("$TAG#viewModel.scale") {
+ launch {
viewModel.scale.collect { scaleViewModel ->
if (scaleViewModel.scaleClockOnly) {
// For clocks except weather clock, we have scale transition
@@ -265,7 +263,7 @@
}
if (NotificationIconContainerRefactor.isEnabled) {
- launch("$TAG#viewModel.isNotifIconContainerVisible") {
+ launch {
val iconsAppearTranslationPx =
configuration
.getDimensionPixelSize(R.dimen.shelf_appear_translation)
@@ -282,7 +280,7 @@
}
interactionJankMonitor?.let { jankMonitor ->
- launch("$TAG#viewModel.goneToAodTransition") {
+ launch {
viewModel.goneToAodTransition.collect {
when (it.transitionState) {
TransitionState.STARTED -> {
@@ -308,7 +306,7 @@
}
}
- launch("$TAG#shadeInteractor.isAnyFullyExpanded") {
+ launch {
shadeInteractor.isAnyFullyExpanded.collect { isFullyAnyExpanded ->
view.visibility =
if (isFullyAnyExpanded) {
@@ -319,12 +317,10 @@
}
}
- launch("$TAG#burnInParams.collect") {
- burnInParams.collect { viewModel.updateBurnInParams(it) }
- }
+ launch { burnInParams.collect { viewModel.updateBurnInParams(it) } }
if (deviceEntryHapticsInteractor != null && vibratorHelper != null) {
- launch("$TAG#deviceEntryHapticsInteractor.playSuccessHaptic") {
+ launch {
deviceEntryHapticsInteractor.playSuccessHaptic.collect {
vibratorHelper.performHapticFeedback(
view,
@@ -334,7 +330,7 @@
}
}
- launch("$TAG#deviceEntryHapticsInteractor.playErrorHaptic") {
+ launch {
deviceEntryHapticsInteractor.playErrorHaptic.collect {
vibratorHelper.performHapticFeedback(
view,
@@ -589,5 +585,4 @@
private const val ID = "occluding_app_device_entry_unlock_msg"
private const val AOD_ICONS_APPEAR_DURATION: Long = 200
- private const val TAG = "KeyguardRootViewBinder"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 40ea445..bda5be4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -390,7 +390,6 @@
null, // device entry haptics not required for preview mode
null, // falsing manager not required for preview mode
null, // keyguard view mediator is not required for preview mode
- mainDispatcher,
)
}
rootView.addView(
@@ -417,7 +416,8 @@
previewContext,
keyguardRootView,
clockViewModel,
- ::updateClockAppearance
+ clockRegistry,
+ ::updateClockAppearance,
)
} else {
KeyguardPreviewClockViewBinder.bind(
@@ -457,7 +457,6 @@
alpha = flowOf(1f),
falsingManager = falsingManager,
vibratorHelper = vibratorHelper,
- mainImmediateDispatcher = mainDispatcher,
) { message ->
indicationController.showTransientIndication(message)
}
@@ -472,7 +471,6 @@
alpha = flowOf(1f),
falsingManager = falsingManager,
vibratorHelper = vibratorHelper,
- mainImmediateDispatcher = mainDispatcher,
) { message ->
indicationController.showTransientIndication(message)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
index b4e57cc..04ac7bf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
@@ -17,13 +17,9 @@
package com.android.systemui.keyguard.ui.view.layout.blueprints
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.WEATHER_CLOCK_BLUEPRINT_ID
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
-import com.android.systemui.keyguard.shared.model.KeyguardSection
import dagger.Binds
import dagger.Module
-import dagger.Provides
import dagger.multibindings.IntoSet
@Module
@@ -45,26 +41,4 @@
abstract fun bindShortcutsBesideUdfpsLockscreenBlueprint(
shortcutsBesideUdfpsLockscreenBlueprint: ShortcutsBesideUdfpsKeyguardBlueprint
): KeyguardBlueprint
-
- companion object {
- /** This is a place holder for weather clock in compose. */
- @Provides
- @IntoSet
- fun bindWeatherClockBlueprintPlaceHolder(): KeyguardBlueprint {
- return object : KeyguardBlueprint {
- override val id: String = WEATHER_CLOCK_BLUEPRINT_ID
- override val sections: List<KeyguardSection> = listOf()
- }
- }
-
- /** This is a place holder for weather clock in compose. */
- @Provides
- @IntoSet
- fun bindSplitShadeWeatherClockBlueprintPlaceHolder(): KeyguardBlueprint {
- return object : KeyguardBlueprint {
- override val id: String = SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
- override val sections: List<KeyguardSection> = listOf()
- }
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
index 5404729..2e96638 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
@@ -36,7 +36,6 @@
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
class AlignShortcutsToUdfpsSection
@Inject
@@ -48,7 +47,6 @@
private val falsingManager: FalsingManager,
private val indicationController: KeyguardIndicationController,
private val vibratorHelper: VibratorHelper,
- @Main private val mainImmediateDispatcher: CoroutineDispatcher,
) : BaseShortcutSection() {
override fun addViews(constraintLayout: ConstraintLayout) {
if (KeyguardBottomAreaRefactor.isEnabled) {
@@ -66,7 +64,6 @@
keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
falsingManager,
vibratorHelper,
- mainImmediateDispatcher,
) {
indicationController.showTransientIndication(it)
}
@@ -77,7 +74,6 @@
keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
falsingManager,
vibratorHelper,
- mainImmediateDispatcher,
) {
indicationController.showTransientIndication(it)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index e0bf815..78a1fcf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -45,7 +45,6 @@
import com.android.systemui.shared.R as sharedR
import dagger.Lazy
import javax.inject.Inject
-import kotlinx.coroutines.DisposableHandle
internal fun ConstraintSet.setVisibility(
views: Iterable<View>,
@@ -66,23 +65,19 @@
val smartspaceViewModel: KeyguardSmartspaceViewModel,
val blueprintInteractor: Lazy<KeyguardBlueprintInteractor>,
) : KeyguardSection() {
- private var handle: DisposableHandle? = null
-
override fun addViews(constraintLayout: ConstraintLayout) {}
override fun bindData(constraintLayout: ConstraintLayout) {
if (!MigrateClocksToBlueprint.isEnabled) {
return
}
- handle?.dispose()
- handle =
- KeyguardClockViewBinder.bind(
- this,
- constraintLayout,
- keyguardClockViewModel,
- clockInteractor,
- blueprintInteractor.get()
- )
+ KeyguardClockViewBinder.bind(
+ this,
+ constraintLayout,
+ keyguardClockViewModel,
+ clockInteractor,
+ blueprintInteractor.get()
+ )
}
override fun applyConstraints(constraintSet: ConstraintSet) {
@@ -94,13 +89,7 @@
}
}
- override fun removeViews(constraintLayout: ConstraintLayout) {
- if (!MigrateClocksToBlueprint.isEnabled) {
- return
- }
- handle?.dispose()
- handle = null
- }
+ override fun removeViews(constraintLayout: ConstraintLayout) {}
private fun buildConstraints(
clock: ClockController,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index 865e989..29041d1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -30,7 +30,6 @@
import com.android.keyguard.LockIconViewController
import com.android.systemui.biometrics.AuthController
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -48,7 +47,6 @@
import com.android.systemui.statusbar.VibratorHelper
import dagger.Lazy
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -69,7 +67,6 @@
private val deviceEntryBackgroundViewModel: Lazy<DeviceEntryBackgroundViewModel>,
private val falsingManager: Lazy<FalsingManager>,
private val vibratorHelper: Lazy<VibratorHelper>,
- @Main private val mainImmediateDispatcher: CoroutineDispatcher,
) : KeyguardSection() {
private val deviceEntryIconViewId = R.id.device_entry_icon_view
@@ -107,7 +104,6 @@
deviceEntryBackgroundViewModel.get(),
falsingManager.get(),
vibratorHelper.get(),
- mainImmediateDispatcher,
)
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
index 27ca5cd..45b8257 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
@@ -35,7 +35,6 @@
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
class DefaultShortcutsSection
@Inject
@@ -47,7 +46,6 @@
private val falsingManager: FalsingManager,
private val indicationController: KeyguardIndicationController,
private val vibratorHelper: VibratorHelper,
- @Main private val mainImmediateDispatcher: CoroutineDispatcher,
) : BaseShortcutSection() {
override fun addViews(constraintLayout: ConstraintLayout) {
if (KeyguardBottomAreaRefactor.isEnabled) {
@@ -65,7 +63,6 @@
keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
falsingManager,
vibratorHelper,
- mainImmediateDispatcher,
) {
indicationController.showTransientIndication(it)
}
@@ -76,7 +73,6 @@
keyguardQuickAffordancesCombinedViewModel.transitionAlpha,
falsingManager,
vibratorHelper,
- mainImmediateDispatcher,
) {
indicationController.showTransientIndication(it)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
index c9251c7..bbbe140 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
@@ -113,18 +113,38 @@
)
val currentClockLayout: StateFlow<ClockLayout> =
- combine(isLargeClockVisible, clockShouldBeCentered, shadeInteractor.shadeMode) {
+ combine(
isLargeClockVisible,
clockShouldBeCentered,
- shadeMode ->
+ shadeInteractor.shadeMode,
+ currentClock
+ ) { isLargeClockVisible, clockShouldBeCentered, shadeMode, currentClock ->
val shouldUseSplitShade = shadeMode == ShadeMode.Split
- when {
- shouldUseSplitShade && clockShouldBeCentered -> ClockLayout.LARGE_CLOCK
- shouldUseSplitShade && isLargeClockVisible ->
- ClockLayout.SPLIT_SHADE_LARGE_CLOCK
- shouldUseSplitShade -> ClockLayout.SPLIT_SHADE_SMALL_CLOCK
- isLargeClockVisible -> ClockLayout.LARGE_CLOCK
- else -> ClockLayout.SMALL_CLOCK
+ // TODO(b/326098079): make id a constant field in config
+ if (currentClock?.config?.id == "DIGITAL_CLOCK_WEATHER") {
+ val weatherClockLayout =
+ when {
+ shouldUseSplitShade && clockShouldBeCentered ->
+ ClockLayout.WEATHER_LARGE_CLOCK
+ shouldUseSplitShade && isLargeClockVisible ->
+ ClockLayout.SPLIT_SHADE_WEATHER_LARGE_CLOCK
+ shouldUseSplitShade -> ClockLayout.SPLIT_SHADE_SMALL_CLOCK
+ isLargeClockVisible -> ClockLayout.WEATHER_LARGE_CLOCK
+ else -> ClockLayout.SMALL_CLOCK
+ }
+ weatherClockLayout
+ } else {
+ val clockLayout =
+ when {
+ shouldUseSplitShade && clockShouldBeCentered -> ClockLayout.LARGE_CLOCK
+ shouldUseSplitShade && isLargeClockVisible ->
+ ClockLayout.SPLIT_SHADE_LARGE_CLOCK
+ shouldUseSplitShade -> ClockLayout.SPLIT_SHADE_SMALL_CLOCK
+ isLargeClockVisible -> ClockLayout.LARGE_CLOCK
+ else -> ClockLayout.SMALL_CLOCK
+ }
+
+ clockLayout
}
}
.stateIn(
@@ -179,5 +199,7 @@
SMALL_CLOCK,
SPLIT_SHADE_LARGE_CLOCK,
SPLIT_SHADE_SMALL_CLOCK,
+ WEATHER_LARGE_CLOCK,
+ SPLIT_SHADE_WEATHER_LARGE_CLOCK,
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index 993e81b..d4c8456e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -37,6 +37,8 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.stateIn
/** Models UI state and handles user input for the lockscreen scene. */
@@ -52,16 +54,23 @@
val notifications: NotificationsPlaceholderViewModel,
) {
val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
- combine(
- deviceEntryInteractor.isUnlocked,
- communalInteractor.isCommunalAvailable,
- shadeInteractor.shadeMode,
- ) { isDeviceUnlocked, isCommunalAvailable, shadeMode ->
- destinationScenes(
- isDeviceUnlocked = isDeviceUnlocked,
- isCommunalAvailable = isCommunalAvailable,
- shadeMode = shadeMode,
- )
+ shadeInteractor.isShadeTouchable
+ .flatMapLatest { isShadeTouchable ->
+ if (!isShadeTouchable) {
+ flowOf(emptyMap())
+ } else {
+ combine(
+ deviceEntryInteractor.isUnlocked,
+ communalInteractor.isCommunalAvailable,
+ shadeInteractor.shadeMode,
+ ) { isDeviceUnlocked, isCommunalAvailable, shadeMode ->
+ destinationScenes(
+ isDeviceUnlocked = isDeviceUnlocked,
+ isCommunalAvailable = isCommunalAvailable,
+ shadeMode = shadeMode,
+ )
+ }
+ }
}
.stateIn(
scope = applicationScope,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
index 4e0b576..ab0b0b7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
@@ -52,8 +52,6 @@
val destinationScenes =
qsSceneAdapter.isCustomizing.flatMapLatest { customizing ->
if (customizing) {
- // TODO(b/332749288) Empty map so there are no back handlers and back can close
- // customizer
flowOf(emptyMap())
// TODO(b/330200163) Add an Up from Bottom to be able to collapse the shade
// while customizing
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 32d72e0..1f935f97 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -47,6 +47,7 @@
import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -103,6 +104,7 @@
private val headsUpInteractor: HeadsUpNotificationInteractor,
private val occlusionInteractor: SceneContainerOcclusionInteractor,
private val faceUnlockInteractor: DeviceEntryFaceAuthInteractor,
+ private val shadeInteractor: ShadeInteractor,
) : CoreStartable {
override fun start() {
@@ -185,6 +187,14 @@
/** Switches between scenes based on ever-changing application state. */
private fun automaticallySwitchScenes() {
+ handleBouncerImeVisibility()
+ handleSimUnlock()
+ handleDeviceUnlockStatus()
+ handlePowerState()
+ handleShadeTouchability()
+ }
+
+ private fun handleBouncerImeVisibility() {
applicationScope.launch {
// TODO (b/308001302): Move this to a bouncer specific interactor.
bouncerInteractor.onImeHiddenByUser.collectLatest {
@@ -196,6 +206,9 @@
}
}
}
+ }
+
+ private fun handleSimUnlock() {
applicationScope.launch {
simBouncerInteractor
.get()
@@ -229,6 +242,9 @@
}
}
}
+ }
+
+ private fun handleDeviceUnlockStatus() {
applicationScope.launch {
deviceUnlockedInteractor.deviceUnlockStatus
.mapNotNull { deviceUnlockStatus ->
@@ -288,7 +304,9 @@
)
}
}
+ }
+ private fun handlePowerState() {
applicationScope.launch {
powerInteractor.isAsleep.collect { isAsleep ->
if (isAsleep) {
@@ -317,7 +335,7 @@
) {
switchToScene(
targetSceneKey = Scenes.Bouncer,
- loggingReason = "device is starting to wake up with a locked sim"
+ loggingReason = "device is starting to wake up with a locked sim",
)
}
}
@@ -325,6 +343,20 @@
}
}
+ private fun handleShadeTouchability() {
+ applicationScope.launch {
+ shadeInteractor.isShadeTouchable
+ .distinctUntilChanged()
+ .filter { !it }
+ .collect {
+ switchToScene(
+ targetSceneKey = Scenes.Lockscreen,
+ loggingReason = "device became non-interactive",
+ )
+ }
+ }
+ }
+
/** Keeps [SysUiState] up-to-date */
private fun hydrateSystemUiState() {
applicationScope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java b/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java
index ab8fc65..12bff49 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java
@@ -15,6 +15,7 @@
*/
package com.android.systemui.screenshot;
+import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.app.IAssistDataReceiver;
@@ -55,7 +56,7 @@
* Called when the {@link android.app.assist.AssistContent} of the requested task is
* available.
**/
- void onAssistContentAvailable(AssistContent assistContent);
+ void onAssistContentAvailable(@Nullable AssistContent assistContent);
}
private final IActivityTaskManager mActivityTaskManager;
@@ -117,15 +118,9 @@
@Override
public void onHandleAssistData(Bundle data) {
- if (data == null) {
- return;
- }
-
- final AssistContent content = data.getParcelable(ASSIST_KEY_CONTENT);
- if (content == null) {
- Log.e(TAG, "Received AssistData, but no AssistContent found");
- return;
- }
+ final AssistContent content = (data == null) ? null
+ : data.getParcelable(
+ ASSIST_KEY_CONTENT, AssistContent.class);
AssistContentRequester requester = mParentRef.get();
if (requester != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
index 3287425..07e143a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
@@ -42,7 +42,11 @@
fun onScrollChipReady(onClick: Runnable)
fun setCompletedScreenshot(result: ScreenshotSavedResult)
- fun onAssistContentAvailable(assistContent: AssistContent) {}
+ /**
+ * Provide the AssistContent for the focused task if available, null if the focused task isn't
+ * known or didn't return data.
+ */
+ fun onAssistContent(assistContent: AssistContent?) {}
interface Factory {
fun create(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 0e48bef..6871084 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -412,9 +412,9 @@
if (screenshot.getTaskId() >= 0) {
mAssistContentRequester.requestAssistContent(screenshot.getTaskId(),
- assistContent -> {
- mActionsProvider.onAssistContentAvailable(assistContent);
- });
+ assistContent -> mActionsProvider.onAssistContent(assistContent));
+ } else {
+ mActionsProvider.onAssistContent(null);
}
} else {
saveScreenshotInWorkerThread(screenshot.getUserHandle(), finisher,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
index 32e9296..d9a5102 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
@@ -65,7 +65,9 @@
}
launch {
viewModel.actions.collect { actions ->
- if (actions.isNotEmpty()) {
+ val visibleActions = actions.filter { it.visible }
+
+ if (visibleActions.isNotEmpty()) {
view
.requireViewById<View>(R.id.actions_container_background)
.visibility = View.VISIBLE
@@ -75,7 +77,7 @@
// any new actions and update any that are already there.
// This assumes that actions can never change order and that each action
// ID is unique.
- val newIds = actions.map { it.id }
+ val newIds = visibleActions.map { it.id }
for (view in actionsContainer.children.toList()) {
if (view.tag !in newIds) {
@@ -83,7 +85,7 @@
}
}
- for ((index, action) in actions.withIndex()) {
+ for ((index, action) in visibleActions.withIndex()) {
val currentView: View? = actionsContainer.getChildAt(index)
if (action.id == currentView?.tag) {
// Same ID, update the display
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonViewModel.kt
index 64b0105..c5fa8db 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonViewModel.kt
@@ -19,6 +19,7 @@
data class ActionButtonViewModel(
val appearance: ActionButtonAppearance,
val id: Int,
+ val visible: Boolean,
val onClicked: (() -> Unit)?,
) {
companion object {
@@ -29,6 +30,6 @@
fun withNextId(
appearance: ActionButtonAppearance,
onClicked: (() -> Unit)?
- ): ActionButtonViewModel = ActionButtonViewModel(appearance, getId(), onClicked)
+ ): ActionButtonViewModel = ActionButtonViewModel(appearance, getId(), true, onClicked)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
index fa34803..f67ad40 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
@@ -48,12 +48,34 @@
return action.id
}
+ fun setActionVisibility(actionId: Int, visible: Boolean) {
+ val actionList = _actions.value.toMutableList()
+ val index = actionList.indexOfFirst { it.id == actionId }
+ if (index >= 0) {
+ actionList[index] =
+ ActionButtonViewModel(
+ actionList[index].appearance,
+ actionId,
+ visible,
+ actionList[index].onClicked
+ )
+ _actions.value = actionList
+ } else {
+ Log.w(TAG, "Attempted to update unknown action id $actionId")
+ }
+ }
+
fun updateActionAppearance(actionId: Int, appearance: ActionButtonAppearance) {
val actionList = _actions.value.toMutableList()
val index = actionList.indexOfFirst { it.id == actionId }
if (index >= 0) {
actionList[index] =
- ActionButtonViewModel(appearance, actionId, actionList[index].onClicked)
+ ActionButtonViewModel(
+ appearance,
+ actionId,
+ actionList[index].visible,
+ actionList[index].onClicked
+ )
_actions.value = actionList
} else {
Log.w(TAG, "Attempted to update unknown action id $actionId")
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index f6b1bcc..f418e7e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -23,7 +23,13 @@
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
+import androidx.activity.OnBackPressedDispatcher
+import androidx.activity.OnBackPressedDispatcherOwner
+import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
import androidx.compose.ui.platform.ComposeView
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
import com.android.compose.theme.PlatformTheme
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.communal.dagger.Communal
@@ -33,6 +39,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -40,6 +47,7 @@
import com.android.systemui.util.kotlin.collectFlow
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
/**
* Controller that's responsible for the glanceable hub container view and its touch handling.
@@ -139,13 +147,33 @@
): View {
return initView(
ComposeView(context).apply {
- setContent {
- PlatformTheme {
- CommunalContainer(
- viewModel = communalViewModel,
- dataSourceDelegator = dataSourceDelegator,
- dialogFactory = dialogFactory,
- )
+ repeatWhenAttached {
+ lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ setViewTreeOnBackPressedDispatcherOwner(
+ object : OnBackPressedDispatcherOwner {
+ override val onBackPressedDispatcher =
+ OnBackPressedDispatcher().apply {
+ setOnBackInvokedDispatcher(
+ viewRootImpl.onBackInvokedDispatcher
+ )
+ }
+
+ override val lifecycle: Lifecycle =
+ this@repeatWhenAttached.lifecycle
+ }
+ )
+
+ setContent {
+ PlatformTheme {
+ CommunalContainer(
+ viewModel = communalViewModel,
+ dataSourceDelegator = dataSourceDelegator,
+ dialogFactory = dialogFactory,
+ )
+ }
+ }
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
index ebebbe6..8c15817 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
@@ -124,7 +124,6 @@
// release focus immediately to kick off focus change transition
notificationShadeWindowController.setNotificationShadeFocusable(false)
notificationStackScrollLayout.cancelExpandHelper()
- sceneInteractor.changeScene(Scenes.Shade, "ShadeController.animateExpandShade")
if (delayed) {
scope.launch {
delay(125)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 9ce38db..8b673c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -49,7 +49,6 @@
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
-import android.util.ArraySet;
import android.view.ContentInfo;
import androidx.annotation.NonNull;
@@ -150,10 +149,8 @@
private int mCachedContrastColor = COLOR_INVALID;
private int mCachedContrastColorIsFor = COLOR_INVALID;
private InflationTask mRunningTask = null;
- private Throwable mDebugThrowable;
public CharSequence remoteInputTextWhenReset;
public long lastRemoteInputSent = NOT_LAUNCHED_YET;
- public final ArraySet<Integer> mActiveAppOps = new ArraySet<>(3);
private final MutableStateFlow<CharSequence> mHeadsUpStatusBarText =
StateFlowKt.MutableStateFlow(null);
@@ -190,11 +187,6 @@
private boolean mBlockable;
/**
- * The {@link SystemClock#elapsedRealtime()} when this notification entry was created.
- */
- public long mCreationElapsedRealTime;
-
- /**
* Whether this notification has ever been a non-sticky HUN.
*/
private boolean mIsDemoted = false;
@@ -264,13 +256,8 @@
mKey = sbn.getKey();
setSbn(sbn);
setRanking(ranking);
- mCreationElapsedRealTime = SystemClock.elapsedRealtime();
}
- @VisibleForTesting
- public void setCreationElapsedRealTime(long time) {
- mCreationElapsedRealTime = time;
- }
@Override
public NotificationEntry getRepresentativeEntry() {
return this;
@@ -581,19 +568,6 @@
return mRunningTask;
}
- /**
- * Set a throwable that is used for debugging
- *
- * @param debugThrowable the throwable to save
- */
- public void setDebugThrowable(Throwable debugThrowable) {
- mDebugThrowable = debugThrowable;
- }
-
- public Throwable getDebugThrowable() {
- return mDebugThrowable;
- }
-
public void onRemoteInputInserted() {
lastRemoteInputSent = NOT_LAUNCHED_YET;
remoteInputTextWhenReset = null;
@@ -749,12 +723,6 @@
return row != null && row.areChildrenExpanded();
}
-
- //TODO: probably less confusing to say "is group fully visible"
- public boolean isGroupNotFullyVisible() {
- return row == null || row.isGroupNotFullyVisible();
- }
-
public NotificationGuts getGuts() {
if (row != null) return row.getGuts();
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
index d6118a0..d3c874c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.row
+import android.app.Flags
import android.app.Notification
import android.app.Notification.MessagingStyle
import android.app.Person
@@ -131,7 +132,7 @@
val senderName =
systemUiContext.resources.getString(
R.string.conversation_single_line_name_display,
- name
+ if (Flags.cleanUpSpansAndNewLines()) name?.toString() else name
)
// We need to find back-up values for those texts if they are needed and empty
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
index 37646ae..8ab1eca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
@@ -479,7 +479,7 @@
val runnable = Runnable {
assistManagerLazy.get().hideAssist()
- intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
+ intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.addFlags(flags)
val result = intArrayOf(ActivityManager.START_CANCELED)
activityTransitionAnimator.startIntentWithAnimation(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
index a306606..11cbc9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
@@ -176,7 +176,7 @@
bool1 = alert
bool2 = hasEntry
}, {
- "request: update notification $str1 alert: $bool1 hasEntry: $bool2 reason: $str2"
+ "request: update notification $str1 alert: $bool1 hasEntry: $bool2"
})
}
@@ -186,7 +186,7 @@
bool1 = alert
bool2 = hasEntry
}, {
- "update notification $str1 alert: $bool1 hasEntry: $bool2 reason: $str2"
+ "update notification $str1 alert: $bool1 hasEntry: $bool2"
})
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index ca55dd8..f32d5b8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -142,7 +142,6 @@
context.resources,
context,
mainExecutor,
- IMMEDIATE,
bgExecutor,
clockBuffers,
withDeps.featureFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
index e7caf00..7311f4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
@@ -41,8 +41,6 @@
import com.android.systemui.DejankUtils;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.wakelock.WakeLockFake;
import org.junit.After;
@@ -71,7 +69,6 @@
private Handler mHandler;
private HandlerThread mHandlerThread;
private DozeUi mDozeUi;
- private FakeExecutor mFakeExecutor;
@Before
public void setUp() throws Exception {
@@ -83,9 +80,9 @@
mHandlerThread.start();
mWakeLock = new WakeLockFake();
mHandler = mHandlerThread.getThreadHandler();
- mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+
mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
- mDozeParameters, mFakeExecutor, mDozeLog);
+ mDozeParameters, mDozeLog);
mDozeUi.setDozeMachine(mMachine);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
index b6b4571..4270236 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
@@ -27,16 +27,12 @@
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.WEATHER_CLOCK_BLUEPRINT_ID
import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.blueprints.SplitShadeKeyguardBlueprint
import com.android.systemui.kosmos.testScope
-import com.android.systemui.plugins.clocks.ClockConfig
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.res.R
import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
@@ -103,72 +99,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
- fun composeLockscreenOff_DoesAppliesSplitShadeWeatherClockBlueprint() {
- testScope.runTest {
- val blueprint by collectLastValue(underTest.blueprint)
- whenever(clockController.config)
- .thenReturn(
- ClockConfig(
- id = "DIGITAL_CLOCK_WEATHER",
- name = "clock",
- description = "clock",
- )
- )
- clockRepository.setCurrentClock(clockController)
- overrideResource(R.bool.config_use_split_notification_shade, true)
- configurationRepository.onConfigurationChange()
- runCurrent()
-
- assertThat(blueprint?.id).isNotEqualTo(SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID)
- }
- }
-
- @Test
- @EnableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
- fun testDoesAppliesSplitShadeWeatherClockBlueprint() {
- testScope.runTest {
- val blueprint by collectLastValue(underTest.blueprint)
- whenever(clockController.config)
- .thenReturn(
- ClockConfig(
- id = "DIGITAL_CLOCK_WEATHER",
- name = "clock",
- description = "clock",
- )
- )
- clockRepository.setCurrentClock(clockController)
- overrideResource(R.bool.config_use_split_notification_shade, true)
- configurationRepository.onConfigurationChange()
- runCurrent()
-
- assertThat(blueprint?.id).isEqualTo(SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID)
- }
- }
-
- @Test
- @EnableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
- fun testAppliesWeatherClockBlueprint() {
- testScope.runTest {
- val blueprint by collectLastValue(underTest.blueprint)
- whenever(clockController.config)
- .thenReturn(
- ClockConfig(
- id = "DIGITAL_CLOCK_WEATHER",
- name = "clock",
- description = "clock",
- )
- )
- clockRepository.setCurrentClock(clockController)
- overrideResource(R.bool.config_use_split_notification_shade, false)
- configurationRepository.onConfigurationChange()
- runCurrent()
-
- assertThat(blueprint?.id).isEqualTo(WEATHER_CLOCK_BLUEPRINT_ID)
- }
- }
-
- @Test
@EnableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
fun testDoesNotApplySplitShadeBlueprint() {
testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
index b5f668c..4f2b690 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
@@ -38,7 +38,6 @@
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.TestScope
@@ -86,7 +85,6 @@
{ mock(DeviceEntryBackgroundViewModel::class.java) },
{ falsingManager },
{ mock(VibratorHelper::class.java) },
- mock(CoroutineDispatcher::class.java),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt
index 75489b6..8954231 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt
@@ -17,8 +17,6 @@
package com.android.systemui.keyguard.data.repository
import android.os.fakeExecutorHandler
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
-import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.WEATHER_CLOCK_BLUEPRINT_ID
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint.Companion.DEFAULT
@@ -34,8 +32,6 @@
setOf(
defaultBlueprint,
splitShadeBlueprint,
- weatherClockBlueprint,
- splitShadeWeatherClockBlueprint,
),
handler = fakeExecutorHandler,
assert = mock<ThreadAssert>(),
@@ -50,22 +46,6 @@
get() = listOf()
}
-private val weatherClockBlueprint =
- object : KeyguardBlueprint {
- override val id: String
- get() = WEATHER_CLOCK_BLUEPRINT_ID
- override val sections: List<KeyguardSection>
- get() = listOf()
- }
-
-private val splitShadeWeatherClockBlueprint =
- object : KeyguardBlueprint {
- override val id: String
- get() = SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
- override val sections: List<KeyguardSection>
- get() = listOf()
- }
-
private val splitShadeBlueprint =
object : KeyguardBlueprint {
override val id: String
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/FakeSceneContainerFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/FakeSceneContainerFlags.kt
index bae5257..ded7256 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/FakeSceneContainerFlags.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/FakeSceneContainerFlags.kt
@@ -21,7 +21,7 @@
import dagger.Provides
class FakeSceneContainerFlags(
- var enabled: Boolean = false,
+ var enabled: Boolean = SceneContainerFlag.isEnabled,
) : SceneContainerFlags {
override fun isEnabled(): Boolean {
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongArrayMultiStateCounter_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongArrayMultiStateCounter_host.java
index 7414110..0f65544 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongArrayMultiStateCounter_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongArrayMultiStateCounter_host.java
@@ -100,6 +100,16 @@
mLastStateChangeTimestampMs = timestampMs;
}
+ public void copyStatesFrom(LongArrayMultiStateCounterRavenwood source) {
+ for (int i = 0; i < mStateCount; i++) {
+ mStates[i].mTimeInStateSinceUpdate = source.mStates[i].mTimeInStateSinceUpdate;
+ Arrays.fill(mStates[i].mCounter, 0);
+ }
+ mCurrentState = source.mCurrentState;
+ mLastStateChangeTimestampMs = source.mLastStateChangeTimestampMs;
+ mLastUpdateTimestampMs = source.mLastUpdateTimestampMs;
+ }
+
public void setValue(int state, long[] values) {
System.arraycopy(values, 0, mStates[state].mCounter, 0, mArrayLength);
}
@@ -335,6 +345,10 @@
getInstance(instanceId).setState(state, timestampMs);
}
+ public static void native_copyStatesFrom(long targetInstanceId, long sourceInstanceId) {
+ getInstance(targetInstanceId).copyStatesFrom(getInstance(sourceInstanceId));
+ }
+
public static void native_incrementValues(long instanceId, long containerInstanceId,
long timestampMs) {
getInstance(instanceId).incrementValues(
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 4f46ecd..f98799d 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -124,7 +124,9 @@
import com.android.server.power.stats.BatteryStatsDumpHelperImpl;
import com.android.server.power.stats.BatteryStatsImpl;
import com.android.server.power.stats.BatteryUsageStatsProvider;
-import com.android.server.power.stats.CpuAggregatedPowerStatsProcessor;
+import com.android.server.power.stats.CpuPowerStatsProcessor;
+import com.android.server.power.stats.MobileRadioPowerStatsProcessor;
+import com.android.server.power.stats.PhoneCallPowerStatsProcessor;
import com.android.server.power.stats.PowerStatsAggregator;
import com.android.server.power.stats.PowerStatsExporter;
import com.android.server.power.stats.PowerStatsScheduler;
@@ -408,11 +410,18 @@
com.android.internal.R.bool.config_batteryStatsResetOnUnplugAfterSignificantCharge);
final long powerStatsThrottlePeriodCpu = context.getResources().getInteger(
com.android.internal.R.integer.config_defaultPowerStatsThrottlePeriodCpu);
+ final long powerStatsThrottlePeriodMobileRadio = context.getResources().getInteger(
+ com.android.internal.R.integer.config_defaultPowerStatsThrottlePeriodMobileRadio);
mBatteryStatsConfig =
new BatteryStatsImpl.BatteryStatsConfig.Builder()
.setResetOnUnplugHighBatteryLevel(resetOnUnplugHighBatteryLevel)
.setResetOnUnplugAfterSignificantCharge(resetOnUnplugAfterSignificantCharge)
- .setPowerStatsThrottlePeriodCpu(powerStatsThrottlePeriodCpu)
+ .setPowerStatsThrottlePeriodMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU,
+ powerStatsThrottlePeriodCpu)
+ .setPowerStatsThrottlePeriodMillis(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ powerStatsThrottlePeriodMobileRadio)
.build();
mPowerStatsUidResolver = new PowerStatsUidResolver();
mStats = new BatteryStatsImpl(mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock,
@@ -470,7 +479,20 @@
AggregatedPowerStatsConfig.STATE_SCREEN,
AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
.setProcessor(
- new CpuAggregatedPowerStatsProcessor(mPowerProfile, mCpuScalingPolicies));
+ new CpuPowerStatsProcessor(mPowerProfile, mCpuScalingPolicies));
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessor(
+ new MobileRadioPowerStatsProcessor(mPowerProfile));
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_PHONE,
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+ .setProcessor(new PhoneCallPowerStatsProcessor());
return config;
}
@@ -494,8 +516,16 @@
}
public void systemServicesReady() {
- mStats.setPowerStatsCollectorEnabled(Flags.streamlinedBatteryStats());
- mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(Flags.streamlinedBatteryStats());
+ mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_CPU,
+ Flags.streamlinedBatteryStats());
+ mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ Flags.streamlinedConnectivityBatteryStats());
+ mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ BatteryConsumer.POWER_COMPONENT_CPU,
+ Flags.streamlinedBatteryStats());
+ mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ Flags.streamlinedConnectivityBatteryStats());
mWorker.systemServicesReady();
mStats.systemServicesReady(mContext);
mCpuWakeupStats.systemServicesReady();
@@ -536,7 +566,7 @@
* Notifies BatteryStatsService that the system server is ready.
*/
public void onSystemReady() {
- mStats.onSystemReady();
+ mStats.onSystemReady(mContext);
mPowerStatsScheduler.start(Flags.streamlinedBatteryStats());
}
@@ -1591,19 +1621,14 @@
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
mHandler.post(() -> {
- final boolean update;
synchronized (mStats) {
// Ignore if no power state change.
if (mLastPowerStateFromRadio == powerState) return;
mLastPowerStateFromRadio = powerState;
- update = mStats.noteMobileRadioPowerStateLocked(powerState, timestampNs, uid,
+ mStats.noteMobileRadioPowerStateLocked(powerState, timestampNs, uid,
elapsedRealtime, uptime);
}
-
- if (update) {
- mWorker.scheduleSync("modem-data", BatteryExternalStatsWorker.UPDATE_RADIO);
- }
});
}
FrameworkStatsLog.write_non_chained(
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 390dca3..3e3ec17 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -145,6 +145,7 @@
"core_experiments_team_internal",
"core_graphics",
"core_libraries",
+ "crumpet",
"dck_framework",
"devoptions_settings",
"game",
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 85a231f..1c169a0 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -585,7 +585,7 @@
* </point>
* </luxThresholds>
* </idleScreenRefreshRateTimeout>
- *
+ * <supportsVrr>true</supportsVrr>
*
* </displayConfiguration>
* }
@@ -872,6 +872,8 @@
*/
private float mBrightnessCapForWearBedtimeMode;
+ private boolean mVrrSupportEnabled;
+
private final DisplayManagerFlags mFlags;
@VisibleForTesting
@@ -1606,6 +1608,13 @@
return mBrightnessCapForWearBedtimeMode;
}
+ /**
+ * @return true if display supports dvrr
+ */
+ public boolean isVrrSupportEnabled() {
+ return mVrrSupportEnabled;
+ }
+
@Override
public String toString() {
return "DisplayDeviceConfig{"
@@ -1705,6 +1714,8 @@
+ "\n"
+ "mEvenDimmerBrightnessData:" + (mEvenDimmerBrightnessData != null
? mEvenDimmerBrightnessData.toString() : "null")
+ + "\n"
+ + "mVrrSupported= " + mVrrSupportEnabled + "\n"
+ "}";
}
@@ -1779,6 +1790,7 @@
mHdrBrightnessData = HdrBrightnessData.loadConfig(config);
loadBrightnessCapForWearBedtimeMode(config);
loadIdleScreenRefreshRateTimeoutConfigs(config);
+ mVrrSupportEnabled = config.getSupportsVrr();
} else {
Slog.w(TAG, "DisplayDeviceConfig file is null");
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 31092f2..8f1277b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2764,6 +2764,7 @@
+ requestedRefreshRate + " on Display: " + displayId);
}
}
+
mDisplayModeDirector.getAppRequestObserver().setAppRequest(
displayId, requestedModeId, requestedMinRefreshRate, requestedMaxRefreshRate);
@@ -4939,6 +4940,18 @@
}
@Override
+ public boolean isVrrSupportEnabled(int displayId) {
+ DisplayDevice device;
+ synchronized (mSyncRoot) {
+ device = getDeviceForDisplayLocked(displayId);
+ }
+ if (device == null) {
+ return false;
+ }
+ return device.getDisplayDeviceConfig().isVrrSupportEnabled();
+ }
+
+ @Override
public void setWindowManagerMirroring(int displayId, boolean isMirroring) {
synchronized (mSyncRoot) {
final DisplayDevice device = getDeviceForDisplayLocked(displayId);
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index d084d1c..a862b6e 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -105,6 +105,7 @@
* picked by the system based on system-wide and display-specific configuration.
*/
public class DisplayModeDirector {
+
public static final float SYNCHRONIZED_REFRESH_RATE_TARGET = DEFAULT_LOW_REFRESH_RATE;
public static final float SYNCHRONIZED_REFRESH_RATE_TOLERANCE = 1;
private static final String TAG = "DisplayModeDirector";
@@ -149,6 +150,9 @@
// A map from the display ID to the default mode of that display.
private SparseArray<Display.Mode> mDefaultModeByDisplay;
+ // a map from display id to vrr support
+ private SparseBooleanArray mVrrSupportedByDisplay;
+
private BrightnessObserver mBrightnessObserver;
private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener;
@@ -189,8 +193,6 @@
private final DisplayManagerFlags mDisplayManagerFlags;
- private final boolean mDvrrSupported;
-
public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
@NonNull DisplayManagerFlags displayManagerFlags) {
@@ -200,8 +202,6 @@
public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
@NonNull Injector injector,
@NonNull DisplayManagerFlags displayManagerFlags) {
- mDvrrSupported = context.getResources().getBoolean(
- com.android.internal.R.bool.config_supportsDvrr);
mIsDisplayResolutionRangeVotingEnabled = displayManagerFlags
.isDisplayResolutionRangeVotingEnabled();
mIsUserPreferredModeVoteEnabled = displayManagerFlags.isUserPreferredModeVoteEnabled();
@@ -219,23 +219,23 @@
displayManagerFlags.isRefreshRateVotingTelemetryEnabled());
mSupportedModesByDisplay = new SparseArray<>();
mDefaultModeByDisplay = new SparseArray<>();
+ mVrrSupportedByDisplay = new SparseBooleanArray();
mAppRequestObserver = new AppRequestObserver();
mConfigParameterProvider = new DeviceConfigParameterProvider(injector.getDeviceConfig());
mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
- mSettingsObserver = new SettingsObserver(context, handler, mDvrrSupported,
- displayManagerFlags);
- mBrightnessObserver = new BrightnessObserver(context, handler, injector, mDvrrSupported,
+ mSettingsObserver = new SettingsObserver(context, handler, displayManagerFlags);
+ mBrightnessObserver = new BrightnessObserver(context, handler, injector,
displayManagerFlags);
mDefaultDisplayDeviceConfig = null;
mUdfpsObserver = new UdfpsObserver();
mVotesStorage = new VotesStorage(this::notifyDesiredDisplayModeSpecsChangedLocked,
mVotesStatsReporter);
- mDisplayObserver = new DisplayObserver(context, handler, mVotesStorage);
+ mDisplayObserver = new DisplayObserver(context, handler, mVotesStorage, injector);
mSensorObserver = new ProximitySensorObserver(mVotesStorage, injector);
mSkinThermalStatusObserver = new SkinThermalStatusObserver(injector, mVotesStorage);
mHbmObserver = new HbmObserver(injector, mVotesStorage, BackgroundThread.getHandler(),
mDeviceConfigDisplaySettings);
- if (mDvrrSupported && displayManagerFlags.isRestrictDisplayModesEnabled()) {
+ if (displayManagerFlags.isRestrictDisplayModesEnabled()) {
mSystemRequestObserver = new SystemRequestObserver(mVotesStorage);
} else {
mSystemRequestObserver = null;
@@ -315,7 +315,8 @@
List<Display.Mode> availableModes = new ArrayList<>();
availableModes.add(defaultMode);
VoteSummary primarySummary = new VoteSummary(mIsDisplayResolutionRangeVotingEnabled,
- mDvrrSupported, mLoggingEnabled, mSupportsFrameRateOverride);
+ mVrrSupportedByDisplay.get(displayId),
+ mLoggingEnabled, mSupportsFrameRateOverride);
int lowestConsideredPriority = Vote.MIN_PRIORITY;
int highestConsideredPriority = Vote.MAX_PRIORITY;
@@ -355,7 +356,8 @@
}
VoteSummary appRequestSummary = new VoteSummary(mIsDisplayResolutionRangeVotingEnabled,
- mDvrrSupported, mLoggingEnabled, mSupportsFrameRateOverride);
+ mVrrSupportedByDisplay.get(displayId),
+ mLoggingEnabled, mSupportsFrameRateOverride);
appRequestSummary.applyVotes(votes,
Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF,
@@ -442,6 +444,15 @@
return mAppRequestObserver;
}
+ private boolean isVrrSupportedByAnyDisplayLocked() {
+ for (int i = 0; i < mVrrSupportedByDisplay.size(); i++) {
+ if (mVrrSupportedByDisplay.valueAt(i)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Sets the desiredDisplayModeSpecsListener for changes to display mode and refresh rate ranges.
*/
@@ -539,7 +550,13 @@
*/
public void requestDisplayModes(IBinder token, int displayId, int[] modeIds) {
if (mSystemRequestObserver != null) {
- mSystemRequestObserver.requestDisplayModes(token, displayId, modeIds);
+ boolean vrrSupported;
+ synchronized (mLock) {
+ vrrSupported = mVrrSupportedByDisplay.get(displayId);
+ }
+ if (vrrSupported) {
+ mSystemRequestObserver.requestDisplayModes(token, displayId, modeIds);
+ }
}
}
@@ -627,6 +644,11 @@
}
@VisibleForTesting
+ void injectVrrByDisplay(SparseBooleanArray vrrByDisplay) {
+ mVrrSupportedByDisplay = vrrByDisplay;
+ }
+
+ @VisibleForTesting
void injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay) {
mVotesStorage.injectVotesByDisplay(votesByDisplay);
}
@@ -906,11 +928,11 @@
private float mDefaultPeakRefreshRate;
private float mDefaultRefreshRate;
- SettingsObserver(@NonNull Context context, @NonNull Handler handler, boolean dvrrSupported,
+ SettingsObserver(@NonNull Context context, @NonNull Handler handler,
DisplayManagerFlags flags) {
super(handler);
mContext = context;
- mVsynLowPowerVoteEnabled = dvrrSupported && flags.isVsyncLowPowerVoteEnabled();
+ mVsynLowPowerVoteEnabled = flags.isVsyncLowPowerVoteEnabled();
// We don't want to load from the DeviceConfig while constructing since this leads to
// a spike in the latency of DisplayManagerService startup. This happens because
// reading from the DeviceConfig is an intensive IO operation and having it in the
@@ -1020,7 +1042,7 @@
boolean inLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0;
final Vote vote;
- if (inLowPowerMode && mVsynLowPowerVoteEnabled) {
+ if (inLowPowerMode && mVsynLowPowerVoteEnabled && isVrrSupportedByAnyDisplayLocked()) {
vote = Vote.forSupportedRefreshRates(List.of(
new SupportedRefreshRatesVote.RefreshRates(/* peakRefreshRate= */ 60f,
/* vsyncRate= */ 240f),
@@ -1190,8 +1212,8 @@
/**
* Sets refresh rates from app request
*/
- public void setAppRequest(int displayId, int modeId, float requestedMinRefreshRateRange,
- float requestedMaxRefreshRateRange) {
+ public void setAppRequest(int displayId, int modeId,
+ float requestedMinRefreshRateRange, float requestedMaxRefreshRateRange) {
synchronized (mLock) {
setAppRequestedModeLocked(displayId, modeId);
setAppPreferredRefreshRateRangeLocked(displayId, requestedMinRefreshRateRange,
@@ -1204,7 +1226,6 @@
if (Objects.equals(requestedMode, mAppRequestedModeByDisplay.get(displayId))) {
return;
}
-
final Vote baseModeRefreshRateVote;
final Vote sizeVote;
if (requestedMode != null) {
@@ -1295,13 +1316,16 @@
private final Context mContext;
private final Handler mHandler;
private final VotesStorage mVotesStorage;
+
+ private DisplayManagerInternal mDisplayManagerInternal;
private int mExternalDisplayPeakWidth;
private int mExternalDisplayPeakHeight;
private int mExternalDisplayPeakRefreshRate;
private final boolean mRefreshRateSynchronizationEnabled;
private final Set<Integer> mExternalDisplaysConnected = new HashSet<>();
- DisplayObserver(Context context, Handler handler, VotesStorage votesStorage) {
+ DisplayObserver(Context context, Handler handler, VotesStorage votesStorage,
+ Injector injector) {
mContext = context;
mHandler = handler;
mVotesStorage = votesStorage;
@@ -1330,6 +1354,7 @@
}
public void observe() {
+ mDisplayManagerInternal = mInjector.getDisplayManagerInternal();
mInjector.registerDisplayListener(this, mHandler);
// Populate existing displays
@@ -1342,17 +1367,21 @@
modes.put(displayId, info.supportedModes);
defaultModes.put(displayId, info.getDefaultMode());
}
+ boolean vrrSupportedByDefaultDisplay = mDisplayManagerInternal
+ .isVrrSupportEnabled(Display.DEFAULT_DISPLAY);
synchronized (mLock) {
final int size = modes.size();
for (int i = 0; i < size; i++) {
mSupportedModesByDisplay.put(modes.keyAt(i), modes.valueAt(i));
mDefaultModeByDisplay.put(defaultModes.keyAt(i), defaultModes.valueAt(i));
}
+ mVrrSupportedByDisplay.put(Display.DEFAULT_DISPLAY, vrrSupportedByDefaultDisplay);
}
}
@Override
public void onDisplayAdded(int displayId) {
+ updateVrrStatus(displayId);
DisplayInfo displayInfo = getDisplayInfo(displayId);
updateDisplayModes(displayId, displayInfo);
updateLayoutLimitedFrameRate(displayId, displayInfo);
@@ -1366,6 +1395,7 @@
synchronized (mLock) {
mSupportedModesByDisplay.remove(displayId);
mDefaultModeByDisplay.remove(displayId);
+ mVrrSupportedByDisplay.delete(displayId);
mSettingsObserver.removeRefreshRateSetting(displayId);
}
updateLayoutLimitedFrameRate(displayId, null);
@@ -1376,6 +1406,7 @@
@Override
public void onDisplayChanged(int displayId) {
+ updateVrrStatus(displayId);
DisplayInfo displayInfo = getDisplayInfo(displayId);
updateDisplayModes(displayId, displayInfo);
updateLayoutLimitedFrameRate(displayId, displayInfo);
@@ -1505,6 +1536,13 @@
mVotesStorage.updateGlobalVote(Vote.PRIORITY_SYNCHRONIZED_REFRESH_RATE, null);
}
+ private void updateVrrStatus(int displayId) {
+ boolean isVrrSupported = mDisplayManagerInternal.isVrrSupportEnabled(displayId);
+ synchronized (mLock) {
+ mVrrSupportedByDisplay.put(displayId, isVrrSupported);
+ }
+ }
+
private void updateDisplayModes(int displayId, @Nullable DisplayInfo info) {
if (info == null) {
return;
@@ -1631,7 +1669,7 @@
private @Temperature.ThrottlingStatus int mThermalStatus = Temperature.THROTTLING_NONE;
BrightnessObserver(Context context, Handler handler, Injector injector,
- boolean dvrrSupported , DisplayManagerFlags flags) {
+ DisplayManagerFlags flags) {
mContext = context;
mHandler = handler;
mInjector = injector;
@@ -1639,7 +1677,7 @@
/* attemptReadFromFeatureParams= */ false);
mRefreshRateInHighZone = context.getResources().getInteger(
R.integer.config_fixedRefreshRateInHighZone);
- mVsyncLowLightBlockingVoteEnabled = dvrrSupported && flags.isVsyncLowLightVoteEnabled();
+ mVsyncLowLightBlockingVoteEnabled = flags.isVsyncLowLightVoteEnabled();
}
/**
@@ -2225,7 +2263,8 @@
}
}
- if (mVsyncLowLightBlockingVoteEnabled) {
+ if (mVsyncLowLightBlockingVoteEnabled
+ && mVrrSupportedByDisplay.get(Display.DEFAULT_DISPLAY)) {
refreshRateSwitchingVote = Vote.forSupportedRefreshRatesAndDisableSwitching(
List.of(
new SupportedRefreshRatesVote.RefreshRates(
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index a100fe0..3d01c5f 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -410,7 +410,7 @@
Slog.v(TAG,
"Removing window token: " + mCurToken + " for display: " + curTokenDisplayId);
}
- mWindowManagerInternal.removeWindowToken(mCurToken, false /* removeWindows */,
+ mWindowManagerInternal.removeWindowToken(mCurToken, true /* removeWindows */,
false /* animateExit */, curTokenDisplayId);
mCurToken = null;
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 62a9471..93f68a8 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -1362,12 +1362,18 @@
@Override
public IBinder getBinderForSetQueue() throws RemoteException {
- return new ParcelableListBinder<QueueItem>((list) -> {
- synchronized (mLock) {
- mQueue = list;
- }
- mHandler.post(MessageHandler.MSG_UPDATE_QUEUE);
- });
+ return new ParcelableListBinder<QueueItem>(
+ (list) -> {
+ // Checking list items are instanceof QueueItem to validate against
+ // malicious apps calling it directly via reflection with non compilable
+ // items. See b/317048338 for more details
+ List<QueueItem> sanitizedQueue =
+ list.stream().filter(it -> it instanceof QueueItem).toList();
+ synchronized (mLock) {
+ mQueue = sanitizedQueue;
+ }
+ mHandler.post(MessageHandler.MSG_UPDATE_QUEUE);
+ });
}
@Override
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 956e10c..ebc1a2a 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3399,21 +3399,21 @@
// ============================================================================
@Override
- public void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration,
+ public boolean enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration,
boolean isUiContext, int displayId,
@Nullable ITransientNotificationCallback textCallback) {
- enqueueToast(pkg, token, text, /* callback= */ null, duration, isUiContext, displayId,
- textCallback);
+ return enqueueToast(pkg, token, text, /* callback= */ null, duration, isUiContext,
+ displayId, textCallback);
}
@Override
- public void enqueueToast(String pkg, IBinder token, ITransientNotification callback,
+ public boolean enqueueToast(String pkg, IBinder token, ITransientNotification callback,
int duration, boolean isUiContext, int displayId) {
- enqueueToast(pkg, token, /* text= */ null, callback, duration, isUiContext, displayId,
- /* textCallback= */ null);
+ return enqueueToast(pkg, token, /* text= */ null, callback, duration, isUiContext,
+ displayId, /* textCallback= */ null);
}
- private void enqueueToast(String pkg, IBinder token, @Nullable CharSequence text,
+ private boolean enqueueToast(String pkg, IBinder token, @Nullable CharSequence text,
@Nullable ITransientNotification callback, int duration, boolean isUiContext,
int displayId, @Nullable ITransientNotificationCallback textCallback) {
if (DBG) {
@@ -3425,7 +3425,7 @@
|| (text != null && callback != null) || token == null) {
Slog.e(TAG, "Not enqueuing toast. pkg=" + pkg + " text=" + text + " callback="
+ " token=" + token);
- return;
+ return false;
}
final int callingUid = Binder.getCallingUid();
@@ -3451,7 +3451,7 @@
boolean isAppRenderedToast = (callback != null);
if (!checkCanEnqueueToast(pkg, callingUid, displayId, isAppRenderedToast,
isSystemToast)) {
- return;
+ return false;
}
synchronized (mToastQueue) {
@@ -3477,7 +3477,7 @@
if (count >= MAX_PACKAGE_TOASTS) {
Slog.e(TAG, "Package has already queued " + count
+ " toasts. Not showing more. Package=" + pkg);
- return;
+ return false;
}
}
}
@@ -3513,6 +3513,7 @@
Binder.restoreCallingIdentity(callingId);
}
}
+ return true;
}
@GuardedBy("mToastQueue")
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
index 894226c..e1b4b88 100644
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
@@ -34,11 +34,14 @@
import java.io.IOException;
import java.io.StringWriter;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.TimeZone;
/**
* This class represents aggregated power stats for a variety of power components (CPU, WiFi,
@@ -66,7 +69,7 @@
aggregatedPowerStatsConfig.getPowerComponentsAggregatedStatsConfigs();
mPowerComponentStats = new PowerComponentAggregatedPowerStats[configs.size()];
for (int i = 0; i < configs.size(); i++) {
- mPowerComponentStats[i] = new PowerComponentAggregatedPowerStats(configs.get(i));
+ mPowerComponentStats[i] = new PowerComponentAggregatedPowerStats(this, configs.get(i));
}
}
@@ -223,7 +226,7 @@
if (i == 0) {
baseTime = clockUpdate.monotonicTime;
sb.append("Start time: ")
- .append(DateFormat.format("yyyy-MM-dd-HH-mm-ss", clockUpdate.currentTime))
+ .append(formatDateTime(clockUpdate.currentTime))
.append(" (")
.append(baseTime)
.append(") duration: ")
@@ -235,8 +238,7 @@
TimeUtils.formatDuration(
clockUpdate.monotonicTime - baseTime, sb,
TimeUtils.HUNDRED_DAY_FIELD_LEN + 3);
- sb.append(" ").append(
- DateFormat.format("yyyy-MM-dd-HH-mm-ss", clockUpdate.currentTime));
+ sb.append(" ").append(formatDateTime(clockUpdate.currentTime));
ipw.increaseIndent();
ipw.println(sb);
ipw.decreaseIndent();
@@ -267,6 +269,12 @@
}
}
+ private static String formatDateTime(long timeInMillis) {
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
+ format.getCalendar().setTimeZone(TimeZone.getTimeZone("GMT"));
+ return format.format(new Date(timeInMillis));
+ }
+
@Override
public String toString() {
StringWriter sw = new StringWriter();
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
index 6fbbc0f..5aad570 100644
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
@@ -75,7 +75,7 @@
private final int mPowerComponentId;
private @TrackedState int[] mTrackedDeviceStates;
private @TrackedState int[] mTrackedUidStates;
- private AggregatedPowerStatsProcessor mProcessor = NO_OP_PROCESSOR;
+ private PowerStatsProcessor mProcessor = NO_OP_PROCESSOR;
PowerComponent(int powerComponentId) {
this.mPowerComponentId = powerComponentId;
@@ -85,6 +85,9 @@
* Configures which states should be tracked as separate dimensions for the entire device.
*/
public PowerComponent trackDeviceStates(@TrackedState int... states) {
+ if (mTrackedDeviceStates != null) {
+ throw new IllegalStateException("Component is already configured");
+ }
mTrackedDeviceStates = states;
return this;
}
@@ -93,6 +96,9 @@
* Configures which states should be tracked as separate dimensions on a per-UID basis.
*/
public PowerComponent trackUidStates(@TrackedState int... states) {
+ if (mTrackedUidStates != null) {
+ throw new IllegalStateException("Component is already configured");
+ }
mTrackedUidStates = states;
return this;
}
@@ -102,7 +108,7 @@
* before giving the aggregates stats to consumers. The processor can complete the
* aggregation process, for example by computing estimated power usage.
*/
- public PowerComponent setProcessor(@NonNull AggregatedPowerStatsProcessor processor) {
+ public PowerComponent setProcessor(@NonNull PowerStatsProcessor processor) {
mProcessor = processor;
return this;
}
@@ -137,7 +143,7 @@
}
@NonNull
- public AggregatedPowerStatsProcessor getProcessor() {
+ public PowerStatsProcessor getProcessor() {
return mProcessor;
}
@@ -153,6 +159,7 @@
}
return false;
}
+
}
private final List<PowerComponent> mPowerComponents = new ArrayList<>();
@@ -168,23 +175,55 @@
return builder;
}
+ /**
+ * Creates a configuration for the specified power component, which is a subcomponent
+ * of a different power component. The tracked states will be the same as the parent
+ * component's.
+ */
+ public PowerComponent trackPowerComponent(int powerComponentId,
+ int parentPowerComponentId) {
+ PowerComponent parent = null;
+ for (int i = 0; i < mPowerComponents.size(); i++) {
+ PowerComponent powerComponent = mPowerComponents.get(i);
+ if (powerComponent.getPowerComponentId() == parentPowerComponentId) {
+ parent = powerComponent;
+ break;
+ }
+ }
+
+ if (parent == null) {
+ throw new IllegalArgumentException(
+ "Parent component " + parentPowerComponentId + " is not configured");
+ }
+
+ PowerComponent powerComponent = trackPowerComponent(powerComponentId);
+ powerComponent.mTrackedDeviceStates = parent.mTrackedDeviceStates;
+ powerComponent.mTrackedUidStates = parent.mTrackedUidStates;
+ return powerComponent;
+ }
+
public List<PowerComponent> getPowerComponentsAggregatedStatsConfigs() {
return mPowerComponents;
}
- private static final AggregatedPowerStatsProcessor NO_OP_PROCESSOR =
- new AggregatedPowerStatsProcessor() {
+ private static final PowerStatsProcessor NO_OP_PROCESSOR =
+ new PowerStatsProcessor() {
@Override
- public void finish(PowerComponentAggregatedPowerStats stats) {
+ void finish(PowerComponentAggregatedPowerStats stats) {
}
@Override
- public String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
+ String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
return Arrays.toString(stats);
}
@Override
- public String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
+ String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) {
+ return descriptor.getStateLabel(key) + " " + Arrays.toString(stats);
+ }
+
+ @Override
+ String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
return Arrays.toString(stats);
}
};
diff --git a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
index a8eda3c..cb10da9 100644
--- a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
@@ -24,6 +24,7 @@
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.net.wifi.WifiManager;
+import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.Bundle;
import android.os.OutcomeReceiver;
@@ -603,24 +604,31 @@
}
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) {
- // We were asked to fetch Telephony data.
- if (mTelephony != null) {
- CompletableFuture<ModemActivityInfo> temp = new CompletableFuture<>();
- mTelephony.requestModemActivityInfo(Runnable::run,
- new OutcomeReceiver<ModemActivityInfo,
- TelephonyManager.ModemActivityInfoException>() {
- @Override
- public void onResult(ModemActivityInfo result) {
- temp.complete(result);
- }
+ @SuppressWarnings("GuardedBy")
+ PowerStatsCollector collector = mStats.getPowerStatsCollector(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO);
+ if (collector.isEnabled()) {
+ collector.schedule();
+ } else {
+ // We were asked to fetch Telephony data.
+ if (mTelephony != null) {
+ CompletableFuture<ModemActivityInfo> temp = new CompletableFuture<>();
+ mTelephony.requestModemActivityInfo(Runnable::run,
+ new OutcomeReceiver<ModemActivityInfo,
+ TelephonyManager.ModemActivityInfoException>() {
+ @Override
+ public void onResult(ModemActivityInfo result) {
+ temp.complete(result);
+ }
- @Override
- public void onError(TelephonyManager.ModemActivityInfoException e) {
- Slog.w(TAG, "error reading modem stats:" + e);
- temp.complete(null);
- }
- });
- modemFuture = temp;
+ @Override
+ public void onError(TelephonyManager.ModemActivityInfoException e) {
+ Slog.w(TAG, "error reading modem stats:" + e);
+ temp.complete(null);
+ }
+ });
+ modemFuture = temp;
+ }
}
if (!railUpdated) {
synchronized (mStats) {
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 3a84897..fc2df57 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -22,6 +22,8 @@
import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
+import static com.android.server.power.stats.MobileRadioPowerStatsCollector.mapRadioAccessNetworkTypeToRadioAccessTechnology;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -35,6 +37,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.hardware.usb.UsbManager;
import android.location.GnssSignalQuality;
@@ -71,6 +74,7 @@
import android.os.connectivity.GpsBatteryStats;
import android.os.connectivity.WifiActivityEnergyInfo;
import android.os.connectivity.WifiBatteryStats;
+import android.power.PowerStatsInternal;
import android.provider.Settings;
import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation.NetworkType;
@@ -99,6 +103,7 @@
import android.util.Printer;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.SparseDoubleArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
@@ -137,6 +142,7 @@
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.LocalServices;
import com.android.server.power.optimization.Flags;
import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
@@ -166,8 +172,12 @@
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
/**
* All information we are collecting about things that can happen that impact
@@ -280,8 +290,9 @@
private KernelMemoryBandwidthStats mKernelMemoryBandwidthStats;
private final LongSparseArray<SamplingTimer> mKernelMemoryStats = new LongSparseArray<>();
private int[] mCpuPowerBracketMap;
- private final CpuPowerStatsCollector mCpuPowerStatsCollector;
- private boolean mPowerStatsCollectorEnabled;
+ private CpuPowerStatsCollector mCpuPowerStatsCollector;
+ private MobileRadioPowerStatsCollector mMobileRadioPowerStatsCollector;
+ private final SparseBooleanArray mPowerStatsCollectorEnabled = new SparseBooleanArray();
public LongSparseArray<SamplingTimer> getKernelMemoryStats() {
return mKernelMemoryStats;
@@ -433,9 +444,11 @@
public static class BatteryStatsConfig {
static final int RESET_ON_UNPLUG_HIGH_BATTERY_LEVEL_FLAG = 1 << 0;
static final int RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG = 1 << 1;
+ static final long DEFAULT_POWER_STATS_COLLECTION_THROTTLE_PERIOD =
+ TimeUnit.HOURS.toMillis(1);
private final int mFlags;
- private final long mPowerStatsThrottlePeriodCpu;
+ private SparseLongArray mPowerStatsThrottlePeriods;
private BatteryStatsConfig(Builder builder) {
int flags = 0;
@@ -446,7 +459,7 @@
flags |= RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG;
}
mFlags = flags;
- mPowerStatsThrottlePeriodCpu = builder.mPowerStatsThrottlePeriodCpu;
+ mPowerStatsThrottlePeriods = builder.mPowerStatsThrottlePeriods;
}
/**
@@ -467,8 +480,9 @@
== RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG;
}
- long getPowerStatsThrottlePeriodCpu() {
- return mPowerStatsThrottlePeriodCpu;
+ long getPowerStatsThrottlePeriod(@BatteryConsumer.PowerComponent int powerComponent) {
+ return mPowerStatsThrottlePeriods.get(powerComponent,
+ DEFAULT_POWER_STATS_COLLECTION_THROTTLE_PERIOD);
}
/**
@@ -477,12 +491,16 @@
public static class Builder {
private boolean mResetOnUnplugHighBatteryLevel;
private boolean mResetOnUnplugAfterSignificantCharge;
- private long mPowerStatsThrottlePeriodCpu;
+ private SparseLongArray mPowerStatsThrottlePeriods;
public Builder() {
mResetOnUnplugHighBatteryLevel = true;
mResetOnUnplugAfterSignificantCharge = true;
- mPowerStatsThrottlePeriodCpu = 60000;
+ mPowerStatsThrottlePeriods = new SparseLongArray();
+ setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_CPU,
+ TimeUnit.MINUTES.toMillis(1));
+ setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ TimeUnit.HOURS.toMillis(1));
}
/**
@@ -512,10 +530,11 @@
/**
* Sets the minimum amount of time (in millis) to wait between passes
- * of CPU power stats collection.
+ * of power stats collection for the specified power component.
*/
- public Builder setPowerStatsThrottlePeriodCpu(long periodMs) {
- mPowerStatsThrottlePeriodCpu = periodMs;
+ public Builder setPowerStatsThrottlePeriodMillis(
+ @BatteryConsumer.PowerComponent int powerComponent, long periodMs) {
+ mPowerStatsThrottlePeriods.put(powerComponent, periodMs);
return this;
}
}
@@ -597,7 +616,7 @@
@SuppressWarnings("GuardedBy") // errorprone false positive on getProcStateTimeCounter
@VisibleForTesting
public void updateProcStateCpuTimesLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
- if (mPowerStatsCollectorEnabled) {
+ if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)) {
return;
}
@@ -653,7 +672,7 @@
*/
@SuppressWarnings("GuardedBy") // errorprone false positive on getProcStateTimeCounter
public void updateCpuTimesForAllUids() {
- if (mPowerStatsCollectorEnabled && mCpuPowerStatsCollector != null) {
+ if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)) {
mCpuPowerStatsCollector.schedule();
return;
}
@@ -713,7 +732,8 @@
@GuardedBy("this")
private void ensureKernelSingleUidTimeReaderLocked() {
- if (mPowerStatsCollectorEnabled || mKernelSingleUidTimeReader != null) {
+ if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)
+ || mKernelSingleUidTimeReader != null) {
return;
}
@@ -830,8 +850,6 @@
private final HistoryEventTracker mActiveEvents = new HistoryEventTracker();
private final HistoryStepDetailsCalculatorImpl mStepDetailsCalculator =
new HistoryStepDetailsCalculatorImpl();
- private final PowerStats.DescriptorRegistry mPowerStatsDescriptorRegistry =
- new PowerStats.DescriptorRegistry();
private boolean mHaveBatteryLevel = false;
private boolean mBatteryPluggedIn;
@@ -1838,19 +1856,28 @@
FrameworkStatsLog.write(
FrameworkStatsLog.PHONE_SIGNAL_STRENGTH_CHANGED, strengthBin);
}
+
+ /**
+ * Records a statsd event when the batterystats config file is written to disk.
+ */
+ public void writeCommitSysConfigFile(String fileName, long durationMs) {
+ com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(fileName,
+ durationMs);
+ }
}
private final FrameworkStatsLogger mFrameworkStatsLogger;
@VisibleForTesting
- public BatteryStatsImpl(Clock clock, File historyDirectory, @NonNull Handler handler,
+ public BatteryStatsImpl(@NonNull BatteryStatsConfig config, Clock clock, File historyDirectory,
+ @NonNull Handler handler,
@NonNull PowerStatsUidResolver powerStatsUidResolver,
@NonNull FrameworkStatsLogger frameworkStatsLogger,
@NonNull BatteryStatsHistory.TraceDelegate traceDelegate,
@NonNull BatteryStatsHistory.EventLogger eventLogger) {
+ mBatteryStatsConfig = config;
mClock = clock;
initKernelStatsReaders();
- mBatteryStatsConfig = new BatteryStatsConfig.Builder().build();
mHandler = handler;
mPowerStatsUidResolver = powerStatsUidResolver;
mFrameworkStatsLogger = frameworkStatsLogger;
@@ -1873,7 +1900,7 @@
mPlatformIdleStateCallback = null;
mEnergyConsumerRetriever = null;
mUserInfoProvider = null;
- mCpuPowerStatsCollector = null;
+ initPowerStatsCollectors();
}
private void initKernelStatsReaders() {
@@ -1893,6 +1920,105 @@
mTmpRailStats = new RailStats();
}
+ private class PowerStatsCollectorInjector implements CpuPowerStatsCollector.Injector,
+ MobileRadioPowerStatsCollector.Injector {
+ private PackageManager mPackageManager;
+ private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+ private NetworkStatsManager mNetworkStatsManager;
+ private TelephonyManager mTelephonyManager;
+
+ void setContext(Context context) {
+ mPackageManager = context.getPackageManager();
+ mConsumedEnergyRetriever = new PowerStatsCollector.ConsumedEnergyRetrieverImpl(
+ LocalServices.getService(PowerStatsInternal.class));
+ mNetworkStatsManager = context.getSystemService(NetworkStatsManager.class);
+ mTelephonyManager = context.getSystemService(TelephonyManager.class);
+ }
+
+ @Override
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ @Override
+ public Clock getClock() {
+ return mClock;
+ }
+
+ @Override
+ public PowerStatsUidResolver getUidResolver() {
+ return mPowerStatsUidResolver;
+ }
+
+ @Override
+ public CpuScalingPolicies getCpuScalingPolicies() {
+ return mCpuScalingPolicies;
+ }
+
+ @Override
+ public PowerProfile getPowerProfile() {
+ return mPowerProfile;
+ }
+
+ @Override
+ public CpuPowerStatsCollector.KernelCpuStatsReader getKernelCpuStatsReader() {
+ return new CpuPowerStatsCollector.KernelCpuStatsReader();
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+ @Override
+ public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+ return mConsumedEnergyRetriever;
+ }
+
+ @Override
+ public IntSupplier getVoltageSupplier() {
+ return () -> mBatteryVoltageMv;
+ }
+
+ @Override
+ public Supplier<NetworkStats> getMobileNetworkStatsSupplier() {
+ return () -> readMobileNetworkStatsLocked(mNetworkStatsManager);
+ }
+
+ @Override
+ public TelephonyManager getTelephonyManager() {
+ return mTelephonyManager;
+ }
+
+ @Override
+ public LongSupplier getCallDurationSupplier() {
+ return () -> mPhoneOnTimer.getTotalTimeLocked(mClock.elapsedRealtime() * 1000,
+ STATS_SINCE_CHARGED);
+ }
+
+ @Override
+ public LongSupplier getPhoneSignalScanDurationSupplier() {
+ return () -> mPhoneSignalScanningTimer.getTotalTimeLocked(
+ mClock.elapsedRealtime() * 1000, STATS_SINCE_CHARGED);
+ }
+ }
+
+ private final PowerStatsCollectorInjector mPowerStatsCollectorInjector =
+ new PowerStatsCollectorInjector();
+
+ @SuppressWarnings("GuardedBy") // Accessed from constructor only
+ private void initPowerStatsCollectors() {
+ mCpuPowerStatsCollector = new CpuPowerStatsCollector(mPowerStatsCollectorInjector,
+ mBatteryStatsConfig.getPowerStatsThrottlePeriod(
+ BatteryConsumer.POWER_COMPONENT_CPU));
+ mCpuPowerStatsCollector.addConsumer(this::recordPowerStats);
+
+ mMobileRadioPowerStatsCollector = new MobileRadioPowerStatsCollector(
+ mPowerStatsCollectorInjector, mBatteryStatsConfig.getPowerStatsThrottlePeriod(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO));
+ mMobileRadioPowerStatsCollector.addConsumer(this::recordPowerStats);
+ }
+
/**
* TimeBase observer.
*/
@@ -5738,16 +5864,19 @@
mMobileRadioActiveTimer.stopRunningLocked(realElapsedRealtimeMs);
mMobileRadioActivePerAppTimer.stopRunningLocked(realElapsedRealtimeMs);
- if (mLastModemActivityInfo != null) {
- if (elapsedRealtimeMs < mLastModemActivityInfo.getTimestampMillis()
+ if (mMobileRadioPowerStatsCollector.isEnabled()) {
+ mMobileRadioPowerStatsCollector.schedule();
+ } else {
+ // Check if modem Activity info has been collected recently, don't bother
+ // triggering another update.
+ if (mLastModemActivityInfo == null
+ || elapsedRealtimeMs >= mLastModemActivityInfo.getTimestampMillis()
+ MOBILE_RADIO_POWER_STATE_UPDATE_FREQ_MS) {
- // Modem Activity info has been collected recently, don't bother
- // triggering another update.
- return false;
+ mExternalSync.scheduleSync("modem-data",
+ BatteryExternalStatsWorker.UPDATE_RADIO);
+ return true;
}
}
- // Tell the caller to collect radio network/power stats.
- return true;
}
}
return false;
@@ -5915,6 +6044,7 @@
mPhoneOnTimer.startRunningLocked(elapsedRealtimeMs);
if (mConstants.PHONE_ON_EXTERNAL_STATS_COLLECTION) {
scheduleSyncExternalStatsLocked("phone-on", ExternalStatsSync.UPDATE_RADIO);
+ mMobileRadioPowerStatsCollector.schedule();
}
}
}
@@ -5927,6 +6057,7 @@
mPhoneOn = false;
mPhoneOnTimer.stopRunningLocked(elapsedRealtimeMs);
scheduleSyncExternalStatsLocked("phone-off", ExternalStatsSync.UPDATE_RADIO);
+ mMobileRadioPowerStatsCollector.schedule();
}
}
@@ -6269,27 +6400,6 @@
}
}
- @RadioAccessTechnology
- private static int mapRadioAccessNetworkTypeToRadioAccessTechnology(
- @AccessNetworkConstants.RadioAccessNetworkType int dataType) {
- switch (dataType) {
- case AccessNetworkConstants.AccessNetworkType.NGRAN:
- return RADIO_ACCESS_TECHNOLOGY_NR;
- case AccessNetworkConstants.AccessNetworkType.EUTRAN:
- return RADIO_ACCESS_TECHNOLOGY_LTE;
- case AccessNetworkConstants.AccessNetworkType.UNKNOWN: //fallthrough
- case AccessNetworkConstants.AccessNetworkType.GERAN: //fallthrough
- case AccessNetworkConstants.AccessNetworkType.UTRAN: //fallthrough
- case AccessNetworkConstants.AccessNetworkType.CDMA2000: //fallthrough
- case AccessNetworkConstants.AccessNetworkType.IWLAN:
- return RADIO_ACCESS_TECHNOLOGY_OTHER;
- default:
- Slog.w(TAG,
- "Unhandled RadioAccessNetworkType (" + dataType + "), mapping to OTHER");
- return RADIO_ACCESS_TECHNOLOGY_OTHER;
- }
- }
-
@GuardedBy("this")
public void noteWifiOnLocked(long elapsedRealtimeMs, long uptimeMs) {
if (!mWifiOn) {
@@ -8311,7 +8421,7 @@
@GuardedBy("mBsi")
private void ensureMultiStateCounters(long timestampMs) {
- if (mBsi.mPowerStatsCollectorEnabled) {
+ if (mBsi.mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)) {
throw new IllegalStateException("Multi-state counters used in streamlined mode");
}
@@ -10612,7 +10722,8 @@
mProcessStateTimer[uidRunningState].startRunningLocked(elapsedRealtimeMs);
}
- if (!mBsi.mPowerStatsCollectorEnabled && mBsi.trackPerProcStateCpuTimes()) {
+ if (!mBsi.mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)
+ && mBsi.trackPerProcStateCpuTimes()) {
mBsi.updateProcStateCpuTimesLocked(mUid, elapsedRealtimeMs, uptimeMs);
LongArrayMultiStateCounter onBatteryCounter =
@@ -10634,7 +10745,8 @@
final int batteryConsumerProcessState =
mapUidProcessStateToBatteryConsumerProcessState(uidRunningState);
- if (mBsi.mSystemReady && mBsi.mPowerStatsCollectorEnabled) {
+ if (mBsi.mSystemReady && mBsi.mPowerStatsCollectorEnabled.get(
+ BatteryConsumer.POWER_COMPONENT_CPU)) {
mBsi.mHistory.recordProcessStateChange(elapsedRealtimeMs, uptimeMs, mUid,
batteryConsumerProcessState);
}
@@ -11016,11 +11128,7 @@
mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock, mMonotonicClock);
}
- mCpuPowerStatsCollector = new CpuPowerStatsCollector(mCpuScalingPolicies, mPowerProfile,
- mPowerStatsUidResolver, () -> mBatteryVoltageMv, mHandler,
- mBatteryStatsConfig.getPowerStatsThrottlePeriodCpu());
- mCpuPowerStatsCollector.addConsumer(this::recordPowerStats);
-
+ initPowerStatsCollectors();
mStartCount++;
initTimersAndCounters();
mOnBattery = mOnBatteryInternal = false;
@@ -11296,8 +11404,7 @@
memStream.writeTo(stream);
stream.flush();
mDailyFile.finishWrite(stream);
- com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
- "batterystats-daily",
+ mFrameworkStatsLogger.writeCommitSysConfigFile("batterystats-daily",
initialTimeMs + SystemClock.uptimeMillis() - startTimeMs2);
} catch (IOException e) {
Slog.w("BatteryStats",
@@ -11809,7 +11916,7 @@
// Store the empty state to disk to ensure consistency
writeSyncLocked();
- if (mPowerStatsCollectorEnabled) {
+ if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)) {
schedulePowerStatsSampleCollection();
}
@@ -11953,7 +12060,7 @@
return networkStatsManager.getWifiUidStats();
}
- private static class NetworkStatsDelta {
+ static class NetworkStatsDelta {
int mUid;
int mSet;
long mRxBytes;
@@ -11985,9 +12092,16 @@
public long getTxPackets() {
return mTxPackets;
}
+
+ @Override
+ public String toString() {
+ return "NetworkStatsDelta{mUid=" + mUid + ", mSet=" + mSet + ", mRxBytes=" + mRxBytes
+ + ", mRxPackets=" + mRxPackets + ", mTxBytes=" + mTxBytes + ", mTxPackets="
+ + mTxPackets + '}';
+ }
}
- private List<NetworkStatsDelta> computeDelta(NetworkStats currentStats,
+ static List<NetworkStatsDelta> computeDelta(NetworkStats currentStats,
NetworkStats lastStats) {
List<NetworkStatsDelta> deltaList = new ArrayList<>();
for (NetworkStats.Entry entry : currentStats) {
@@ -12418,13 +12532,11 @@
addModemTxPowerToHistory(deltaInfo, elapsedRealtimeMs, uptimeMs);
// Grab a separate lock to acquire the network stats, which may do I/O.
- NetworkStats delta = null;
+ List<NetworkStatsDelta> delta = null;
synchronized (mModemNetworkLock) {
final NetworkStats latestStats = readMobileNetworkStatsLocked(networkStatsManager);
if (latestStats != null) {
- delta = latestStats.subtract(mLastModemNetworkStats != null
- ? mLastModemNetworkStats
- : new NetworkStats(0, -1));
+ delta = computeDelta(latestStats, mLastModemNetworkStats);
mLastModemNetworkStats = latestStats;
}
}
@@ -12527,7 +12639,7 @@
long totalRxPackets = 0;
long totalTxPackets = 0;
if (delta != null) {
- for (NetworkStats.Entry entry : delta) {
+ for (NetworkStatsDelta entry : delta) {
if (entry.getRxPackets() == 0 && entry.getTxPackets() == 0) {
continue;
}
@@ -12568,7 +12680,7 @@
// Now distribute proportional blame to the apps that did networking.
long totalPackets = totalRxPackets + totalTxPackets;
if (totalPackets > 0) {
- for (NetworkStats.Entry entry : delta) {
+ for (NetworkStatsDelta entry : delta) {
if (entry.getRxPackets() == 0 && entry.getTxPackets() == 0) {
continue;
}
@@ -14408,17 +14520,41 @@
/**
* Notifies BatteryStatsImpl that the system server is ready.
*/
- public void onSystemReady() {
+ public void onSystemReady(Context context) {
if (mCpuUidFreqTimeReader != null) {
mCpuUidFreqTimeReader.onSystemReady();
}
- if (mCpuPowerStatsCollector != null) {
- mCpuPowerStatsCollector.setEnabled(mPowerStatsCollectorEnabled);
- }
+
+ mPowerStatsCollectorInjector.setContext(context);
+
+ mCpuPowerStatsCollector.setEnabled(
+ mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU));
+ mCpuPowerStatsCollector.schedule();
+
+ mMobileRadioPowerStatsCollector.setEnabled(
+ mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO));
+ mMobileRadioPowerStatsCollector.schedule();
+
mSystemReady = true;
}
/**
+ * Returns a PowerStatsCollector for the specified power component or null if unavailable.
+ */
+ @Nullable
+ PowerStatsCollector getPowerStatsCollector(
+ @BatteryConsumer.PowerComponent int powerComponent) {
+ switch (powerComponent) {
+ case BatteryConsumer.POWER_COMPONENT_CPU:
+ return mCpuPowerStatsCollector;
+ case BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO:
+ return mMobileRadioPowerStatsCollector;
+ }
+ return null;
+ }
+
+
+ /**
* Force recording of all history events regardless of the "charging" state.
*/
@VisibleForTesting
@@ -14561,9 +14697,10 @@
stream.write(parcel.marshall());
stream.flush();
mCheckinFile.finishWrite(stream);
- com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
- "batterystats-checkin", initialTimeMs
- + SystemClock.uptimeMillis() - startTimeMs2);
+ mFrameworkStatsLogger.writeCommitSysConfigFile(
+ "batterystats-checkin",
+ initialTimeMs + SystemClock.uptimeMillis()
+ - startTimeMs2);
} catch (IOException e) {
Slog.w("BatteryStats",
"Error writing checkin battery statistics", e);
@@ -15437,9 +15574,10 @@
/**
* Enables or disables the PowerStatsCollector mode.
*/
- public void setPowerStatsCollectorEnabled(boolean enabled) {
+ public void setPowerStatsCollectorEnabled(@BatteryConsumer.PowerComponent int powerComponent,
+ boolean enabled) {
synchronized (this) {
- mPowerStatsCollectorEnabled = enabled;
+ mPowerStatsCollectorEnabled.put(powerComponent, enabled);
}
}
@@ -15944,10 +16082,8 @@
* Callers will need to wait for the collection to complete on the handler thread.
*/
public void schedulePowerStatsSampleCollection() {
- if (mCpuPowerStatsCollector == null) {
- return;
- }
mCpuPowerStatsCollector.forceSchedule();
+ mMobileRadioPowerStatsCollector.forceSchedule();
}
/**
@@ -15965,6 +16101,7 @@
*/
public void dumpStatsSample(PrintWriter pw) {
mCpuPowerStatsCollector.collectAndDump(pw);
+ mMobileRadioPowerStatsCollector.collectAndDump(pw);
}
private final Runnable mWriteAsyncRunnable = () -> {
@@ -16036,7 +16173,7 @@
+ " duration ms:" + (SystemClock.uptimeMillis() - startTimeMs)
+ " bytes:" + p.dataSize());
}
- com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
+ mFrameworkStatsLogger.writeCommitSysConfigFile(
"batterystats", SystemClock.uptimeMillis() - startTimeMs);
} catch (IOException e) {
Slog.w(TAG, "Error writing battery statistics", e);
@@ -17262,10 +17399,8 @@
pw.println();
dumpConstantsLocked(pw);
- if (mCpuPowerStatsCollector != null) {
- pw.println();
- mCpuPowerStatsCollector.dumpCpuPowerBracketsLocked(pw);
- }
+ pw.println();
+ mCpuPowerStatsCollector.dumpCpuPowerBracketsLocked(pw);
pw.println();
dumpEnergyConsumerStatsLocked(pw);
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index 30b80ae..97f0986 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -27,6 +27,7 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import com.android.internal.os.Clock;
import com.android.internal.os.CpuScalingPolicies;
@@ -43,7 +44,7 @@
public class BatteryUsageStatsProvider {
private static final String TAG = "BatteryUsageStatsProv";
private final Context mContext;
- private boolean mPowerStatsExporterEnabled;
+ private final SparseBooleanArray mPowerStatsExporterEnabled = new SparseBooleanArray();
private final PowerStatsExporter mPowerStatsExporter;
private final PowerStatsStore mPowerStatsStore;
private final PowerProfile mPowerProfile;
@@ -71,14 +72,20 @@
// Power calculators are applied in the order of registration
mPowerCalculators.add(new BatteryChargeCalculator());
- if (!mPowerStatsExporterEnabled) {
+ if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)) {
mPowerCalculators.add(
new CpuPowerCalculator(mCpuScalingPolicies, mPowerProfile));
}
mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile));
mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile));
if (!BatteryStats.checkWifiOnly(mContext)) {
- mPowerCalculators.add(new MobileRadioPowerCalculator(mPowerProfile));
+ if (!mPowerStatsExporterEnabled.get(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) {
+ mPowerCalculators.add(new MobileRadioPowerCalculator(mPowerProfile));
+ }
+ if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_PHONE)) {
+ mPowerCalculators.add(new PhonePowerCalculator(mPowerProfile));
+ }
}
mPowerCalculators.add(new WifiPowerCalculator(mPowerProfile));
mPowerCalculators.add(new BluetoothPowerCalculator(mPowerProfile));
@@ -89,7 +96,6 @@
mPowerCalculators.add(new FlashlightPowerCalculator(mPowerProfile));
mPowerCalculators.add(new AudioPowerCalculator(mPowerProfile));
mPowerCalculators.add(new VideoPowerCalculator(mPowerProfile));
- mPowerCalculators.add(new PhonePowerCalculator(mPowerProfile));
mPowerCalculators.add(new ScreenPowerCalculator(mPowerProfile));
mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile));
mPowerCalculators.add(new IdlePowerCalculator(mPowerProfile));
@@ -228,7 +234,7 @@
}
}
- if (mPowerStatsExporterEnabled) {
+ if (mPowerStatsExporterEnabled.indexOfValue(true) >= 0) {
mPowerStatsExporter.exportAggregatedPowerStats(batteryUsageStatsBuilder,
monotonicStartTime, monotonicEndTime);
}
@@ -393,7 +399,10 @@
return builder.build();
}
- public void setPowerStatsExporterEnabled(boolean enabled) {
- mPowerStatsExporterEnabled = enabled;
+ /**
+ * Specify whether PowerStats based attribution is supported for the specified component.
+ */
+ public void setPowerStatsExporterEnabled(int powerComponentId, boolean enabled) {
+ mPowerStatsExporterEnabled.put(powerComponentId, enabled);
}
}
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
index 1af1271..b1b2cc9 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
@@ -16,14 +16,11 @@
package com.android.server.power.stats;
-import android.hardware.power.stats.EnergyConsumer;
-import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryConsumer;
import android.os.Handler;
import android.os.PersistableBundle;
import android.os.Process;
-import android.power.PowerStatsInternal;
import android.util.Slog;
import android.util.SparseArray;
@@ -34,20 +31,11 @@
import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.PowerStats;
-import com.android.server.LocalServices;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Comparator;
-import java.util.List;
import java.util.Locale;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
import java.util.function.IntSupplier;
-import java.util.function.Supplier;
/**
* Collects snapshots of power-related system statistics.
@@ -63,213 +51,54 @@
private static final int DEFAULT_CPU_POWER_BRACKETS_PER_ENERGY_CONSUMER = 2;
private static final long POWER_STATS_ENERGY_CONSUMERS_TIMEOUT = 20000;
+ interface Injector {
+ Handler getHandler();
+ Clock getClock();
+ PowerStatsUidResolver getUidResolver();
+ CpuScalingPolicies getCpuScalingPolicies();
+ PowerProfile getPowerProfile();
+ KernelCpuStatsReader getKernelCpuStatsReader();
+ ConsumedEnergyRetriever getConsumedEnergyRetriever();
+ IntSupplier getVoltageSupplier();
+
+ default int getDefaultCpuPowerBrackets() {
+ return DEFAULT_CPU_POWER_BRACKETS;
+ }
+
+ default int getDefaultCpuPowerBracketsPerEnergyConsumer() {
+ return DEFAULT_CPU_POWER_BRACKETS_PER_ENERGY_CONSUMER;
+ }
+ }
+
+ private final Injector mInjector;
+
private boolean mIsInitialized;
- private final CpuScalingPolicies mCpuScalingPolicies;
- private final PowerProfile mPowerProfile;
- private final KernelCpuStatsReader mKernelCpuStatsReader;
- private final PowerStatsUidResolver mUidResolver;
- private final Supplier<PowerStatsInternal> mPowerStatsSupplier;
- private final IntSupplier mVoltageSupplier;
- private final int mDefaultCpuPowerBrackets;
- private final int mDefaultCpuPowerBracketsPerEnergyConsumer;
+ private CpuScalingPolicies mCpuScalingPolicies;
+ private PowerProfile mPowerProfile;
+ private KernelCpuStatsReader mKernelCpuStatsReader;
+ private PowerStatsUidResolver mUidResolver;
+ private ConsumedEnergyRetriever mConsumedEnergyRetriever;
+ private IntSupplier mVoltageSupplier;
+ private int mDefaultCpuPowerBrackets;
+ private int mDefaultCpuPowerBracketsPerEnergyConsumer;
private long[] mCpuTimeByScalingStep;
private long[] mTempCpuTimeByScalingStep;
private long[] mTempUidStats;
private final SparseArray<UidStats> mUidStats = new SparseArray<>();
private boolean mIsPerUidTimeInStateSupported;
- private PowerStatsInternal mPowerStatsInternal;
private int[] mCpuEnergyConsumerIds = new int[0];
private PowerStats.Descriptor mPowerStatsDescriptor;
// Reusable instance
private PowerStats mCpuPowerStats;
- private CpuStatsArrayLayout mLayout;
+ private CpuPowerStatsLayout mLayout;
private long mLastUpdateTimestampNanos;
private long mLastUpdateUptimeMillis;
private int mLastVoltageMv;
private long[] mLastConsumedEnergyUws;
- /**
- * Captures the positions and lengths of sections of the stats array, such as time-in-state,
- * power usage estimates etc.
- */
- public static class CpuStatsArrayLayout extends StatsArrayLayout {
- private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION = "dt";
- private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT = "dtc";
- private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION = "dc";
- private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT = "dcc";
- private static final String EXTRA_UID_BRACKETS_POSITION = "ub";
- private static final String EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET = "us";
-
- private int mDeviceCpuTimeByScalingStepPosition;
- private int mDeviceCpuTimeByScalingStepCount;
- private int mDeviceCpuTimeByClusterPosition;
- private int mDeviceCpuTimeByClusterCount;
-
- private int mUidPowerBracketsPosition;
- private int mUidPowerBracketCount;
-
- private int[] mScalingStepToPowerBracketMap;
-
- /**
- * Declare that the stats array has a section capturing CPU time per scaling step
- */
- public void addDeviceSectionCpuTimeByScalingStep(int scalingStepCount) {
- mDeviceCpuTimeByScalingStepPosition = addDeviceSection(scalingStepCount);
- mDeviceCpuTimeByScalingStepCount = scalingStepCount;
- }
-
- public int getCpuScalingStepCount() {
- return mDeviceCpuTimeByScalingStepCount;
- }
-
- /**
- * Saves the time duration in the <code>stats</code> element
- * corresponding to the CPU scaling <code>state</code>.
- */
- public void setTimeByScalingStep(long[] stats, int step, long value) {
- stats[mDeviceCpuTimeByScalingStepPosition + step] = value;
- }
-
- /**
- * Extracts the time duration from the <code>stats</code> element
- * corresponding to the CPU scaling <code>step</code>.
- */
- public long getTimeByScalingStep(long[] stats, int step) {
- return stats[mDeviceCpuTimeByScalingStepPosition + step];
- }
-
- /**
- * Declare that the stats array has a section capturing CPU time in each cluster
- */
- public void addDeviceSectionCpuTimeByCluster(int clusterCount) {
- mDeviceCpuTimeByClusterPosition = addDeviceSection(clusterCount);
- mDeviceCpuTimeByClusterCount = clusterCount;
- }
-
- public int getCpuClusterCount() {
- return mDeviceCpuTimeByClusterCount;
- }
-
- /**
- * Saves the time duration in the <code>stats</code> element
- * corresponding to the CPU <code>cluster</code>.
- */
- public void setTimeByCluster(long[] stats, int cluster, long value) {
- stats[mDeviceCpuTimeByClusterPosition + cluster] = value;
- }
-
- /**
- * Extracts the time duration from the <code>stats</code> element
- * corresponding to the CPU <code>cluster</code>.
- */
- public long getTimeByCluster(long[] stats, int cluster) {
- return stats[mDeviceCpuTimeByClusterPosition + cluster];
- }
-
- /**
- * Declare that the UID stats array has a section capturing CPU time per power bracket.
- */
- public void addUidSectionCpuTimeByPowerBracket(int[] scalingStepToPowerBracketMap) {
- mScalingStepToPowerBracketMap = scalingStepToPowerBracketMap;
- updatePowerBracketCount();
- mUidPowerBracketsPosition = addUidSection(mUidPowerBracketCount);
- }
-
- private void updatePowerBracketCount() {
- mUidPowerBracketCount = 1;
- for (int bracket : mScalingStepToPowerBracketMap) {
- if (bracket >= mUidPowerBracketCount) {
- mUidPowerBracketCount = bracket + 1;
- }
- }
- }
-
- public int[] getScalingStepToPowerBracketMap() {
- return mScalingStepToPowerBracketMap;
- }
-
- public int getCpuPowerBracketCount() {
- return mUidPowerBracketCount;
- }
-
- /**
- * Saves time in <code>bracket</code> in the corresponding section of <code>stats</code>.
- */
- public void setUidTimeByPowerBracket(long[] stats, int bracket, long value) {
- stats[mUidPowerBracketsPosition + bracket] = value;
- }
-
- /**
- * Extracts the time in <code>bracket</code> from a UID stats array.
- */
- public long getUidTimeByPowerBracket(long[] stats, int bracket) {
- return stats[mUidPowerBracketsPosition + bracket];
- }
-
- /**
- * Copies the elements of the stats array layout into <code>extras</code>
- */
- public void toExtras(PersistableBundle extras) {
- super.toExtras(extras);
- extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION,
- mDeviceCpuTimeByScalingStepPosition);
- extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT,
- mDeviceCpuTimeByScalingStepCount);
- extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION,
- mDeviceCpuTimeByClusterPosition);
- extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT,
- mDeviceCpuTimeByClusterCount);
- extras.putInt(EXTRA_UID_BRACKETS_POSITION, mUidPowerBracketsPosition);
- putIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET,
- mScalingStepToPowerBracketMap);
- }
-
- /**
- * Retrieves elements of the stats array layout from <code>extras</code>
- */
- public void fromExtras(PersistableBundle extras) {
- super.fromExtras(extras);
- mDeviceCpuTimeByScalingStepPosition =
- extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION);
- mDeviceCpuTimeByScalingStepCount =
- extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT);
- mDeviceCpuTimeByClusterPosition =
- extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION);
- mDeviceCpuTimeByClusterCount =
- extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT);
- mUidPowerBracketsPosition = extras.getInt(EXTRA_UID_BRACKETS_POSITION);
- mScalingStepToPowerBracketMap =
- getIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET);
- if (mScalingStepToPowerBracketMap == null) {
- mScalingStepToPowerBracketMap = new int[mDeviceCpuTimeByScalingStepCount];
- }
- updatePowerBracketCount();
- }
- }
-
- public CpuPowerStatsCollector(CpuScalingPolicies cpuScalingPolicies, PowerProfile powerProfile,
- PowerStatsUidResolver uidResolver, IntSupplier voltageSupplier, Handler handler,
- long throttlePeriodMs) {
- this(cpuScalingPolicies, powerProfile, handler, new KernelCpuStatsReader(), uidResolver,
- () -> LocalServices.getService(PowerStatsInternal.class), voltageSupplier,
- throttlePeriodMs, Clock.SYSTEM_CLOCK, DEFAULT_CPU_POWER_BRACKETS,
- DEFAULT_CPU_POWER_BRACKETS_PER_ENERGY_CONSUMER);
- }
-
- public CpuPowerStatsCollector(CpuScalingPolicies cpuScalingPolicies, PowerProfile powerProfile,
- Handler handler, KernelCpuStatsReader kernelCpuStatsReader,
- PowerStatsUidResolver uidResolver, Supplier<PowerStatsInternal> powerStatsSupplier,
- IntSupplier voltageSupplier, long throttlePeriodMs, Clock clock,
- int defaultCpuPowerBrackets, int defaultCpuPowerBracketsPerEnergyConsumer) {
- super(handler, throttlePeriodMs, clock);
- mCpuScalingPolicies = cpuScalingPolicies;
- mPowerProfile = powerProfile;
- mKernelCpuStatsReader = kernelCpuStatsReader;
- mUidResolver = uidResolver;
- mPowerStatsSupplier = powerStatsSupplier;
- mVoltageSupplier = voltageSupplier;
- mDefaultCpuPowerBrackets = defaultCpuPowerBrackets;
- mDefaultCpuPowerBracketsPerEnergyConsumer = defaultCpuPowerBracketsPerEnergyConsumer;
+ public CpuPowerStatsCollector(Injector injector, long throttlePeriodMs) {
+ super(injector.getHandler(), throttlePeriodMs, injector.getClock());
+ mInjector = injector;
}
private boolean ensureInitialized() {
@@ -281,19 +110,28 @@
return false;
}
- mIsPerUidTimeInStateSupported = mKernelCpuStatsReader.nativeIsSupportedFeature();
- mPowerStatsInternal = mPowerStatsSupplier.get();
+ mCpuScalingPolicies = mInjector.getCpuScalingPolicies();
+ mPowerProfile = mInjector.getPowerProfile();
+ mKernelCpuStatsReader = mInjector.getKernelCpuStatsReader();
+ mUidResolver = mInjector.getUidResolver();
+ mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
+ mVoltageSupplier = mInjector.getVoltageSupplier();
+ mDefaultCpuPowerBrackets = mInjector.getDefaultCpuPowerBrackets();
+ mDefaultCpuPowerBracketsPerEnergyConsumer =
+ mInjector.getDefaultCpuPowerBracketsPerEnergyConsumer();
- if (mPowerStatsInternal != null) {
- readCpuEnergyConsumerIds();
- }
+ mIsPerUidTimeInStateSupported = mKernelCpuStatsReader.isSupportedFeature();
+ mCpuEnergyConsumerIds =
+ mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.CPU_CLUSTER);
+ mLastConsumedEnergyUws = new long[mCpuEnergyConsumerIds.length];
+ Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);
int cpuScalingStepCount = mCpuScalingPolicies.getScalingStepCount();
mCpuTimeByScalingStep = new long[cpuScalingStepCount];
mTempCpuTimeByScalingStep = new long[cpuScalingStepCount];
int[] scalingStepToPowerBracketMap = initPowerBrackets();
- mLayout = new CpuStatsArrayLayout();
+ mLayout = new CpuPowerStatsLayout();
mLayout.addDeviceSectionCpuTimeByScalingStep(cpuScalingStepCount);
mLayout.addDeviceSectionCpuTimeByCluster(mCpuScalingPolicies.getPolicies().length);
mLayout.addDeviceSectionUsageDuration();
@@ -306,7 +144,8 @@
mLayout.toExtras(extras);
mPowerStatsDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU,
- mLayout.getDeviceStatsArrayLength(), mLayout.getUidStatsArrayLength(), extras);
+ mLayout.getDeviceStatsArrayLength(), /* stateLabels */null,
+ /* stateStatsArrayLength */ 0, mLayout.getUidStatsArrayLength(), extras);
mCpuPowerStats = new PowerStats(mPowerStatsDescriptor);
mTempUidStats = new long[mLayout.getCpuPowerBracketCount()];
@@ -315,32 +154,6 @@
return true;
}
- private void readCpuEnergyConsumerIds() {
- EnergyConsumer[] energyConsumerInfo = mPowerStatsInternal.getEnergyConsumerInfo();
- if (energyConsumerInfo == null) {
- return;
- }
-
- List<EnergyConsumer> cpuEnergyConsumers = new ArrayList<>();
- for (EnergyConsumer energyConsumer : energyConsumerInfo) {
- if (energyConsumer.type == EnergyConsumerType.CPU_CLUSTER) {
- cpuEnergyConsumers.add(energyConsumer);
- }
- }
- if (cpuEnergyConsumers.isEmpty()) {
- return;
- }
-
- cpuEnergyConsumers.sort(Comparator.comparing(c -> c.ordinal));
-
- mCpuEnergyConsumerIds = new int[cpuEnergyConsumers.size()];
- for (int i = 0; i < mCpuEnergyConsumerIds.length; i++) {
- mCpuEnergyConsumerIds[i] = cpuEnergyConsumers.get(i).id;
- }
- mLastConsumedEnergyUws = new long[cpuEnergyConsumers.size()];
- Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);
- }
-
private int[] initPowerBrackets() {
if (mPowerProfile.getCpuPowerBracketCount() != PowerProfile.POWER_BRACKETS_UNSPECIFIED) {
return initPowerBracketsFromPowerProfile();
@@ -372,6 +185,7 @@
return stepToBracketMap;
}
+
private int[] initPowerBracketsByCluster(int defaultBracketCountPerCluster) {
int[] stepToBracketMap = new int[mCpuScalingPolicies.getScalingStepCount()];
int index = 0;
@@ -531,7 +345,7 @@
mCpuPowerStats.uidStats.clear();
// TODO(b/305120724): additionally retrieve time-in-cluster for each CPU cluster
- long newTimestampNanos = mKernelCpuStatsReader.nativeReadCpuStats(this::processUidStats,
+ long newTimestampNanos = mKernelCpuStatsReader.readCpuStats(this::processUidStats,
mLayout.getScalingStepToPowerBracketMap(), mLastUpdateTimestampNanos,
mTempCpuTimeByScalingStep, mTempUidStats);
for (int step = mLayout.getCpuScalingStepCount() - 1; step >= 0; step--) {
@@ -571,35 +385,20 @@
int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
mLastVoltageMv = voltageMv;
- CompletableFuture<EnergyConsumerResult[]> future =
- mPowerStatsInternal.getEnergyConsumedAsync(mCpuEnergyConsumerIds);
- EnergyConsumerResult[] results = null;
- try {
- results = future.get(
- POWER_STATS_ENERGY_CONSUMERS_TIMEOUT, TimeUnit.MILLISECONDS);
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- Slog.e(TAG, "Could not obtain energy consumers from PowerStatsService", e);
- }
- if (results == null) {
+ long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mCpuEnergyConsumerIds);
+ if (energyUws == null) {
return;
}
- for (int i = 0; i < mCpuEnergyConsumerIds.length; i++) {
- int id = mCpuEnergyConsumerIds[i];
- for (EnergyConsumerResult result : results) {
- if (result.id == id) {
- long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED
- ? result.energyUWs - mLastConsumedEnergyUws[i] : 0;
- if (energyDelta < 0) {
- // Likely, restart of powerstats HAL
- energyDelta = 0;
- }
- mLayout.setConsumedEnergy(mCpuPowerStats.stats, i,
- uJtoUc(energyDelta, averageVoltage));
- mLastConsumedEnergyUws[i] = result.energyUWs;
- break;
- }
+ for (int i = energyUws.length - 1; i >= 0; i--) {
+ long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED
+ ? energyUws[i] - mLastConsumedEnergyUws[i] : 0;
+ if (energyDelta < 0) {
+ // Likely, restart of powerstats HAL
+ energyDelta = 0;
}
+ mLayout.setConsumedEnergy(mCpuPowerStats.stats, i, uJtoUc(energyDelta, averageVoltage));
+ mLastConsumedEnergyUws[i] = energyUws[i];
}
}
@@ -652,6 +451,17 @@
* Native class that retrieves CPU stats from the kernel.
*/
public static class KernelCpuStatsReader {
+ protected boolean isSupportedFeature() {
+ return nativeIsSupportedFeature();
+ }
+
+ protected long readCpuStats(KernelCpuStatsCallback callback,
+ int[] scalingStepToPowerBracketMap, long lastUpdateTimestampNanos,
+ long[] outCpuTimeByScalingStep, long[] tempForUidStats) {
+ return nativeReadCpuStats(callback, scalingStepToPowerBracketMap,
+ lastUpdateTimestampNanos, outCpuTimeByScalingStep, tempForUidStats);
+ }
+
protected native boolean nativeIsSupportedFeature();
protected native long nativeReadCpuStats(KernelCpuStatsCallback callback,
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java
new file mode 100644
index 0000000..1bcb2c4
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2024 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.power.stats;
+
+import android.os.PersistableBundle;
+
+/**
+ * Captures the positions and lengths of sections of the stats array, such as time-in-state,
+ * power usage estimates etc.
+ */
+public class CpuPowerStatsLayout extends PowerStatsLayout {
+ private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION = "dt";
+ private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT = "dtc";
+ private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION = "dc";
+ private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT = "dcc";
+ private static final String EXTRA_UID_BRACKETS_POSITION = "ub";
+ private static final String EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET = "us";
+
+ private int mDeviceCpuTimeByScalingStepPosition;
+ private int mDeviceCpuTimeByScalingStepCount;
+ private int mDeviceCpuTimeByClusterPosition;
+ private int mDeviceCpuTimeByClusterCount;
+
+ private int mUidPowerBracketsPosition;
+ private int mUidPowerBracketCount;
+
+ private int[] mScalingStepToPowerBracketMap;
+
+ /**
+ * Declare that the stats array has a section capturing CPU time per scaling step
+ */
+ public void addDeviceSectionCpuTimeByScalingStep(int scalingStepCount) {
+ mDeviceCpuTimeByScalingStepPosition = addDeviceSection(scalingStepCount);
+ mDeviceCpuTimeByScalingStepCount = scalingStepCount;
+ }
+
+ public int getCpuScalingStepCount() {
+ return mDeviceCpuTimeByScalingStepCount;
+ }
+
+ /**
+ * Saves the time duration in the <code>stats</code> element
+ * corresponding to the CPU scaling <code>state</code>.
+ */
+ public void setTimeByScalingStep(long[] stats, int step, long value) {
+ stats[mDeviceCpuTimeByScalingStepPosition + step] = value;
+ }
+
+ /**
+ * Extracts the time duration from the <code>stats</code> element
+ * corresponding to the CPU scaling <code>step</code>.
+ */
+ public long getTimeByScalingStep(long[] stats, int step) {
+ return stats[mDeviceCpuTimeByScalingStepPosition + step];
+ }
+
+ /**
+ * Declare that the stats array has a section capturing CPU time in each cluster
+ */
+ public void addDeviceSectionCpuTimeByCluster(int clusterCount) {
+ mDeviceCpuTimeByClusterPosition = addDeviceSection(clusterCount);
+ mDeviceCpuTimeByClusterCount = clusterCount;
+ }
+
+ public int getCpuClusterCount() {
+ return mDeviceCpuTimeByClusterCount;
+ }
+
+ /**
+ * Saves the time duration in the <code>stats</code> element
+ * corresponding to the CPU <code>cluster</code>.
+ */
+ public void setTimeByCluster(long[] stats, int cluster, long value) {
+ stats[mDeviceCpuTimeByClusterPosition + cluster] = value;
+ }
+
+ /**
+ * Extracts the time duration from the <code>stats</code> element
+ * corresponding to the CPU <code>cluster</code>.
+ */
+ public long getTimeByCluster(long[] stats, int cluster) {
+ return stats[mDeviceCpuTimeByClusterPosition + cluster];
+ }
+
+ /**
+ * Declare that the UID stats array has a section capturing CPU time per power bracket.
+ */
+ public void addUidSectionCpuTimeByPowerBracket(int[] scalingStepToPowerBracketMap) {
+ mScalingStepToPowerBracketMap = scalingStepToPowerBracketMap;
+ updatePowerBracketCount();
+ mUidPowerBracketsPosition = addUidSection(mUidPowerBracketCount);
+ }
+
+ private void updatePowerBracketCount() {
+ mUidPowerBracketCount = 1;
+ for (int bracket : mScalingStepToPowerBracketMap) {
+ if (bracket >= mUidPowerBracketCount) {
+ mUidPowerBracketCount = bracket + 1;
+ }
+ }
+ }
+
+ public int[] getScalingStepToPowerBracketMap() {
+ return mScalingStepToPowerBracketMap;
+ }
+
+ public int getCpuPowerBracketCount() {
+ return mUidPowerBracketCount;
+ }
+
+ /**
+ * Saves time in <code>bracket</code> in the corresponding section of <code>stats</code>.
+ */
+ public void setUidTimeByPowerBracket(long[] stats, int bracket, long value) {
+ stats[mUidPowerBracketsPosition + bracket] = value;
+ }
+
+ /**
+ * Extracts the time in <code>bracket</code> from a UID stats array.
+ */
+ public long getUidTimeByPowerBracket(long[] stats, int bracket) {
+ return stats[mUidPowerBracketsPosition + bracket];
+ }
+
+ /**
+ * Copies the elements of the stats array layout into <code>extras</code>
+ */
+ public void toExtras(PersistableBundle extras) {
+ super.toExtras(extras);
+ extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION,
+ mDeviceCpuTimeByScalingStepPosition);
+ extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT,
+ mDeviceCpuTimeByScalingStepCount);
+ extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION,
+ mDeviceCpuTimeByClusterPosition);
+ extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT,
+ mDeviceCpuTimeByClusterCount);
+ extras.putInt(EXTRA_UID_BRACKETS_POSITION, mUidPowerBracketsPosition);
+ putIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET,
+ mScalingStepToPowerBracketMap);
+ }
+
+ /**
+ * Retrieves elements of the stats array layout from <code>extras</code>
+ */
+ public void fromExtras(PersistableBundle extras) {
+ super.fromExtras(extras);
+ mDeviceCpuTimeByScalingStepPosition =
+ extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION);
+ mDeviceCpuTimeByScalingStepCount =
+ extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT);
+ mDeviceCpuTimeByClusterPosition =
+ extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION);
+ mDeviceCpuTimeByClusterCount =
+ extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT);
+ mUidPowerBracketsPosition = extras.getInt(EXTRA_UID_BRACKETS_POSITION);
+ mScalingStepToPowerBracketMap =
+ getIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET);
+ if (mScalingStepToPowerBracketMap == null) {
+ mScalingStepToPowerBracketMap = new int[mDeviceCpuTimeByScalingStepCount];
+ }
+ updatePowerBracketCount();
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java
similarity index 97%
rename from services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java
rename to services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java
index ed9414f..c34b8a8 100644
--- a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java
@@ -29,8 +29,8 @@
import java.util.List;
import java.util.concurrent.TimeUnit;
-public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProcessor {
- private static final String TAG = "CpuAggregatedPowerStatsProcessor";
+public class CpuPowerStatsProcessor extends PowerStatsProcessor {
+ private static final String TAG = "CpuPowerStatsProcessor";
private static final double HOUR_IN_MILLIS = TimeUnit.HOURS.toMillis(1);
private static final int UNKNOWN = -1;
@@ -64,7 +64,7 @@
private PowerStats.Descriptor mLastUsedDescriptor;
// Cached results of parsing of current PowerStats.Descriptor. Only refreshed when
// mLastUsedDescriptor changes
- private CpuPowerStatsCollector.CpuStatsArrayLayout mStatsLayout;
+ private CpuPowerStatsLayout mStatsLayout;
// Sequence of steps for power estimation and intermediate results.
private PowerEstimationPlan mPlan;
@@ -73,8 +73,7 @@
// Temp array for retrieval of UID power stats, to avoid repeated allocations
private long[] mTmpUidStatsArray;
- public CpuAggregatedPowerStatsProcessor(PowerProfile powerProfile,
- CpuScalingPolicies scalingPolicies) {
+ public CpuPowerStatsProcessor(PowerProfile powerProfile, CpuScalingPolicies scalingPolicies) {
mCpuScalingPolicies = scalingPolicies;
mCpuScalingStepCount = scalingPolicies.getScalingStepCount();
mScalingStepToCluster = new int[mCpuScalingStepCount];
@@ -106,7 +105,7 @@
}
mLastUsedDescriptor = descriptor;
- mStatsLayout = new CpuPowerStatsCollector.CpuStatsArrayLayout();
+ mStatsLayout = new CpuPowerStatsLayout();
mStatsLayout.fromExtras(descriptor.extras);
mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
@@ -527,6 +526,12 @@
}
@Override
+ String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) {
+ // Unsupported for this power component
+ return null;
+ }
+
+ @Override
public String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
unpackPowerStatsDescriptor(descriptor);
StringBuilder sb = new StringBuilder();
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java
index 9ea143e..c01363a9 100644
--- a/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java
@@ -387,92 +387,14 @@
return consumptionMah;
}
- private static long buildModemPowerProfileKey(@ModemPowerProfile.ModemDrainType int drainType,
- @BatteryStats.RadioAccessTechnology int rat, @ServiceState.FrequencyRange int freqRange,
- int txLevel) {
- long key = PowerProfile.SUBSYSTEM_MODEM;
-
- // Attach Modem drain type to the key if specified.
- if (drainType != IGNORE) {
- key |= drainType;
- }
-
- // Attach RadioAccessTechnology to the key if specified.
- switch (rat) {
- case IGNORE:
- // do nothing
- break;
- case BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER:
- key |= ModemPowerProfile.MODEM_RAT_TYPE_DEFAULT;
- break;
- case BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE:
- key |= ModemPowerProfile.MODEM_RAT_TYPE_LTE;
- break;
- case BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR:
- key |= ModemPowerProfile.MODEM_RAT_TYPE_NR;
- break;
- default:
- Log.w(TAG, "Unexpected RadioAccessTechnology : " + rat);
- }
-
- // Attach NR Frequency Range to the key if specified.
- switch (freqRange) {
- case IGNORE:
- // do nothing
- break;
- case ServiceState.FREQUENCY_RANGE_UNKNOWN:
- key |= ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_DEFAULT;
- break;
- case ServiceState.FREQUENCY_RANGE_LOW:
- key |= ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_LOW;
- break;
- case ServiceState.FREQUENCY_RANGE_MID:
- key |= ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MID;
- break;
- case ServiceState.FREQUENCY_RANGE_HIGH:
- key |= ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_HIGH;
- break;
- case ServiceState.FREQUENCY_RANGE_MMWAVE:
- key |= ModemPowerProfile.MODEM_NR_FREQUENCY_RANGE_MMWAVE;
- break;
- default:
- Log.w(TAG, "Unexpected NR frequency range : " + freqRange);
- }
-
- // Attach transmission level to the key if specified.
- switch (txLevel) {
- case IGNORE:
- // do nothing
- break;
- case 0:
- key |= ModemPowerProfile.MODEM_TX_LEVEL_0;
- break;
- case 1:
- key |= ModemPowerProfile.MODEM_TX_LEVEL_1;
- break;
- case 2:
- key |= ModemPowerProfile.MODEM_TX_LEVEL_2;
- break;
- case 3:
- key |= ModemPowerProfile.MODEM_TX_LEVEL_3;
- break;
- case 4:
- key |= ModemPowerProfile.MODEM_TX_LEVEL_4;
- break;
- default:
- Log.w(TAG, "Unexpected transmission level : " + txLevel);
- }
- return key;
- }
-
/**
* Calculates active receive radio power consumption (in milliamp-hours) from the given state's
* duration.
*/
public double calcRxStatePowerMah(@BatteryStats.RadioAccessTechnology int rat,
@ServiceState.FrequencyRange int freqRange, long rxDurationMs) {
- final long rxKey = buildModemPowerProfileKey(ModemPowerProfile.MODEM_DRAIN_TYPE_RX, rat,
- freqRange, IGNORE);
+ final long rxKey = ModemPowerProfile.getAverageBatteryDrainKey(
+ ModemPowerProfile.MODEM_DRAIN_TYPE_RX, rat, freqRange, IGNORE);
final double drainRateMa = mPowerProfile.getAverageBatteryDrainOrDefaultMa(rxKey,
Double.NaN);
if (Double.isNaN(drainRateMa)) {
@@ -495,8 +417,8 @@
*/
public double calcTxStatePowerMah(@BatteryStats.RadioAccessTechnology int rat,
@ServiceState.FrequencyRange int freqRange, int txLevel, long txDurationMs) {
- final long txKey = buildModemPowerProfileKey(ModemPowerProfile.MODEM_DRAIN_TYPE_TX, rat,
- freqRange, txLevel);
+ final long txKey = ModemPowerProfile.getAverageBatteryDrainKey(
+ ModemPowerProfile.MODEM_DRAIN_TYPE_TX, rat, freqRange, txLevel);
final double drainRateMa = mPowerProfile.getAverageBatteryDrainOrDefaultMa(txKey,
Double.NaN);
if (Double.isNaN(drainRateMa)) {
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
new file mode 100644
index 0000000..8c154e4
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2024 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.power.stats;
+
+import android.content.pm.PackageManager;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.net.NetworkStats;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.OutcomeReceiver;
+import android.os.PersistableBundle;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.ModemActivityInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.Clock;
+import com.android.internal.os.PowerStats;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
+
+public class MobileRadioPowerStatsCollector extends PowerStatsCollector {
+ private static final String TAG = "MobileRadioPowerStatsCollector";
+
+ /**
+ * The soonest the Mobile Radio stats can be updated due to a mobile radio power state change
+ * after it was last updated.
+ */
+ @VisibleForTesting
+ protected static final long MOBILE_RADIO_POWER_STATE_UPDATE_FREQ_MS = 1000 * 60 * 10;
+
+ private static final long MODEM_ACTIVITY_REQUEST_TIMEOUT = 20000;
+
+ private static final long ENERGY_UNSPECIFIED = -1;
+
+ @VisibleForTesting
+ @AccessNetworkConstants.RadioAccessNetworkType
+ static final int[] NETWORK_TYPES = {
+ AccessNetworkConstants.AccessNetworkType.UNKNOWN,
+ AccessNetworkConstants.AccessNetworkType.GERAN,
+ AccessNetworkConstants.AccessNetworkType.UTRAN,
+ AccessNetworkConstants.AccessNetworkType.EUTRAN,
+ AccessNetworkConstants.AccessNetworkType.CDMA2000,
+ AccessNetworkConstants.AccessNetworkType.IWLAN,
+ AccessNetworkConstants.AccessNetworkType.NGRAN
+ };
+
+ interface Injector {
+ Handler getHandler();
+ Clock getClock();
+ PowerStatsUidResolver getUidResolver();
+ PackageManager getPackageManager();
+ ConsumedEnergyRetriever getConsumedEnergyRetriever();
+ IntSupplier getVoltageSupplier();
+ Supplier<NetworkStats> getMobileNetworkStatsSupplier();
+ TelephonyManager getTelephonyManager();
+ LongSupplier getCallDurationSupplier();
+ LongSupplier getPhoneSignalScanDurationSupplier();
+ }
+
+ private final Injector mInjector;
+
+ private MobileRadioPowerStatsLayout mLayout;
+ private boolean mIsInitialized;
+
+ private PowerStats mPowerStats;
+ private long[] mDeviceStats;
+ private PowerStatsUidResolver mPowerStatsUidResolver;
+ private volatile TelephonyManager mTelephonyManager;
+ private LongSupplier mCallDurationSupplier;
+ private LongSupplier mScanDurationSupplier;
+ private volatile Supplier<NetworkStats> mNetworkStatsSupplier;
+ private ConsumedEnergyRetriever mConsumedEnergyRetriever;
+ private IntSupplier mVoltageSupplier;
+ private int[] mEnergyConsumerIds = new int[0];
+ private long mLastUpdateTimestampMillis;
+ private ModemActivityInfo mLastModemActivityInfo;
+ private NetworkStats mLastNetworkStats;
+ private long[] mLastConsumedEnergyUws;
+ private int mLastVoltageMv;
+ private long mLastCallDuration;
+ private long mLastScanDuration;
+
+ public MobileRadioPowerStatsCollector(Injector injector, long throttlePeriodMs) {
+ super(injector.getHandler(), throttlePeriodMs, injector.getClock());
+ mInjector = injector;
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ if (enabled) {
+ PackageManager packageManager = mInjector.getPackageManager();
+ super.setEnabled(packageManager != null
+ && packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY));
+ } else {
+ super.setEnabled(false);
+ }
+ }
+
+ private boolean ensureInitialized() {
+ if (mIsInitialized) {
+ return true;
+ }
+
+ if (!isEnabled()) {
+ return false;
+ }
+
+ mPowerStatsUidResolver = mInjector.getUidResolver();
+ mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
+ mVoltageSupplier = mInjector.getVoltageSupplier();
+
+ mTelephonyManager = mInjector.getTelephonyManager();
+ mNetworkStatsSupplier = mInjector.getMobileNetworkStatsSupplier();
+ mCallDurationSupplier = mInjector.getCallDurationSupplier();
+ mScanDurationSupplier = mInjector.getPhoneSignalScanDurationSupplier();
+
+ mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds(
+ EnergyConsumerType.MOBILE_RADIO);
+ mLastConsumedEnergyUws = new long[mEnergyConsumerIds.length];
+ Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);
+
+ mLayout = new MobileRadioPowerStatsLayout();
+ mLayout.addDeviceMobileActivity();
+ mLayout.addDeviceSectionEnergyConsumers(mEnergyConsumerIds.length);
+ mLayout.addStateStats();
+ mLayout.addUidNetworkStats();
+ mLayout.addDeviceSectionUsageDuration();
+ mLayout.addDeviceSectionPowerEstimate();
+ mLayout.addUidSectionPowerEstimate();
+
+ SparseArray<String> stateLabels = new SparseArray<>();
+ for (int rat = 0; rat < BatteryStats.RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) {
+ final int freqCount = rat == BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR
+ ? ServiceState.FREQUENCY_RANGE_COUNT : 1;
+ for (int freq = 0; freq < freqCount; freq++) {
+ int stateKey = makeStateKey(rat, freq);
+ StringBuilder sb = new StringBuilder();
+ if (rat != BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER) {
+ sb.append(BatteryStats.RADIO_ACCESS_TECHNOLOGY_NAMES[rat]);
+ }
+ if (freq != ServiceState.FREQUENCY_RANGE_UNKNOWN) {
+ if (!sb.isEmpty()) {
+ sb.append(" ");
+ }
+ sb.append(ServiceState.frequencyRangeToString(freq));
+ }
+ stateLabels.put(stateKey, !sb.isEmpty() ? sb.toString() : "other");
+ }
+ }
+
+ PersistableBundle extras = new PersistableBundle();
+ mLayout.toExtras(extras);
+ PowerStats.Descriptor powerStatsDescriptor = new PowerStats.Descriptor(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, mLayout.getDeviceStatsArrayLength(),
+ stateLabels, mLayout.getStateStatsArrayLength(), mLayout.getUidStatsArrayLength(),
+ extras);
+ mPowerStats = new PowerStats(powerStatsDescriptor);
+ mDeviceStats = mPowerStats.stats;
+
+ mIsInitialized = true;
+ return true;
+ }
+
+ @Override
+ protected PowerStats collectStats() {
+ if (!ensureInitialized()) {
+ return null;
+ }
+
+ collectModemActivityInfo();
+
+ collectNetworkStats();
+
+ if (mEnergyConsumerIds.length != 0) {
+ collectEnergyConsumers();
+ }
+
+ if (mPowerStats.durationMs == 0) {
+ setTimestamp(mClock.elapsedRealtime());
+ }
+
+ return mPowerStats;
+ }
+
+ private void collectModemActivityInfo() {
+ if (mTelephonyManager == null) {
+ return;
+ }
+
+ CompletableFuture<ModemActivityInfo> immediateFuture = new CompletableFuture<>();
+ mTelephonyManager.requestModemActivityInfo(Runnable::run,
+ new OutcomeReceiver<>() {
+ @Override
+ public void onResult(ModemActivityInfo result) {
+ immediateFuture.complete(result);
+ }
+
+ @Override
+ public void onError(TelephonyManager.ModemActivityInfoException e) {
+ Slog.w(TAG, "error reading modem stats:" + e);
+ immediateFuture.complete(null);
+ }
+ });
+
+ ModemActivityInfo activityInfo;
+ try {
+ activityInfo = immediateFuture.get(MODEM_ACTIVITY_REQUEST_TIMEOUT,
+ TimeUnit.MILLISECONDS);
+ } catch (Exception e) {
+ Slog.e(TAG, "Cannot acquire ModemActivityInfo");
+ activityInfo = null;
+ }
+
+ ModemActivityInfo deltaInfo = mLastModemActivityInfo == null
+ ? (activityInfo == null ? null : activityInfo.getDelta(activityInfo))
+ : mLastModemActivityInfo.getDelta(activityInfo);
+
+ mLastModemActivityInfo = activityInfo;
+
+ if (deltaInfo == null) {
+ return;
+ }
+
+ setTimestamp(deltaInfo.getTimestampMillis());
+ mLayout.setDeviceSleepTime(mDeviceStats, deltaInfo.getSleepTimeMillis());
+ mLayout.setDeviceIdleTime(mDeviceStats, deltaInfo.getIdleTimeMillis());
+
+ long callDuration = mCallDurationSupplier.getAsLong();
+ if (callDuration >= mLastCallDuration) {
+ mLayout.setDeviceCallTime(mDeviceStats, callDuration - mLastCallDuration);
+ }
+ mLastCallDuration = callDuration;
+
+ long scanDuration = mScanDurationSupplier.getAsLong();
+ if (scanDuration >= mLastScanDuration) {
+ mLayout.setDeviceScanTime(mDeviceStats, scanDuration - mLastScanDuration);
+ }
+ mLastScanDuration = scanDuration;
+
+ SparseArray<long[]> stateStats = mPowerStats.stateStats;
+ stateStats.clear();
+
+ if (deltaInfo.getSpecificInfoLength() == 0) {
+ mLayout.addRxTxTimesForRat(stateStats,
+ AccessNetworkConstants.AccessNetworkType.UNKNOWN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN,
+ deltaInfo.getReceiveTimeMillis(),
+ deltaInfo.getTransmitTimeMillis());
+ } else {
+ for (int rat = 0; rat < NETWORK_TYPES.length; rat++) {
+ if (rat == AccessNetworkConstants.AccessNetworkType.NGRAN) {
+ for (int freq = 0; freq < ServiceState.FREQUENCY_RANGE_COUNT; freq++) {
+ mLayout.addRxTxTimesForRat(stateStats, rat, freq,
+ deltaInfo.getReceiveTimeMillis(rat, freq),
+ deltaInfo.getTransmitTimeMillis(rat, freq));
+ }
+ } else {
+ mLayout.addRxTxTimesForRat(stateStats, rat,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN,
+ deltaInfo.getReceiveTimeMillis(rat),
+ deltaInfo.getTransmitTimeMillis(rat));
+ }
+ }
+ }
+ }
+
+ private void collectNetworkStats() {
+ mPowerStats.uidStats.clear();
+
+ NetworkStats networkStats = mNetworkStatsSupplier.get();
+ if (networkStats == null) {
+ return;
+ }
+
+ List<BatteryStatsImpl.NetworkStatsDelta> delta =
+ BatteryStatsImpl.computeDelta(networkStats, mLastNetworkStats);
+ mLastNetworkStats = networkStats;
+ for (int i = delta.size() - 1; i >= 0; i--) {
+ BatteryStatsImpl.NetworkStatsDelta uidDelta = delta.get(i);
+ long rxBytes = uidDelta.getRxBytes();
+ long txBytes = uidDelta.getTxBytes();
+ long rxPackets = uidDelta.getRxPackets();
+ long txPackets = uidDelta.getTxPackets();
+ if (rxBytes == 0 && txBytes == 0 && rxPackets == 0 && txPackets == 0) {
+ continue;
+ }
+
+ int uid = mPowerStatsUidResolver.mapUid(uidDelta.getUid());
+ long[] stats = mPowerStats.uidStats.get(uid);
+ if (stats == null) {
+ stats = new long[mLayout.getUidStatsArrayLength()];
+ mPowerStats.uidStats.put(uid, stats);
+ mLayout.setUidRxBytes(stats, rxBytes);
+ mLayout.setUidTxBytes(stats, txBytes);
+ mLayout.setUidRxPackets(stats, rxPackets);
+ mLayout.setUidTxPackets(stats, txPackets);
+ } else {
+ mLayout.setUidRxBytes(stats, mLayout.getUidRxBytes(stats) + rxBytes);
+ mLayout.setUidTxBytes(stats, mLayout.getUidTxBytes(stats) + txBytes);
+ mLayout.setUidRxPackets(stats, mLayout.getUidRxPackets(stats) + rxPackets);
+ mLayout.setUidTxPackets(stats, mLayout.getUidTxPackets(stats) + txPackets);
+ }
+ }
+ }
+
+ private void collectEnergyConsumers() {
+ int voltageMv = mVoltageSupplier.getAsInt();
+ if (voltageMv <= 0) {
+ Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
+ + " mV) when querying energy consumers");
+ return;
+ }
+
+ int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
+ mLastVoltageMv = voltageMv;
+
+ long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mEnergyConsumerIds);
+ if (energyUws == null) {
+ return;
+ }
+
+ for (int i = energyUws.length - 1; i >= 0; i--) {
+ long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED
+ ? energyUws[i] - mLastConsumedEnergyUws[i] : 0;
+ if (energyDelta < 0) {
+ // Likely, restart of powerstats HAL
+ energyDelta = 0;
+ }
+ mLayout.setConsumedEnergy(mPowerStats.stats, i, uJtoUc(energyDelta, averageVoltage));
+ mLastConsumedEnergyUws[i] = energyUws[i];
+ }
+ }
+
+ static int makeStateKey(int rat, int freqRange) {
+ if (rat == BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR) {
+ return rat | (freqRange << 8);
+ } else {
+ return rat;
+ }
+ }
+
+ private void setTimestamp(long timestamp) {
+ mPowerStats.durationMs = Math.max(timestamp - mLastUpdateTimestampMillis, 0);
+ mLastUpdateTimestampMillis = timestamp;
+ }
+
+ @BatteryStats.RadioAccessTechnology
+ static int mapRadioAccessNetworkTypeToRadioAccessTechnology(
+ @AccessNetworkConstants.RadioAccessNetworkType int networkType) {
+ switch (networkType) {
+ case AccessNetworkConstants.AccessNetworkType.NGRAN:
+ return BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR;
+ case AccessNetworkConstants.AccessNetworkType.EUTRAN:
+ return BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE;
+ case AccessNetworkConstants.AccessNetworkType.UNKNOWN: //fallthrough
+ case AccessNetworkConstants.AccessNetworkType.GERAN: //fallthrough
+ case AccessNetworkConstants.AccessNetworkType.UTRAN: //fallthrough
+ case AccessNetworkConstants.AccessNetworkType.CDMA2000: //fallthrough
+ case AccessNetworkConstants.AccessNetworkType.IWLAN:
+ return BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER;
+ default:
+ Slog.w(TAG,
+ "Unhandled RadioAccessNetworkType (" + networkType + "), mapping to OTHER");
+ return BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java
new file mode 100644
index 0000000..81d7c2f
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2024 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.power.stats;
+
+import android.annotation.NonNull;
+import android.os.PersistableBundle;
+import android.telephony.ModemActivityInfo;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.os.PowerStats;
+
+/**
+ * Captures the positions and lengths of sections of the stats array, such as time-in-state,
+ * power usage estimates etc.
+ */
+class MobileRadioPowerStatsLayout extends PowerStatsLayout {
+ private static final String TAG = "MobileRadioPowerStatsLayout";
+ private static final String EXTRA_DEVICE_SLEEP_TIME_POSITION = "dt-sleep";
+ private static final String EXTRA_DEVICE_IDLE_TIME_POSITION = "dt-idle";
+ private static final String EXTRA_DEVICE_SCAN_TIME_POSITION = "dt-scan";
+ private static final String EXTRA_DEVICE_CALL_TIME_POSITION = "dt-call";
+ private static final String EXTRA_DEVICE_CALL_POWER_POSITION = "dp-call";
+ private static final String EXTRA_STATE_RX_TIME_POSITION = "srx";
+ private static final String EXTRA_STATE_TX_TIMES_POSITION = "stx";
+ private static final String EXTRA_STATE_TX_TIMES_COUNT = "stxc";
+ private static final String EXTRA_UID_RX_BYTES_POSITION = "urxb";
+ private static final String EXTRA_UID_TX_BYTES_POSITION = "utxb";
+ private static final String EXTRA_UID_RX_PACKETS_POSITION = "urxp";
+ private static final String EXTRA_UID_TX_PACKETS_POSITION = "utxp";
+
+ private int mDeviceSleepTimePosition;
+ private int mDeviceIdleTimePosition;
+ private int mDeviceScanTimePosition;
+ private int mDeviceCallTimePosition;
+ private int mDeviceCallPowerPosition;
+ private int mStateRxTimePosition;
+ private int mStateTxTimesPosition;
+ private int mStateTxTimesCount;
+ private int mUidRxBytesPosition;
+ private int mUidTxBytesPosition;
+ private int mUidRxPacketsPosition;
+ private int mUidTxPacketsPosition;
+
+ MobileRadioPowerStatsLayout() {
+ }
+
+ MobileRadioPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
+ super(descriptor);
+ }
+
+ void addDeviceMobileActivity() {
+ mDeviceSleepTimePosition = addDeviceSection(1);
+ mDeviceIdleTimePosition = addDeviceSection(1);
+ mDeviceScanTimePosition = addDeviceSection(1);
+ mDeviceCallTimePosition = addDeviceSection(1);
+ }
+
+ void addStateStats() {
+ mStateRxTimePosition = addStateSection(1);
+ mStateTxTimesCount = ModemActivityInfo.getNumTxPowerLevels();
+ mStateTxTimesPosition = addStateSection(mStateTxTimesCount);
+ }
+
+ void addUidNetworkStats() {
+ mUidRxBytesPosition = addUidSection(1);
+ mUidTxBytesPosition = addUidSection(1);
+ mUidRxPacketsPosition = addUidSection(1);
+ mUidTxPacketsPosition = addUidSection(1);
+ }
+
+ @Override
+ public void addDeviceSectionPowerEstimate() {
+ super.addDeviceSectionPowerEstimate();
+ mDeviceCallPowerPosition = addDeviceSection(1);
+ }
+
+ public void setDeviceSleepTime(long[] stats, long durationMillis) {
+ stats[mDeviceSleepTimePosition] = durationMillis;
+ }
+
+ public long getDeviceSleepTime(long[] stats) {
+ return stats[mDeviceSleepTimePosition];
+ }
+
+ public void setDeviceIdleTime(long[] stats, long durationMillis) {
+ stats[mDeviceIdleTimePosition] = durationMillis;
+ }
+
+ public long getDeviceIdleTime(long[] stats) {
+ return stats[mDeviceIdleTimePosition];
+ }
+
+ public void setDeviceScanTime(long[] stats, long durationMillis) {
+ stats[mDeviceScanTimePosition] = durationMillis;
+ }
+
+ public long getDeviceScanTime(long[] stats) {
+ return stats[mDeviceScanTimePosition];
+ }
+
+ public void setDeviceCallTime(long[] stats, long durationMillis) {
+ stats[mDeviceCallTimePosition] = durationMillis;
+ }
+
+ public long getDeviceCallTime(long[] stats) {
+ return stats[mDeviceCallTimePosition];
+ }
+
+ public void setDeviceCallPowerEstimate(long[] stats, double power) {
+ stats[mDeviceCallPowerPosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
+ }
+
+ public double getDeviceCallPowerEstimate(long[] stats) {
+ return stats[mDeviceCallPowerPosition] / MILLI_TO_NANO_MULTIPLIER;
+ }
+
+ public void setStateRxTime(long[] stats, long durationMillis) {
+ stats[mStateRxTimePosition] = durationMillis;
+ }
+
+ public long getStateRxTime(long[] stats) {
+ return stats[mStateRxTimePosition];
+ }
+
+ public void setStateTxTime(long[] stats, int level, int durationMillis) {
+ stats[mStateTxTimesPosition + level] = durationMillis;
+ }
+
+ public long getStateTxTime(long[] stats, int level) {
+ return stats[mStateTxTimesPosition + level];
+ }
+
+ public void setUidRxBytes(long[] stats, long count) {
+ stats[mUidRxBytesPosition] = count;
+ }
+
+ public long getUidRxBytes(long[] stats) {
+ return stats[mUidRxBytesPosition];
+ }
+
+ public void setUidTxBytes(long[] stats, long count) {
+ stats[mUidTxBytesPosition] = count;
+ }
+
+ public long getUidTxBytes(long[] stats) {
+ return stats[mUidTxBytesPosition];
+ }
+
+ public void setUidRxPackets(long[] stats, long count) {
+ stats[mUidRxPacketsPosition] = count;
+ }
+
+ public long getUidRxPackets(long[] stats) {
+ return stats[mUidRxPacketsPosition];
+ }
+
+ public void setUidTxPackets(long[] stats, long count) {
+ stats[mUidTxPacketsPosition] = count;
+ }
+
+ public long getUidTxPackets(long[] stats) {
+ return stats[mUidTxPacketsPosition];
+ }
+
+ /**
+ * Copies the elements of the stats array layout into <code>extras</code>
+ */
+ public void toExtras(PersistableBundle extras) {
+ super.toExtras(extras);
+ extras.putInt(EXTRA_DEVICE_SLEEP_TIME_POSITION, mDeviceSleepTimePosition);
+ extras.putInt(EXTRA_DEVICE_IDLE_TIME_POSITION, mDeviceIdleTimePosition);
+ extras.putInt(EXTRA_DEVICE_SCAN_TIME_POSITION, mDeviceScanTimePosition);
+ extras.putInt(EXTRA_DEVICE_CALL_TIME_POSITION, mDeviceCallTimePosition);
+ extras.putInt(EXTRA_DEVICE_CALL_POWER_POSITION, mDeviceCallPowerPosition);
+ extras.putInt(EXTRA_STATE_RX_TIME_POSITION, mStateRxTimePosition);
+ extras.putInt(EXTRA_STATE_TX_TIMES_POSITION, mStateTxTimesPosition);
+ extras.putInt(EXTRA_STATE_TX_TIMES_COUNT, mStateTxTimesCount);
+ extras.putInt(EXTRA_UID_RX_BYTES_POSITION, mUidRxBytesPosition);
+ extras.putInt(EXTRA_UID_TX_BYTES_POSITION, mUidTxBytesPosition);
+ extras.putInt(EXTRA_UID_RX_PACKETS_POSITION, mUidRxPacketsPosition);
+ extras.putInt(EXTRA_UID_TX_PACKETS_POSITION, mUidTxPacketsPosition);
+ }
+
+ /**
+ * Retrieves elements of the stats array layout from <code>extras</code>
+ */
+ public void fromExtras(PersistableBundle extras) {
+ super.fromExtras(extras);
+ mDeviceSleepTimePosition = extras.getInt(EXTRA_DEVICE_SLEEP_TIME_POSITION);
+ mDeviceIdleTimePosition = extras.getInt(EXTRA_DEVICE_IDLE_TIME_POSITION);
+ mDeviceScanTimePosition = extras.getInt(EXTRA_DEVICE_SCAN_TIME_POSITION);
+ mDeviceCallTimePosition = extras.getInt(EXTRA_DEVICE_CALL_TIME_POSITION);
+ mDeviceCallPowerPosition = extras.getInt(EXTRA_DEVICE_CALL_POWER_POSITION);
+ mStateRxTimePosition = extras.getInt(EXTRA_STATE_RX_TIME_POSITION);
+ mStateTxTimesPosition = extras.getInt(EXTRA_STATE_TX_TIMES_POSITION);
+ mStateTxTimesCount = extras.getInt(EXTRA_STATE_TX_TIMES_COUNT);
+ mUidRxBytesPosition = extras.getInt(EXTRA_UID_RX_BYTES_POSITION);
+ mUidTxBytesPosition = extras.getInt(EXTRA_UID_TX_BYTES_POSITION);
+ mUidRxPacketsPosition = extras.getInt(EXTRA_UID_RX_PACKETS_POSITION);
+ mUidTxPacketsPosition = extras.getInt(EXTRA_UID_TX_PACKETS_POSITION);
+ }
+
+ public void addRxTxTimesForRat(SparseArray<long[]> stateStats, int networkType, int freqRange,
+ long rxTime, int[] txTime) {
+ if (txTime.length != mStateTxTimesCount) {
+ Slog.wtf(TAG, "Invalid TX time array size: " + txTime.length);
+ return;
+ }
+
+ boolean nonZero = false;
+ if (rxTime != 0) {
+ nonZero = true;
+ } else {
+ for (int i = txTime.length - 1; i >= 0; i--) {
+ if (txTime[i] != 0) {
+ nonZero = true;
+ break;
+ }
+ }
+ }
+
+ if (!nonZero) {
+ return;
+ }
+
+ int rat = MobileRadioPowerStatsCollector.mapRadioAccessNetworkTypeToRadioAccessTechnology(
+ networkType);
+ int stateKey = MobileRadioPowerStatsCollector.makeStateKey(rat, freqRange);
+ long[] stats = stateStats.get(stateKey);
+ if (stats == null) {
+ stats = new long[getStateStatsArrayLength()];
+ stateStats.put(stateKey, stats);
+ }
+
+ stats[mStateRxTimePosition] += rxTime;
+ for (int i = mStateTxTimesCount - 1; i >= 0; i--) {
+ stats[mStateTxTimesPosition + i] += txTime[i];
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java
new file mode 100644
index 0000000..c97c64b
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2024 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.power.stats;
+
+import android.os.BatteryStats;
+import android.telephony.CellSignalStrength;
+import android.telephony.ModemActivityInfo;
+import android.telephony.ServiceState;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+import com.android.internal.power.ModemPowerProfile;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class MobileRadioPowerStatsProcessor extends PowerStatsProcessor {
+ private static final String TAG = "MobileRadioPowerStatsProcessor";
+ private static final boolean DEBUG = false;
+
+ private static final int NUM_SIGNAL_STRENGTH_LEVELS =
+ CellSignalStrength.getNumSignalStrengthLevels();
+ private static final int IGNORE = -1;
+
+ private final UsageBasedPowerEstimator mSleepPowerEstimator;
+ private final UsageBasedPowerEstimator mIdlePowerEstimator;
+ private final UsageBasedPowerEstimator mCallPowerEstimator;
+ private final UsageBasedPowerEstimator mScanPowerEstimator;
+
+ private static class RxTxPowerEstimators {
+ UsageBasedPowerEstimator mRxPowerEstimator;
+ UsageBasedPowerEstimator[] mTxPowerEstimators =
+ new UsageBasedPowerEstimator[ModemActivityInfo.getNumTxPowerLevels()];
+ }
+
+ private final SparseArray<RxTxPowerEstimators> mRxTxPowerEstimators = new SparseArray<>();
+
+ private PowerStats.Descriptor mLastUsedDescriptor;
+ private MobileRadioPowerStatsLayout mStatsLayout;
+ // Sequence of steps for power estimation and intermediate results.
+ private PowerEstimationPlan mPlan;
+
+ private long[] mTmpDeviceStatsArray;
+ private long[] mTmpStateStatsArray;
+ private long[] mTmpUidStatsArray;
+
+ public MobileRadioPowerStatsProcessor(PowerProfile powerProfile) {
+ final double sleepDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa(
+ PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_DRAIN_TYPE_SLEEP,
+ Double.NaN);
+ if (Double.isNaN(sleepDrainRateMa)) {
+ mSleepPowerEstimator = null;
+ } else {
+ mSleepPowerEstimator = new UsageBasedPowerEstimator(sleepDrainRateMa);
+ }
+
+ final double idleDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa(
+ PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_DRAIN_TYPE_IDLE,
+ Double.NaN);
+ if (Double.isNaN(idleDrainRateMa)) {
+ mIdlePowerEstimator = null;
+ } else {
+ mIdlePowerEstimator = new UsageBasedPowerEstimator(idleDrainRateMa);
+ }
+
+ // Instantiate legacy power estimators
+ double powerRadioActiveMa =
+ powerProfile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ACTIVE, Double.NaN);
+ if (Double.isNaN(powerRadioActiveMa)) {
+ double sum = 0;
+ sum += powerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX);
+ for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) {
+ sum += powerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
+ }
+ powerRadioActiveMa = sum / (NUM_SIGNAL_STRENGTH_LEVELS + 1);
+ }
+ mCallPowerEstimator = new UsageBasedPowerEstimator(powerRadioActiveMa);
+
+ mScanPowerEstimator = new UsageBasedPowerEstimator(
+ powerProfile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_SCANNING, 0));
+
+ for (int rat = 0; rat < BatteryStats.RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) {
+ final int freqCount = rat == BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR
+ ? ServiceState.FREQUENCY_RANGE_COUNT : 1;
+ for (int freqRange = 0; freqRange < freqCount; freqRange++) {
+ mRxTxPowerEstimators.put(
+ MobileRadioPowerStatsCollector.makeStateKey(rat, freqRange),
+ buildRxTxPowerEstimators(powerProfile, rat, freqRange));
+ }
+ }
+ }
+
+ private static RxTxPowerEstimators buildRxTxPowerEstimators(PowerProfile powerProfile, int rat,
+ int freqRange) {
+ RxTxPowerEstimators estimators = new RxTxPowerEstimators();
+ long rxKey = ModemPowerProfile.getAverageBatteryDrainKey(
+ ModemPowerProfile.MODEM_DRAIN_TYPE_RX, rat, freqRange, IGNORE);
+ double rxDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa(rxKey, Double.NaN);
+ if (Double.isNaN(rxDrainRateMa)) {
+ Log.w(TAG, "Unavailable Power Profile constant for key 0x"
+ + Long.toHexString(rxKey));
+ rxDrainRateMa = 0;
+ }
+ estimators.mRxPowerEstimator = new UsageBasedPowerEstimator(rxDrainRateMa);
+ for (int txLevel = 0; txLevel < ModemActivityInfo.getNumTxPowerLevels(); txLevel++) {
+ long txKey = ModemPowerProfile.getAverageBatteryDrainKey(
+ ModemPowerProfile.MODEM_DRAIN_TYPE_TX, rat, freqRange, txLevel);
+ double txDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa(txKey,
+ Double.NaN);
+ if (Double.isNaN(txDrainRateMa)) {
+ Log.w(TAG, "Unavailable Power Profile constant for key 0x"
+ + Long.toHexString(txKey));
+ txDrainRateMa = 0;
+ }
+ estimators.mTxPowerEstimators[txLevel] = new UsageBasedPowerEstimator(txDrainRateMa);
+ }
+ return estimators;
+ }
+
+ private static class Intermediates {
+ /**
+ * Number of received packets
+ */
+ public long rxPackets;
+ /**
+ * Number of transmitted packets
+ */
+ public long txPackets;
+ /**
+ * Estimated power for the RX state of the modem.
+ */
+ public double rxPower;
+ /**
+ * Estimated power for the TX state of the modem.
+ */
+ public double txPower;
+ /**
+ * Estimated power for IDLE, SLEEP and CELL-SCAN states of the modem.
+ */
+ public double inactivePower;
+ /**
+ * Estimated power for IDLE, SLEEP and CELL-SCAN states of the modem.
+ */
+ public double callPower;
+ /**
+ * Measured consumed energy from power monitoring hardware (micro-coulombs)
+ */
+ public long consumedEnergy;
+ }
+
+ @Override
+ void finish(PowerComponentAggregatedPowerStats stats) {
+ if (stats.getPowerStatsDescriptor() == null) {
+ return;
+ }
+
+ unpackPowerStatsDescriptor(stats.getPowerStatsDescriptor());
+
+ if (mPlan == null) {
+ mPlan = new PowerEstimationPlan(stats.getConfig());
+ }
+
+ for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+ DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
+ Intermediates intermediates = new Intermediates();
+ estimation.intermediates = intermediates;
+ computeDevicePowerEstimates(stats, estimation.stateValues, intermediates);
+ }
+
+ if (mStatsLayout.getEnergyConsumerCount() != 0) {
+ double ratio = computeEstimateAdjustmentRatioUsingConsumedEnergy();
+ if (ratio != 1) {
+ for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+ DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
+ adjustDevicePowerEstimates(stats, estimation.stateValues,
+ (Intermediates) estimation.intermediates, ratio);
+ }
+ }
+ }
+
+ combineDeviceStateEstimates();
+
+ ArrayList<Integer> uids = new ArrayList<>();
+ stats.collectUids(uids);
+ if (!uids.isEmpty()) {
+ for (int uid : uids) {
+ for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
+ computeUidRxTxTotals(stats, uid, mPlan.uidStateEstimates.get(i));
+ }
+ }
+
+ for (int uid : uids) {
+ for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
+ computeUidPowerEstimates(stats, uid, mPlan.uidStateEstimates.get(i));
+ }
+ }
+ }
+ mPlan.resetIntermediates();
+ }
+
+ private void unpackPowerStatsDescriptor(PowerStats.Descriptor descriptor) {
+ if (descriptor.equals(mLastUsedDescriptor)) {
+ return;
+ }
+
+ mLastUsedDescriptor = descriptor;
+ mStatsLayout = new MobileRadioPowerStatsLayout(descriptor);
+ mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
+ mTmpStateStatsArray = new long[descriptor.stateStatsArrayLength];
+ mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
+ }
+
+ /**
+ * Compute power estimates using the power profile.
+ */
+ private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
+ int[] deviceStates, Intermediates intermediates) {
+ if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStates)) {
+ return;
+ }
+
+ for (int i = mStatsLayout.getEnergyConsumerCount() - 1; i >= 0; i--) {
+ intermediates.consumedEnergy += mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, i);
+ }
+
+ if (mSleepPowerEstimator != null) {
+ intermediates.inactivePower += mSleepPowerEstimator.calculatePower(
+ mStatsLayout.getDeviceSleepTime(mTmpDeviceStatsArray));
+ }
+
+ if (mIdlePowerEstimator != null) {
+ intermediates.inactivePower += mIdlePowerEstimator.calculatePower(
+ mStatsLayout.getDeviceIdleTime(mTmpDeviceStatsArray));
+ }
+
+ if (mScanPowerEstimator != null) {
+ intermediates.inactivePower += mScanPowerEstimator.calculatePower(
+ mStatsLayout.getDeviceScanTime(mTmpDeviceStatsArray));
+ }
+
+ stats.forEachStateStatsKey(key -> {
+ RxTxPowerEstimators estimators = mRxTxPowerEstimators.get(key);
+ stats.getStateStats(mTmpStateStatsArray, key, deviceStates);
+ long rxTime = mStatsLayout.getStateRxTime(mTmpStateStatsArray);
+ intermediates.rxPower += estimators.mRxPowerEstimator.calculatePower(rxTime);
+ for (int txLevel = 0; txLevel < ModemActivityInfo.getNumTxPowerLevels(); txLevel++) {
+ long txTime = mStatsLayout.getStateTxTime(mTmpStateStatsArray, txLevel);
+ intermediates.txPower +=
+ estimators.mTxPowerEstimators[txLevel].calculatePower(txTime);
+ }
+ });
+
+ if (mCallPowerEstimator != null) {
+ intermediates.callPower = mCallPowerEstimator.calculatePower(
+ mStatsLayout.getDeviceCallTime(mTmpDeviceStatsArray));
+ }
+
+ mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
+ intermediates.rxPower + intermediates.txPower + intermediates.inactivePower);
+ mStatsLayout.setDeviceCallPowerEstimate(mTmpDeviceStatsArray, intermediates.callPower);
+ stats.setDeviceStats(deviceStates, mTmpDeviceStatsArray);
+ }
+
+ /**
+ * Compute an adjustment ratio using the total power estimated using the power profile
+ * and the total power measured by hardware.
+ */
+ private double computeEstimateAdjustmentRatioUsingConsumedEnergy() {
+ long totalConsumedEnergy = 0;
+ double totalPower = 0;
+
+ for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+ Intermediates intermediates =
+ (Intermediates) mPlan.deviceStateEstimations.get(i).intermediates;
+ totalPower += intermediates.rxPower + intermediates.txPower
+ + intermediates.inactivePower + intermediates.callPower;
+ totalConsumedEnergy += intermediates.consumedEnergy;
+ }
+
+ if (totalPower == 0) {
+ return 1;
+ }
+
+ return uCtoMah(totalConsumedEnergy) / totalPower;
+ }
+
+ /**
+ * Uniformly apply the same adjustment to all power estimates in order to ensure that the total
+ * estimated power matches the measured consumed power. We are not claiming that all
+ * averages captured in the power profile have to be off by the same percentage in reality.
+ */
+ private void adjustDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
+ int[] deviceStates, Intermediates intermediates, double ratio) {
+ intermediates.rxPower *= ratio;
+ intermediates.txPower *= ratio;
+ intermediates.inactivePower *= ratio;
+ intermediates.callPower *= ratio;
+
+ if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStates)) {
+ return;
+ }
+
+ mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
+ intermediates.rxPower + intermediates.txPower + intermediates.inactivePower);
+ mStatsLayout.setDeviceCallPowerEstimate(mTmpDeviceStatsArray, intermediates.callPower);
+ stats.setDeviceStats(deviceStates, mTmpDeviceStatsArray);
+ }
+
+ /**
+ * This step is effectively a no-op in the cases where we track the same states for
+ * the entire device and all UIDs (e.g. screen on/off, on-battery/on-charger etc). However,
+ * if the lists of tracked states are not the same, we need to combine some estimates
+ * before distributing them proportionally to UIDs.
+ */
+ private void combineDeviceStateEstimates() {
+ for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
+ CombinedDeviceStateEstimate cdse = mPlan.combinedDeviceStateEstimations.get(i);
+ Intermediates cdseIntermediates = new Intermediates();
+ cdse.intermediates = cdseIntermediates;
+ List<DeviceStateEstimation> deviceStateEstimations = cdse.deviceStateEstimations;
+ for (int j = deviceStateEstimations.size() - 1; j >= 0; j--) {
+ DeviceStateEstimation dse = deviceStateEstimations.get(j);
+ Intermediates intermediates = (Intermediates) dse.intermediates;
+ cdseIntermediates.rxPower += intermediates.rxPower;
+ cdseIntermediates.txPower += intermediates.txPower;
+ cdseIntermediates.inactivePower += intermediates.inactivePower;
+ cdseIntermediates.consumedEnergy += intermediates.consumedEnergy;
+ }
+ }
+ }
+
+ private void computeUidRxTxTotals(PowerComponentAggregatedPowerStats stats, int uid,
+ UidStateEstimate uidStateEstimate) {
+ Intermediates intermediates =
+ (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
+ for (UidStateProportionalEstimate proportionalEstimate :
+ uidStateEstimate.proportionalEstimates) {
+ if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) {
+ continue;
+ }
+
+ intermediates.rxPackets += mStatsLayout.getUidRxPackets(mTmpUidStatsArray);
+ intermediates.txPackets += mStatsLayout.getUidTxPackets(mTmpUidStatsArray);
+ }
+ }
+
+ private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats, int uid,
+ UidStateEstimate uidStateEstimate) {
+ Intermediates intermediates =
+ (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
+ for (UidStateProportionalEstimate proportionalEstimate :
+ uidStateEstimate.proportionalEstimates) {
+ if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) {
+ continue;
+ }
+
+ double power = 0;
+ if (intermediates.rxPackets != 0) {
+ power += intermediates.rxPower * mStatsLayout.getUidRxPackets(mTmpUidStatsArray)
+ / intermediates.rxPackets;
+ }
+ if (intermediates.txPackets != 0) {
+ power += intermediates.txPower * mStatsLayout.getUidTxPackets(mTmpUidStatsArray)
+ / intermediates.txPackets;
+ }
+
+ mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+ stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+
+ if (DEBUG) {
+ Slog.d(TAG, "UID: " + uid
+ + " states: " + Arrays.toString(proportionalEstimate.stateValues)
+ + " stats: " + Arrays.toString(mTmpUidStatsArray)
+ + " rx: " + mStatsLayout.getUidRxPackets(mTmpUidStatsArray)
+ + " rx-power: " + intermediates.rxPower
+ + " rx-packets: " + intermediates.rxPackets
+ + " tx: " + mStatsLayout.getUidTxPackets(mTmpUidStatsArray)
+ + " tx-power: " + intermediates.txPower
+ + " tx-packets: " + intermediates.txPackets
+ + " power: " + power);
+ }
+ }
+ }
+
+ @Override
+ String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
+ unpackPowerStatsDescriptor(descriptor);
+ return "idle: " + mStatsLayout.getDeviceIdleTime(stats)
+ + " sleep: " + mStatsLayout.getDeviceSleepTime(stats)
+ + " scan: " + mStatsLayout.getDeviceScanTime(stats)
+ + " power: " + mStatsLayout.getDevicePowerEstimate(stats);
+ }
+
+ @Override
+ String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) {
+ unpackPowerStatsDescriptor(descriptor);
+ StringBuilder sb = new StringBuilder();
+ sb.append(descriptor.getStateLabel(key));
+ sb.append(" rx: ").append(mStatsLayout.getStateRxTime(stats));
+ sb.append(" tx: ");
+ for (int txLevel = 0; txLevel < ModemActivityInfo.getNumTxPowerLevels(); txLevel++) {
+ if (txLevel != 0) {
+ sb.append(", ");
+ }
+ sb.append(mStatsLayout.getStateTxTime(stats, txLevel));
+ }
+ return sb.toString();
+ }
+
+ @Override
+ String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
+ unpackPowerStatsDescriptor(descriptor);
+ return "rx: " + mStatsLayout.getUidRxPackets(stats)
+ + " tx: " + mStatsLayout.getUidTxPackets(stats)
+ + " power: " + mStatsLayout.getUidPowerEstimate(stats);
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/MultiStateStats.java b/services/core/java/com/android/server/power/stats/MultiStateStats.java
index 9356950..6c4a2b6 100644
--- a/services/core/java/com/android/server/power/stats/MultiStateStats.java
+++ b/services/core/java/com/android/server/power/stats/MultiStateStats.java
@@ -288,6 +288,14 @@
}
/**
+ * Copies time-in-state and timestamps from the supplied prototype. Does not
+ * copy accumulated counts.
+ */
+ public void copyStatesFrom(MultiStateStats otherStats) {
+ mCounter.copyStatesFrom(otherStats.mCounter);
+ }
+
+ /**
* Updates the current composite state by changing one of the States supplied to the Factory
* constructor.
*
diff --git a/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java
new file mode 100644
index 0000000..62b653f
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2024 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.power.stats;
+
+import android.os.BatteryConsumer;
+import android.os.PersistableBundle;
+
+import com.android.internal.os.PowerStats;
+
+public class PhoneCallPowerStatsProcessor extends PowerStatsProcessor {
+ private final PowerStatsLayout mStatsLayout;
+ private final PowerStats.Descriptor mDescriptor;
+ private final long[] mTmpDeviceStats;
+ private PowerStats.Descriptor mMobileRadioStatsDescriptor;
+ private MobileRadioPowerStatsLayout mMobileRadioStatsLayout;
+ private long[] mTmpMobileRadioDeviceStats;
+
+ public PhoneCallPowerStatsProcessor() {
+ mStatsLayout = new PowerStatsLayout();
+ mStatsLayout.addDeviceSectionPowerEstimate();
+ PersistableBundle extras = new PersistableBundle();
+ mStatsLayout.toExtras(extras);
+ mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_PHONE,
+ mStatsLayout.getDeviceStatsArrayLength(), null, 0, 0, extras);
+ mTmpDeviceStats = new long[mDescriptor.statsArrayLength];
+ }
+
+ @Override
+ void finish(PowerComponentAggregatedPowerStats stats) {
+ stats.setPowerStatsDescriptor(mDescriptor);
+
+ PowerComponentAggregatedPowerStats mobileRadioStats =
+ stats.getAggregatedPowerStats().getPowerComponentStats(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO);
+ if (mobileRadioStats == null) {
+ return;
+ }
+
+ if (mMobileRadioStatsDescriptor == null) {
+ mMobileRadioStatsDescriptor = mobileRadioStats.getPowerStatsDescriptor();
+ if (mMobileRadioStatsDescriptor == null) {
+ return;
+ }
+
+ mMobileRadioStatsLayout =
+ new MobileRadioPowerStatsLayout(
+ mMobileRadioStatsDescriptor);
+ mTmpMobileRadioDeviceStats = new long[mMobileRadioStatsDescriptor.statsArrayLength];
+ }
+
+ MultiStateStats.States[] deviceStateConfig =
+ mobileRadioStats.getConfig().getDeviceStateConfig();
+
+ // Phone call power estimates have already been calculated by the mobile radio stats
+ // processor. All that remains to be done is copy the estimates over.
+ MultiStateStats.States.forEachTrackedStateCombination(deviceStateConfig,
+ states -> {
+ mobileRadioStats.getDeviceStats(mTmpMobileRadioDeviceStats, states);
+ double callPowerEstimate =
+ mMobileRadioStatsLayout.getDeviceCallPowerEstimate(
+ mTmpMobileRadioDeviceStats);
+ mStatsLayout.setDevicePowerEstimate(mTmpDeviceStats, callPowerEstimate);
+ stats.setDeviceStats(states, mTmpDeviceStats);
+ });
+ }
+
+ @Override
+ String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
+ return "power: " + mStatsLayout.getDevicePowerEstimate(stats);
+ }
+
+ @Override
+ String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) {
+ // Unsupported for this power component
+ return null;
+ }
+
+ @Override
+ String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
+ // Unsupported for this power component
+ return null;
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
index 1637022..6d58307 100644
--- a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.UserHandle;
import android.util.IndentingPrintWriter;
import android.util.SparseArray;
@@ -29,7 +30,10 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.function.IntConsumer;
/**
* Aggregated power stats for a specific power component (e.g. CPU, WiFi, etc). This class
@@ -41,22 +45,28 @@
static final String XML_TAG_POWER_COMPONENT = "power_component";
static final String XML_ATTR_ID = "id";
private static final String XML_TAG_DEVICE_STATS = "device-stats";
+ private static final String XML_TAG_STATE_STATS = "state-stats";
+ private static final String XML_ATTR_KEY = "key";
private static final String XML_TAG_UID_STATS = "uid-stats";
private static final String XML_ATTR_UID = "uid";
private static final long UNKNOWN = -1;
public final int powerComponentId;
- private final MultiStateStats.States[] mDeviceStateConfig;
- private final MultiStateStats.States[] mUidStateConfig;
+ @NonNull
+ private final AggregatedPowerStats mAggregatedPowerStats;
@NonNull
private final AggregatedPowerStatsConfig.PowerComponent mConfig;
+ private final MultiStateStats.States[] mDeviceStateConfig;
+ private final MultiStateStats.States[] mUidStateConfig;
private final int[] mDeviceStates;
private MultiStateStats.Factory mStatsFactory;
+ private MultiStateStats.Factory mStateStatsFactory;
private MultiStateStats.Factory mUidStatsFactory;
private PowerStats.Descriptor mPowerStatsDescriptor;
private long mPowerStatsTimestamp;
private MultiStateStats mDeviceStats;
+ private final SparseArray<MultiStateStats> mStateStats = new SparseArray<>();
private final SparseArray<UidStats> mUidStats = new SparseArray<>();
private static class UidStats {
@@ -64,7 +74,9 @@
public MultiStateStats stats;
}
- PowerComponentAggregatedPowerStats(AggregatedPowerStatsConfig.PowerComponent config) {
+ PowerComponentAggregatedPowerStats(@NonNull AggregatedPowerStats aggregatedPowerStats,
+ @NonNull AggregatedPowerStatsConfig.PowerComponent config) {
+ mAggregatedPowerStats = aggregatedPowerStats;
mConfig = config;
powerComponentId = config.getPowerComponentId();
mDeviceStateConfig = config.getDeviceStateConfig();
@@ -74,6 +86,11 @@
}
@NonNull
+ AggregatedPowerStats getAggregatedPowerStats() {
+ return mAggregatedPowerStats;
+ }
+
+ @NonNull
public AggregatedPowerStatsConfig.PowerComponent getConfig() {
return mConfig;
}
@@ -83,16 +100,25 @@
return mPowerStatsDescriptor;
}
- void setState(@AggregatedPowerStatsConfig.TrackedState int stateId, int state, long time) {
+ public void setPowerStatsDescriptor(PowerStats.Descriptor powerStatsDescriptor) {
+ mPowerStatsDescriptor = powerStatsDescriptor;
+ }
+
+ void setState(@AggregatedPowerStatsConfig.TrackedState int stateId, int state,
+ long timestampMs) {
if (mDeviceStats == null) {
- createDeviceStats();
+ createDeviceStats(timestampMs);
}
mDeviceStates[stateId] = state;
if (mDeviceStateConfig[stateId].isTracked()) {
if (mDeviceStats != null) {
- mDeviceStats.setState(stateId, state, time);
+ mDeviceStats.setState(stateId, state, timestampMs);
+ }
+ for (int i = mStateStats.size() - 1; i >= 0; i--) {
+ MultiStateStats stateStats = mStateStats.valueAt(i);
+ stateStats.setState(stateId, state, timestampMs);
}
}
@@ -100,36 +126,39 @@
for (int i = mUidStats.size() - 1; i >= 0; i--) {
PowerComponentAggregatedPowerStats.UidStats uidStats = mUidStats.valueAt(i);
if (uidStats.stats == null) {
- createUidStats(uidStats);
+ createUidStats(uidStats, timestampMs);
}
uidStats.states[stateId] = state;
if (uidStats.stats != null) {
- uidStats.stats.setState(stateId, state, time);
+ uidStats.stats.setState(stateId, state, timestampMs);
}
}
}
}
void setUidState(int uid, @AggregatedPowerStatsConfig.TrackedState int stateId, int state,
- long time) {
+ long timestampMs) {
if (!mUidStateConfig[stateId].isTracked()) {
return;
}
UidStats uidStats = getUidStats(uid);
if (uidStats.stats == null) {
- createUidStats(uidStats);
+ createUidStats(uidStats, timestampMs);
}
uidStats.states[stateId] = state;
if (uidStats.stats != null) {
- uidStats.stats.setState(stateId, state, time);
+ uidStats.stats.setState(stateId, state, timestampMs);
}
}
void setDeviceStats(@AggregatedPowerStatsConfig.TrackedState int[] states, long[] values) {
+ if (mDeviceStats == null) {
+ createDeviceStats(0);
+ }
mDeviceStats.setStats(states, values);
}
@@ -147,16 +176,24 @@
mPowerStatsDescriptor = powerStats.descriptor;
if (mDeviceStats == null) {
- createDeviceStats();
+ createDeviceStats(timestampMs);
}
+ for (int i = powerStats.stateStats.size() - 1; i >= 0; i--) {
+ int key = powerStats.stateStats.keyAt(i);
+ MultiStateStats stateStats = mStateStats.get(key);
+ if (stateStats == null) {
+ stateStats = createStateStats(key, timestampMs);
+ }
+ stateStats.increment(powerStats.stateStats.valueAt(i), timestampMs);
+ }
mDeviceStats.increment(powerStats.stats, timestampMs);
for (int i = powerStats.uidStats.size() - 1; i >= 0; i--) {
int uid = powerStats.uidStats.keyAt(i);
PowerComponentAggregatedPowerStats.UidStats uidStats = getUidStats(uid);
if (uidStats.stats == null) {
- createUidStats(uidStats);
+ createUidStats(uidStats, timestampMs);
}
uidStats.stats.increment(powerStats.uidStats.valueAt(i), timestampMs);
}
@@ -168,6 +205,7 @@
mStatsFactory = null;
mUidStatsFactory = null;
mDeviceStats = null;
+ mStateStats.clear();
for (int i = mUidStats.size() - 1; i >= 0; i--) {
mUidStats.valueAt(i).stats = null;
}
@@ -178,6 +216,13 @@
if (uidStats == null) {
uidStats = new UidStats();
uidStats.states = new int[mUidStateConfig.length];
+ for (int stateId = 0; stateId < mUidStateConfig.length; stateId++) {
+ if (mUidStateConfig[stateId].isTracked()
+ && stateId < mDeviceStateConfig.length
+ && mDeviceStateConfig[stateId].isTracked()) {
+ uidStats.states[stateId] = mDeviceStates[stateId];
+ }
+ }
mUidStats.put(uid, uidStats);
}
return uidStats;
@@ -204,6 +249,26 @@
return false;
}
+ boolean getStateStats(long[] outValues, int key, int[] deviceStates) {
+ if (deviceStates.length != mDeviceStateConfig.length) {
+ throw new IllegalArgumentException(
+ "Invalid number of tracked states: " + deviceStates.length
+ + " expected: " + mDeviceStateConfig.length);
+ }
+ MultiStateStats stateStats = mStateStats.get(key);
+ if (stateStats != null) {
+ stateStats.getStats(outValues, deviceStates);
+ return true;
+ }
+ return false;
+ }
+
+ void forEachStateStatsKey(IntConsumer consumer) {
+ for (int i = mStateStats.size() - 1; i >= 0; i--) {
+ consumer.accept(mStateStats.keyAt(i));
+ }
+ }
+
boolean getUidStats(long[] outValues, int uid, int[] uidStates) {
if (uidStates.length != mUidStateConfig.length) {
throw new IllegalArgumentException(
@@ -218,7 +283,7 @@
return false;
}
- private void createDeviceStats() {
+ private void createDeviceStats(long timestampMs) {
if (mStatsFactory == null) {
if (mPowerStatsDescriptor == null) {
return;
@@ -229,13 +294,39 @@
mDeviceStats = mStatsFactory.create();
if (mPowerStatsTimestamp != UNKNOWN) {
+ timestampMs = mPowerStatsTimestamp;
+ }
+ if (timestampMs != UNKNOWN) {
for (int stateId = 0; stateId < mDeviceStateConfig.length; stateId++) {
- mDeviceStats.setState(stateId, mDeviceStates[stateId], mPowerStatsTimestamp);
+ int state = mDeviceStates[stateId];
+ mDeviceStats.setState(stateId, state, timestampMs);
+ for (int i = mStateStats.size() - 1; i >= 0; i--) {
+ MultiStateStats stateStats = mStateStats.valueAt(i);
+ stateStats.setState(stateId, state, timestampMs);
+ }
}
}
}
- private void createUidStats(UidStats uidStats) {
+ private MultiStateStats createStateStats(int key, long timestampMs) {
+ if (mStateStatsFactory == null) {
+ if (mPowerStatsDescriptor == null) {
+ return null;
+ }
+ mStateStatsFactory = new MultiStateStats.Factory(
+ mPowerStatsDescriptor.stateStatsArrayLength, mDeviceStateConfig);
+ }
+
+ MultiStateStats stateStats = mStateStatsFactory.create();
+ mStateStats.put(key, stateStats);
+ if (mDeviceStats != null) {
+ stateStats.copyStatesFrom(mDeviceStats);
+ }
+
+ return stateStats;
+ }
+
+ private void createUidStats(UidStats uidStats, long timestampMs) {
if (mUidStatsFactory == null) {
if (mPowerStatsDescriptor == null) {
return;
@@ -245,9 +336,13 @@
}
uidStats.stats = mUidStatsFactory.create();
- for (int stateId = 0; stateId < mUidStateConfig.length; stateId++) {
- if (mPowerStatsTimestamp != UNKNOWN) {
- uidStats.stats.setState(stateId, uidStats.states[stateId], mPowerStatsTimestamp);
+
+ if (mPowerStatsTimestamp != UNKNOWN) {
+ timestampMs = mPowerStatsTimestamp;
+ }
+ if (timestampMs != UNKNOWN) {
+ for (int stateId = 0; stateId < mUidStateConfig.length; stateId++) {
+ uidStats.stats.setState(stateId, uidStats.states[stateId], timestampMs);
}
}
}
@@ -268,6 +363,13 @@
serializer.endTag(null, XML_TAG_DEVICE_STATS);
}
+ for (int i = 0; i < mStateStats.size(); i++) {
+ serializer.startTag(null, XML_TAG_STATE_STATS);
+ serializer.attributeInt(null, XML_ATTR_KEY, mStateStats.keyAt(i));
+ mStateStats.valueAt(i).writeXml(serializer);
+ serializer.endTag(null, XML_TAG_STATE_STATS);
+ }
+
for (int i = mUidStats.size() - 1; i >= 0; i--) {
int uid = mUidStats.keyAt(i);
UidStats uidStats = mUidStats.valueAt(i);
@@ -285,8 +387,10 @@
public boolean readFromXml(TypedXmlPullParser parser) throws XmlPullParserException,
IOException {
+ String outerTag = parser.getName();
int eventType = parser.getEventType();
- while (eventType != XmlPullParser.END_DOCUMENT) {
+ while (eventType != XmlPullParser.END_DOCUMENT
+ && !(eventType == XmlPullParser.END_TAG && parser.getName().equals(outerTag))) {
if (eventType == XmlPullParser.START_TAG) {
switch (parser.getName()) {
case PowerStats.Descriptor.XML_TAG_DESCRIPTOR:
@@ -297,17 +401,27 @@
break;
case XML_TAG_DEVICE_STATS:
if (mDeviceStats == null) {
- createDeviceStats();
+ createDeviceStats(UNKNOWN);
}
if (!mDeviceStats.readFromXml(parser)) {
return false;
}
break;
+ case XML_TAG_STATE_STATS:
+ int key = parser.getAttributeInt(null, XML_ATTR_KEY);
+ MultiStateStats stats = mStateStats.get(key);
+ if (stats == null) {
+ stats = createStateStats(key, UNKNOWN);
+ }
+ if (!stats.readFromXml(parser)) {
+ return false;
+ }
+ break;
case XML_TAG_UID_STATS:
int uid = parser.getAttributeInt(null, XML_ATTR_UID);
UidStats uidStats = getUidStats(uid);
if (uidStats.stats == null) {
- createUidStats(uidStats);
+ createUidStats(uidStats, UNKNOWN);
}
if (!uidStats.stats.readFromXml(parser)) {
return false;
@@ -328,6 +442,21 @@
mConfig.getProcessor().deviceStatsToString(mPowerStatsDescriptor, stats));
ipw.decreaseIndent();
}
+
+ if (mStateStats.size() != 0) {
+ ipw.increaseIndent();
+ ipw.println(mPowerStatsDescriptor.name + " states");
+ ipw.increaseIndent();
+ for (int i = 0; i < mStateStats.size(); i++) {
+ int key = mStateStats.keyAt(i);
+ MultiStateStats stateStats = mStateStats.valueAt(i);
+ stateStats.dump(ipw, stats ->
+ mConfig.getProcessor().stateStatsToString(mPowerStatsDescriptor, key,
+ stats));
+ }
+ ipw.decreaseIndent();
+ ipw.decreaseIndent();
+ }
}
void dumpUid(IndentingPrintWriter ipw, int uid) {
@@ -340,4 +469,29 @@
ipw.decreaseIndent();
}
}
+
+ @Override
+ public String toString() {
+ StringWriter sw = new StringWriter();
+ IndentingPrintWriter ipw = new IndentingPrintWriter(sw);
+ ipw.increaseIndent();
+ dumpDevice(ipw);
+ ipw.decreaseIndent();
+
+ int[] uids = new int[mUidStats.size()];
+ for (int i = uids.length - 1; i >= 0; i--) {
+ uids[i] = mUidStats.keyAt(i);
+ }
+ Arrays.sort(uids);
+ for (int uid : uids) {
+ ipw.println(UserHandle.formatUid(uid));
+ ipw.increaseIndent();
+ dumpUid(ipw, uid);
+ ipw.decreaseIndent();
+ }
+
+ ipw.flush();
+
+ return sw.toString();
+ }
}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
index ba4c127..6a4c1f0 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
@@ -32,7 +32,7 @@
private static final long UNINITIALIZED = -1;
private final AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
private final BatteryStatsHistory mHistory;
- private final SparseArray<AggregatedPowerStatsProcessor> mProcessors = new SparseArray<>();
+ private final SparseArray<PowerStatsProcessor> mProcessors = new SparseArray<>();
private AggregatedPowerStats mStats;
private int mCurrentBatteryState = AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
private int mCurrentScreenState = AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
@@ -43,7 +43,7 @@
mHistory = history;
for (AggregatedPowerStatsConfig.PowerComponent powerComponentsConfig :
aggregatedPowerStatsConfig.getPowerComponentsAggregatedStatsConfigs()) {
- AggregatedPowerStatsProcessor processor = powerComponentsConfig.getProcessor();
+ PowerStatsProcessor processor = powerComponentsConfig.getProcessor();
mProcessors.put(powerComponentsConfig.getPowerComponentId(), processor);
}
}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
index c76797ba..5dd11db 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
@@ -17,9 +17,12 @@
package com.android.server.power.stats;
import android.annotation.Nullable;
+import android.hardware.power.stats.EnergyConsumer;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyConsumerType;
import android.os.ConditionVariable;
import android.os.Handler;
-import android.os.PersistableBundle;
+import android.power.PowerStatsInternal;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -30,7 +33,12 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
/**
@@ -43,6 +51,7 @@
public abstract class PowerStatsCollector {
private static final String TAG = "PowerStatsCollector";
private static final int MILLIVOLTS_PER_VOLT = 1000;
+ private static final long POWER_STATS_ENERGY_CONSUMERS_TIMEOUT = 20000;
private final Handler mHandler;
protected final Clock mClock;
private final long mThrottlePeriodMs;
@@ -50,200 +59,6 @@
private boolean mEnabled;
private long mLastScheduledUpdateMs = -1;
- /**
- * Captures the positions and lengths of sections of the stats array, such as usage duration,
- * power usage estimates etc.
- */
- public static class StatsArrayLayout {
- private static final String EXTRA_DEVICE_POWER_POSITION = "dp";
- private static final String EXTRA_DEVICE_DURATION_POSITION = "dd";
- private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION = "de";
- private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT = "dec";
- private static final String EXTRA_UID_POWER_POSITION = "up";
-
- protected static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0;
-
- private int mDeviceStatsArrayLength;
- private int mUidStatsArrayLength;
-
- protected int mDeviceDurationPosition;
- private int mDeviceEnergyConsumerPosition;
- private int mDeviceEnergyConsumerCount;
- private int mDevicePowerEstimatePosition;
- private int mUidPowerEstimatePosition;
-
- public int getDeviceStatsArrayLength() {
- return mDeviceStatsArrayLength;
- }
-
- public int getUidStatsArrayLength() {
- return mUidStatsArrayLength;
- }
-
- protected int addDeviceSection(int length) {
- int position = mDeviceStatsArrayLength;
- mDeviceStatsArrayLength += length;
- return position;
- }
-
- protected int addUidSection(int length) {
- int position = mUidStatsArrayLength;
- mUidStatsArrayLength += length;
- return position;
- }
-
- /**
- * Declare that the stats array has a section capturing usage duration
- */
- public void addDeviceSectionUsageDuration() {
- mDeviceDurationPosition = addDeviceSection(1);
- }
-
- /**
- * Saves the usage duration in the corresponding <code>stats</code> element.
- */
- public void setUsageDuration(long[] stats, long value) {
- stats[mDeviceDurationPosition] = value;
- }
-
- /**
- * Extracts the usage duration from the corresponding <code>stats</code> element.
- */
- public long getUsageDuration(long[] stats) {
- return stats[mDeviceDurationPosition];
- }
-
- /**
- * Declares that the stats array has a section capturing EnergyConsumer data from
- * PowerStatsService.
- */
- public void addDeviceSectionEnergyConsumers(int energyConsumerCount) {
- mDeviceEnergyConsumerPosition = addDeviceSection(energyConsumerCount);
- mDeviceEnergyConsumerCount = energyConsumerCount;
- }
-
- public int getEnergyConsumerCount() {
- return mDeviceEnergyConsumerCount;
- }
-
- /**
- * Saves the accumulated energy for the specified rail the corresponding
- * <code>stats</code> element.
- */
- public void setConsumedEnergy(long[] stats, int index, long energy) {
- stats[mDeviceEnergyConsumerPosition + index] = energy;
- }
-
- /**
- * Extracts the EnergyConsumer data from a device stats array for the specified
- * EnergyConsumer.
- */
- public long getConsumedEnergy(long[] stats, int index) {
- return stats[mDeviceEnergyConsumerPosition + index];
- }
-
- /**
- * Declare that the stats array has a section capturing a power estimate
- */
- public void addDeviceSectionPowerEstimate() {
- mDevicePowerEstimatePosition = addDeviceSection(1);
- }
-
- /**
- * Converts the supplied mAh power estimate to a long and saves it in the corresponding
- * element of <code>stats</code>.
- */
- public void setDevicePowerEstimate(long[] stats, double power) {
- stats[mDevicePowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
- }
-
- /**
- * Extracts the power estimate from a device stats array and converts it to mAh.
- */
- public double getDevicePowerEstimate(long[] stats) {
- return stats[mDevicePowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
- }
-
- /**
- * Declare that the UID stats array has a section capturing a power estimate
- */
- public void addUidSectionPowerEstimate() {
- mUidPowerEstimatePosition = addUidSection(1);
- }
-
- /**
- * Converts the supplied mAh power estimate to a long and saves it in the corresponding
- * element of <code>stats</code>.
- */
- public void setUidPowerEstimate(long[] stats, double power) {
- stats[mUidPowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
- }
-
- /**
- * Extracts the power estimate from a UID stats array and converts it to mAh.
- */
- public double getUidPowerEstimate(long[] stats) {
- return stats[mUidPowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
- }
-
- /**
- * Copies the elements of the stats array layout into <code>extras</code>
- */
- public void toExtras(PersistableBundle extras) {
- extras.putInt(EXTRA_DEVICE_DURATION_POSITION, mDeviceDurationPosition);
- extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION,
- mDeviceEnergyConsumerPosition);
- extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT,
- mDeviceEnergyConsumerCount);
- extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition);
- extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition);
- }
-
- /**
- * Retrieves elements of the stats array layout from <code>extras</code>
- */
- public void fromExtras(PersistableBundle extras) {
- mDeviceDurationPosition = extras.getInt(EXTRA_DEVICE_DURATION_POSITION);
- mDeviceEnergyConsumerPosition = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION);
- mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT);
- mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION);
- mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION);
- }
-
- protected void putIntArray(PersistableBundle extras, String key, int[] array) {
- if (array == null) {
- return;
- }
-
- StringBuilder sb = new StringBuilder();
- for (int value : array) {
- if (!sb.isEmpty()) {
- sb.append(',');
- }
- sb.append(value);
- }
- extras.putString(key, sb.toString());
- }
-
- protected int[] getIntArray(PersistableBundle extras, String key) {
- String string = extras.getString(key);
- if (string == null) {
- return null;
- }
- String[] values = string.trim().split(",");
- int[] result = new int[values.length];
- for (int i = 0; i < values.length; i++) {
- try {
- result[i] = Integer.parseInt(values[i]);
- } catch (NumberFormatException e) {
- Slog.wtf(TAG, "Invalid CSV format: " + string);
- return null;
- }
- }
- return result;
- }
- }
-
@GuardedBy("this")
@SuppressWarnings("unchecked")
private volatile List<Consumer<PowerStats>> mConsumerList = Collections.emptyList();
@@ -389,9 +204,83 @@
}
/** Calculate charge consumption (in microcoulombs) from a given energy and voltage */
- protected long uJtoUc(long deltaEnergyUj, int avgVoltageMv) {
+ protected static long uJtoUc(long deltaEnergyUj, int avgVoltageMv) {
// To overflow, a 3.7V 10000mAh battery would need to completely drain 69244 times
// since the last snapshot. Round off to the nearest whole long.
return (deltaEnergyUj * MILLIVOLTS_PER_VOLT + (avgVoltageMv / 2)) / avgVoltageMv;
}
+
+ interface ConsumedEnergyRetriever {
+ int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType);
+
+ @Nullable
+ long[] getConsumedEnergyUws(int[] energyConsumerIds);
+ }
+
+ static class ConsumedEnergyRetrieverImpl implements ConsumedEnergyRetriever {
+ private final PowerStatsInternal mPowerStatsInternal;
+
+ ConsumedEnergyRetrieverImpl(PowerStatsInternal powerStatsInternal) {
+ mPowerStatsInternal = powerStatsInternal;
+ }
+
+ @Override
+ public int[] getEnergyConsumerIds(int energyConsumerType) {
+ if (mPowerStatsInternal == null) {
+ return new int[0];
+ }
+
+ EnergyConsumer[] energyConsumerInfo = mPowerStatsInternal.getEnergyConsumerInfo();
+ if (energyConsumerInfo == null) {
+ return new int[0];
+ }
+
+ List<EnergyConsumer> energyConsumers = new ArrayList<>();
+ for (EnergyConsumer energyConsumer : energyConsumerInfo) {
+ if (energyConsumer.type == energyConsumerType) {
+ energyConsumers.add(energyConsumer);
+ }
+ }
+ if (energyConsumers.isEmpty()) {
+ return new int[0];
+ }
+
+ energyConsumers.sort(Comparator.comparing(c -> c.ordinal));
+
+ int[] ids = new int[energyConsumers.size()];
+ for (int i = 0; i < ids.length; i++) {
+ ids[i] = energyConsumers.get(i).id;
+ }
+ return ids;
+ }
+
+ @Override
+ public long[] getConsumedEnergyUws(int[] energyConsumerIds) {
+ CompletableFuture<EnergyConsumerResult[]> future =
+ mPowerStatsInternal.getEnergyConsumedAsync(energyConsumerIds);
+ EnergyConsumerResult[] results = null;
+ try {
+ results = future.get(
+ POWER_STATS_ENERGY_CONSUMERS_TIMEOUT, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ Slog.e(TAG, "Could not obtain energy consumers from PowerStatsService", e);
+ }
+
+ if (results == null) {
+ return null;
+ }
+
+ long[] energy = new long[energyConsumerIds.length];
+ for (int i = 0; i < energyConsumerIds.length; i++) {
+ int id = energyConsumerIds[i];
+ for (EnergyConsumerResult result : results) {
+ if (result.id == id) {
+ energy[i] = result.energyUWs;
+ break;
+ }
+ }
+ }
+ return energy;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsExporter.java b/services/core/java/com/android/server/power/stats/PowerStatsExporter.java
index 4f4ddca..f6b198a8 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsExporter.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsExporter.java
@@ -139,7 +139,7 @@
return;
}
- PowerStatsCollector.StatsArrayLayout layout = new PowerStatsCollector.StatsArrayLayout();
+ PowerStatsLayout layout = new PowerStatsLayout();
layout.fromExtras(descriptor.extras);
long[] deviceStats = new long[descriptor.statsArrayLength];
@@ -164,9 +164,20 @@
deviceScope.addConsumedPower(powerComponentId,
totalPower[0], BatteryConsumer.POWER_MODEL_UNDEFINED);
+ if (layout.isUidPowerAttributionSupported()) {
+ populateUidBatteryConsumers(batteryUsageStatsBuilder, powerComponent,
+ powerComponentStats, layout);
+ }
+ }
+
+ private static void populateUidBatteryConsumers(
+ BatteryUsageStats.Builder batteryUsageStatsBuilder,
+ AggregatedPowerStatsConfig.PowerComponent powerComponent,
+ PowerComponentAggregatedPowerStats powerComponentStats,
+ PowerStatsLayout layout) {
+ int powerComponentId = powerComponent.getPowerComponentId();
+ PowerStats.Descriptor descriptor = powerComponentStats.getPowerStatsDescriptor();
long[] uidStats = new long[descriptor.uidStatsArrayLength];
- ArrayList<Integer> uids = new ArrayList<>();
- powerComponentStats.collectUids(uids);
boolean breakDownByProcState =
batteryUsageStatsBuilder.isProcessStateDataNeeded()
@@ -177,6 +188,8 @@
double[] powerByProcState =
new double[breakDownByProcState ? BatteryConsumer.PROCESS_STATE_COUNT : 1];
double powerAllApps = 0;
+ ArrayList<Integer> uids = new ArrayList<>();
+ powerComponentStats.collectUids(uids);
for (int uid : uids) {
UidBatteryConsumer.Builder builder =
batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid);
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsLayout.java b/services/core/java/com/android/server/power/stats/PowerStatsLayout.java
new file mode 100644
index 0000000..aa96409
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/PowerStatsLayout.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2024 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.power.stats;
+
+import android.os.PersistableBundle;
+import android.util.Slog;
+
+import com.android.internal.os.PowerStats;
+
+/**
+ * Captures the positions and lengths of sections of the stats array, such as usage duration,
+ * power usage estimates etc.
+ */
+public class PowerStatsLayout {
+ private static final String TAG = "PowerStatsLayout";
+ private static final String EXTRA_DEVICE_POWER_POSITION = "dp";
+ private static final String EXTRA_DEVICE_DURATION_POSITION = "dd";
+ private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION = "de";
+ private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT = "dec";
+ private static final String EXTRA_UID_POWER_POSITION = "up";
+
+ protected static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0;
+ protected static final int UNSUPPORTED = -1;
+
+ private int mDeviceStatsArrayLength;
+ private int mStateStatsArrayLength;
+ private int mUidStatsArrayLength;
+
+ protected int mDeviceDurationPosition = UNSUPPORTED;
+ private int mDeviceEnergyConsumerPosition;
+ private int mDeviceEnergyConsumerCount;
+ private int mDevicePowerEstimatePosition = UNSUPPORTED;
+ private int mUidPowerEstimatePosition = UNSUPPORTED;
+
+ public PowerStatsLayout() {
+ }
+
+ public PowerStatsLayout(PowerStats.Descriptor descriptor) {
+ fromExtras(descriptor.extras);
+ }
+
+ public int getDeviceStatsArrayLength() {
+ return mDeviceStatsArrayLength;
+ }
+
+ public int getStateStatsArrayLength() {
+ return mStateStatsArrayLength;
+ }
+
+ public int getUidStatsArrayLength() {
+ return mUidStatsArrayLength;
+ }
+
+ protected int addDeviceSection(int length) {
+ int position = mDeviceStatsArrayLength;
+ mDeviceStatsArrayLength += length;
+ return position;
+ }
+
+ protected int addStateSection(int length) {
+ int position = mStateStatsArrayLength;
+ mStateStatsArrayLength += length;
+ return position;
+ }
+
+ protected int addUidSection(int length) {
+ int position = mUidStatsArrayLength;
+ mUidStatsArrayLength += length;
+ return position;
+ }
+
+ /**
+ * Declare that the stats array has a section capturing usage duration
+ */
+ public void addDeviceSectionUsageDuration() {
+ mDeviceDurationPosition = addDeviceSection(1);
+ }
+
+ /**
+ * Saves the usage duration in the corresponding <code>stats</code> element.
+ */
+ public void setUsageDuration(long[] stats, long value) {
+ stats[mDeviceDurationPosition] = value;
+ }
+
+ /**
+ * Extracts the usage duration from the corresponding <code>stats</code> element.
+ */
+ public long getUsageDuration(long[] stats) {
+ return stats[mDeviceDurationPosition];
+ }
+
+ /**
+ * Declares that the stats array has a section capturing EnergyConsumer data from
+ * PowerStatsService.
+ */
+ public void addDeviceSectionEnergyConsumers(int energyConsumerCount) {
+ mDeviceEnergyConsumerPosition = addDeviceSection(energyConsumerCount);
+ mDeviceEnergyConsumerCount = energyConsumerCount;
+ }
+
+ public int getEnergyConsumerCount() {
+ return mDeviceEnergyConsumerCount;
+ }
+
+ /**
+ * Saves the accumulated energy for the specified rail the corresponding
+ * <code>stats</code> element.
+ */
+ public void setConsumedEnergy(long[] stats, int index, long energy) {
+ stats[mDeviceEnergyConsumerPosition + index] = energy;
+ }
+
+ /**
+ * Extracts the EnergyConsumer data from a device stats array for the specified
+ * EnergyConsumer.
+ */
+ public long getConsumedEnergy(long[] stats, int index) {
+ return stats[mDeviceEnergyConsumerPosition + index];
+ }
+
+ /**
+ * Declare that the stats array has a section capturing a power estimate
+ */
+ public void addDeviceSectionPowerEstimate() {
+ mDevicePowerEstimatePosition = addDeviceSection(1);
+ }
+
+ /**
+ * Converts the supplied mAh power estimate to a long and saves it in the corresponding
+ * element of <code>stats</code>.
+ */
+ public void setDevicePowerEstimate(long[] stats, double power) {
+ stats[mDevicePowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
+ }
+
+ /**
+ * Extracts the power estimate from a device stats array and converts it to mAh.
+ */
+ public double getDevicePowerEstimate(long[] stats) {
+ return stats[mDevicePowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
+ }
+
+ /**
+ * Declare that the UID stats array has a section capturing a power estimate
+ */
+ public void addUidSectionPowerEstimate() {
+ mUidPowerEstimatePosition = addUidSection(1);
+ }
+
+ /**
+ * Returns true if power for this component is attributed to UIDs (apps).
+ */
+ public boolean isUidPowerAttributionSupported() {
+ return mUidPowerEstimatePosition != UNSUPPORTED;
+ }
+
+ /**
+ * Converts the supplied mAh power estimate to a long and saves it in the corresponding
+ * element of <code>stats</code>.
+ */
+ public void setUidPowerEstimate(long[] stats, double power) {
+ stats[mUidPowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
+ }
+
+ /**
+ * Extracts the power estimate from a UID stats array and converts it to mAh.
+ */
+ public double getUidPowerEstimate(long[] stats) {
+ return stats[mUidPowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
+ }
+
+ /**
+ * Copies the elements of the stats array layout into <code>extras</code>
+ */
+ public void toExtras(PersistableBundle extras) {
+ extras.putInt(EXTRA_DEVICE_DURATION_POSITION, mDeviceDurationPosition);
+ extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION,
+ mDeviceEnergyConsumerPosition);
+ extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT,
+ mDeviceEnergyConsumerCount);
+ extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition);
+ extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition);
+ }
+
+ /**
+ * Retrieves elements of the stats array layout from <code>extras</code>
+ */
+ public void fromExtras(PersistableBundle extras) {
+ mDeviceDurationPosition = extras.getInt(EXTRA_DEVICE_DURATION_POSITION);
+ mDeviceEnergyConsumerPosition = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION);
+ mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT);
+ mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION);
+ mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION);
+ }
+
+ protected void putIntArray(PersistableBundle extras, String key, int[] array) {
+ if (array == null) {
+ return;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (int value : array) {
+ if (!sb.isEmpty()) {
+ sb.append(',');
+ }
+ sb.append(value);
+ }
+ extras.putString(key, sb.toString());
+ }
+
+ protected int[] getIntArray(PersistableBundle extras, String key) {
+ String string = extras.getString(key);
+ if (string == null) {
+ return null;
+ }
+ String[] values = string.trim().split(",");
+ int[] result = new int[values.length];
+ for (int i = 0; i < values.length; i++) {
+ try {
+ result[i] = Integer.parseInt(values[i]);
+ } catch (NumberFormatException e) {
+ Slog.wtf(TAG, "Invalid CSV format: " + string);
+ return null;
+ }
+ }
+ return result;
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
similarity index 98%
rename from services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java
rename to services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
index 7feb964..0d5c542 100644
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
@@ -27,7 +27,7 @@
import java.util.List;
/*
- * The power estimation algorithm used by AggregatedPowerStatsProcessor can roughly be
+ * The power estimation algorithm used by PowerStatsProcessor can roughly be
* described like this:
*
* 1. Estimate power usage for each state combination (e.g. power-battery/screen-on) using
@@ -39,8 +39,8 @@
* 2. For each UID, compute the proportion of the combined estimates in each state
* and attribute the corresponding portion of the total power estimate in that state to the UID.
*/
-abstract class AggregatedPowerStatsProcessor {
- private static final String TAG = "AggregatedPowerStatsProcessor";
+abstract class PowerStatsProcessor {
+ private static final String TAG = "PowerStatsProcessor";
private static final int INDEX_DOES_NOT_EXIST = -1;
private static final double MILLIAMPHOUR_PER_MICROCOULOMB = 1.0 / 1000.0 / 60.0 / 60.0;
@@ -49,6 +49,8 @@
abstract String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats);
+ abstract String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats);
+
abstract String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats);
protected static class PowerEstimationPlan {
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 2a93255..c8bcc51 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -54,8 +54,10 @@
import android.os.UserManager;
import android.os.ext.SdkExtensions;
import android.provider.DeviceConfig;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.LongArrayQueue;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
@@ -173,6 +175,8 @@
// Accessed on the handler thread only.
private long mRelativeBootTime = calculateRelativeBootTime();
+ private final ArrayMap<Integer, Pair<Context, BroadcastReceiver>> mUserBroadcastReceivers;
+
RollbackManagerServiceImpl(Context context) {
mContext = context;
// Note that we're calling onStart here because this object is only constructed on
@@ -210,6 +214,8 @@
}
});
+ mUserBroadcastReceivers = new ArrayMap<>();
+
UserManager userManager = mContext.getSystemService(UserManager.class);
for (UserHandle user : userManager.getUserHandles(true)) {
registerUserCallbacks(user);
@@ -275,7 +281,9 @@
}
}, enableRollbackTimedOutFilter, null, getHandler());
- IntentFilter userAddedIntentFilter = new IntentFilter(Intent.ACTION_USER_ADDED);
+ IntentFilter userIntentFilter = new IntentFilter();
+ userIntentFilter.addAction(Intent.ACTION_USER_ADDED);
+ userIntentFilter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -287,9 +295,15 @@
return;
}
registerUserCallbacks(UserHandle.of(newUserId));
+ } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
+ final int newUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (newUserId == -1) {
+ return;
+ }
+ unregisterUserCallbacks(UserHandle.of(newUserId));
}
}
- }, userAddedIntentFilter, null, getHandler());
+ }, userIntentFilter, null, getHandler());
registerTimeChangeReceiver();
}
@@ -335,7 +349,7 @@
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
filter.addDataScheme("package");
- context.registerReceiver(new BroadcastReceiver() {
+ BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
assertInWorkerThread();
@@ -354,7 +368,21 @@
onPackageFullyRemoved(packageName);
}
}
- }, filter, null, getHandler());
+ };
+ context.registerReceiver(receiver, filter, null, getHandler());
+ mUserBroadcastReceivers.put(user.getIdentifier(), new Pair(context, receiver));
+ }
+
+ @AnyThread
+ private void unregisterUserCallbacks(UserHandle user) {
+ Pair<Context, BroadcastReceiver> pair = mUserBroadcastReceivers.get(user.getIdentifier());
+ if (pair == null || pair.first == null || pair.second == null) {
+ Slog.e(TAG, "No receiver found for the user" + user);
+ return;
+ }
+
+ pair.first.unregisterReceiver(pair.second);
+ mUserBroadcastReceivers.remove(user.getIdentifier());
}
@ExtThread
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
index 96f045d..8138168 100644
--- a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
@@ -44,6 +44,8 @@
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_PHYSICAL_EMULATION);
private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
+ private static final VibrationAttributes COMMUNICATION_REQUEST_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_COMMUNICATION_REQUEST);
private final VibratorInfo mVibratorInfo;
private final boolean mHapticTextHandleEnabled;
@@ -120,7 +122,6 @@
return getKeyboardVibration(effectId);
case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
- case HapticFeedbackConstants.ENTRY_BUMP:
case HapticFeedbackConstants.DRAG_CROSSING:
return getVibration(
effectId,
@@ -131,6 +132,7 @@
case HapticFeedbackConstants.EDGE_RELEASE:
case HapticFeedbackConstants.CALENDAR_DATE:
case HapticFeedbackConstants.CONFIRM:
+ case HapticFeedbackConstants.BIOMETRIC_CONFIRM:
case HapticFeedbackConstants.GESTURE_START:
case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
case HapticFeedbackConstants.SCROLL_LIMIT:
@@ -143,6 +145,7 @@
return getVibration(effectId, VibrationEffect.EFFECT_HEAVY_CLICK);
case HapticFeedbackConstants.REJECT:
+ case HapticFeedbackConstants.BIOMETRIC_REJECT:
return getVibration(effectId, VibrationEffect.EFFECT_DOUBLE_CLICK);
case HapticFeedbackConstants.SAFE_MODE_ENABLED:
@@ -207,6 +210,10 @@
case HapticFeedbackConstants.KEYBOARD_RELEASE:
attrs = createKeyboardVibrationAttributes(fromIme);
break;
+ case HapticFeedbackConstants.BIOMETRIC_CONFIRM:
+ case HapticFeedbackConstants.BIOMETRIC_REJECT:
+ attrs = COMMUNICATION_REQUEST_VIBRATION_ATTRIBUTES;
+ break;
default:
attrs = TOUCH_VIBRATION_ATTRIBUTES;
}
@@ -225,6 +232,23 @@
return flags == 0 ? attrs : new VibrationAttributes.Builder(attrs).setFlags(flags).build();
}
+ /**
+ * Returns true if given haptic feedback is restricted to system apps with permission
+ * {@code android.permission.VIBRATE_SYSTEM_CONSTANTS}.
+ *
+ * @param effectId the haptic feedback effect ID to check.
+ * @return true if the haptic feedback is restricted, false otherwise.
+ */
+ public boolean isRestrictedHapticFeedback(int effectId) {
+ switch (effectId) {
+ case HapticFeedbackConstants.BIOMETRIC_CONFIRM:
+ case HapticFeedbackConstants.BIOMETRIC_REJECT:
+ return true;
+ default:
+ return false;
+ }
+ }
+
/** Dumps relevant state. */
public void dump(String prefix, PrintWriter pw) {
pw.print("mHapticTextHandleEnabled="); pw.println(mHapticTextHandleEnabled);
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 9e9025e..8281ac1 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -439,6 +439,11 @@
Slog.w(TAG, "performHapticFeedback; haptic vibration provider not ready.");
return null;
}
+ if (hapticVibrationProvider.isRestrictedHapticFeedback(constant)
+ && !hasPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS)) {
+ Slog.w(TAG, "performHapticFeedback; no permission for effect " + constant);
+ return null;
+ }
VibrationEffect effect = hapticVibrationProvider.getVibrationForHapticFeedback(constant);
if (effect == null) {
Slog.w(TAG, "performHapticFeedback; vibration absent for effect " + constant);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 9a5961a..f1ba755 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2269,6 +2269,12 @@
Point croppedBitmapSize = new Point(
(int) (0.5f + wallpaper.cropHint.width() / wallpaper.mSampleSize),
(int) (0.5f + wallpaper.cropHint.height() / wallpaper.mSampleSize));
+ if (croppedBitmapSize.equals(0, 0)) {
+ // There is an ImageWallpaper, but there are no crop hints and the bitmap size is
+ // unknown (e.g. the default wallpaper). Return a special "null" value that will be
+ // handled by WallpaperManager, which will fetch the dimensions of the wallpaper.
+ return null;
+ }
SparseArray<Rect> relativeDefaultCrops =
mWallpaperCropper.getDefaultCrops(relativeSuggestedCrops, croppedBitmapSize);
SparseArray<Rect> adjustedRelativeSuggestedCrops = new SparseArray<>();
diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
index c6b401b..36e5200 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
@@ -525,6 +525,10 @@
futureFromWearableSensingService.complete(null);
return;
}
+ if (pfdFromApp == null) {
+ futureFromWearableSensingService.complete(null);
+ return;
+ }
if (isReadOnly(pfdFromApp)) {
futureFromWearableSensingService.complete(
pfdFromApp);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 207d42b6..88f86cc 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -8544,7 +8544,9 @@
}
// If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
// are already calculated in resolveFixedOrientationConfiguration.
- } else if (!isLetterboxedForFixedOrientationAndAspectRatio()) {
+ // Don't apply aspect ratio if app is overridden to fullscreen by device user/manufacturer.
+ } else if (!isLetterboxedForFixedOrientationAndAspectRatio()
+ && !mLetterboxUiController.hasFullscreenOverride()) {
resolveAspectRatioRestriction(newParentConfiguration);
}
@@ -9060,8 +9062,8 @@
// vertically centered within parent bounds with insets, so position vertical bounds
// within parent bounds with insets to prevent insets from unnecessarily trimming
// vertical bounds.
- final int bottom = Math.min(parentBoundsWithInsets.top + parentBounds.width() - 1,
- parentBoundsWithInsets.bottom);
+ final int bottom = Math.min(parentBoundsWithInsets.top
+ + parentBoundsWithInsets.width() - 1, parentBoundsWithInsets.bottom);
containingBounds.set(parentBounds.left, parentBoundsWithInsets.top, parentBounds.right,
bottom);
containingBoundsWithInsets.set(parentBoundsWithInsets.left, parentBoundsWithInsets.top,
@@ -9072,8 +9074,8 @@
// horizontally centered within parent bounds with insets, so position horizontal bounds
// within parent bounds with insets to prevent insets from unnecessarily trimming
// horizontal bounds.
- final int right = Math.min(parentBoundsWithInsets.left + parentBounds.height(),
- parentBoundsWithInsets.right);
+ final int right = Math.min(parentBoundsWithInsets.left
+ + parentBoundsWithInsets.height(), parentBoundsWithInsets.right);
containingBounds.set(parentBoundsWithInsets.left, parentBounds.top, right,
parentBounds.bottom);
containingBoundsWithInsets.set(parentBoundsWithInsets.left, parentBoundsWithInsets.top,
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index f7b4a67..b9979adb 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -174,27 +174,6 @@
}
}
- // This is needed to bridge the old and new back behavior with recents. While in
- // Overview with live tile enabled, the previous app is technically focused but we
- // add an input consumer to capture all input that would otherwise go to the apps
- // being controlled by the animation. This means that the window resolved is not
- // the right window to consume back while in overview, so we need to route it to
- // launcher and use the legacy behavior of injecting KEYCODE_BACK since the existing
- // compat callback in VRI only works when the window is focused.
- // This symptom also happen while shell transition enabled, we can check that by
- // isTransientLaunch to know whether the focus window is point to live tile.
- final RecentsAnimationController recentsAnimationController =
- wmService.getRecentsAnimationController();
- final ActivityRecord tmpAR = window.mActivityRecord;
- if ((tmpAR != null && tmpAR.isActivityTypeHomeOrRecents()
- && tmpAR.mTransitionController.isTransientLaunch(tmpAR))
- || (recentsAnimationController != null
- && recentsAnimationController.shouldApplyInputConsumer(tmpAR))) {
- ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Current focused window being animated by "
- + "recents. Overriding back callback to recents controller callback.");
- return null;
- }
-
if (!window.isDrawn()) {
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
"Focused window didn't have a valid surface drawn.");
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index a8cbc62..8858766 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -519,8 +519,17 @@
final SurfaceControl leash = mAdapter.mCapturedLeash;
mControlTarget = target;
updateVisibility();
+ boolean initiallyVisible = mClientVisible;
+ if (mSource.getType() == WindowInsets.Type.ime()) {
+ // The IME cannot be initially visible, see ControlAdapter#startAnimation below.
+ // Also, the ImeInsetsSourceConsumer clears the client visibility upon losing control,
+ // but this won't have reached here yet by the time the new control is created.
+ // Note: The DisplayImeController needs the correct previous client's visibility, so we
+ // only override the initiallyVisible here.
+ initiallyVisible = false;
+ }
mControl = new InsetsSourceControl(mSource.getId(), mSource.getType(), leash,
- mClientVisible, surfacePosition, getInsetsHint());
+ initiallyVisible, surfacePosition, getInsetsHint());
ProtoLog.d(WM_DEBUG_WINDOW_INSETS,
"InsetsSource Control %s for target %s", mControl, mControlTarget);
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 3f24545..f220c9d 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -1084,6 +1084,10 @@
|| mUserAspectRatio == USER_MIN_ASPECT_RATIO_FULLSCREEN);
}
+ boolean hasFullscreenOverride() {
+ return isSystemOverrideToFullscreenEnabled() || shouldApplyUserFullscreenOverride();
+ }
+
float getUserMinAspectRatio() {
switch (mUserAspectRatio) {
case USER_MIN_ASPECT_RATIO_DISPLAY_SIZE:
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 14ec41f..e708883 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1367,10 +1367,10 @@
final IBinder callerActivityToken = operation.getActivityToken();
final Intent activityIntent = operation.getActivityIntent();
final Bundle activityOptions = operation.getBundle();
- final int result = mService.getActivityStartController()
+ final int result = waitAsyncStart(() -> mService.getActivityStartController()
.startActivityInTaskFragment(taskFragment, activityIntent, activityOptions,
callerActivityToken, caller.mUid, caller.mPid,
- errorCallbackToken);
+ errorCallbackToken));
if (!isStartResultSuccessful(result)) {
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment,
opType, convertStartFailureToThrowable(result, activityIntent));
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index f1962cb..6143f1d 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -176,7 +176,9 @@
<xs:element type="idleScreenRefreshRateTimeout" name="idleScreenRefreshRateTimeout" minOccurs="0">
<xs:annotation name="final"/>
</xs:element>
-
+ <xs:element name="supportsVrr" type="xs:boolean" minOccurs="0">
+ <xs:annotation name="final"/>
+ </xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 170434c..45ec8f2 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -152,6 +152,7 @@
method public final java.math.BigDecimal getScreenBrightnessRampSlowIncreaseIdle();
method public final com.android.server.display.config.SensorDetails getScreenOffBrightnessSensor();
method public final com.android.server.display.config.IntegerArray getScreenOffBrightnessSensorValueToLux();
+ method public final boolean getSupportsVrr();
method public final com.android.server.display.config.SensorDetails getTempSensor();
method @NonNull public final com.android.server.display.config.ThermalThrottling getThermalThrottling();
method public final com.android.server.display.config.UsiVersion getUsiVersion();
@@ -188,6 +189,7 @@
method public final void setScreenBrightnessRampSlowIncreaseIdle(java.math.BigDecimal);
method public final void setScreenOffBrightnessSensor(com.android.server.display.config.SensorDetails);
method public final void setScreenOffBrightnessSensorValueToLux(com.android.server.display.config.IntegerArray);
+ method public final void setSupportsVrr(boolean);
method public final void setTempSensor(com.android.server.display.config.SensorDetails);
method public final void setThermalThrottling(@NonNull com.android.server.display.config.ThermalThrottling);
method public final void setUsiVersion(com.android.server.display.config.UsiVersion);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index f39d019..065c14e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -103,7 +103,7 @@
UserManager.DISALLOW_CELLULAR_2G);
//TODO(b/295504706) : Speak to security team to decide what to set Policy_Size_Limit
- private static final int DEFAULT_POLICY_SIZE_LIMIT = -1;
+ static final int DEFAULT_POLICY_SIZE_LIMIT = -1;
private final Context mContext;
private final UserManager mUserManager;
@@ -225,7 +225,7 @@
synchronized (mLock) {
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
if (!handleAdminPolicySizeLimit(localPolicyState, enforcingAdmin, value,
policyDefinition, userId)) {
return;
@@ -350,7 +350,7 @@
}
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
decreasePolicySizeForAdmin(localPolicyState, enforcingAdmin);
}
@@ -496,7 +496,7 @@
synchronized (mLock) {
PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
if (!handleAdminPolicySizeLimit(globalPolicyState, enforcingAdmin, value,
policyDefinition, UserHandle.USER_ALL)) {
return;
@@ -568,7 +568,7 @@
synchronized (mLock) {
PolicyState<V> policyState = getGlobalPolicyStateLocked(policyDefinition);
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
decreasePolicySizeForAdmin(policyState, enforcingAdmin);
}
@@ -1598,6 +1598,7 @@
existingPolicySize = sizeOf(policyState.getPoliciesSetByAdmins().get(admin));
}
int policySize = sizeOf(value);
+
// Policy size limit is disabled if mPolicySizeLimit is -1.
if (mPolicySizeLimit == -1
|| currentAdminPoliciesSize + policySize - existingPolicySize < mPolicySizeLimit) {
@@ -1657,10 +1658,6 @@
* the limitation.
*/
void setMaxPolicyStorageLimit(int storageLimit) {
- if (storageLimit < DEFAULT_POLICY_SIZE_LIMIT && storageLimit != -1) {
- throw new IllegalArgumentException("Can't set a size limit less than the minimum "
- + "allowed size.");
- }
mPolicySizeLimit = storageLimit;
}
@@ -1672,6 +1669,15 @@
return mPolicySizeLimit;
}
+ int getPolicySizeForAdmin(EnforcingAdmin admin) {
+ if (mAdminPolicySize.contains(admin.getUserId())
+ && mAdminPolicySize.get(
+ admin.getUserId()).containsKey(admin)) {
+ return mAdminPolicySize.get(admin.getUserId()).get(admin);
+ }
+ return 0;
+ }
+
public void dump(IndentingPrintWriter pw) {
synchronized (mLock) {
pw.println("Local Policies: ");
@@ -1906,7 +1912,7 @@
private void writeEnforcingAdminSizeInner(TypedXmlSerializer serializer)
throws IOException {
- if (Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
if (mAdminPolicySize != null) {
for (int i = 0; i < mAdminPolicySize.size(); i++) {
int userId = mAdminPolicySize.keyAt(i);
@@ -1930,7 +1936,7 @@
private void writeMaxPolicySizeInner(TypedXmlSerializer serializer)
throws IOException {
- if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
return;
}
serializer.startTag(/* namespace= */ null, TAG_MAX_POLICY_SIZE_LIMIT);
@@ -2095,7 +2101,7 @@
private void readMaxPolicySizeInner(TypedXmlPullParser parser)
throws XmlPullParserException, IOException {
- if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
return;
}
mPolicySizeLimit = parser.getAttributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index bba8189..cb63757 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -88,6 +88,7 @@
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WIFI;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WINDOWS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WIPE_DATA;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT;
import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS;
import static android.Manifest.permission.MASTER_CLEAR;
import static android.Manifest.permission.NOTIFY_PENDING_SYSTEM_UPDATE;
@@ -268,6 +269,7 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
+import static com.android.server.devicepolicy.DevicePolicyEngine.DEFAULT_POLICY_SIZE_LIMIT;
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
@@ -2252,11 +2254,41 @@
if (userHandle == UserHandle.USER_SYSTEM) {
mStateCache.setDeviceProvisioned(policy.mUserSetupComplete);
}
+ if (Flags.headlessSingleUserBadDeviceAdminStateFix()) {
+ fixBadDeviceAdminStateForInternalUsers(userHandle, policy);
+ }
}
return policy;
}
}
+ private void fixBadDeviceAdminStateForInternalUsers(int userId, DevicePolicyData policy) {
+ ComponentName component = mOwners.getDeviceOwnerComponent();
+ int doUserId = mOwners.getDeviceOwnerUserId();
+ ComponentName cloudDpc = new ComponentName(
+ "com.google.android.apps.work.clouddpc",
+ "com.google.android.apps.work.clouddpc.receivers.CloudDeviceAdminReceiver");
+ if (component == null || doUserId != userId || !component.equals(cloudDpc)) {
+ return;
+ }
+ Slogf.i(LOG_TAG, "Attempting to apply a temp fix for cloudpc internal users' bad state.");
+ final int n = policy.mAdminList.size();
+ for (int i = 0; i < n; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (component.equals(admin.info.getComponent())) {
+ Slogf.i(LOG_TAG, "An ActiveAdmin already exists, fix not required.");
+ return;
+ }
+ }
+ DeviceAdminInfo dai = findAdmin(component, userId, /* throwForMissingPermission= */ false);
+ if (dai != null) {
+ ActiveAdmin ap = new ActiveAdmin(dai, /* parent */ false);
+ policy.mAdminMap.put(ap.info.getComponent(), ap);
+ policy.mAdminList.add(ap);
+ Slogf.i(LOG_TAG, "Fix applied, an ActiveAdmin has been added.");
+ }
+ }
+
/**
* Creates and loads the policy data from xml for data that is shared between
* various profiles of a user. In contrast to {@link #getUserData(int)}
@@ -12139,7 +12171,7 @@
}
if (packageList != null) {
- if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
for (String pkg : packageList) {
PolicySizeVerifier.enforceMaxPackageNameLength(pkg);
}
@@ -13914,7 +13946,7 @@
return;
}
- if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
PolicySizeVerifier.enforceMaxStringLength(accountType, "account type");
}
@@ -14528,7 +14560,7 @@
public void setLockTaskPackages(ComponentName who, String callerPackageName, String[] packages)
throws SecurityException {
Objects.requireNonNull(packages, "packages is null");
- if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
for (String pkg : packages) {
PolicySizeVerifier.enforceMaxPackageNameLength(pkg);
}
@@ -24537,19 +24569,23 @@
@Override
public void setMaxPolicyStorageLimit(String callerPackageName, int storageLimit) {
- if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
return;
}
CallerIdentity caller = getCallerIdentity(callerPackageName);
enforcePermission(MANAGE_PROFILE_AND_DEVICE_OWNERS, caller.getPackageName(),
caller.getUserId());
+ if (storageLimit < DEFAULT_POLICY_SIZE_LIMIT && storageLimit != -1) {
+ throw new IllegalArgumentException("Can't set a size limit less than the minimum "
+ + "allowed size.");
+ }
mDevicePolicyEngine.setMaxPolicyStorageLimit(storageLimit);
}
@Override
public int getMaxPolicyStorageLimit(String callerPackageName) {
- if (!Flags.devicePolicySizeTrackingInternalEnabled()) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
return -1;
}
CallerIdentity caller = getCallerIdentity(callerPackageName);
@@ -24560,6 +24596,32 @@
}
@Override
+ public void forceSetMaxPolicyStorageLimit(String callerPackageName, int storageLimit) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ return;
+ }
+ CallerIdentity caller = getCallerIdentity(callerPackageName);
+ enforcePermission(MANAGE_DEVICE_POLICY_STORAGE_LIMIT, caller.getPackageName(),
+ caller.getUserId());
+
+ mDevicePolicyEngine.setMaxPolicyStorageLimit(storageLimit);
+ }
+
+ @Override
+ public int getPolicySizeForAdmin(
+ String callerPackageName, android.app.admin.EnforcingAdmin admin) {
+ if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
+ return -1;
+ }
+ CallerIdentity caller = getCallerIdentity(callerPackageName);
+ enforcePermission(MANAGE_DEVICE_POLICY_STORAGE_LIMIT, caller.getPackageName(),
+ caller.getUserId());
+
+ return mDevicePolicyEngine.getPolicySizeForAdmin(
+ EnforcingAdmin.createEnforcingAdmin(admin));
+ }
+
+ @Override
public int getHeadlessDeviceOwnerMode(String callerPackageName) {
final CallerIdentity caller = getCallerIdentity(callerPackageName);
enforcePermission(MANAGE_PROFILE_AND_DEVICE_OWNERS, caller.getPackageName(),
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
index d234dee..02590f9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
@@ -21,6 +21,7 @@
import android.app.admin.Authority;
import android.app.admin.DeviceAdminAuthority;
import android.app.admin.DpcAuthority;
+import android.app.admin.PackagePermissionPolicyKey;
import android.app.admin.RoleAuthority;
import android.app.admin.UnknownAuthority;
import android.content.ComponentName;
@@ -105,6 +106,32 @@
userId, activeAdmin);
}
+ static EnforcingAdmin createEnforcingAdmin(android.app.admin.EnforcingAdmin admin) {
+ Objects.requireNonNull(admin);
+ Authority authority = admin.getAuthority();
+ Set<String> internalAuthorities = new HashSet<>();
+ if (DpcAuthority.DPC_AUTHORITY.equals(authority)) {
+ return new EnforcingAdmin(
+ admin.getPackageName(), admin.getComponentName(),
+ Set.of(DPC_AUTHORITY), admin.getUserHandle().getIdentifier(),
+ /* activeAdmin = */ null);
+ } else if (DeviceAdminAuthority.DEVICE_ADMIN_AUTHORITY.equals(authority)) {
+ return new EnforcingAdmin(
+ admin.getPackageName(), admin.getComponentName(),
+ Set.of(DEVICE_ADMIN_AUTHORITY), admin.getUserHandle().getIdentifier(),
+ /* activeAdmin = */ null);
+ } else if (authority instanceof RoleAuthority roleAuthority) {
+ return new EnforcingAdmin(
+ admin.getPackageName(), admin.getComponentName(),
+ Set.of(DEVICE_ADMIN_AUTHORITY), admin.getUserHandle().getIdentifier(),
+ /* activeAdmin = */ null,
+ /* isRoleAuthority = */ true);
+ }
+ return new EnforcingAdmin(admin.getPackageName(), admin.getComponentName(),
+ Set.of(), admin.getUserHandle().getIdentifier(),
+ /* activeAdmin = */ null);
+ }
+
static String getRoleAuthorityOf(String roleName) {
return ROLE_AUTHORITY_PREFIX + roleName;
}
@@ -154,6 +181,20 @@
mActiveAdmin = activeAdmin;
}
+ private EnforcingAdmin(
+ String packageName, @Nullable ComponentName componentName, Set<String> authorities,
+ int userId, @Nullable ActiveAdmin activeAdmin, boolean isRoleAuthority) {
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(authorities);
+
+ mIsRoleAuthority = isRoleAuthority;
+ mPackageName = packageName;
+ mComponentName = componentName;
+ mAuthorities = new HashSet<>(authorities);
+ mUserId = userId;
+ mActiveAdmin = activeAdmin;
+ }
+
private static Set<String> getRoleAuthoritiesOrDefault(String packageName, int userId) {
Set<String> roles = getRoles(packageName, userId);
Set<String> authorities = new HashSet<>();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index eea94a4..b0eee08 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -133,8 +133,8 @@
import com.android.internal.R;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
+import com.android.internal.util.test.LocalServiceKeeperRule;
import com.android.modules.utils.testing.ExtendedMockitoRule;
-import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.display.DisplayManagerService.DeviceStateListener;
@@ -218,6 +218,9 @@
@Rule
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+ @Rule(order = 2)
+ public LocalServiceKeeperRule mLocalServiceKeeperRule = new LocalServiceKeeperRule();
+
private Context mContext;
private Resources mResources;
@@ -354,6 +357,7 @@
@Mock SensorManager mSensorManager;
@Mock DisplayDeviceConfig mMockDisplayDeviceConfig;
@Mock PackageManagerInternal mMockPackageManagerInternal;
+ @Mock DisplayManagerInternal mMockDisplayManagerInternal;
@Mock DisplayAdapter mMockDisplayAdapter;
@Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor;
@@ -371,20 +375,20 @@
when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(false);
mSetFlagsRule.disableFlags(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR);
- LocalServices.removeServiceForTest(InputManagerInternal.class);
- LocalServices.addService(InputManagerInternal.class, mMockInputManagerInternal);
- LocalServices.removeServiceForTest(WindowManagerInternal.class);
- LocalServices.addService(WindowManagerInternal.class, mMockWindowManagerInternal);
- LocalServices.removeServiceForTest(LightsManager.class);
- LocalServices.addService(LightsManager.class, mMockLightsManager);
- LocalServices.removeServiceForTest(SensorManagerInternal.class);
- LocalServices.addService(SensorManagerInternal.class, mMockSensorManagerInternal);
- LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
- LocalServices.addService(
+ mLocalServiceKeeperRule.overrideLocalService(
+ InputManagerInternal.class, mMockInputManagerInternal);
+ mLocalServiceKeeperRule.overrideLocalService(
+ WindowManagerInternal.class, mMockWindowManagerInternal);
+ mLocalServiceKeeperRule.overrideLocalService(
+ LightsManager.class, mMockLightsManager);
+ mLocalServiceKeeperRule.overrideLocalService(
+ SensorManagerInternal.class, mMockSensorManagerInternal);
+ mLocalServiceKeeperRule.overrideLocalService(
VirtualDeviceManagerInternal.class, mMockVirtualDeviceManagerInternal);
- LocalServices.removeServiceForTest(PackageManagerInternal.class);
- LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
- // TODO: b/287945043
+ mLocalServiceKeeperRule.overrideLocalService(
+ PackageManagerInternal.class, mMockPackageManagerInternal);
+ mLocalServiceKeeperRule.overrideLocalService(
+ DisplayManagerInternal.class, mMockDisplayManagerInternal);
Display display = mock(Display.class);
when(display.getDisplayAdjustments()).thenReturn(new DisplayAdjustments());
when(display.getBrightnessInfo()).thenReturn(mock(BrightnessInfo.class));
@@ -2629,11 +2633,19 @@
// Create default display device
createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
callback.waitForExpectedEvent();
+
+ callback.expectsEvent(EVENT_DISPLAY_ADDED);
FakeDisplayDevice displayDevice =
createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_EXTERNAL);
+ callback.waitForExpectedEvent();
+
+ callback.expectsEvent(EVENT_DISPLAY_REMOVED);
+ displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ callback.waitForExpectedEvent();
+
+ callback.expectsEvent(EVENT_DISPLAY_ADDED);
LogicalDisplay display =
logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
- callback.expectsEvent(EVENT_DISPLAY_ADDED);
logicalDisplayMapper.setEnabledLocked(display, /* isEnabled= */ true);
logicalDisplayMapper.updateLogicalDisplays();
callback.waitForExpectedEvent();
@@ -2656,6 +2668,7 @@
LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
bs.registerCallbackWithEventMask(callback, STANDARD_DISPLAY_EVENTS);
+ displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
callback.expectsEvent(EVENT_DISPLAY_ADDED);
// Create default display device
createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
@@ -2669,7 +2682,6 @@
logicalDisplayMapper.setEnabledLocked(display, /* isEnabled= */ true);
logicalDisplayMapper.updateLogicalDisplays();
callback.waitForExpectedEvent();
- callback.clear();
assertThrows(SecurityException.class, () -> bs.disableConnectedDisplay(displayId));
}
@@ -3006,6 +3018,7 @@
new DisplayManagerService(mContext, mBasicInjector);
displayManager.systemReady(false /* safeMode */);
+ displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
ArgumentMatcher<IntentFilter> matchesFilter =
(filter) -> Intent.ACTION_SETTING_RESTORED.equals(filter.getAction(0));
verify(mContext, times(0)).registerReceiver(any(BroadcastReceiver.class),
@@ -3371,7 +3384,7 @@
void waitForExpectedEvent(Duration timeout) {
try {
- assertWithMessage("Event '" + mExpectedEvent + "' is received.")
+ assertWithMessage("Expected '" + mExpectedEvent + "'")
.that(mLatch.await(timeout.toMillis(), TimeUnit.MILLISECONDS)).isTrue();
} catch (InterruptedException ex) {
throw new AssertionError("Waiting for expected event interrupted", ex);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt
index b182cce..0cf0850 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.content.ContextWrapper
import android.hardware.display.BrightnessInfo
+import android.util.SparseBooleanArray
import android.view.Display
import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.SmallTest
@@ -62,8 +63,11 @@
whenever(mockFlags.isVsyncLowLightVoteEnabled).thenReturn(testCase.vsyncLowLightVoteEnabled)
val displayModeDirector = DisplayModeDirector(
spyContext, testHandler, mockInjector, mockFlags)
+ val vrrByDisplay = SparseBooleanArray()
+ vrrByDisplay.put(Display.DEFAULT_DISPLAY, testCase.vrrSupported)
+ displayModeDirector.injectVrrByDisplay(vrrByDisplay)
val brightnessObserver = displayModeDirector.BrightnessObserver(
- spyContext, testHandler, mockInjector, testCase.vrrSupported, mockFlags)
+ spyContext, testHandler, mockInjector, mockFlags)
brightnessObserver.onRefreshRateSettingChangedLocked(0.0f, 120.0f)
brightnessObserver.updateBlockingZoneThresholds(mockDeviceConfig, false)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index fbc38a2..0efd046 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -338,8 +338,6 @@
.thenReturn(false);
when(resources.getBoolean(R.bool.config_refreshRateSynchronizationEnabled))
.thenReturn(false);
- when(resources.getBoolean(R.bool.config_supportsDvrr))
- .thenReturn(false);
when(resources.getInteger(R.integer.config_displayWhiteBalanceBrightnessFilterHorizon))
.thenReturn(10000);
when(resources.getInteger(R.integer.config_defaultPeakRefreshRate))
@@ -393,41 +391,87 @@
private DisplayModeDirector createDirectorFromRefreshRateArray(
float[] refreshRates, int baseModeId, float defaultRefreshRate) {
- Display.Mode[] modes = new Display.Mode[refreshRates.length];
- Display.Mode defaultMode = null;
- for (int i = 0; i < refreshRates.length; i++) {
- modes[i] = new Display.Mode(
- /*modeId=*/baseModeId + i, /*width=*/1000, /*height=*/1000, refreshRates[i]);
- if (refreshRates[i] == defaultRefreshRate) {
- defaultMode = modes[i];
- }
- }
+ return createDirectorFromRefreshRateArray(refreshRates, baseModeId, defaultRefreshRate,
+ new int[]{DISPLAY_ID});
+ }
+
+ private DisplayModeDirector createDirectorFromRefreshRateArray(
+ float[] refreshRates, int baseModeId, float defaultRefreshRate, int[] displayIds) {
+ Display.Mode[] modes = createDisplayModes(refreshRates, baseModeId);
+ Display.Mode defaultMode = getDefaultMode(modes, defaultRefreshRate);
+
assertThat(defaultMode).isNotNull();
- return createDirectorFromModeArray(modes, defaultMode);
+ return createDirectorFromModeArray(modes, defaultMode, displayIds);
}
private DisplayModeDirector createDirectorFromModeArray(Display.Mode[] modes,
Display.Mode defaultMode) {
+ return createDirectorFromModeArray(modes, defaultMode, new int[]{DISPLAY_ID});
+ }
+
+ private DisplayModeDirector createDirectorFromModeArray(Display.Mode[] modes,
+ Display.Mode defaultMode, int[] displayIds) {
DisplayModeDirector director =
new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags);
director.setLoggingEnabled(true);
- SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
- supportedModesByDisplay.put(DISPLAY_ID, modes);
- director.injectSupportedModesByDisplay(supportedModesByDisplay);
- SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<>();
- defaultModesByDisplay.put(DISPLAY_ID, defaultMode);
- director.injectDefaultModeByDisplay(defaultModesByDisplay);
+ setupModesForDisplays(director, displayIds , modes, defaultMode);
return director;
}
private DisplayModeDirector createDirectorFromFpsRange(int minFps, int maxFps) {
+ return createDirectorFromRefreshRateArray(
+ createRefreshRateRanges(minFps, maxFps),
+ /*baseModeId=*/minFps,
+ /*defaultRefreshRate=*/minFps,
+ new int[]{DISPLAY_ID});
+ }
+
+ private DisplayModeDirector createDirectorFromFpsRange(
+ int minFps, int maxFps, int[] displayIds) {
+ return createDirectorFromRefreshRateArray(
+ createRefreshRateRanges(minFps, maxFps),
+ /*baseModeId=*/minFps,
+ /*defaultRefreshRate=*/minFps,
+ displayIds);
+ }
+
+ private void setupModesForDisplays(DisplayModeDirector director, int[] displayIds,
+ Display.Mode[] modes, Display.Mode defaultMode) {
+ SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
+ SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<>();
+ for (int displayId: displayIds) {
+ supportedModesByDisplay.put(displayId, modes);
+ defaultModesByDisplay.put(displayId, defaultMode);
+ }
+ director.injectSupportedModesByDisplay(supportedModesByDisplay);
+ director.injectDefaultModeByDisplay(defaultModesByDisplay);
+ }
+
+ private Display.Mode[] createDisplayModes(float[] refreshRates, int baseModeId) {
+ Display.Mode[] modes = new Display.Mode[refreshRates.length];
+ for (int i = 0; i < refreshRates.length; i++) {
+ modes[i] = new Display.Mode(
+ /*modeId=*/baseModeId + i, /*width=*/1000, /*height=*/1000, refreshRates[i]);
+ }
+ return modes;
+ }
+
+ private Display.Mode getDefaultMode(Display.Mode[] modes, float defaultRefreshRate) {
+ for (Display.Mode mode : modes) {
+ if (mode.getRefreshRate() == defaultRefreshRate) {
+ return mode;
+ }
+ }
+ return null;
+ }
+
+ private float[] createRefreshRateRanges(int minFps, int maxFps) {
int numRefreshRates = maxFps - minFps + 1;
float[] refreshRates = new float[numRefreshRates];
for (int i = 0; i < numRefreshRates; i++) {
refreshRates[i] = minFps + i;
}
- return createDirectorFromRefreshRateArray(refreshRates, /*baseModeId=*/minFps,
- /*defaultRefreshRate=*/minFps);
+ return refreshRates;
}
@Test
@@ -1893,6 +1937,7 @@
mInjector.mDisplayInfo.displayId = DISPLAY_ID_2;
DisplayModeDirector director = createDirectorFromModeArray(TEST_MODES, DEFAULT_MODE_60);
+ director.start(createMockSensorManager());
SparseArray<Vote> votes = new SparseArray<>();
votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 50f));
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
index 92016df..d0dd921 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
@@ -39,6 +39,7 @@
import android.content.ContextWrapper;
import android.content.res.Resources;
import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerInternal;
import android.os.Handler;
import android.os.Looper;
import android.provider.DeviceConfigInterface;
@@ -426,8 +427,12 @@
return true;
}).when(mInjector).getDisplayInfo(eq(EXTERNAL_DISPLAY), /*displayInfo=*/ any());
- doAnswer(c -> mock(SensorManagerInternal.class)).when(mInjector).getSensorManagerInternal();
+ doAnswer(c -> mock(SensorManagerInternal.class))
+ .when(mInjector).getSensorManagerInternal();
doAnswer(c -> mock(DeviceConfigInterface.class)).when(mInjector).getDeviceConfig();
+ doAnswer(c -> mock(DisplayManagerInternal.class))
+ .when(mInjector).getDisplayManagerInternal();
+
mDefaultDisplay = mock(Display.class);
when(mDefaultDisplay.getDisplayId()).thenReturn(DEFAULT_DISPLAY);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
index 230317b..196a202 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
@@ -19,6 +19,8 @@
import android.content.Context
import android.content.ContextWrapper
import android.provider.Settings
+import android.util.SparseBooleanArray
+import android.view.Display
import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.SmallTest
import com.android.internal.util.test.FakeSettingsProvider
@@ -39,7 +41,6 @@
@SmallTest
@RunWith(TestParameterInjector::class)
class SettingsObserverTest {
-
@get:Rule
val mockitoRule = MockitoJUnit.rule()
@@ -68,8 +69,11 @@
val displayModeDirector = DisplayModeDirector(
spyContext, testHandler, mockInjector, mockFlags)
+ val vrrByDisplay = SparseBooleanArray()
+ vrrByDisplay.put(Display.DEFAULT_DISPLAY, testCase.vrrSupported)
+ displayModeDirector.injectVrrByDisplay(vrrByDisplay)
val settingsObserver = displayModeDirector.SettingsObserver(
- spyContext, testHandler, testCase.dvrrSupported, mockFlags)
+ spyContext, testHandler, mockFlags)
settingsObserver.onChange(
false, Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE), 1)
@@ -79,7 +83,7 @@
}
enum class SettingsObserverTestCase(
- val dvrrSupported: Boolean,
+ val vrrSupported: Boolean,
val vsyncLowPowerVoteEnabled: Boolean,
val lowPowerModeEnabled: Boolean,
internal val expectedVote: Vote?
diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp
index 51c9d0a..f2b4136 100644
--- a/services/tests/powerstatstests/Android.bp
+++ b/services/tests/powerstatstests/Android.bp
@@ -4,58 +4,6 @@
default_applicable_licenses: ["frameworks_base_license"],
}
-filegroup {
- name: "power_stats_ravenwood_tests",
- srcs: [
- "src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java",
- "src/com/android/server/power/stats/AggregatedPowerStatsTest.java",
- "src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java",
- "src/com/android/server/power/stats/AudioPowerCalculatorTest.java",
- "src/com/android/server/power/stats/BatteryChargeCalculatorTest.java",
- "src/com/android/server/power/stats/BatteryStatsCounterTest.java",
- "src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java",
- "src/com/android/server/power/stats/BatteryStatsDualTimerTest.java",
- "src/com/android/server/power/stats/BatteryStatsDurationTimerTest.java",
- "src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java",
- "src/com/android/server/power/stats/BatteryStatsHistoryTest.java",
- "src/com/android/server/power/stats/BatteryStatsImplTest.java",
- "src/com/android/server/power/stats/BatteryStatsNoteTest.java",
- "src/com/android/server/power/stats/BatteryStatsSamplingTimerTest.java",
- "src/com/android/server/power/stats/BatteryStatsSensorTest.java",
- "src/com/android/server/power/stats/BatteryStatsServTest.java",
- "src/com/android/server/power/stats/BatteryStatsStopwatchTimerTest.java",
- "src/com/android/server/power/stats/BatteryStatsTimeBaseTest.java",
- "src/com/android/server/power/stats/BatteryStatsTimerTest.java",
- "src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java",
- "src/com/android/server/power/stats/BatteryUsageStatsTest.java",
- "src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java",
- "src/com/android/server/power/stats/CameraPowerCalculatorTest.java",
- "src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java",
- "src/com/android/server/power/stats/CpuPowerCalculatorTest.java",
- "src/com/android/server/power/stats/CustomEnergyConsumerPowerCalculatorTest.java",
- "src/com/android/server/power/stats/EnergyConsumerSnapshotTest.java",
- "src/com/android/server/power/stats/FlashlightPowerCalculatorTest.java",
- "src/com/android/server/power/stats/GnssPowerCalculatorTest.java",
- "src/com/android/server/power/stats/IdlePowerCalculatorTest.java",
- "src/com/android/server/power/stats/LongSamplingCounterArrayTest.java",
- "src/com/android/server/power/stats/LongSamplingCounterTest.java",
- "src/com/android/server/power/stats/MemoryPowerCalculatorTest.java",
- "src/com/android/server/power/stats/MultiStateStatsTest.java",
- "src/com/android/server/power/stats/PowerStatsAggregatorTest.java",
- "src/com/android/server/power/stats/PowerStatsCollectorTest.java",
- "src/com/android/server/power/stats/PowerStatsExporterTest.java",
- "src/com/android/server/power/stats/PowerStatsSchedulerTest.java",
- "src/com/android/server/power/stats/PowerStatsStoreTest.java",
- "src/com/android/server/power/stats/PowerStatsUidResolverTest.java",
- "src/com/android/server/power/stats/ScreenPowerCalculatorTest.java",
- "src/com/android/server/power/stats/SensorPowerCalculatorTest.java",
- "src/com/android/server/power/stats/UserPowerCalculatorTest.java",
- "src/com/android/server/power/stats/VideoPowerCalculatorTest.java",
- "src/com/android/server/power/stats/WakelockPowerCalculatorTest.java",
- "src/com/android/server/power/stats/WifiPowerCalculatorTest.java",
- ],
-}
-
android_test {
name: "PowerStatsTests",
@@ -79,7 +27,6 @@
"servicestests-utils",
"platform-test-annotations",
"flag-junit",
- "ravenwood-junit",
],
libs: [
@@ -112,17 +59,20 @@
name: "PowerStatsTestsRavenwood",
static_libs: [
"services.core",
- "modules-utils-binary-xml",
+ "coretests-aidl",
+ "ravenwood-junit",
+ "truth",
"androidx.annotation_annotation",
"androidx.test.rules",
- "truth",
+ "androidx.test.uiautomator_uiautomator",
+ "modules-utils-binary-xml",
+ "flag-junit",
],
srcs: [
- ":power_stats_ravenwood_tests",
-
- "src/com/android/server/power/stats/BatteryUsageStatsRule.java",
- "src/com/android/server/power/stats/MockBatteryStatsImpl.java",
- "src/com/android/server/power/stats/MockClock.java",
+ "src/com/android/server/power/stats/*.java",
+ ],
+ java_resources: [
+ "res/xml/power_profile*.xml",
],
auto_gen_config: true,
}
diff --git a/services/tests/powerstatstests/TEST_MAPPING b/services/tests/powerstatstests/TEST_MAPPING
index 6d3db1c..fb24361 100644
--- a/services/tests/powerstatstests/TEST_MAPPING
+++ b/services/tests/powerstatstests/TEST_MAPPING
@@ -12,7 +12,11 @@
"ravenwood-presubmit": [
{
"name": "PowerStatsTestsRavenwood",
- "host": true
+ "host": true,
+ "options": [
+ {"include-filter": "com.android.server.power.stats"},
+ {"exclude-annotation": "android.platform.test.annotations.DisabledOnRavenwood"}
+ ]
}
],
"postsubmit": [
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java
index ca7de7c..9975190 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java
@@ -20,6 +20,7 @@
import android.os.BatteryConsumer;
import android.os.PersistableBundle;
+import android.util.SparseArray;
import android.util.Xml;
import androidx.test.filters.SmallTest;
@@ -43,6 +44,9 @@
private static final int TEST_POWER_COMPONENT = 1077;
private static final int APP_1 = 27;
private static final int APP_2 = 42;
+ private static final int COMPONENT_STATE_0 = 0;
+ private static final int COMPONENT_STATE_1 = 1;
+ private static final int COMPONENT_STATE_2 = 2;
private AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
private PowerStats.Descriptor mPowerComponentDescriptor;
@@ -59,8 +63,10 @@
AggregatedPowerStatsConfig.STATE_SCREEN,
AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
- mPowerComponentDescriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT, "fan", 2, 3,
- PersistableBundle.forPair("speed", "fast"));
+ SparseArray<String> stateLabels = new SparseArray<>();
+ stateLabels.put(COMPONENT_STATE_1, "one");
+ mPowerComponentDescriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT, "fan", 2,
+ stateLabels, 1, 3, PersistableBundle.forPair("speed", "fast"));
}
@Test
@@ -107,6 +113,9 @@
ps.stats[0] = 100;
ps.stats[1] = 987;
+ ps.stateStats.put(COMPONENT_STATE_0, new long[]{1111});
+ ps.stateStats.put(COMPONENT_STATE_1, new long[]{5000});
+
ps.uidStats.put(APP_1, new long[]{389, 0, 739});
ps.uidStats.put(APP_2, new long[]{278, 314, 628});
@@ -120,11 +129,14 @@
ps.stats[0] = 444;
ps.stats[1] = 0;
+ ps.stateStats.clear();
+ ps.stateStats.put(COMPONENT_STATE_1, new long[]{1000});
+ ps.stateStats.put(COMPONENT_STATE_2, new long[]{9000});
+
ps.uidStats.put(APP_1, new long[]{0, 0, 400});
ps.uidStats.put(APP_2, new long[]{100, 200, 300});
stats.addPowerStats(ps, 5000);
-
return stats;
}
@@ -147,6 +159,31 @@
AggregatedPowerStatsConfig.SCREEN_STATE_OTHER))
.isEqualTo(new long[]{222, 0});
+ assertThat(getStateStats(stats, COMPONENT_STATE_0,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_ON))
+ .isEqualTo(new long[]{1111});
+
+ assertThat(getStateStats(stats, COMPONENT_STATE_1,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_ON))
+ .isEqualTo(new long[]{5500});
+
+ assertThat(getStateStats(stats, COMPONENT_STATE_1,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_OTHER))
+ .isEqualTo(new long[]{500});
+
+ assertThat(getStateStats(stats, COMPONENT_STATE_2,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_ON))
+ .isEqualTo(new long[]{4500});
+
+ assertThat(getStateStats(stats, COMPONENT_STATE_2,
+ AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+ AggregatedPowerStatsConfig.SCREEN_STATE_OTHER))
+ .isEqualTo(new long[]{4500});
+
assertThat(getUidDeviceStats(stats,
APP_1,
AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
@@ -191,14 +228,26 @@
}
private static long[] getDeviceStats(AggregatedPowerStats stats, int... states) {
- long[] out = new long[states.length];
- stats.getPowerComponentStats(TEST_POWER_COMPONENT).getDeviceStats(out, states);
+ PowerComponentAggregatedPowerStats powerComponentStats =
+ stats.getPowerComponentStats(TEST_POWER_COMPONENT);
+ long[] out = new long[powerComponentStats.getPowerStatsDescriptor().statsArrayLength];
+ powerComponentStats.getDeviceStats(out, states);
+ return out;
+ }
+
+ private static long[] getStateStats(AggregatedPowerStats stats, int key, int... states) {
+ PowerComponentAggregatedPowerStats powerComponentStats =
+ stats.getPowerComponentStats(TEST_POWER_COMPONENT);
+ long[] out = new long[powerComponentStats.getPowerStatsDescriptor().stateStatsArrayLength];
+ powerComponentStats.getStateStats(out, key, states);
return out;
}
private static long[] getUidDeviceStats(AggregatedPowerStats stats, int uid, int... states) {
- long[] out = new long[states.length];
- stats.getPowerComponentStats(TEST_POWER_COMPONENT).getUidStats(out, uid, states);
+ PowerComponentAggregatedPowerStats powerComponentStats =
+ stats.getPowerComponentStats(TEST_POWER_COMPONENT);
+ long[] out = new long[powerComponentStats.getPowerStatsDescriptor().uidStatsArrayLength];
+ powerComponentStats.getUidStats(out, uid, states);
return out;
}
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java
index 3ab1c2e..9b45ca7 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryChargeCalculatorTest.java
@@ -16,9 +16,11 @@
package com.android.server.power.stats;
-
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
import android.os.BatteryManager;
import android.os.BatteryUsageStats;
import android.platform.test.ravenwood.RavenwoodRule;
@@ -28,6 +30,7 @@
import com.android.internal.os.PowerProfile;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,6 +49,11 @@
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
.setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 4000.0);
+ @Before
+ public void setup() {
+ mStatsRule.getBatteryStats().onSystemReady(mock(Context.class));
+ }
+
@Test
public void testDischargeTotals() {
// Nominal battery capacity should be ignored
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
index 997b771..0a9c8c0 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
@@ -36,6 +36,7 @@
import android.hardware.power.stats.EnergyMeasurement;
import android.hardware.power.stats.PowerEntity;
import android.hardware.power.stats.StateResidencyResult;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.power.PowerStatsInternal;
import android.util.IntArray;
import android.util.SparseArray;
@@ -47,6 +48,7 @@
import com.android.internal.os.PowerProfile;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import java.util.Arrays;
@@ -59,7 +61,10 @@
* atest FrameworksServicesTests:BatteryExternalStatsWorkerTest
*/
@SuppressWarnings("GuardedBy")
+@android.platform.test.annotations.DisabledOnRavenwood
public class BatteryExternalStatsWorkerTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
private BatteryExternalStatsWorker mBatteryExternalStatsWorker;
private TestBatteryStatsImpl mBatteryStatsImpl;
private TestPowerStatsInternal mPowerStatsInternal;
@@ -215,7 +220,8 @@
public class TestBatteryStatsImpl extends BatteryStatsImpl {
public TestBatteryStatsImpl(Context context) {
- super(Clock.SYSTEM_CLOCK, null, null, null, null, null, null);
+ super(new BatteryStatsConfig.Builder().build(), Clock.SYSTEM_CLOCK, null, null, null,
+ null, null, null);
mPowerProfile = new PowerProfile(context, true /* forTest */);
SparseArray<int[]> cpusByPolicy = new SparseArray<>();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBackgroundStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBackgroundStatsTest.java
index 4d3fcb6..ad05b51 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBackgroundStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBackgroundStatsTest.java
@@ -18,25 +18,37 @@
import static android.os.BatteryStats.STATS_SINCE_CHARGED;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
import android.app.ActivityManager;
import android.os.BatteryStats;
import android.os.WorkSource;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.ArrayMap;
import android.view.Display;
import androidx.test.filters.SmallTest;
-import junit.framework.TestCase;
+import org.junit.Rule;
+import org.junit.Test;
/**
* Test BatteryStatsImpl onBatteryBackgroundTimeBase TimeBase.
*/
-public class BatteryStatsBackgroundStatsTest extends TestCase {
+public class BatteryStatsBackgroundStatsTest {
+
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
private static final int UID = 10500;
/** Test that BatteryStatsImpl.Uid.mOnBatteryBackgroundTimeBase works correctly. */
@SmallTest
+ @Test
public void testBgTimeBase() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
@@ -105,6 +117,7 @@
/** Test that BatteryStatsImpl.Uid.mOnBatteryScreenOffBackgroundTimeBase works correctly. */
@SmallTest
+ @Test
public void testScreenOffBgTimeBase() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
@@ -153,6 +166,7 @@
}
@SmallTest
+ @Test
public void testWifiScan() throws Exception {
final MockClock clocks = new MockClock();
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
@@ -195,11 +209,13 @@
}
@SmallTest
+ @Test
public void testAppBluetoothScan() throws Exception {
doTestAppBluetoothScanInternal(new WorkSource(UID));
}
@SmallTest
+ @Test
public void testAppBluetoothScan_workChain() throws Exception {
WorkSource ws = new WorkSource();
ws.createWorkChain().addNode(UID, "foo");
@@ -275,6 +291,7 @@
}
@SmallTest
+ @Test
public void testJob() throws Exception {
final MockClock clocks = new MockClock();
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
@@ -336,6 +353,7 @@
}
@SmallTest
+ @Test
public void testSyncs() throws Exception {
final MockClock clocks = new MockClock();
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java
index 3f101a9..4dfc3fc 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsBinderCallStatsTest.java
@@ -16,20 +16,20 @@
package com.android.server.power.stats;
+import static org.junit.Assert.assertEquals;
+
import android.os.Binder;
import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.ArraySet;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.BinderTransactionNameResolver;
-import junit.framework.TestCase;
-
+import org.junit.Rule;
import org.junit.Test;
-import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.Collection;
@@ -37,9 +37,14 @@
/**
* Test cases for android.os.BatteryStats, system server Binder call stats.
*/
-@RunWith(AndroidJUnit4.class)
@SmallTest
-public class BatteryStatsBinderCallStatsTest extends TestCase {
+@android.platform.test.annotations.DisabledOnRavenwood(blockedBy = BinderCallsStats.class)
+public class BatteryStatsBinderCallStatsTest {
+
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
private static final int TRANSACTION_CODE1 = 100;
private static final int TRANSACTION_CODE2 = 101;
@@ -89,7 +94,6 @@
assertEquals(500, value.recordedCpuTimeMicros);
}
-
@Test
public void testProportionalSystemServiceUsage_noStatsForSomeMethods() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java
index 6e62147..eff1b7b 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsCpuTimesTest.java
@@ -118,7 +118,8 @@
mClocks = new MockClock();
Handler handler = new Handler(Looper.getMainLooper());
mPowerStatsUidResolver = new PowerStatsUidResolver();
- mBatteryStatsImpl = new MockBatteryStatsImpl(mClocks, null, handler, mPowerStatsUidResolver)
+ mBatteryStatsImpl = new MockBatteryStatsImpl(MockBatteryStatsImpl.DEFAULT_CONFIG,
+ mClocks, null, handler, mPowerStatsUidResolver)
.setTestCpuScalingPolicies()
.setKernelCpuUidUserSysTimeReader(mCpuUidUserSysTimeReader)
.setKernelCpuUidFreqTimeReader(mCpuUidFreqTimeReader)
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
index c58c92b..e40a3e3 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
@@ -403,7 +403,7 @@
@Test
public void recordPowerStats() {
- PowerStats.Descriptor descriptor = new PowerStats.Descriptor(42, "foo", 1, 2,
+ PowerStats.Descriptor descriptor = new PowerStats.Descriptor(42, "foo", 1, null, 0, 2,
new PersistableBundle());
PowerStats powerStats = new PowerStats(descriptor);
powerStats.durationMs = 100;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsManagerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsManagerTest.java
index 7ae1117..9a64ce1 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsManagerTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsManagerTest.java
@@ -25,15 +25,21 @@
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.UidBatteryConsumer;
+import android.platform.test.ravenwood.RavenwoodRule;
+import org.junit.Rule;
import org.junit.Test;
/**
* Test BatteryStatsManager and CellularBatteryStats to ensure that valid data is being reported
* and that invalid data is not reported.
*/
+@android.platform.test.annotations.DisabledOnRavenwood(reason = "Integration test")
public class BatteryStatsManagerTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
@Test
public void testBatteryUsageStatsDataConsistency() {
BatteryStatsManager bsm = getContext().getSystemService(BatteryStatsManager.class);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
index 07cefa9..afbe9159 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
@@ -170,8 +170,8 @@
public void testNoteStartWakeLocked_isolatedUid() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
PowerStatsUidResolver uidResolver = new PowerStatsUidResolver();
- MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks, null,
- new Handler(Looper.getMainLooper()), uidResolver);
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(MockBatteryStatsImpl.DEFAULT_CONFIG,
+ clocks, null, new Handler(Looper.getMainLooper()), uidResolver);
int pid = 10;
String name = "name";
@@ -212,8 +212,8 @@
public void testNoteStartWakeLocked_isolatedUidRace() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
PowerStatsUidResolver uidResolver = new PowerStatsUidResolver();
- MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks, null,
- new Handler(Looper.getMainLooper()), uidResolver);
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(MockBatteryStatsImpl.DEFAULT_CONFIG,
+ clocks, null, new Handler(Looper.getMainLooper()), uidResolver);
int pid = 10;
String name = "name";
@@ -256,8 +256,8 @@
public void testNoteLongPartialWakelockStart_isolatedUid() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
PowerStatsUidResolver uidResolver = new PowerStatsUidResolver();
- MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks, null,
- new Handler(Looper.getMainLooper()), uidResolver);
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(MockBatteryStatsImpl.DEFAULT_CONFIG,
+ clocks, null, new Handler(Looper.getMainLooper()), uidResolver);
bi.setRecordAllHistoryLocked(true);
bi.forceRecordAllHistory();
@@ -311,8 +311,8 @@
public void testNoteLongPartialWakelockStart_isolatedUidRace() throws Exception {
final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
PowerStatsUidResolver uidResolver = new PowerStatsUidResolver();
- MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks, null,
- new Handler(Looper.getMainLooper()), uidResolver);
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(MockBatteryStatsImpl.DEFAULT_CONFIG,
+ clocks, null, new Handler(Looper.getMainLooper()), uidResolver);
bi.setRecordAllHistoryLocked(true);
bi.forceRecordAllHistory();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java
index a0fb631..d29bf1a 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java
@@ -18,21 +18,32 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+
import android.content.Context;
import android.os.BatteryManager;
+import android.platform.test.ravenwood.RavenwoodRule;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.IOException;
+import java.nio.file.Files;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BatteryStatsResetTest {
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
private static final int BATTERY_NOMINAL_VOLTAGE_MV = 3700;
private static final int BATTERY_CAPACITY_UAH = 4_000_000;
private static final int BATTERY_CHARGE_RATE_SECONDS_PER_LEVEL = 100;
@@ -79,13 +90,11 @@
private long mBatteryChargeTimeToFullSeconds;
@Before
- public void setUp() {
- final Context context = InstrumentationRegistry.getContext();
-
+ public void setUp() throws IOException {
mMockClock = new MockClock();
- mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock, context.getFilesDir());
- mBatteryStatsImpl.onSystemReady();
-
+ mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock,
+ Files.createTempDirectory("BatteryStatsResetTest").toFile());
+ mBatteryStatsImpl.onSystemReady(mock(Context.class));
// Set up the battery state. Start off with a fully charged plugged in battery.
mBatteryStatus = BatteryManager.BATTERY_STATUS_FULL;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsUserLifecycleTests.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsUserLifecycleTests.java
index c4561b1..3931201 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsUserLifecycleTests.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsUserLifecycleTests.java
@@ -28,6 +28,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.ArraySet;
import androidx.test.InstrumentationRegistry;
@@ -38,6 +39,7 @@
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,8 +48,10 @@
@LargeTest
@RunWith(AndroidJUnit4.class)
-@android.platform.test.annotations.IgnoreUnderRavenwood
+@android.platform.test.annotations.DisabledOnRavenwood(reason = "Integration test")
public class BatteryStatsUserLifecycleTests {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
private static final long POLL_INTERVAL_MS = 500;
private static final long USER_REMOVE_TIMEOUT_MS = 5_000;
@@ -65,6 +69,10 @@
@BeforeClass
public static void setUpOnce() {
+ if (RavenwoodRule.isOnRavenwood()) {
+ return;
+ }
+
assumeTrue(UserManager.getMaxSupportedUsers() > 1);
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
index 296ad0e..2d7cb22 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
@@ -24,7 +24,8 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
-import android.annotation.XmlRes;
+import android.content.Context;
+import android.content.res.Resources;
import android.net.NetworkStats;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
@@ -35,9 +36,9 @@
import android.os.HandlerThread;
import android.os.UidBatteryConsumer;
import android.os.UserBatteryConsumer;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.SparseArray;
-
-import androidx.test.InstrumentationRegistry;
+import android.util.Xml;
import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.PowerProfile;
@@ -47,6 +48,7 @@
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.mockito.stubbing.Answer;
+import org.xmlpull.v1.XmlPullParser;
import java.io.File;
import java.io.IOException;
@@ -81,6 +83,7 @@
private boolean[] mSupportedStandardBuckets;
private String[] mCustomPowerComponentNames;
private Throwable mThrowable;
+ private final BatteryStatsImpl.BatteryStatsConfig.Builder mBatteryStatsConfigBuilder;
public BatteryUsageStatsRule() {
this(0);
@@ -94,6 +97,11 @@
mCpusByPolicy.put(4, new int[]{4, 5, 6, 7});
mFreqsByPolicy.put(0, new int[]{300000, 1000000, 2000000});
mFreqsByPolicy.put(4, new int[]{300000, 1000000, 2500000, 3000000});
+ mBatteryStatsConfigBuilder = new BatteryStatsImpl.BatteryStatsConfig.Builder()
+ .setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_CPU,
+ 10000)
+ .setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ 10000);
}
private void initBatteryStats() {
@@ -107,7 +115,8 @@
}
clearDirectory();
}
- mBatteryStats = new MockBatteryStatsImpl(mMockClock, mHistoryDir, mHandler);
+ mBatteryStats = new MockBatteryStatsImpl(mBatteryStatsConfigBuilder.build(),
+ mMockClock, mHistoryDir, mHandler, new PowerStatsUidResolver());
mBatteryStats.setPowerProfile(mPowerProfile);
mBatteryStats.setCpuScalingPolicies(new CpuScalingPolicies(mCpusByPolicy, mFreqsByPolicy));
synchronized (mBatteryStats) {
@@ -116,8 +125,6 @@
}
mBatteryStats.informThatAllExternalStatsAreFlushed();
- mBatteryStats.onSystemReady();
-
if (mDisplayCount != -1) {
mBatteryStats.setDisplayCountLocked(mDisplayCount);
}
@@ -148,11 +155,27 @@
return this;
}
- public BatteryUsageStatsRule setTestPowerProfile(@XmlRes int xmlId) {
- mPowerProfile.forceInitForTesting(InstrumentationRegistry.getContext(), xmlId);
+ public BatteryUsageStatsRule setTestPowerProfile(String resourceName) {
+ mPowerProfile.initForTesting(resolveParser(resourceName));
return this;
}
+ public static XmlPullParser resolveParser(String resourceName) {
+ if (RavenwoodRule.isOnRavenwood()) {
+ try {
+ return Xml.resolvePullParser(BatteryUsageStatsRule.class.getClassLoader()
+ .getResourceAsStream("res/xml/" + resourceName + ".xml"));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ Context context = androidx.test.InstrumentationRegistry.getContext();
+ Resources resources = context.getResources();
+ int resId = resources.getIdentifier(resourceName, "xml", context.getPackageName());
+ return resources.getXml(resId);
+ }
+ }
+
public BatteryUsageStatsRule setCpuScalingPolicy(int policy, int[] relatedCpus,
int[] frequencies) {
if (mDefaultCpuScalingPolicy) {
@@ -265,6 +288,12 @@
return this;
}
+ public BatteryUsageStatsRule setPowerStatsThrottlePeriodMillis(int powerComponent,
+ long throttleMs) {
+ mBatteryStatsConfigBuilder.setPowerStatsThrottlePeriodMillis(powerComponent, throttleMs);
+ return this;
+ }
+
public BatteryUsageStatsRule startWithScreenOn(boolean screenOn) {
mScreenOn = screenOn;
return this;
@@ -291,23 +320,21 @@
}
private void before() {
- initBatteryStats();
HandlerThread bgThread = new HandlerThread("bg thread");
bgThread.setUncaughtExceptionHandler((thread, throwable)-> {
mThrowable = throwable;
});
bgThread.start();
mHandler = new Handler(bgThread.getLooper());
- mBatteryStats.setHandler(mHandler);
+
+ initBatteryStats();
mBatteryStats.setOnBatteryInternal(true);
mBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0);
mBatteryStats.getOnBatteryScreenOffTimeBase().setRunning(!mScreenOn, 0, 0);
}
private void after() throws Throwable {
- if (mHandler != null) {
- waitForBackgroundThread();
- }
+ waitForBackgroundThread();
}
public void waitForBackgroundThread() throws Throwable {
@@ -316,11 +343,12 @@
}
ConditionVariable done = new ConditionVariable();
- mHandler.post(done::open);
- assertThat(done.block(10000)).isTrue();
-
- if (mThrowable != null) {
- throw mThrowable;
+ if (mHandler.post(done::open)) {
+ boolean success = done.block(5000);
+ if (mThrowable != null) {
+ throw mThrowable;
+ }
+ assertThat(success).isTrue();
}
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BstatsCpuTimesValidationTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BstatsCpuTimesValidationTest.java
index 29e2f5e..e4ab227 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BstatsCpuTimesValidationTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BstatsCpuTimesValidationTest.java
@@ -46,6 +46,7 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.SystemClock;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.DebugUtils;
@@ -74,9 +75,11 @@
import java.util.regex.Pattern;
@LargeTest
-@RunWith(AndroidJUnit4.class)
-@android.platform.test.annotations.IgnoreUnderRavenwood
+@android.platform.test.annotations.DisabledOnRavenwood(reason = "Integration test")
public class BstatsCpuTimesValidationTest {
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
private static final String TAG = BstatsCpuTimesValidationTest.class.getSimpleName();
private static final String TEST_PKG = "com.android.coretests.apps.bstatstestapp";
@@ -112,10 +115,15 @@
private static boolean sCpuFreqTimesAvailable;
private static boolean sPerProcStateTimesAvailable;
- @Rule public TestName testName = new TestName();
+ @Rule(order = 1)
+ public TestName testName = new TestName();
@BeforeClass
public static void setupOnce() throws Exception {
+ if (RavenwoodRule.isOnRavenwood()) {
+ return;
+ }
+
sContext = InstrumentationRegistry.getContext();
sUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
sContext.getPackageManager().setApplicationEnabledSetting(TEST_PKG,
@@ -127,6 +135,10 @@
@AfterClass
public static void tearDownOnce() throws Exception {
+ if (RavenwoodRule.isOnRavenwood()) {
+ return;
+ }
+
executeCmd("cmd deviceidle whitelist -" + TEST_PKG);
if (sBatteryStatsConstsUpdated) {
Settings.Global.putString(sContext.getContentResolver(),
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
index 64d5414..ad29392 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
@@ -23,65 +23,127 @@
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.when;
-import android.content.Context;
-import android.hardware.power.stats.EnergyConsumer;
-import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryConsumer;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
-import android.power.PowerStatsInternal;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.util.SparseArray;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.frameworks.powerstatstests.R;
+import com.android.internal.os.Clock;
import com.android.internal.os.CpuScalingPolicies;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.PowerStats;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParserException;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
+import java.io.IOException;
+import java.util.function.IntSupplier;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class CpuPowerStatsCollectorTest {
+
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
private static final int ISOLATED_UID = 99123;
private static final int UID_1 = 42;
private static final int UID_2 = 99;
- private Context mContext;
private final MockClock mMockClock = new MockClock();
private final HandlerThread mHandlerThread = new HandlerThread("test");
private Handler mHandler;
private PowerStats mCollectedStats;
- private PowerProfile mPowerProfile;
+ private PowerProfile mPowerProfile = new PowerProfile();
@Mock
private PowerStatsUidResolver mUidResolver;
@Mock
private CpuPowerStatsCollector.KernelCpuStatsReader mMockKernelCpuStatsReader;
@Mock
- private PowerStatsInternal mPowerStatsInternal;
+ private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
private CpuScalingPolicies mCpuScalingPolicies;
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mContext = InstrumentationRegistry.getContext();
+ private class TestInjector implements CpuPowerStatsCollector.Injector {
+ private final int mDefaultCpuPowerBrackets;
+ private final int mDefaultCpuPowerBracketsPerEnergyConsumer;
+ TestInjector(int defaultCpuPowerBrackets, int defaultCpuPowerBracketsPerEnergyConsumer) {
+ mDefaultCpuPowerBrackets = defaultCpuPowerBrackets;
+ mDefaultCpuPowerBracketsPerEnergyConsumer = defaultCpuPowerBracketsPerEnergyConsumer;
+ }
+
+ @Override
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ @Override
+ public Clock getClock() {
+ return mMockClock;
+ }
+
+ @Override
+ public PowerStatsUidResolver getUidResolver() {
+ return mUidResolver;
+ }
+
+ @Override
+ public CpuScalingPolicies getCpuScalingPolicies() {
+ return mCpuScalingPolicies;
+ }
+
+ @Override
+ public PowerProfile getPowerProfile() {
+ return mPowerProfile;
+ }
+
+ @Override
+ public CpuPowerStatsCollector.KernelCpuStatsReader getKernelCpuStatsReader() {
+ return mMockKernelCpuStatsReader;
+ }
+
+ @Override
+ public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+ return mConsumedEnergyRetriever;
+ }
+
+ @Override
+ public IntSupplier getVoltageSupplier() {
+ return () -> 3500;
+ }
+
+ @Override
+ public int getDefaultCpuPowerBrackets() {
+ return mDefaultCpuPowerBrackets;
+ }
+
+ @Override
+ public int getDefaultCpuPowerBracketsPerEnergyConsumer() {
+ return mDefaultCpuPowerBracketsPerEnergyConsumer;
+ }
+ };
+
+ @Before
+ public void setup() throws XmlPullParserException, IOException {
+ MockitoAnnotations.initMocks(this);
mHandlerThread.start();
mHandler = mHandlerThread.getThreadHandler();
- when(mMockKernelCpuStatsReader.nativeIsSupportedFeature()).thenReturn(true);
+ when(mMockKernelCpuStatsReader.isSupportedFeature()).thenReturn(true);
when(mUidResolver.mapUid(anyInt())).thenAnswer(invocation -> {
int uid = invocation.getArgument(0);
if (uid == ISOLATED_UID) {
@@ -90,12 +152,13 @@
return uid;
}
});
+ when(mConsumedEnergyRetriever.getEnergyConsumerIds(anyInt())).thenReturn(new int[0]);
}
@Test
public void powerBrackets_specifiedInPowerProfile() {
- mPowerProfile = new PowerProfile(mContext);
- mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test_power_brackets);
+ mPowerProfile.initForTesting(
+ BatteryUsageStatsRule.resolveParser("power_profile_test_power_brackets"));
mCpuScalingPolicies = new CpuScalingPolicies(
new SparseArray<>() {{
put(0, new int[]{0});
@@ -114,8 +177,7 @@
@Test
public void powerBrackets_default_noEnergyConsumers() {
- mPowerProfile = new PowerProfile(mContext);
- mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
+ mPowerProfile.initForTesting(BatteryUsageStatsRule.resolveParser("power_profile_test"));
mockCpuScalingPolicies(2);
CpuPowerStatsCollector collector = createCollector(3, 0);
@@ -134,8 +196,7 @@
@Test
public void powerBrackets_moreBracketsThanStates() {
- mPowerProfile = new PowerProfile(mContext);
- mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
+ mPowerProfile.initForTesting(BatteryUsageStatsRule.resolveParser("power_profile_test"));
mockCpuScalingPolicies(2);
CpuPowerStatsCollector collector = createCollector(8, 0);
@@ -146,8 +207,7 @@
@Test
public void powerBrackets_energyConsumers() throws Exception {
- mPowerProfile = new PowerProfile(mContext);
- mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
+ mPowerProfile.initForTesting(BatteryUsageStatsRule.resolveParser("power_profile_test"));
mockCpuScalingPolicies(2);
mockEnergyConsumers();
@@ -159,8 +219,7 @@
@Test
public void powerStatsDescriptor() throws Exception {
- mPowerProfile = new PowerProfile(mContext);
- mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
+ mPowerProfile.initForTesting(BatteryUsageStatsRule.resolveParser("power_profile_test"));
mockCpuScalingPolicies(2);
mockEnergyConsumers();
@@ -170,8 +229,8 @@
assertThat(descriptor.name).isEqualTo("cpu");
assertThat(descriptor.statsArrayLength).isEqualTo(13);
assertThat(descriptor.uidStatsArrayLength).isEqualTo(5);
- CpuPowerStatsCollector.CpuStatsArrayLayout layout =
- new CpuPowerStatsCollector.CpuStatsArrayLayout();
+ CpuPowerStatsLayout layout =
+ new CpuPowerStatsLayout();
layout.fromExtras(descriptor.extras);
long[] deviceStats = new long[descriptor.statsArrayLength];
@@ -209,8 +268,8 @@
mockEnergyConsumers();
CpuPowerStatsCollector collector = createCollector(8, 0);
- CpuPowerStatsCollector.CpuStatsArrayLayout layout =
- new CpuPowerStatsCollector.CpuStatsArrayLayout();
+ CpuPowerStatsLayout layout =
+ new CpuPowerStatsLayout();
layout.fromExtras(collector.getPowerStatsDescriptor().extras);
mockKernelCpuStats(new long[]{1111, 2222, 3333},
@@ -296,10 +355,9 @@
private CpuPowerStatsCollector createCollector(int defaultCpuPowerBrackets,
int defaultCpuPowerBracketsPerEnergyConsumer) {
- CpuPowerStatsCollector collector = new CpuPowerStatsCollector(mCpuScalingPolicies,
- mPowerProfile, mHandler, mMockKernelCpuStatsReader, mUidResolver,
- () -> mPowerStatsInternal, () -> 3500, 60_000, mMockClock,
- defaultCpuPowerBrackets, defaultCpuPowerBracketsPerEnergyConsumer);
+ CpuPowerStatsCollector collector = new CpuPowerStatsCollector(
+ new TestInjector(defaultCpuPowerBrackets, defaultCpuPowerBracketsPerEnergyConsumer),
+ 0);
collector.addConsumer(stats -> mCollectedStats = stats);
collector.setEnabled(true);
return collector;
@@ -307,7 +365,7 @@
private void mockKernelCpuStats(long[] deviceStats, SparseArray<long[]> uidToCpuStats,
long expectedLastUpdateTimestampMs, long newLastUpdateTimestampMs) {
- when(mMockKernelCpuStatsReader.nativeReadCpuStats(
+ when(mMockKernelCpuStatsReader.readCpuStats(
any(CpuPowerStatsCollector.KernelCpuStatsCallback.class),
any(int[].class), anyLong(), any(long[].class), any(long[].class)))
.thenAnswer(invocation -> {
@@ -335,63 +393,18 @@
});
}
- @SuppressWarnings("unchecked")
- private void mockEnergyConsumers() throws Exception {
- when(mPowerStatsInternal.getEnergyConsumerInfo())
- .thenReturn(new EnergyConsumer[]{
- new EnergyConsumer() {{
- id = 1;
- type = EnergyConsumerType.CPU_CLUSTER;
- ordinal = 0;
- name = "CPU0";
- }},
- new EnergyConsumer() {{
- id = 2;
- type = EnergyConsumerType.CPU_CLUSTER;
- ordinal = 1;
- name = "CPU4";
- }},
- new EnergyConsumer() {{
- id = 3;
- type = EnergyConsumerType.BLUETOOTH;
- name = "BT";
- }},
- });
-
- CompletableFuture<EnergyConsumerResult[]> future1 = mock(CompletableFuture.class);
- when(future1.get(anyLong(), any(TimeUnit.class)))
- .thenReturn(new EnergyConsumerResult[]{
- new EnergyConsumerResult() {{
- id = 1;
- energyUWs = 1000;
- }},
- new EnergyConsumerResult() {{
- id = 2;
- energyUWs = 2000;
- }}
- });
-
- CompletableFuture<EnergyConsumerResult[]> future2 = mock(CompletableFuture.class);
- when(future2.get(anyLong(), any(TimeUnit.class)))
- .thenReturn(new EnergyConsumerResult[]{
- new EnergyConsumerResult() {{
- id = 1;
- energyUWs = 1500;
- }},
- new EnergyConsumerResult() {{
- id = 2;
- energyUWs = 2700;
- }}
- });
-
- when(mPowerStatsInternal.getEnergyConsumedAsync(eq(new int[]{1, 2})))
- .thenReturn(future1)
- .thenReturn(future2);
+ private void mockEnergyConsumers() {
+ reset(mConsumedEnergyRetriever);
+ when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.CPU_CLUSTER))
+ .thenReturn(new int[]{1, 2});
+ when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{1, 2})))
+ .thenReturn(new long[]{1000, 2000})
+ .thenReturn(new long[]{1500, 2700});
}
private static int[] getScalingStepToPowerBracketMap(CpuPowerStatsCollector collector) {
- CpuPowerStatsCollector.CpuStatsArrayLayout layout =
- new CpuPowerStatsCollector.CpuStatsArrayLayout();
+ CpuPowerStatsLayout layout =
+ new CpuPowerStatsLayout();
layout.fromExtras(collector.getPowerStatsDescriptor().extras);
return layout.getScalingStepToPowerBracketMap();
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
index cbce7e8..70c40f5 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
@@ -28,6 +28,8 @@
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.RavenwoodFlagsValueProvider;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.provider.DeviceConfig;
import androidx.test.InstrumentationRegistry;
@@ -52,11 +54,15 @@
@RunWith(AndroidJUnit4.class)
@LargeTest
-@android.platform.test.annotations.IgnoreUnderRavenwood
+@android.platform.test.annotations.DisabledOnRavenwood(reason = "Integration test")
public class CpuPowerStatsCollectorValidationTest {
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+ @Rule(order = 1)
+ public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isOnRavenwood()
+ ? RavenwoodFlagsValueProvider.createAllOnCheckFlagsRule()
+ : DeviceFlagsValueProvider.createCheckFlagsRule();
private static final int WORK_DURATION_MS = 2000;
private static final String TEST_PKG = "com.android.coretests.apps.bstatstestapp";
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsProcessorTest.java
similarity index 96%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsProcessorTest.java
index 5c0e268..6b5da81 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsProcessorTest.java
@@ -30,6 +30,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
import android.os.BatteryConsumer;
import android.os.PersistableBundle;
@@ -55,7 +56,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class CpuAggregatedPowerStatsProcessorTest {
+public class CpuPowerStatsProcessorTest {
@Rule(order = 0)
public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
.setProvideMainThread(true)
@@ -77,7 +78,7 @@
.setCpuPowerBracket(2, 0, 2);
private AggregatedPowerStatsConfig.PowerComponent mConfig;
- private CpuAggregatedPowerStatsProcessor mProcessor;
+ private CpuPowerStatsProcessor mProcessor;
private MockPowerComponentAggregatedPowerStats mStats;
@Before
@@ -86,7 +87,7 @@
.trackDeviceStates(STATE_POWER, STATE_SCREEN)
.trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE);
- mProcessor = new CpuAggregatedPowerStatsProcessor(
+ mProcessor = new CpuPowerStatsProcessor(
mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies());
}
@@ -197,7 +198,7 @@
private static class MockPowerComponentAggregatedPowerStats extends
PowerComponentAggregatedPowerStats {
- private final CpuPowerStatsCollector.CpuStatsArrayLayout mStatsLayout;
+ private final CpuPowerStatsLayout mStatsLayout;
private final PowerStats.Descriptor mDescriptor;
private HashMap<String, long[]> mDeviceStats = new HashMap<>();
private HashMap<String, long[]> mUidStats = new HashMap<>();
@@ -207,8 +208,8 @@
MockPowerComponentAggregatedPowerStats(AggregatedPowerStatsConfig.PowerComponent config,
boolean useEnergyConsumers) {
- super(config);
- mStatsLayout = new CpuPowerStatsCollector.CpuStatsArrayLayout();
+ super(new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
+ mStatsLayout = new CpuPowerStatsLayout();
mStatsLayout.addDeviceSectionCpuTimeByScalingStep(3);
mStatsLayout.addDeviceSectionCpuTimeByCluster(2);
mStatsLayout.addDeviceSectionUsageDuration();
@@ -222,8 +223,8 @@
PersistableBundle extras = new PersistableBundle();
mStatsLayout.toExtras(extras);
mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU,
- mStatsLayout.getDeviceStatsArrayLength(), mStatsLayout.getUidStatsArrayLength(),
- extras);
+ mStatsLayout.getDeviceStatsArrayLength(), null, 0,
+ mStatsLayout.getUidStatsArrayLength(), extras);
}
@Override
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/KernelWakelockReaderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/KernelWakelockReaderTest.java
index e023866..f035465 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/KernelWakelockReaderTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/KernelWakelockReaderTest.java
@@ -16,16 +16,26 @@
package com.android.server.power.stats;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.ravenwood.RavenwoodRule;
import android.system.suspend.internal.WakeLockInfo;
import androidx.test.filters.SmallTest;
-import junit.framework.TestCase;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
import java.nio.charset.Charset;
-@android.platform.test.annotations.IgnoreUnderRavenwood
-public class KernelWakelockReaderTest extends TestCase {
+@android.platform.test.annotations.DisabledOnRavenwood(reason = "Kernel dependency")
+public class KernelWakelockReaderTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
/**
* Helper class that builds the mock Kernel module file /d/wakeup_sources.
*/
@@ -105,14 +115,14 @@
private KernelWakelockReader mReader;
- @Override
+ @Before
public void setUp() throws Exception {
- super.setUp();
mReader = new KernelWakelockReader();
}
// ------------------------- Legacy Wakelock Stats Test ------------------------
@SmallTest
+ @Test
public void testParseEmptyFile() throws Exception {
KernelWakelockStats staleStats = mReader.parseProcWakelocks(new byte[0], 0, true,
new KernelWakelockStats());
@@ -121,6 +131,7 @@
}
@SmallTest
+ @Test
public void testOnlyHeader() throws Exception {
byte[] buffer = new ProcFileBuilder().getBytes();
@@ -131,6 +142,7 @@
}
@SmallTest
+ @Test
public void testOneWakelock() throws Exception {
byte[] buffer = new ProcFileBuilder()
.addLine("Wakelock", 34, 123, 456) // Milliseconds
@@ -150,6 +162,7 @@
}
@SmallTest
+ @Test
public void testTwoWakelocks() throws Exception {
byte[] buffer = new ProcFileBuilder()
.addLine("Wakelock", 1, 10)
@@ -166,6 +179,7 @@
}
@SmallTest
+ @Test
public void testDuplicateWakelocksAccumulate() throws Exception {
byte[] buffer = new ProcFileBuilder()
.addLine("Wakelock", 1, 10) // Milliseconds
@@ -184,6 +198,7 @@
}
@SmallTest
+ @Test
public void testWakelocksBecomeStale() throws Exception {
KernelWakelockStats staleStats = new KernelWakelockStats();
@@ -209,6 +224,7 @@
// -------------------- SystemSuspend Wakelock Stats Test -------------------
@SmallTest
+ @Test
public void testEmptyWakeLockInfoList() {
KernelWakelockStats staleStats = mReader.updateWakelockStats(new WakeLockInfo[0],
new KernelWakelockStats());
@@ -217,6 +233,7 @@
}
@SmallTest
+ @Test
public void testOneWakeLockInfo() {
WakeLockInfo[] wlStats = new WakeLockInfo[1];
wlStats[0] = createWakeLockInfo("WakeLock", 20, 1000, 500); // Milliseconds
@@ -235,6 +252,7 @@
}
@SmallTest
+ @Test
public void testTwoWakeLockInfos() {
WakeLockInfo[] wlStats = new WakeLockInfo[2];
wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); // Milliseconds
@@ -258,6 +276,7 @@
}
@SmallTest
+ @Test
public void testWakeLockInfosBecomeStale() {
WakeLockInfo[] wlStats = new WakeLockInfo[1];
wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); // Milliseconds
@@ -288,6 +307,7 @@
// -------------------- Aggregate Wakelock Stats Tests --------------------
@SmallTest
+ @Test
public void testAggregateStatsEmpty() throws Exception {
KernelWakelockStats staleStats = new KernelWakelockStats();
@@ -300,6 +320,7 @@
}
@SmallTest
+ @Test
public void testAggregateStatsNoNativeWakelocks() throws Exception {
KernelWakelockStats staleStats = new KernelWakelockStats();
@@ -320,6 +341,7 @@
}
@SmallTest
+ @Test
public void testAggregateStatsNoKernelWakelocks() throws Exception {
KernelWakelockStats staleStats = new KernelWakelockStats();
@@ -339,6 +361,7 @@
}
@SmallTest
+ @Test
public void testAggregateStatsBothKernelAndNativeWakelocks() throws Exception {
KernelWakelockStats staleStats = new KernelWakelockStats();
@@ -364,6 +387,7 @@
}
@SmallTest
+ @Test
public void testAggregateStatsUpdate() throws Exception {
KernelWakelockStats staleStats = new KernelWakelockStats();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java
index 888a168..9b810bc 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.annotation.Nullable;
import android.app.usage.NetworkStatsManager;
import android.net.NetworkCapabilities;
import android.net.NetworkStats;
@@ -34,6 +35,7 @@
import android.os.BatteryUsageStatsQuery;
import android.os.Process;
import android.os.UidBatteryConsumer;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.telephony.AccessNetworkConstants;
import android.telephony.ActivityStatsTechSpecificInfo;
import android.telephony.CellSignalStrength;
@@ -46,8 +48,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.frameworks.powerstatstests.R;
-
import com.google.common.collect.Range;
import org.junit.Rule;
@@ -56,23 +56,29 @@
import org.mockito.Mock;
import java.util.ArrayList;
+import java.util.List;
@RunWith(AndroidJUnit4.class)
@SmallTest
@SuppressWarnings("GuardedBy")
public class MobileRadioPowerCalculatorTest {
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
private static final double PRECISION = 0.00001;
private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
@Mock
NetworkStatsManager mNetworkStatsManager;
- @Rule
+ @Rule(order = 1)
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
@Test
public void testCounterBasedModel() {
- mStatsRule.setTestPowerProfile(R.xml.power_profile_test_modem_calculator)
+ mStatsRule.setTestPowerProfile("power_profile_test_modem_calculator")
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
@@ -126,10 +132,10 @@
stats.notePhoneSignalStrengthLocked(signalStrength, 9665, 9665);
// Note application network activity
- NetworkStats networkStats = new NetworkStats(10000, 1)
- .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 2000, 30, 100))
- .addEntry(new NetworkStats.Entry("cellular", APP_UID2, 0, 0,
+ NetworkStats networkStats = mockNetworkStats(10000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 2000, 30, 100),
+ mockNetworkStatsEntry("cellular", APP_UID2, 0, 0,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 500, 50, 300, 10, 111));
mStatsRule.setNetworkStats(networkStats);
@@ -192,7 +198,7 @@
@Test
public void testCounterBasedModel_multipleDefinedRat() {
- mStatsRule.setTestPowerProfile(R.xml.power_profile_test_modem_calculator_multiactive)
+ mStatsRule.setTestPowerProfile("power_profile_test_modem_calculator_multiactive")
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
@@ -246,10 +252,10 @@
stats.notePhoneSignalStrengthLocked(signalStrength, 9665, 9665);
// Note application network activity
- NetworkStats networkStats = new NetworkStats(10000, 1)
- .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 2000, 30, 100))
- .addEntry(new NetworkStats.Entry("cellular", APP_UID2, 0, 0,
+ NetworkStats networkStats = mockNetworkStats(10000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 2000, 30, 100),
+ mockNetworkStatsEntry("cellular", APP_UID2, 0, 0,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 500, 50, 300, 10, 111));
mStatsRule.setNetworkStats(networkStats);
@@ -349,7 +355,7 @@
@Test
public void testCounterBasedModel_legacyPowerProfile() {
- mStatsRule.setTestPowerProfile(R.xml.power_profile_test_legacy_modem)
+ mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem")
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
@@ -403,10 +409,10 @@
stats.notePhoneSignalStrengthLocked(signalStrength, 9665, 9665);
// Note application network activity
- NetworkStats networkStats = new NetworkStats(10000, 1)
- .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 2000, 30, 100))
- .addEntry(new NetworkStats.Entry("cellular", APP_UID2, 0, 0,
+ NetworkStats networkStats = mockNetworkStats(10000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 2000, 30, 100),
+ mockNetworkStatsEntry("cellular", APP_UID2, 0, 0,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 500, 50, 300, 10, 111));
mStatsRule.setNetworkStats(networkStats);
@@ -469,7 +475,7 @@
@Test
public void testTimerBasedModel_byProcessState() {
- mStatsRule.setTestPowerProfile(R.xml.power_profile_test_legacy_modem)
+ mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem")
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
BatteryStatsImpl.Uid uid = stats.getUidStatsLocked(APP_UID);
@@ -521,8 +527,8 @@
stats.notePhoneSignalStrengthLocked(signalStrength, 9665, 9665);
// Note application network activity
- mStatsRule.setNetworkStats(new NetworkStats(10000, 1)
- .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
+ mStatsRule.setNetworkStats(mockNetworkStats(10000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 100, 2000, 20, 100)));
stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 10000, 10000,
@@ -531,8 +537,8 @@
uid.setProcessStateForTest(
BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000);
- mStatsRule.setNetworkStats(new NetworkStats(12000, 1)
- .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
+ mStatsRule.setNetworkStats(mockNetworkStats(12000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 250, 2000, 80, 200)));
stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 12000, 12000,
@@ -586,7 +592,7 @@
@Test
public void testMeasuredEnergyBasedModel_mobileRadioActiveTimeModel() {
- mStatsRule.setTestPowerProfile(R.xml.power_profile_test_legacy_modem)
+ mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem")
.setPerUidModemModel(
BatteryStatsImpl.PER_UID_MODEM_POWER_MODEL_MOBILE_RADIO_ACTIVE_TIME)
.initMeasuredEnergyStatsLocked();
@@ -619,8 +625,8 @@
stats.notePhoneOnLocked(9800, 9800);
// Note application network activity
- NetworkStats networkStats = new NetworkStats(10000, 1)
- .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
+ NetworkStats networkStats = mockNetworkStats(10000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 100, 2000, 20, 100));
mStatsRule.setNetworkStats(networkStats);
@@ -662,11 +668,9 @@
.isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
}
-
-
@Test
public void testMeasuredEnergyBasedModel_modemActivityInfoRxTxModel() {
- mStatsRule.setTestPowerProfile(R.xml.power_profile_test_modem_calculator_multiactive)
+ mStatsRule.setTestPowerProfile("power_profile_test_modem_calculator_multiactive")
.setPerUidModemModel(
BatteryStatsImpl.PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX)
.initMeasuredEnergyStatsLocked();
@@ -728,10 +732,10 @@
stats.notePhoneSignalStrengthLocked(signalStrength, 9665, 9665);
// Note application network activity
- NetworkStats networkStats = new NetworkStats(10000, 1)
- .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 300, 10, 100))
- .addEntry(new NetworkStats.Entry("cellular", APP_UID2, 0, 0,
+ NetworkStats networkStats = mockNetworkStats(10000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 150, 300, 10, 100),
+ mockNetworkStatsEntry("cellular", APP_UID2, 0, 0,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 500, 50, 2000, 30, 111));
mStatsRule.setNetworkStats(networkStats);
@@ -850,7 +854,7 @@
@Test
public void testMeasuredEnergyBasedModel_modemActivityInfoRxTxModel_legacyPowerProfile() {
- mStatsRule.setTestPowerProfile(R.xml.power_profile_test_legacy_modem)
+ mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem")
.setPerUidModemModel(
BatteryStatsImpl.PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX)
.initMeasuredEnergyStatsLocked();
@@ -908,8 +912,8 @@
stats.notePhoneSignalStrengthLocked(signalStrength, 9665, 9665);
// Note application network activity
- NetworkStats networkStats = new NetworkStats(10000, 1)
- .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
+ NetworkStats networkStats = mockNetworkStats(10000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 100, 2000, 20, 100));
mStatsRule.setNetworkStats(networkStats);
@@ -957,7 +961,7 @@
@Test
public void testMeasuredEnergyBasedModel_byProcessState() {
- mStatsRule.setTestPowerProfile(R.xml.power_profile_test_legacy_modem)
+ mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem")
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
BatteryStatsImpl.Uid uid = stats.getUidStatsLocked(APP_UID);
@@ -988,8 +992,8 @@
new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
// Note application network activity
- mStatsRule.setNetworkStats(new NetworkStats(10000, 1)
- .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
+ mStatsRule.setNetworkStats(mockNetworkStats(10000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 100, 2000, 20, 100)));
stats.noteModemControllerActivity(null, 10_000_000, 10000, 10000, mNetworkStatsManager);
@@ -997,8 +1001,8 @@
uid.setProcessStateForTest(
BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000);
- mStatsRule.setNetworkStats(new NetworkStats(12000, 1)
- .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
+ mStatsRule.setNetworkStats(mockNetworkStats(12000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 250, 2000, 80, 200)));
stats.noteModemControllerActivity(null, 15_000_000, 12000, 12000, mNetworkStatsManager);
@@ -1047,4 +1051,40 @@
final ModemActivityInfo emptyMai = new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L);
stats.noteModemControllerActivity(emptyMai, 0, 0, 0, mNetworkStatsManager);
}
+
+ private NetworkStats mockNetworkStats(int elapsedTime, int initialSize,
+ NetworkStats.Entry... entries) {
+ NetworkStats stats;
+ if (RavenwoodRule.isOnRavenwood()) {
+ stats = mock(NetworkStats.class);
+ when(stats.iterator()).thenAnswer(inv -> List.of(entries).iterator());
+ } else {
+ stats = new NetworkStats(elapsedTime, initialSize);
+ for (NetworkStats.Entry entry : entries) {
+ stats = stats.addEntry(entry);
+ }
+ }
+ return stats;
+ }
+
+ private static NetworkStats.Entry mockNetworkStatsEntry(@Nullable String iface, int uid,
+ int set, int tag, int metered, int roaming, int defaultNetwork, long rxBytes,
+ long rxPackets, long txBytes, long txPackets, long operations) {
+ if (RavenwoodRule.isOnRavenwood()) {
+ NetworkStats.Entry entry = mock(NetworkStats.Entry.class);
+ when(entry.getUid()).thenReturn(uid);
+ when(entry.getMetered()).thenReturn(metered);
+ when(entry.getRoaming()).thenReturn(roaming);
+ when(entry.getDefaultNetwork()).thenReturn(defaultNetwork);
+ when(entry.getRxBytes()).thenReturn(rxBytes);
+ when(entry.getRxPackets()).thenReturn(rxPackets);
+ when(entry.getTxBytes()).thenReturn(txBytes);
+ when(entry.getTxPackets()).thenReturn(txPackets);
+ when(entry.getOperations()).thenReturn(operations);
+ return entry;
+ } else {
+ return new NetworkStats.Entry(iface, uid, set, tag, metered,
+ roaming, defaultNetwork, rxBytes, rxPackets, txBytes, txPackets, operations);
+ }
+ }
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
new file mode 100644
index 0000000..f93c4da
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
@@ -0,0 +1,497 @@
+/*
+ * 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.power.stats;
+
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.net.NetworkStats;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.OutcomeReceiver;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.ActivityStatsTechSpecificInfo;
+import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.ModemActivityInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.PowerStats;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.StringWriter;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
+
+public class MobileRadioPowerStatsCollectorTest {
+ private static final int APP_UID1 = 42;
+ private static final int APP_UID2 = 24;
+ private static final int APP_UID3 = 44;
+ private static final int ISOLATED_UID = 99123;
+
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
+ @Rule(order = 1)
+ public final BatteryUsageStatsRule mStatsRule =
+ new BatteryUsageStatsRule().setPowerStatsThrottlePeriodMillis(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, 10000);
+
+ private MockBatteryStatsImpl mBatteryStats;
+
+ private final MockClock mClock = mStatsRule.getMockClock();
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private TelephonyManager mTelephony;
+ @Mock
+ private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+ @Mock
+ private Supplier<NetworkStats> mNetworkStatsSupplier;
+ @Mock
+ private PowerStatsUidResolver mPowerStatsUidResolver;
+ @Mock
+ private LongSupplier mCallDurationSupplier;
+ @Mock
+ private LongSupplier mScanDurationSupplier;
+
+ private final List<PowerStats> mRecordedPowerStats = new ArrayList<>();
+
+ private MobileRadioPowerStatsCollector.Injector mInjector =
+ new MobileRadioPowerStatsCollector.Injector() {
+ @Override
+ public Handler getHandler() {
+ return mStatsRule.getHandler();
+ }
+
+ @Override
+ public Clock getClock() {
+ return mStatsRule.getMockClock();
+ }
+
+ @Override
+ public PowerStatsUidResolver getUidResolver() {
+ return mPowerStatsUidResolver;
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+ @Override
+ public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+ return mConsumedEnergyRetriever;
+ }
+
+ @Override
+ public IntSupplier getVoltageSupplier() {
+ return () -> 3500;
+ }
+
+ @Override
+ public Supplier<NetworkStats> getMobileNetworkStatsSupplier() {
+ return mNetworkStatsSupplier;
+ }
+
+ @Override
+ public TelephonyManager getTelephonyManager() {
+ return mTelephony;
+ }
+
+ @Override
+ public LongSupplier getCallDurationSupplier() {
+ return mCallDurationSupplier;
+ }
+
+ @Override
+ public LongSupplier getPhoneSignalScanDurationSupplier() {
+ return mScanDurationSupplier;
+ }
+ };
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true);
+ when(mPowerStatsUidResolver.mapUid(anyInt())).thenAnswer(invocation -> {
+ int uid = invocation.getArgument(0);
+ if (uid == ISOLATED_UID) {
+ return APP_UID2;
+ } else {
+ return uid;
+ }
+ });
+ mBatteryStats = mStatsRule.getBatteryStats();
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void triggering() throws Throwable {
+ PowerStatsCollector collector = mBatteryStats.getPowerStatsCollector(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO);
+ collector.addConsumer(mRecordedPowerStats::add);
+
+ mBatteryStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ true);
+
+ mockModemActivityInfo(1000, 2000, 3000, 600, new int[]{100, 200, 300, 400, 500});
+
+ // This should trigger a sample collection
+ mBatteryStats.onSystemReady(mContext);
+
+ mStatsRule.waitForBackgroundThread();
+ assertThat(mRecordedPowerStats).hasSize(1);
+
+ mRecordedPowerStats.clear();
+ mStatsRule.setTime(20000, 20000);
+ mBatteryStats.notePhoneOnLocked(mClock.realtime, mClock.uptime);
+ mStatsRule.waitForBackgroundThread();
+ assertThat(mRecordedPowerStats).hasSize(1);
+
+ mRecordedPowerStats.clear();
+ mStatsRule.setTime(40000, 40000);
+ mBatteryStats.notePhoneOffLocked(mClock.realtime, mClock.uptime);
+ mStatsRule.waitForBackgroundThread();
+ assertThat(mRecordedPowerStats).hasSize(1);
+
+ mRecordedPowerStats.clear();
+ mStatsRule.setTime(45000, 55000);
+ mBatteryStats.noteMobileRadioPowerStateLocked(
+ DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH, 0, APP_UID1, mClock.realtime,
+ mClock.uptime);
+ mStatsRule.setTime(50001, 50001);
+ // Elapsed time under the throttling threshold - shouldn't trigger stats collection
+ mBatteryStats.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
+ 0, APP_UID1, mClock.realtime, mClock.uptime);
+ mStatsRule.waitForBackgroundThread();
+ assertThat(mRecordedPowerStats).hasSize(1);
+
+ mRecordedPowerStats.clear();
+ mStatsRule.setTime(50002, 50002);
+ mBatteryStats.noteMobileRadioPowerStateLocked(
+ DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH, 0, APP_UID1, mClock.realtime,
+ mClock.uptime);
+ mStatsRule.setTime(55000, 50000);
+ // Elapsed time under the throttling threshold - shouldn't trigger stats collection
+ mBatteryStats.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
+ 0, APP_UID1, mClock.realtime, mClock.uptime);
+ mStatsRule.waitForBackgroundThread();
+ assertThat(mRecordedPowerStats).isEmpty();
+ }
+
+ @Test
+ public void collectStats() throws Throwable {
+ PowerStats powerStats = collectPowerStats(true);
+ assertThat(powerStats.durationMs).isEqualTo(100);
+
+ PowerStats.Descriptor descriptor = powerStats.descriptor;
+ MobileRadioPowerStatsLayout layout =
+ new MobileRadioPowerStatsLayout(descriptor);
+ assertThat(layout.getDeviceSleepTime(powerStats.stats)).isEqualTo(200);
+ assertThat(layout.getDeviceIdleTime(powerStats.stats)).isEqualTo(300);
+ assertThat(layout.getDeviceCallTime(powerStats.stats)).isEqualTo(40000);
+ assertThat(layout.getDeviceScanTime(powerStats.stats)).isEqualTo(60000);
+ assertThat(layout.getConsumedEnergy(powerStats.stats, 0))
+ .isEqualTo((64321 - 10000) * 1000 / 3500);
+
+ assertThat(powerStats.stateStats.size()).isEqualTo(2);
+ long[] state1 = powerStats.stateStats.get(MobileRadioPowerStatsCollector.makeStateKey(
+ BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR,
+ ServiceState.FREQUENCY_RANGE_MMWAVE
+ ));
+ assertThat(layout.getStateRxTime(state1)).isEqualTo(6000);
+ assertThat(layout.getStateTxTime(state1, 0)).isEqualTo(1000);
+ assertThat(layout.getStateTxTime(state1, 1)).isEqualTo(2000);
+ assertThat(layout.getStateTxTime(state1, 2)).isEqualTo(3000);
+ assertThat(layout.getStateTxTime(state1, 3)).isEqualTo(4000);
+ assertThat(layout.getStateTxTime(state1, 4)).isEqualTo(5000);
+
+ long[] state2 = powerStats.stateStats.get(MobileRadioPowerStatsCollector.makeStateKey(
+ BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE,
+ ServiceState.FREQUENCY_RANGE_LOW
+ ));
+ assertThat(layout.getStateRxTime(state2)).isEqualTo(7000);
+ assertThat(layout.getStateTxTime(state2, 0)).isEqualTo(8000);
+ assertThat(layout.getStateTxTime(state2, 1)).isEqualTo(9000);
+ assertThat(layout.getStateTxTime(state2, 2)).isEqualTo(1000);
+ assertThat(layout.getStateTxTime(state2, 3)).isEqualTo(2000);
+ assertThat(layout.getStateTxTime(state2, 4)).isEqualTo(3000);
+
+ assertThat(powerStats.uidStats.size()).isEqualTo(2);
+ long[] actual1 = powerStats.uidStats.get(APP_UID1);
+ assertThat(layout.getUidRxBytes(actual1)).isEqualTo(1000);
+ assertThat(layout.getUidTxBytes(actual1)).isEqualTo(2000);
+ assertThat(layout.getUidRxPackets(actual1)).isEqualTo(100);
+ assertThat(layout.getUidTxPackets(actual1)).isEqualTo(200);
+
+ // Combines APP_UID2 and ISOLATED_UID
+ long[] actual2 = powerStats.uidStats.get(APP_UID2);
+ assertThat(layout.getUidRxBytes(actual2)).isEqualTo(6000);
+ assertThat(layout.getUidTxBytes(actual2)).isEqualTo(3000);
+ assertThat(layout.getUidRxPackets(actual2)).isEqualTo(60);
+ assertThat(layout.getUidTxPackets(actual2)).isEqualTo(30);
+
+ assertThat(powerStats.uidStats.get(ISOLATED_UID)).isNull();
+ assertThat(powerStats.uidStats.get(APP_UID3)).isNull();
+ }
+
+ @Test
+ public void collectStats_noPerNetworkTypeData() throws Throwable {
+ PowerStats powerStats = collectPowerStats(false);
+ assertThat(powerStats.durationMs).isEqualTo(100);
+
+ PowerStats.Descriptor descriptor = powerStats.descriptor;
+ MobileRadioPowerStatsLayout layout =
+ new MobileRadioPowerStatsLayout(descriptor);
+ assertThat(layout.getDeviceSleepTime(powerStats.stats)).isEqualTo(200);
+ assertThat(layout.getDeviceIdleTime(powerStats.stats)).isEqualTo(300);
+ assertThat(layout.getConsumedEnergy(powerStats.stats, 0))
+ .isEqualTo((64321 - 10000) * 1000 / 3500);
+
+ assertThat(powerStats.stateStats.size()).isEqualTo(1);
+ long[] stateStats = powerStats.stateStats.get(MobileRadioPowerStatsCollector.makeStateKey(
+ AccessNetworkConstants.AccessNetworkType.UNKNOWN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN
+ ));
+ assertThat(layout.getStateRxTime(stateStats)).isEqualTo(6000);
+ assertThat(layout.getStateTxTime(stateStats, 0)).isEqualTo(1000);
+ assertThat(layout.getStateTxTime(stateStats, 1)).isEqualTo(2000);
+ assertThat(layout.getStateTxTime(stateStats, 2)).isEqualTo(3000);
+ assertThat(layout.getStateTxTime(stateStats, 3)).isEqualTo(4000);
+ assertThat(layout.getStateTxTime(stateStats, 4)).isEqualTo(5000);
+
+ assertThat(powerStats.uidStats.size()).isEqualTo(2);
+ long[] actual1 = powerStats.uidStats.get(APP_UID1);
+ assertThat(layout.getUidRxBytes(actual1)).isEqualTo(1000);
+ assertThat(layout.getUidTxBytes(actual1)).isEqualTo(2000);
+ assertThat(layout.getUidRxPackets(actual1)).isEqualTo(100);
+ assertThat(layout.getUidTxPackets(actual1)).isEqualTo(200);
+
+ // Combines APP_UID2 and ISOLATED_UID
+ long[] actual2 = powerStats.uidStats.get(APP_UID2);
+ assertThat(layout.getUidRxBytes(actual2)).isEqualTo(6000);
+ assertThat(layout.getUidTxBytes(actual2)).isEqualTo(3000);
+ assertThat(layout.getUidRxPackets(actual2)).isEqualTo(60);
+ assertThat(layout.getUidTxPackets(actual2)).isEqualTo(30);
+
+ assertThat(powerStats.uidStats.get(ISOLATED_UID)).isNull();
+ assertThat(powerStats.uidStats.get(APP_UID3)).isNull();
+ }
+
+ @Test
+ public void dump() throws Throwable {
+ PowerStats powerStats = collectPowerStats(true);
+ StringWriter sw = new StringWriter();
+ IndentingPrintWriter pw = new IndentingPrintWriter(sw);
+ powerStats.dump(pw);
+ pw.flush();
+ String dump = sw.toString();
+ assertThat(dump).contains("duration=100");
+ assertThat(dump).contains(
+ "stats=[200, 300, 60000, 40000, " + ((64321 - 10000) * 1000 / 3500) + ", 0, 0, 0]");
+ assertThat(dump).contains("state LTE: [7000, 8000, 9000, 1000, 2000, 3000]");
+ assertThat(dump).contains("state NR MMWAVE: [6000, 1000, 2000, 3000, 4000, 5000]");
+ assertThat(dump).contains("UID 24: [6000, 3000, 60, 30, 0]");
+ assertThat(dump).contains("UID 42: [1000, 2000, 100, 200, 0]");
+ }
+
+ private PowerStats collectPowerStats(boolean perNetworkTypeData) throws Throwable {
+ MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0);
+ collector.setEnabled(true);
+
+ when(mConsumedEnergyRetriever.getEnergyConsumerIds(
+ EnergyConsumerType.MOBILE_RADIO)).thenReturn(new int[]{777});
+
+ if (perNetworkTypeData) {
+ mockModemActivityInfo(1000, 2000, 3000,
+ AccessNetworkConstants.AccessNetworkType.NGRAN,
+ ServiceState.FREQUENCY_RANGE_MMWAVE,
+ 600, new int[]{100, 200, 300, 400, 500},
+ AccessNetworkConstants.AccessNetworkType.EUTRAN,
+ ServiceState.FREQUENCY_RANGE_LOW,
+ 700, new int[]{800, 900, 100, 200, 300});
+ } else {
+ mockModemActivityInfo(1000, 2000, 3000, 600, new int[]{100, 200, 300, 400, 500});
+ }
+ mockNetworkStats(1000,
+ 4321, 321, 1234, 23,
+ 4000, 40, 2000, 20);
+
+ when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{777})))
+ .thenReturn(new long[]{10000});
+
+ when(mCallDurationSupplier.getAsLong()).thenReturn(10000L);
+ when(mScanDurationSupplier.getAsLong()).thenReturn(20000L);
+
+ collector.collectStats();
+
+ if (perNetworkTypeData) {
+ mockModemActivityInfo(1100, 2200, 3300,
+ AccessNetworkConstants.AccessNetworkType.NGRAN,
+ ServiceState.FREQUENCY_RANGE_MMWAVE,
+ 6600, new int[]{1100, 2200, 3300, 4400, 5500},
+ AccessNetworkConstants.AccessNetworkType.EUTRAN,
+ ServiceState.FREQUENCY_RANGE_LOW,
+ 7700, new int[]{8800, 9900, 1100, 2200, 3300});
+ } else {
+ mockModemActivityInfo(1100, 2200, 3300, 6600, new int[]{1100, 2200, 3300, 4400, 5500});
+ }
+ mockNetworkStats(1100,
+ 5321, 421, 3234, 223,
+ 8000, 80, 4000, 40);
+
+ when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{777})))
+ .thenReturn(new long[]{64321});
+ when(mCallDurationSupplier.getAsLong()).thenReturn(50000L);
+ when(mScanDurationSupplier.getAsLong()).thenReturn(80000L);
+
+ mStatsRule.setTime(20000, 20000);
+ return collector.collectStats();
+ }
+
+ private void mockModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
+ int networkType1, int freqRange1, int rxTimeMs1, @NonNull int[] txTimeMs1,
+ int networkType2, int freqRange2, int rxTimeMs2, @NonNull int[] txTimeMs2) {
+ ModemActivityInfo info = new ModemActivityInfo(timestamp, sleepTimeMs, idleTimeMs,
+ new ActivityStatsTechSpecificInfo[]{
+ new ActivityStatsTechSpecificInfo(networkType1, freqRange1, txTimeMs1,
+ rxTimeMs1),
+ new ActivityStatsTechSpecificInfo(networkType2, freqRange2, txTimeMs2,
+ rxTimeMs2)});
+ doAnswer(invocation -> {
+ OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException>
+ receiver = invocation.getArgument(1);
+ receiver.onResult(info);
+ return null;
+ }).when(mTelephony).requestModemActivityInfo(any(), any());
+ }
+
+ private void mockModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
+ int rxTimeMs, @NonNull int[] txTimeMs) {
+ ModemActivityInfo info = new ModemActivityInfo(timestamp, sleepTimeMs, idleTimeMs, txTimeMs,
+ rxTimeMs);
+ doAnswer(invocation -> {
+ OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException>
+ receiver = invocation.getArgument(1);
+ receiver.onResult(info);
+ return null;
+ }).when(mTelephony).requestModemActivityInfo(any(), any());
+ }
+
+ private void mockNetworkStats(long elapsedRealtime,
+ long rxBytes1, long rxPackets1, long txBytes1, long txPackets1,
+ long rxBytes2, long rxPackets2, long txBytes2, long txPackets2) {
+ NetworkStats stats;
+ if (RavenwoodRule.isOnRavenwood()) {
+ stats = mock(NetworkStats.class);
+ List<NetworkStats.Entry> entries = List.of(
+ mockNetworkStatsEntry(APP_UID1, rxBytes1, rxPackets1, txBytes1, txPackets1),
+ mockNetworkStatsEntry(APP_UID2, rxBytes2, rxPackets2, txBytes2, txPackets2),
+ mockNetworkStatsEntry(ISOLATED_UID, rxBytes2 / 2, rxPackets2 / 2, txBytes2 / 2,
+ txPackets2 / 2),
+ mockNetworkStatsEntry(APP_UID3, 314, 281, 314, 281));
+ when(stats.iterator()).thenAnswer(inv -> entries.iterator());
+ } else {
+ stats = new NetworkStats(elapsedRealtime, 1)
+ .addEntry(new NetworkStats.Entry("mobile", APP_UID1, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes1, rxPackets1,
+ txBytes1, txPackets1, 100))
+ .addEntry(new NetworkStats.Entry("mobile", APP_UID2, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes2, rxPackets2,
+ txBytes2, txPackets2, 111))
+ .addEntry(new NetworkStats.Entry("mobile", ISOLATED_UID, 0, 0, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes2 / 2, rxPackets2 / 2,
+ txBytes2 / 2, txPackets2 / 2, 111))
+ .addEntry(new NetworkStats.Entry("mobile", APP_UID3, 0, 0, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 314, 281, 314, 281, 111));
+ }
+ when(mNetworkStatsSupplier.get()).thenReturn(stats);
+ }
+
+ private static NetworkStats.Entry mockNetworkStatsEntry(int uid, long rxBytes, long rxPackets,
+ long txBytes, long txPackets) {
+ NetworkStats.Entry entry = mock(NetworkStats.Entry.class);
+ when(entry.getUid()).thenReturn(uid);
+ when(entry.getMetered()).thenReturn(METERED_NO);
+ when(entry.getRoaming()).thenReturn(ROAMING_NO);
+ when(entry.getDefaultNetwork()).thenReturn(DEFAULT_NETWORK_NO);
+ when(entry.getRxBytes()).thenReturn(rxBytes);
+ when(entry.getRxPackets()).thenReturn(rxPackets);
+ when(entry.getTxBytes()).thenReturn(txBytes);
+ when(entry.getTxPackets()).thenReturn(txPackets);
+ when(entry.getOperations()).thenReturn(100L);
+ return entry;
+ }
+
+ @Test
+ public void networkTypeConstants() throws Throwable {
+ Class<AccessNetworkConstants.AccessNetworkType> clazz =
+ AccessNetworkConstants.AccessNetworkType.class;
+ for (Field field : clazz.getDeclaredFields()) {
+ final int modifiers = field.getModifiers();
+ if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)
+ && field.getType().equals(int.class)) {
+ boolean found = false;
+ int value = field.getInt(null);
+ for (int i = 0; i < MobileRadioPowerStatsCollector.NETWORK_TYPES.length; i++) {
+ if (MobileRadioPowerStatsCollector.NETWORK_TYPES[i] == value) {
+ found = true;
+ break;
+ }
+ }
+ assertWithMessage("New network type, " + field.getName() + " not represented in "
+ + MobileRadioPowerStatsCollector.class).that(found).isTrue();
+ }
+ }
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java
new file mode 100644
index 0000000..4ac7ad8
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) 2024 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.power.stats;
+
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
+import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
+
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.net.NetworkStats;
+import android.os.BatteryConsumer;
+import android.os.Handler;
+import android.os.OutcomeReceiver;
+import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.telephony.ModemActivityInfo;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.PowerStats;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
+
+public class MobileRadioPowerStatsProcessorTest {
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
+ private static final double PRECISION = 0.00001;
+ private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
+ private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
+ private static final int MOBILE_RADIO_ENERGY_CONSUMER_ID = 1;
+ private static final int VOLTAGE_MV = 3500;
+
+ @Rule(order = 1)
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
+ @Mock
+ private Context mContext;
+ @Mock
+ private PowerStatsUidResolver mPowerStatsUidResolver;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+ @Mock
+ private Supplier<NetworkStats> mNetworkStatsSupplier;
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ @Mock
+ private LongSupplier mCallDurationSupplier;
+ @Mock
+ private LongSupplier mScanDurationSupplier;
+
+ private final MobileRadioPowerStatsCollector.Injector mInjector =
+ new MobileRadioPowerStatsCollector.Injector() {
+ @Override
+ public Handler getHandler() {
+ return mStatsRule.getHandler();
+ }
+
+ @Override
+ public Clock getClock() {
+ return mStatsRule.getMockClock();
+ }
+
+ @Override
+ public PowerStatsUidResolver getUidResolver() {
+ return mPowerStatsUidResolver;
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+ @Override
+ public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+ return mConsumedEnergyRetriever;
+ }
+
+ @Override
+ public IntSupplier getVoltageSupplier() {
+ return () -> VOLTAGE_MV;
+ }
+
+ @Override
+ public Supplier<NetworkStats> getMobileNetworkStatsSupplier() {
+ return mNetworkStatsSupplier;
+ }
+
+ @Override
+ public TelephonyManager getTelephonyManager() {
+ return mTelephonyManager;
+ }
+
+ @Override
+ public LongSupplier getCallDurationSupplier() {
+ return mCallDurationSupplier;
+ }
+
+ @Override
+ public LongSupplier getPhoneSignalScanDurationSupplier() {
+ return mScanDurationSupplier;
+ }
+ };
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true);
+ when(mPowerStatsUidResolver.mapUid(anyInt()))
+ .thenAnswer(invocation -> invocation.getArgument(0));
+ }
+
+ @Test
+ public void powerProfileModel() {
+ // No power monitoring hardware
+ when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.MOBILE_RADIO))
+ .thenReturn(new int[0]);
+
+ mStatsRule.setTestPowerProfile("power_profile_test_modem_calculator");
+
+ MobileRadioPowerStatsProcessor processor =
+ new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile());
+
+ AggregatedPowerStatsConfig.PowerComponent config =
+ new AggregatedPowerStatsConfig.PowerComponent(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+ .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+ .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
+ .setProcessor(processor);
+
+ PowerComponentAggregatedPowerStats aggregatedStats =
+ new PowerComponentAggregatedPowerStats(
+ new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
+
+ aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+ aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+ aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
+ aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+ MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0);
+ collector.setEnabled(true);
+
+ // Initial empty ModemActivityInfo.
+ mockModemActivityInfo(new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L));
+
+ // Establish a baseline
+ aggregatedStats.addPowerStats(collector.collectStats(), 0);
+
+ // Turn the screen off after 2.5 seconds
+ aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+ aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+ aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
+ 5000);
+
+ // Note application network activity
+ NetworkStats networkStats = mockNetworkStats(10000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 10000, 1500, 20000, 300, 100),
+ mockNetworkStatsEntry("cellular", APP_UID2, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 5000, 500, 3000, 100, 111));
+
+ when(mNetworkStatsSupplier.get()).thenReturn(networkStats);
+
+ ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
+ new int[]{100, 200, 300, 400, 500}, 600);
+ mockModemActivityInfo(mai);
+
+ when(mCallDurationSupplier.getAsLong()).thenReturn(200L);
+ when(mScanDurationSupplier.getAsLong()).thenReturn(5555L);
+
+ mStatsRule.setTime(10_000, 10_000);
+
+ PowerStats powerStats = collector.collectStats();
+
+ aggregatedStats.addPowerStats(powerStats, 10_000);
+
+ processor.finish(aggregatedStats);
+
+ MobileRadioPowerStatsLayout statsLayout =
+ new MobileRadioPowerStatsLayout(
+ aggregatedStats.getPowerStatsDescriptor());
+
+ // 720 mA * 100 ms (level 0 TX drain rate * level 0 TX duration)
+ // + 1080 mA * 200 ms (level 1 TX drain rate * level 1 TX duration)
+ // + 1440 mA * 300 ms (level 2 TX drain rate * level 2 TX duration)
+ // + 1800 mA * 400 ms (level 3 TX drain rate * level 3 TX duration)
+ // + 2160 mA * 500 ms (level 4 TX drain rate * level 4 TX duration)
+ // + 1440 mA * 600 ms (RX drain rate * RX duration)
+ // + 360 mA * 3000 ms (idle drain rate * idle duration)
+ // + 70 mA * 2000 ms (sleep drain rate * sleep duration)
+ // _________________
+ // = 4604000 mA-ms or 1.27888 mA-h
+ // 25% of 1.27888 = 0.319722
+ // 75% of 1.27888 = 0.959166
+ double totalPower = 0;
+ long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength];
+ aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(0.319722);
+ totalPower += statsLayout.getDevicePowerEstimate(deviceStats);
+
+ aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(0.959166);
+ totalPower += statsLayout.getDevicePowerEstimate(deviceStats);
+
+ assertThat(totalPower).isWithin(PRECISION).of(1.27888);
+
+ // 720 mA * 100 ms (level 0 TX drain rate * level 0 TX duration)
+ // + 1080 mA * 200 ms (level 1 TX drain rate * level 1 TX duration)
+ // + 1440 mA * 300 ms (level 2 TX drain rate * level 2 TX duration)
+ // + 1800 mA * 400 ms (level 3 TX drain rate * level 3 TX duration)
+ // + 2160 mA * 500 ms (level 4 TX drain rate * level 4 TX duration)
+ // + 1440 mA * 600 ms (RX drain rate * RX duration)
+ // _________________
+ // = 3384000 mA-ms or 0.94 mA-h
+ double uidPower1 = 0;
+ long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength];
+ aggregatedStats.getUidStats(uidStats, APP_UID,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.17625);
+ uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
+
+ aggregatedStats.getUidStats(uidStats, APP_UID,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.17625);
+ uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
+
+ aggregatedStats.getUidStats(uidStats, APP_UID,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.3525);
+ uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
+
+ double uidPower2 = 0;
+ aggregatedStats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.05875);
+ uidPower2 += statsLayout.getUidPowerEstimate(uidStats);
+
+ aggregatedStats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.17625);
+ uidPower2 += statsLayout.getUidPowerEstimate(uidStats);
+
+ assertThat(uidPower1 + uidPower2)
+ .isWithin(PRECISION).of(0.94);
+
+ // 3/4 of total packets were sent by APP_UID so 75% of total
+ assertThat(uidPower1 / (uidPower1 + uidPower2))
+ .isWithin(PRECISION).of(0.75);
+ }
+
+ @Test
+ public void measuredEnergyModel() {
+ // PowerStats hardware is available
+ when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.MOBILE_RADIO))
+ .thenReturn(new int[] {MOBILE_RADIO_ENERGY_CONSUMER_ID});
+
+ mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem")
+ .initMeasuredEnergyStatsLocked();
+
+ MobileRadioPowerStatsProcessor processor =
+ new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile());
+
+ AggregatedPowerStatsConfig.PowerComponent config =
+ new AggregatedPowerStatsConfig.PowerComponent(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+ .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+ .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
+ .setProcessor(processor);
+
+ PowerComponentAggregatedPowerStats aggregatedStats =
+ new PowerComponentAggregatedPowerStats(
+ new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
+
+ aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+ aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+ aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
+ aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+ MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0);
+ collector.setEnabled(true);
+
+ // Initial empty ModemActivityInfo.
+ mockModemActivityInfo(new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L));
+
+ when(mConsumedEnergyRetriever.getConsumedEnergyUws(
+ new int[]{MOBILE_RADIO_ENERGY_CONSUMER_ID}))
+ .thenReturn(new long[]{0});
+
+ // Establish a baseline
+ aggregatedStats.addPowerStats(collector.collectStats(), 0);
+
+ // Turn the screen off after 2.5 seconds
+ aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+ aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+ aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
+ 5000);
+
+ // Note application network activity
+ NetworkStats networkStats = mockNetworkStats(10000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 10000, 1500, 20000, 300, 100),
+ mockNetworkStatsEntry("cellular", APP_UID2, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 5000, 500, 3000, 100, 111));
+
+ when(mNetworkStatsSupplier.get()).thenReturn(networkStats);
+
+ ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
+ new int[]{100, 200, 300, 400, 500}, 600);
+ mockModemActivityInfo(mai);
+
+ mStatsRule.setTime(10_000, 10_000);
+
+ long energyUws = 10_000_000L * VOLTAGE_MV / 1000L;
+ when(mConsumedEnergyRetriever.getConsumedEnergyUws(
+ new int[]{MOBILE_RADIO_ENERGY_CONSUMER_ID})).thenReturn(new long[]{energyUws});
+
+ when(mCallDurationSupplier.getAsLong()).thenReturn(200L);
+ when(mScanDurationSupplier.getAsLong()).thenReturn(5555L);
+
+ PowerStats powerStats = collector.collectStats();
+
+ aggregatedStats.addPowerStats(powerStats, 10_000);
+
+ processor.finish(aggregatedStats);
+
+ MobileRadioPowerStatsLayout statsLayout =
+ new MobileRadioPowerStatsLayout(
+ aggregatedStats.getPowerStatsDescriptor());
+
+ // 10_000_000 micro-Coulomb * 1/1000 milli/micro * 1/3600 hour/second = 2.77778 mAh
+ double totalPower = 0;
+ long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength];
+ aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(0.671837);
+ totalPower += statsLayout.getDevicePowerEstimate(deviceStats);
+ assertThat(statsLayout.getDeviceCallPowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(0.022494);
+ totalPower += statsLayout.getDeviceCallPowerEstimate(deviceStats);
+
+ aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(2.01596);
+ totalPower += statsLayout.getDevicePowerEstimate(deviceStats);
+ assertThat(statsLayout.getDeviceCallPowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(0.067484);
+ totalPower += statsLayout.getDeviceCallPowerEstimate(deviceStats);
+
+ // These estimates are supposed to add up to the measured energy, 2.77778 mAh
+ assertThat(totalPower).isWithin(PRECISION).of(2.77778);
+
+ double uidPower1 = 0;
+ long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength];
+ aggregatedStats.getUidStats(uidStats, APP_UID,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.198236);
+ uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
+
+ aggregatedStats.getUidStats(uidStats, APP_UID,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.198236);
+ uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
+
+ aggregatedStats.getUidStats(uidStats, APP_UID,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.396473);
+ uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
+
+ double uidPower2 = 0;
+ aggregatedStats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.066078);
+ uidPower2 += statsLayout.getUidPowerEstimate(uidStats);
+
+ aggregatedStats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.198236);
+ uidPower2 += statsLayout.getUidPowerEstimate(uidStats);
+
+ // Total power attributed to apps is significantly less than the grand total,
+ // because we only attribute TX/RX to apps but not maintaining a connection with the cell.
+ assertThat(uidPower1 + uidPower2)
+ .isWithin(PRECISION).of(1.057259);
+
+ // 3/4 of total packets were sent by APP_UID so 75% of total RX/TX power is attributed to it
+ assertThat(uidPower1 / (uidPower1 + uidPower2))
+ .isWithin(PRECISION).of(0.75);
+ }
+
+ private int[] states(int... states) {
+ return states;
+ }
+
+ private void mockModemActivityInfo(ModemActivityInfo emptyMai) {
+ doAnswer(invocation -> {
+ OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException>
+ receiver = invocation.getArgument(1);
+ receiver.onResult(emptyMai);
+ return null;
+ }).when(mTelephonyManager).requestModemActivityInfo(any(), any());
+ }
+
+ private NetworkStats mockNetworkStats(int elapsedTime, int initialSize,
+ NetworkStats.Entry... entries) {
+ NetworkStats stats;
+ if (RavenwoodRule.isOnRavenwood()) {
+ stats = mock(NetworkStats.class);
+ when(stats.iterator()).thenAnswer(inv -> List.of(entries).iterator());
+ } else {
+ stats = new NetworkStats(elapsedTime, initialSize);
+ for (NetworkStats.Entry entry : entries) {
+ stats = stats.addEntry(entry);
+ }
+ }
+ return stats;
+ }
+
+ private static NetworkStats.Entry mockNetworkStatsEntry(@Nullable String iface, int uid,
+ int set, int tag, int metered, int roaming, int defaultNetwork, long rxBytes,
+ long rxPackets, long txBytes, long txPackets, long operations) {
+ if (RavenwoodRule.isOnRavenwood()) {
+ NetworkStats.Entry entry = mock(NetworkStats.Entry.class);
+ when(entry.getUid()).thenReturn(uid);
+ when(entry.getMetered()).thenReturn(metered);
+ when(entry.getRoaming()).thenReturn(roaming);
+ when(entry.getDefaultNetwork()).thenReturn(defaultNetwork);
+ when(entry.getRxBytes()).thenReturn(rxBytes);
+ when(entry.getRxPackets()).thenReturn(rxPackets);
+ when(entry.getTxBytes()).thenReturn(txBytes);
+ when(entry.getTxPackets()).thenReturn(txPackets);
+ when(entry.getOperations()).thenReturn(operations);
+ return entry;
+ } else {
+ return new NetworkStats.Entry(iface, uid, set, tag, metered,
+ roaming, defaultNetwork, rxBytes, rxPackets, txBytes, txPackets, operations);
+ }
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
index 9f06913..da38346 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
@@ -52,6 +52,8 @@
// The mNetworkStats will be used for both wifi and mobile categories
private NetworkStats mNetworkStats;
private DummyExternalStatsSync mExternalStatsSync = new DummyExternalStatsSync();
+ public static final BatteryStatsConfig DEFAULT_CONFIG =
+ new BatteryStatsConfig.Builder().build();
MockBatteryStatsImpl() {
this(new MockClock());
@@ -66,12 +68,12 @@
}
MockBatteryStatsImpl(Clock clock, File historyDirectory, Handler handler) {
- this(clock, historyDirectory, handler, new PowerStatsUidResolver());
+ this(DEFAULT_CONFIG, clock, historyDirectory, handler, new PowerStatsUidResolver());
}
- MockBatteryStatsImpl(Clock clock, File historyDirectory, Handler handler,
- PowerStatsUidResolver powerStatsUidResolver) {
- super(clock, historyDirectory, handler, powerStatsUidResolver,
+ MockBatteryStatsImpl(BatteryStatsConfig config, Clock clock, File historyDirectory,
+ Handler handler, PowerStatsUidResolver powerStatsUidResolver) {
+ super(config, clock, historyDirectory, handler, powerStatsUidResolver,
mock(FrameworkStatsLogger.class), mock(BatteryStatsHistory.TraceDelegate.class),
mock(BatteryStatsHistory.EventLogger.class));
initTimersAndCounters();
@@ -276,10 +278,6 @@
public void writeSyncLocked() {
}
- public void setHandler(Handler handler) {
- mHandler = handler;
- }
-
@Override
protected void updateBatteryPropertiesLocked() {
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java
new file mode 100644
index 0000000..dadcf3f
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2024 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.power.stats;
+
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.net.NetworkStats;
+import android.os.BatteryConsumer;
+import android.os.Handler;
+import android.os.OutcomeReceiver;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.telephony.ModemActivityInfo;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.os.Clock;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
+
+public class PhoneCallPowerStatsProcessorTest {
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
+ private static final double PRECISION = 0.00001;
+ private static final int VOLTAGE_MV = 3500;
+
+ @Rule(order = 1)
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
+ @Mock
+ private Context mContext;
+ @Mock
+ private PowerStatsUidResolver mPowerStatsUidResolver;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+ @Mock
+ private Supplier<NetworkStats> mNetworkStatsSupplier;
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ @Mock
+ private LongSupplier mCallDurationSupplier;
+ @Mock
+ private LongSupplier mScanDurationSupplier;
+
+ private final MobileRadioPowerStatsCollector.Injector mInjector =
+ new MobileRadioPowerStatsCollector.Injector() {
+ @Override
+ public Handler getHandler() {
+ return mStatsRule.getHandler();
+ }
+
+ @Override
+ public Clock getClock() {
+ return mStatsRule.getMockClock();
+ }
+
+ @Override
+ public PowerStatsUidResolver getUidResolver() {
+ return mPowerStatsUidResolver;
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+ @Override
+ public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+ return mConsumedEnergyRetriever;
+ }
+
+ @Override
+ public IntSupplier getVoltageSupplier() {
+ return () -> VOLTAGE_MV;
+ }
+
+ @Override
+ public Supplier<NetworkStats> getMobileNetworkStatsSupplier() {
+ return mNetworkStatsSupplier;
+ }
+
+ @Override
+ public TelephonyManager getTelephonyManager() {
+ return mTelephonyManager;
+ }
+
+ @Override
+ public LongSupplier getCallDurationSupplier() {
+ return mCallDurationSupplier;
+ }
+
+ @Override
+ public LongSupplier getPhoneSignalScanDurationSupplier() {
+ return mScanDurationSupplier;
+ }
+ };
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true);
+ when(mPowerStatsUidResolver.mapUid(anyInt()))
+ .thenAnswer(invocation -> invocation.getArgument(0));
+
+ // No power monitoring hardware
+ when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.MOBILE_RADIO))
+ .thenReturn(new int[0]);
+
+ mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem");
+ }
+
+ @Test
+ public void copyEstimatesFromMobileRadioPowerStats() {
+ MobileRadioPowerStatsProcessor mobileStatsProcessor =
+ new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile());
+
+ PhoneCallPowerStatsProcessor phoneStatsProcessor =
+ new PhoneCallPowerStatsProcessor();
+
+ AggregatedPowerStatsConfig aggregatedPowerStatsConfig = new AggregatedPowerStatsConfig();
+ aggregatedPowerStatsConfig.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+ .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+ .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
+ .setProcessor(mobileStatsProcessor);
+ aggregatedPowerStatsConfig.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_PHONE,
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+ .setProcessor(phoneStatsProcessor);
+
+ AggregatedPowerStats aggregatedPowerStats =
+ new AggregatedPowerStats(aggregatedPowerStatsConfig);
+ PowerComponentAggregatedPowerStats mobileRadioStats =
+ aggregatedPowerStats.getPowerComponentStats(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO);
+
+ aggregatedPowerStats.setDeviceState(STATE_POWER, POWER_STATE_OTHER, 0);
+ aggregatedPowerStats.setDeviceState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+
+ MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0);
+ collector.setEnabled(true);
+
+ // Initial empty ModemActivityInfo.
+ mockModemActivityInfo(new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L));
+
+ // Establish a baseline
+ aggregatedPowerStats.addPowerStats(collector.collectStats(), 0);
+
+ // Turn the screen off after 2.5 seconds
+ aggregatedPowerStats.setDeviceState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+
+ ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
+ new int[]{100, 200, 300, 400, 500}, 600);
+ mockModemActivityInfo(mai);
+
+ // A phone call was made
+ when(mCallDurationSupplier.getAsLong()).thenReturn(7000L);
+
+ mStatsRule.setTime(10_000, 10_000);
+
+ aggregatedPowerStats.addPowerStats(collector.collectStats(), 10_000);
+
+ mobileStatsProcessor.finish(mobileRadioStats);
+
+ PowerComponentAggregatedPowerStats stats =
+ aggregatedPowerStats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_PHONE);
+ phoneStatsProcessor.finish(stats);
+
+ PowerStatsLayout statsLayout =
+ new PowerStatsLayout(stats.getPowerStatsDescriptor());
+
+ long[] deviceStats = new long[stats.getPowerStatsDescriptor().statsArrayLength];
+ stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(0.7);
+ stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(2.1);
+ }
+
+ private void mockModemActivityInfo(ModemActivityInfo emptyMai) {
+ doAnswer(invocation -> {
+ OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException>
+ receiver = invocation.getArgument(1);
+ receiver.onResult(emptyMai);
+ return null;
+ }).when(mTelephonyManager).requestModemActivityInfo(any(), any());
+ }
+
+ private int[] states(int... states) {
+ return states;
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
index 2ea86a4..03b02cf 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
@@ -76,9 +76,8 @@
@Test
public void stateUpdates() {
- PowerStats.Descriptor descriptor =
- new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1,
- new PersistableBundle());
+ PowerStats.Descriptor descriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT,
+ "majorDrain", 1, null, 0, 1, new PersistableBundle());
PowerStats powerStats = new PowerStats(descriptor);
mClock.currentTime = 1222156800000L; // An important date in world history
@@ -186,9 +185,8 @@
@Test
public void incompatiblePowerStats() {
- PowerStats.Descriptor descriptor =
- new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1,
- new PersistableBundle());
+ PowerStats.Descriptor descriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT,
+ "majorDrain", 1, null, 0, 1, new PersistableBundle());
PowerStats powerStats = new PowerStats(descriptor);
mHistory.forceRecordAllHistory();
@@ -209,7 +207,7 @@
advance(1000);
- descriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, 1,
+ descriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, null, 0, 1,
PersistableBundle.forPair("something", "changed"));
powerStats = new PowerStats(descriptor);
powerStats.stats = new long[]{20000};
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
index 17a7d3e..df1200b 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
@@ -18,11 +18,22 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.hardware.power.stats.EnergyConsumer;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyConsumerType;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.PersistableBundle;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
+import android.power.PowerStatsInternal;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -34,6 +45,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PowerStatsCollectorTest {
@@ -57,7 +71,8 @@
mMockClock) {
@Override
protected PowerStats collectStats() {
- return new PowerStats(new PowerStats.Descriptor(0, 0, 0, new PersistableBundle()));
+ return new PowerStats(
+ new PowerStats.Descriptor(0, 0, null, 0, 0, new PersistableBundle()));
}
};
mCollector.addConsumer(stats -> mCollectedStats = stats);
@@ -92,4 +107,74 @@
mHandler.post(done::open);
done.block();
}
+
+ @Test
+ @DisabledOnRavenwood
+ public void consumedEnergyRetriever() throws Exception {
+ PowerStatsInternal powerStatsInternal = mock(PowerStatsInternal.class);
+ mockEnergyConsumers(powerStatsInternal);
+
+ PowerStatsCollector.ConsumedEnergyRetrieverImpl retriever =
+ new PowerStatsCollector.ConsumedEnergyRetrieverImpl(powerStatsInternal);
+ int[] energyConsumerIds = retriever.getEnergyConsumerIds(EnergyConsumerType.CPU_CLUSTER);
+ assertThat(energyConsumerIds).isEqualTo(new int[]{1, 2});
+ long[] energy = retriever.getConsumedEnergyUws(energyConsumerIds);
+ assertThat(energy).isEqualTo(new long[]{1000, 2000});
+ energy = retriever.getConsumedEnergyUws(energyConsumerIds);
+ assertThat(energy).isEqualTo(new long[]{1500, 2700});
+ }
+
+ @SuppressWarnings("unchecked")
+ private void mockEnergyConsumers(PowerStatsInternal powerStatsInternal) throws Exception {
+ when(powerStatsInternal.getEnergyConsumerInfo())
+ .thenReturn(new EnergyConsumer[]{
+ new EnergyConsumer() {{
+ id = 1;
+ type = EnergyConsumerType.CPU_CLUSTER;
+ ordinal = 0;
+ name = "CPU0";
+ }},
+ new EnergyConsumer() {{
+ id = 2;
+ type = EnergyConsumerType.CPU_CLUSTER;
+ ordinal = 1;
+ name = "CPU4";
+ }},
+ new EnergyConsumer() {{
+ id = 3;
+ type = EnergyConsumerType.BLUETOOTH;
+ name = "BT";
+ }},
+ });
+
+ CompletableFuture<EnergyConsumerResult[]> future1 = mock(CompletableFuture.class);
+ when(future1.get(anyLong(), any(TimeUnit.class)))
+ .thenReturn(new EnergyConsumerResult[]{
+ new EnergyConsumerResult() {{
+ id = 1;
+ energyUWs = 1000;
+ }},
+ new EnergyConsumerResult() {{
+ id = 2;
+ energyUWs = 2000;
+ }}
+ });
+
+ CompletableFuture<EnergyConsumerResult[]> future2 = mock(CompletableFuture.class);
+ when(future2.get(anyLong(), any(TimeUnit.class)))
+ .thenReturn(new EnergyConsumerResult[]{
+ new EnergyConsumerResult() {{
+ id = 1;
+ energyUWs = 1500;
+ }},
+ new EnergyConsumerResult() {{
+ id = 2;
+ energyUWs = 2700;
+ }}
+ });
+
+ when(powerStatsInternal.getEnergyConsumedAsync(eq(new int[]{1, 2})))
+ .thenReturn(future1)
+ .thenReturn(future2);
+ }
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java
index 18d7b90..412fc88 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java
@@ -77,7 +77,7 @@
private PowerStatsStore mPowerStatsStore;
private PowerStatsAggregator mPowerStatsAggregator;
private BatteryStatsHistory mHistory;
- private CpuPowerStatsCollector.CpuStatsArrayLayout mCpuStatsArrayLayout;
+ private CpuPowerStatsLayout mCpuStatsArrayLayout;
private PowerStats.Descriptor mPowerStatsDescriptor;
@Before
@@ -93,7 +93,7 @@
AggregatedPowerStatsConfig.STATE_SCREEN,
AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
.setProcessor(
- new CpuAggregatedPowerStatsProcessor(mStatsRule.getPowerProfile(),
+ new CpuPowerStatsProcessor(mStatsRule.getPowerProfile(),
mStatsRule.getCpuScalingPolicies()));
mPowerStatsStore = new PowerStatsStore(storeDirectory, new TestHandler(), config);
@@ -102,9 +102,10 @@
mMonotonicClock, null, null);
mPowerStatsAggregator = new PowerStatsAggregator(config, mHistory);
- mCpuStatsArrayLayout = new CpuPowerStatsCollector.CpuStatsArrayLayout();
+ mCpuStatsArrayLayout = new CpuPowerStatsLayout();
mCpuStatsArrayLayout.addDeviceSectionCpuTimeByScalingStep(1);
mCpuStatsArrayLayout.addDeviceSectionCpuTimeByCluster(1);
+ mCpuStatsArrayLayout.addDeviceSectionUsageDuration();
mCpuStatsArrayLayout.addDeviceSectionPowerEstimate();
mCpuStatsArrayLayout.addUidSectionCpuTimeByPowerBracket(new int[]{0});
mCpuStatsArrayLayout.addUidSectionPowerEstimate();
@@ -113,7 +114,7 @@
mPowerStatsDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU,
mCpuStatsArrayLayout.getDeviceStatsArrayLength(),
- mCpuStatsArrayLayout.getUidStatsArrayLength(), extras);
+ null, 0, mCpuStatsArrayLayout.getUidStatsArrayLength(), extras);
}
@Test
@@ -126,20 +127,20 @@
BatteryUsageStats actual = builder.build();
String message = "Actual BatteryUsageStats: " + actual;
- assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53);
- assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53);
+ assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
+ assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_ANY, 13.5);
+ BatteryConsumer.PROCESS_STATE_ANY, 3.97099);
assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_FOREGROUND, 7.47);
+ BatteryConsumer.PROCESS_STATE_FOREGROUND, 2.198082);
assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_BACKGROUND, 6.03);
+ BatteryConsumer.PROCESS_STATE_BACKGROUND, 1.772916);
assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_ANY, 12.03);
+ BatteryConsumer.PROCESS_STATE_ANY, 3.538999);
assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 12.03);
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 3.538999);
actual.close();
}
@@ -154,20 +155,20 @@
BatteryUsageStats actual = builder.build();
String message = "Actual BatteryUsageStats: " + actual;
- assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 15.4);
- assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 15.4);
+ assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 4.526749);
+ assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 4.526749);
assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_ANY, 4.06);
+ BatteryConsumer.PROCESS_STATE_ANY, 1.193332);
assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_FOREGROUND, 1.35);
+ BatteryConsumer.PROCESS_STATE_FOREGROUND, 0.397749);
assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_BACKGROUND, 2.70);
+ BatteryConsumer.PROCESS_STATE_BACKGROUND, 0.795583);
assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_ANY, 11.33);
+ BatteryConsumer.PROCESS_STATE_ANY, 3.333249);
assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 11.33);
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 3.333249);
actual.close();
}
@@ -182,13 +183,13 @@
BatteryUsageStats actual = builder.build();
String message = "Actual BatteryUsageStats: " + actual;
- assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53);
- assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 25.53);
+ assertDevicePowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
+ assertAllAppsPowerEstimate(message, actual, BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_ANY, 13.5);
+ BatteryConsumer.PROCESS_STATE_ANY, 3.97099);
assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
- BatteryConsumer.PROCESS_STATE_ANY, 12.03);
+ BatteryConsumer.PROCESS_STATE_ANY, 3.538999);
UidBatteryConsumer uidScope = actual.getUidBatteryConsumers().stream()
.filter(us -> us.getUid() == APP_UID1).findFirst().orElse(null);
// There shouldn't be any per-procstate data
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsProcessorTest.java
similarity index 90%
rename from services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java
rename to services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsProcessorTest.java
index af83be0..02e446a 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsProcessorTest.java
@@ -35,7 +35,7 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class AggregatedPowerStatsProcessorTest {
+public class PowerStatsProcessorTest {
@Test
public void createPowerEstimationPlan_allDeviceStatesPresentInUidStates() {
@@ -44,8 +44,8 @@
.trackDeviceStates(STATE_POWER, STATE_SCREEN)
.trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE);
- AggregatedPowerStatsProcessor.PowerEstimationPlan plan =
- new AggregatedPowerStatsProcessor.PowerEstimationPlan(config);
+ PowerStatsProcessor.PowerEstimationPlan plan =
+ new PowerStatsProcessor.PowerEstimationPlan(config);
assertThat(deviceStateEstimatesToStrings(plan))
.containsExactly("[0, 0]", "[0, 1]", "[1, 0]", "[1, 1]");
assertThat(combinedDeviceStatsToStrings(plan))
@@ -65,8 +65,8 @@
.trackDeviceStates(STATE_POWER, STATE_SCREEN)
.trackUidStates(STATE_POWER, STATE_PROCESS_STATE);
- AggregatedPowerStatsProcessor.PowerEstimationPlan plan =
- new AggregatedPowerStatsProcessor.PowerEstimationPlan(config);
+ PowerStatsProcessor.PowerEstimationPlan plan =
+ new PowerStatsProcessor.PowerEstimationPlan(config);
assertThat(deviceStateEstimatesToStrings(plan))
.containsExactly("[0, 0]", "[0, 1]", "[1, 0]", "[1, 1]");
@@ -81,13 +81,13 @@
}
private static List<String> deviceStateEstimatesToStrings(
- AggregatedPowerStatsProcessor.PowerEstimationPlan plan) {
+ PowerStatsProcessor.PowerEstimationPlan plan) {
return plan.deviceStateEstimations.stream()
.map(dse -> dse.stateValues).map(Arrays::toString).toList();
}
private static List<String> combinedDeviceStatsToStrings(
- AggregatedPowerStatsProcessor.PowerEstimationPlan plan) {
+ PowerStatsProcessor.PowerEstimationPlan plan) {
return plan.combinedDeviceStateEstimations.stream()
.map(cds -> cds.deviceStateEstimations)
.map(dses -> dses.stream()
@@ -97,7 +97,7 @@
}
private static List<String> uidStateEstimatesToStrings(
- AggregatedPowerStatsProcessor.PowerEstimationPlan plan,
+ PowerStatsProcessor.PowerEstimationPlan plan,
AggregatedPowerStatsConfig.PowerComponent config) {
MultiStateStats.States[] uidStateConfig = config.getUidStateConfig();
return plan.uidStateEstimates.stream()
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServerCpuThreadReaderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServerCpuThreadReaderTest.java
index 80cbe0d..d67d408 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServerCpuThreadReaderTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServerCpuThreadReaderTest.java
@@ -18,11 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
+import android.platform.test.ravenwood.RavenwoodRule;
+
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.KernelSingleProcessCpuThreadReader;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -30,7 +33,10 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
+@android.platform.test.annotations.DisabledOnRavenwood(reason = "Kernel dependency")
public class SystemServerCpuThreadReaderTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
@Test
public void testReadDelta() throws IOException {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java
index 8e53d52..ef0b570 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java
@@ -31,9 +31,10 @@
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.RavenwoodFlagsValueProvider;
+import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.KernelCpuSpeedReader;
@@ -46,7 +47,6 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -55,17 +55,21 @@
import java.util.Collection;
@SmallTest
-@RunWith(AndroidJUnit4.class)
@SuppressWarnings("GuardedBy")
public class SystemServicePowerCalculatorTest {
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+ @Rule(order = 1)
+ public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isOnRavenwood()
+ ? RavenwoodFlagsValueProvider.createAllOnCheckFlagsRule()
+ : DeviceFlagsValueProvider.createCheckFlagsRule();
private static final double PRECISION = 0.000001;
private static final int APP_UID1 = 100;
private static final int APP_UID2 = 200;
- @Rule
+ @Rule(order = 2)
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
.setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720)
.setCpuScalingPolicy(0, new int[]{0, 1}, new int[]{100, 200})
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 6106278..f08fbde 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -7916,8 +7916,9 @@
setAppInForegroundForToasts(mUid, true);
// enqueue toast -> toast should still enqueue
- enqueueToast(testPackage, new TestableToastCallback());
+ boolean wasEnqueued = enqueueToast(testPackage, new TestableToastCallback());
assertEquals(1, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isTrue();
}
@Test
@@ -7936,8 +7937,9 @@
setAppInForegroundForToasts(mUid, false);
// enqueue toast -> no toasts enqueued
- enqueueToast(testPackage, new TestableToastCallback());
+ boolean wasEnqueued = enqueueToast(testPackage, new TestableToastCallback());
assertEquals(0, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isFalse();
}
@Test
@@ -8045,8 +8047,9 @@
setAppInForegroundForToasts(mUid, true);
// enqueue toast -> toast should still enqueue
- enqueueTextToast(testPackage, "Text");
+ boolean wasEnqueued = enqueueTextToast(testPackage, "Text");
assertEquals(1, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isTrue();
}
@Test
@@ -8065,8 +8068,9 @@
setAppInForegroundForToasts(mUid, false);
// enqueue toast -> toast should still enqueue
- enqueueTextToast(testPackage, "Text");
+ boolean wasEnqueued = enqueueTextToast(testPackage, "Text");
assertEquals(1, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isTrue();
}
@Test
@@ -8220,8 +8224,9 @@
setAppInForegroundForToasts(mUid, false);
// enqueue toast -> toast should still enqueue
- enqueueToast(testPackage, new TestableToastCallback());
+ boolean wasEnqueued = enqueueToast(testPackage, new TestableToastCallback());
assertEquals(1, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isTrue();
verify(mAm).setProcessImportant(any(), anyInt(), eq(true), any());
}
@@ -8242,8 +8247,9 @@
setAppInForegroundForToasts(mUid, true);
// enqueue toast -> toast should still enqueue
- enqueueTextToast(testPackage, "Text");
+ boolean wasEnqueued = enqueueTextToast(testPackage, "Text");
assertEquals(1, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isTrue();
verify(mAm).setProcessImportant(any(), anyInt(), eq(false), any());
}
@@ -8264,8 +8270,9 @@
setAppInForegroundForToasts(mUid, false);
// enqueue toast -> toast should still enqueue
- enqueueTextToast(testPackage, "Text");
+ boolean wasEnqueued = enqueueTextToast(testPackage, "Text");
assertEquals(1, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isTrue();
verify(mAm).setProcessImportant(any(), anyInt(), eq(false), any());
}
@@ -8274,7 +8281,8 @@
allowTestPackageToToast();
// enqueue toast -> no toasts enqueued
- enqueueTextToast(TEST_PACKAGE, "Text");
+ boolean wasEnqueued = enqueueTextToast(TEST_PACKAGE, "Text");
+ assertThat(wasEnqueued).isTrue();
verifyToastShownForTestPackage("Text", DEFAULT_DISPLAY);
}
@@ -8367,10 +8375,11 @@
.thenReturn(false);
// enqueue toast -> no toasts enqueued
- enqueueTextToast(testPackage, "Text");
+ boolean wasEnqueued = enqueueTextToast(testPackage, "Text");
verify(mStatusBar, never()).showToast(anyInt(), any(), any(), any(), any(), anyInt(), any(),
anyInt());
assertEquals(0, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isFalse();
}
@Test
@@ -8390,10 +8399,11 @@
when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
// enqueue toast -> no toasts enqueued
- enqueueToast(testPackage, new TestableToastCallback());
+ boolean wasEnqueued = enqueueToast(testPackage, new TestableToastCallback());
verify(mStatusBar, never()).showToast(anyInt(), any(), any(), any(), any(), anyInt(), any(),
anyInt());
assertEquals(0, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isFalse();
}
@Test
@@ -8415,8 +8425,9 @@
setAppInForegroundForToasts(mUid, false);
// enqueue toast -> no toasts enqueued
- enqueueToast(testPackage, new TestableToastCallback());
+ boolean wasEnqueued = enqueueToast(testPackage, new TestableToastCallback());
assertEquals(0, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isFalse();
}
@Test
@@ -8437,8 +8448,9 @@
setAppInForegroundForToasts(mUid, false);
// enqueue toast -> system toast can still be enqueued
- enqueueToast(testPackage, new TestableToastCallback());
+ boolean wasEnqueued = enqueueToast(testPackage, new TestableToastCallback());
assertEquals(1, mService.mToastQueue.size());
+ assertThat(wasEnqueued).isTrue();
}
@Test
@@ -8458,7 +8470,12 @@
// Trying to quickly enqueue more toast than allowed.
for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_TOASTS + 1; i++) {
- enqueueTextToast(testPackage, "Text");
+ boolean wasEnqueued = enqueueTextToast(testPackage, "Text");
+ if (i < NotificationManagerService.MAX_PACKAGE_TOASTS) {
+ assertThat(wasEnqueued).isTrue();
+ } else {
+ assertThat(wasEnqueued).isFalse();
+ }
}
// Only allowed number enqueued, rest ignored.
assertEquals(NotificationManagerService.MAX_PACKAGE_TOASTS, mService.mToastQueue.size());
@@ -15089,25 +15106,27 @@
.thenReturn(false);
}
- private void enqueueToast(String testPackage, ITransientNotification callback)
+ private boolean enqueueToast(String testPackage, ITransientNotification callback)
throws RemoteException {
- enqueueToast((INotificationManager) mService.mService, testPackage, new Binder(), callback);
+ return enqueueToast((INotificationManager) mService.mService, testPackage, new Binder(),
+ callback);
}
- private void enqueueToast(INotificationManager service, String testPackage,
+ private boolean enqueueToast(INotificationManager service, String testPackage,
IBinder token, ITransientNotification callback) throws RemoteException {
- service.enqueueToast(testPackage, token, callback, TOAST_DURATION, /* isUiContext= */ true,
- DEFAULT_DISPLAY);
+ return service.enqueueToast(testPackage, token, callback, TOAST_DURATION, /* isUiContext= */
+ true, DEFAULT_DISPLAY);
}
- private void enqueueTextToast(String testPackage, CharSequence text) throws RemoteException {
- enqueueTextToast(testPackage, text, /* isUiContext= */ true, DEFAULT_DISPLAY);
+ private boolean enqueueTextToast(String testPackage, CharSequence text) throws RemoteException {
+ return enqueueTextToast(testPackage, text, /* isUiContext= */ true, DEFAULT_DISPLAY);
}
- private void enqueueTextToast(String testPackage, CharSequence text, boolean isUiContext,
+ private boolean enqueueTextToast(String testPackage, CharSequence text, boolean isUiContext,
int displayId) throws RemoteException {
- ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(), text,
- TOAST_DURATION, isUiContext, displayId, /* textCallback= */ null);
+ return ((INotificationManager) mService.mService).enqueueTextToast(testPackage,
+ new Binder(), text, TOAST_DURATION, isUiContext, displayId,
+ /* textCallback= */ null);
}
private void mockIsVisibleBackgroundUsersSupported(boolean supported) {
diff --git a/services/tests/vibrator/AndroidManifest.xml b/services/tests/vibrator/AndroidManifest.xml
index a14ea55..c0f514f 100644
--- a/services/tests/vibrator/AndroidManifest.xml
+++ b/services/tests/vibrator/AndroidManifest.xml
@@ -30,6 +30,8 @@
<uses-permission android:name="android.permission.ACCESS_VIBRATOR_STATE" />
<!-- Required to set always-on vibrations -->
<uses-permission android:name="android.permission.VIBRATE_ALWAYS_ON" />
+ <!-- Required to play system-only haptic feedback constants -->
+ <uses-permission android:name="android.permission.VIBRATE_SYSTEM_CONSTANTS" />
<application android:debuggable="true">
<uses-library android:name="android.test.mock" android:required="true" />
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
index e3d4596..633a3c9 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
@@ -27,6 +27,8 @@
import static android.os.VibrationEffect.EFFECT_CLICK;
import static android.os.VibrationEffect.EFFECT_TEXTURE_TICK;
import static android.os.VibrationEffect.EFFECT_TICK;
+import static android.view.HapticFeedbackConstants.BIOMETRIC_CONFIRM;
+import static android.view.HapticFeedbackConstants.BIOMETRIC_REJECT;
import static android.view.HapticFeedbackConstants.CLOCK_TICK;
import static android.view.HapticFeedbackConstants.CONTEXT_CLICK;
import static android.view.HapticFeedbackConstants.KEYBOARD_RELEASE;
@@ -80,6 +82,8 @@
new int[] {SCROLL_ITEM_FOCUS, SCROLL_LIMIT, SCROLL_TICK};
private static final int[] KEYBOARD_FEEDBACK_CONSTANTS =
new int[] {KEYBOARD_TAP, KEYBOARD_RELEASE};
+ private static final int[] BIOMETRIC_FEEDBACK_CONSTANTS =
+ new int[] {BIOMETRIC_CONFIRM, BIOMETRIC_REJECT};
private static final float KEYBOARD_VIBRATION_FIXED_AMPLITUDE = 0.62f;
@@ -283,6 +287,17 @@
}
@Test
+ public void testVibrationAttribute_biometricConstants_returnsCommunicationRequestUsage() {
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ for (int effectId : BIOMETRIC_FEEDBACK_CONSTANTS) {
+ VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+ effectId, /* bypassVibrationIntensitySetting= */ false, /* fromIme= */ false);
+ assertThat(attrs.getUsage()).isEqualTo(VibrationAttributes.USAGE_COMMUNICATION_REQUEST);
+ }
+ }
+
+ @Test
public void testVibrationAttribute_forNotBypassingIntensitySettings() {
HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
@@ -422,6 +437,15 @@
}
}
+ @Test
+ public void testIsRestricted_biometricConstants_returnsTrue() {
+ HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+
+ for (int effectId : BIOMETRIC_FEEDBACK_CONSTANTS) {
+ assertThat(hapticProvider.isRestrictedHapticFeedback(effectId)).isTrue();
+ }
+ }
+
private HapticFeedbackVibrationProvider createProviderWithDefaultCustomizations() {
return createProvider(/* customizations= */ null);
}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 185677f..d6c0fef 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -1410,6 +1410,70 @@
}
@Test
+ public void performHapticFeedback_restrictedConstantsWithoutPermission_doesNotVibrate()
+ throws Exception {
+ // Deny permission to vibrate with restricted constants
+ denyPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS);
+ // Public constant, no permission required
+ mHapticFeedbackVibrationMap.put(
+ HapticFeedbackConstants.CONFIRM,
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
+ // Hidden system-only constant, permission required
+ mHapticFeedbackVibrationMap.put(
+ HapticFeedbackConstants.BIOMETRIC_CONFIRM,
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK));
+ mockVibrators(1);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setSupportedEffects(
+ VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_HEAVY_CLICK);
+ VibratorManagerService service = createSystemReadyService();
+
+ performHapticFeedbackAndWaitUntilFinished(
+ service, HapticFeedbackConstants.CONFIRM, /* always= */ false);
+
+ performHapticFeedbackAndWaitUntilFinished(
+ service, HapticFeedbackConstants.BIOMETRIC_CONFIRM, /* always= */ false);
+
+ List<VibrationEffectSegment> playedSegments = fakeVibrator.getAllEffectSegments();
+ assertEquals(1, playedSegments.size());
+ PrebakedSegment segment = (PrebakedSegment) playedSegments.get(0);
+ assertEquals(VibrationEffect.EFFECT_CLICK, segment.getEffectId());
+ }
+
+ @Test
+ public void performHapticFeedback_restrictedConstantsWithPermission_playsVibration()
+ throws Exception {
+ // Grant permission to vibrate with restricted constants
+ grantPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS);
+ // Public constant, no permission required
+ mHapticFeedbackVibrationMap.put(
+ HapticFeedbackConstants.CONFIRM,
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
+ // Hidden system-only constant, permission required
+ mHapticFeedbackVibrationMap.put(
+ HapticFeedbackConstants.BIOMETRIC_CONFIRM,
+ VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK));
+ mockVibrators(1);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setSupportedEffects(
+ VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_HEAVY_CLICK);
+ VibratorManagerService service = createSystemReadyService();
+
+ performHapticFeedbackAndWaitUntilFinished(
+ service, HapticFeedbackConstants.CONFIRM, /* always= */ false);
+
+ performHapticFeedbackAndWaitUntilFinished(
+ service, HapticFeedbackConstants.BIOMETRIC_CONFIRM, /* always= */ false);
+
+ List<VibrationEffectSegment> playedSegments = fakeVibrator.getAllEffectSegments();
+ assertEquals(2, playedSegments.size());
+ assertEquals(VibrationEffect.EFFECT_CLICK,
+ ((PrebakedSegment) playedSegments.get(0)).getEffectId());
+ assertEquals(VibrationEffect.EFFECT_HEAVY_CLICK,
+ ((PrebakedSegment) playedSegments.get(1)).getEffectId());
+ }
+
+ @Test
public void performHapticFeedback_doesNotVibrateWhenVibratorInfoNotReady() throws Exception {
denyPermission(android.Manifest.permission.VIBRATE);
mHapticFeedbackVibrationMap.put(
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 856ad2a..fbf1426 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -130,6 +130,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
@@ -2363,6 +2364,92 @@
}
@Test
+ public void testUserOverrideFullscreenForLandscapeDisplay() {
+ final int displayWidth = 1600;
+ final int displayHeight = 1400;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ spyOn(mActivity.mWmService.mLetterboxConfiguration);
+ doReturn(true).when(mActivity.mWmService.mLetterboxConfiguration)
+ .isUserAppAspectRatioFullscreenEnabled();
+
+ // Set user aspect ratio override
+ spyOn(mActivity.mLetterboxUiController);
+ doReturn(USER_MIN_ASPECT_RATIO_FULLSCREEN).when(mActivity.mLetterboxUiController)
+ .getUserMinAspectRatioOverrideCode();
+
+ prepareMinAspectRatio(mActivity, 16 / 9f, SCREEN_ORIENTATION_PORTRAIT);
+
+ final Rect bounds = mActivity.getBounds();
+
+ // bounds should be fullscreen
+ assertEquals(displayHeight, bounds.height());
+ assertEquals(displayWidth, bounds.width());
+ }
+
+ @Test
+ public void testUserOverrideFullscreenForPortraitDisplay() {
+ final int displayWidth = 1400;
+ final int displayHeight = 1600;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ spyOn(mActivity.mWmService.mLetterboxConfiguration);
+ doReturn(true).when(mActivity.mWmService.mLetterboxConfiguration)
+ .isUserAppAspectRatioFullscreenEnabled();
+
+ // Set user aspect ratio override
+ spyOn(mActivity.mLetterboxUiController);
+ doReturn(USER_MIN_ASPECT_RATIO_FULLSCREEN).when(mActivity.mLetterboxUiController)
+ .getUserMinAspectRatioOverrideCode();
+
+ prepareMinAspectRatio(mActivity, 16 / 9f, SCREEN_ORIENTATION_LANDSCAPE);
+
+ final Rect bounds = mActivity.getBounds();
+
+ // bounds should be fullscreen
+ assertEquals(displayHeight, bounds.height());
+ assertEquals(displayWidth, bounds.width());
+ }
+
+ @Test
+ public void testSystemFullscreenOverrideForLandscapeDisplay() {
+ final int displayWidth = 1600;
+ final int displayHeight = 1400;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ spyOn(mActivity.mLetterboxUiController);
+ doReturn(true).when(mActivity.mLetterboxUiController)
+ .isSystemOverrideToFullscreenEnabled();
+
+ prepareMinAspectRatio(mActivity, 16 / 9f, SCREEN_ORIENTATION_PORTRAIT);
+
+ final Rect bounds = mActivity.getBounds();
+
+ // bounds should be fullscreen
+ assertEquals(displayHeight, bounds.height());
+ assertEquals(displayWidth, bounds.width());
+ }
+
+ @Test
+ public void testSystemFullscreenOverrideForPortraitDisplay() {
+ final int displayWidth = 1400;
+ final int displayHeight = 1600;
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ spyOn(mActivity.mLetterboxUiController);
+ doReturn(true).when(mActivity.mLetterboxUiController)
+ .isSystemOverrideToFullscreenEnabled();
+
+ prepareMinAspectRatio(mActivity, 16 / 9f, SCREEN_ORIENTATION_LANDSCAPE);
+
+ final Rect bounds = mActivity.getBounds();
+
+ // bounds should be fullscreen
+ assertEquals(displayHeight, bounds.height());
+ assertEquals(displayWidth, bounds.width());
+ }
+
+ @Test
public void testUserOverrideSplitScreenAspectRatioForLandscapeDisplay() {
final int displayWidth = 1600;
final int displayHeight = 1400;
@@ -4117,6 +4204,7 @@
}
@Test
+ @Ignore // TODO(b/330888878): fix test in main
public void testPortraitCloseToSquareDisplayWithTaskbar_notLetterboxed() {
if (Flags.insetsDecoupledConfiguration()) {
// TODO (b/151861875): Re-enable it. This is disabled temporarily because the config