Merge "Team food CALL_LAYOUT_ASYNC_SET_DATA" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 75fb215..e3cbd92 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -21,6 +21,7 @@
":android.hardware.flags-aconfig-java{.generated_srcjars}",
":android.hardware.radio.flags-aconfig-java{.generated_srcjars}",
":android.location.flags-aconfig-java{.generated_srcjars}",
+ ":android.net.vcn.flags-aconfig-java{.generated_srcjars}",
":android.nfc.flags-aconfig-java{.generated_srcjars}",
":android.os.flags-aconfig-java{.generated_srcjars}",
":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
@@ -568,6 +569,19 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// VCN
+aconfig_declarations {
+ name: "android.net.vcn.flags-aconfig",
+ package: "android.net.vcn",
+ srcs: ["core/java/android/net/vcn/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.net.vcn.flags-aconfig-java",
+ aconfig_declarations: "android.net.vcn.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// DevicePolicy
aconfig_declarations {
name: "device_policy_aconfig_flags",
diff --git a/Android.bp b/Android.bp
index 986a071..b5f7e99 100644
--- a/Android.bp
+++ b/Android.bp
@@ -695,12 +695,10 @@
"--hide CallbackInterface",
"--hide DeprecationMismatch",
"--hide HiddenSuperclass",
- "--hide HiddenTypeParameter",
"--hide MissingPermission",
"--hide RequiresPermission",
"--hide SdkConstant",
"--hide Todo",
- "--hide UnavailableSymbol",
"--hide-package android.audio.policy.configuration.V7_0",
"--hide-package com.android.server",
"--manifest $(location :frameworks-base-core-AndroidManifest.xml)",
diff --git a/api/gen_combined_removed_dex.sh b/api/gen_combined_removed_dex.sh
index 71f366a..e0153f7 100755
--- a/api/gen_combined_removed_dex.sh
+++ b/api/gen_combined_removed_dex.sh
@@ -6,6 +6,6 @@
# Convert each removed.txt to the "dex format" equivalent, and print all output.
for f in "$@"; do
- "$metalava_path" "$f" --dex-api "${tmp_dir}/tmp"
+ "$metalava_path" signature-to-dex "$f" "${tmp_dir}/tmp"
cat "${tmp_dir}/tmp"
done
diff --git a/core/api/current.txt b/core/api/current.txt
index 3f1f720..cce8329 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -13322,9 +13322,12 @@
public final class SigningInfo implements android.os.Parcelable {
ctor public SigningInfo();
+ ctor @FlaggedApi("android.content.pm.archiving") public SigningInfo(@IntRange(from=0) int, @Nullable java.util.Collection<android.content.pm.Signature>, @Nullable java.util.Collection<java.security.PublicKey>, @Nullable java.util.Collection<android.content.pm.Signature>);
ctor public SigningInfo(android.content.pm.SigningInfo);
method public int describeContents();
method public android.content.pm.Signature[] getApkContentsSigners();
+ method @FlaggedApi("android.content.pm.archiving") @NonNull public java.util.Collection<java.security.PublicKey> getPublicKeys();
+ method @FlaggedApi("android.content.pm.archiving") @IntRange(from=0) public int getSchemeVersion();
method public android.content.pm.Signature[] getSigningCertificateHistory();
method public boolean hasMultipleSigners();
method public boolean hasPastSigningCertificates();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 796c800..83234a5 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -872,7 +872,7 @@
ctor public AttributionSource(int, @Nullable String, @Nullable String);
ctor public AttributionSource(int, @Nullable String, @Nullable String, @NonNull android.os.IBinder);
ctor public AttributionSource(int, @Nullable String, @Nullable String, @Nullable java.util.Set<java.lang.String>, @Nullable android.content.AttributionSource);
- ctor public AttributionSource(int, int, @Nullable String, @Nullable String, @NonNull android.os.IBinder, @Nullable String[], @Nullable android.content.AttributionSource);
+ ctor @FlaggedApi("android.permission.flags.attribution_source_constructor") public AttributionSource(int, int, @Nullable String, @Nullable String, @NonNull android.os.IBinder, @Nullable String[], @Nullable android.content.AttributionSource);
ctor @FlaggedApi("android.permission.flags.device_aware_permission_apis") public AttributionSource(int, int, @Nullable String, @Nullable String, @NonNull android.os.IBinder, @Nullable String[], int, @Nullable android.content.AttributionSource);
method public void enforceCallingPid();
}
@@ -2148,7 +2148,9 @@
}
public final class BugreportParams {
+ field @FlaggedApi("android.app.admin.flags.onboarding_bugreport_v2_enabled") public static final int BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL = 4; // 0x4
field @FlaggedApi("android.os.bugreport_mode_max_value") public static final int BUGREPORT_MODE_MAX_VALUE = 7; // 0x7
+ field @FlaggedApi("android.app.admin.flags.onboarding_bugreport_v2_enabled") public static final int BUGREPORT_MODE_ONBOARDING = 7; // 0x7
}
public class Build {
@@ -2287,7 +2289,7 @@
public final class PowerManager {
method public boolean areAutoPowerSaveModesEnabled();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER}) public void forceLowPowerStandbyActive(boolean);
- method public boolean isBatterySaverSupported();
+ method @FlaggedApi("android.os.battery_saver_supported_check_api") public boolean isBatterySaverSupported();
field public static final String ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED = "android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED";
field @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public static final int SYSTEM_WAKELOCK = -2147483648; // 0x80000000
}
diff --git a/core/java/android/app/smartspace/flags.aconfig b/core/java/android/app/smartspace/flags.aconfig
index 6aefa38..12af888 100644
--- a/core/java/android/app/smartspace/flags.aconfig
+++ b/core/java/android/app/smartspace/flags.aconfig
@@ -6,3 +6,10 @@
description: "Flag to enable the FlaggedApi to include RemoteViews in SmartspaceTarget"
bug: "300157758"
}
+
+flag {
+ name: "access_smartspace"
+ namespace: "sysui_integrations"
+ description: "Flag to enable the ACCESS_SMARTSPACE check in SmartspaceManagerService"
+ bug: "297207196"
+}
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 62fbcaf..4b2cee6 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -155,6 +155,7 @@
/** @hide */
@TestApi
+ @FlaggedApi(Flags.FLAG_ATTRIBUTION_SOURCE_CONSTRUCTOR)
public AttributionSource(int uid, int pid, @Nullable String packageName,
@Nullable String attributionTag, @NonNull IBinder token,
@Nullable String[] renouncedPermissions,
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index aefa55f..323592c 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1204,12 +1204,15 @@
/**
* This change id is the gatekeeper for all treatments that force a given min aspect ratio.
* Enabling this change will allow the following min aspect ratio treatments to be applied:
- * OVERRIDE_MIN_ASPECT_RATIO_MEDIUM
- * OVERRIDE_MIN_ASPECT_RATIO_LARGE
+ * <ul>
+ * <li>OVERRIDE_MIN_ASPECT_RATIO_MEDIUM
+ * <li>OVERRIDE_MIN_ASPECT_RATIO_LARGE
+ * </ul>
*
* If OVERRIDE_MIN_ASPECT_RATIO is applied, the min aspect ratio given in the app's manifest
* will be overridden to the largest enabled aspect ratio treatment unless the app's manifest
- * value is higher.
+ * value is higher. By default, this will only apply to activities with fixed portrait
+ * orientation if OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY is not explicitly disabled.
* @hide
*/
@ChangeId
diff --git a/core/java/android/content/pm/SigningInfo.java b/core/java/android/content/pm/SigningInfo.java
index 554de0c..543703e 100644
--- a/core/java/android/content/pm/SigningInfo.java
+++ b/core/java/android/content/pm/SigningInfo.java
@@ -16,9 +16,16 @@
package android.content.pm;
+import android.annotation.FlaggedApi;
+import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.ArraySet;
+
+import java.security.PublicKey;
+import java.util.Collection;
/**
* Information pertaining to the signing certificates used to sign a package.
@@ -33,6 +40,43 @@
}
/**
+ * Creates a new instance of information used to sign the APK.
+ *
+ * @param schemeVersion version of signing schema.
+ * @param apkContentsSigners signing certificates.
+ * @param publicKeys for the signing certificates.
+ * @param signingCertificateHistory All signing certificates the package has proven it is
+ * authorized to use.
+ *
+ * @see
+ * <a href="https://source.android.com/docs/security/features/apksigning#schemes">APK signing
+ * schemas</a>
+ */
+ @FlaggedApi(Flags.FLAG_ARCHIVING)
+ public SigningInfo(@IntRange(from = 0) int schemeVersion,
+ @Nullable Collection<Signature> apkContentsSigners,
+ @Nullable Collection<PublicKey> publicKeys,
+ @Nullable Collection<Signature> signingCertificateHistory) {
+ if (schemeVersion <= 0 || apkContentsSigners == null) {
+ mSigningDetails = SigningDetails.UNKNOWN;
+ return;
+ }
+ Signature[] signatures = apkContentsSigners != null && !apkContentsSigners.isEmpty()
+ ? apkContentsSigners.toArray(new Signature[apkContentsSigners.size()])
+ : null;
+ Signature[] pastSignatures =
+ signingCertificateHistory != null && !signingCertificateHistory.isEmpty()
+ ? signingCertificateHistory.toArray(new Signature[signingCertificateHistory.size()])
+ : null;
+ if (Signature.areExactArraysMatch(signatures, pastSignatures)) {
+ pastSignatures = null;
+ }
+ ArraySet<PublicKey> keys =
+ publicKeys != null && !publicKeys.isEmpty() ? new ArraySet<>(publicKeys) : null;
+ mSigningDetails = new SigningDetails(signatures, schemeVersion, keys, pastSignatures);
+ }
+
+ /**
* @hide only packagemanager should be populating this
*/
public SigningInfo(SigningDetails signingDetails) {
@@ -116,6 +160,26 @@
return mSigningDetails.getSignatures();
}
+ /**
+ * Returns the version of signing schema used to sign the APK.
+ *
+ * @see
+ * <a href="https://source.android.com/docs/security/features/apksigning#schemes">APK signing
+ * schemas</a>
+ */
+ @FlaggedApi(Flags.FLAG_ARCHIVING)
+ public @IntRange(from = 0) int getSchemeVersion() {
+ return mSigningDetails.getSignatureSchemeVersion();
+ }
+
+ /**
+ * Returns the public keys for the signing certificates.
+ */
+ @FlaggedApi(Flags.FLAG_ARCHIVING)
+ public @NonNull Collection<PublicKey> getPublicKeys() {
+ return mSigningDetails.getPublicKeys();
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index d2a15d1..75f0ceb 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -1435,12 +1435,10 @@
sExtraDisplayListenerLogging = !TextUtils.isEmpty(EXTRA_LOGGING_PACKAGE_NAME)
&& EXTRA_LOGGING_PACKAGE_NAME.equals(sCurrentPackageName);
}
- // TODO: b/306170135 - return sExtraDisplayListenerLogging instead
- return true;
+ return sExtraDisplayListenerLogging;
}
private static boolean extraLogging() {
- // TODO: b/306170135 - return sExtraDisplayListenerLogging & package name check instead
- return true;
+ return sExtraDisplayListenerLogging;
}
}
diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig
new file mode 100644
index 0000000..6956916
--- /dev/null
+++ b/core/java/android/net/vcn/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.net.vcn"
+
+flag {
+ name: "safe_mode_config"
+ namespace: "vcn"
+ description: "Feature flag for safe mode configurability"
+ bug: "276358140"
+}
\ No newline at end of file
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 58def6e..960e84d 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -26,6 +26,7 @@
import android.annotation.SuppressAutoDoc;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.UserHandleAware;
import android.annotation.WorkerThread;
import android.app.ActivityManager;
import android.content.Context;
@@ -280,8 +281,8 @@
*
* <p>{@link BugreportManager} takes ownership of {@code bugreportFd}.
*
- * <p>The caller may only request to retrieve a given bugreport once. Subsequent calls will fail
- * with error code {@link BugreportCallback#BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE}.
+ * <p>The caller can reattempt to retrieve the bugreport multiple times if the user has not
+ * consented on previous attempts.
*
* @param bugreportFile the identifier for a bugreport that was previously generated for this
* caller using {@code startBugreport}.
@@ -294,6 +295,7 @@
@SystemApi
@RequiresPermission(Manifest.permission.DUMP)
@WorkerThread
+ @UserHandleAware
public void retrieveBugreport(
@NonNull String bugreportFile,
@NonNull ParcelFileDescriptor bugreportFd,
@@ -307,8 +309,10 @@
Preconditions.checkNotNull(callback);
DumpstateListener dsListener = new DumpstateListener(executor, callback, false, false);
mBinder.retrieveBugreport(Binder.getCallingUid(), mContext.getOpPackageName(),
+ mContext.getUserId(),
bugreportFd.getFileDescriptor(),
bugreportFile,
+ /* keepBugreportOnRetrieval = */ false,
dsListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/os/BugreportParams.java b/core/java/android/os/BugreportParams.java
index e8ad303..8510084 100644
--- a/core/java/android/os/BugreportParams.java
+++ b/core/java/android/os/BugreportParams.java
@@ -20,6 +20,7 @@
import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.admin.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -128,6 +129,8 @@
*
* @hide
*/
+ @TestApi
+ @FlaggedApi(Flags.FLAG_ONBOARDING_BUGREPORT_V2_ENABLED)
public static final int BUGREPORT_MODE_ONBOARDING = IDumpstate.BUGREPORT_MODE_ONBOARDING;
/**
@@ -145,7 +148,8 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "BUGREPORT_FLAG_" }, value = {
BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA,
- BUGREPORT_FLAG_DEFER_CONSENT
+ BUGREPORT_FLAG_DEFER_CONSENT,
+ BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL
})
public @interface BugreportFlag {}
@@ -165,4 +169,20 @@
* String, ParcelFileDescriptor, Executor, BugreportManager.BugreportCallback)}.
*/
public static final int BUGREPORT_FLAG_DEFER_CONSENT = IDumpstate.BUGREPORT_FLAG_DEFER_CONSENT;
+
+ /**
+ * Flag for keeping a bugreport stored even after it has been retrieved via
+ * {@link BugreportManager#retrieveBugreport}.
+ *
+ * <p>This flag can only be used when {@link #BUGREPORT_FLAG_DEFER_CONSENT} is set.
+ * The bugreport may be retrieved multiple times using
+ * {@link BugreportManager#retrieveBugreport(
+ * String, ParcelFileDescriptor, Executor, BugreportManager.BugreportCallback)}.
+ *
+ * @hide
+ */
+ @TestApi
+ @FlaggedApi(Flags.FLAG_ONBOARDING_BUGREPORT_V2_ENABLED)
+ public static final int BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL =
+ IDumpstate.BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL;
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index fce715a..d2c1755 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.FlaggedApi;
import android.Manifest.permission;
import android.annotation.CallbackExecutor;
import android.annotation.CurrentTimeMillisLong;
@@ -1940,6 +1941,7 @@
*
* @hide
*/
+ @FlaggedApi(android.os.Flags.FLAG_BATTERY_SAVER_SUPPORTED_CHECK_API)
@TestApi
public boolean isBatterySaverSupported() {
try {
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 10b9e3a..86f03cd 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -41,3 +41,10 @@
description: "Guards the ADPF power efficiency API"
bug: "288117936"
}
+
+flag {
+ name: "battery_saver_supported_check_api"
+ namespace: "backstage_power"
+ description: "Guards a new API in PowerManager to check if battery saver is supported or not."
+ bug: "305067031"
+}
\ No newline at end of file
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 3f06a91..0798f65 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -35,3 +35,10 @@
description: "enable the shouldRegisterAttributionSource API"
bug: "305057691"
}
+
+flag {
+ name: "attribution_source_constructor"
+ namespace: "permissions"
+ description: "enable AttributionSource(int, int, String, String, IBinder, String[], AttributionSource)"
+ bug: "304478648"
+}
\ No newline at end of file
diff --git a/core/java/android/service/voice/HotwordTrainingDataLimitEnforcer.java b/core/java/android/service/voice/HotwordTrainingDataLimitEnforcer.java
index 76e506c..7eb5280 100644
--- a/core/java/android/service/voice/HotwordTrainingDataLimitEnforcer.java
+++ b/core/java/android/service/voice/HotwordTrainingDataLimitEnforcer.java
@@ -27,10 +27,9 @@
import com.android.internal.annotations.VisibleForTesting;
import java.io.File;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-import java.util.TimeZone;
+import java.time.LocalDate;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
/**
* Enforces daily limits on the egress of {@link HotwordTrainingData} from the hotword detection
@@ -111,9 +110,9 @@
}
private boolean incrementTrainingDataEgressCountLocked() {
- SimpleDateFormat dt = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
- dt.setTimeZone(TimeZone.getTimeZone("UTC"));
- String currentDate = dt.format(new Date());
+ LocalDate utcDate = LocalDate.now(ZoneOffset.UTC);
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ String currentDate = utcDate.format(formatter);
String storedDate = mSharedPreferences.getString(TRAINING_DATA_EGRESS_DATE, "");
int storedCount = mSharedPreferences.getInt(TRAINING_DATA_EGRESS_COUNT, 0);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index dfada58..4da02f9 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1020,8 +1020,7 @@
mDisplay = display;
mBasePackageName = context.getBasePackageName();
final String name = DisplayProperties.debug_vri_package().orElse(null);
- // TODO: b/306170135 - return to using textutils check on package name.
- mExtraDisplayListenerLogging = true;
+ mExtraDisplayListenerLogging = !TextUtils.isEmpty(name) && name.equals(mBasePackageName);
mThread = Thread.currentThread();
mLocation = new WindowLeaked(null);
mLocation.fillInStackTrace();
@@ -11613,7 +11612,14 @@
Log.d(mTag, "registerCallbacksForSync syncBuffer=" + syncBuffer);
}
- surfaceSyncGroup.addTransaction(mPendingTransaction);
+ final Transaction t;
+ if (mHasPendingTransactions) {
+ t = new Transaction();
+ t.merge(mPendingTransaction);
+ } else {
+ t = null;
+ }
+
mAttachInfo.mThreadedRenderer.registerRtFrameCallback(new FrameDrawingCallback() {
@Override
public void onFrameDraw(long frame) {
@@ -11626,6 +11632,9 @@
"Received frameDrawingCallback syncResult=" + syncResult + " frameNum="
+ frame + ".");
}
+ if (t != null) {
+ mergeWithNextTransaction(t, frame);
+ }
// If the syncResults are SYNC_LOST_SURFACE_REWARD_IF_FOUND or
// SYNC_CONTEXT_IS_STOPPED it means nothing will draw. There's no need to set up
diff --git a/core/java/android/window/TaskFragmentParentInfo.java b/core/java/android/window/TaskFragmentParentInfo.java
index 841354a..e6eeca4 100644
--- a/core/java/android/window/TaskFragmentParentInfo.java
+++ b/core/java/android/window/TaskFragmentParentInfo.java
@@ -35,17 +35,21 @@
private final boolean mVisible;
+ private final boolean mHasDirectActivity;
+
public TaskFragmentParentInfo(@NonNull Configuration configuration, int displayId,
- boolean visible) {
+ boolean visible, boolean hasDirectActivity) {
mConfiguration.setTo(configuration);
mDisplayId = displayId;
mVisible = visible;
+ mHasDirectActivity = hasDirectActivity;
}
public TaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) {
mConfiguration.setTo(info.getConfiguration());
mDisplayId = info.mDisplayId;
mVisible = info.mVisible;
+ mHasDirectActivity = info.mHasDirectActivity;
}
/** The {@link Configuration} of the parent Task */
@@ -68,6 +72,14 @@
}
/**
+ * Whether the parent Task has any direct child activity, which is not embedded in any
+ * TaskFragment, or not
+ */
+ public boolean hasDirectActivity() {
+ return mHasDirectActivity;
+ }
+
+ /**
* Returns {@code true} if the parameters which are important for task fragment
* organizers are equal between this {@link TaskFragmentParentInfo} and {@code that}.
* Note that this method is usually called with
@@ -80,7 +92,7 @@
return false;
}
return getWindowingMode() == that.getWindowingMode() && mDisplayId == that.mDisplayId
- && mVisible == that.mVisible;
+ && mVisible == that.mVisible && mHasDirectActivity == that.mHasDirectActivity;
}
@WindowConfiguration.WindowingMode
@@ -94,6 +106,7 @@
+ "config=" + mConfiguration
+ ", displayId=" + mDisplayId
+ ", visible=" + mVisible
+ + ", hasDirectActivity=" + mHasDirectActivity
+ "}";
}
@@ -114,7 +127,8 @@
final TaskFragmentParentInfo that = (TaskFragmentParentInfo) obj;
return mConfiguration.equals(that.mConfiguration)
&& mDisplayId == that.mDisplayId
- && mVisible == that.mVisible;
+ && mVisible == that.mVisible
+ && mHasDirectActivity == that.mHasDirectActivity;
}
@Override
@@ -122,6 +136,7 @@
int result = mConfiguration.hashCode();
result = 31 * result + mDisplayId;
result = 31 * result + (mVisible ? 1 : 0);
+ result = 31 * result + (mHasDirectActivity ? 1 : 0);
return result;
}
@@ -130,12 +145,14 @@
mConfiguration.writeToParcel(dest, flags);
dest.writeInt(mDisplayId);
dest.writeBoolean(mVisible);
+ dest.writeBoolean(mHasDirectActivity);
}
private TaskFragmentParentInfo(Parcel in) {
mConfiguration.readFromParcel(in);
mDisplayId = in.readInt();
mVisible = in.readBoolean();
+ mHasDirectActivity = in.readBoolean();
}
public static final Creator<TaskFragmentParentInfo> CREATOR =
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 0399430..7d78f29 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -603,6 +603,17 @@
}
/**
+ * Returns the monotonic clock time when the available battery history collection started.
+ */
+ public long getStartTime() {
+ if (!mHistoryFiles.isEmpty()) {
+ return mHistoryFiles.get(0).monotonicTimeMs;
+ } else {
+ return mHistoryBufferStartTime;
+ }
+ }
+
+ /**
* Start iterating history files and history buffer.
*
* @param startTimeMs monotonic time (the HistoryItem.time field) to start iterating from,
diff --git a/core/java/com/android/internal/os/MultiStateStats.java b/core/java/com/android/internal/os/MultiStateStats.java
index dc5055a..ecfed53 100644
--- a/core/java/com/android/internal/os/MultiStateStats.java
+++ b/core/java/com/android/internal/os/MultiStateStats.java
@@ -29,6 +29,8 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
+import java.util.function.Consumer;
+import java.util.function.Function;
/**
* Maintains multidimensional multi-state stats. States could be something like on-battery (0,1),
@@ -52,13 +54,55 @@
public States(String name, boolean tracked, String... labels) {
mName = name;
- this.mTracked = tracked;
- this.mLabels = labels;
+ mTracked = tracked;
+ mLabels = labels;
}
public boolean isTracked() {
return mTracked;
}
+
+ public String getName() {
+ return mName;
+ }
+
+ public String[] getLabels() {
+ return mLabels;
+ }
+
+ /**
+ * Iterates over all combinations of tracked states and invokes <code>consumer</code>
+ * for each of them.
+ */
+ public static void forEachTrackedStateCombination(States[] states,
+ Consumer<int[]> consumer) {
+ forEachTrackedStateCombination(consumer, states, new int[states.length], 0);
+ }
+
+ /**
+ * Recursive function that does a depth-first traversal of the multi-dimensional
+ * state space. Each time the traversal reaches the end of the <code>states</code> array,
+ * <code>statesValues</code> contains a unique combination of values for all tracked states.
+ * For untracked states, the corresponding values are left as 0. The end result is
+ * that the <code>consumer</code> is invoked for every unique combination of tracked state
+ * values. For example, it may be a sequence of calls like screen-on/power-on,
+ * screen-on/power-off, screen-off/power-on, screen-off/power-off.
+ */
+ private static void forEachTrackedStateCombination(Consumer<int[]> consumer,
+ States[] states, int[] statesValues, int stateIndex) {
+ if (stateIndex < statesValues.length) {
+ if (!states[stateIndex].mTracked) {
+ forEachTrackedStateCombination(consumer, states, statesValues, stateIndex + 1);
+ return;
+ }
+ for (int i = 0; i < states[stateIndex].mLabels.length; i++) {
+ statesValues[stateIndex] = i;
+ forEachTrackedStateCombination(consumer, states, statesValues, stateIndex + 1);
+ }
+ return;
+ }
+ consumer.accept(statesValues);
+ }
}
/**
@@ -276,6 +320,13 @@
}
/**
+ * Updates the stats values for the provided combination of states.
+ */
+ public void setStats(int[] states, long[] values) {
+ mCounter.setValues(mFactory.getSerialState(states), values);
+ }
+
+ /**
* Resets the counters.
*/
public void reset() {
@@ -293,24 +344,27 @@
*/
public void writeXml(TypedXmlSerializer serializer) throws IOException {
long[] tmpArray = new long[mCounter.getArrayLength()];
- writeXmlAllStates(serializer, new int[mFactory.mStates.length], 0, tmpArray);
+
+ try {
+ States.forEachTrackedStateCombination(mFactory.mStates,
+ states -> {
+ try {
+ writeXmlForStates(serializer, states, tmpArray);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ } catch (RuntimeException e) {
+ if (e.getCause() instanceof IOException) {
+ throw (IOException) e.getCause();
+ } else {
+ throw e;
+ }
+ }
}
- private void writeXmlAllStates(TypedXmlSerializer serializer, int[] states, int stateIndex,
- long[] values) throws IOException {
- if (stateIndex < states.length) {
- if (!mFactory.mStates[stateIndex].mTracked) {
- writeXmlAllStates(serializer, states, stateIndex + 1, values);
- return;
- }
-
- for (int i = 0; i < mFactory.mStates[stateIndex].mLabels.length; i++) {
- states[stateIndex] = i;
- writeXmlAllStates(serializer, states, stateIndex + 1, values);
- }
- return;
- }
-
+ private void writeXmlForStates(TypedXmlSerializer serializer, int[] states, long[] values)
+ throws IOException {
mCounter.getCounts(values, mFactory.getSerialState(states));
boolean nonZero = false;
for (long value : values) {
@@ -391,48 +445,33 @@
/**
* Prints the accumulated stats, one line of every combination of states that has data.
*/
- public void dump(PrintWriter pw) {
- long[] tmpArray = new long[mCounter.getArrayLength()];
- dumpAllStates(pw, new int[mFactory.mStates.length], 0, tmpArray);
- }
-
- private void dumpAllStates(PrintWriter pw, int[] states, int stateIndex, long[] values) {
- if (stateIndex < states.length) {
- if (!mFactory.mStates[stateIndex].mTracked) {
- dumpAllStates(pw, states, stateIndex + 1, values);
+ public void dump(PrintWriter pw, Function<long[], String> statsFormatter) {
+ long[] values = new long[mCounter.getArrayLength()];
+ States.forEachTrackedStateCombination(mFactory.mStates, states -> {
+ mCounter.getCounts(values, mFactory.getSerialState(states));
+ boolean nonZero = false;
+ for (long value : values) {
+ if (value != 0) {
+ nonZero = true;
+ break;
+ }
+ }
+ if (!nonZero) {
return;
}
- for (int i = 0; i < mFactory.mStates[stateIndex].mLabels.length; i++) {
- states[stateIndex] = i;
- dumpAllStates(pw, states, stateIndex + 1, values);
- }
- return;
- }
-
- mCounter.getCounts(values, mFactory.getSerialState(states));
- boolean nonZero = false;
- for (long value : values) {
- if (value != 0) {
- nonZero = true;
- break;
- }
- }
- if (!nonZero) {
- return;
- }
-
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < states.length; i++) {
- if (mFactory.mStates[i].mTracked) {
- if (sb.length() != 0) {
- sb.append(" ");
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < states.length; i++) {
+ if (mFactory.mStates[i].mTracked) {
+ if (sb.length() != 0) {
+ sb.append(" ");
+ }
+ sb.append(mFactory.mStates[i].mLabels[states[i]]);
}
- sb.append(mFactory.mStates[i].mLabels[states[i]]);
}
- }
- sb.append(" ");
- sb.append(Arrays.toString(values));
- pw.println(sb);
+ sb.append(" ");
+ sb.append(statsFormatter.apply(values));
+ pw.println(sb);
+ });
}
}
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 503e689..2298cbd 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -44,7 +44,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.Locale;
/**
* Reports power consumption values for various device activities. Reads values from an XML file.
@@ -295,7 +294,7 @@
private static final long SUBSYSTEM_FIELDS_MASK = 0xFFFF_FFFF;
- private static final int DEFAULT_CPU_POWER_BRACKET_NUMBER = 3;
+ public static final int POWER_BRACKETS_UNSPECIFIED = -1;
/**
* A map from Power Use Item to its power consumption.
@@ -361,7 +360,7 @@
}
initCpuClusters();
initCpuScalingPolicies();
- initCpuPowerBrackets(DEFAULT_CPU_POWER_BRACKET_NUMBER);
+ initCpuPowerBrackets();
initDisplays();
initModem();
}
@@ -560,8 +559,7 @@
/**
* Parses or computes CPU power brackets: groups of states with similar power requirements.
*/
- @VisibleForTesting
- public void initCpuPowerBrackets(int defaultCpuPowerBracketNumber) {
+ private void initCpuPowerBrackets() {
boolean anyBracketsSpecified = false;
boolean allBracketsSpecified = true;
for (int i = mCpuScalingPolicies.size() - 1; i >= 0; i--) {
@@ -580,79 +578,32 @@
"Power brackets should be specified for all scaling policies or none");
}
+ if (!allBracketsSpecified) {
+ mCpuPowerBracketCount = POWER_BRACKETS_UNSPECIFIED;
+ return;
+ }
+
mCpuPowerBracketCount = 0;
- if (allBracketsSpecified) {
- for (int i = mCpuScalingPolicies.size() - 1; i >= 0; i--) {
- int policy = mCpuScalingPolicies.keyAt(i);
- CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i);
- final Double[] data = sPowerArrayMap.get(CPU_POWER_BRACKETS_PREFIX + policy);
- if (data.length != cpuScalingPolicyPower.powerBrackets.length) {
- throw new RuntimeException(
- "Wrong number of items in " + CPU_POWER_BRACKETS_PREFIX + policy
- + ", expected: "
- + cpuScalingPolicyPower.powerBrackets.length);
- }
-
- for (int j = 0; j < data.length; j++) {
- final int bracket = (int) Math.round(data[j]);
- cpuScalingPolicyPower.powerBrackets[j] = bracket;
- if (bracket > mCpuPowerBracketCount) {
- mCpuPowerBracketCount = bracket;
- }
- }
- }
- mCpuPowerBracketCount++;
- } else {
- double minPower = Double.MAX_VALUE;
- double maxPower = Double.MIN_VALUE;
- int stateCount = 0;
- for (int i = mCpuScalingPolicies.size() - 1; i >= 0; i--) {
- int policy = mCpuScalingPolicies.keyAt(i);
- CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i);
- final int steps = cpuScalingPolicyPower.stepPower.length;
- for (int step = 0; step < steps; step++) {
- final double power = getAveragePowerForCpuScalingStep(policy, step);
- if (power < minPower) {
- minPower = power;
- }
- if (power > maxPower) {
- maxPower = power;
- }
- }
- stateCount += steps;
+ for (int i = mCpuScalingPolicies.size() - 1; i >= 0; i--) {
+ int policy = mCpuScalingPolicies.keyAt(i);
+ CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i);
+ final Double[] data = sPowerArrayMap.get(CPU_POWER_BRACKETS_PREFIX + policy);
+ if (data.length != cpuScalingPolicyPower.powerBrackets.length) {
+ throw new RuntimeException(
+ "Wrong number of items in " + CPU_POWER_BRACKETS_PREFIX + policy
+ + ", expected: "
+ + cpuScalingPolicyPower.powerBrackets.length);
}
- if (stateCount <= defaultCpuPowerBracketNumber) {
- mCpuPowerBracketCount = stateCount;
- int bracket = 0;
- for (int i = 0; i < mCpuScalingPolicies.size(); i++) {
- CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i);
- final int steps = cpuScalingPolicyPower.stepPower.length;
- for (int step = 0; step < steps; step++) {
- cpuScalingPolicyPower.powerBrackets[step] = bracket++;
- }
- }
- } else {
- mCpuPowerBracketCount = defaultCpuPowerBracketNumber;
- final double minLogPower = Math.log(minPower);
- final double logBracket = (Math.log(maxPower) - minLogPower)
- / defaultCpuPowerBracketNumber;
-
- for (int i = mCpuScalingPolicies.size() - 1; i >= 0; i--) {
- int policy = mCpuScalingPolicies.keyAt(i);
- CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i);
- final int steps = cpuScalingPolicyPower.stepPower.length;
- for (int step = 0; step < steps; step++) {
- final double power = getAveragePowerForCpuScalingStep(policy, step);
- int bracket = (int) ((Math.log(power) - minLogPower) / logBracket);
- if (bracket >= defaultCpuPowerBracketNumber) {
- bracket = defaultCpuPowerBracketNumber - 1;
- }
- cpuScalingPolicyPower.powerBrackets[step] = bracket;
- }
+ for (int j = 0; j < data.length; j++) {
+ final int bracket = (int) Math.round(data[j]);
+ cpuScalingPolicyPower.powerBrackets[j] = bracket;
+ if (bracket > mCpuPowerBracketCount) {
+ mCpuPowerBracketCount = bracket;
}
}
}
+ mCpuPowerBracketCount++;
}
private static class CpuScalingPolicyPower {
@@ -771,44 +722,13 @@
/**
* Returns the number of CPU power brackets: groups of states with similar power requirements.
+ * If power brackets are not specified, returns {@link #POWER_BRACKETS_UNSPECIFIED}
*/
public int getCpuPowerBracketCount() {
return mCpuPowerBracketCount;
}
/**
- * Description of a CPU power bracket: which cluster/frequency combinations are included.
- */
- public String getCpuPowerBracketDescription(CpuScalingPolicies cpuScalingPolicies,
- int powerBracket) {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < mCpuScalingPolicies.size(); i++) {
- int policy = mCpuScalingPolicies.keyAt(i);
- CpuScalingPolicyPower cpuScalingPolicyPower = mCpuScalingPolicies.valueAt(i);
- int[] brackets = cpuScalingPolicyPower.powerBrackets;
- int[] freqs = cpuScalingPolicies.getFrequencies(policy);
- for (int step = 0; step < brackets.length; step++) {
- if (brackets[step] == powerBracket) {
- if (sb.length() != 0) {
- sb.append(", ");
- }
- if (mCpuScalingPolicies.size() > 1) {
- sb.append(policy).append('/');
- }
- if (step < freqs.length) {
- sb.append(freqs[step] / 1000);
- }
- sb.append('(');
- sb.append(String.format(Locale.US, "%.1f",
- getAveragePowerForCpuScalingStep(policy, step)));
- sb.append(')');
- }
- }
- }
- return sb.toString();
- }
-
- /**
* Returns the CPU power bracket corresponding to the specified scaling policy and frequency
* step
*/
diff --git a/core/java/com/android/internal/os/PowerStats.java b/core/java/com/android/internal/os/PowerStats.java
index 1130a45..1a7efac 100644
--- a/core/java/com/android/internal/os/PowerStats.java
+++ b/core/java/com/android/internal/os/PowerStats.java
@@ -55,12 +55,12 @@
private static final int STATS_ARRAY_LENGTH_SHIFT =
Integer.numberOfTrailingZeros(STATS_ARRAY_LENGTH_MASK);
public static final int MAX_STATS_ARRAY_LENGTH =
- 2 ^ Integer.bitCount(STATS_ARRAY_LENGTH_MASK) - 1;
+ (1 << Integer.bitCount(STATS_ARRAY_LENGTH_MASK)) - 1;
private static final int UID_STATS_ARRAY_LENGTH_MASK = 0x00FF0000;
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 =
- (2 ^ Integer.bitCount(UID_STATS_ARRAY_LENGTH_MASK)) - 1;
+ (1 << Integer.bitCount(UID_STATS_ARRAY_LENGTH_MASK)) - 1;
/**
* Descriptor of the stats collected for a given power component (e.g. CPU, WiFi etc).
diff --git a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
index 72969f7..37a499a 100644
--- a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
+++ b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
@@ -104,19 +104,15 @@
private static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT";
private static final Path[] UI_TRACES_PREDUMPED = {
+ Paths.get("/data/misc/perfetto-traces/bugreport/systrace.pftrace"),
Paths.get("/data/misc/wmtrace/ime_trace_clients.winscope"),
Paths.get("/data/misc/wmtrace/ime_trace_managerservice.winscope"),
Paths.get("/data/misc/wmtrace/ime_trace_service.winscope"),
Paths.get("/data/misc/wmtrace/wm_trace.winscope"),
Paths.get("/data/misc/wmtrace/wm_log.winscope"),
- Paths.get("/data/misc/wmtrace/layers_trace.winscope"),
- Paths.get("/data/misc/wmtrace/transactions_trace.winscope"),
- Paths.get("/data/misc/wmtrace/transition_trace.winscope"),
+ Paths.get("/data/misc/wmtrace/wm_transition_trace.winscope"),
Paths.get("/data/misc/wmtrace/shell_transition_trace.winscope"),
};
- private static final Path[] UI_TRACES_GENERATED_DURING_BUGREPORT = {
- Paths.get("/data/misc/wmtrace/layers_trace_from_transactions.winscope"),
- };
private Handler mHandler;
private Executor mExecutor;
@@ -210,7 +206,7 @@
mBrm.preDumpUiData();
waitTillDumpstateExitedOrTimeout();
- List<File> expectedPreDumpedTraceFiles = copyFilesAsRoot(UI_TRACES_PREDUMPED);
+ List<File> expectedPreDumpedTraceFiles = copyFiles(UI_TRACES_PREDUMPED);
BugreportCallbackImpl callback = new BugreportCallbackImpl();
mBrm.startBugreport(mBugreportFd, null, fullWithUsePreDumpFlag(), mExecutor,
@@ -225,7 +221,6 @@
assertFdsAreClosed(mBugreportFd);
assertThatBugreportContainsFiles(UI_TRACES_PREDUMPED);
- assertThatBugreportContainsFiles(UI_TRACES_GENERATED_DURING_BUGREPORT);
List<File> actualPreDumpedTraceFiles = extractFilesFromBugreport(UI_TRACES_PREDUMPED);
assertThatAllFileContentsAreEqual(actualPreDumpedTraceFiles, expectedPreDumpedTraceFiles);
@@ -240,9 +235,9 @@
// In some corner cases, data dumped as part of the full bugreport could be the same as the
// pre-dumped data and this test would fail. Hence, here we create fake/artificial
// pre-dumped data that we know it won't match with the full bugreport data.
- createFilesWithFakeDataAsRoot(UI_TRACES_PREDUMPED, "system");
+ createFakeTraceFiles(UI_TRACES_PREDUMPED);
- List<File> preDumpedTraceFiles = copyFilesAsRoot(UI_TRACES_PREDUMPED);
+ List<File> preDumpedTraceFiles = copyFiles(UI_TRACES_PREDUMPED);
BugreportCallbackImpl callback = new BugreportCallbackImpl();
mBrm.startBugreport(mBugreportFd, null, full(), mExecutor,
@@ -257,7 +252,6 @@
assertFdsAreClosed(mBugreportFd);
assertThatBugreportContainsFiles(UI_TRACES_PREDUMPED);
- assertThatBugreportContainsFiles(UI_TRACES_GENERATED_DURING_BUGREPORT);
List<File> actualTraceFiles = extractFilesFromBugreport(UI_TRACES_PREDUMPED);
assertThatAllFileContentsAreDifferent(preDumpedTraceFiles, actualTraceFiles);
@@ -480,7 +474,32 @@
return f;
}
- private static void startPreDumpedUiTraces() {
+ private static void startPreDumpedUiTraces() throws Exception {
+ // Perfetto traces
+ String perfettoConfig =
+ "buffers: {\n"
+ + " size_kb: 2048\n"
+ + " fill_policy: RING_BUFFER\n"
+ + "}\n"
+ + "data_sources: {\n"
+ + " config {\n"
+ + " name: \"android.surfaceflinger.transactions\"\n"
+ + " }\n"
+ + "}\n"
+ + "bugreport_score: 10\n";
+ File tmp = createTempFile("tmp", ".cfg");
+ Files.write(perfettoConfig.getBytes(StandardCharsets.UTF_8), tmp);
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+ "install -m 644 -o root -g root "
+ + tmp.getAbsolutePath() + " /data/misc/perfetto-configs/bugreport-manager-test.cfg"
+ );
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+ "perfetto --background-wait"
+ + " --config /data/misc/perfetto-configs/bugreport-manager-test.cfg --txt"
+ + " --out /data/misc/perfetto-traces/not-used.perfetto-trace"
+ );
+
+ // Legacy traces
InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
"cmd input_method tracing start"
);
@@ -563,19 +582,24 @@
return extractedFile;
}
- private static void createFilesWithFakeDataAsRoot(Path[] paths, String owner) throws Exception {
+ private static void createFakeTraceFiles(Path[] paths) throws Exception {
File src = createTempFile("fake", ".data");
Files.write("fake data".getBytes(StandardCharsets.UTF_8), src);
for (Path path : paths) {
InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
- "install -m 611 -o " + owner + " -g " + owner
- + " " + src.getAbsolutePath() + " " + path.toString()
+ "install -m 644 -o system -g system "
+ + src.getAbsolutePath() + " " + path.toString()
);
}
+
+ // Dumpstate executes "perfetto --save-for-bugreport" as shell
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+ "chown shell:shell /data/misc/perfetto-traces/bugreport/systrace.pftrace"
+ );
}
- private static List<File> copyFilesAsRoot(Path[] paths) throws Exception {
+ private static List<File> copyFiles(Path[] paths) throws Exception {
ArrayList<File> files = new ArrayList<File>();
for (Path src : paths) {
File dst = createTempFile(src.getFileName().toString(), ".copy");
diff --git a/core/tests/coretests/src/android/content/BrickDeniedTest.java b/core/tests/coretests/src/android/content/BrickDeniedTest.java
deleted file mode 100644
index d8c9baa..0000000
--- a/core/tests/coretests/src/android/content/BrickDeniedTest.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content;
-
-import android.test.AndroidTestCase;
-
-import androidx.test.filters.SmallTest;
-
-/** Test to make sure brick intents <b>don't</b> work without permission. */
-public class BrickDeniedTest extends AndroidTestCase {
- @SmallTest
- public void testBrick() {
- // Try both the old and new brick intent names. Neither should work,
- // since this test application doesn't have the required permission.
- // If it does work, well, the test certainly won't pass.
- getContext().sendBroadcast(new Intent("SHES_A_BRICK_HOUSE"));
- getContext().sendBroadcast(new Intent("android.intent.action.BRICK"));
- }
-}
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 8fa6376..77202d1 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
@@ -21,16 +21,12 @@
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 com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import android.annotation.XmlRes;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
-import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -540,66 +536,4 @@
private void assertEquals(double expected, double actual) {
Assert.assertEquals(expected, actual, 0.1);
}
-
- @Test
- public void powerBrackets_specifiedInPowerProfile() {
- mProfile.forceInitForTesting(mContext, R.xml.power_profile_test_power_brackets);
- mProfile.initCpuPowerBrackets(8);
-
- int cpuPowerBracketCount = mProfile.getCpuPowerBracketCount();
- assertThat(cpuPowerBracketCount).isEqualTo(2);
- assertThat(new int[]{
- mProfile.getCpuPowerBracketForScalingStep(0, 0),
- mProfile.getCpuPowerBracketForScalingStep(4, 0),
- mProfile.getCpuPowerBracketForScalingStep(4, 1),
- }).isEqualTo(new int[]{1, 1, 0});
- }
-
- @Test
- public void powerBrackets_automatic() {
- mProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
- CpuScalingPolicies scalingPolicies = new CpuScalingPolicies(
- new SparseArray<>() {{
- put(0, new int[]{0, 1, 2});
- put(3, new int[]{3, 4});
- }},
- new SparseArray<>() {{
- put(0, new int[]{300000, 1000000, 2000000});
- put(3, new int[]{300000, 1000000, 2500000, 3000000});
- }});
-
- assertThat(mProfile.getCpuPowerBracketCount()).isEqualTo(3);
- assertThat(mProfile.getCpuPowerBracketDescription(scalingPolicies, 0))
- .isEqualTo("0/300(10.0)");
- assertThat(mProfile.getCpuPowerBracketDescription(scalingPolicies, 1))
- .isEqualTo("0/1000(20.0), 0/2000(30.0), 3/300(25.0)");
- assertThat(mProfile.getCpuPowerBracketDescription(scalingPolicies, 2))
- .isEqualTo("3/1000(35.0), 3/2500(50.0), 3/3000(60.0)");
- assertThat(new int[]{
- mProfile.getCpuPowerBracketForScalingStep(0, 0),
- mProfile.getCpuPowerBracketForScalingStep(0, 1),
- mProfile.getCpuPowerBracketForScalingStep(0, 2),
- mProfile.getCpuPowerBracketForScalingStep(3, 0),
- mProfile.getCpuPowerBracketForScalingStep(3, 1),
- mProfile.getCpuPowerBracketForScalingStep(3, 2),
- mProfile.getCpuPowerBracketForScalingStep(3, 3),
- }).isEqualTo(new int[]{0, 1, 1, 1, 2, 2, 2});
- }
-
- @Test
- public void powerBrackets_moreBracketsThanStates() {
- mProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
- mProfile.initCpuPowerBrackets(8);
-
- assertThat(mProfile.getCpuPowerBracketCount()).isEqualTo(7);
- assertThat(new int[]{
- mProfile.getCpuPowerBracketForScalingStep(0, 0),
- mProfile.getCpuPowerBracketForScalingStep(0, 1),
- mProfile.getCpuPowerBracketForScalingStep(0, 2),
- mProfile.getCpuPowerBracketForScalingStep(3, 0),
- mProfile.getCpuPowerBracketForScalingStep(3, 1),
- mProfile.getCpuPowerBracketForScalingStep(3, 2),
- mProfile.getCpuPowerBracketForScalingStep(3, 3),
- }).isEqualTo(new int[]{0, 1, 2, 3, 4, 5, 6});
- }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 49606f0..7743ad5 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -1763,6 +1763,15 @@
return;
}
+ if (container.isFinished()) {
+ return;
+ }
+
+ if (container.isOverlay()) {
+ updateOverlayContainer(wct, container);
+ return;
+ }
+
if (launchPlaceholderIfNecessary(wct, container)) {
// Placeholder was launched, the positions will be updated when the activity is added
// to the secondary container.
@@ -1783,6 +1792,25 @@
updateSplitContainerIfNeeded(splitContainer, wct, null /* splitAttributes */);
}
+
+ @VisibleForTesting
+ // Suppress GuardedBy warning because lint ask to mark this method as
+ // @GuardedBy(mPresenter.mController.mLock), which is mLock itself
+ @SuppressWarnings("GuardedBy")
+ @GuardedBy("mLock")
+ void updateOverlayContainer(@NonNull WindowContainerTransaction wct,
+ @NonNull TaskFragmentContainer container) {
+ final TaskContainer taskContainer = container.getTaskContainer();
+ // Dismiss the overlay container if it's the only container in the task and there's no
+ // direct activity in the parent task.
+ if (taskContainer.getTaskFragmentContainers().size() == 1
+ && !taskContainer.hasDirectActivity()) {
+ container.finish(false /* shouldFinishDependent */, mPresenter, wct, this);
+ }
+
+ // TODO(b/295805054): Add the logic to update overlay container
+ }
+
/**
* Updates {@link SplitContainer} with the given {@link SplitAttributes} if the
* {@link SplitContainer} is the top most and not finished. If passed {@link SplitAttributes}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 9e53380..eeb3ccf 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -75,6 +75,8 @@
private boolean mIsVisible;
+ private boolean mHasDirectActivity;
+
/**
* TaskFragments that the organizer has requested to be closed. They should be removed when
* the organizer receives
@@ -102,8 +104,9 @@
mConfiguration = taskProperties.getConfiguration();
mDisplayId = taskProperties.getDisplayId();
// Note that it is always called when there's a new Activity is started, which implies
- // the host task is visible.
+ // the host task is visible and has an activity in the task.
mIsVisible = true;
+ mHasDirectActivity = true;
}
int getTaskId() {
@@ -118,6 +121,10 @@
return mIsVisible;
}
+ boolean hasDirectActivity() {
+ return mHasDirectActivity;
+ }
+
@NonNull
TaskProperties getTaskProperties() {
return new TaskProperties(mDisplayId, mConfiguration);
@@ -127,6 +134,7 @@
mConfiguration.setTo(info.getConfiguration());
mDisplayId = info.getDisplayId();
mIsVisible = info.isVisible();
+ mHasDirectActivity = info.hasDirectActivity();
}
/**
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 405f0b2..e74d5fb 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -58,6 +58,7 @@
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentParentInfo;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
@@ -413,6 +414,33 @@
.isEqualTo(overlayContainer);
}
+ @Test
+ public void testUpdateContainer_dontInvokeUpdateOverlayForNonOverlayContainer() {
+ TaskFragmentContainer taskFragmentContainer = createMockTaskFragmentContainer(mActivity);
+
+ mSplitController.updateContainer(mTransaction, taskFragmentContainer);
+ verify(mSplitController, never()).updateOverlayContainer(any(), any());
+ }
+
+ @Test
+ public void testUpdateOverlayContainer_dismissOverlayIfNeeded() {
+ TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test");
+
+ mSplitController.updateOverlayContainer(mTransaction, overlayContainer);
+
+ final TaskContainer taskContainer = overlayContainer.getTaskContainer();
+ assertThat(taskContainer.getTaskFragmentContainers()).containsExactly(overlayContainer);
+
+ taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(Configuration.EMPTY,
+ DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
+
+ mSplitController.updateOverlayContainer(mTransaction, overlayContainer);
+
+ assertWithMessage("The overlay must be dismissed since there's no activity"
+ + " in the task and other taskFragment.")
+ .that(taskContainer.getTaskFragmentContainers()).isEmpty();
+ }
+
/**
* A simplified version of {@link SplitController.ActivityStartMonitor
* #createOrUpdateOverlayTaskFragmentIfNeeded}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index 96839c5..02031a6 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -1139,7 +1139,7 @@
public void testOnTransactionReady_taskFragmentParentInfoChanged() {
final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(Configuration.EMPTY,
- DEFAULT_DISPLAY, true);
+ DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */);
transaction.addChange(new TaskFragmentTransaction.Change(
TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED)
.setTaskId(TASK_ID)
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
index 2188996..e3f5169 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -79,14 +79,14 @@
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */));
+ DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
assertEquals(WINDOWING_MODE_MULTI_WINDOW,
taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */));
+ DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
assertEquals(WINDOWING_MODE_FREEFORM,
taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
@@ -106,13 +106,13 @@
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */));
+ DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
assertFalse(taskContainer.isInPictureInPicture());
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
- DEFAULT_DISPLAY, true /* visible */));
+ DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */));
assertTrue(taskContainer.isInPictureInPicture());
}
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
index fa56516..c525a29 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
@@ -35,8 +35,8 @@
<ImageView
android:id="@+id/application_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
+ android:layout_width="@dimen/desktop_mode_caption_icon_radius"
+ android:layout_height="@dimen/desktop_mode_caption_icon_radius"
android:layout_gravity="center_vertical"
android:contentDescription="@string/app_icon_text" />
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
index 87e0b28..c6f85a0 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
@@ -34,10 +34,10 @@
<ImageView
android:id="@+id/application_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_marginStart="14dp"
- android:layout_marginEnd="14dp"
+ android:layout_width="@dimen/desktop_mode_caption_icon_radius"
+ android:layout_height="@dimen/desktop_mode_caption_icon_radius"
+ android:layout_marginStart="12dp"
+ android:layout_marginEnd="12dp"
android:contentDescription="@string/app_icon_text"/>
<TextView
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 1f6f7ae..c4be384 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -454,6 +454,9 @@
<!-- The radius of the caption menu corners. -->
<dimen name="desktop_mode_handle_menu_corner_radius">26dp</dimen>
+ <!-- The radius of the caption menu icon. -->
+ <dimen name="desktop_mode_caption_icon_radius">28dp</dimen>
+
<!-- The radius of the caption menu shadow. -->
<dimen name="desktop_mode_handle_menu_shadow_radius">2dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index ac5ba51e..3660fa2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -57,6 +57,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
+import android.view.ViewPropertyAnimator;
import android.view.ViewTreeObserver;
import android.view.WindowManagerPolicyConstants;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -212,7 +213,8 @@
private ExpandedViewAnimationController mExpandedViewAnimationController;
private View mScrim;
- private boolean mScrimAnimating;
+ @Nullable
+ private ViewPropertyAnimator mScrimAnimation;
private View mManageMenuScrim;
private FrameLayout mExpandedViewContainer;
@@ -748,8 +750,8 @@
float collapsed = -Math.min(dy, 0);
mExpandedViewAnimationController.updateDrag((int) collapsed);
- // Update scrim
- if (!mScrimAnimating) {
+ // Update scrim if it's not animating already
+ if (mScrimAnimation == null) {
mScrim.setAlpha(getScrimAlphaForDrag(collapsed));
}
}
@@ -768,8 +770,8 @@
} else {
mExpandedViewAnimationController.animateBackToExpanded();
- // Update scrim
- if (!mScrimAnimating) {
+ // Update scrim if it's not animating already
+ if (mScrimAnimation == null) {
showScrim(true, null /* runnable */);
}
}
@@ -2237,26 +2239,27 @@
private void showScrim(boolean show, Runnable after) {
AnimatorListenerAdapter listener = new AnimatorListenerAdapter() {
@Override
- public void onAnimationStart(Animator animation) {
- mScrimAnimating = true;
- }
-
- @Override
public void onAnimationEnd(Animator animation) {
- mScrimAnimating = false;
+ mScrimAnimation = null;
if (after != null) {
after.run();
}
}
};
+ if (mScrimAnimation != null) {
+ // Cancel scrim animation if it animates
+ mScrimAnimation.cancel();
+ }
if (show) {
- mScrim.animate()
+ mScrimAnimation = mScrim.animate();
+ mScrimAnimation
.setInterpolator(ALPHA_IN)
.alpha(BUBBLE_EXPANDED_SCRIM_ALPHA)
.setListener(listener)
.start();
} else {
- mScrim.animate()
+ mScrimAnimation = mScrim.animate();
+ mScrimAnimation
.alpha(0f)
.setInterpolator(ALPHA_OUT)
.setListener(listener)
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 de03f58..9cd318f 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
@@ -25,6 +25,7 @@
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED;
import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE;
@@ -515,7 +516,7 @@
}
if (backgroundColorForTransition != 0) {
- addBackgroundColorOnTDA(info, backgroundColorForTransition, startTransaction,
+ addBackgroundColor(info, backgroundColorForTransition, startTransaction,
finishTransaction);
}
@@ -546,7 +547,7 @@
return true;
}
- private void addBackgroundColorOnTDA(@NonNull TransitionInfo info,
+ private void addBackgroundColor(@NonNull TransitionInfo info,
@ColorInt int color, @NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction) {
final Color bgColor = Color.valueOf(color);
@@ -558,9 +559,19 @@
.setName("animation-background")
.setCallsite("DefaultTransitionHandler")
.setColorLayer();
-
- mRootTDAOrganizer.attachToDisplayArea(displayId, colorLayerBuilder);
final SurfaceControl backgroundSurface = colorLayerBuilder.build();
+
+ // Attaching the background surface to the transition root could unexpectedly make it
+ // cover one of the split root tasks. To avoid this, put the background surface just
+ // above the display area when split is on.
+ final boolean isSplitTaskInvolved =
+ info.getChanges().stream().anyMatch(c-> c.getTaskInfo() != null
+ && c.getTaskInfo().getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW);
+ if (isSplitTaskInvolved) {
+ mRootTDAOrganizer.attachToDisplayArea(displayId, colorLayerBuilder);
+ } else {
+ startTransaction.reparent(backgroundSurface, info.getRootLeash());
+ }
startTransaction.setColor(backgroundSurface, colorArray)
.setLayer(backgroundSurface, -1)
.show(backgroundSurface);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index ca2c3b4..293b660 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -146,13 +146,13 @@
});
}
};
+ // If the remote is actually in the same process, then make a copy of parameters since
+ // remote impls assume that they have to clean-up native references.
+ final SurfaceControl.Transaction remoteStartT =
+ copyIfLocal(startTransaction, remote.getRemoteTransition());
+ final TransitionInfo remoteInfo =
+ remoteStartT == startTransaction ? info : info.localRemoteCopy();
try {
- // If the remote is actually in the same process, then make a copy of parameters since
- // remote impls assume that they have to clean-up native references.
- final SurfaceControl.Transaction remoteStartT =
- copyIfLocal(startTransaction, remote.getRemoteTransition());
- final TransitionInfo remoteInfo =
- remoteStartT == startTransaction ? info : info.localRemoteCopy();
handleDeath(remote.asBinder(), finishCallback);
remote.getRemoteTransition().startAnimation(transition, remoteInfo, remoteStartT, cb);
// assume that remote will apply the start transaction.
@@ -160,6 +160,10 @@
Transitions.setRunningRemoteTransitionDelegate(remote.getAppThread());
} catch (RemoteException e) {
Log.e(Transitions.TAG, "Error running remote transition.", e);
+ if (remoteStartT != startTransaction) {
+ remoteStartT.close();
+ }
+ startTransaction.apply();
unhandleDeath(remote.asBinder(), finishCallback);
mRequestedRemotes.remove(transition);
mMainExecutor.execute(() -> finishCallback.onTransitionFinished(null /* wct */));
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 3aed9eb..a976584 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
@@ -20,6 +20,8 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.windowingModeToString;
+import static com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT;
+
import android.app.ActivityManager;
import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
@@ -27,6 +29,7 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -44,6 +47,7 @@
import android.window.WindowContainerTransaction;
import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.R;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
@@ -93,7 +97,8 @@
private ResizeVeil mResizeVeil;
- private Drawable mAppIcon;
+ private Drawable mAppIconDrawable;
+ private Bitmap mAppIconBitmap;
private CharSequence mAppName;
private ExclusionRegionListener mExclusionRegionListener;
@@ -255,7 +260,7 @@
mOnCaptionButtonClickListener,
mOnCaptionLongClickListener,
mAppName,
- mAppIcon
+ mAppIconBitmap
);
} else {
throw new IllegalArgumentException("Unexpected layout resource id");
@@ -362,10 +367,15 @@
String packageName = mTaskInfo.realActivity.getPackageName();
PackageManager pm = mContext.getApplicationContext().getPackageManager();
try {
- IconProvider provider = new IconProvider(mContext);
- mAppIcon = provider.getIcon(pm.getActivityInfo(mTaskInfo.baseActivity,
+ final IconProvider provider = new IconProvider(mContext);
+ mAppIconDrawable = provider.getIcon(pm.getActivityInfo(mTaskInfo.baseActivity,
PackageManager.ComponentInfoFlags.of(0)));
- ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName,
+ 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 = pm.getApplicationInfo(packageName,
PackageManager.ApplicationInfoFlags.of(0));
mAppName = pm.getApplicationLabel(applicationInfo);
} catch (PackageManager.NameNotFoundException e) {
@@ -386,7 +396,7 @@
* until a resize event calls showResizeVeil below.
*/
void createResizeVeil() {
- mResizeVeil = new ResizeVeil(mContext, mAppIcon, mTaskInfo,
+ mResizeVeil = new ResizeVeil(mContext, mAppIconDrawable, mTaskInfo,
mSurfaceControlBuilderSupplier, mDisplay, mSurfaceControlTransactionSupplier);
}
@@ -459,7 +469,7 @@
*/
void createHandleMenu() {
mHandleMenu = new HandleMenu.Builder(this)
- .setAppIcon(mAppIcon)
+ .setAppIcon(mAppIconBitmap)
.setAppName(mAppName)
.setOnClickListener(mOnCaptionButtonClickListener)
.setOnTouchListener(mOnCaptionTouchListener)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
index 6391518..1941d66 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
@@ -29,9 +29,9 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PointF;
-import android.graphics.drawable.Drawable;
import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.View;
@@ -58,7 +58,7 @@
private WindowDecoration.AdditionalWindow mHandleMenuWindow;
private final PointF mHandleMenuPosition = new PointF();
private final boolean mShouldShowWindowingPill;
- private final Drawable mAppIcon;
+ private final Bitmap mAppIconBitmap;
private final CharSequence mAppName;
private final View.OnClickListener mOnClickListener;
private final View.OnTouchListener mOnTouchListener;
@@ -76,7 +76,7 @@
HandleMenu(WindowDecoration parentDecor, int layoutResId, int captionX, int captionY,
View.OnClickListener onClickListener, View.OnTouchListener onTouchListener,
- Drawable appIcon, CharSequence appName, boolean shouldShowWindowingPill,
+ Bitmap appIcon, CharSequence appName, boolean shouldShowWindowingPill,
int captionHeight) {
mParentDecor = parentDecor;
mContext = mParentDecor.mDecorWindowContext;
@@ -86,7 +86,7 @@
mCaptionY = captionY;
mOnClickListener = onClickListener;
mOnTouchListener = onTouchListener;
- mAppIcon = appIcon;
+ mAppIconBitmap = appIcon;
mAppName = appName;
mShouldShowWindowingPill = shouldShowWindowingPill;
mCaptionHeight = captionHeight;
@@ -150,7 +150,7 @@
final ImageView appIcon = handleMenu.findViewById(R.id.application_icon);
final TextView appName = handleMenu.findViewById(R.id.application_name);
collapseBtn.setOnClickListener(mOnClickListener);
- appIcon.setImageDrawable(mAppIcon);
+ appIcon.setImageBitmap(mAppIconBitmap);
appName.setText(mAppName);
}
@@ -335,7 +335,7 @@
static final class Builder {
private final WindowDecoration mParent;
private CharSequence mName;
- private Drawable mAppIcon;
+ private Bitmap mAppIcon;
private View.OnClickListener mOnClickListener;
private View.OnTouchListener mOnTouchListener;
private int mLayoutId;
@@ -354,7 +354,7 @@
return this;
}
- Builder setAppIcon(@Nullable Drawable appIcon) {
+ Builder setAppIcon(@Nullable Bitmap appIcon) {
mAppIcon = appIcon;
return this;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
index 400dec4..d64312a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
@@ -2,7 +2,7 @@
import android.app.ActivityManager.RunningTaskInfo
import android.content.res.ColorStateList
-import android.graphics.drawable.Drawable
+import android.graphics.Bitmap
import android.graphics.drawable.GradientDrawable
import android.view.View
import android.view.View.OnLongClickListener
@@ -22,7 +22,7 @@
onCaptionButtonClickListener: View.OnClickListener,
onLongClickListener: OnLongClickListener,
appName: CharSequence,
- appIcon: Drawable
+ appIconBitmap: Bitmap
) : DesktopModeWindowDecorationViewHolder(rootView) {
private val captionView: View = rootView.requireViewById(R.id.desktop_mode_caption)
@@ -44,7 +44,7 @@
maximizeWindowButton.onLongClickListener = onLongClickListener
closeWindowButton.setOnTouchListener(onCaptionTouchListener)
appNameTextView.text = appName
- appIconImageView.setImageDrawable(appIcon)
+ appIconImageView.setImageBitmap(appIconBitmap)
}
override fun bindData(taskInfo: RunningTaskInfo) {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 5b88079..9ad5c3e 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -20,6 +20,8 @@
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
import static android.content.Context.DEVICE_ID_DEFAULT;
+import static com.android.media.audio.flags.Flags.autoPublicVolumeApiHardening;
+
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
@@ -1060,8 +1062,17 @@
* @see #isVolumeFixed()
*/
public void adjustVolume(int direction, @PublicVolumeFlags int flags) {
- MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
- helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags);
+ if (autoPublicVolumeApiHardening()) {
+ final IAudioService service = getService();
+ try {
+ service.adjustVolume(direction, flags);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
+ helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags);
+ }
}
/**
@@ -1090,8 +1101,17 @@
*/
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType,
@PublicVolumeFlags int flags) {
- MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
- helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
+ if (autoPublicVolumeApiHardening()) {
+ final IAudioService service = getService();
+ try {
+ service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
+ helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
+ }
}
/** @hide */
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 0e7718b..8584dbc 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -498,6 +498,10 @@
in String packageName, int uid, int pid, in UserHandle userHandle,
int targetSdkVersion);
+ oneway void adjustVolume(int direction, int flags);
+
+ oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags);
+
boolean isMusicActive(in boolean remotely);
int getDeviceMaskForStream(in int streamType);
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index c9cfa67..386534b 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -34,3 +34,10 @@
description: "Fallbacks to the default handling for volume adjustment when media session has fixed volume handling and its app is in the foreground and setting a media controller."
bug: "293743975"
}
+
+flag {
+ namespace: "media_solutions"
+ name: "enable_waiting_state_for_system_session_creation_request"
+ description: "Introduces a waiting state for the session creation request and prevents it from early failing when the selectedRoute from the bluetooth stack doesn't match the pending request route id."
+ bug: "307723189"
+}
diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl
index 10c880d..24efbd1 100644
--- a/media/java/android/media/projection/IMediaProjectionManager.aidl
+++ b/media/java/android/media/projection/IMediaProjectionManager.aidl
@@ -158,26 +158,6 @@
in @nullable IMediaProjection projection);
/**
- * Notifies system server that we are handling a particular state during the consent flow.
- *
- * <p>Only used for emitting atoms.
- *
- * @param hostUid The uid of the process requesting consent to capture, may be an app or
- * SystemUI.
- * @param state The state that SystemUI is handling during the consent flow.
- * Must be a valid
- * state defined in the MediaProjectionState enum.
- * @param sessionCreationSource Only set if the state is MEDIA_PROJECTION_STATE_INITIATED.
- * Indicates the entry point for requesting the permission. Must be
- * a valid state defined
- * in the SessionCreationSource enum.
- */
- @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
- @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
- + ".permission.MANAGE_MEDIA_PROJECTION)")
- oneway void notifyPermissionRequestStateChange(int hostUid, int state, int sessionCreationSource);
-
- /**
* Notifies system server that the permission request was initiated.
*
* <p>Only used for emitting atoms.
@@ -208,6 +188,19 @@
oneway void notifyPermissionRequestDisplayed(int hostUid);
/**
+ * Notifies system server that the permission request was cancelled.
+ *
+ * <p>Only used for emitting atoms.
+ *
+ * @param hostUid The uid of the process requesting consent to capture, may be an app or
+ * SystemUI.
+ */
+ @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
+ @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+ + ".permission.MANAGE_MEDIA_PROJECTION)")
+ oneway void notifyPermissionRequestCancelled(int hostUid);
+
+ /**
* Notifies system server that the app selector was displayed.
*
* <p>Only used for emitting atoms.
diff --git a/packages/CredentialManager/shared/Android.bp b/packages/CredentialManager/shared/Android.bp
index 0d4af2a..47ca944 100644
--- a/packages/CredentialManager/shared/Android.bp
+++ b/packages/CredentialManager/shared/Android.bp
@@ -16,5 +16,6 @@
"androidx.core_core-ktx",
"androidx.credentials_credentials",
"guava",
+ "hilt_android",
],
}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/PasswordRepository.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/PasswordRepository.kt
index 1cce3ba..5738fee 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/PasswordRepository.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/PasswordRepository.kt
@@ -25,8 +25,11 @@
import com.android.credentialmanager.TAG
import com.android.credentialmanager.model.Password
import com.android.credentialmanager.model.Request
+import javax.inject.Inject
+import javax.inject.Singleton
-class PasswordRepository {
+@Singleton
+class PasswordRepository @Inject constructor() {
suspend fun selectPassword(
password: Password,
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt
index 5ab5ab9..1973fc1 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt
@@ -16,17 +16,20 @@
package com.android.credentialmanager.repository
-import android.app.Application
import android.content.Intent
+import android.content.pm.PackageManager
import android.util.Log
import com.android.credentialmanager.TAG
import com.android.credentialmanager.model.Request
import com.android.credentialmanager.parse
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
+import javax.inject.Inject
+import javax.inject.Singleton
-class RequestRepository(
- private val application: Application,
+@Singleton
+class RequestRepository @Inject constructor(
+ private val packageManager: PackageManager,
) {
private val _requests = MutableStateFlow<Request?>(null)
@@ -34,7 +37,7 @@
suspend fun processRequest(intent: Intent, previousIntent: Intent? = null) {
val request = intent.parse(
- packageManager = application.packageManager,
+ packageManager = packageManager,
previousIntent = previousIntent
)
diff --git a/packages/CredentialManager/wear/Android.bp b/packages/CredentialManager/wear/Android.bp
index e5f5cc2..c883b1f2 100644
--- a/packages/CredentialManager/wear/Android.bp
+++ b/packages/CredentialManager/wear/Android.bp
@@ -22,6 +22,7 @@
static_libs: [
"CredentialManagerShared",
+ "hilt_android",
"Horologist",
"PlatformComposeCore",
"androidx.activity_activity-compose",
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 273d0b1..0a63cb7 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -29,13 +29,13 @@
import com.android.credentialmanager.ui.screens.single.password.SinglePasswordScreen
import com.google.android.horologist.annotations.ExperimentalHorologistApi
import com.google.android.horologist.compose.layout.belowTimeTextPreview
+import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
-class CredentialSelectorActivity : ComponentActivity() {
+@AndroidEntryPoint(ComponentActivity::class)
+class CredentialSelectorActivity : Hilt_CredentialSelectorActivity() {
- private val viewModel: CredentialSelectorViewModel by viewModels {
- CredentialSelectorViewModel.Factory
- }
+ private val viewModel: CredentialSelectorViewModel by viewModels()
@OptIn(ExperimentalHorologistApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
index e8e4033..6bd166e 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
@@ -17,18 +17,7 @@
package com.android.credentialmanager
import android.app.Application
-import com.android.credentialmanager.di.inject
-import com.android.credentialmanager.repository.PasswordRepository
-import com.android.credentialmanager.repository.RequestRepository
+import dagger.hilt.android.HiltAndroidApp
-class CredentialSelectorApp : Application() {
-
- lateinit var requestRepository: RequestRepository
- lateinit var passwordRepository: PasswordRepository
-
- override fun onCreate() {
- super.onCreate()
-
- inject()
- }
-}
\ No newline at end of file
+@HiltAndroidApp(Application::class)
+class CredentialSelectorApp : Hilt_CredentialSelectorApp()
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index d557dc0..435cd37 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -18,20 +18,20 @@
import android.content.Intent
import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.viewModelScope
-import androidx.lifecycle.viewmodel.CreationExtras
import com.android.credentialmanager.model.Request
import com.android.credentialmanager.repository.RequestRepository
import com.android.credentialmanager.ui.mappers.toGet
+import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
+import javax.inject.Inject
-class CredentialSelectorViewModel(
+@HiltViewModel
+class CredentialSelectorViewModel @Inject constructor(
private val requestRepository: RequestRepository,
) : ViewModel() {
@@ -56,22 +56,6 @@
requestRepository.processRequest(intent = intent, previousIntent = previousIntent)
}
}
-
- companion object {
- val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
- @Suppress("UNCHECKED_CAST")
- override fun <T : ViewModel> create(
- modelClass: Class<T>,
- extras: CreationExtras
- ): T {
- val application = checkNotNull(extras[APPLICATION_KEY])
-
- return CredentialSelectorViewModel(
- requestRepository = (application as CredentialSelectorApp).requestRepository,
- ) as T
- }
- }
- }
}
sealed class CredentialSelectorUiState {
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt
new file mode 100644
index 0000000..cb1a4a1
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/AppModule.kt
@@ -0,0 +1,18 @@
+package com.android.credentialmanager.di
+
+import android.content.Context
+import android.content.pm.PackageManager
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.qualifiers.ApplicationContext
+import dagger.hilt.components.SingletonComponent
+@Module
+@InstallIn(SingletonComponent::class)
+internal object AppModule {
+ @Provides
+ @JvmStatic
+ fun providePackageManager(@ApplicationContext context: Context): PackageManager =
+ context.packageManager
+}
+
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt
deleted file mode 100644
index 1e8f83d..0000000
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.android.credentialmanager.di
-
-import android.app.Application
-import com.android.credentialmanager.CredentialSelectorApp
-import com.android.credentialmanager.repository.PasswordRepository
-import com.android.credentialmanager.repository.RequestRepository
-
-// TODO b/301601582 add Hilt for dependency injection
-
-fun CredentialSelectorApp.inject() {
- requestRepository = requestRepository(application = this)
- passwordRepository = passwordRepository()
-}
-
-private fun requestRepository(
- application: Application,
-): RequestRepository = RequestRepository(
- application = application,
-)
-
-private fun passwordRepository(): PasswordRepository = PasswordRepository()
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
index c87cfd3..81a0672 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
@@ -47,8 +47,7 @@
columnState: ScalingLazyColumnState,
onCloseApp: () -> Unit,
modifier: Modifier = Modifier,
- viewModel: SinglePasswordScreenViewModel =
- viewModel(factory = SinglePasswordScreenViewModel.Factory),
+ viewModel: SinglePasswordScreenViewModel = viewModel(),
) {
viewModel.initialize()
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
index 3167e67..43514a0 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
@@ -21,11 +21,7 @@
import androidx.activity.result.IntentSenderRequest
import androidx.annotation.MainThread
import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.viewModelScope
-import androidx.lifecycle.viewmodel.CreationExtras
-import com.android.credentialmanager.CredentialSelectorApp
import com.android.credentialmanager.TAG
import com.android.credentialmanager.ktx.getIntentSenderRequest
import com.android.credentialmanager.model.Password
@@ -33,12 +29,15 @@
import com.android.credentialmanager.repository.PasswordRepository
import com.android.credentialmanager.repository.RequestRepository
import com.android.credentialmanager.ui.model.PasswordUiModel
+import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
+import javax.inject.Inject
-class SinglePasswordScreenViewModel(
+@HiltViewModel
+class SinglePasswordScreenViewModel @Inject constructor(
private val requestRepository: RequestRepository,
private val passwordRepository: PasswordRepository,
) : ViewModel() {
@@ -105,23 +104,6 @@
_uiState.value = SinglePasswordScreenUiState.Completed
}
}
-
- companion object {
- val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
- @Suppress("UNCHECKED_CAST")
- override fun <T : ViewModel> create(
- modelClass: Class<T>,
- extras: CreationExtras
- ): T {
- val application = checkNotNull(extras[APPLICATION_KEY])
-
- return SinglePasswordScreenViewModel(
- requestRepository = (application as CredentialSelectorApp).requestRepository,
- passwordRepository = application.passwordRepository,
- ) as T
- }
- }
- }
}
sealed class SinglePasswordScreenUiState {
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
index 155cfbb..b633337 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
@@ -173,13 +173,12 @@
return mCoordinatorLayout;
}
- /** Sets the title on the collapsing layout, delegating to host if needed. */
+ /** Sets the title on the collapsing layout and delegates to host. */
public void setTitle(CharSequence title) {
if (mCollapsingToolbarLayout != null) {
mCollapsingToolbarLayout.setTitle(title);
- } else {
- mHostCallback.setOuterTitle(title);
}
+ mHostCallback.setOuterTitle(title);
}
/** Returns an instance of collapsing toolbar. */
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 9687674..6eaabbb 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1098,19 +1098,6 @@
<!-- Used to let users know that they have more than some amount of battery life remaining. ex: more than 1 day remaining [CHAR LIMIT = 40] -->
<string name="power_remaining_only_more_than_subtext">More than <xliff:g id="time_remaining">%1$s</xliff:g> left</string>
- <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
- <string name="power_remaining_duration_only_shutdown_imminent" product="default">Phone may shut down soon</string>
- <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
- <string name="power_remaining_duration_only_shutdown_imminent" product="tablet">Tablet may shut down soon</string>
- <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
- <string name="power_remaining_duration_only_shutdown_imminent" product="device">Device may shut down soon</string>
- <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
- <string name="power_remaining_duration_shutdown_imminent" product="default">Phone may shut down soon (<xliff:g id="level">%1$s</xliff:g>)</string>
- <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
- <string name="power_remaining_duration_shutdown_imminent" product="tablet">Tablet may shut down soon (<xliff:g id="level">%1$s</xliff:g>)</string>
- <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
- <string name="power_remaining_duration_shutdown_imminent" product="device">Device may shut down soon (<xliff:g id="level">%1$s</xliff:g>)</string>
-
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging -->
<string name="power_charging"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="state">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery charging -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
index 363e20aa..7fbd35b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
@@ -19,7 +19,7 @@
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
-import android.widget.Switch;
+import android.widget.CompoundButton;
import androidx.annotation.Keep;
import androidx.annotation.Nullable;
@@ -35,7 +35,7 @@
*/
public class PrimarySwitchPreference extends RestrictedPreference {
- private Switch mSwitch;
+ private CompoundButton mSwitch;
private boolean mChecked;
private boolean mCheckedSet;
private boolean mEnableSwitch = true;
@@ -65,7 +65,7 @@
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
- mSwitch = (Switch) holder.findViewById(R.id.switchWidget);
+ mSwitch = (CompoundButton) holder.findViewById(R.id.switchWidget);
if (mSwitch != null) {
mSwitch.setOnClickListener(v -> {
if (mSwitch != null && !mSwitch.isEnabled()) {
@@ -153,7 +153,7 @@
setSwitchEnabled(admin == null);
}
- public Switch getSwitch() {
+ public CompoundButton getSwitch() {
return mSwitch;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index 758f090..60321eb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -36,18 +36,17 @@
import android.widget.TextView;
import androidx.annotation.VisibleForTesting;
-import androidx.core.content.res.TypedArrayUtils;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceViewHolder;
-import androidx.preference.SwitchPreference;
+import androidx.preference.SwitchPreferenceCompat;
import com.android.settingslib.utils.BuildCompatUtils;
/**
- * Version of SwitchPreference that can be disabled by a device admin
+ * Version of SwitchPreferenceCompat that can be disabled by a device admin
* using a user restriction.
*/
-public class RestrictedSwitchPreference extends SwitchPreference {
+public class RestrictedSwitchPreference extends SwitchPreferenceCompat {
RestrictedPreferenceHelper mHelper;
AppOpsManager mAppOpsManager;
boolean mUseAdditionalSummary = false;
@@ -93,8 +92,7 @@
}
public RestrictedSwitchPreference(Context context, AttributeSet attrs) {
- this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.switchPreferenceStyle,
- android.R.attr.switchPreferenceStyle));
+ this(context, attrs, androidx.preference.R.attr.switchPreferenceCompatStyle);
}
public RestrictedSwitchPreference(Context context) {
@@ -113,7 +111,7 @@
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
- final View switchView = holder.findViewById(android.R.id.switch_widget);
+ final View switchView = holder.findViewById(androidx.preference.R.id.switchWidget);
if (switchView != null) {
final View rootView = switchView.getRootView();
rootView.setFilterTouchesWhenObscured(true);
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SettingsJankMonitor.kt b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SettingsJankMonitor.kt
index 02d76304..f988837 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SettingsJankMonitor.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SettingsJankMonitor.kt
@@ -37,7 +37,7 @@
const val MONITORED_ANIMATION_DURATION_MS = 300L
/**
- * Detects the jank when click on a SwitchPreference.
+ * Detects the jank when click on a TwoStatePreference.
*
* @param recyclerView the recyclerView contains the preference
* @param preference the clicked preference
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
index 2999c83..1501e27 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
@@ -31,8 +31,8 @@
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
+import android.widget.CompoundButton;
import android.widget.ImageView;
-import android.widget.Switch;
import android.widget.Toast;
import androidx.preference.Preference;
@@ -138,7 +138,7 @@
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
- final Switch switchWidget = getSwitch();
+ final CompoundButton switchWidget = getSwitch();
if (switchWidget != null) {
// Avoid default behavior in {@link PrimarySwitchPreference#onBindViewHolder}.
switchWidget.setOnClickListener(v -> {
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
index 673f243..2272654 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
@@ -44,45 +44,6 @@
private static final long ONE_MIN_MILLIS = TimeUnit.MINUTES.toMillis(1);
/**
- * This method produces the text used in various places throughout the system to describe the
- * remaining battery life of the phone in a consistent manner.
- *
- * @param context
- * @param drainTimeMs The estimated time remaining before the phone dies in milliseconds.
- * @param percentageString An optional percentage of battery remaining string.
- * @param basedOnUsage Whether this estimate is based on usage or simple extrapolation.
- * @return a properly formatted and localized string describing how much time remains
- * before the battery runs out.
- */
- public static String getBatteryRemainingStringFormatted(Context context, long drainTimeMs,
- @Nullable String percentageString, boolean basedOnUsage) {
- if (drainTimeMs > 0) {
- if (drainTimeMs <= SEVEN_MINUTES_MILLIS) {
- // show a imminent shutdown warning if less than 7 minutes remain
- return getShutdownImminentString(context, percentageString);
- } else if (drainTimeMs <= FIFTEEN_MINUTES_MILLIS) {
- // show a less than 15 min remaining warning if appropriate
- CharSequence timeString = StringUtil.formatElapsedTime(context,
- FIFTEEN_MINUTES_MILLIS,
- false /* withSeconds */, false /* collapseTimeUnit */);
- return getUnderFifteenString(context, timeString, percentageString);
- } else if (drainTimeMs >= TWO_DAYS_MILLIS) {
- // just say more than two day if over 48 hours
- return getMoreThanTwoDaysString(context, percentageString);
- } else if (drainTimeMs >= ONE_DAY_MILLIS) {
- // show remaining days & hours if more than a day
- return getMoreThanOneDayString(context, drainTimeMs,
- percentageString, basedOnUsage);
- } else {
- // show the time of day we think you'll run out
- return getRegularTimeRemainingString(context, drainTimeMs,
- percentageString, basedOnUsage);
- }
- }
- return null;
- }
-
- /**
* Method to produce a shortened string describing the remaining battery. Suitable for Quick
* Settings and other areas where space is constrained.
*
@@ -128,14 +89,6 @@
}
}
- private static String getShutdownImminentString(Context context, String percentageString) {
- return TextUtils.isEmpty(percentageString)
- ? context.getString(R.string.power_remaining_duration_only_shutdown_imminent)
- : context.getString(
- R.string.power_remaining_duration_shutdown_imminent,
- percentageString);
- }
-
private static String getUnderFifteenString(Context context, CharSequence timeString,
String percentageString) {
return TextUtils.isEmpty(percentageString)
@@ -268,4 +221,4 @@
return time - remainder + multiple;
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java
index d9cf9f2..debfa49 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java
@@ -23,8 +23,8 @@
import android.content.Context;
import android.view.LayoutInflater;
+import android.widget.CompoundButton;
import android.widget.LinearLayout;
-import android.widget.Switch;
import androidx.preference.Preference.OnPreferenceChangeListener;
import androidx.preference.PreferenceViewHolder;
@@ -62,7 +62,7 @@
@Test
public void setChecked_shouldUpdateButtonCheckedState() {
- final Switch toggle = (Switch) mHolder.findViewById(R.id.switchWidget);
+ final CompoundButton toggle = (CompoundButton) mHolder.findViewById(R.id.switchWidget);
mPreference.onBindViewHolder(mHolder);
mPreference.setChecked(true);
@@ -74,7 +74,7 @@
@Test
public void setSwitchEnabled_shouldUpdateButtonEnabledState() {
- final Switch toggle = (Switch) mHolder.findViewById(R.id.switchWidget);
+ final CompoundButton toggle = (CompoundButton) mHolder.findViewById(R.id.switchWidget);
mPreference.onBindViewHolder(mHolder);
mPreference.setSwitchEnabled(true);
@@ -86,7 +86,7 @@
@Test
public void setSwitchEnabled_shouldUpdateButtonEnabledState_beforeViewBound() {
- final Switch toggle = (Switch) mHolder.findViewById(R.id.switchWidget);
+ final CompoundButton toggle = (CompoundButton) mHolder.findViewById(R.id.switchWidget);
mPreference.setSwitchEnabled(false);
mPreference.onBindViewHolder(mHolder);
@@ -97,7 +97,7 @@
public void clickWidgetView_shouldToggleButton() {
assertThat(mWidgetView).isNotNull();
- final Switch toggle = (Switch) mHolder.findViewById(R.id.switchWidget);
+ final CompoundButton toggle = (CompoundButton) mHolder.findViewById(R.id.switchWidget);
mPreference.onBindViewHolder(mHolder);
toggle.performClick();
@@ -111,7 +111,7 @@
public void clickWidgetView_shouldNotToggleButtonIfDisabled() {
assertThat(mWidgetView).isNotNull();
- final Switch toggle = (Switch) mHolder.findViewById(R.id.switchWidget);
+ final CompoundButton toggle = (CompoundButton) mHolder.findViewById(R.id.switchWidget);
mPreference.onBindViewHolder(mHolder);
toggle.setEnabled(false);
@@ -122,7 +122,7 @@
@Test
public void clickWidgetView_shouldNotifyPreferenceChanged() {
- final Switch toggle = (Switch) mHolder.findViewById(R.id.switchWidget);
+ final CompoundButton toggle = (CompoundButton) mHolder.findViewById(R.id.switchWidget);
final OnPreferenceChangeListener listener = mock(OnPreferenceChangeListener.class);
mPreference.setOnPreferenceChangeListener(listener);
@@ -139,7 +139,7 @@
@Test
public void setDisabledByAdmin_hasEnforcedAdmin_shouldDisableButton() {
- final Switch toggle = (Switch) mHolder.findViewById(R.id.switchWidget);
+ final CompoundButton toggle = (CompoundButton) mHolder.findViewById(R.id.switchWidget);
toggle.setEnabled(true);
mPreference.onBindViewHolder(mHolder);
@@ -149,7 +149,7 @@
@Test
public void setDisabledByAdmin_noEnforcedAdmin_shouldEnableButton() {
- final Switch toggle = (Switch) mHolder.findViewById(R.id.switchWidget);
+ final CompoundButton toggle = (CompoundButton) mHolder.findViewById(R.id.switchWidget);
toggle.setEnabled(false);
mPreference.onBindViewHolder(mHolder);
@@ -159,7 +159,7 @@
@Test
public void onBindViewHolder_toggleButtonShouldHaveContentDescription() {
- final Switch toggle = (Switch) mHolder.findViewById(R.id.switchWidget);
+ final CompoundButton toggle = (CompoundButton) mHolder.findViewById(R.id.switchWidget);
final String label = "TestButton";
mPreference.setTitle(label);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
index ae54206..2e7905f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
@@ -59,155 +59,6 @@
}
@Test
- public void testGetBatteryRemainingStringFormatted_moreThanFifteenMinutes_withPercentage() {
- String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
- SEVENTEEN_MIN_MILLIS,
- TEST_BATTERY_LEVEL_10,
- true /* basedOnUsage */);
- String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
- SEVENTEEN_MIN_MILLIS,
- TEST_BATTERY_LEVEL_10,
- false /* basedOnUsage */);
-
- // We only add special mention for the long string
- // ex: Will last about 1:15 PM based on your usage (10%)
- assertThat(info).containsMatch(Pattern.compile(
- NORMAL_CASE_EXPECTED_PREFIX
- + TIME_OF_DAY_REGEX
- + ENHANCED_SUFFIX
- + PERCENTAGE_REGEX));
- // shortened string should not have extra text
- // ex: Will last about 1:15 PM (10%)
- assertThat(info2).containsMatch(Pattern.compile(
- NORMAL_CASE_EXPECTED_PREFIX
- + TIME_OF_DAY_REGEX
- + PERCENTAGE_REGEX));
- }
-
- @Test
- public void testGetBatteryRemainingStringFormatted_moreThanFifteenMinutes_noPercentage() {
- String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
- SEVENTEEN_MIN_MILLIS,
- null /* percentageString */,
- true /* basedOnUsage */);
- String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
- SEVENTEEN_MIN_MILLIS,
- null /* percentageString */,
- false /* basedOnUsage */);
-
- // We only have % when it is provided
- // ex: Will last about 1:15 PM based on your usage
- assertThat(info).containsMatch(Pattern.compile(
- NORMAL_CASE_EXPECTED_PREFIX
- + TIME_OF_DAY_REGEX
- + ENHANCED_SUFFIX
- + "(" + PERCENTAGE_REGEX + "){0}")); // no percentage
- // shortened string should not have extra text
- // ex: Will last about 1:15 PM
- assertThat(info2).containsMatch(Pattern.compile(
- NORMAL_CASE_EXPECTED_PREFIX
- + TIME_OF_DAY_REGEX
- + "(" + PERCENTAGE_REGEX + "){0}")); // no percentage
- }
-
- @Test
- public void testGetBatteryRemainingStringFormatted_lessThanSevenMinutes_usesCorrectString() {
- String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
- FIVE_MINUTES_MILLIS,
- TEST_BATTERY_LEVEL_10 /* percentageString */,
- true /* basedOnUsage */);
- String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
- FIVE_MINUTES_MILLIS,
- null /* percentageString */,
- true /* basedOnUsage */);
-
- // additional battery percentage in this string
- assertThat(info.contains("may shut down soon (10%)")).isTrue();
- // shortened string should not have percentage
- assertThat(info2.contains("may shut down soon")).isTrue();
- }
-
- @Test
- public void testGetBatteryRemainingStringFormatted_betweenSevenAndFifteenMinutes_usesCorrectString() {
- String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
- TEN_MINUTES_MILLIS,
- null /* percentageString */,
- true /* basedOnUsage */);
- String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
- TEN_MINUTES_MILLIS,
- TEST_BATTERY_LEVEL_10 /* percentageString */,
- true /* basedOnUsage */);
-
- // shortened string should not have percentage
- assertThat(info).isEqualTo("Less than 15 min left");
- // Add percentage to string when provided
- assertThat(info2).isEqualTo("Less than 15 min left (10%)");
- }
-
- @Test
- public void testGetBatteryRemainingStringFormatted_betweenOneAndTwoDays_usesCorrectString() {
- String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
- THIRTY_HOURS_MILLIS,
- null /* percentageString */,
- true /* basedOnUsage */);
- String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
- THIRTY_HOURS_MILLIS,
- TEST_BATTERY_LEVEL_10 /* percentageString */,
- false /* basedOnUsage */);
- String info3 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
- THIRTY_HOURS_MILLIS + TEN_MINUTES_MILLIS,
- null /* percentageString */,
- false /* basedOnUsage */);
-
- // We only add special mention for the long string
- assertThat(info).isEqualTo("About 1 day, 6 hr left based on your usage");
- // shortened string should not have extra text
- assertThat(info2).isEqualTo("About 1 day, 6 hr left (10%)");
- // present 2 time unit at most
- assertThat(info3).isEqualTo("About 1 day, 6 hr left");
- }
-
- @Test
- public void testGetBatteryRemainingStringFormatted_lessThanOneDay_usesCorrectString() {
- String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
- TEN_HOURS_MILLIS,
- null /* percentageString */,
- true /* basedOnUsage */);
- String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
- TEN_HOURS_MILLIS,
- TEST_BATTERY_LEVEL_10 /* percentageString */,
- false /* basedOnUsage */);
- String info3 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
- TEN_HOURS_MILLIS + TEN_MINUTES_MILLIS + TEN_SEC_MILLIS,
- null /* percentageString */,
- false /* basedOnUsage */);
-
- // We only add special mention for the long string
- assertThat(info).isEqualTo("About 10 hr left based on your usage");
- // shortened string should not have extra text
- assertThat(info2).isEqualTo("About 10 hr left (10%)");
- // present 2 time unit at most
- assertThat(info3).isEqualTo("About 10 hr, 10 min left");
- }
-
- @Test
- public void testGetBatteryRemainingStringFormatted_moreThanTwoDays_usesCorrectString() {
- String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
- THREE_DAYS_MILLIS,
- null /* percentageString */,
- true /* basedOnUsage */);
- String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
- THREE_DAYS_MILLIS,
- TEST_BATTERY_LEVEL_10 /* percentageString */,
- true /* basedOnUsage */);
-
- // shortened string should not have percentage
- assertThat(info).isEqualTo("More than 2 days left");
- // Add percentage to string when provided
- assertThat(info2).isEqualTo("More than 2 days left (10%)");
- }
-
- @Test
public void getBatteryTipStringFormatted_moreThanOneDay_usesCorrectString() {
String info = PowerUtil.getBatteryTipStringFormatted(mContext,
THREE_DAYS_MILLIS);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
index 02ec486..cd35f67 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
@@ -45,7 +45,8 @@
private static final boolean DEBUG = false;
- private final Object mLock;
+ // This lock is not the same lock used in SettingsProvider and SettingsState
+ private final Object mLock = new Object();
// Key -> backingStore mapping
@GuardedBy("mLock")
@@ -74,8 +75,7 @@
private final int mMaxNumBackingStore;
- GenerationRegistry(Object lock, int maxNumUsers) {
- mLock = lock;
+ GenerationRegistry(int maxNumUsers) {
// Add some buffer to maxNumUsers to accommodate corner cases when the actual number of
// users in the system exceeds the limit
maxNumUsers = maxNumUsers + 2;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 95d7039..5acc1ca 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2884,7 +2884,7 @@
public SettingsRegistry() {
mHandler = new MyHandler(getContext().getMainLooper());
- mGenerationRegistry = new GenerationRegistry(mLock, UserManager.getMaxSupportedUsers());
+ mGenerationRegistry = new GenerationRegistry(UserManager.getMaxSupportedUsers());
mBackupManager = new BackupManager(getContext());
}
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
index 12865f4..8029785 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java
@@ -36,7 +36,7 @@
public class GenerationRegistryTest {
@Test
public void testGenerationsWithRegularSetting() throws IOException {
- final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 2);
+ final GenerationRegistry generationRegistry = new GenerationRegistry(2);
final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0);
final String testSecureSetting = "test_secure_setting";
Bundle b = new Bundle();
@@ -93,7 +93,7 @@
@Test
public void testGenerationsWithConfigSetting() throws IOException {
- final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 1);
+ final GenerationRegistry generationRegistry = new GenerationRegistry(1);
final String prefix = "test_namespace/";
final int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
@@ -110,7 +110,7 @@
@Test
public void testMaxNumBackingStores() throws IOException {
- final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 2);
+ final GenerationRegistry generationRegistry = new GenerationRegistry(2);
final String testSecureSetting = "test_secure_setting";
Bundle b = new Bundle();
for (int i = 0; i < generationRegistry.getMaxNumBackingStores(); i++) {
@@ -133,7 +133,7 @@
@Test
public void testMaxSizeBackingStore() throws IOException {
- final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 1);
+ final GenerationRegistry generationRegistry = new GenerationRegistry(1);
final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0);
final String testSecureSetting = "test_secure_setting";
Bundle b = new Bundle();
@@ -153,7 +153,7 @@
@Test
public void testUnsetSettings() throws IOException {
- final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 1);
+ final GenerationRegistry generationRegistry = new GenerationRegistry(1);
final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0);
final String testSecureSetting = "test_secure_setting";
Bundle b = new Bundle();
@@ -172,7 +172,7 @@
@Test
public void testGlobalSettings() throws IOException {
- final GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 2);
+ final GenerationRegistry generationRegistry = new GenerationRegistry(2);
final int globalKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_GLOBAL, 0);
final String testGlobalSetting = "test_global_setting";
final Bundle b = new Bundle();
@@ -190,11 +190,11 @@
@Test
public void testNumberOfBackingStores() {
- GenerationRegistry generationRegistry = new GenerationRegistry(new Object(), 0);
+ GenerationRegistry generationRegistry = new GenerationRegistry(0);
// Test that the capacity of the backing stores is always valid
assertThat(generationRegistry.getMaxNumBackingStores()).isEqualTo(
GenerationRegistry.MIN_NUM_BACKING_STORE);
- generationRegistry = new GenerationRegistry(new Object(), 100);
+ generationRegistry = new GenerationRegistry(100);
// Test that the capacity of the backing stores is always valid
assertThat(generationRegistry.getMaxNumBackingStores()).isEqualTo(
GenerationRegistry.MAX_NUM_BACKING_STORE);
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
index 17c74ba..0e7694e 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
@@ -315,7 +315,8 @@
}
}
- override fun shouldAnimateExit(): Boolean = isComposed.value
+ override fun shouldAnimateExit(): Boolean =
+ isComposed.value && composeViewRoot.isAttachedToWindow && composeViewRoot.isShown
override fun onExitAnimationCancelled() {
isDialogShowing.value = false
diff --git a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
index 8241070..ad9a775 100644
--- a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
@@ -115,8 +115,7 @@
android:layout_marginStart="@dimen/dream_overlay_status_icon_margin"
android:src="@drawable/dream_overlay_assistant_attention_indicator"
android:visibility="gone"
- android:contentDescription=
- "@string/dream_overlay_status_bar_assistant_attention_indicator" />
+ android:contentDescription="@string/assistant_attention_content_description" />
</LinearLayout>
</com.android.systemui.dreams.DreamOverlayStatusBarView>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 2e5bc47..4c41ca4 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3022,8 +3022,6 @@
<string name="dream_overlay_status_bar_mic_off">Mic is off</string>
<!-- Content description for the camera and mic off icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
<string name="dream_overlay_status_bar_camera_mic_off">Camera and mic are off</string>
- <!-- Content description for the assistant attention indicator [CHAR LIMIT=NONE] -->
- <string name="dream_overlay_status_bar_assistant_attention_indicator">Assistant is listening</string>
<!-- Content description for the notifications indicator icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
<string name="dream_overlay_status_bar_notification_indicator">{count, plural,
=1 {# notification}
@@ -3209,7 +3207,7 @@
<string name="priority_mode_dream_overlay_content_description">Priority mode on</string>
<!-- Content description for when assistant attention is active [CHAR LIMIT=NONE] -->
- <string name="assistant_attention_content_description">Assistant attention on</string>
+ <string name="assistant_attention_content_description">User presence is detected</string>
<!--- Content of toast triggered when the notes app entry point is triggered without setting a default notes app. [CHAR LIMIT=NONE] -->
<string name="set_default_notes_app_toast_content">Set default notes app in Settings</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 8efe165..7bf3e8f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -59,6 +59,7 @@
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder;
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
@@ -346,7 +347,7 @@
int getNotificationIconAreaHeight() {
if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
return 0;
- } else if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+ } else if (NotificationIconContainerRefactor.isEnabled()) {
return mAodIconContainer != null ? mAodIconContainer.getHeight() : 0;
} else {
return mNotificationIconAreaController.getHeight();
@@ -565,7 +566,7 @@
NotificationIconContainer nic = (NotificationIconContainer)
mView.findViewById(
com.android.systemui.res.R.id.left_aligned_notification_icon_container);
- if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+ if (NotificationIconContainerRefactor.isEnabled()) {
if (mAodIconsBindJob != null) {
mAodIconsBindJob.dispose();
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 584357b..0bd4859 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -1461,8 +1461,7 @@
mDragView.setColorFilter(filter);
}
- @VisibleForTesting
- void setBounceEffectDuration(int duration) {
+ private void setBounceEffectDuration(int duration) {
mBounceEffectDuration = duration;
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt
index 323070a..0781451 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt
@@ -22,6 +22,7 @@
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
+import android.view.ViewConfiguration
import com.android.systemui.shade.TouchLogger
import kotlin.math.pow
import kotlin.math.sqrt
@@ -36,11 +37,18 @@
class LongPressHandlingView(
context: Context,
attrs: AttributeSet?,
+ private val longPressDuration: () -> Long,
) :
View(
context,
attrs,
) {
+
+ constructor(
+ context: Context,
+ attrs: AttributeSet?,
+ ) : this(context, attrs, { ViewConfiguration.getLongPressTimeout().toLong() })
+
interface Listener {
/** Notifies that a long-press has been detected by the given view. */
fun onLongPressDetected(
@@ -77,6 +85,7 @@
)
},
onSingleTapDetected = { listener?.onSingleTapDetected(this@LongPressHandlingView) },
+ longPressDuration = longPressDuration,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt
index c2d4d12..a742e8d 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt
@@ -33,6 +33,8 @@
private val onLongPressDetected: (x: Int, y: Int) -> Unit,
/** Callback reporting the a single tap gesture was detected at the given coordinates. */
private val onSingleTapDetected: () -> Unit,
+ /** Time for the touch to be considered a long-press in ms */
+ private val longPressDuration: () -> Long,
) {
sealed class MotionEventModel {
object Other : MotionEventModel()
@@ -77,7 +79,7 @@
cancelScheduledLongPress()
if (
event.distanceMoved <= ViewConfiguration.getTouchSlop() &&
- event.gestureDuration < ViewConfiguration.getLongPressTimeout()
+ event.gestureDuration < longPressDuration()
) {
dispatchSingleTap()
}
@@ -103,7 +105,7 @@
y = y,
)
},
- ViewConfiguration.getLongPressTimeout().toLong(),
+ longPressDuration(),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 01ded58..ff1df36 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -87,11 +87,6 @@
@JvmField
val NOTIFICATION_SHELF_REFACTOR = releasedFlag("notification_shelf_refactor")
- // TODO(b/290787599): Tracking Bug
- @JvmField
- val NOTIFICATION_ICON_CONTAINER_REFACTOR =
- unreleasedFlag("notification_icon_container_refactor")
-
// TODO(b/288326013): Tracking Bug
@JvmField
val NOTIFICATION_ASYNC_HYBRID_VIEW_INFLATION =
@@ -123,7 +118,7 @@
// TODO(b/301955929)
@JvmField
val NOTIF_LS_BACKGROUND_THREAD =
- unreleasedFlag("notification_lockscreen_mgr_bg_thread")
+ unreleasedFlag("notification_lockscreen_mgr_bg_thread", teamfood = true)
// 200 - keyguard/lockscreen
// ** Flag retired **
@@ -484,7 +479,7 @@
val MEDIA_REMOTE_RESUME = unreleasedFlag("media_remote_resume")
// TODO(b/304506662): Tracking Bug
- val MEDIA_DEVICE_NAME_FIX = unreleasedFlag("media_device_name_fix", teamfood = true)
+ val MEDIA_DEVICE_NAME_FIX = releasedFlag("media_device_name_fix")
// 1000 - dock
val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag("simulate_dock_through_charging")
@@ -623,7 +618,7 @@
/** TODO(b/295143676): Tracking bug. When enable, captures a screenshot for each display. */
@JvmField
- val MULTI_DISPLAY_SCREENSHOT = unreleasedFlag("multi_display_screenshot", teamfood = true)
+ val MULTI_DISPLAY_SCREENSHOT = releasedFlag("multi_display_screenshot")
// 1400 - columbus
// TODO(b/254512756): Tracking Bug
@@ -727,7 +722,7 @@
@JvmField val KEYBOARD_BACKLIGHT_INDICATOR = releasedFlag("keyboard_backlight_indicator")
// TODO(b/277192623): Tracking Bug
- @JvmField val KEYBOARD_EDUCATION = unreleasedFlag("keyboard_education", teamfood = true)
+ @JvmField val KEYBOARD_EDUCATION = releasedFlag("keyboard_education")
// TODO(b/277201412): Tracking Bug
@JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
index 8a9ea25c..b7fe960 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
@@ -35,6 +35,7 @@
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.NotificationIconAreaController
import com.android.systemui.statusbar.phone.NotificationIconContainer
@@ -86,7 +87,7 @@
return
}
- if (featureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+ if (NotificationIconContainerRefactor.isEnabled) {
nic.setOnLockScreen(true)
nicBindingDisposable?.dispose()
nicBindingDisposable =
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index aecfdaa..e768f16 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -91,7 +91,7 @@
@SysUISingleton
@NotifInflationLog
public static LogBuffer provideNotifInflationLogBuffer(LogBufferFactory factory) {
- return factory.create("NotifInflationLog", 100);
+ return factory.create("NotifInflationLog", 250);
}
/** Provides a logging buffer for notification interruption calculations. */
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
index 1962119..98a3896 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLogger.kt
@@ -16,7 +16,6 @@
package com.android.systemui.mediaprojection
import android.media.projection.IMediaProjectionManager
-import android.os.Process
import android.os.RemoteException
import android.util.Log
import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_APP as METRICS_CREATION_SOURCE_APP
@@ -66,6 +65,19 @@
}
/**
+ * Request to log that the permission request was cancelled.
+ *
+ * @param hostUid The UID of the package that initiates MediaProjection.
+ */
+ fun notifyProjectionRequestCancelled(hostUid: Int) {
+ try {
+ service.notifyPermissionRequestCancelled(hostUid)
+ } catch (e: RemoteException) {
+ Log.e(TAG, "Error notifying server of projection cancelled", e)
+ }
+ }
+
+ /**
* Request to log that the app selector was displayed.
*
* @param hostUid The UID of the package that initiates MediaProjection.
@@ -78,47 +90,6 @@
}
}
- /**
- * Request to log that the permission request moved to the given state.
- *
- * Should not be used for the initialization state, since that should use {@link
- * MediaProjectionMetricsLogger#notifyProjectionInitiated(Int)} and pass the
- * sessionCreationSource.
- */
- fun notifyPermissionProgress(state: Int) {
- // TODO validate state is valid
- notifyToServer(state, SessionCreationSource.UNKNOWN)
- }
-
- /**
- * Notifies system server that we are handling a particular state during the consent flow.
- *
- * Only used for emitting atoms.
- *
- * @param state The state that SystemUI is handling during the consent flow. Must be a valid
- * state defined in the MediaProjectionState enum.
- * @param sessionCreationSource Only set if the state is MEDIA_PROJECTION_STATE_INITIATED.
- * Indicates the entry point for requesting the permission. Must be a valid state defined in
- * the SessionCreationSource enum.
- */
- private fun notifyToServer(state: Int, sessionCreationSource: SessionCreationSource) {
- Log.v(TAG, "FOO notifyToServer of state $state and source $sessionCreationSource")
- try {
- service.notifyPermissionRequestStateChange(
- Process.myUid(),
- state,
- sessionCreationSource.toMetricsConstant()
- )
- } catch (e: RemoteException) {
- Log.e(
- TAG,
- "Error notifying server of permission flow state $state from source " +
- "$sessionCreationSource",
- e
- )
- }
- }
-
companion object {
const val TAG = "MediaProjectionMetricsLogger"
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
index 04d5566..50e9e751 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
@@ -210,6 +210,10 @@
reviewGrantedConsentRequired,
/* projection= */ null
)
+ if (isFinishing) {
+ // Only log dismissed when actually finishing, and not when changing configuration.
+ controller.onSelectorDismissed()
+ }
}
activityLauncher.destroy()
controller.destroy()
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
index 67ef119..575e198 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
@@ -18,7 +18,6 @@
import android.content.ComponentName
import android.os.UserHandle
-import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED as STATE_APP_SELECTOR_DISPLAYED
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
@@ -76,6 +75,10 @@
scope.cancel()
}
+ fun onSelectorDismissed() {
+ logger.notifyProjectionRequestCancelled(hostUid)
+ }
+
private suspend fun refreshForegroundTaskThumbnails(tasks: List<RecentTask>) {
coroutineScope {
val thumbnails: List<Deferred<ThumbnailData?>> =
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
index 8b437c3..eea369f 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
@@ -32,6 +32,7 @@
import androidx.annotation.DrawableRes
import androidx.annotation.LayoutRes
import androidx.annotation.StringRes
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -40,16 +41,31 @@
context: Context,
private val screenShareOptions: List<ScreenShareOption>,
private val appName: String?,
+ private val hostUid: Int,
+ private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
@DrawableRes private val dialogIconDrawable: Int? = null,
- @ColorRes private val dialogIconTint: Int? = null
+ @ColorRes private val dialogIconTint: Int? = null,
) : SystemUIDialog(context), AdapterView.OnItemSelectedListener {
private lateinit var dialogTitle: TextView
private lateinit var startButton: TextView
private lateinit var cancelButton: TextView
private lateinit var warning: TextView
private lateinit var screenShareModeSpinner: Spinner
+ private var hasCancelBeenLogged: Boolean = false
var selectedScreenShareOption: ScreenShareOption = screenShareOptions.first()
+ override fun dismiss() {
+ super.dismiss()
+
+ // Dismiss can be called multiple times and we only want to log once.
+ if (hasCancelBeenLogged) {
+ return
+ }
+
+ mediaProjectionMetricsLogger.notifyProjectionRequestCancelled(hostUid)
+ hasCancelBeenLogged = true
+ }
+
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index f7cc589..eacfa57 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -222,13 +222,19 @@
// the correct screen width when in split screen.
Context dialogContext = getApplicationContext();
if (isPartialScreenSharingEnabled()) {
- mDialog = new MediaProjectionPermissionDialog(dialogContext, getMediaProjectionConfig(),
+ mDialog = new MediaProjectionPermissionDialog(
+ dialogContext,
+ getMediaProjectionConfig(),
() -> {
MediaProjectionPermissionDialog dialog =
(MediaProjectionPermissionDialog) mDialog;
ScreenShareOption selectedOption = dialog.getSelectedScreenShareOption();
grantMediaProjectionPermission(selectedOption.getMode());
- }, () -> finish(RECORD_CANCEL, /* projection= */ null), appName);
+ },
+ () -> finish(RECORD_CANCEL, /* projection= */ null),
+ appName,
+ mUid,
+ mMediaProjectionMetricsLogger);
} else {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(dialogContext,
R.style.Theme_SystemUI_Dialog)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
index b9bafd4..cff22b0 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
@@ -18,6 +18,7 @@
import android.content.Context
import android.media.projection.MediaProjectionConfig
import android.os.Bundle
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.res.R
/** Dialog to select screen recording options */
@@ -26,12 +27,16 @@
mediaProjectionConfig: MediaProjectionConfig?,
private val onStartRecordingClicked: Runnable,
private val onCancelClicked: Runnable,
- private val appName: String?
+ private val appName: String?,
+ hostUid: Int,
+ mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
) :
BaseScreenSharePermissionDialog(
context,
createOptionList(context, appName, mediaProjectionConfig),
- appName
+ appName,
+ hostUid,
+ mediaProjectionMetricsLogger
) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 38204ab..a6c6233 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -64,14 +64,13 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatterySaverUtils;
-import com.android.settingslib.utils.PowerUtil;
-import com.android.systemui.res.R;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -376,14 +375,6 @@
TAG_AUTO_SAVER, SystemMessage.NOTE_AUTO_SAVER_SUGGESTION, n, UserHandle.ALL);
}
- private String getHybridContentString(String percentage) {
- return PowerUtil.getBatteryRemainingStringFormatted(
- mContext,
- mCurrentBatterySnapshot.getTimeRemainingMillis(),
- percentage,
- mCurrentBatterySnapshot.isBasedOnUsage());
- }
-
private PendingIntent pendingBroadcast(String action) {
return PendingIntent.getBroadcastAsUser(
mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
index 68bf88b..3b3844a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
@@ -74,6 +74,7 @@
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(getView());
mQsImpl = mQsImplProvider.get();
+ mQsImpl.onCreate(null);
mQsImpl.onComponentCreated(qsFragmentComponent, savedInstanceState);
}
@@ -85,21 +86,13 @@
}
@Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (mQsImpl != null) {
- mQsImpl.onCreate(savedInstanceState);
- }
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
+ public void onDestroyView() {
if (mQsImpl != null) {
mQsImpl.onDestroy();
+ mQsImpl = null;
}
+ super.onDestroyView();
}
-
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index 35c2b06..fab7e95 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -943,7 +943,7 @@
@Override
public void dump(PrintWriter pw, String[] args) {
IndentingPrintWriter indentingPw = new IndentingPrintWriter(pw, /* singleIndent= */ " ");
- indentingPw.println("QSFragment:");
+ indentingPw.println("QSImpl:");
indentingPw.increaseIndent();
indentingPw.println("mQsBounds: " + mQsBounds);
indentingPw.println("mQsExpanded: " + mQsExpanded);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index fc2f5f9..11db69b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -174,7 +174,9 @@
@Override
public void destroy() {
- super.destroy();
+ // Don't call super as this may be called before the view is dettached and calling super
+ // will remove the attach listener. We don't need to do that, because once this object is
+ // detached from the graph, it will be gc.
mHost.removeCallback(mQSHostCallback);
for (TileRecord record : mRecords) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index 8f26e69..bd4c6e1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -32,7 +32,6 @@
import com.android.systemui.qs.pipeline.dagger.QSPipelineModule;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.qs.tiles.di.QSTilesModule;
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.policy.CastController;
@@ -73,13 +72,6 @@
@Multibinds
Map<String, QSTileImpl<?>> tileMap();
- /**
- * A map of internal QS tile ViewModels. Ensures that this can be injected even if
- * it is empty
- */
- @Multibinds
- Map<String, QSTileViewModel> tileViewModelMap();
-
@Provides
@SysUISingleton
static AutoTileManager provideAutoTileManager(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt
index 936bf9c..736f7cf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt
@@ -18,6 +18,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor
import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
@@ -25,7 +26,7 @@
import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
import com.android.systemui.qs.tiles.base.logging.QSTileLogger
import com.android.systemui.qs.tiles.impl.di.QSTileComponent
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.time.SystemClock
@@ -53,6 +54,7 @@
private val falsingManager: FalsingManager,
private val qsTileAnalytics: QSTileAnalytics,
private val qsTileLogger: QSTileLogger,
+ private val qsTileConfigProvider: QSTileConfigProvider,
private val systemClock: SystemClock,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) : QSTileViewModelFactory<T> {
@@ -61,9 +63,9 @@
* Creates [QSTileViewModelImpl] based on the interactors obtained from [component].
* Reference of that [component] is then stored along the view model.
*/
- fun create(component: QSTileComponent<T>): QSTileViewModelImpl<T> =
+ fun create(tileSpec: TileSpec, component: QSTileComponent<T>): QSTileViewModelImpl<T> =
QSTileViewModelImpl(
- component::config,
+ qsTileConfigProvider.getConfig(tileSpec.spec),
component::userActionInteractor,
component::dataInteractor,
component::dataToStateMapper,
@@ -89,12 +91,13 @@
private val falsingManager: FalsingManager,
private val qsTileAnalytics: QSTileAnalytics,
private val qsTileLogger: QSTileLogger,
+ private val qsTileConfigProvider: QSTileConfigProvider,
private val systemClock: SystemClock,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) : QSTileViewModelFactory<T> {
/**
- * @param config contains all the static information (like TileSpec) about the tile.
+ * @param tileSpec of the created tile.
* @param userActionInteractor encapsulates user input processing logic. Use it to start
* activities, show dialogs or otherwise update the tile state.
* @param tileDataInteractor provides [DATA_TYPE] and its availability.
@@ -103,13 +106,13 @@
* operations there.
*/
fun create(
- config: QSTileConfig,
+ tileSpec: TileSpec,
userActionInteractor: QSTileUserActionInteractor<T>,
tileDataInteractor: QSTileDataInteractor<T>,
mapper: QSTileDataToStateMapper<T>,
): QSTileViewModelImpl<T> =
QSTileViewModelImpl(
- { config },
+ qsTileConfigProvider.getConfig(tileSpec.spec),
{ userActionInteractor },
{ tileDataInteractor },
{ mapper },
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
index bbb7445..0bee48f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
@@ -67,7 +67,7 @@
*/
@OptIn(ExperimentalCoroutinesApi::class)
class QSTileViewModelImpl<DATA_TYPE>(
- val tileConfig: () -> QSTileConfig,
+ override val config: QSTileConfig,
private val userActionInteractor: () -> QSTileUserActionInteractor<DATA_TYPE>,
private val tileDataInteractor: () -> QSTileDataInteractor<DATA_TYPE>,
private val mapper: () -> QSTileDataToStateMapper<DATA_TYPE>,
@@ -92,8 +92,6 @@
private val tileData: SharedFlow<DATA_TYPE> = createTileDataFlow()
- override val config
- get() = tileConfig()
override val state: SharedFlow<QSTileState> =
tileData
.map { data ->
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
index 0a6becd..7d7af64 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
@@ -19,6 +19,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.qs.QSFactory
import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
import com.android.systemui.qs.tiles.viewmodel.QSTileViewModelAdapter
import javax.inject.Inject
@@ -29,11 +30,19 @@
class NewQSTileFactory
@Inject
constructor(
+ qsTileConfigProvider: QSTileConfigProvider,
private val adapterFactory: QSTileViewModelAdapter.Factory,
private val tileMap:
Map<String, @JvmSuppressWildcards Provider<@JvmSuppressWildcards QSTileViewModel>>,
) : QSFactory {
+ init {
+ for (viewModelTileSpec in tileMap.keys) {
+ // throws an exception when there is no config for a tileSpec of an injected viewModel
+ qsTileConfigProvider.getConfig(viewModelTileSpec)
+ }
+ }
+
override fun createTile(tileSpec: String): QSTile? =
tileMap[tileSpec]?.let {
val tile = it.get()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
index 94b39b6..32522ad 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
@@ -17,7 +17,13 @@
package com.android.systemui.qs.tiles.di
import com.android.systemui.qs.tiles.impl.custom.di.CustomTileComponent
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProviderImpl
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import dagger.Binds
import dagger.Module
+import dagger.multibindings.Multibinds
/** Module listing subcomponents */
@Module(
@@ -26,4 +32,17 @@
CustomTileComponent::class,
]
)
-interface QSTilesModule
+interface QSTilesModule {
+
+ /**
+ * A map of internal QS tile ViewModels. Ensures that this can be injected even if it is empty
+ */
+ @Multibinds fun tileViewModelConfigs(): Map<String, QSTileConfig>
+
+ /**
+ * A map of internal QS tile ViewModels. Ensures that this can be injected even if it is empty
+ */
+ @Multibinds fun tileViewModelMap(): Map<String, QSTileViewModel>
+
+ @Binds fun bindQSTileConfigProvider(impl: QSTileConfigProviderImpl): QSTileConfigProvider
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileComponent.kt
index 6f351cd..b3d916a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileComponent.kt
@@ -19,7 +19,6 @@
import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
/**
* Base QS tile component. It should be used with [QSTileScope] to create a custom tile scoped
@@ -32,7 +31,5 @@
fun userActionInteractor(): QSTileUserActionInteractor<T>
- fun config(): QSTileConfig
-
fun dataToStateMapper(): QSTileDataToStateMapper<T>
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
index 4a3bcae..c4d7dfb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
@@ -16,20 +16,49 @@
package com.android.systemui.qs.tiles.viewmodel
+import android.content.res.Resources
+import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import com.android.internal.logging.InstanceId
-import com.android.systemui.common.shared.model.Icon
import com.android.systemui.qs.pipeline.shared.TileSpec
data class QSTileConfig(
val tileSpec: TileSpec,
- val tileIcon: Icon,
- @StringRes val tileLabelRes: Int,
+ val uiConfig: QSTileUIConfig,
val instanceId: InstanceId,
val metricsSpec: String = tileSpec.spec,
val policy: QSTilePolicy = QSTilePolicy.NoRestrictions,
)
+/**
+ * Static tile icon and label to be used when the fully operational tile isn't needed (ex. in edit
+ * mode). Icon and label are resources to better support config/locale changes.
+ */
+sealed interface QSTileUIConfig {
+
+ val tileIconRes: Int
+ @DrawableRes get
+ val tileLabelRes: Int
+ @StringRes get
+
+ /**
+ * Represents the absence of static UI state. This should be avoided by platform tiles in favour
+ * of [Resource]. Returns [Resources.ID_NULL] for each field.
+ */
+ data object Empty : QSTileUIConfig {
+ override val tileIconRes: Int
+ get() = Resources.ID_NULL
+ override val tileLabelRes: Int
+ get() = Resources.ID_NULL
+ }
+
+ /** Config containing actual icon and label resources. */
+ data class Resource(
+ @StringRes override val tileIconRes: Int,
+ @StringRes override val tileLabelRes: Int,
+ ) : QSTileUIConfig
+}
+
/** Represents policy restrictions that may be imposed on the tile. */
sealed interface QSTilePolicy {
/** Tile has no policy restrictions */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
new file mode 100644
index 0000000..3f3b94e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 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.qs.tiles.viewmodel
+
+import com.android.internal.util.Preconditions
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+interface QSTileConfigProvider {
+
+ /**
+ * Returns a [QSTileConfig] for a [tileSpec] or throws [IllegalArgumentException] if there is no
+ * config for such [tileSpec].
+ */
+ fun getConfig(tileSpec: String): QSTileConfig
+}
+
+@SysUISingleton
+class QSTileConfigProviderImpl @Inject constructor(private val configs: Map<String, QSTileConfig>) :
+ QSTileConfigProvider {
+
+ init {
+ for (entry in configs.entries) {
+ val configTileSpec = entry.value.tileSpec.spec
+ val keyTileSpec = entry.key
+ Preconditions.checkArgument(
+ configTileSpec == keyTileSpec,
+ "A wrong config is injected keySpec=$keyTileSpec configSpec=$configTileSpec"
+ )
+ }
+ }
+
+ override fun getConfig(tileSpec: String): QSTileConfig =
+ configs[tileSpec] ?: throw IllegalArgumentException("There is no config for spec=$tileSpec")
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index 28536f5..efa6da7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -189,7 +189,13 @@
override fun getInstanceId(): InstanceId = qsTileViewModel.config.instanceId
override fun getTileLabel(): CharSequence =
- context.getString(qsTileViewModel.config.tileLabelRes)
+ with(qsTileViewModel.config.uiConfig) {
+ when (this) {
+ is QSTileUIConfig.Empty -> qsTileViewModel.currentState?.label ?: ""
+ is QSTileUIConfig.Resource -> context.getString(tileLabelRes)
+ }
+ }
+
override fun getTileSpec(): String = qsTileViewModel.config.tileSpec.spec
private companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index ea1205a..05f125f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -163,8 +163,7 @@
}
mMediaProjectionMetricsLogger.notifyProjectionInitiated(
- mUserContextProvider.getUserContext().getUserId(),
- SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
+ getHostUid(), SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
return flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
? new ScreenRecordPermissionDialog(
@@ -174,7 +173,8 @@
/* controller= */ this,
activityStarter,
mUserContextProvider,
- onStartRecordingClicked)
+ onStartRecordingClicked,
+ mMediaProjectionMetricsLogger)
: new ScreenRecordDialog(
context,
/* controller= */ this,
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index f2e94e9..e7481cc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -66,8 +66,10 @@
private Switch mAudioSwitch;
private Spinner mOptions;
- public ScreenRecordDialog(Context context, RecordingController controller,
- UserContextProvider userContextProvider, @Nullable Runnable onStartRecordingClicked) {
+ public ScreenRecordDialog(Context context,
+ RecordingController controller,
+ UserContextProvider userContextProvider,
+ @Nullable Runnable onStartRecordingClicked) {
super(context);
mController = controller;
mUserContextProvider = userContextProvider;
@@ -89,7 +91,6 @@
TextView cancelBtn = findViewById(R.id.button_cancel);
cancelBtn.setOnClickListener(v -> dismiss());
-
TextView startBtn = findViewById(R.id.button_start);
startBtn.setOnClickListener(v -> {
if (mOnStartRecordingClicked != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
index 3b3aa53..f74234b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -33,6 +33,7 @@
import android.widget.Switch
import androidx.annotation.LayoutRes
import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity
import com.android.systemui.mediaprojection.permission.BaseScreenSharePermissionDialog
import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
@@ -50,12 +51,15 @@
private val controller: RecordingController,
private val activityStarter: ActivityStarter,
private val userContextProvider: UserContextProvider,
- private val onStartRecordingClicked: Runnable?
+ private val onStartRecordingClicked: Runnable?,
+ mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
) :
BaseScreenSharePermissionDialog(
context,
createOptionList(),
appName = null,
+ hostUid = hostUid,
+ mediaProjectionMetricsLogger,
R.drawable.ic_screenrecord,
R.color.screenrecord_icon_color
) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 8dc97c0..cc59f87 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -2629,7 +2629,6 @@
if (isPanelExpanded() != isExpanded) {
setExpandedOrAwaitingInputTransfer(isExpanded);
updateSystemUiStateFlags();
- mShadeExpansionStateManager.onShadeExpansionFullyChanged(isExpanded);
if (!isExpanded) {
mQsController.closeQsCustomizer();
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index 3c68438..0ec7a36 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -65,7 +65,6 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
@@ -77,6 +76,7 @@
import com.android.systemui.media.controls.ui.MediaHierarchyManager;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.res.R;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shade.data.repository.ShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
@@ -1026,7 +1026,6 @@
&& mPanelViewControllerLazy.get().mAnimateBack) {
mPanelViewControllerLazy.get().adjustBackAnimationScale(adjustedExpansionFraction);
}
- mShadeExpansionStateManager.onQsExpansionFractionChanged(qsExpansionFraction);
mMediaHierarchyManager.setQsExpansion(qsExpansionFraction);
int qsPanelBottomY = calculateBottomPosition(qsExpansionFraction);
mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
index 0554c58..9493989 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
@@ -36,10 +36,7 @@
class ShadeExpansionStateManager @Inject constructor() : ShadeStateEvents {
private val expansionListeners = CopyOnWriteArrayList<ShadeExpansionListener>()
- private val fullExpansionListeners = CopyOnWriteArrayList<ShadeFullExpansionListener>()
private val qsExpansionListeners = CopyOnWriteArrayList<ShadeQsExpansionListener>()
- private val qsExpansionFractionListeners =
- CopyOnWriteArrayList<ShadeQsExpansionFractionListener>()
private val stateListeners = CopyOnWriteArrayList<ShadeStateListener>()
private val shadeStateEventsListeners = CopyOnWriteArrayList<ShadeStateEventsListener>()
@@ -67,15 +64,6 @@
expansionListeners.remove(listener)
}
- fun addFullExpansionListener(listener: ShadeFullExpansionListener) {
- fullExpansionListeners.add(listener)
- listener.onShadeExpansionFullyChanged(qsExpanded)
- }
-
- fun removeFullExpansionListener(listener: ShadeFullExpansionListener) {
- fullExpansionListeners.remove(listener)
- }
-
fun addQsExpansionListener(listener: ShadeQsExpansionListener) {
qsExpansionListeners.add(listener)
listener.onQsExpansionChanged(qsExpanded)
@@ -85,25 +73,11 @@
qsExpansionListeners.remove(listener)
}
- fun addQsExpansionFractionListener(listener: ShadeQsExpansionFractionListener) {
- qsExpansionFractionListeners.add(listener)
- listener.onQsExpansionFractionChanged(qsExpansionFraction)
- }
-
- fun removeQsExpansionFractionListener(listener: ShadeQsExpansionFractionListener) {
- qsExpansionFractionListeners.remove(listener)
- }
-
/** Adds a listener that will be notified when the panel state has changed. */
fun addStateListener(listener: ShadeStateListener) {
stateListeners.add(listener)
}
- /** Removes a state listener. */
- fun removeStateListener(listener: ShadeStateListener) {
- stateListeners.remove(listener)
- }
-
override fun addShadeStateEventsListener(listener: ShadeStateEventsListener) {
shadeStateEventsListeners.addIfAbsent(listener)
}
@@ -187,22 +161,6 @@
qsExpansionListeners.forEach { it.onQsExpansionChanged(qsExpanded) }
}
- fun onQsExpansionFractionChanged(qsExpansionFraction: Float) {
- this.qsExpansionFraction = qsExpansionFraction
-
- debugLog("qsExpansionFraction=$qsExpansionFraction")
- qsExpansionFractionListeners.forEach {
- it.onQsExpansionFractionChanged(qsExpansionFraction)
- }
- }
-
- fun onShadeExpansionFullyChanged(isExpanded: Boolean) {
- this.expanded = isExpanded
-
- debugLog("expanded=$isExpanded")
- fullExpansionListeners.forEach { it.onShadeExpansionFullyChanged(isExpanded) }
- }
-
/** Updates the panel state if necessary. */
fun updateState(@PanelState state: Int) {
debugLog(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeFullExpansionListener.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeFullExpansionListener.kt
deleted file mode 100644
index 6d13e19..0000000
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeFullExpansionListener.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (c) 2022 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.shade
-
-/** A listener interface to be notified of expansion events for the notification shade. */
-fun interface ShadeFullExpansionListener {
- /** Invoked whenever the shade expansion changes, when it is fully collapsed or expanded */
- fun onShadeExpansionFullyChanged(isExpanded: Boolean)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index 2f68476..f043c71 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -148,12 +148,13 @@
*
* TODO(b/300258424) remove all but the first sentence of this comment
*/
- val isAnyExpanded: Flow<Boolean> =
+ val isAnyExpanded: StateFlow<Boolean> =
if (sceneContainerFlags.isEnabled()) {
- anyExpansion.map { it > 0f }.distinctUntilChanged()
- } else {
- repository.legacyExpandedOrAwaitingInputTransfer
- }
+ anyExpansion.map { it > 0f }.distinctUntilChanged()
+ } else {
+ repository.legacyExpandedOrAwaitingInputTransfer
+ }
+ .stateIn(scope, SharingStarted.Eagerly, false)
/**
* Whether the user is expanding or collapsing the shade with user input. This will be true even
@@ -184,7 +185,7 @@
* but a transition they initiated is still animating.
*/
val isUserInteracting: Flow<Boolean> =
- combine(isUserInteractingWithShade, isUserInteractingWithShade) { shade, qs -> shade || qs }
+ combine(isUserInteractingWithShade, isUserInteractingWithQs) { shade, qs -> shade || qs }
.distinctUntilChanged()
/** Are touches allowed on the notification panel? */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
index 7d81e55..c8669072 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
@@ -48,6 +48,13 @@
fadeOut(view, duration, delay, (Runnable) null);
}
+ /**
+ * Perform a fade-out animation, invoking {@code endRunnable} when the animation ends. It will
+ * not be invoked if the animation is cancelled.
+ *
+ * @deprecated Use {@link #fadeOut(View, long, int, Animator.AnimatorListener)} instead.
+ */
+ @Deprecated
public static void fadeOut(final View view, long duration, int delay,
@Nullable final Runnable endRunnable) {
view.animate().cancel();
@@ -155,6 +162,13 @@
fadeIn(view, duration, delay, /* endRunnable= */ (Runnable) null);
}
+ /**
+ * Perform a fade-in animation, invoking {@code endRunnable} when the animation ends. It will
+ * not be invoked if the animation is cancelled.
+ *
+ * @deprecated Use {@link #fadeIn(View, long, int, Animator.AnimatorListener)} instead.
+ */
+ @Deprecated
public static void fadeIn(final View view, long duration, int delay,
@Nullable Runnable endRunnable) {
view.animate().cancel();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 2e1e395..49743bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -29,14 +29,13 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.flags.FeatureFlagsClassic;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
import com.android.systemui.statusbar.domain.interactor.SilentNotificationStatusIconsVisibilityInteractor;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
import com.android.systemui.statusbar.notification.collection.PipelineDumper;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins;
import com.android.systemui.util.time.SystemClock;
@@ -62,7 +61,6 @@
private static final long MAX_RANKING_DELAY_MILLIS = 500L;
private final Context mContext;
- private final FeatureFlagsClassic mFeatureFlags;
private final NotificationManager mNotificationManager;
private final SilentNotificationStatusIconsVisibilityInteractor mStatusIconInteractor;
private final SystemClock mSystemClock;
@@ -80,7 +78,6 @@
@Inject
public NotificationListener(
Context context,
- FeatureFlagsClassic featureFlags,
NotificationManager notificationManager,
SilentNotificationStatusIconsVisibilityInteractor statusIconInteractor,
SystemClock systemClock,
@@ -88,7 +85,6 @@
PluginManager pluginManager) {
super(pluginManager);
mContext = context;
- mFeatureFlags = featureFlags;
mNotificationManager = notificationManager;
mStatusIconInteractor = statusIconInteractor;
mSystemClock = systemClock;
@@ -106,6 +102,7 @@
/** Registers a listener that's notified when any notification-related settings change. */
@Deprecated
public void addNotificationSettingsListener(NotificationSettingsListener listener) {
+ NotificationIconContainerRefactor.assertInLegacyMode();
mSettingsListeners.add(listener);
}
@@ -240,7 +237,7 @@
@Override
public void onSilentStatusBarIconsVisibilityChanged(boolean hideSilentStatusIcons) {
- if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+ if (NotificationIconContainerRefactor.isEnabled()) {
mStatusIconInteractor.setHideSilentStatusIcons(hideSilentStatusIcons);
} else {
for (NotificationSettingsListener listener : mSettingsListeners) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 61ebcc0..5f0b298 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -48,11 +48,13 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dumpable;
-import com.android.systemui.dump.DumpManager;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.res.R;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.dagger.CentralSurfacesDependenciesModule;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.RemoteInputControllerLogger;
@@ -65,6 +67,7 @@
import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.util.DumpUtilsKt;
import com.android.systemui.util.ListenerSet;
+import com.android.systemui.util.kotlin.JavaAdapter;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -72,13 +75,16 @@
import java.util.Objects;
import java.util.function.Consumer;
+import javax.inject.Inject;
+
/**
* Class for handling remote input state over a set of notifications. This class handles things
* like keeping notifications temporarily that were cancelled as a response to a remote input
* interaction, keeping track of notifications to remove when NotificationPresenter is collapsed,
* and handling clicks on remote views.
*/
-public class NotificationRemoteInputManager implements Dumpable {
+@SysUISingleton
+public class NotificationRemoteInputManager implements CoreStartable {
public static final boolean ENABLE_REMOTE_INPUT =
SystemProperties.getBoolean("debug.enable_remote_input", true);
public static boolean FORCE_REMOTE_INPUT_HISTORY =
@@ -94,6 +100,8 @@
private final NotificationVisibilityProvider mVisibilityProvider;
private final PowerInteractor mPowerInteractor;
private final ActionClickLogger mLogger;
+ private final JavaAdapter mJavaAdapter;
+ private final ShadeInteractor mShadeInteractor;
protected final Context mContext;
protected final NotifPipelineFlags mNotifPipelineFlags;
private final UserManager mUserManager;
@@ -249,6 +257,7 @@
/**
* Injected constructor. See {@link CentralSurfacesDependenciesModule}.
*/
+ @Inject
public NotificationRemoteInputManager(
Context context,
NotifPipelineFlags notifPipelineFlags,
@@ -261,7 +270,8 @@
RemoteInputControllerLogger remoteInputControllerLogger,
NotificationClickNotifier clickNotifier,
ActionClickLogger logger,
- DumpManager dumpManager) {
+ JavaAdapter javaAdapter,
+ ShadeInteractor shadeInteractor) {
mContext = context;
mNotifPipelineFlags = notifPipelineFlags;
mLockscreenUserManager = lockscreenUserManager;
@@ -269,6 +279,8 @@
mVisibilityProvider = visibilityProvider;
mPowerInteractor = powerInteractor;
mLogger = logger;
+ mJavaAdapter = javaAdapter;
+ mShadeInteractor = shadeInteractor;
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
@@ -277,8 +289,25 @@
mRemoteInputUriController = remoteInputUriController;
mRemoteInputControllerLogger = remoteInputControllerLogger;
mClickNotifier = clickNotifier;
+ }
- dumpManager.registerDumpable(this);
+ @Override
+ public void start() {
+ mJavaAdapter.alwaysCollectFlow(mShadeInteractor.isAnyExpanded(),
+ this::onShadeOrQsExpanded);
+ }
+
+ private void onShadeOrQsExpanded(boolean expanded) {
+ if (expanded && mStatusBarStateController.getState() != StatusBarState.KEYGUARD) {
+ try {
+ mBarService.clearNotificationEffects();
+ } catch (RemoteException e) {
+ // Won't fail unless the world has ended.
+ }
+ }
+ if (!expanded) {
+ onPanelCollapsed();
+ }
}
/** Add a listener for various remote input events. Works with NEW pipeline only. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 37a4ef1..537f8a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -42,15 +42,16 @@
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardClockSwitch;
import com.android.systemui.DejankUtils;
-import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.res.R;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.systemui.util.Compile;
+import com.android.systemui.util.kotlin.JavaAdapter;
+
+import dagger.Lazy;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -64,8 +65,7 @@
@SysUISingleton
public class StatusBarStateControllerImpl implements
SysuiStatusBarStateController,
- CallbackController<StateListener>,
- Dumpable {
+ CallbackController<StateListener> {
private static final String TAG = "SbStateController";
private static final boolean DEBUG_IMMERSIVE_APPS =
SystemProperties.getBoolean("persist.debug.immersive_apps", false);
@@ -95,6 +95,8 @@
private final ArrayList<RankedListener> mListeners = new ArrayList<>();
private final UiEventLogger mUiEventLogger;
private final InteractionJankMonitor mInteractionJankMonitor;
+ private final JavaAdapter mJavaAdapter;
+ private final Lazy<ShadeInteractor> mShadeInteractorLazy;
private int mState;
private int mLastState;
private int mUpcomingState;
@@ -156,18 +158,22 @@
@Inject
public StatusBarStateControllerImpl(
UiEventLogger uiEventLogger,
- DumpManager dumpManager,
InteractionJankMonitor interactionJankMonitor,
- ShadeExpansionStateManager shadeExpansionStateManager
- ) {
+ JavaAdapter javaAdapter,
+ Lazy<ShadeInteractor> shadeInteractorLazy) {
mUiEventLogger = uiEventLogger;
mInteractionJankMonitor = interactionJankMonitor;
+ mJavaAdapter = javaAdapter;
+ mShadeInteractorLazy = shadeInteractorLazy;
for (int i = 0; i < HISTORY_SIZE; i++) {
mHistoricalRecords[i] = new HistoricalState();
}
- shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
+ }
- dumpManager.registerDumpable(this);
+ @Override
+ public void start() {
+ mJavaAdapter.alwaysCollectFlow(mShadeInteractorLazy.get().isAnyExpanded(),
+ this::onShadeOrQsExpanded);
}
@Override
@@ -345,7 +351,7 @@
}
}
- private void onShadeExpansionFullyChanged(Boolean isExpanded) {
+ private void onShadeOrQsExpanded(Boolean isExpanded) {
if (mIsExpanded != isExpanded) {
mIsExpanded = isExpanded;
String tag = getClass().getSimpleName() + "#setIsExpanded";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index aa32d5c..8104755 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.view.View;
+import com.android.systemui.CoreStartable;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -29,7 +30,7 @@
/**
* Sends updates to {@link StateListener}s about changes to the status bar state and dozing state
*/
-public interface SysuiStatusBarStateController extends StatusBarStateController {
+public interface SysuiStatusBarStateController extends StatusBarStateController, CoreStartable {
// TODO: b/115739177 (remove this explicit ordering if we can)
@Retention(SOURCE)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 1fe6b83..a957095 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -23,6 +23,7 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.CoreStartable;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.AnimationFeatureFlags;
import com.android.systemui.animation.DialogLaunchAnimator;
@@ -33,24 +34,19 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.ShadeSurface;
import com.android.systemui.shade.carrier.ShadeCarrierGroupController;
-import com.android.systemui.statusbar.ActionClickLogger;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationClickNotifier;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.commandline.CommandRegistry;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.RemoteInputControllerLogger;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -63,12 +59,13 @@
import com.android.systemui.statusbar.phone.StatusBarNotificationPresenterModule;
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.RemoteInputUriController;
import dagger.Binds;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
/**
* This module provides instances needed to construct {@link CentralSurfacesImpl}. These are moved to
@@ -78,36 +75,12 @@
*/
@Module(includes = {StatusBarNotificationPresenterModule.class})
public interface CentralSurfacesDependenciesModule {
+
/** */
- @SysUISingleton
- @Provides
- static NotificationRemoteInputManager provideNotificationRemoteInputManager(
- Context context,
- NotifPipelineFlags notifPipelineFlags,
- NotificationLockscreenUserManager lockscreenUserManager,
- SmartReplyController smartReplyController,
- NotificationVisibilityProvider visibilityProvider,
- PowerInteractor powerInteractor,
- StatusBarStateController statusBarStateController,
- RemoteInputUriController remoteInputUriController,
- RemoteInputControllerLogger remoteInputControllerLogger,
- NotificationClickNotifier clickNotifier,
- ActionClickLogger actionClickLogger,
- DumpManager dumpManager) {
- return new NotificationRemoteInputManager(
- context,
- notifPipelineFlags,
- lockscreenUserManager,
- smartReplyController,
- visibilityProvider,
- powerInteractor,
- statusBarStateController,
- remoteInputUriController,
- remoteInputControllerLogger,
- clickNotifier,
- actionClickLogger,
- dumpManager);
- }
+ @Binds
+ @IntoMap
+ @ClassKey(NotificationRemoteInputManager.class)
+ CoreStartable bindsStartNotificationRemoteInputManager(NotificationRemoteInputManager nrim);
/** */
@SysUISingleton
@@ -164,20 +137,23 @@
return new CommandQueue(context, displayTracker, registry, dumpHandler, powerInteractor);
}
- /**
- */
+ /** */
@Binds
ManagedProfileController provideManagedProfileController(
ManagedProfileControllerImpl controllerImpl);
- /**
- */
+ /** */
@Binds
SysuiStatusBarStateController providesSysuiStatusBarStateController(
StatusBarStateControllerImpl statusBarStateControllerImpl);
- /**
- */
+ /** */
+ @Binds
+ @IntoMap
+ @ClassKey(SysuiStatusBarStateController.class)
+ CoreStartable bindsStartStatusBarStateController(StatusBarStateControllerImpl sbsc);
+
+ /** */
@Binds
StatusBarIconController provideStatusBarIconController(
StatusBarIconControllerImpl controllerImpl);
@@ -212,16 +188,14 @@
ShadeCarrierGroupController.SlotIndexResolver provideSlotIndexResolver(
ShadeCarrierGroupController.SubscriptionManagerSlotIndexResolver impl);
- /**
- */
+ /** */
@Provides
@SysUISingleton
static ActivityLaunchAnimator provideActivityLaunchAnimator() {
return new ActivityLaunchAnimator();
}
- /**
- */
+ /** */
@Provides
@SysUISingleton
static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager,
@@ -253,8 +227,7 @@
return new DialogLaunchAnimator(callback, interactionJankMonitor, animationFeatureFlags);
}
- /**
- */
+ /** */
@Provides
@SysUISingleton
static AnimationFeatureFlags provideAnimationFeatureFlags(FeatureFlags featureFlags) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
index 07e84bb..e0c4bfa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.notification.collection.coordinator
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
@@ -25,6 +23,7 @@
import com.android.systemui.statusbar.notification.collection.render.NotifStackController
import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.domain.interactor.RenderNotificationListInteractor
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
import com.android.systemui.statusbar.phone.NotificationIconAreaController
import com.android.systemui.util.traceSection
@@ -38,7 +37,6 @@
class StackCoordinator
@Inject
internal constructor(
- private val featureFlags: FeatureFlagsClassic,
private val groupExpansionManagerImpl: GroupExpansionManagerImpl,
private val notificationIconAreaController: NotificationIconAreaController,
private val renderListInteractor: RenderNotificationListInteractor,
@@ -52,7 +50,7 @@
fun onAfterRenderList(entries: List<ListEntry>, controller: NotifStackController) =
traceSection("StackCoordinator.onAfterRenderList") {
controller.setNotifStats(calculateNotifStats(entries))
- if (featureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+ if (NotificationIconContainerRefactor.isEnabled) {
renderListInteractor.setRenderedList(entries)
} else {
notificationIconAreaController.updateNotificationIcons(entries)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt
index 41b42e3..1992ea8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconAreaControllerViewBinderWrapperImpl.kt
@@ -22,6 +22,7 @@
import com.android.systemui.statusbar.NotificationShelfController
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl
import com.android.systemui.statusbar.phone.NotificationIconAreaController
import com.android.systemui.statusbar.phone.NotificationIconContainer
@@ -71,7 +72,8 @@
val unsupported: Nothing
get() =
error(
- "Code path not supported when NOTIFICATION_ICON_CONTAINER_REFACTOR is disabled"
+ "Code path not supported when ${NotificationIconContainerRefactor.FLAG_NAME}" +
+ " is disabled"
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
index 7592619..c1f728a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
@@ -17,13 +17,15 @@
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
+import android.graphics.Color
import android.graphics.Rect
import android.view.View
+import android.view.ViewGroup
import android.view.ViewPropertyAnimator
import android.widget.FrameLayout
+import androidx.annotation.ColorInt
import androidx.collection.ArrayMap
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
+import androidx.lifecycle.lifecycleScope
import com.android.app.animation.Interpolators
import com.android.internal.policy.SystemBarUtils
import com.android.internal.util.ContrastColorUtil
@@ -37,9 +39,12 @@
import com.android.systemui.statusbar.notification.NotificationUtils
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconColors
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconsViewData
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconColorLookup
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconColors
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconsViewData
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.NotificationIconContainer
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
@@ -49,6 +54,7 @@
import com.android.systemui.util.kotlin.mapValuesNotNullTo
import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.kotlin.stateFlow
+import com.android.systemui.util.ui.AnimatedValue
import com.android.systemui.util.ui.isAnimating
import com.android.systemui.util.ui.stopAnimating
import com.android.systemui.util.ui.value
@@ -62,12 +68,53 @@
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
-/** Binds a [NotificationIconContainer] to its [view model][NotificationIconContainerViewModel]. */
+/** Binds a view-model to a [NotificationIconContainer]. */
object NotificationIconContainerViewBinder {
@JvmStatic
fun bind(
view: NotificationIconContainer,
- viewModel: NotificationIconContainerViewModel,
+ viewModel: NotificationIconContainerShelfViewModel,
+ configuration: ConfigurationState,
+ configurationController: ConfigurationController,
+ viewStore: ShelfNotificationIconViewStore,
+ ): DisposableHandle {
+ return view.repeatWhenAttached {
+ lifecycleScope.launch {
+ viewModel.icons.bindIcons(view, configuration, configurationController, viewStore)
+ }
+ }
+ }
+
+ @JvmStatic
+ fun bind(
+ view: NotificationIconContainer,
+ viewModel: NotificationIconContainerStatusBarViewModel,
+ configuration: ConfigurationState,
+ configurationController: ConfigurationController,
+ viewStore: StatusBarNotificationIconViewStore,
+ ): DisposableHandle {
+ val contrastColorUtil = ContrastColorUtil.getInstance(view.context)
+ return view.repeatWhenAttached {
+ lifecycleScope.run {
+ launch {
+ viewModel.icons.bindIcons(
+ view,
+ configuration,
+ configurationController,
+ viewStore
+ )
+ }
+ launch { viewModel.iconColors.bindIconColors(view, contrastColorUtil) }
+ launch { viewModel.bindIsolatedIcon(view, viewStore) }
+ launch { viewModel.animationsEnabled.bindAnimationsEnabled(view) }
+ }
+ }
+ }
+
+ @JvmStatic
+ fun bind(
+ view: NotificationIconContainer,
+ viewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
configuration: ConfigurationState,
configurationController: ConfigurationController,
dozeParameters: DozeParameters,
@@ -75,60 +122,72 @@
screenOffAnimationController: ScreenOffAnimationController,
viewStore: IconViewStore,
): DisposableHandle {
- val contrastColorUtil = ContrastColorUtil.getInstance(view.context)
return view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- launch { bindAnimationsEnabled(viewModel, view) }
- launch { bindIsDozing(viewModel, view, dozeParameters) }
- // TODO(b/278765923): this should live where AOD is bound, not inside of the NIC
- // view-binder
+ lifecycleScope.launch {
launch {
- bindVisibility(
- viewModel,
- view,
- configuration,
- featureFlags,
- screenOffAnimationController,
- )
- }
- launch { bindIconColors(viewModel, view, contrastColorUtil) }
- launch {
- bindIconViewData(
- viewModel,
+ viewModel.icons.bindIcons(
view,
configuration,
configurationController,
viewStore,
)
}
- launch { bindIsolatedIcon(viewModel, view, viewStore) }
+ launch { viewModel.animationsEnabled.bindAnimationsEnabled(view) }
+ launch { viewModel.isDozing.bindIsDozing(view, dozeParameters) }
+ // TODO(b/278765923): this should live where AOD is bound, not inside of the NIC
+ // view-binder
+ launch {
+ viewModel.isVisible.bindIsVisible(
+ view,
+ configuration,
+ featureFlags,
+ screenOffAnimationController,
+ )
+ }
+ launch {
+ configuration
+ .getColorAttr(R.attr.wallpaperTextColor, DEFAULT_AOD_ICON_COLOR)
+ .bindIconColors(view)
+ }
}
}
}
- private suspend fun bindAnimationsEnabled(
- viewModel: NotificationIconContainerViewModel,
- view: NotificationIconContainer
- ) {
- viewModel.animationsEnabled.collect(view::setAnimationsEnabled)
+ /** Binds to [NotificationIconContainer.setAnimationsEnabled] */
+ private suspend fun Flow<Boolean>.bindAnimationsEnabled(view: NotificationIconContainer) {
+ collect(view::setAnimationsEnabled)
}
- private suspend fun bindIconColors(
- viewModel: NotificationIconContainerViewModel,
+ /**
+ * Binds to the [StatusBarIconView.setStaticDrawableColor] and [StatusBarIconView.setDecorColor]
+ * of the [children] of an [NotificationIconContainer].
+ */
+ private suspend fun Flow<NotificationIconColorLookup>.bindIconColors(
view: NotificationIconContainer,
contrastColorUtil: ContrastColorUtil,
) {
- viewModel.iconColors
- .mapNotNull { lookup -> lookup.iconColors(view.viewBounds) }
- .collect { iconLookup -> applyTint(view, iconLookup, contrastColorUtil) }
+ mapNotNull { lookup -> lookup.iconColors(view.viewBounds) }
+ .collect { iconLookup -> view.applyTint(iconLookup, contrastColorUtil) }
}
- private suspend fun bindIsDozing(
- viewModel: NotificationIconContainerViewModel,
+ /**
+ * Binds to the [StatusBarIconView.setStaticDrawableColor] and [StatusBarIconView.setDecorColor]
+ * of the [children] of an [NotificationIconContainer].
+ */
+ private suspend fun Flow<Int>.bindIconColors(view: NotificationIconContainer) {
+ collect { tint ->
+ view.children.filterIsInstance<StatusBarIconView>().forEach { icon ->
+ icon.staticDrawableColor = tint
+ icon.setDecorColor(tint)
+ }
+ }
+ }
+
+ private suspend fun Flow<AnimatedValue<Boolean>>.bindIsDozing(
view: NotificationIconContainer,
dozeParameters: DozeParameters,
) {
- viewModel.isDozing.collect { isDozing ->
+ collect { isDozing ->
if (isDozing.isAnimating) {
val animate = !dozeParameters.displayNeedsBlanking
view.setDozing(
@@ -147,19 +206,18 @@
}
}
- private suspend fun bindIsolatedIcon(
- viewModel: NotificationIconContainerViewModel,
+ private suspend fun NotificationIconContainerStatusBarViewModel.bindIsolatedIcon(
view: NotificationIconContainer,
viewStore: IconViewStore,
) {
coroutineScope {
launch {
- viewModel.isolatedIconLocation.collect { location ->
+ isolatedIconLocation.collect { location ->
view.setIsolatedIconLocation(location, true)
}
}
launch {
- viewModel.isolatedIcon.collect { iconInfo ->
+ isolatedIcon.collect { iconInfo ->
val iconView = iconInfo.value?.let { viewStore.iconView(it.notifKey) }
if (iconInfo.isAnimating) {
view.showIconIsolatedAnimated(iconView, iconInfo::stopAnimating)
@@ -171,8 +229,8 @@
}
}
- private suspend fun bindIconViewData(
- viewModel: NotificationIconContainerViewModel,
+ /** Binds [NotificationIconsViewData] to a [NotificationIconContainer]'s [children]. */
+ private suspend fun Flow<NotificationIconsViewData>.bindIcons(
view: NotificationIconContainer,
configuration: ConfigurationState,
configurationController: ConfigurationController,
@@ -205,11 +263,11 @@
}
}
- var prevIcons = IconsViewData()
- viewModel.iconsViewData.sample(layoutParams, ::Pair).collect {
- (iconsData: IconsViewData, layoutParams: FrameLayout.LayoutParams),
+ var prevIcons = NotificationIconsViewData()
+ sample(layoutParams, ::Pair).collect {
+ (iconsData: NotificationIconsViewData, layoutParams: FrameLayout.LayoutParams),
->
- val iconsDiff = IconsViewData.computeDifference(iconsData, prevIcons)
+ val iconsDiff = NotificationIconsViewData.computeDifference(iconsData, prevIcons)
prevIcons = iconsData
val replacingIcons =
@@ -255,30 +313,27 @@
// TODO(b/305739416): Once StatusBarIconView has its own Recommended Architecture stack, this
// can be moved there and cleaned up.
- private fun applyTint(
- view: NotificationIconContainer,
- iconColors: IconColors,
+ private fun ViewGroup.applyTint(
+ iconColors: NotificationIconColors,
contrastColorUtil: ContrastColorUtil,
) {
- view.children
+ children
.filterIsInstance<StatusBarIconView>()
.filter { it.width != 0 }
- .forEach { iv -> updateTintForIcon(iv, iconColors, contrastColorUtil) }
+ .forEach { iv -> iv.updateTintForIcon(iconColors, contrastColorUtil) }
}
- private fun updateTintForIcon(
- v: StatusBarIconView,
- iconColors: IconColors,
+ private fun StatusBarIconView.updateTintForIcon(
+ iconColors: NotificationIconColors,
contrastColorUtil: ContrastColorUtil,
) {
- val isPreL = java.lang.Boolean.TRUE == v.getTag(R.id.icon_is_pre_L)
- val isColorized = !isPreL || NotificationUtils.isGrayscale(v, contrastColorUtil)
- v.staticDrawableColor = iconColors.staticDrawableColor(v.viewBounds, isColorized)
- v.setDecorColor(iconColors.tint)
+ val isPreL = java.lang.Boolean.TRUE == getTag(R.id.icon_is_pre_L)
+ val isColorized = !isPreL || NotificationUtils.isGrayscale(this, contrastColorUtil)
+ staticDrawableColor = iconColors.staticDrawableColor(viewBounds, isColorized)
+ setDecorColor(iconColors.tint)
}
- private suspend fun bindVisibility(
- viewModel: NotificationIconContainerViewModel,
+ private suspend fun Flow<AnimatedValue<Boolean>>.bindIsVisible(
view: NotificationIconContainer,
configuration: ConfigurationState,
featureFlags: FeatureFlagsClassic,
@@ -287,7 +342,7 @@
val iconAppearTranslation =
configuration.getDimensionPixelSize(R.dimen.shelf_appear_translation).stateIn(this)
val statusViewMigrated = featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)
- viewModel.isVisible.collect { isVisible ->
+ collect { isVisible ->
view.animate().cancel()
val animatorListener =
object : AnimatorListenerAdapter() {
@@ -304,7 +359,7 @@
view.visibility = if (isVisible.value) View.VISIBLE else View.INVISIBLE
}
featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION) -> {
- animateInIconTranslation(view, statusViewMigrated)
+ view.animateInIconTranslation(statusViewMigrated)
if (isVisible.value) {
CrossFadeHelper.fadeIn(view, animatorListener)
} else {
@@ -313,15 +368,14 @@
}
!isVisible.value -> {
// Let's make sure the icon are translated to 0, since we cancelled it above
- animateInIconTranslation(view, statusViewMigrated)
+ view.animateInIconTranslation(statusViewMigrated)
CrossFadeHelper.fadeOut(view, animatorListener)
}
view.visibility != View.VISIBLE -> {
// No fading here, let's just appear the icons instead!
view.visibility = View.VISIBLE
view.alpha = 1f
- appearIcons(
- view,
+ view.appearIcons(
animate = screenOffAnimationController.shouldAnimateAodIcons(),
iconAppearTranslation.value,
statusViewMigrated,
@@ -330,7 +384,7 @@
}
else -> {
// Let's make sure the icons are translated to 0, since we cancelled it above
- animateInIconTranslation(view, statusViewMigrated)
+ view.animateInIconTranslation(statusViewMigrated)
// We were fading out, let's fade in instead
CrossFadeHelper.fadeIn(view, animatorListener)
}
@@ -338,8 +392,7 @@
}
}
- private fun appearIcons(
- view: View,
+ private fun View.appearIcons(
animate: Boolean,
iconAppearTranslation: Int,
statusViewMigrated: Boolean,
@@ -347,11 +400,10 @@
) {
if (animate) {
if (!statusViewMigrated) {
- view.translationY = -iconAppearTranslation.toFloat()
+ translationY = -iconAppearTranslation.toFloat()
}
- view.alpha = 0f
- view
- .animate()
+ alpha = 0f
+ animate()
.alpha(1f)
.setInterpolator(Interpolators.LINEAR)
.setDuration(AOD_ICONS_APPEAR_DURATION)
@@ -359,40 +411,29 @@
.setListener(animatorListener)
.start()
} else {
- view.alpha = 1.0f
+ alpha = 1.0f
if (!statusViewMigrated) {
- view.translationY = 0f
+ translationY = 0f
}
}
}
- private fun animateInIconTranslation(view: View, statusViewMigrated: Boolean) {
+ private fun View.animateInIconTranslation(statusViewMigrated: Boolean) {
if (!statusViewMigrated) {
- view.animate().animateInIconTranslation().setDuration(AOD_ICONS_APPEAR_DURATION).start()
+ animate().animateInIconTranslation().setDuration(AOD_ICONS_APPEAR_DURATION).start()
}
}
private fun ViewPropertyAnimator.animateInIconTranslation(): ViewPropertyAnimator =
setInterpolator(Interpolators.DECELERATE_QUINT).translationY(0f)
- private const val AOD_ICONS_APPEAR_DURATION: Long = 200
-
- private val View.viewBounds: Rect
- get() {
- val tmpArray = intArrayOf(0, 0)
- getLocationOnScreen(tmpArray)
- return Rect(
- /* left = */ tmpArray[0],
- /* top = */ tmpArray[1],
- /* right = */ left + width,
- /* bottom = */ top + height,
- )
- }
-
/** External storage for [StatusBarIconView] instances. */
fun interface IconViewStore {
fun iconView(key: String): StatusBarIconView?
}
+
+ private const val AOD_ICONS_APPEAR_DURATION: Long = 200
+ @ColorInt private val DEFAULT_AOD_ICON_COLOR = Color.WHITE
}
/** [IconViewStore] for the [com.android.systemui.statusbar.NotificationShelf] */
@@ -424,3 +465,15 @@
override fun iconView(key: String): StatusBarIconView? =
notifCollection.getEntry(key)?.icons?.statusBarIcon
}
+
+private val View.viewBounds: Rect
+ get() {
+ val tmpArray = intArrayOf(0, 0)
+ getLocationOnScreen(tmpArray)
+ return Rect(
+ /* left = */ tmpArray[0],
+ /* top = */ tmpArray[1],
+ /* right = */ left + width,
+ /* bottom = */ top + height,
+ )
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconColors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconColors.kt
new file mode 100644
index 0000000..97d1e1b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconColors.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ */
+
+package com.android.systemui.statusbar.notification.icon.ui.viewmodel
+
+import android.graphics.Rect
+
+/**
+ * Lookup the colors to use for the notification icons based on the bounds of the icon container. A
+ * result of `null` indicates that no color changes should be applied.
+ */
+fun interface NotificationIconColorLookup {
+ fun iconColors(viewBounds: Rect): NotificationIconColors?
+}
+
+/** Colors to apply to notification icons. */
+interface NotificationIconColors {
+
+ /** A tint to apply to the icons. */
+ val tint: Int
+
+ /**
+ * Returns the color to be applied to an icon, based on that icon's view bounds and whether or
+ * not the notification icon is colorized.
+ */
+ fun staticDrawableColor(viewBounds: Rect, isColorized: Boolean): Int
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
index 120d342..611ed89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
@@ -15,10 +15,7 @@
*/
package com.android.systemui.statusbar.notification.icon.ui.viewmodel
-import android.graphics.Color
import android.graphics.Rect
-import androidx.annotation.ColorInt
-import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.flags.FeatureFlagsClassic
@@ -27,14 +24,9 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
import com.android.systemui.statusbar.notification.icon.domain.interactor.AlwaysOnDisplayNotificationIconsInteractor
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.ColorLookup
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconColors
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconInfo
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconsViewData
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.util.kotlin.pairwise
@@ -47,8 +39,6 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.emptyFlow
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
/** View-model for the row of notification icons displayed on the always-on display. */
@@ -56,7 +46,6 @@
class NotificationIconContainerAlwaysOnDisplayViewModel
@Inject
constructor(
- configuration: ConfigurationState,
private val deviceEntryInteractor: DeviceEntryInteractor,
private val dozeParameters: DozeParameters,
private val featureFlags: FeatureFlagsClassic,
@@ -66,14 +55,10 @@
private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor,
screenOffAnimationController: ScreenOffAnimationController,
shadeInteractor: ShadeInteractor,
-) : NotificationIconContainerViewModel {
+) {
- override val iconColors: Flow<ColorLookup> =
- configuration.getColorAttr(R.attr.wallpaperTextColor, DEFAULT_AOD_ICON_COLOR).map { tint ->
- ColorLookup { IconColorsImpl(tint) }
- }
-
- override val animationsEnabled: Flow<Boolean> =
+ /** Are changes to the icon container animated? */
+ val animationsEnabled: Flow<Boolean> =
combine(
shadeInteractor.isShadeTouchable,
keyguardInteractor.isKeyguardVisible,
@@ -81,7 +66,8 @@
panelTouchesEnabled && isKeyguardVisible
}
- override val isDozing: Flow<AnimatedValue<Boolean>> =
+ /** Should icons be rendered in "dozing" mode? */
+ val isDozing: Flow<AnimatedValue<Boolean>> =
keyguardTransitionInteractor.startedKeyguardTransitionStep
// Determine if we're dozing based on the most recent transition
.map { step: TransitionStep ->
@@ -98,7 +84,8 @@
.distinctUntilChanged()
.toAnimatedValueFlow()
- override val isVisible: Flow<AnimatedValue<Boolean>> =
+ /** Is the icon container visible? */
+ val isVisible: Flow<AnimatedValue<Boolean>> =
combine(
keyguardTransitionInteractor.finishedKeyguardState.map { it != KeyguardState.GONE },
deviceEntryInteractor.isBypassEnabled,
@@ -136,17 +123,14 @@
}
.distinctUntilChanged()
- override val iconsViewData: Flow<IconsViewData> =
+ /** [NotificationIconsViewData] indicating which icons to display in the view. */
+ val icons: Flow<NotificationIconsViewData> =
iconsInteractor.aodNotifs.map { entries ->
- IconsViewData(
+ NotificationIconsViewData(
visibleKeys = entries.mapNotNull { it.toIconInfo(it.aodIcon) },
)
}
- override val isolatedIcon: Flow<AnimatedValue<IconInfo?>> =
- flowOf(AnimatedValue.NotAnimating(null))
- override val isolatedIconLocation: Flow<Rect> = emptyFlow()
-
/** Is there an expanded pulse, are we animating in response? */
private fun isPulseExpandingAnimated(): Flow<AnimatedValue<Boolean>> {
return notificationsKeyguardInteractor.isPulseExpanding
@@ -182,11 +166,7 @@
.toAnimatedValueFlow()
}
- private class IconColorsImpl(override val tint: Int) : IconColors {
+ private class IconColorsImpl(override val tint: Int) : NotificationIconColors {
override fun staticDrawableColor(viewBounds: Rect, isColorized: Boolean): Int = tint
}
-
- companion object {
- @ColorInt private val DEFAULT_AOD_ICON_COLOR = Color.WHITE
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
index c6aabb7..1560106 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerShelfViewModel.kt
@@ -15,16 +15,9 @@
*/
package com.android.systemui.statusbar.notification.icon.ui.viewmodel
-import android.graphics.Rect
import com.android.systemui.statusbar.notification.icon.domain.interactor.NotificationIconsInteractor
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.ColorLookup
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconInfo
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconsViewData
-import com.android.systemui.util.ui.AnimatedValue
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.emptyFlow
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
/** View-model for the overflow row of notification icons displayed in the notification shade. */
@@ -32,19 +25,11 @@
@Inject
constructor(
interactor: NotificationIconsInteractor,
-) : NotificationIconContainerViewModel {
-
- override val animationsEnabled: Flow<Boolean> = flowOf(true)
- override val isDozing: Flow<AnimatedValue<Boolean>> = emptyFlow()
- override val isVisible: Flow<AnimatedValue<Boolean>> = emptyFlow()
- override val iconColors: Flow<ColorLookup> = emptyFlow()
- override val isolatedIcon: Flow<AnimatedValue<IconInfo?>> =
- flowOf(AnimatedValue.NotAnimating(null))
- override val isolatedIconLocation: Flow<Rect> = emptyFlow()
-
- override val iconsViewData: Flow<IconsViewData> =
+) {
+ /** [NotificationIconsViewData] indicating which icons to display in the view. */
+ val icons: Flow<NotificationIconsViewData> =
interactor.filteredNotifSet().map { entries ->
- IconsViewData(
+ NotificationIconsViewData(
visibleKeys = entries.mapNotNull { it.toIconInfo(it.shelfIcon) },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
index 4d14024..53631e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
@@ -22,10 +22,6 @@
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor
import com.android.systemui.statusbar.notification.icon.domain.interactor.StatusBarNotificationIconsInteractor
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.ColorLookup
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconColors
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconInfo
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconsViewData
import com.android.systemui.statusbar.phone.domain.interactor.DarkIconInteractor
import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.kotlin.sample
@@ -35,7 +31,6 @@
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
@@ -49,8 +44,10 @@
keyguardInteractor: KeyguardInteractor,
notificationsInteractor: ActiveNotificationsInteractor,
shadeInteractor: ShadeInteractor,
-) : NotificationIconContainerViewModel {
- override val animationsEnabled: Flow<Boolean> =
+) {
+
+ /** Are changes to the icon container animated? */
+ val animationsEnabled: Flow<Boolean> =
combine(
shadeInteractor.isShadeTouchable,
keyguardInteractor.isKeyguardShowing,
@@ -58,14 +55,15 @@
panelTouchesEnabled && !isKeyguardShowing
}
- override val iconColors: Flow<ColorLookup> =
+ /** The colors with which to display the notification icons. */
+ val iconColors: Flow<NotificationIconColorLookup> =
combine(
darkIconInteractor.tintAreas,
darkIconInteractor.tintColor,
// Included so that tints are re-applied after entries are changed.
notificationsInteractor.notifications,
) { areas, tint, _ ->
- ColorLookup { viewBounds: Rect ->
+ NotificationIconColorLookup { viewBounds: Rect ->
if (DarkIconDispatcher.isInAreas(areas, viewBounds)) {
IconColorsImpl(tint, areas)
} else {
@@ -74,20 +72,19 @@
}
}
- override val isDozing: Flow<AnimatedValue<Boolean>> = emptyFlow()
- override val isVisible: Flow<AnimatedValue<Boolean>> = emptyFlow()
-
- override val iconsViewData: Flow<IconsViewData> =
+ /** [NotificationIconsViewData] indicating which icons to display in the view. */
+ val icons: Flow<NotificationIconsViewData> =
iconsInteractor.statusBarNotifs.map { entries ->
- IconsViewData(
+ NotificationIconsViewData(
visibleKeys = entries.mapNotNull { it.toIconInfo(it.statusBarIcon) },
)
}
- override val isolatedIcon: Flow<AnimatedValue<IconInfo?>> =
+ /** An Icon to show "isolated" in the IconContainer. */
+ val isolatedIcon: Flow<AnimatedValue<NotificationIconInfo?>> =
headsUpIconInteractor.isolatedNotification
.pairwise(initialValue = null)
- .sample(combine(iconsViewData, shadeInteractor.shadeExpansion, ::Pair)) {
+ .sample(combine(icons, shadeInteractor.shadeExpansion, ::Pair)) {
(prev, isolatedNotif),
(iconsViewData, shadeExpansion),
->
@@ -105,13 +102,14 @@
}
.toAnimatedValueFlow()
- override val isolatedIconLocation: Flow<Rect> =
+ /** Location to show an isolated icon, if there is one. */
+ val isolatedIconLocation: Flow<Rect> =
headsUpIconInteractor.isolatedIconLocation.filterNotNull()
private class IconColorsImpl(
override val tint: Int,
private val areas: Collection<Rect>,
- ) : IconColors {
+ ) : NotificationIconColors {
override fun staticDrawableColor(viewBounds: Rect, isColorized: Boolean): Int {
return if (isColorized && DarkIconDispatcher.isInAreas(areas, viewBounds)) {
tint
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt
deleted file mode 100644
index a611323..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerViewModel.kt
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.statusbar.notification.icon.ui.viewmodel
-
-import android.graphics.Rect
-import android.graphics.drawable.Icon
-import androidx.collection.ArrayMap
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel.IconInfo
-import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
-import com.android.systemui.util.kotlin.mapValuesNotNullTo
-import com.android.systemui.util.ui.AnimatedValue
-import kotlinx.coroutines.flow.Flow
-
-/**
- * View-model for the row of notification icons displayed in the NotificationShelf, StatusBar, and
- * AOD.
- */
-interface NotificationIconContainerViewModel {
-
- /** Are changes to the icon container animated? */
- val animationsEnabled: Flow<Boolean>
-
- /** Should icons be rendered in "dozing" mode? */
- val isDozing: Flow<AnimatedValue<Boolean>>
-
- /** Is the icon container visible? */
- val isVisible: Flow<AnimatedValue<Boolean>>
-
- /** The colors with which to display the notification icons. */
- val iconColors: Flow<ColorLookup>
-
- /** [IconsViewData] indicating which icons to display in the view. */
- val iconsViewData: Flow<IconsViewData>
-
- /** An Icon to show "isolated" in the IconContainer. */
- val isolatedIcon: Flow<AnimatedValue<IconInfo?>>
-
- /** Location to show an isolated icon, if there is one. */
- val isolatedIconLocation: Flow<Rect>
-
- /**
- * Lookup the colors to use for the notification icons based on the bounds of the icon
- * container. A result of `null` indicates that no color changes should be applied.
- */
- fun interface ColorLookup {
- fun iconColors(viewBounds: Rect): IconColors?
- }
-
- /** Colors to apply to notification icons. */
- interface IconColors {
-
- /** A tint to apply to the icons. */
- val tint: Int
-
- /**
- * Returns the color to be applied to an icon, based on that icon's view bounds and whether
- * or not the notification icon is colorized.
- */
- fun staticDrawableColor(viewBounds: Rect, isColorized: Boolean): Int
- }
-
- /** Encapsulates the collection of notification icons present on the device. */
- data class IconsViewData(
- /** Icons that are visible in the container. */
- val visibleKeys: List<IconInfo> = emptyList(),
- /** Keys of icons that are "behind" the overflow dot. */
- val collapsedKeys: Set<String> = emptySet(),
- /** Whether the overflow dot should be shown regardless if [collapsedKeys] is empty. */
- val forceShowDot: Boolean = false,
- ) {
- /** The difference between two [IconsViewData]s. */
- data class Diff(
- /** Icons added in the newer dataset. */
- val added: List<IconInfo> = emptyList(),
- /** Icons removed from the older dataset. */
- val removed: List<String> = emptyList(),
- /**
- * Groups whose icon was replaced with a single new notification icon. The key of the
- * [Map] is the notification group key, and the value is the new icon.
- *
- * Specifically, this models a difference where the older dataset had notification
- * groups with a single icon in the set, and the newer dataset has a single, different
- * icon for the same group. A view binder can use this information for special
- * animations for this specific change.
- */
- val groupReplacements: Map<String, IconInfo> = emptyMap(),
- )
-
- companion object {
- /**
- * Returns an [IconsViewData.Diff] calculated from a [new] and [previous][prev]
- * [IconsViewData] state.
- */
- fun computeDifference(new: IconsViewData, prev: IconsViewData): Diff {
- val added: List<IconInfo> =
- new.visibleKeys.filter {
- it.notifKey !in prev.visibleKeys.asSequence().map { it.notifKey }
- }
- val removed: List<IconInfo> =
- prev.visibleKeys.filter {
- it.notifKey !in new.visibleKeys.asSequence().map { it.notifKey }
- }
- val groupsToShow: Set<IconGroupInfo> =
- new.visibleKeys.asSequence().map { it.groupInfo }.toSet()
- val replacements: ArrayMap<String, IconInfo> =
- removed
- .asSequence()
- .filter { keyToRemove -> keyToRemove.groupInfo in groupsToShow }
- .groupBy { it.groupInfo.groupKey }
- .mapValuesNotNullTo(ArrayMap()) { (_, vs) ->
- vs.takeIf { it.size == 1 }?.get(0)
- }
- return Diff(added, removed.map { it.notifKey }, replacements)
- }
- }
- }
-
- /** An Icon, and keys for unique identification. */
- data class IconInfo(
- val sourceIcon: Icon,
- val notifKey: String,
- val groupKey: String,
- )
-}
-
-/**
- * Construct an [IconInfo] out of an [ActiveNotificationModel], or return `null` if one cannot be
- * created due to missing information.
- */
-fun ActiveNotificationModel.toIconInfo(sourceIcon: Icon?): IconInfo? {
- return sourceIcon?.let {
- groupKey?.let { groupKey ->
- IconInfo(
- sourceIcon = sourceIcon,
- notifKey = key,
- groupKey = groupKey,
- )
- }
- }
-}
-
-private val IconInfo.groupInfo: IconGroupInfo
- get() = IconGroupInfo(sourceIcon, groupKey)
-
-private data class IconGroupInfo(
- val sourceIcon: Icon,
- val groupKey: String,
-) {
-
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
-
- other as IconGroupInfo
-
- if (groupKey != other.groupKey) return false
- return sourceIcon.sameAs(other.sourceIcon)
- }
-
- override fun hashCode(): Int {
- var result = groupKey.hashCode()
- result = 31 * result + sourceIcon.type.hashCode()
- when (sourceIcon.type) {
- Icon.TYPE_BITMAP,
- Icon.TYPE_ADAPTIVE_BITMAP -> {
- result = 31 * result + sourceIcon.bitmap.hashCode()
- }
- Icon.TYPE_DATA -> {
- result = 31 * result + sourceIcon.dataLength.hashCode()
- result = 31 * result + sourceIcon.dataOffset.hashCode()
- }
- Icon.TYPE_RESOURCE -> {
- result = 31 * result + sourceIcon.resId.hashCode()
- result = 31 * result + sourceIcon.resPackage.hashCode()
- }
- Icon.TYPE_URI,
- Icon.TYPE_URI_ADAPTIVE_BITMAP -> {
- result = 31 * result + sourceIcon.uriString.hashCode()
- }
- }
- return result
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconsViewData.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconsViewData.kt
new file mode 100644
index 0000000..867be84
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconsViewData.kt
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ *
+ */
+
+package com.android.systemui.statusbar.notification.icon.ui.viewmodel
+
+import android.graphics.drawable.Icon
+import androidx.collection.ArrayMap
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import com.android.systemui.util.kotlin.mapValuesNotNullTo
+
+/** Encapsulates the collection of notification icons present on the device. */
+data class NotificationIconsViewData(
+ /** Icons that are visible in the container. */
+ val visibleKeys: List<NotificationIconInfo> = emptyList(),
+ /** Keys of icons that are "behind" the overflow dot. */
+ val collapsedKeys: Set<String> = emptySet(),
+ /** Whether the overflow dot should be shown regardless if [collapsedKeys] is empty. */
+ val forceShowDot: Boolean = false,
+) {
+ /** The difference between two [NotificationIconsViewData]s. */
+ data class Diff(
+ /** Icons added in the newer dataset. */
+ val added: List<NotificationIconInfo> = emptyList(),
+ /** Icons removed from the older dataset. */
+ val removed: List<String> = emptyList(),
+ /**
+ * Groups whose icon was replaced with a single new notification icon. The key of the [Map]
+ * is the notification group key, and the value is the new icon.
+ *
+ * Specifically, this models a difference where the older dataset had notification groups
+ * with a single icon in the set, and the newer dataset has a single, different icon for the
+ * same group. A view binder can use this information for special animations for this
+ * specific change.
+ */
+ val groupReplacements: Map<String, NotificationIconInfo> = emptyMap(),
+ )
+
+ companion object {
+ /**
+ * Returns an [NotificationIconsViewData.Diff] calculated from a [new] and [previous][prev]
+ * [NotificationIconsViewData] state.
+ */
+ fun computeDifference(
+ new: NotificationIconsViewData,
+ prev: NotificationIconsViewData
+ ): Diff {
+ val added: List<NotificationIconInfo> =
+ new.visibleKeys.filter {
+ it.notifKey !in prev.visibleKeys.asSequence().map { it.notifKey }
+ }
+ val removed: List<NotificationIconInfo> =
+ prev.visibleKeys.filter {
+ it.notifKey !in new.visibleKeys.asSequence().map { it.notifKey }
+ }
+ val groupsToShow: Set<IconGroupInfo> =
+ new.visibleKeys.asSequence().map { it.groupInfo }.toSet()
+ val replacements: ArrayMap<String, NotificationIconInfo> =
+ removed
+ .asSequence()
+ .filter { keyToRemove -> keyToRemove.groupInfo in groupsToShow }
+ .groupBy { it.groupInfo.groupKey }
+ .mapValuesNotNullTo(ArrayMap()) { (_, vs) ->
+ vs.takeIf { it.size == 1 }?.get(0)
+ }
+ return Diff(added, removed.map { it.notifKey }, replacements)
+ }
+ }
+}
+
+/** An Icon, and keys for unique identification. */
+data class NotificationIconInfo(
+ val sourceIcon: Icon,
+ val notifKey: String,
+ val groupKey: String,
+)
+
+/**
+ * Construct an [NotificationIconInfo] out of an [ActiveNotificationModel], or return `null` if one
+ * cannot be created due to missing information.
+ */
+fun ActiveNotificationModel.toIconInfo(sourceIcon: Icon?): NotificationIconInfo? {
+ return sourceIcon?.let {
+ groupKey?.let { groupKey ->
+ NotificationIconInfo(
+ sourceIcon = sourceIcon,
+ notifKey = key,
+ groupKey = groupKey,
+ )
+ }
+ }
+}
+
+private val NotificationIconInfo.groupInfo: IconGroupInfo
+ get() = IconGroupInfo(sourceIcon, groupKey)
+
+private data class IconGroupInfo(
+ val sourceIcon: Icon,
+ val groupKey: String,
+) {
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as IconGroupInfo
+
+ if (groupKey != other.groupKey) return false
+ return sourceIcon.sameAs(other.sourceIcon)
+ }
+
+ override fun hashCode(): Int {
+ var result = groupKey.hashCode()
+ result = 31 * result + sourceIcon.type.hashCode()
+ when (sourceIcon.type) {
+ Icon.TYPE_BITMAP,
+ Icon.TYPE_ADAPTIVE_BITMAP -> {
+ result = 31 * result + sourceIcon.bitmap.hashCode()
+ }
+ Icon.TYPE_DATA -> {
+ result = 31 * result + sourceIcon.dataLength.hashCode()
+ result = 31 * result + sourceIcon.dataOffset.hashCode()
+ }
+ Icon.TYPE_RESOURCE -> {
+ result = 31 * result + sourceIcon.resId.hashCode()
+ result = 31 * result + sourceIcon.resPackage.hashCode()
+ }
+ Icon.TYPE_URI,
+ Icon.TYPE_URI_ADAPTIVE_BITMAP -> {
+ result = 31 * result + sourceIcon.uriString.hashCode()
+ }
+ }
+ return result
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
index 4a823a4..2fffd37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
@@ -239,11 +239,11 @@
})
}
- fun logNoPulsingNotificationHidden(entry: NotificationEntry) {
+ fun logNoPulsingNotificationHiddenOverride(entry: NotificationEntry) {
buffer.log(TAG, DEBUG, {
str1 = entry.logKey
}, {
- "No pulsing: notification hidden on lock screen: $str1"
+ "No pulsing: notification hidden on lock screen by override: $str1"
})
}
@@ -290,11 +290,11 @@
})
}
- fun keyguardHideNotification(entry: NotificationEntry) {
+ fun logNoAlertingNotificationHidden(entry: NotificationEntry) {
buffer.log(TAG, DEBUG, {
str1 = entry.logKey
}, {
- "Keyguard Hide Notification: $str1"
+ "No alerting: notification hidden on lock screen: $str1"
})
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
index 6ec9dbe..b0155f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
@@ -180,4 +180,9 @@
* Add a component that can suppress visual interruptions.
*/
void addSuppressor(NotificationInterruptSuppressor suppressor);
+
+ /**
+ * Remove a component that can suppress visual interruptions.
+ */
+ void removeSuppressor(NotificationInterruptSuppressor suppressor);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 3819843..301ddbf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -175,6 +175,11 @@
}
@Override
+ public void removeSuppressor(NotificationInterruptSuppressor suppressor) {
+ mSuppressors.remove(suppressor);
+ }
+
+ @Override
public boolean shouldBubbleUp(NotificationEntry entry) {
final StatusBarNotification sbn = entry.getSbn();
@@ -505,7 +510,7 @@
if (entry.getRanking().getLockscreenVisibilityOverride()
== Notification.VISIBILITY_PRIVATE) {
- if (log) mLogger.logNoPulsingNotificationHidden(entry);
+ if (log) mLogger.logNoPulsingNotificationHiddenOverride(entry);
return false;
}
@@ -536,7 +541,7 @@
}
if (mKeyguardNotificationVisibilityProvider.shouldHideNotification(entry)) {
- if (log) mLogger.keyguardHideNotification(entry);
+ if (log) mLogger.logNoAlertingNotificationHidden(entry);
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt
index ebdeded..d7f0baf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt
@@ -58,6 +58,10 @@
wrapped.addSuppressor(suppressor)
}
+ override fun removeLegacySuppressor(suppressor: NotificationInterruptSuppressor) {
+ wrapped.removeSuppressor(suppressor)
+ }
+
override fun makeUnloggedHeadsUpDecision(entry: NotificationEntry): Decision =
wrapped.checkHeadsUp(entry, /* log= */ false).let { DecisionImpl.of(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
index 454ba02..920bbe9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
@@ -60,6 +60,13 @@
fun addLegacySuppressor(suppressor: NotificationInterruptSuppressor)
/**
+ * Removes a [component][suppressor] that can suppress visual interruptions.
+ *
+ * @param[suppressor] the suppressor to remove
+ */
+ fun removeLegacySuppressor(suppressor: NotificationInterruptSuppressor)
+
+ /**
* Decides whether a [notification][entry] should display as heads-up or not, but does not log
* that decision.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
new file mode 100644
index 0000000..4ef80e3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.interruption
+
+import com.android.internal.logging.UiEventLogger.UiEventEnum
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+
+/**
+ * A reason why visual interruptions might be suppressed.
+ *
+ * @see VisualInterruptionCondition
+ * @see VisualInterruptionFilter
+ */
+enum class VisualInterruptionType {
+ /* HUN when awake */
+ PEEK,
+
+ /* HUN when dozing */
+ PULSE,
+
+ /* Bubble */
+ BUBBLE
+}
+
+/**
+ * A reason why visual interruptions might be suppressed.
+ *
+ * @see VisualInterruptionCondition
+ * @see VisualInterruptionFilter
+ */
+sealed interface VisualInterruptionSuppressor {
+ /** The type(s) of interruption that this suppresses. */
+ val types: Set<VisualInterruptionType>
+
+ /** A human-readable string to be logged to explain why this suppressed an interruption. */
+ val reason: String
+
+ /** An optional UiEvent ID to be recorded when this suppresses an interruption. */
+ val uiEventId: UiEventEnum?
+}
+
+/** A reason why visual interruptions might be suppressed regardless of the notification. */
+abstract class VisualInterruptionCondition(
+ override val types: Set<VisualInterruptionType>,
+ override val reason: String,
+ override val uiEventId: UiEventEnum? = null
+) : VisualInterruptionSuppressor {
+ /** @return true if these interruptions should be suppressed right now. */
+ abstract fun shouldSuppress(): Boolean
+}
+
+/** A reason why visual interruptions might be suppressed based on the notification. */
+abstract class VisualInterruptionFilter(
+ override val types: Set<VisualInterruptionType>,
+ override val reason: String,
+ override val uiEventId: UiEventEnum? = null
+) : VisualInterruptionSuppressor {
+ /**
+ * @param entry the notification to consider suppressing
+ * @return true if these interruptions should be suppressed for this notification right now
+ */
+ abstract fun shouldSuppress(entry: NotificationEntry): Boolean
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
index 2a7d087..d35e4b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
@@ -21,8 +21,6 @@
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.LegacyNotificationShelfControllerImpl
@@ -31,13 +29,12 @@
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ShelfNotificationIconViewStore
import com.android.systemui.statusbar.notification.row.ui.viewbinder.ActivatableNotificationViewBinder
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
-import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.NotificationIconAreaController
import com.android.systemui.statusbar.phone.NotificationIconContainer
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.statusbar.policy.ConfigurationController
import javax.inject.Inject
import kotlinx.coroutines.awaitCancellation
@@ -84,24 +81,18 @@
viewModel: NotificationShelfViewModel,
configuration: ConfigurationState,
configurationController: ConfigurationController,
- dozeParameters: DozeParameters,
falsingManager: FalsingManager,
- featureFlags: FeatureFlagsClassic,
notificationIconAreaController: NotificationIconAreaController,
- screenOffAnimationController: ScreenOffAnimationController,
shelfIconViewStore: ShelfNotificationIconViewStore,
) {
ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager)
shelf.apply {
- if (featureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+ if (NotificationIconContainerRefactor.isEnabled) {
NotificationIconContainerViewBinder.bind(
shelfIcons,
viewModel.icons,
configuration,
configurationController,
- dozeParameters,
- featureFlags,
- screenOffAnimationController,
shelfIconViewStore,
)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index b770b83..21efd63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -217,8 +217,6 @@
private final NotificationDismissibilityProvider mDismissibilityProvider;
private final ActivityStarter mActivityStarter;
private final ConfigurationState mConfigurationState;
- private final DozeParameters mDozeParameters;
- private final ScreenOffAnimationController mScreenOffAnimationController;
private final ShelfNotificationIconViewStore mShelfIconViewStore;
private View mLongPressedView;
@@ -683,8 +681,7 @@
NotificationDismissibilityProvider dismissibilityProvider,
ActivityStarter activityStarter,
SplitShadeStateController splitShadeStateController,
- ConfigurationState configurationState, DozeParameters dozeParameters,
- ScreenOffAnimationController screenOffAnimationController,
+ ConfigurationState configurationState,
ShelfNotificationIconViewStore shelfIconViewStore) {
mView = view;
mKeyguardTransitionRepo = keyguardTransitionRepo;
@@ -736,8 +733,6 @@
mDismissibilityProvider = dismissibilityProvider;
mActivityStarter = activityStarter;
mConfigurationState = configurationState;
- mDozeParameters = dozeParameters;
- mScreenOffAnimationController = screenOffAnimationController;
mShelfIconViewStore = shelfIconViewStore;
mView.passSplitShadeStateController(splitShadeStateController);
updateResources();
@@ -848,8 +843,8 @@
mViewModel.ifPresent(
vm -> NotificationListViewBinder
.bind(mView, vm, mConfigurationState, mConfigurationController,
- mDozeParameters, mFalsingManager, mFeatureFlags,
- mNotifIconAreaController, mScreenOffAnimationController,
+ mFalsingManager,
+ mNotifIconAreaController,
mShelfIconViewStore));
collectFlow(mView, mKeyguardTransitionRepo.getTransitions(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index 69b96fa..95b467f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -19,7 +19,6 @@
import android.view.LayoutInflater
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.common.ui.reinflateAndBindLatest
-import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
@@ -30,9 +29,7 @@
import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinder
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel
-import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.NotificationIconAreaController
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.traceSection
@@ -44,11 +41,8 @@
viewModel: NotificationListViewModel,
configuration: ConfigurationState,
configurationController: ConfigurationController,
- dozeParameters: DozeParameters,
falsingManager: FalsingManager,
- featureFlags: FeatureFlagsClassic,
iconAreaController: NotificationIconAreaController,
- screenOffAnimationController: ScreenOffAnimationController,
shelfIconViewStore: ShelfNotificationIconViewStore,
) {
val shelf =
@@ -59,11 +53,8 @@
viewModel.shelf,
configuration,
configurationController,
- dozeParameters,
falsingManager,
- featureFlags,
iconAreaController,
- screenOffAnimationController,
shelfIconViewStore,
)
view.setShelf(shelf)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 8295f65..897bb42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -206,6 +206,7 @@
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
@@ -236,8 +237,6 @@
import dalvik.annotation.optimization.NeverCompile;
-import dagger.Lazy;
-
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
@@ -249,6 +248,8 @@
import javax.inject.Named;
import javax.inject.Provider;
+import dagger.Lazy;
+
/**
* A class handling initialization and coordination between some of the key central surfaces in
* System UI: The notification shade, the keyguard (lockscreen), and the status bar.
@@ -809,8 +810,6 @@
mShadeExpansionStateManager.addExpansionListener(shadeExpansionListener);
shadeExpansionListener.onPanelExpansionChanged(currentState);
- mShadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
-
mActivityIntentHelper = new ActivityIntentHelper(mContext);
mActivityLaunchAnimator = activityLaunchAnimator;
@@ -1397,20 +1396,6 @@
}
}
- @VisibleForTesting
- void onShadeExpansionFullyChanged(Boolean isExpanded) {
- if (isExpanded && mStatusBarStateController.getState() != StatusBarState.KEYGUARD) {
- if (DEBUG) {
- Log.v(TAG, "clearing notification effects from Height");
- }
- clearNotificationEffects();
- }
-
- if (!isExpanded) {
- mRemoteInputManager.onPanelCollapsed();
- }
- }
-
@NonNull
@Override
public Lifecycle getLifecycle() {
@@ -2635,7 +2620,7 @@
!mDozeServiceHost.isPulsing(), mDeviceProvisionedController.isFrpActive());
mShadeSurface.setTouchAndAnimationDisabled(disabled);
- if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+ if (!NotificationIconContainerRefactor.isEnabled()) {
mNotificationIconAreaController.setAnimationsEnabled(!disabled);
}
}
@@ -3102,7 +3087,7 @@
}
// TODO: Bring these out of CentralSurfaces.
mUserInfoControllerImpl.onDensityOrFontScaleChanged();
- if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+ if (!NotificationIconContainerRefactor.isEnabled()) {
mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
}
}
@@ -3122,7 +3107,7 @@
if (mAmbientIndicationContainer instanceof AutoReinflateContainer) {
((AutoReinflateContainer) mAmbientIndicationContainer).inflateLayout();
}
- if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+ if (!NotificationIconContainerRefactor.isEnabled()) {
mNotificationIconAreaController.onThemeChanged();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 66341ba..600d4af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -38,7 +38,6 @@
import com.android.systemui.doze.DozeLog;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.flags.FeatureFlagsClassic;
-import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
import com.android.systemui.shade.NotificationShadeWindowViewController;
@@ -49,18 +48,18 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.util.Assert;
-import dagger.Lazy;
-
import java.util.ArrayList;
import javax.inject.Inject;
+import dagger.Lazy;
import kotlinx.coroutines.ExperimentalCoroutinesApi;
/**
@@ -178,7 +177,7 @@
void fireNotificationPulse(NotificationEntry entry) {
Runnable pulseSuppressedListener = () -> {
- if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+ if (NotificationIconContainerRefactor.isEnabled()) {
mHeadsUpManager.removeNotification(
entry.getKey(), /* releaseImmediately= */ true, /* animate= */ false);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 8fee5c0..beeee1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -27,7 +27,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ViewClippingUtil;
import com.android.systemui.flags.FeatureFlagsClassic;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
@@ -42,6 +41,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope;
@@ -179,7 +179,7 @@
mHeadsUpManager.addListener(this);
mView.setOnDrawingRectChangedListener(
() -> updateIsolatedIconLocation(true /* requireUpdate */));
- if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+ if (NotificationIconContainerRefactor.isEnabled()) {
updateIsolatedIconLocation(true);
}
mWakeUpCoordinator.addListener(this);
@@ -197,7 +197,7 @@
protected void onViewDetached() {
mHeadsUpManager.removeListener(this);
mView.setOnDrawingRectChangedListener(null);
- if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+ if (NotificationIconContainerRefactor.isEnabled()) {
mHeadsUpNotificationIconInteractor.setIsolatedIconLocation(null);
}
mWakeUpCoordinator.removeListener(this);
@@ -208,7 +208,7 @@
}
private void updateIsolatedIconLocation(boolean requireStateUpdate) {
- if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+ if (NotificationIconContainerRefactor.isEnabled()) {
mHeadsUpNotificationIconInteractor
.setIsolatedIconLocation(mView.getIconDrawingRect());
} else {
@@ -250,7 +250,7 @@
setShown(true);
animateIsolation = !isExpanded();
}
- if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+ if (NotificationIconContainerRefactor.isEnabled()) {
mHeadsUpNotificationIconInteractor.setIsolatedIconNotificationKey(
newEntry == null ? null : newEntry.getKey());
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index f4862c7..3a95e6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -34,7 +34,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.res.R;
-import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener;
@@ -48,6 +48,7 @@
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.OnHeadsUpPhoneListenerChange;
+import com.android.systemui.util.kotlin.JavaAdapter;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -105,7 +106,8 @@
///////////////////////////////////////////////////////////////////////////////////////////////
// Constructor:
@Inject
- public HeadsUpManagerPhone(@NonNull final Context context,
+ public HeadsUpManagerPhone(
+ @NonNull final Context context,
HeadsUpManagerLogger logger,
StatusBarStateController statusBarStateController,
KeyguardBypassController bypassController,
@@ -115,7 +117,8 @@
@Main Handler handler,
AccessibilityManagerWrapper accessibilityManagerWrapper,
UiEventLogger uiEventLogger,
- ShadeExpansionStateManager shadeExpansionStateManager) {
+ JavaAdapter javaAdapter,
+ ShadeInteractor shadeInteractor) {
super(context, logger, handler, accessibilityManagerWrapper, uiEventLogger);
Resources resources = mContext.getResources();
mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
@@ -136,8 +139,7 @@
updateResources();
}
});
-
- shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
+ javaAdapter.alwaysCollectFlow(shadeInteractor.isAnyExpanded(), this::onShadeOrQsExpanded);
}
public void setAnimationStateHandler(AnimationStateHandler handler) {
@@ -230,7 +232,7 @@
mTrackingHeadsUp = trackingHeadsUp;
}
- private void onShadeExpansionFullyChanged(Boolean isExpanded) {
+ private void onShadeOrQsExpanded(Boolean isExpanded) {
if (isExpanded != mIsExpanded) {
mIsExpanded = isExpanded;
if (isExpanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerModule.kt
index d1ddd51..ba69370 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerModule.kt
@@ -15,9 +15,8 @@
*/
package com.android.systemui.statusbar.phone
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconAreaControllerViewBinderWrapperImpl
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import dagger.Module
import dagger.Provides
import javax.inject.Provider
@@ -26,11 +25,10 @@
object NotificationIconAreaControllerModule {
@Provides
fun provideNotificationIconAreaControllerImpl(
- featureFlags: FeatureFlags,
legacyProvider: Provider<LegacyNotificationIconAreaControllerImpl>,
newProvider: Provider<NotificationIconAreaControllerViewBinderWrapperImpl>,
): NotificationIconAreaController =
- if (featureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+ if (NotificationIconContainerRefactor.isEnabled) {
newProvider.get()
} else {
legacyProvider.get()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 535f6ac..efb8e2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -40,10 +40,9 @@
import com.android.app.animation.Interpolators;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.settingslib.Utils;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.flags.RefactorFlag;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.statusbar.notification.stack.AnimationFilter;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ViewState;
@@ -133,9 +132,6 @@
}
}.setDuration(CONTENT_FADE_DURATION);
- private final RefactorFlag mIconContainerRefactorFlag =
- RefactorFlag.forView(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR);
-
/* Maximum number of icons on AOD when also showing overflow dot. */
private int mMaxIconsOnAod;
@@ -352,7 +348,7 @@
StatusBarIconView iconView = (StatusBarIconView) child;
Icon sourceIcon = iconView.getSourceIcon();
String groupKey = iconView.getNotification().getGroupKey();
- if (mIconContainerRefactorFlag.isEnabled()) {
+ if (NotificationIconContainerRefactor.isEnabled()) {
if (mReplacingIcons == null) {
return false;
}
@@ -695,18 +691,18 @@
}
public void setReplacingIconsLegacy(ArrayMap<String, ArrayList<StatusBarIcon>> replacingIcons) {
- mIconContainerRefactorFlag.assertInLegacyMode();
+ NotificationIconContainerRefactor.assertInLegacyMode();
mReplacingIconsLegacy = replacingIcons;
}
public void setReplacingIcons(ArrayMap<String, StatusBarIcon> replacingIcons) {
- if (mIconContainerRefactorFlag.isUnexpectedlyInLegacyMode()) return;
+ if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return;
mReplacingIcons = replacingIcons;
}
@Deprecated
public void showIconIsolated(StatusBarIconView icon, boolean animated) {
- mIconContainerRefactorFlag.assertInLegacyMode();
+ NotificationIconContainerRefactor.assertInLegacyMode();
if (animated) {
showIconIsolatedAnimated(icon, null);
} else {
@@ -716,14 +712,14 @@
public void showIconIsolatedAnimated(StatusBarIconView icon,
@Nullable Runnable onAnimationEnd) {
- if (mIconContainerRefactorFlag.isUnexpectedlyInLegacyMode()) return;
+ if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return;
mIsolatedIconForAnimation = icon != null ? icon : mIsolatedIcon;
mIsolatedIconAnimationEndRunnable = onAnimationEnd;
showIconIsolated(icon);
}
public void showIconIsolated(StatusBarIconView icon) {
- if (mIconContainerRefactorFlag.isUnexpectedlyInLegacyMode()) return;
+ if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return;
mIsolatedIcon = icon;
updateState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
index 4d9de09..fa6d279 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
@@ -3,12 +3,15 @@
import android.app.StatusBarManager
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.util.concurrency.DelayableExecutor
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
import java.io.PrintWriter
import javax.inject.Inject
@@ -25,14 +28,14 @@
*/
@SysUISingleton
class StatusBarHideIconsForBouncerManager @Inject constructor(
- private val commandQueue: CommandQueue,
- @Main private val mainExecutor: DelayableExecutor,
- statusBarWindowStateController: StatusBarWindowStateController,
- shadeExpansionStateManager: ShadeExpansionStateManager,
- dumpManager: DumpManager
+ @Application private val scope: CoroutineScope,
+ private val commandQueue: CommandQueue,
+ @Main private val mainExecutor: DelayableExecutor,
+ statusBarWindowStateController: StatusBarWindowStateController,
+ val shadeInteractor: ShadeInteractor,
+ dumpManager: DumpManager
) : Dumpable {
// State variables set by external classes.
- private var panelExpanded: Boolean = false
private var isOccluded: Boolean = false
private var bouncerShowing: Boolean = false
private var topAppHidesStatusBar: Boolean = false
@@ -49,10 +52,9 @@
statusBarWindowStateController.addListener {
state -> setStatusBarStateAndTriggerUpdate(state)
}
- shadeExpansionStateManager.addFullExpansionListener { isExpanded ->
- if (panelExpanded != isExpanded) {
- panelExpanded = isExpanded
- updateHideIconsForBouncer(animate = false)
+ scope.launch {
+ shadeInteractor.isAnyExpanded.collect {
+ updateHideIconsForBouncer(false)
}
}
}
@@ -101,7 +103,7 @@
topAppHidesStatusBar &&
isOccluded &&
(statusBarWindowHidden || bouncerShowing)
- val hideBecauseKeyguard = !panelExpanded && !isOccluded && bouncerShowing
+ val hideBecauseKeyguard = !isShadeOrQsExpanded() && !isOccluded && bouncerShowing
val shouldHideIconsForBouncer = hideBecauseApp || hideBecauseKeyguard
if (hideIconsForBouncer != shouldHideIconsForBouncer) {
hideIconsForBouncer = shouldHideIconsForBouncer
@@ -125,9 +127,13 @@
}
}
+ private fun isShadeOrQsExpanded(): Boolean {
+ return shadeInteractor.isAnyExpanded.value
+ }
+
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("---- State variables set externally ----")
- pw.println("panelExpanded=$panelExpanded")
+ pw.println("isShadeOrQsExpanded=${isShadeOrQsExpanded()}")
pw.println("isOccluded=$isOccluded")
pw.println("bouncerShowing=$bouncerShowing")
pw.println("topAppHideStatusBar=$topAppHidesStatusBar")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index ba73c10..6d8ec44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -39,6 +39,7 @@
import com.android.systemui.scene.domain.interactor.SceneInteractor;
import com.android.systemui.scene.shared.flag.SceneContainerFlags;
import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -86,8 +87,9 @@
ConfigurationController configurationController,
HeadsUpManager headsUpManager,
ShadeExpansionStateManager shadeExpansionStateManager,
+ ShadeInteractor shadeInteractor,
Provider<SceneInteractor> sceneInteractor,
- Provider<JavaAdapter> javaAdapter,
+ JavaAdapter javaAdapter,
SceneContainerFlags sceneContainerFlags,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
PrimaryBouncerInteractor primaryBouncerInteractor,
@@ -126,12 +128,12 @@
});
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
- shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
+ javaAdapter.alwaysCollectFlow(shadeInteractor.isAnyExpanded(), this::onShadeOrQsExpanded);
if (sceneContainerFlags.isEnabled()) {
- javaAdapter.get().alwaysCollectFlow(
+ javaAdapter.alwaysCollectFlow(
sceneInteractor.get().isVisible(),
- this::onShadeExpansionFullyChanged);
+ this::onShadeOrQsExpanded);
}
mPrimaryBouncerInteractor = primaryBouncerInteractor;
@@ -151,7 +153,7 @@
pw.println(mTouchableRegion);
}
- private void onShadeExpansionFullyChanged(Boolean isExpanded) {
+ private void onShadeOrQsExpanded(Boolean isExpanded) {
if (isExpanded != mIsStatusBarExpanded) {
mIsStatusBarExpanded = isExpanded;
if (isExpanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index e2a4714..3921e69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -44,7 +44,6 @@
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlagsClassic;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeExpansionStateManager;
@@ -59,12 +58,10 @@
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarNotificationIconViewStore;
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel;
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerViewModel;
-import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
@@ -86,6 +83,8 @@
import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener;
import com.android.systemui.util.settings.SecureSettings;
+import kotlin.Unit;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -96,8 +95,6 @@
import javax.inject.Inject;
-import kotlin.Unit;
-
/**
* Contains the collapsed status bar and handles hiding/showing based on disable flags
* and keyguard state. Also manages lifecycle to make sure the views it contains are being
@@ -155,12 +152,10 @@
private final DumpManager mDumpManager;
private final StatusBarWindowStateController mStatusBarWindowStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final NotificationIconContainerViewModel mStatusBarIconsViewModel;
+ private final NotificationIconContainerStatusBarViewModel mStatusBarIconsViewModel;
private final ConfigurationState mConfigurationState;
private final ConfigurationController mConfigurationController;
- private final DozeParameters mDozeParameters;
- private final ScreenOffAnimationController mScreenOffAnimationController;
- private final NotificationIconContainerViewBinder.IconViewStore mStatusBarIconViewStore;
+ private final StatusBarNotificationIconViewStore mStatusBarIconViewStore;
private final DemoModeController mDemoModeController;
private List<String> mBlockedIcons = new ArrayList<>();
@@ -252,8 +247,6 @@
NotificationIconContainerStatusBarViewModel statusBarIconsViewModel,
ConfigurationState configurationState,
ConfigurationController configurationController,
- DozeParameters dozeParameters,
- ScreenOffAnimationController screenOffAnimationController,
StatusBarNotificationIconViewStore statusBarIconViewStore,
DemoModeController demoModeController) {
mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
@@ -283,8 +276,6 @@
mStatusBarIconsViewModel = statusBarIconsViewModel;
mConfigurationState = configurationState;
mConfigurationController = configurationController;
- mDozeParameters = dozeParameters;
- mScreenOffAnimationController = screenOffAnimationController;
mStatusBarIconViewStore = statusBarIconViewStore;
mDemoModeController = demoModeController;
}
@@ -317,7 +308,7 @@
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mStatusBarWindowStateController.addListener(mStatusBarWindowStateListener);
- if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+ if (NotificationIconContainerRefactor.isEnabled()) {
mDemoModeController.addCallback(mDemoModeCallback);
}
}
@@ -326,7 +317,7 @@
public void onDestroy() {
super.onDestroy();
mStatusBarWindowStateController.removeListener(mStatusBarWindowStateListener);
- if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+ if (NotificationIconContainerRefactor.isEnabled()) {
mDemoModeController.removeCallback(mDemoModeCallback);
}
}
@@ -469,7 +460,7 @@
/** Initializes views related to the notification icon area. */
public void initNotificationIconArea() {
ViewGroup notificationIconArea = mStatusBar.requireViewById(R.id.notification_icon_area);
- if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR)) {
+ if (NotificationIconContainerRefactor.isEnabled()) {
mNotificationIconAreaInner =
LayoutInflater.from(getContext())
.inflate(R.layout.notification_icon_area, notificationIconArea, true);
@@ -480,9 +471,6 @@
mStatusBarIconsViewModel,
mConfigurationState,
mConfigurationController,
- mDozeParameters,
- mFeatureFlags,
- mScreenOffAnimationController,
mStatusBarIconViewStore);
} else {
mNotificationIconAreaInner =
@@ -606,7 +594,7 @@
// Hide notifications if the disable flag is set or we have an ongoing call.
if (disableNotifications || hasOngoingCall) {
- hideNotificationIconArea(animate);
+ hideNotificationIconArea(animate && !hasOngoingCall);
} else {
showNotificationIconArea(animate);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt
index eaae0f0..fa6ea4c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt
@@ -30,10 +30,13 @@
import android.util.Log
import android.view.View.MeasureSpec
import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.Dependency
import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.shade.ShadeLogger
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.util.ViewController
import com.android.systemui.util.time.SystemClock
import java.text.FieldPosition
@@ -83,7 +86,7 @@
class VariableDateViewController(
private val systemClock: SystemClock,
private val broadcastDispatcher: BroadcastDispatcher,
- private val shadeExpansionStateManager: ShadeExpansionStateManager,
+ private val shadeInteractor: ShadeInteractor,
private val shadeLogger: ShadeLogger,
private val timeTickHandler: Handler,
view: VariableDateView
@@ -174,8 +177,11 @@
broadcastDispatcher.registerReceiver(intentReceiver, filter,
HandlerExecutor(timeTickHandler), UserHandle.SYSTEM)
-
- shadeExpansionStateManager.addQsExpansionFractionListener(::onQsExpansionFractionChanged)
+ mView.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ shadeInteractor.qsExpansion.collect(::onQsExpansionFractionChanged)
+ }
+ }
post(::updateClock)
mView.onAttach(onMeasureListener)
}
@@ -183,7 +189,6 @@
override fun onViewDetached() {
dateFormat = null
mView.onAttach(null)
- shadeExpansionStateManager.removeQsExpansionFractionListener(::onQsExpansionFractionChanged)
broadcastDispatcher.unregisterReceiver(intentReceiver)
}
@@ -237,18 +242,18 @@
class Factory @Inject constructor(
private val systemClock: SystemClock,
private val broadcastDispatcher: BroadcastDispatcher,
- private val shadeExpansionStateManager: ShadeExpansionStateManager,
+ private val shadeInteractor: ShadeInteractor,
private val shadeLogger: ShadeLogger,
@Named(Dependency.TIME_TICK_HANDLER_NAME) private val handler: Handler
) {
fun create(view: VariableDateView): VariableDateViewController {
return VariableDateViewController(
- systemClock,
- broadcastDispatcher,
- shadeExpansionStateManager,
- shadeLogger,
- handler,
- view
+ systemClock,
+ broadcastDispatcher,
+ shadeInteractor,
+ shadeLogger,
+ handler,
+ view
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
index 153f3f7..ac40ba6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
@@ -21,6 +21,7 @@
import static com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR;
import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
import static com.android.systemui.flags.Flags.MIGRATE_KEYGUARD_STATUS_VIEW;
+import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.atLeast;
@@ -39,7 +40,6 @@
import com.android.systemui.common.ui.ConfigurationState;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
import com.android.systemui.log.LogBuffer;
@@ -58,6 +58,7 @@
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore;
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
@@ -143,6 +144,7 @@
public void setup() {
MockitoAnnotations.initMocks(this);
+ setFlagDefault(mSetFlagsRule, NotificationIconContainerRefactor.FLAG_NAME);
mFakeDateView.setTag(R.id.tag_smartspace_view, new Object());
mFakeWeatherView.setTag(R.id.tag_smartspace_view, new Object());
mFakeSmartspaceView.setTag(R.id.tag_smartspace_view, new Object());
@@ -173,7 +175,6 @@
when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
mExecutor = new FakeExecutor(new FakeSystemClock());
mFakeFeatureFlags = new FakeFeatureFlags();
- mFakeFeatureFlags.setDefault(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR);
mFakeFeatureFlags.set(FACE_AUTH_REFACTOR, false);
mFakeFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false);
mFakeFeatureFlags.set(MIGRATE_KEYGUARD_STATUS_VIEW, false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MockMagnificationAnimationCallback.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MockMagnificationAnimationCallback.java
deleted file mode 100644
index 89389b0..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MockMagnificationAnimationCallback.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2022 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.accessibility;
-
-import android.os.RemoteException;
-import android.view.accessibility.IRemoteMagnificationAnimationCallback;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicInteger;
-
-public class MockMagnificationAnimationCallback extends IRemoteMagnificationAnimationCallback.Stub {
-
- private final CountDownLatch mCountDownLatch;
- private final AtomicInteger mSuccessCount;
- private final AtomicInteger mFailedCount;
-
- MockMagnificationAnimationCallback(CountDownLatch countDownLatch) {
- mCountDownLatch = countDownLatch;
- mSuccessCount = new AtomicInteger();
- mFailedCount = new AtomicInteger();
- }
-
- public int getSuccessCount() {
- return mSuccessCount.get();
- }
-
- public int getFailedCount() {
- return mFailedCount.get();
- }
-
- @Override
- public void onResult(boolean success) throws RemoteException {
- if (success) {
- mSuccessCount.getAndIncrement();
- } else {
- mFailedCount.getAndIncrement();
- }
- // It should be put at the last line to avoid making CountDownLatch#await passed without
- // updating values.
- mCountDownLatch.countDown();
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index f15164e..284c273 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -35,7 +35,6 @@
import android.os.Handler;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
@@ -68,7 +67,6 @@
@LargeTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
@Rule
@@ -135,7 +133,9 @@
@After
public void tearDown() throws Exception {
- mController.deleteWindowMagnification();
+ mInstrumentation.runOnMainSync(() -> {
+ mController.deleteWindowMagnification();
+ });
}
@Test
@@ -170,8 +170,7 @@
final float targetCenterX = DEFAULT_CENTER_X + 100;
final float targetCenterY = DEFAULT_CENTER_Y + 100;
- mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
- targetCenterX, targetCenterY, null);
+ enableWindowMagnificationWithoutAnimation(targetScale, targetCenterX, targetCenterY);
verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
}
@@ -179,10 +178,11 @@
@Test
public void enableWindowMagnificationWithScaleOne_disabled_NoAnimationAndInvokeCallback()
throws RemoteException {
- mWindowMagnificationAnimationController.enableWindowMagnification(1,
+ enableWindowMagnificationAndWaitAnimating(
+ mWaitAnimationDuration, /* targetScale= */ 1.0f,
DEFAULT_CENTER_X, DEFAULT_CENTER_Y, mAnimationCallback);
- verify(mSpyController).enableWindowMagnificationInternal(1, DEFAULT_CENTER_X,
+ verify(mSpyController).enableWindowMagnificationInternal(1.0f, DEFAULT_CENTER_X,
DEFAULT_CENTER_Y, 0f, 0f);
verify(mAnimationCallback).onResult(true);
}
@@ -196,13 +196,15 @@
final float targetCenterX = DEFAULT_CENTER_X + 100;
final float targetCenterY = DEFAULT_CENTER_Y + 100;
- Mockito.reset(mSpyController);
- mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
- targetCenterX, targetCenterY, mAnimationCallback2);
- mCurrentScale.set(mController.getScale());
- mCurrentCenterX.set(mController.getCenterX());
- mCurrentCenterY.set(mController.getCenterY());
- advanceTimeBy(mWaitAnimationDuration);
+ resetMockObjects();
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
+ targetCenterX, targetCenterY, mAnimationCallback2);
+ mCurrentScale.set(mController.getScale());
+ mCurrentCenterX.set(mController.getCenterX());
+ mCurrentCenterY.set(mController.getCenterY());
+ advanceTimeBy(mWaitAnimationDuration);
+ });
verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
mScaleCaptor.capture(),
@@ -224,9 +226,8 @@
enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
mAnimationCallback);
- mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN,
+ enableWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, Float.NaN,
Float.NaN, Float.NaN, mAnimationCallback2);
- advanceTimeBy(mWaitAnimationDuration);
// The callback in 2nd enableWindowMagnification will return true
verify(mAnimationCallback2).onResult(true);
@@ -245,12 +246,14 @@
final float targetCenterY = DEFAULT_CENTER_Y + 100;
Mockito.reset(mSpyController);
- mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
- targetCenterX, targetCenterY, mAnimationCallback);
- mCurrentScale.set(mController.getScale());
- mCurrentCenterX.set(mController.getCenterX());
- mCurrentCenterY.set(mController.getCenterY());
- advanceTimeBy(mWaitAnimationDuration);
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
+ targetCenterX, targetCenterY, mAnimationCallback);
+ mCurrentScale.set(mController.getScale());
+ mCurrentCenterX.set(mController.getCenterX());
+ mCurrentCenterY.set(mController.getCenterY());
+ advanceTimeBy(mWaitAnimationDuration);
+ });
verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
mScaleCaptor.capture(),
@@ -279,12 +282,14 @@
final float targetCenterY = DEFAULT_CENTER_Y + 100;
Mockito.reset(mSpyController);
- mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
- targetCenterX, targetCenterY, mAnimationCallback);
- mCurrentScale.set(mController.getScale());
- mCurrentCenterX.set(mController.getCenterX());
- mCurrentCenterY.set(mController.getCenterY());
- advanceTimeBy(mWaitAnimationDuration);
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
+ targetCenterX, targetCenterY, mAnimationCallback);
+ mCurrentScale.set(mController.getScale());
+ mCurrentCenterX.set(mController.getCenterX());
+ mCurrentCenterY.set(mController.getCenterY());
+ advanceTimeBy(mWaitAnimationDuration);
+ });
verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
mScaleCaptor.capture(),
@@ -314,8 +319,7 @@
final float targetCenterY = DEFAULT_CENTER_Y + 100;
Mockito.reset(mSpyController);
- mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
- targetCenterX, targetCenterY, null);
+ enableWindowMagnificationWithoutAnimation(targetScale, targetCenterX, targetCenterY);
verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
assertEquals(WindowMagnificationAnimationController.STATE_DISABLED,
@@ -333,8 +337,7 @@
final float targetCenterY = DEFAULT_CENTER_Y + 100;
Mockito.reset(mSpyController);
- mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
- targetCenterX, targetCenterY, null);
+ enableWindowMagnificationWithoutAnimation(targetScale, targetCenterX, targetCenterY);
verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
verify(mAnimationCallback).onResult(false);
@@ -347,9 +350,8 @@
mAnimationCallback);
Mockito.reset(mSpyController);
- mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN,
- Float.NaN, Float.NaN, mAnimationCallback2);
- advanceTimeBy(mWaitAnimationDuration);
+ enableWindowMagnificationAndWaitAnimating(
+ mWaitAnimationDuration, Float.NaN, Float.NaN, Float.NaN, mAnimationCallback2);
verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(),
anyFloat());
@@ -368,20 +370,25 @@
final float targetCenterY = DEFAULT_CENTER_Y + 100;
Mockito.reset(mSpyController);
- mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
- targetCenterX, targetCenterY, mAnimationCallback2);
- mCurrentScale.set(mController.getScale());
- mCurrentCenterX.set(mController.getCenterX());
- mCurrentCenterY.set(mController.getCenterY());
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
+ targetCenterX, targetCenterY, mAnimationCallback2);
+ mCurrentScale.set(mController.getScale());
+ mCurrentCenterX.set(mController.getCenterX());
+ mCurrentCenterY.set(mController.getCenterY());
+ });
// Current spec shouldn't match given spec.
verify(mAnimationCallback2, never()).onResult(anyBoolean());
verify(mAnimationCallback).onResult(false);
- // ValueAnimator.reverse() could not work correctly with the AnimatorTestRule since it is
- // using SystemClock in reverse() (b/305731398). Therefore, we call end() on the animator
- // directly to verify the result of animation is correct instead of querying the animation
- // frame at a specific timing.
- mValueAnimator.end();
+
+ mInstrumentation.runOnMainSync(() -> {
+ // ValueAnimator.reverse() could not work correctly with the AnimatorTestRule since it
+ // is using SystemClock in reverse() (b/305731398). Therefore, we call end() on the
+ // animator directly to verify the result of animation is correct instead of querying
+ // the animation frame at a specific timing.
+ mValueAnimator.end();
+ });
verify(mSpyController).enableWindowMagnificationInternal(
mScaleCaptor.capture(),
@@ -410,8 +417,7 @@
final float targetCenterY = DEFAULT_CENTER_Y + 100;
Mockito.reset(mSpyController);
- mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
- targetCenterX, targetCenterY, null);
+ enableWindowMagnificationWithoutAnimation(targetScale, targetCenterX, targetCenterY);
verify(mAnimationCallback).onResult(false);
verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
@@ -425,9 +431,8 @@
mAnimationCallback);
Mockito.reset(mSpyController);
- mWindowMagnificationAnimationController.enableWindowMagnification(Float.NaN,
+ enableWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, Float.NaN,
Float.NaN, Float.NaN, mAnimationCallback2);
- advanceTimeBy(mWaitAnimationDuration);
verify(mSpyController, never()).enableWindowMagnificationInternal(anyFloat(), anyFloat(),
anyFloat());
@@ -445,12 +450,14 @@
final float targetCenterY = DEFAULT_CENTER_Y + 100;
Mockito.reset(mSpyController);
- mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
- targetCenterX, targetCenterY, mAnimationCallback2);
- mCurrentScale.set(mController.getScale());
- mCurrentCenterX.set(mController.getCenterX());
- mCurrentCenterY.set(mController.getCenterY());
- advanceTimeBy(mWaitAnimationDuration);
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
+ targetCenterX, targetCenterY, mAnimationCallback2);
+ mCurrentScale.set(mController.getScale());
+ mCurrentCenterX.set(mController.getCenterX());
+ mCurrentCenterY.set(mController.getCenterY());
+ advanceTimeBy(mWaitAnimationDuration);
+ });
verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
mScaleCaptor.capture(),
@@ -471,25 +478,26 @@
final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
Mockito.reset(mSpyController);
- mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
- windowBounds.exactCenterX(), windowBounds.exactCenterY(),
- offsetRatio, offsetRatio, mAnimationCallback);
- advanceTimeBy(mWaitAnimationDuration);
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
+ windowBounds.exactCenterX(), windowBounds.exactCenterY(),
+ offsetRatio, offsetRatio, mAnimationCallback);
+ advanceTimeBy(mWaitAnimationDuration);
+ });
- // We delay the time of verifying to wait for the measurement and layout of the view
- mHandler.postDelayed(() -> {
- final View attachedView = mWindowManager.getAttachedView();
- assertNotNull(attachedView);
- final Rect mirrorViewBound = new Rect();
- final View mirrorView = attachedView.findViewById(R.id.surface_view);
- assertNotNull(mirrorView);
- mirrorView.getBoundsOnScreen(mirrorViewBound);
+ // Wait for Rects update
+ waitForIdleSync();
+ final View attachedView = mWindowManager.getAttachedView();
+ assertNotNull(attachedView);
+ final Rect mirrorViewBound = new Rect();
+ final View mirrorView = attachedView.findViewById(R.id.surface_view);
+ assertNotNull(mirrorView);
+ mirrorView.getBoundsOnScreen(mirrorViewBound);
- assertEquals((int) (offsetRatio * mirrorViewBound.width() / 2),
- (int) (mirrorViewBound.exactCenterX() - windowBounds.exactCenterX()));
- assertEquals((int) (offsetRatio * mirrorViewBound.height() / 2),
- (int) (mirrorViewBound.exactCenterY() - windowBounds.exactCenterY()));
- }, 100);
+ assertEquals((int) (offsetRatio * mirrorViewBound.width() / 2),
+ (int) (mirrorViewBound.exactCenterX() - windowBounds.exactCenterX()));
+ assertEquals((int) (offsetRatio * mirrorViewBound.height() / 2),
+ (int) (mirrorViewBound.exactCenterY() - windowBounds.exactCenterY()));
}
@Test
@@ -498,9 +506,11 @@
final float targetCenterY = DEFAULT_CENTER_Y + 100;
enableWindowMagnificationWithoutAnimation();
- mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
- targetCenterX, targetCenterY, mAnimationCallback);
- advanceTimeBy(mWaitAnimationDuration);
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
+ targetCenterX, targetCenterY, mAnimationCallback);
+ advanceTimeBy(mWaitAnimationDuration);
+ });
verify(mAnimationCallback).onResult(true);
verify(mAnimationCallback, never()).onResult(false);
@@ -512,15 +522,17 @@
throws RemoteException {
enableWindowMagnificationWithoutAnimation();
- mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
- DEFAULT_CENTER_X + 10, DEFAULT_CENTER_Y + 10, mAnimationCallback);
- mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
- DEFAULT_CENTER_X + 20, DEFAULT_CENTER_Y + 20, mAnimationCallback);
- mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
- DEFAULT_CENTER_X + 30, DEFAULT_CENTER_Y + 30, mAnimationCallback);
- mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
- DEFAULT_CENTER_X + 40, DEFAULT_CENTER_Y + 40, mAnimationCallback2);
- advanceTimeBy(mWaitAnimationDuration);
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
+ DEFAULT_CENTER_X + 10, DEFAULT_CENTER_Y + 10, mAnimationCallback);
+ mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
+ DEFAULT_CENTER_X + 20, DEFAULT_CENTER_Y + 20, mAnimationCallback);
+ mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
+ DEFAULT_CENTER_X + 30, DEFAULT_CENTER_Y + 30, mAnimationCallback);
+ mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
+ DEFAULT_CENTER_X + 40, DEFAULT_CENTER_Y + 40, mAnimationCallback2);
+ advanceTimeBy(mWaitAnimationDuration);
+ });
// only the last one callback will return true
verify(mAnimationCallback2).onResult(true);
@@ -538,9 +550,11 @@
enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
mAnimationCallback);
- mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
- targetCenterX, targetCenterY, mAnimationCallback2);
- advanceTimeBy(mWaitAnimationDuration);
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
+ targetCenterX, targetCenterY, mAnimationCallback2);
+ advanceTimeBy(mWaitAnimationDuration);
+ });
// The callback in moveWindowMagnifierToPosition will return true
verify(mAnimationCallback2).onResult(true);
@@ -556,9 +570,11 @@
enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
mAnimationCallback);
- mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
- Float.NaN, Float.NaN, mAnimationCallback2);
- advanceTimeBy(mWaitAnimationDuration);
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
+ Float.NaN, Float.NaN, mAnimationCallback2);
+ advanceTimeBy(mWaitAnimationDuration);
+ });
// The callback in moveWindowMagnifierToPosition will return true
verify(mAnimationCallback2).onResult(true);
@@ -584,6 +600,7 @@
throws RemoteException {
enableWindowMagnificationWithoutAnimation();
+ resetMockObjects();
deleteWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, mAnimationCallback);
verify(mSpyController, atLeast(2)).enableWindowMagnificationInternal(
@@ -625,16 +642,18 @@
mAnimationCallback);
Mockito.reset(mSpyController);
- mWindowMagnificationAnimationController.deleteWindowMagnification(
- mAnimationCallback2);
- mCurrentScale.set(mController.getScale());
- mCurrentCenterX.set(mController.getCenterX());
- mCurrentCenterY.set(mController.getCenterY());
- // ValueAnimator.reverse() could not work correctly with the AnimatorTestRule since it is
- // using SystemClock in reverse() (b/305731398). Therefore, we call end() on the animator
- // directly to verify the result of animation is correct instead of querying the animation
- // frame at a specific timing.
- mValueAnimator.end();
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationAnimationController.deleteWindowMagnification(
+ mAnimationCallback2);
+ mCurrentScale.set(mController.getScale());
+ mCurrentCenterX.set(mController.getCenterX());
+ mCurrentCenterY.set(mController.getCenterY());
+ // ValueAnimator.reverse() could not work correctly with the AnimatorTestRule since it
+ // is using SystemClock in reverse() (b/305731398). Therefore, we call end() on the
+ // animator directly to verify the result of animation is correct instead of querying
+ // the animation frame at a specific timing.
+ mValueAnimator.end();
+ });
verify(mSpyController).enableWindowMagnificationInternal(
mScaleCaptor.capture(),
@@ -661,7 +680,7 @@
mAnimationCallback);
Mockito.reset(mSpyController);
- mWindowMagnificationAnimationController.deleteWindowMagnification(null);
+ deleteWindowMagnificationWithoutAnimation();
verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
verify(mAnimationCallback).onResult(false);
@@ -673,6 +692,7 @@
deleteWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
mAnimationCallback);
+ resetMockObjects();
deleteWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, mAnimationCallback2);
verify(mSpyController).enableWindowMagnificationInternal(
@@ -710,8 +730,7 @@
final float offsetY =
(float) Math.ceil(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE)
+ 1.0f;
-
- mController.moveWindowMagnifier(offsetX, offsetY);
+ mInstrumentation.runOnMainSync(()-> mController.moveWindowMagnifier(offsetX, offsetY));
verify(mSpyController).moveWindowMagnifier(offsetX, offsetY);
verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y + offsetY);
@@ -726,8 +745,8 @@
final float offsetY =
(float) Math.floor(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE)
- 1.0f;
-
- mController.moveWindowMagnifier(offsetX, offsetY);
+ mInstrumentation.runOnMainSync(() ->
+ mController.moveWindowMagnifier(offsetX, offsetY));
verify(mSpyController).moveWindowMagnifier(offsetX, offsetY);
verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + offsetX, DEFAULT_CENTER_Y);
@@ -742,8 +761,10 @@
(float) Math.ceil(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE);
// while diagonal scrolling enabled,
// should move with both offsetX and offsetY without regrading offsetY/offsetX
- mController.setDiagonalScrolling(true);
- mController.moveWindowMagnifier(offsetX, offsetY);
+ mInstrumentation.runOnMainSync(() -> {
+ mController.setDiagonalScrolling(true);
+ mController.moveWindowMagnifier(offsetX, offsetY);
+ });
verify(mSpyController).moveWindowMagnifier(offsetX, offsetY);
verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + offsetX, DEFAULT_CENTER_Y + offsetY);
@@ -755,9 +776,11 @@
final float targetCenterY = DEFAULT_CENTER_Y + 100;
enableWindowMagnificationWithoutAnimation();
- mController.moveWindowMagnifierToPosition(targetCenterX, targetCenterY,
- mAnimationCallback);
- advanceTimeBy(mWaitAnimationDuration);
+ mInstrumentation.runOnMainSync(() -> {
+ mController.moveWindowMagnifierToPosition(targetCenterX, targetCenterY,
+ mAnimationCallback);
+ advanceTimeBy(mWaitAnimationDuration);
+ });
verifyFinalSpec(DEFAULT_SCALE, targetCenterX, targetCenterY);
}
@@ -774,24 +797,49 @@
}
private void enableWindowMagnificationWithoutAnimation() {
- Mockito.reset(mSpyController);
- mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
- DEFAULT_CENTER_X, DEFAULT_CENTER_Y, null);
+ enableWindowMagnificationWithoutAnimation(
+ DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
+ }
+
+ private void enableWindowMagnificationWithoutAnimation(
+ float targetScale, float targetCenterX, float targetCenterY) {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationAnimationController.enableWindowMagnification(
+ targetScale, targetCenterX, targetCenterY, null);
+ });
}
private void enableWindowMagnificationAndWaitAnimating(long duration,
@Nullable IRemoteMagnificationAnimationCallback callback) {
- Mockito.reset(mSpyController);
- mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
- DEFAULT_CENTER_X, DEFAULT_CENTER_Y, callback);
- advanceTimeBy(duration);
+ enableWindowMagnificationAndWaitAnimating(
+ duration, DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y, callback);
+ }
+
+ private void enableWindowMagnificationAndWaitAnimating(
+ long duration,
+ float targetScale,
+ float targetCenterX,
+ float targetCenterY,
+ @Nullable IRemoteMagnificationAnimationCallback callback) {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationAnimationController.enableWindowMagnification(
+ targetScale, targetCenterX, targetCenterY, callback);
+ advanceTimeBy(duration);
+ });
+ }
+
+ private void deleteWindowMagnificationWithoutAnimation() {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationAnimationController.deleteWindowMagnification(null);
+ });
}
private void deleteWindowMagnificationAndWaitAnimating(long duration,
@Nullable IRemoteMagnificationAnimationCallback callback) {
- resetMockObjects();
- mWindowMagnificationAnimationController.deleteWindowMagnification(callback);
- advanceTimeBy(duration);
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationAnimationController.deleteWindowMagnification(callback);
+ advanceTimeBy(duration);
+ });
}
private void verifyStartValue(ArgumentCaptor<Float> captor, float startValue) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 86ae517..06421db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -45,6 +45,7 @@
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
@@ -53,6 +54,7 @@
import android.animation.ValueAnimator;
import android.annotation.IdRes;
+import android.annotation.Nullable;
import android.app.Instrumentation;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -89,6 +91,7 @@
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.AnimatorTestRule;
import com.android.systemui.model.SysUiState;
import com.android.systemui.res.R;
import com.android.systemui.settings.FakeDisplayTracker;
@@ -102,6 +105,7 @@
import org.junit.Assume;
import org.junit.Before;
import org.junit.Ignore;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
@@ -111,8 +115,6 @@
import org.mockito.MockitoAnnotations;
import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@LargeTest
@@ -120,12 +122,10 @@
@RunWith(AndroidTestingRunner.class)
public class WindowMagnificationControllerTest extends SysuiTestCase {
+ @Rule
+ public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule();
+
private static final int LAYOUT_CHANGE_TIMEOUT_MS = 5000;
- // The duration couldn't too short, otherwise the animation check on bounce effect
- // won't work in expectation. (b/299537784)
- private static final int BOUNCE_EFFECT_DURATION_MS = 2000;
- private static final long ANIMATION_DURATION_MS = 300;
- private final long mWaitingAnimationPeriod = 2 * ANIMATION_DURATION_MS;
@Mock
private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
@Mock
@@ -134,11 +134,16 @@
private WindowMagnifierCallback mWindowMagnifierCallback;
@Mock
IRemoteMagnificationAnimationCallback mAnimationCallback;
+ @Mock
+ IRemoteMagnificationAnimationCallback mAnimationCallback2;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
@Mock
private SecureSettings mSecureSettings;
+ private long mWaitAnimationDuration;
+ private long mWaitBounceEffectDuration;
+
private Handler mHandler;
private TestableWindowManager mWindowManager;
private SysUiState mSysUiState;
@@ -194,6 +199,13 @@
mResources.getConfiguration().orientation = ORIENTATION_PORTRAIT;
}
+ // Using the animation duration in WindowMagnificationAnimationController for testing.
+ mWaitAnimationDuration = mResources.getInteger(
+ com.android.internal.R.integer.config_longAnimTime);
+ // Using the bounce effect duration in WindowMagnificationController for testing.
+ mWaitBounceEffectDuration = mResources.getInteger(
+ com.android.internal.R.integer.config_shortAnimTime);
+
mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
mContext, mValueAnimator);
mWindowMagnificationController =
@@ -208,7 +220,6 @@
mSysUiState,
() -> mWindowSessionSpy,
mSecureSettings);
- mWindowMagnificationController.setBounceEffectDuration(BOUNCE_EFFECT_DURATION_MS);
verify(mMirrorWindowControl).setWindowDelegate(
any(MirrorWindowControl.MirrorWindowDelegate.class));
@@ -281,9 +292,9 @@
/* magnificationFrameOffsetRatioY= */ 0,
Mockito.mock(IRemoteMagnificationAnimationCallback.class));
});
+ advanceTimeBy(LAYOUT_CHANGE_TIMEOUT_MS);
- verify(mSfVsyncFrameProvider,
- timeout(LAYOUT_CHANGE_TIMEOUT_MS).atLeast(2)).postFrameCallback(any());
+ verify(mSfVsyncFrameProvider, atLeast(2)).postFrameCallback(any());
}
@Test
@@ -401,14 +412,12 @@
@Test
public void moveWindowMagnifierToPositionWithAnimation_expectedValuesAndInvokeCallback()
- throws InterruptedException {
+ throws RemoteException {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
Float.NaN, 0, 0, null);
});
- final CountDownLatch countDownLatch = new CountDownLatch(1);
- final MockMagnificationAnimationCallback animationCallback =
- new MockMagnificationAnimationCallback(countDownLatch);
+
final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
.onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
@@ -417,12 +426,12 @@
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.moveWindowMagnifierToPosition(
- targetCenterX, targetCenterY, animationCallback);
+ targetCenterX, targetCenterY, mAnimationCallback);
});
+ advanceTimeBy(mWaitAnimationDuration);
- assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS));
- assertEquals(1, animationCallback.getSuccessCount());
- assertEquals(0, animationCallback.getFailedCount());
+ verify(mAnimationCallback, times(1)).onResult(eq(true));
+ verify(mAnimationCallback, never()).onResult(eq(false));
verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
.onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
assertEquals(mWindowMagnificationController.getCenterX(),
@@ -435,14 +444,12 @@
@Test
public void moveWindowMagnifierToPositionMultipleTimes_expectedValuesAndInvokeCallback()
- throws InterruptedException {
+ throws RemoteException {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
Float.NaN, 0, 0, null);
});
- final CountDownLatch countDownLatch = new CountDownLatch(4);
- final MockMagnificationAnimationCallback animationCallback =
- new MockMagnificationAnimationCallback(countDownLatch);
+
final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
.onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
@@ -451,20 +458,20 @@
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.moveWindowMagnifierToPosition(
- centerX + 10, centerY + 10, animationCallback);
+ centerX + 10, centerY + 10, mAnimationCallback);
mWindowMagnificationController.moveWindowMagnifierToPosition(
- centerX + 20, centerY + 20, animationCallback);
+ centerX + 20, centerY + 20, mAnimationCallback);
mWindowMagnificationController.moveWindowMagnifierToPosition(
- centerX + 30, centerY + 30, animationCallback);
+ centerX + 30, centerY + 30, mAnimationCallback);
mWindowMagnificationController.moveWindowMagnifierToPosition(
- centerX + 40, centerY + 40, animationCallback);
+ centerX + 40, centerY + 40, mAnimationCallback2);
});
+ advanceTimeBy(mWaitAnimationDuration);
- assertTrue(countDownLatch.await(mWaitingAnimationPeriod, TimeUnit.MILLISECONDS));
// only the last one callback will return true
- assertEquals(1, animationCallback.getSuccessCount());
+ verify(mAnimationCallback2).onResult(eq(true));
// the others will return false
- assertEquals(3, animationCallback.getFailedCount());
+ verify(mAnimationCallback, times(3)).onResult(eq(false));
verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
.onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
assertEquals(mWindowMagnificationController.getCenterX(),
@@ -1078,27 +1085,16 @@
final View mirrorView = mWindowManager.getAttachedView();
- final long timeout = SystemClock.uptimeMillis() + 5000;
final AtomicDouble maxScaleX = new AtomicDouble();
- final Runnable onAnimationFrame = new Runnable() {
- @Override
- public void run() {
- // For some reason the fancy way doesn't compile...
-// maxScaleX.getAndAccumulate(mirrorView.getScaleX(), Math::max);
- final double oldMax = maxScaleX.get();
- final double newMax = Math.max(mirrorView.getScaleX(), oldMax);
- assertTrue(maxScaleX.compareAndSet(oldMax, newMax));
+ advanceTimeBy(mWaitBounceEffectDuration, /* runnableOnEachRefresh= */ () -> {
+ // For some reason the fancy way doesn't compile...
+ // maxScaleX.getAndAccumulate(mirrorView.getScaleX(), Math::max);
+ final double oldMax = maxScaleX.get();
+ final double newMax = Math.max(mirrorView.getScaleX(), oldMax);
+ assertTrue(maxScaleX.compareAndSet(oldMax, newMax));
+ });
- if (SystemClock.uptimeMillis() < timeout) {
- mirrorView.postOnAnimation(this);
- }
- }
- };
- mirrorView.postOnAnimation(onAnimationFrame);
-
- waitForIdleSync();
-
- ReferenceTestUtils.waitForCondition(() -> maxScaleX.get() > 1.0);
+ assertTrue(maxScaleX.get() > 1.0);
}
@Test
@@ -1455,4 +1451,23 @@
return newRotation;
}
+ // advance time based on the device frame refresh rate
+ private void advanceTimeBy(long timeDelta) {
+ advanceTimeBy(timeDelta, /* runnableOnEachRefresh= */ null);
+ }
+
+ // advance time based on the device frame refresh rate, and trigger runnable on each refresh
+ private void advanceTimeBy(long timeDelta, @Nullable Runnable runnableOnEachRefresh) {
+ final float frameRate = mContext.getDisplay().getRefreshRate();
+ final int timeSlot = (int) (1000 / frameRate);
+ int round = (int) Math.ceil((double) timeDelta / timeSlot);
+ for (; round >= 0; round--) {
+ mInstrumentation.runOnMainSync(() -> {
+ mAnimatorTestRule.advanceTimeBy(timeSlot);
+ if (runnableOnEachRefresh != null) {
+ runnableOnEachRefresh.run();
+ }
+ });
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
index 1b2fc93d..2f4fc96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
@@ -67,6 +67,7 @@
isAttachedToWindow = { isAttachedToWindow },
onLongPressDetected = onLongPressDetected,
onSingleTapDetected = onSingleTapDetected,
+ longPressDuration = { ViewConfiguration.getLongPressTimeout().toLong() }
)
underTest.isLongPressHandlingEnabled = true
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/SetFlagsRuleExtensions.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/SetFlagsRuleExtensions.kt
index e16b8d4..f4d2cfd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/SetFlagsRuleExtensions.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/SetFlagsRuleExtensions.kt
@@ -19,15 +19,15 @@
import android.platform.test.flag.junit.SetFlagsRule
/**
- * Set the given flag's value to the real value for the current build configuration.
- * This prevents test code from crashing because it is reading an unspecified flag value.
+ * Set the given flag's value to the real value for the current build configuration. This prevents
+ * test code from crashing because it is reading an unspecified flag value.
*
- * REMINDER: You should always test your code with your flag in both configurations, so
- * generally you should be explicitly enabling or disabling your flag. This method is for
- * situations where the flag needs to be read (e.g. in the class constructor), but its value
- * shouldn't affect the actual test cases. In those cases, it's mildly safer to use this method
- * than to hard-code `false` or `true` because then at least if you're wrong, and the flag value
- * *does* matter, you'll notice when the flag is flipped and tests start failing.
+ * REMINDER: You should always test your code with your flag in both configurations, so generally
+ * you should be explicitly enabling or disabling your flag. This method is for situations where the
+ * flag needs to be read (e.g. in the class constructor), but its value shouldn't affect the actual
+ * test cases. In those cases, it's mildly safer to use this method than to hard-code `false` or
+ * `true` because then at least if you're wrong, and the flag value *does* matter, you'll notice
+ * when the flag is flipped and tests start failing.
*/
fun SetFlagsRule.setFlagDefault(flagName: String) {
if (getFlagDefault(flagName)) {
@@ -37,6 +37,19 @@
}
}
+/**
+ * Set the given flag to an explicit value, or, if null, to the real value for the current build
+ * configuration. This allows for convenient provisioning in tests where certain tests don't care
+ * what the value is (`setFlagValue(FLAG_FOO, null)`), and others want an explicit value.
+ */
+fun SetFlagsRule.setFlagValue(name: String, value: Boolean?) {
+ when (value) {
+ null -> setFlagDefault(name)
+ true -> enableFlags(name)
+ false -> disableFlags(name)
+ }
+}
+
// NOTE: This code uses reflection to gain access to private members of aconfig generated
// classes (in the same way SetFlagsRule does internally) because this is the only way to get
// at the underlying information and read the current value of the flag.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt
index fd1e2c7..da448aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt
@@ -74,6 +74,15 @@
}
@Test
+ fun notifyProjectionCancelled_forwardsToServiceWithMetricsValue() {
+ val hostUid = 123
+
+ logger.notifyProjectionRequestCancelled(hostUid)
+
+ verify(service).notifyPermissionRequestCancelled(hostUid)
+ }
+
+ @Test
fun notifyAppSelectorDisplayed_forwardsToService() {
val hostUid = 654
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
index 5255f71..44798ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
@@ -4,7 +4,6 @@
import android.os.UserHandle
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED as STATE_APP_SELECTOR_DISPLAYED
import com.android.systemui.SysuiTestCase
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.mediaprojection.appselector.data.RecentTask
@@ -214,7 +213,7 @@
@Test
fun init_firstStart_logsAppSelectorDisplayed() {
val hostUid = 123456789
- val controller = createController(isFirstStart = true, hostUid)
+ val controller = createController(isFirstStart = true, hostUid)
controller.init()
@@ -231,6 +230,15 @@
verify(logger, never()).notifyAppSelectorDisplayed(hostUid)
}
+ @Test
+ fun onSelectorDismissed_logsProjectionRequestCancelled() {
+ val hostUid = 123
+
+ createController(hostUid = hostUid).onSelectorDismissed()
+
+ verify(logger).notifyProjectionRequestCancelled(hostUid)
+ }
+
private fun givenCaptureAllowed(isAllow: Boolean) {
whenever(policyResolver.isScreenCaptureAllowed(any(), any())).thenReturn(isAllow)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
new file mode 100644
index 0000000..682b2d0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 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.qs.tiles.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class QSTileConfigProviderTest : SysuiTestCase() {
+
+ private val underTest =
+ QSTileConfigProviderImpl(
+ mapOf(VALID_SPEC.spec to QSTileConfigTestBuilder.build { tileSpec = VALID_SPEC })
+ )
+
+ @Test
+ fun providerReturnsConfig() {
+ assertThat(underTest.getConfig(VALID_SPEC.spec)).isNotNull()
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun throwsForInvalidSpec() {
+ underTest.getConfig(INVALID_SPEC.spec)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun validatesSpecUponCreation() {
+ QSTileConfigProviderImpl(
+ mapOf(VALID_SPEC.spec to QSTileConfigTestBuilder.build { tileSpec = INVALID_SPEC })
+ )
+ }
+
+ private companion object {
+
+ val VALID_SPEC = TileSpec.create("valid_tile_spec")
+ val INVALID_SPEC = TileSpec.create("invalid_tile_spec")
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
index 1a45535..9bf4a75 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
@@ -20,12 +20,10 @@
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
-import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor
import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
@@ -93,7 +91,7 @@
config: QSTileConfig = TEST_QS_TILE_CONFIG,
): QSTileViewModel =
QSTileViewModelImpl(
- { config },
+ config,
{ fakeQSTileUserActionInteractor },
{ fakeQSTileDataInteractor },
{
@@ -114,12 +112,6 @@
private companion object {
- val TEST_QS_TILE_CONFIG =
- QSTileConfig(
- TileSpec.create("default"),
- Icon.Resource(0, null),
- 0,
- InstanceId.fakeInstanceId(0),
- )
+ val TEST_QS_TILE_CONFIG = QSTileConfigTestBuilder.build {}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index c439cfe..49049bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.screenrecord;
+import static android.os.Process.myUid;
+
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertFalse;
@@ -31,8 +33,8 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.os.Looper;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
@@ -60,6 +62,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
/**
* Tests for exception handling and bitmap configuration in adding smart actions to Screenshot
* Notification.
@@ -117,10 +120,6 @@
// starting, and notifies listeners.
@Test
public void testCancelCountdown() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
-
mController.startCountdown(100, 10, null, null);
assertTrue(mController.isStarting());
@@ -137,10 +136,6 @@
// Test that when recording is started, the start intent is sent and listeners are notified.
@Test
public void testStartRecording() throws PendingIntent.CanceledException {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
-
PendingIntent startIntent = Mockito.mock(PendingIntent.class);
mController.startCountdown(0, 0, startIntent, null);
@@ -151,10 +146,6 @@
// Test that when recording is stopped, the stop intent is sent and listeners are notified.
@Test
public void testStopRecording() throws PendingIntent.CanceledException {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
-
PendingIntent startIntent = Mockito.mock(PendingIntent.class);
PendingIntent stopIntent = Mockito.mock(PendingIntent.class);
@@ -182,10 +173,6 @@
// Test that broadcast will update state
@Test
public void testUpdateStateBroadcast() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
-
// When a recording has started
PendingIntent startIntent = Mockito.mock(PendingIntent.class);
mController.startCountdown(0, 0, startIntent, null);
@@ -211,10 +198,6 @@
// Test that switching users will stop an ongoing recording
@Test
public void testUserChange() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
-
// If we are recording
PendingIntent startIntent = Mockito.mock(PendingIntent.class);
PendingIntent stopIntent = Mockito.mock(PendingIntent.class);
@@ -231,10 +214,6 @@
@Test
public void testPoliciesFlagDisabled_screenCapturingNotAllowed_returnsNullDevicePolicyDialog() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
-
mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, true);
mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, false);
when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(true);
@@ -247,10 +226,6 @@
@Test
public void testPartialScreenSharingDisabled_returnsLegacyDialog() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
-
mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, false);
mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, false);
@@ -262,10 +237,6 @@
@Test
public void testPoliciesFlagEnabled_screenCapturingNotAllowed_returnsDevicePolicyDialog() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
-
mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, true);
mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(true);
@@ -278,10 +249,6 @@
@Test
public void testPoliciesFlagEnabled_screenCapturingAllowed_returnsNullDevicePolicyDialog() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
-
mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, true);
mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(false);
@@ -294,9 +261,6 @@
@Test
public void testPoliciesFlagEnabled_screenCapturingAllowed_logsProjectionInitiated() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING, true);
mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(false);
@@ -306,7 +270,7 @@
verify(mMediaProjectionMetricsLogger)
.notifyProjectionInitiated(
- TEST_USER_ID,
+ /* hostUid= */ myUid(),
SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
index bf12d7d..fd38139 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity
import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
import com.android.systemui.mediaprojection.permission.SINGLE_APP
@@ -41,7 +42,6 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito
import org.mockito.Mockito.eq
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
@@ -57,6 +57,7 @@
@Mock private lateinit var userContextProvider: UserContextProvider
@Mock private lateinit var flags: FeatureFlags
@Mock private lateinit var onStartRecordingClicked: Runnable
+ @Mock private lateinit var mediaProjectionMetricsLogger: MediaProjectionMetricsLogger
private lateinit var dialog: ScreenRecordPermissionDialog
@@ -72,7 +73,8 @@
controller,
starter,
userContextProvider,
- onStartRecordingClicked
+ onStartRecordingClicked,
+ mediaProjectionMetricsLogger,
)
dialog.onCreate(null)
whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
@@ -149,6 +151,28 @@
assertThat(dialog.isShowing).isFalse()
}
+ @Test
+ fun showDialog_cancelClickedMultipleTimes_projectionRequestCancelledIsLoggedOnce() {
+ dialog.show()
+
+ clickOnCancel()
+ clickOnCancel()
+
+ verify(mediaProjectionMetricsLogger).notifyProjectionRequestCancelled(TEST_HOST_UID)
+ }
+
+ @Test
+ fun dismissDialog_dismissCalledMultipleTimes_projectionRequestCancelledIsLoggedOnce() {
+ dialog.show()
+
+ TestableLooper.get(this).runWithLooper {
+ dialog.dismiss()
+ dialog.dismiss()
+ }
+
+ verify(mediaProjectionMetricsLogger).notifyProjectionRequestCancelled(TEST_HOST_UID)
+ }
+
private fun clickOnCancel() {
dialog.requireViewById<View>(android.R.id.button2).performClick()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 6223e25..2ce4b04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -84,6 +84,7 @@
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
import com.android.systemui.common.ui.view.LongPressHandlingView;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.dump.DumpManager;
@@ -120,6 +121,8 @@
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.qs.QSFragmentLegacy;
import com.android.systemui.res.R;
+import com.android.systemui.scene.SceneTestUtils;
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.data.repository.ShadeRepository;
@@ -137,6 +140,7 @@
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository;
import com.android.systemui.statusbar.notification.ConversationNotificationManager;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
@@ -168,6 +172,7 @@
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.TapAgainViewController;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -176,8 +181,10 @@
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
+import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
+import com.android.systemui.user.domain.interactor.UserSwitcherInteractor;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.time.SystemClock;
@@ -197,6 +204,8 @@
import java.util.Optional;
import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.flow.StateFlowKt;
+import kotlinx.coroutines.test.TestScope;
public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
@@ -324,7 +333,6 @@
mEmptySpaceClickListenerCaptor;
@Mock protected ActivityStarter mActivityStarter;
@Mock protected KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
- @Mock private ShadeInteractor mShadeInteractor;
@Mock private JavaAdapter mJavaAdapter;
@Mock private CastController mCastController;
@Mock private KeyguardRootView mKeyguardRootView;
@@ -335,6 +343,9 @@
protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
protected FakeKeyguardRepository mFakeKeyguardRepository;
protected KeyguardInteractor mKeyguardInteractor;
+ protected SceneTestUtils mUtils = new SceneTestUtils(this);
+ protected TestScope mTestScope = mUtils.getTestScope();
+ protected ShadeInteractor mShadeInteractor;
protected PowerInteractor mPowerInteractor;
protected NotificationPanelViewController.TouchHandler mTouchHandler;
protected ConfigurationController mConfigurationController;
@@ -370,10 +381,31 @@
mKeyguardInteractor = keyguardInteractorDeps.getKeyguardInteractor();
mShadeRepository = new FakeShadeRepository();
mPowerInteractor = keyguardInteractorDeps.getPowerInteractor();
+ when(mKeyguardTransitionInteractor.isInTransitionToStateWhere(any())).thenReturn(
+ StateFlowKt.MutableStateFlow(false));
+ mShadeInteractor = new ShadeInteractor(
+ mTestScope.getBackgroundScope(),
+ new FakeDeviceProvisioningRepository(),
+ new FakeDisableFlagsRepository(),
+ mDozeParameters,
+ new FakeSceneContainerFlags(),
+ mUtils::sceneInteractor,
+ mFakeKeyguardRepository,
+ mKeyguardTransitionInteractor,
+ mPowerInteractor,
+ new FakeUserSetupRepository(),
+ mock(UserSwitcherInteractor.class),
+ new SharedNotificationContainerInteractor(
+ new FakeConfigurationRepository(),
+ mContext,
+ new ResourcesSplitShadeStateController()
+ ),
+ mShadeRepository
+ );
SystemClock systemClock = new FakeSystemClock();
- mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager,
- mInteractionJankMonitor, mShadeExpansionStateManager);
+ mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger,
+ mInteractionJankMonitor, mJavaAdapter, () -> mShadeInteractor);
KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
keyguardStatusView.setId(R.id.keyguard_status_view);
@@ -532,8 +564,9 @@
new NotificationWakeUpCoordinator(
mDumpManager,
mock(HeadsUpManager.class),
- new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager,
- mInteractionJankMonitor, mShadeExpansionStateManager),
+ new StatusBarStateControllerImpl(new UiEventLoggerFake(),
+ mInteractionJankMonitor,
+ mJavaAdapter, () -> mShadeInteractor),
mKeyguardBypassController,
mDozeParameters,
mScreenOffAnimationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index 7931e9e..2f45b12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -187,8 +187,8 @@
public void setup() {
MockitoAnnotations.initMocks(this);
when(mPanelViewControllerLazy.get()).thenReturn(mNotificationPanelViewController);
- mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager,
- mInteractionJankMonitor, mShadeExpansionStateManager);
+ mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger,
+ mInteractionJankMonitor, mock(JavaAdapter.class), () -> mShadeInteractor);
FakeDeviceProvisioningRepository deviceProvisioningRepository =
new FakeDeviceProvisioningRepository();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
index 8f06e63..6eabf44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
@@ -145,22 +145,15 @@
}
@Test
- public void testOnRepeatedlyLoadUnload_PluginFreed() {
+ public void testOnUnloadAfterLoad() {
mPluginInstance.onCreate();
mPluginInstance.loadPlugin();
+ assertNotNull(mPluginInstance.getPlugin());
assertInstances(1, 1);
mPluginInstance.unloadPlugin();
assertNull(mPluginInstance.getPlugin());
assertInstances(0, 0);
-
- mPluginInstance.loadPlugin();
- assertInstances(1, 1);
-
- mPluginInstance.unloadPlugin();
- mPluginInstance.onDestroy();
- assertNull(mPluginInstance.getPlugin());
- assertInstances(0, 0);
}
@Test
@@ -169,7 +162,7 @@
mPluginInstance.onCreate();
assertEquals(1, mPluginListener.mAttachedCount);
assertEquals(0, mPluginListener.mLoadCount);
- assertEquals(null, mPluginInstance.getPlugin());
+ assertNull(mPluginInstance.getPlugin());
assertInstances(0, 0);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index 2b3fd34..a4c12f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -37,12 +39,11 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FakeFeatureFlagsClassic;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
import com.android.systemui.statusbar.data.repository.NotificationListenerSettingsRepository;
import com.android.systemui.statusbar.domain.interactor.SilentNotificationStatusIconsVisibilityInteractor;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -71,12 +72,9 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
-
- FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic();
- featureFlags.setDefault(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR);
+ setFlagDefault(mSetFlagsRule, NotificationIconContainerRefactor.FLAG_NAME);
mListener = new NotificationListener(
mContext,
- featureFlags,
mNotificationManager,
new SilentNotificationStatusIconsVisibilityInteractor(
new NotificationListenerSettingsRepository()),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 764f7b6..560ebc6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -33,9 +33,9 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.RemoteInputControllerLogger;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -43,6 +43,7 @@
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
+import com.android.systemui.util.kotlin.JavaAdapter;
import org.junit.Before;
import org.junit.Test;
@@ -87,7 +88,8 @@
new RemoteInputControllerLogger(logcatLogBuffer()),
mClickNotifier,
new ActionClickLogger(logcatLogBuffer()),
- mock(DumpManager.class));
+ mock(JavaAdapter.class),
+ mock(ShadeInteractor.class));
mEntry = new NotificationEntryBuilder()
.setPkg(TEST_PACKAGE_NAME)
.setOpPkg(TEST_PACKAGE_NAME)
@@ -145,7 +147,8 @@
RemoteInputControllerLogger remoteInputControllerLogger,
NotificationClickNotifier clickNotifier,
ActionClickLogger actionClickLogger,
- DumpManager dumpManager) {
+ JavaAdapter javaAdapter,
+ ShadeInteractor shadeInteractor) {
super(
context,
notifPipelineFlags,
@@ -158,7 +161,8 @@
remoteInputControllerLogger,
clickNotifier,
actionClickLogger,
- dumpManager);
+ javaAdapter,
+ shadeInteractor);
}
public void setUpWithPresenterForTest(Callback callback,
@@ -170,3 +174,4 @@
}
}
+
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index 3327e42..d6dfc5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -23,9 +23,30 @@
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.classifier.FalsingCollectorFake
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.keyguard.data.repository.FakeCommandQueue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
+import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
+import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
+import com.android.systemui.util.mockito.mock
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -40,17 +61,22 @@
import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class StatusBarStateControllerImplTest : SysuiTestCase() {
+ private val utils = SceneTestUtils(this)
+ private val testScope = utils.testScope
+ private lateinit var shadeInteractor: ShadeInteractor
+ private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor
+ private lateinit var fromPrimaryBouncerTransitionInteractor:
+ FromPrimaryBouncerTransitionInteractor
@Mock lateinit var interactionJankMonitor: InteractionJankMonitor
- @Mock private lateinit var mockDarkAnimator: ObjectAnimator
- @Mock private lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
+ @Mock lateinit var mockDarkAnimator: ObjectAnimator
private lateinit var controller: StatusBarStateControllerImpl
private lateinit var uiEventLogger: UiEventLoggerFake
@@ -64,11 +90,75 @@
uiEventLogger = UiEventLoggerFake()
controller = object : StatusBarStateControllerImpl(
uiEventLogger,
- mock(DumpManager::class.java),
- interactionJankMonitor, shadeExpansionStateManager
+ interactionJankMonitor,
+ mock(),
+ { shadeInteractor }
) {
override fun createDarkAnimator(): ObjectAnimator { return mockDarkAnimator }
}
+
+ val powerInteractor = PowerInteractor(
+ FakePowerRepository(),
+ FalsingCollectorFake(),
+ mock(),
+ controller)
+ val keyguardRepository = FakeKeyguardRepository()
+ val keyguardTransitionRepository = FakeKeyguardTransitionRepository()
+ val featureFlags = FakeFeatureFlagsClassic()
+ val shadeRepository = FakeShadeRepository()
+ val sceneContainerFlags = FakeSceneContainerFlags()
+ val configurationRepository = FakeConfigurationRepository()
+ val keyguardInteractor = KeyguardInteractor(
+ keyguardRepository,
+ FakeCommandQueue(),
+ powerInteractor,
+ featureFlags,
+ sceneContainerFlags,
+ FakeKeyguardBouncerRepository(),
+ configurationRepository,
+ shadeRepository,
+ utils::sceneInteractor)
+ val keyguardTransitionInteractor = KeyguardTransitionInteractor(
+ testScope.backgroundScope,
+ keyguardTransitionRepository,
+ { keyguardInteractor },
+ { fromLockscreenTransitionInteractor },
+ { fromPrimaryBouncerTransitionInteractor })
+ fromLockscreenTransitionInteractor = FromLockscreenTransitionInteractor(
+ keyguardTransitionRepository,
+ keyguardTransitionInteractor,
+ testScope.backgroundScope,
+ keyguardInteractor,
+ featureFlags,
+ shadeRepository,
+ powerInteractor)
+ fromPrimaryBouncerTransitionInteractor = FromPrimaryBouncerTransitionInteractor(
+ keyguardTransitionRepository,
+ keyguardTransitionInteractor,
+ testScope.backgroundScope,
+ keyguardInteractor,
+ featureFlags,
+ mock(),
+ mock(),
+ powerInteractor)
+ shadeInteractor = ShadeInteractor(
+ testScope.backgroundScope,
+ FakeDeviceProvisioningRepository(),
+ FakeDisableFlagsRepository(),
+ mock(),
+ sceneContainerFlags,
+ utils::sceneInteractor,
+ keyguardRepository,
+ keyguardTransitionInteractor,
+ powerInteractor,
+ FakeUserSetupRepository(),
+ mock(),
+ SharedNotificationContainerInteractor(
+ configurationRepository,
+ mContext,
+ ResourcesSplitShadeStateController()),
+ shadeRepository,
+ )
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
index a736182..e81207e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
@@ -19,8 +19,7 @@
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags
+import com.android.systemui.flags.setFlagValue
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
@@ -30,6 +29,7 @@
import com.android.systemui.statusbar.notification.collection.render.NotifStackController
import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.domain.interactor.RenderNotificationListInteractor
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
import com.android.systemui.statusbar.phone.NotificationIconAreaController
@@ -39,6 +39,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations.initMocks
@@ -59,15 +60,18 @@
@Mock private lateinit var stackController: NotifStackController
@Mock private lateinit var section: NotifSection
- val featureFlags =
- FakeFeatureFlagsClassic().apply { setDefault(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR) }
-
@Before
fun setUp() {
initMocks(this)
+ setUp(NotificationIconContainerRefactor.FLAG_NAME to null)
+ entry = NotificationEntryBuilder().setSection(section).build()
+ }
+
+ private fun setUp(vararg flags: Pair<String, Boolean?>) {
+ flags.forEach { (name, value) -> mSetFlagsRule.setFlagValue(name, value) }
+ reset(pipeline)
coordinator =
StackCoordinator(
- featureFlags,
groupExpansionManagerImpl,
notificationIconAreaController,
renderListInteractor,
@@ -76,19 +80,18 @@
afterRenderListListener = withArgCaptor {
verify(pipeline).addOnAfterRenderListListener(capture())
}
- entry = NotificationEntryBuilder().setSection(section).build()
}
@Test
fun testUpdateNotificationIcons() {
- featureFlags.set(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR, false)
+ setUp(NotificationIconContainerRefactor.FLAG_NAME to false)
afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
verify(notificationIconAreaController).updateNotificationIcons(eq(listOf(entry)))
}
@Test
fun testSetRenderedListOnInteractor() {
- featureFlags.set(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR, true)
+ setUp(NotificationIconContainerRefactor.FLAG_NAME to true)
afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
verify(renderListInteractor).setRenderedList(eq(listOf(entry)))
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 2b944c3..39e3d5da3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -92,10 +92,8 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent;
import com.android.systemui.statusbar.notification.stack.NotificationSwipeHelper.NotificationCallback;
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel;
-import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -722,8 +720,6 @@
mActivityStarter,
new ResourcesSplitShadeStateController(),
mock(ConfigurationState.class),
- mock(DozeParameters.class),
- mock(ScreenOffAnimationController.class),
mock(ShelfNotificationIconViewStore.class));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 41eaf85..6478a3e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -21,6 +21,7 @@
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
+import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
@@ -163,6 +164,7 @@
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
@@ -185,8 +187,6 @@
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.startingsurface.StartingSurface;
-import dagger.Lazy;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -200,6 +200,8 @@
import javax.inject.Provider;
+import dagger.Lazy;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
@@ -335,7 +337,7 @@
mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI, false);
// Set default value to avoid IllegalStateException.
mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false);
- mFeatureFlags.setDefault(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR);
+ setFlagDefault(mSetFlagsRule, NotificationIconContainerRefactor.FLAG_NAME);
// For the Shade to respond to Back gesture, we must enable the event routing
mFeatureFlags.set(Flags.WM_SHADE_ALLOW_BACK_GESTURE, true);
// For the Shade to animate during the Back gesture, we must enable the animation flag.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 472709c..19215e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+
import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -43,7 +45,6 @@
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.flags.FakeFeatureFlagsClassic;
-import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
import com.android.systemui.shade.NotificationShadeWindowViewController;
@@ -53,6 +54,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -105,7 +107,7 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mFeatureFlags.setDefault(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR);
+ setFlagDefault(mSetFlagsRule, NotificationIconContainerRefactor.FLAG_NAME);
mDozeServiceHost = new DozeServiceHost(mDozeLog, mPowerManager, mWakefullnessLifecycle,
mStatusBarStateController, mDeviceProvisionedController, mFeatureFlags,
mHeadsUpManager, mBatteryController, mScrimController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 529e2c9..1fad2a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -35,7 +37,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.flags.FakeFeatureFlagsClassic;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeHeadsUpTracker;
@@ -47,6 +48,7 @@
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.policy.Clock;
@@ -90,7 +92,7 @@
@Before
public void setUp() throws Exception {
allowTestableLooperAsMainThread();
- mFeatureFlags.setDefault(Flags.NOTIFICATION_ICON_CONTAINER_REFACTOR);
+ setFlagDefault(mSetFlagsRule, NotificationIconContainerRefactor.FLAG_NAME);
mTestHelper = new NotificationTestHelper(
mContext,
mDependency,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index cda2a74..48b95d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -34,7 +34,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
-import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.AlertingNotificationManagerTest;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -45,6 +45,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
+import com.android.systemui.util.kotlin.JavaAdapter;
import org.junit.After;
import org.junit.Before;
@@ -56,6 +57,8 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import kotlinx.coroutines.flow.StateFlowKt;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -70,8 +73,9 @@
@Mock private KeyguardBypassController mBypassController;
@Mock private ConfigurationControllerImpl mConfigurationController;
@Mock private AccessibilityManagerWrapper mAccessibilityManagerWrapper;
- @Mock private ShadeExpansionStateManager mShadeExpansionStateManager;
@Mock private UiEventLogger mUiEventLogger;
+ @Mock private JavaAdapter mJavaAdapter;
+ @Mock private ShadeInteractor mShadeInteractor;
private static final class TestableHeadsUpManagerPhone extends HeadsUpManagerPhone {
TestableHeadsUpManagerPhone(
@@ -85,7 +89,8 @@
Handler handler,
AccessibilityManagerWrapper accessibilityManagerWrapper,
UiEventLogger uiEventLogger,
- ShadeExpansionStateManager shadeExpansionStateManager
+ JavaAdapter javaAdapter,
+ ShadeInteractor shadeInteractor
) {
super(
context,
@@ -98,7 +103,8 @@
handler,
accessibilityManagerWrapper,
uiEventLogger,
- shadeExpansionStateManager
+ javaAdapter,
+ shadeInteractor
);
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
@@ -117,7 +123,8 @@
mTestHandler,
mAccessibilityManagerWrapper,
mUiEventLogger,
- mShadeExpansionStateManager
+ mJavaAdapter,
+ mShadeInteractor
);
}
@@ -129,6 +136,7 @@
@Before
@Override
public void setUp() {
+ when(mShadeInteractor.isAnyExpanded()).thenReturn(StateFlowKt.MutableStateFlow(false));
final AccessibilityManagerWrapper accessibilityMgr =
mDependency.injectMockDependency(AccessibilityManagerWrapper.class);
when(accessibilityMgr.getRecommendedTimeoutMillis(anyInt(), anyInt()))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
index 1b8cfd4..92e40df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.flags.SetFlagsRuleExtensionsKt.setFlagDefault;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.verify;
@@ -28,12 +30,14 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.SetFlagsRuleExtensionsKt;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.wm.shell.bubbles.Bubbles;
@@ -82,6 +86,7 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+ mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME);
mController = new LegacyNotificationIconAreaControllerImpl(
mContext,
mStatusBarStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
index c282c1e..2b28562 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
@@ -20,12 +20,15 @@
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.setFlagDefault
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT
import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
@@ -41,6 +44,11 @@
private val iconContainer = NotificationIconContainer(context, /* attrs= */ null)
+ @Before
+ fun setup() {
+ mSetFlagsRule.setFlagDefault(NotificationIconContainerRefactor.FLAG_NAME)
+ }
+
@Test
fun calculateWidthFor_zeroIcons_widthIsZero() {
assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 0f),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 9a77f0c..d1b9b8a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -24,13 +24,8 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Fragment;
@@ -42,8 +37,6 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
-import android.view.ViewPropertyAnimator;
-import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
@@ -67,10 +60,8 @@
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarNotificationIconViewStore;
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel;
-import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
@@ -283,15 +274,15 @@
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
- verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
+ assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
+ assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
- verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
+ assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
}
@Test
@@ -323,7 +314,7 @@
// THEN all views are hidden
assertEquals(View.INVISIBLE, getClockView().getVisibility());
- verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
+ assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
}
@@ -339,7 +330,7 @@
// THEN all views are shown
assertEquals(View.VISIBLE, getClockView().getVisibility());
- verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
+ assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
@@ -356,7 +347,7 @@
// THEN all views are hidden
assertEquals(View.INVISIBLE, getClockView().getVisibility());
- verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
+ assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
// WHEN the shade is updated to no longer be open
@@ -367,7 +358,7 @@
// THEN all views are shown
assertEquals(View.VISIBLE, getClockView().getVisibility());
- verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
+ assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
@@ -381,7 +372,7 @@
// THEN all views are shown
assertEquals(View.VISIBLE, getClockView().getVisibility());
- verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
+ assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
@@ -395,7 +386,7 @@
// THEN all views are hidden
assertEquals(View.GONE, getClockView().getVisibility());
- verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
+ assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
}
@@ -409,7 +400,7 @@
// THEN all views are hidden
assertEquals(View.GONE, getClockView().getVisibility());
- verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
+ assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
// WHEN the transition has finished
@@ -418,7 +409,7 @@
// THEN all views are shown
assertEquals(View.VISIBLE, getClockView().getVisibility());
- verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
+ assertEquals(View.VISIBLE, mNotificationAreaInner.getVisibility());
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
@@ -451,7 +442,7 @@
assertEquals(View.VISIBLE,
mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
- verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
+ assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
}
@Test
@@ -507,6 +498,20 @@
}
@Test
+ public void disable_hasOngoingCall_hidesNotifsWithoutAnimation() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+ // Ongoing call started
+ when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, true);
+
+ // Notification area is hidden without delay
+ assertEquals(0f, mNotificationAreaInner.getAlpha(), 0.01);
+ assertEquals(View.INVISIBLE, mNotificationAreaInner.getVisibility());
+ }
+
+ @Test
public void disable_isDozing_clockAndSystemInfoVisible() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mStatusBarStateController.isDozing()).thenReturn(true);
@@ -713,8 +718,6 @@
mock(NotificationIconContainerStatusBarViewModel.class),
mock(ConfigurationState.class),
mock(ConfigurationController.class),
- mock(DozeParameters.class),
- mock(ScreenOffAnimationController.class),
mock(StatusBarNotificationIconViewStore.class),
mock(DemoModeController.class));
}
@@ -729,18 +732,7 @@
private void setUpNotificationIconAreaController() {
mMockNotificationAreaController = mock(NotificationIconAreaController.class);
- mNotificationAreaInner = mock(View.class);
-
- when(mNotificationAreaInner.getLayoutParams()).thenReturn(
- new FrameLayout.LayoutParams(100, 100));
- // We should probably start using a real view so that we don't need to mock these methods.
- ViewPropertyAnimator viewPropertyAnimator = mock(ViewPropertyAnimator.class);
- when(mNotificationAreaInner.animate()).thenReturn(viewPropertyAnimator);
- when(viewPropertyAnimator.alpha(anyFloat())).thenReturn(viewPropertyAnimator);
- when(viewPropertyAnimator.setDuration(anyLong())).thenReturn(viewPropertyAnimator);
- when(viewPropertyAnimator.setInterpolator(any())).thenReturn(viewPropertyAnimator);
- when(viewPropertyAnimator.setStartDelay(anyLong())).thenReturn(viewPropertyAnimator);
- when(viewPropertyAnimator.withEndAction(any())).thenReturn(viewPropertyAnimator);
+ mNotificationAreaInner = new View(mContext);
when(mMockNotificationAreaController.getNotificationInnerAreaView()).thenReturn(
mNotificationAreaInner);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
index b78e839..63de068 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
@@ -23,26 +23,27 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.anyString
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import java.util.Date
@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
class VariableDateViewControllerTest : SysuiTestCase() {
@@ -57,12 +58,15 @@
private lateinit var broadcastDispatcher: BroadcastDispatcher
@Mock
private lateinit var view: VariableDateView
+ @Mock
+ private lateinit var shadeInteractor: ShadeInteractor
@Captor
private lateinit var onMeasureListenerCaptor: ArgumentCaptor<VariableDateView.OnMeasureListener>
+ private val qsExpansion = MutableStateFlow(0F)
+
private var lastText: String? = null
- private lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
private lateinit var systemClock: FakeSystemClock
private lateinit var testableLooper: TestableLooper
private lateinit var testableHandler: Handler
@@ -80,7 +84,7 @@
systemClock = FakeSystemClock()
systemClock.setCurrentTimeMillis(TIME_STAMP)
- shadeExpansionStateManager = ShadeExpansionStateManager()
+ `when`(shadeInteractor.qsExpansion).thenReturn(qsExpansion)
`when`(view.longerPattern).thenReturn(LONG_PATTERN)
`when`(view.shorterPattern).thenReturn(SHORT_PATTERN)
@@ -91,6 +95,7 @@
Unit
}
`when`(view.isAttachedToWindow).thenReturn(true)
+ `when`(view.viewTreeObserver).thenReturn(mock())
val date = Date(TIME_STAMP)
longText = getTextForFormat(date, getFormatFromPattern(LONG_PATTERN))
@@ -103,12 +108,12 @@
}
controller = VariableDateViewController(
- systemClock,
- broadcastDispatcher,
- shadeExpansionStateManager,
- mock(),
- testableHandler,
- view
+ systemClock,
+ broadcastDispatcher,
+ shadeInteractor,
+ mock(),
+ testableHandler,
+ view
)
controller.init()
@@ -180,7 +185,7 @@
@Test
fun testQsExpansionTrue_ignoreAtMostMeasureRequests() {
- shadeExpansionStateManager.onQsExpansionFractionChanged(0f)
+ qsExpansion.value = 0f
onMeasureListenerCaptor.value.onMeasureAction(
getTextLength(shortText).toInt(),
@@ -195,7 +200,7 @@
@Test
fun testQsExpansionFalse_acceptAtMostMeasureRequests() {
- shadeExpansionStateManager.onQsExpansionFractionChanged(1f)
+ qsExpansion.value = 1f
onMeasureListenerCaptor.value.onMeasureAction(
getTextLength(shortText).toInt(),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index 6ef812b..e6e6b7b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -17,7 +17,6 @@
import static com.android.systemui.Flags.FLAG_EXAMPLE_FLAG;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -38,11 +37,7 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.uiautomator.UiDevice;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.broadcast.FakeBroadcastDispatcher;
-import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.settings.UserTracker;
import org.junit.After;
import org.junit.AfterClass;
@@ -53,7 +48,6 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
import java.util.concurrent.Future;
/**
@@ -86,7 +80,7 @@
public TestableDependency mDependency;
private Instrumentation mRealInstrumentation;
- private FakeBroadcastDispatcher mFakeBroadcastDispatcher;
+ private SysuiTestDependency mSysuiDependency;
@Before
public void SysuiSetup() throws Exception {
@@ -97,18 +91,8 @@
// Set the value of a single gantry flag inside the com.android.systemui package to
// ensure all flags in that package are faked (and thus require a value to be set).
mSetFlagsRule.disableFlags(FLAG_EXAMPLE_FLAG);
-
- mDependency = SysuiTestDependencyKt.installSysuiTestDependency(mContext);
- mFakeBroadcastDispatcher = new FakeBroadcastDispatcher(
- mContext,
- mContext.getMainExecutor(),
- mock(Looper.class),
- mock(Executor.class),
- mock(DumpManager.class),
- mock(BroadcastDispatcherLogger.class),
- mock(UserTracker.class),
- shouldFailOnLeakedReceiver());
-
+ mSysuiDependency = new SysuiTestDependency(mContext, shouldFailOnLeakedReceiver());
+ mDependency = mSysuiDependency.install();
mRealInstrumentation = InstrumentationRegistry.getInstrumentation();
Instrumentation inst = spy(mRealInstrumentation);
when(inst.getContext()).thenAnswer(invocation -> {
@@ -120,11 +104,6 @@
"SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext");
});
InstrumentationRegistry.registerInstance(inst, InstrumentationRegistry.getArguments());
- // Many tests end up creating a BroadcastDispatcher. Instead, give them a fake that will
- // record receivers registered. They are not actually leaked as they are kept just as a weak
- // reference and are never sent to the Context. This will also prevent a real
- // BroadcastDispatcher from actually registering receivers.
- mDependency.injectTestDependency(BroadcastDispatcher.class, mFakeBroadcastDispatcher);
}
protected boolean shouldFailOnLeakedReceiver() {
@@ -144,8 +123,9 @@
}
disallowTestableLooperAsMainThread();
mContext.cleanUpReceivers(this.getClass().getSimpleName());
- if (mFakeBroadcastDispatcher != null) {
- mFakeBroadcastDispatcher.cleanUpReceivers(this.getClass().getSimpleName());
+ FakeBroadcastDispatcher dispatcher = getFakeBroadcastDispatcher();
+ if (dispatcher != null) {
+ dispatcher.cleanUpReceivers(this.getClass().getSimpleName());
}
}
@@ -172,7 +152,7 @@
}
public FakeBroadcastDispatcher getFakeBroadcastDispatcher() {
- return mFakeBroadcastDispatcher;
+ return mSysuiDependency.getFakeBroadcastDispatcher();
}
public SysuiTestableContext getContext() {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt
index c791f4f..d89d7b0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestDependency.kt
@@ -1,26 +1,60 @@
package com.android.systemui
import android.annotation.SuppressLint
-import android.content.Context
+import android.os.Looper
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.animation.fakeDialogLaunchAnimator
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.broadcast.FakeBroadcastDispatcher
+import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.SystemUIDialogManager
+import java.util.concurrent.Executor
+import org.mockito.Mockito.mock
-@SuppressLint("VisibleForTests")
-fun installSysuiTestDependency(context: Context): TestableDependency {
- val initializer: SystemUIInitializer = SystemUIInitializerImpl(context)
- initializer.init(true)
+class SysuiTestDependency(
+ val context: SysuiTestableContext,
+ private val shouldFailOnLeakedReceiver: Boolean
+) {
+ var fakeBroadcastDispatcher: FakeBroadcastDispatcher? = null
- val dependency = TestableDependency(initializer.sysUIComponent.createDependency())
- Dependency.setInstance(dependency)
+ @SuppressLint("VisibleForTests")
+ fun install(): TestableDependency {
+ val initializer: SystemUIInitializer = SystemUIInitializerImpl(context)
+ initializer.init(true)
- dependency.injectMockDependency(KeyguardUpdateMonitor::class.java)
+ val dependency = TestableDependency(initializer.sysUIComponent.createDependency())
+ Dependency.setInstance(dependency)
- // Make sure that all tests on any SystemUIDialog does not crash because this dependency
- // is missing (constructing the actual one would throw).
- // TODO(b/219008720): Remove this.
- dependency.injectMockDependency(SystemUIDialogManager::class.java)
- dependency.injectTestDependency(DialogLaunchAnimator::class.java, fakeDialogLaunchAnimator())
- return dependency
+ dependency.injectMockDependency(KeyguardUpdateMonitor::class.java)
+
+ // Make sure that all tests on any SystemUIDialog does not crash because this dependency
+ // is missing (constructing the actual one would throw).
+ // TODO(b/219008720): Remove this.
+ dependency.injectMockDependency(SystemUIDialogManager::class.java)
+ dependency.injectTestDependency(
+ DialogLaunchAnimator::class.java,
+ fakeDialogLaunchAnimator()
+ )
+
+ // Many tests end up creating a BroadcastDispatcher. Instead, give them a fake that will
+ // record receivers registered. They are not actually leaked as they are kept just as a weak
+ // reference and are never sent to the Context. This will also prevent a real
+ // BroadcastDispatcher from actually registering receivers.
+ fakeBroadcastDispatcher =
+ FakeBroadcastDispatcher(
+ context,
+ context.mainExecutor,
+ mock(Looper::class.java),
+ mock(Executor::class.java),
+ mock(DumpManager::class.java),
+ mock(BroadcastDispatcherLogger::class.java),
+ mock(UserTracker::class.java),
+ shouldFailOnLeakedReceiver
+ )
+ dependency.injectTestDependency(BroadcastDispatcher::class.java, fakeBroadcastDispatcher)
+ return dependency
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt
new file mode 100644
index 0000000..de72a7d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 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.qs.tiles.viewmodel
+
+import com.android.systemui.qs.pipeline.shared.TileSpec
+
+class FakeQSTileConfigProvider : QSTileConfigProvider {
+
+ private val configs: MutableMap<String, QSTileConfig> = mutableMapOf()
+
+ override fun getConfig(tileSpec: String): QSTileConfig = configs.getValue(tileSpec)
+
+ fun putConfig(tileSpec: TileSpec, config: QSTileConfig) {
+ configs[tileSpec.spec] = config
+ }
+
+ fun removeConfig(tileSpec: TileSpec): QSTileConfig? = configs.remove(tileSpec.spec)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt
index 201926d..2a0ee88 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt
@@ -16,10 +16,7 @@
package com.android.systemui.qs.tiles.viewmodel
-import androidx.annotation.StringRes
import com.android.internal.logging.InstanceId
-import com.android.systemui.common.shared.model.ContentDescription
-import com.android.systemui.common.shared.model.Icon
import com.android.systemui.qs.pipeline.shared.TileSpec
object QSTileConfigTestBuilder {
@@ -29,8 +26,7 @@
class BuildingScope {
var tileSpec: TileSpec = TileSpec.create("test_spec")
- var tileIcon: Icon = Icon.Resource(0, ContentDescription.Resource(0))
- @StringRes var tileLabel: Int = 0
+ var uiConfig: QSTileUIConfig = QSTileUIConfig.Empty
var instanceId: InstanceId = InstanceId.fakeInstanceId(0)
var metricsSpec: String = tileSpec.spec
var policy: QSTilePolicy = QSTilePolicy.NoRestrictions
@@ -38,8 +34,7 @@
fun build() =
QSTileConfig(
tileSpec,
- tileIcon,
- tileLabel,
+ uiConfig,
instanceId,
metricsSpec,
policy,
diff --git a/rs/java/android/renderscript/ScriptC.java b/rs/java/android/renderscript/ScriptC.java
index 1866a99..67c2caa 100644
--- a/rs/java/android/renderscript/ScriptC.java
+++ b/rs/java/android/renderscript/ScriptC.java
@@ -16,9 +16,12 @@
package android.renderscript;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.res.Resources;
+import android.util.Slog;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -35,6 +38,15 @@
private static final String TAG = "ScriptC";
/**
+ * In targetSdkVersion 35 and above, Renderscript's ScriptC stops being supported
+ * and an exception is thrown when the class is instantiated.
+ * In targetSdkVersion 34 and below, Renderscript's ScriptC still works.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = 35)
+ private static final long RENDERSCRIPT_SCRIPTC_DEPRECATION_CHANGE_ID = 297019750L;
+
+ /**
* Only intended for use by the generated derived classes.
*
* @param id
@@ -89,7 +101,19 @@
setID(id);
}
+ private static void throwExceptionIfSDKTooHigh() {
+ String message =
+ "ScriptC scripts are not supported when targeting an API Level >= 35. Please refer "
+ + "to https://developer.android.com/guide/topics/renderscript/migration-guide "
+ + "for proposed alternatives.";
+ Slog.w(TAG, message);
+ if (CompatChanges.isChangeEnabled(RENDERSCRIPT_SCRIPTC_DEPRECATION_CHANGE_ID)) {
+ throw new UnsupportedOperationException(message);
+ }
+ }
+
private static synchronized long internalCreate(RenderScript rs, Resources resources, int resourceID) {
+ throwExceptionIfSDKTooHigh();
byte[] pgm;
int pgmLength;
InputStream is = resources.openRawResource(resourceID);
@@ -126,6 +150,7 @@
private static synchronized long internalStringCreate(RenderScript rs, String resName, byte[] bitcode) {
// Log.v(TAG, "Create script for resource = " + resName);
+ throwExceptionIfSDKTooHigh();
return rs.nScriptCCreate(resName, RenderScript.getCachePath(), bitcode, bitcode.length);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 5af80da..2913716 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -145,6 +145,13 @@
/** Flag for intercepting generic motion events. */
static final int FLAG_FEATURE_INTERCEPT_GENERIC_MOTION_EVENTS = 0x00000800;
+ /**
+ * Flag for enabling the two-finger triple-tap magnification feature.
+ *
+ * @see #setUserAndEnabledFeatures(int, int)
+ */
+ static final int FLAG_FEATURE_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP = 0x00001000;
+
static final int FEATURES_AFFECTING_MOTION_EVENTS =
FLAG_FEATURE_INJECT_MOTION_EVENTS
| FLAG_FEATURE_AUTOCLICK
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index d575102..a7b5325 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2786,6 +2786,12 @@
flags |= AccessibilityInputFilter
.FLAG_FEATURE_MAGNIFICATION_SINGLE_FINGER_TRIPLE_TAP;
}
+ if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
+ if (userState.isMagnificationSingleFingerTripleTapEnabledLocked()) {
+ flags |= AccessibilityInputFilter
+ .FLAG_FEATURE_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP;
+ }
+ }
if (userState.isShortcutMagnificationEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER;
}
@@ -3150,6 +3156,21 @@
return false;
}
+ private boolean readMagnificationTwoFingerTripleTapSettingsLocked(
+ AccessibilityUserState userState) {
+ final boolean magnificationTwoFingerTripleTapEnabled = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
+ 0, userState.mUserId) == 1;
+ if ((magnificationTwoFingerTripleTapEnabled
+ != userState.isMagnificationTwoFingerTripleTapEnabledLocked())) {
+ userState.setMagnificationTwoFingerTripleTapEnabledLocked(
+ magnificationTwoFingerTripleTapEnabled);
+ return true;
+ }
+ return false;
+ }
+
private boolean readAutoclickEnabledSettingLocked(AccessibilityUserState userState) {
final boolean autoclickEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
@@ -3385,6 +3406,7 @@
// displays in one display. It's not a real display and there's no input events for it.
final ArrayList<Display> displays = getValidDisplayList();
if (userState.isMagnificationSingleFingerTripleTapEnabledLocked()
+ || userState.isMagnificationTwoFingerTripleTapEnabledLocked()
|| userState.isShortcutMagnificationEnabledLocked()) {
for (int i = 0; i < displays.size(); i++) {
final Display display = displays.get(i);
@@ -4920,6 +4942,9 @@
private final Uri mMagnificationmSingleFingerTripleTapEnabledUri = Settings.Secure
.getUriFor(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
+ private final Uri mMagnificationTwoFingerTripleTapEnabledUri = Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED);
+
private final Uri mAutoclickEnabledUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED);
@@ -4977,6 +5002,10 @@
false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(mMagnificationmSingleFingerTripleTapEnabledUri,
false, this, UserHandle.USER_ALL);
+ if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
+ contentResolver.registerContentObserver(mMagnificationTwoFingerTripleTapEnabledUri,
+ false, this, UserHandle.USER_ALL);
+ }
contentResolver.registerContentObserver(mAutoclickEnabledUri,
false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(mEnabledAccessibilityServicesUri,
@@ -5027,6 +5056,11 @@
if (readMagnificationEnabledSettingsLocked(userState)) {
onUserStateChangedLocked(userState);
}
+ } else if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()
+ && mMagnificationTwoFingerTripleTapEnabledUri.equals(uri)) {
+ if (readMagnificationTwoFingerTripleTapSettingsLocked(userState)) {
+ onUserStateChangedLocked(userState);
+ }
} else if (mAutoclickEnabledUri.equals(uri)) {
if (readAutoclickEnabledSettingLocked(userState)) {
onUserStateChangedLocked(userState);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index b4efec1..68ee780 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -110,6 +110,7 @@
private boolean mIsAudioDescriptionByDefaultRequested;
private boolean mIsAutoclickEnabled;
private boolean mIsMagnificationSingleFingerTripleTapEnabled;
+ private boolean mMagnificationTwoFingerTripleTapEnabled;
private boolean mIsFilterKeyEventsEnabled;
private boolean mIsPerformGesturesEnabled;
private boolean mAccessibilityFocusOnlyInActiveWindow;
@@ -212,6 +213,7 @@
mRequestTwoFingerPassthrough = false;
mSendMotionEventsEnabled = false;
mIsMagnificationSingleFingerTripleTapEnabled = false;
+ mMagnificationTwoFingerTripleTapEnabled = false;
mIsAutoclickEnabled = false;
mUserNonInteractiveUiTimeout = 0;
mUserInteractiveUiTimeout = 0;
@@ -633,6 +635,14 @@
mIsMagnificationSingleFingerTripleTapEnabled = enabled;
}
+ public boolean isMagnificationTwoFingerTripleTapEnabledLocked() {
+ return mMagnificationTwoFingerTripleTapEnabled;
+ }
+
+ public void setMagnificationTwoFingerTripleTapEnabledLocked(boolean enabled) {
+ mMagnificationTwoFingerTripleTapEnabled = enabled;
+ }
+
public boolean isFilterKeyEventsEnabledLocked() {
return mIsFilterKeyEventsEnabled;
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 961e9d3..3985a0e 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -155,6 +155,7 @@
"service-permission.stubs.system_server",
"service-rkp.stubs.system_server",
"service-sdksandbox.stubs.system_server",
+ "device_policy_aconfig_flags_lib",
],
plugins: ["ImmutabilityAnnotationProcessor"],
diff --git a/services/core/java/com/android/server/BrickReceiver.java b/services/core/java/com/android/server/BrickReceiver.java
deleted file mode 100644
index cff3805..0000000
--- a/services/core/java/com/android/server/BrickReceiver.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.BroadcastReceiver;
-import android.os.SystemService;
-import android.util.Slog;
-
-public class BrickReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Slog.w("BrickReceiver", "!!! BRICKING DEVICE !!!");
- SystemService.start("brick");
- }
-}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 0ab81a5..9e48b0a 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -121,6 +121,7 @@
import com.android.server.power.stats.BatteryExternalStatsWorker;
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.PowerStatsAggregator;
import com.android.server.power.stats.PowerStatsScheduler;
import com.android.server.power.stats.PowerStatsStore;
@@ -440,7 +441,9 @@
.trackUidStates(
AggregatedPowerStatsConfig.STATE_POWER,
AggregatedPowerStatsConfig.STATE_SCREEN,
- AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessor(
+ new CpuAggregatedPowerStatsProcessor(mPowerProfile, mCpuScalingPolicies));
return config;
}
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index a57a785..439fe0b 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -123,6 +123,7 @@
"angle",
"arc_next",
"bluetooth",
+ "build",
"biometrics_framework",
"biometrics_integration",
"camera_platform",
@@ -136,10 +137,12 @@
"context_hub",
"core_experiments_team_internal",
"core_graphics",
+ "game",
"haptics",
"hardware_backed_security_mainline",
"input",
"machine_learning",
+ "mainline_modularization",
"mainline_sdk",
"media_audio",
"media_drm",
@@ -152,9 +155,12 @@
"platform_security",
"power",
"preload_safety",
+ "privacy_infra_policy",
+ "resource_manager",
"responsible_apis",
"rust",
"safety_center",
+ "sensors",
"system_performance",
"test_suites",
"text",
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index a1d2e14..d707689 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -924,9 +924,6 @@
@NonNull List<AudioDeviceAttributes> devices) {
int status = AudioSystem.ERROR;
try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
- AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
- "setPreferredDevicesForStrategy, strategy: " + strategy
- + " devices: " + devices)).printLog(TAG));
status = setDevicesRoleForStrategy(
strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices, false /* internal */);
}
@@ -952,10 +949,6 @@
int status = AudioSystem.ERROR;
try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
- AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
- "removePreferredDevicesForStrategy, strategy: "
- + strategy)).printLog(TAG));
-
status = clearDevicesRoleForStrategy(
strategy, AudioSystem.DEVICE_ROLE_PREFERRED, false /*internal */);
}
@@ -974,10 +967,6 @@
try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
List<AudioDeviceAttributes> devices = new ArrayList<>();
devices.add(device);
-
- AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
- "setDeviceAsNonDefaultForStrategyAndSave, strategy: " + strategy
- + " device: " + device)).printLog(TAG));
status = addDevicesRoleForStrategy(
strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices, false /* internal */);
}
@@ -995,11 +984,6 @@
try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
List<AudioDeviceAttributes> devices = new ArrayList<>();
devices.add(device);
-
- AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
- "removeDeviceAsNonDefaultForStrategyAndSave, strategy: "
- + strategy + " devices: " + device)).printLog(TAG));
-
status = removeDevicesRoleForStrategy(
strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices, false /* internal */);
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 9b03afb..a8fa313 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -16,8 +16,8 @@
package com.android.server.audio;
-import static android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED;
import static android.app.BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT;
+import static android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED;
import static android.media.AudioDeviceInfo.TYPE_BLE_HEADSET;
import static android.media.AudioDeviceInfo.TYPE_BLE_SPEAKER;
import static android.media.AudioDeviceInfo.TYPE_BLUETOOTH_A2DP;
@@ -150,6 +150,7 @@
import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionCallback;
import android.media.projection.IMediaProjectionManager;
+import android.media.session.MediaSessionManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -309,6 +310,9 @@
private final ContentResolver mContentResolver;
private final AppOpsManager mAppOps;
+ /** do not use directly, use getMediaSessionManager() which handles lazy initialization */
+ @Nullable private volatile MediaSessionManager mMediaSessionManager;
+
// the platform type affects volume and silent mode behavior
private final int mPlatformType;
@@ -940,6 +944,8 @@
private final SoundDoseHelper mSoundDoseHelper;
+ private final HardeningEnforcer mHardeningEnforcer;
+
private final Object mSupportedSystemUsagesLock = new Object();
@GuardedBy("mSupportedSystemUsagesLock")
private @AttributeSystemUsage int[] mSupportedSystemUsages =
@@ -1314,6 +1320,8 @@
mDisplayManager = context.getSystemService(DisplayManager.class);
mMusicFxHelper = new MusicFxHelper(mContext, mAudioHandler);
+
+ mHardeningEnforcer = new HardeningEnforcer(mContext, isPlatformAutomotive());
}
private void initVolumeStreamStates() {
@@ -1383,7 +1391,6 @@
// check on volume initialization
checkVolumeRangeInitialization("AudioService()");
-
}
private SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionChangedListener =
@@ -1396,6 +1403,14 @@
}
};
+ private MediaSessionManager getMediaSessionManager() {
+ if (mMediaSessionManager == null) {
+ mMediaSessionManager = (MediaSessionManager) mContext
+ .getSystemService(Context.MEDIA_SESSION_SERVICE);
+ }
+ return mMediaSessionManager;
+ }
+
/**
* Initialize intent receives and settings observers for this service.
* Must be called after createStreamStates() as the handling of some events
@@ -2921,11 +2936,11 @@
super.removePreferredDevicesForStrategy_enforcePermission();
final String logString =
- String.format("removePreferredDeviceForStrategy strat:%d", strategy);
+ String.format("removePreferredDevicesForStrategy strat:%d", strategy);
sDeviceLogger.enqueue(new EventLogger.StringEvent(logString).printLog(TAG));
final int status = mDeviceBroker.removePreferredDevicesForStrategySync(strategy);
- if (status != AudioSystem.SUCCESS) {
+ if (status != AudioSystem.SUCCESS && status != AudioSystem.BAD_VALUE) {
Log.e(TAG, String.format("Error %d in %s)", status, logString));
}
return status;
@@ -3009,7 +3024,7 @@
}
final int status = mDeviceBroker.removeDeviceAsNonDefaultForStrategySync(strategy, device);
- if (status != AudioSystem.SUCCESS) {
+ if (status != AudioSystem.SUCCESS && status != AudioSystem.BAD_VALUE) {
Log.e(TAG, String.format("Error %d in %s)", status, logString));
}
return status;
@@ -3129,7 +3144,7 @@
sDeviceLogger.enqueue(new EventLogger.StringEvent(logString).printLog(TAG));
final int status = mDeviceBroker.clearPreferredDevicesForCapturePresetSync(capturePreset);
- if (status != AudioSystem.SUCCESS) {
+ if (status != AudioSystem.SUCCESS && status != AudioSystem.BAD_VALUE) {
Log.e(TAG, String.format("Error %d in %s", status, logString));
}
return status;
@@ -3424,6 +3439,10 @@
* Part of service interface, check permissions here */
public void adjustStreamVolumeWithAttribution(int streamType, int direction, int flags,
String callingPackage, String attributionTag) {
+ if (mHardeningEnforcer.blockVolumeMethod(
+ HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_STREAM_VOLUME)) {
+ return;
+ }
if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) {
Log.w(TAG, "Trying to call adjustStreamVolume() for a11y without"
+ "CHANGE_ACCESSIBILITY_VOLUME / callingPackage=" + callingPackage);
@@ -4200,6 +4219,10 @@
* Part of service interface, check permissions here */
public void setStreamVolumeWithAttribution(int streamType, int index, int flags,
String callingPackage, String attributionTag) {
+ if (mHardeningEnforcer.blockVolumeMethod(
+ HardeningEnforcer.METHOD_AUDIO_MANAGER_SET_STREAM_VOLUME)) {
+ return;
+ }
setStreamVolumeWithAttributionInt(streamType, index, flags, /*device*/ null,
callingPackage, attributionTag);
}
@@ -5052,6 +5075,7 @@
/** @see AudioManager#setMasterMute(boolean, int) */
public void setMasterMute(boolean mute, int flags, String callingPackage, int userId,
String attributionTag) {
+
super.setMasterMute_enforcePermission();
setMasterMuteInternal(mute, flags, callingPackage,
@@ -5417,6 +5441,10 @@
}
public void setRingerModeExternal(int ringerMode, String caller) {
+ if (mHardeningEnforcer.blockVolumeMethod(
+ HardeningEnforcer.METHOD_AUDIO_MANAGER_SET_RINGER_MODE)) {
+ return;
+ }
if (isAndroidNPlus(caller) && wouldToggleZenMode(ringerMode)
&& !mNm.isNotificationPolicyAccessGrantedForPackage(caller)) {
throw new SecurityException("Not allowed to change Do Not Disturb state");
@@ -6169,6 +6197,35 @@
AudioDeviceVolumeManager.ADJUST_MODE_NORMAL);
}
+ /**
+ * @see AudioManager#adjustVolume(int, int)
+ * This method is redirected from AudioManager to AudioService for API hardening rules
+ * enforcement then to MediaSession for implementation.
+ */
+ @Override
+ public void adjustVolume(int direction, int flags) {
+ if (mHardeningEnforcer.blockVolumeMethod(
+ HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_VOLUME)) {
+ return;
+ }
+ getMediaSessionManager().dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE,
+ direction, flags);
+ }
+
+ /**
+ * @see AudioManager#adjustSuggestedStreamVolume(int, int, int)
+ * This method is redirected from AudioManager to AudioService for API hardening rules
+ * enforcement then to MediaSession for implementation.
+ */
+ @Override
+ public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
+ if (mHardeningEnforcer.blockVolumeMethod(
+ HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_SUGGESTED_STREAM_VOLUME)) {
+ return;
+ }
+ getMediaSessionManager().dispatchAdjustVolume(suggestedStreamType, direction, flags);
+ }
+
/** @see AudioManager#setStreamVolumeForUid(int, int, int, String, int, int, int) */
@Override
public void setStreamVolumeForUid(int streamType, int index, int flags,
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 6af409e..7d7e6d0 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -44,7 +44,9 @@
import java.io.PrintWriter;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
/**
@@ -66,6 +68,8 @@
// Bluetooth headset device
private @Nullable BluetoothDevice mBluetoothHeadsetDevice;
+ private final Map<BluetoothDevice, AudioDeviceAttributes> mResolvedScoAudioDevices =
+ new HashMap<>();
private @Nullable BluetoothHearingAid mHearingAid;
@@ -590,7 +594,16 @@
if (mBluetoothHeadsetDevice == null) {
return null;
}
- return btHeadsetDeviceToAudioDevice(mBluetoothHeadsetDevice);
+ return getHeadsetAudioDevice(mBluetoothHeadsetDevice);
+ }
+
+ private @NonNull AudioDeviceAttributes getHeadsetAudioDevice(BluetoothDevice btDevice) {
+ AudioDeviceAttributes deviceAttr = mResolvedScoAudioDevices.get(btDevice);
+ if (deviceAttr != null) {
+ // Returns the cached device attributes so that it is consistent as the previous one.
+ return deviceAttr;
+ }
+ return btHeadsetDeviceToAudioDevice(btDevice);
}
private static AudioDeviceAttributes btHeadsetDeviceToAudioDevice(BluetoothDevice btDevice) {
@@ -628,7 +641,7 @@
return true;
}
int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
- AudioDeviceAttributes audioDevice = btHeadsetDeviceToAudioDevice(btDevice);
+ AudioDeviceAttributes audioDevice = btHeadsetDeviceToAudioDevice(btDevice);
boolean result = false;
if (isActive) {
result |= mDeviceBroker.handleDeviceConnection(audioDevice, isActive, btDevice);
@@ -648,6 +661,13 @@
result = mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
inDevice, audioDevice.getAddress(), audioDevice.getName()),
isActive, btDevice) && result;
+ if (result) {
+ if (isActive) {
+ mResolvedScoAudioDevices.put(btDevice, audioDevice);
+ } else {
+ mResolvedScoAudioDevices.remove(btDevice);
+ }
+ }
return result;
}
diff --git a/services/core/java/com/android/server/audio/HardeningEnforcer.java b/services/core/java/com/android/server/audio/HardeningEnforcer.java
new file mode 100644
index 0000000..c7556da
--- /dev/null
+++ b/services/core/java/com/android/server/audio/HardeningEnforcer.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2023 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.audio;
+
+import static com.android.media.audio.flags.Flags.autoPublicVolumeApiHardening;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.media.AudioManager;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * Class to encapsulate all audio API hardening operations
+ */
+public class HardeningEnforcer {
+
+ private static final String TAG = "AS.HardeningEnforcer";
+
+ final Context mContext;
+ final boolean mIsAutomotive;
+
+ /**
+ * Matches calls from {@link AudioManager#setStreamVolume(int, int, int)}
+ */
+ public static final int METHOD_AUDIO_MANAGER_SET_STREAM_VOLUME = 100;
+ /**
+ * Matches calls from {@link AudioManager#adjustVolume(int, int)}
+ */
+ public static final int METHOD_AUDIO_MANAGER_ADJUST_VOLUME = 101;
+ /**
+ * Matches calls from {@link AudioManager#adjustSuggestedStreamVolume(int, int, int)}
+ */
+ public static final int METHOD_AUDIO_MANAGER_ADJUST_SUGGESTED_STREAM_VOLUME = 102;
+ /**
+ * Matches calls from {@link AudioManager#adjustStreamVolume(int, int, int)}
+ */
+ public static final int METHOD_AUDIO_MANAGER_ADJUST_STREAM_VOLUME = 103;
+ /**
+ * Matches calls from {@link AudioManager#setRingerMode(int)}
+ */
+ public static final int METHOD_AUDIO_MANAGER_SET_RINGER_MODE = 200;
+
+ public HardeningEnforcer(Context ctxt, boolean isAutomotive) {
+ mContext = ctxt;
+ mIsAutomotive = isAutomotive;
+ }
+
+ /**
+ * Checks whether the call in the current thread should be allowed or blocked
+ * @param volumeMethod name of the method to check, for logging purposes
+ * @return false if the method call is allowed, true if it should be a no-op
+ */
+ protected boolean blockVolumeMethod(int volumeMethod) {
+ // for Auto, volume methods require MODIFY_AUDIO_SETTINGS_PRIVILEGED
+ if (mIsAutomotive) {
+ if (!autoPublicVolumeApiHardening()) {
+ // automotive hardening flag disabled, no blocking on auto
+ return false;
+ }
+ if (mContext.checkCallingOrSelfPermission(
+ Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ == PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ if (Binder.getCallingUid() < UserHandle.AID_APP_START) {
+ return false;
+ }
+ // TODO metrics?
+ // TODO log for audio dumpsys?
+ Log.e(TAG, "Preventing volume method " + volumeMethod + " for "
+ + getPackNameForUid(Binder.getCallingUid()));
+ return true;
+ }
+ // not blocking
+ return false;
+ }
+
+ private String getPackNameForUid(int uid) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final String[] names = mContext.getPackageManager().getPackagesForUid(uid);
+ if (names == null
+ || names.length == 0
+ || TextUtils.isEmpty(names[0])) {
+ return "[" + uid + "]";
+ }
+ return names[0];
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b1c92a6..087cf20 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -590,8 +590,7 @@
mSystemReady = false;
mConfigParameterProvider = new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
mExtraDisplayLoggingPackageName = DisplayProperties.debug_vri_package().orElse(null);
- // TODO: b/306170135 - return TextUtils package name check instead
- mExtraDisplayEventLogging = true;
+ mExtraDisplayEventLogging = !TextUtils.isEmpty(mExtraDisplayLoggingPackageName);
}
public void setupSchedulerPolicies() {
@@ -3048,8 +3047,7 @@
}
private boolean extraLogging(String packageName) {
- // TODO: b/306170135 - return mExtraDisplayLoggingPackageName & package name check instead
- return true;
+ return mExtraDisplayEventLogging && mExtraDisplayLoggingPackageName.equals(packageName);
}
// Runs on Handler thread.
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 67a1ccd..78077a8 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -40,6 +40,7 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.media.flags.Flags;
import java.util.List;
import java.util.Objects;
@@ -358,21 +359,8 @@
RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build();
- if (mPendingSessionCreationRequest != null) {
- SessionCreationRequest sessionCreationRequest;
- synchronized (mRequestLock) {
- sessionCreationRequest = mPendingSessionCreationRequest;
- mPendingSessionCreationRequest = null;
- }
- if (sessionCreationRequest != null) {
- if (TextUtils.equals(mSelectedRouteId, sessionCreationRequest.mRouteId)) {
- mCallback.onSessionCreated(this,
- sessionCreationRequest.mRequestId, newSessionInfo);
- } else {
- mCallback.onRequestFailed(this, sessionCreationRequest.mRequestId,
- MediaRoute2ProviderService.REASON_UNKNOWN_ERROR);
- }
- }
+ synchronized (mRequestLock) {
+ reportPendingSessionRequestResultLockedIfNeeded(newSessionInfo);
}
if (Objects.equals(oldSessionInfo, newSessionInfo)) {
@@ -395,6 +383,59 @@
}
}
+ @GuardedBy("mRequestLock")
+ private void reportPendingSessionRequestResultLockedIfNeeded(
+ RoutingSessionInfo newSessionInfo) {
+ if (mPendingSessionCreationRequest == null) {
+ // No pending request, nothing to report.
+ return;
+ }
+
+ long pendingRequestId = mPendingSessionCreationRequest.mRequestId;
+ if (TextUtils.equals(mSelectedRouteId, mPendingSessionCreationRequest.mRouteId)) {
+ if (DEBUG) {
+ Slog.w(
+ TAG,
+ "Session creation success to route "
+ + mPendingSessionCreationRequest.mRouteId);
+ }
+ mPendingSessionCreationRequest = null;
+ mCallback.onSessionCreated(this, pendingRequestId, newSessionInfo);
+ } else {
+ boolean isRequestedRouteConnectedBtRoute = isRequestedRouteConnectedBtRoute();
+ if (!Flags.enableWaitingStateForSystemSessionCreationRequest()
+ || !isRequestedRouteConnectedBtRoute) {
+ if (DEBUG) {
+ Slog.w(
+ TAG,
+ "Session creation failed to route "
+ + mPendingSessionCreationRequest.mRouteId);
+ }
+ mPendingSessionCreationRequest = null;
+ mCallback.onRequestFailed(
+ this, pendingRequestId, MediaRoute2ProviderService.REASON_UNKNOWN_ERROR);
+ } else if (DEBUG) {
+ Slog.w(
+ TAG,
+ "Session creation waiting state to route "
+ + mPendingSessionCreationRequest.mRouteId);
+ }
+ }
+ }
+
+ @GuardedBy("mRequestLock")
+ private boolean isRequestedRouteConnectedBtRoute() {
+ // Using AllRoutes instead of TransferableRoutes as BT Stack sends an intermediate update
+ // where two BT routes are active so the transferable routes list is empty.
+ // See b/307723189 for context
+ for (MediaRoute2Info btRoute : mBluetoothRouteController.getAllBluetoothRoutes()) {
+ if (TextUtils.equals(btRoute.getId(), mPendingSessionCreationRequest.mRouteId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
void publishProviderState() {
updateProviderState();
notifyProviderState();
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 58927d1..893ed61 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -470,6 +470,11 @@
}
@VisibleForTesting
+ void notifyPermissionRequestCancelled(int hostUid) {
+ mMediaProjectionMetricsLogger.logProjectionPermissionRequestCancelled(hostUid);
+ }
+
+ @VisibleForTesting
void notifyAppSelectorDisplayed(int hostUid) {
mMediaProjectionMetricsLogger.logAppSelectorDisplayed(hostUid);
}
@@ -852,19 +857,6 @@
@Override // Binder call
@EnforcePermission(MANAGE_MEDIA_PROJECTION)
- public void notifyPermissionRequestStateChange(int hostUid, int state,
- int sessionCreationSource) {
- notifyPermissionRequestStateChange_enforcePermission();
- final long token = Binder.clearCallingIdentity();
- try {
- mMediaProjectionMetricsLogger.notifyProjectionStateChange(hostUid, state, sessionCreationSource);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override // Binder call
- @EnforcePermission(MANAGE_MEDIA_PROJECTION)
public void notifyPermissionRequestInitiated(int hostUid, int sessionCreationSource) {
notifyPermissionRequestInitiated_enforcePermission();
final long token = Binder.clearCallingIdentity();
@@ -890,6 +882,18 @@
@Override // Binder call
@EnforcePermission(MANAGE_MEDIA_PROJECTION)
+ public void notifyPermissionRequestCancelled(int hostUid) {
+ notifyPermissionRequestCancelled_enforcePermission();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ MediaProjectionManagerService.this.notifyPermissionRequestCancelled(hostUid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ @EnforcePermission(MANAGE_MEDIA_PROJECTION)
public void notifyAppSelectorDisplayed(int hostUid) {
notifyAppSelectorDisplayed_enforcePermission();
final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
index 55a30bf..d7fefeb 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
@@ -118,6 +118,23 @@
}
/**
+ * Logs that requesting permission for media projection was cancelled by the user.
+ *
+ * @param hostUid UID of the package that initiates MediaProjection.
+ */
+ public void logProjectionPermissionRequestCancelled(int hostUid) {
+ write(
+ mSessionIdGenerator.getCurrentSessionId(),
+ FrameworkStatsLog
+ .MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CANCELLED,
+ hostUid,
+ TARGET_UID_UNKNOWN,
+ TIME_SINCE_LAST_ACTIVE_UNKNOWN,
+ FrameworkStatsLog
+ .MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN);
+ }
+
+ /**
* Logs that the app selector dialog is shown for the user.
*
* @param hostUid UID of the package that initiates MediaProjection.
@@ -174,23 +191,6 @@
}
}
- public void notifyProjectionStateChange(int hostUid, int state, int sessionCreationSource) {
- write(hostUid, state, sessionCreationSource);
- }
-
- private void write(int hostUid, int state, int sessionCreationSource) {
- mFrameworkStatsLogWrapper.write(
- /* code */ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED,
- /* session_id */ 123,
- /* state */ state,
- /* previous_state */ FrameworkStatsLog
- .MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN,
- /* host_uid */ hostUid,
- /* target_uid */ -1,
- /* time_since_last_active */ 0,
- /* creation_source */ sessionCreationSource);
- }
-
private void write(
int sessionId,
int state,
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index 1134714..e57ea0f 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -16,6 +16,8 @@
package com.android.server.os;
+import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
+
import android.Manifest;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -52,8 +54,10 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.HashSet;
import java.util.Objects;
import java.util.OptionalInt;
+import java.util.Set;
/**
* Implementation of the service that provides a privileged API to capture and consume bugreports.
@@ -101,10 +105,12 @@
private final ArrayMap<Pair<Integer, String>, ArraySet<String>> mBugreportFiles =
new ArrayMap<>();
+ @GuardedBy("mLock")
+ private final Set<String> mBugreportFilesToPersist = new HashSet<>();
+
/**
* Checks that a given file was generated on behalf of the given caller. If the file was
- * generated on behalf of the caller, it is removed from the bugreport mapping so that it
- * may not be retrieved again. If the file was not generated on behalf of the caller, an
+ * not generated on behalf of the caller, an
* {@link IllegalArgumentException} is thrown.
*
* @param callingInfo a (uid, package name) pair identifying the caller
@@ -114,35 +120,76 @@
* @throws IllegalArgumentException if {@code bugreportFile} is not associated with
* {@code callingInfo}.
*/
+ @RequiresPermission(value = android.Manifest.permission.INTERACT_ACROSS_USERS,
+ conditional = true)
void ensureCallerPreviouslyGeneratedFile(
- Pair<Integer, String> callingInfo, String bugreportFile) {
+ Context context, Pair<Integer, String> callingInfo, int userId,
+ String bugreportFile) {
synchronized (mLock) {
- ArraySet<String> bugreportFilesForCaller = mBugreportFiles.get(callingInfo);
- if (bugreportFilesForCaller != null
- && bugreportFilesForCaller.contains(bugreportFile)) {
- bugreportFilesForCaller.remove(bugreportFile);
- if (bugreportFilesForCaller.isEmpty()) {
- mBugreportFiles.remove(callingInfo);
+ if (onboardingBugreportV2Enabled()) {
+ final int uidForUser = Binder.withCleanCallingIdentity(() -> {
+ try {
+ return context.getPackageManager()
+ .getPackageUidAsUser(callingInfo.second, userId);
+ } catch (PackageManager.NameNotFoundException exception) {
+ throwInvalidBugreportFileForCallerException(
+ bugreportFile, callingInfo.second);
+ return -1;
+ }
+ });
+ if (uidForUser != callingInfo.first && context.checkCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ callingInfo.second + " does not hold the "
+ + "INTERACT_ACROSS_USERS permission to access "
+ + "cross-user bugreports.");
+ }
+ ArraySet<String> bugreportFilesForUid = mBugreportFiles.get(
+ new Pair<>(uidForUser, callingInfo.second));
+ if (bugreportFilesForUid == null
+ || !bugreportFilesForUid.contains(bugreportFile)) {
+ throwInvalidBugreportFileForCallerException(
+ bugreportFile, callingInfo.second);
}
} else {
- throw new IllegalArgumentException(
- "File " + bugreportFile + " was not generated"
- + " on behalf of calling package " + callingInfo.second);
+ ArraySet<String> bugreportFilesForCaller = mBugreportFiles.get(callingInfo);
+ if (bugreportFilesForCaller != null
+ && bugreportFilesForCaller.contains(bugreportFile)) {
+ bugreportFilesForCaller.remove(bugreportFile);
+ if (bugreportFilesForCaller.isEmpty()) {
+ mBugreportFiles.remove(callingInfo);
+ }
+ } else {
+ throwInvalidBugreportFileForCallerException(
+ bugreportFile, callingInfo.second);
+
+ }
}
}
}
+ private static void throwInvalidBugreportFileForCallerException(
+ String bugreportFile, String packageName) {
+ throw new IllegalArgumentException("File " + bugreportFile + " was not generated on"
+ + " behalf of calling package " + packageName);
+ }
+
/**
* Associates a bugreport file with a caller, which is identified as a
* (uid, package name) pair.
*/
- void addBugreportFileForCaller(Pair<Integer, String> caller, String bugreportFile) {
+ void addBugreportFileForCaller(
+ Pair<Integer, String> caller, String bugreportFile, boolean keepOnRetrieval) {
synchronized (mLock) {
if (!mBugreportFiles.containsKey(caller)) {
mBugreportFiles.put(caller, new ArraySet<>());
}
ArraySet<String> bugreportFilesForCaller = mBugreportFiles.get(caller);
bugreportFilesForCaller.add(bugreportFile);
+ if ((onboardingBugreportV2Enabled()) && keepOnRetrieval) {
+ mBugreportFilesToPersist.add(bugreportFile);
+ }
}
}
}
@@ -246,16 +293,17 @@
}
@Override
- @RequiresPermission(Manifest.permission.DUMP)
- public void retrieveBugreport(int callingUidUnused, String callingPackage,
- FileDescriptor bugreportFd, String bugreportFile, IDumpstateListener listener) {
+ @RequiresPermission(value = Manifest.permission.DUMP, conditional = true)
+ public void retrieveBugreport(int callingUidUnused, String callingPackage, int userId,
+ FileDescriptor bugreportFd, String bugreportFile,
+ boolean keepBugreportOnRetrievalUnused, IDumpstateListener listener) {
int callingUid = Binder.getCallingUid();
enforcePermission(callingPackage, callingUid, false);
Slogf.i(TAG, "Retrieving bugreport for %s / %d", callingPackage, callingUid);
try {
mBugreportFileManager.ensureCallerPreviouslyGeneratedFile(
- new Pair<>(callingUid, callingPackage), bugreportFile);
+ mContext, new Pair<>(callingUid, callingPackage), userId, bugreportFile);
} catch (IllegalArgumentException e) {
Slog.e(TAG, e.getMessage());
reportError(listener, IDumpstateListener.BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE);
@@ -281,10 +329,17 @@
// Wrap the listener so we can intercept binder events directly.
DumpstateListener myListener = new DumpstateListener(listener, ds,
new Pair<>(callingUid, callingPackage), /* reportFinishedFile= */ true);
+
+ boolean keepBugreportOnRetrieval = false;
+ if (onboardingBugreportV2Enabled()) {
+ keepBugreportOnRetrieval = mBugreportFileManager.mBugreportFilesToPersist.contains(
+ bugreportFile);
+ }
+
setCurrentDumpstateListenerLocked(myListener);
try {
- ds.retrieveBugreport(callingUid, callingPackage, bugreportFd,
- bugreportFile, myListener);
+ ds.retrieveBugreport(callingUid, callingPackage, userId, bugreportFd,
+ bugreportFile, keepBugreportOnRetrieval, myListener);
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException in retrieveBugreport", e);
}
@@ -317,7 +372,8 @@
private void validateBugreportFlags(int flags) {
flags = clearBugreportFlag(flags,
BugreportParams.BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA
- | BugreportParams.BUGREPORT_FLAG_DEFER_CONSENT);
+ | BugreportParams.BUGREPORT_FLAG_DEFER_CONSENT
+ | BugreportParams.BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL);
if (flags != 0) {
Slog.w(TAG, "Unknown bugreport flags: " + flags);
throw new IllegalArgumentException("Unknown bugreport flags: " + flags);
@@ -482,6 +538,9 @@
boolean reportFinishedFile =
(bugreportFlags & BugreportParams.BUGREPORT_FLAG_DEFER_CONSENT) != 0;
+ boolean keepBugreportOnRetrieval =
+ (bugreportFlags & BugreportParams.BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL) != 0;
+
IDumpstate ds = startAndGetDumpstateBinderServiceLocked();
if (ds == null) {
Slog.w(TAG, "Unable to get bugreport service");
@@ -490,7 +549,8 @@
}
DumpstateListener myListener = new DumpstateListener(listener, ds,
- new Pair<>(callingUid, callingPackage), reportFinishedFile);
+ new Pair<>(callingUid, callingPackage), reportFinishedFile,
+ keepBugreportOnRetrieval);
setCurrentDumpstateListenerLocked(myListener);
try {
ds.startBugreport(callingUid, callingPackage, bugreportFd, screenshotFd, bugreportMode,
@@ -646,9 +706,16 @@
private final boolean mReportFinishedFile;
private int mProgress; // used for debugging purposes only
private boolean mDone;
+ private boolean mKeepBugreportOnRetrieval;
DumpstateListener(IDumpstateListener listener, IDumpstate ds,
Pair<Integer, String> caller, boolean reportFinishedFile) {
+ this(listener, ds, caller, reportFinishedFile, /* keepBugreportOnRetrieval= */ false);
+ }
+
+ DumpstateListener(IDumpstateListener listener, IDumpstate ds,
+ Pair<Integer, String> caller, boolean reportFinishedFile,
+ boolean keepBugreportOnRetrieval) {
if (DEBUG) {
Slogf.d(TAG, "Starting DumpstateListener(id=%d) for caller %s", mId, caller);
}
@@ -656,6 +723,7 @@
mDs = ds;
mCaller = caller;
mReportFinishedFile = reportFinishedFile;
+ mKeepBugreportOnRetrieval = keepBugreportOnRetrieval;
try {
mDs.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
@@ -690,7 +758,8 @@
reportFinishedLocked("File: " + bugreportFile);
}
if (mReportFinishedFile) {
- mBugreportFileManager.addBugreportFileForCaller(mCaller, bugreportFile);
+ mBugreportFileManager.addBugreportFileForCaller(
+ mCaller, bugreportFile, mKeepBugreportOnRetrieval);
} else if (DEBUG) {
Slog.d(TAG, "Not reporting finished file");
}
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index bd9be30..167c17c 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -121,7 +121,7 @@
StorageManagerInternal.class);
for (UserInfo user : umInternal.getUsers(false /*excludeDying*/)) {
final int flags;
- if (StorageManager.isUserKeyUnlocked(user.id)
+ if (StorageManager.isCeStorageUnlocked(user.id)
&& smInternal.isCeStoragePrepared(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
} else if (umInternal.isUserRunning(user.id)) {
@@ -410,7 +410,7 @@
// First look for stale data that doesn't belong, and check if things
// have changed since we did our last restorecon
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
- if (StorageManager.isFileEncrypted() && !StorageManager.isUserKeyUnlocked(userId)) {
+ if (StorageManager.isFileEncrypted() && !StorageManager.isCeStorageUnlocked(userId)) {
throw new RuntimeException(
"Yikes, someone asked us to reconcile CE storage while " + userId
+ " was still locked; this would have caused massive data loss!");
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 510c06e..3ed9f02 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -3556,7 +3556,7 @@
@Override
public int getPackageStartability(boolean safeMode, @NonNull String packageName, int callingUid,
@UserIdInt int userId) {
- final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId);
+ final boolean ceStorageUnlocked = StorageManager.isCeStorageUnlocked(userId);
final PackageStateInternal ps = getPackageStateInternal(packageName);
if (ps == null || shouldFilterApplication(ps, callingUid, userId)
|| !ps.getUserStateOrDefault(userId).isInstalled()) {
@@ -3571,7 +3571,7 @@
return PackageManagerService.PACKAGE_STARTABILITY_FROZEN;
}
- if (!userKeyUnlocked && !AndroidPackageUtils.isEncryptionAware(ps.getPkg())) {
+ if (!ceStorageUnlocked && !AndroidPackageUtils.isEncryptionAware(ps.getPkg())) {
return PackageManagerService.PACKAGE_STARTABILITY_DIRECT_BOOT_UNSUPPORTED;
}
return PackageManagerService.PACKAGE_STARTABILITY_OK;
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 7c32cde..2951ef6 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -2374,12 +2374,6 @@
permissionParamsBuilder.setAutoRevokePermissionsMode(autoRevokePermissionsMode);
mPm.mPermissionManager.onPackageInstalled(pkg, installRequest.getPreviousAppId(),
permissionParamsBuilder.build(), userId);
- // Apply restricted settings on potentially dangerous packages.
- if (installRequest.getPackageSource() == PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE
- || installRequest.getPackageSource()
- == PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE) {
- enableRestrictedSettings(pkgName, pkg.getUid());
- }
}
installRequest.setName(pkgName);
installRequest.setAppId(pkg.getUid());
@@ -2394,16 +2388,13 @@
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- private void enableRestrictedSettings(String pkgName, int appId) {
+ private void enableRestrictedSettings(String pkgName, int appId, int userId) {
final AppOpsManager appOpsManager = mPm.mContext.getSystemService(AppOpsManager.class);
- final int[] allUsersList = mPm.mUserManager.getUserIds();
- for (int userId : allUsersList) {
- final int uid = UserHandle.getUid(userId, appId);
- appOpsManager.setMode(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
- uid,
- pkgName,
- AppOpsManager.MODE_ERRORED);
- }
+ final int uid = UserHandle.getUid(userId, appId);
+ appOpsManager.setMode(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
+ uid,
+ pkgName,
+ AppOpsManager.MODE_ERRORED);
}
/**
@@ -2820,13 +2811,10 @@
mPm.mRequiredInstallerPackage,
/* packageSender= */ mPm, launchedForRestore, killApp, update, archived);
-
// Work that needs to happen on first install within each user
- if (firstUserIds.length > 0) {
- for (int userId : firstUserIds) {
- mPm.restorePermissionsAndUpdateRolesForNewUserInstall(packageName,
- userId);
- }
+ for (int userId : firstUserIds) {
+ mPm.restorePermissionsAndUpdateRolesForNewUserInstall(packageName,
+ userId);
}
if (request.isAllNewUsers() && !update) {
@@ -2835,6 +2823,16 @@
mPm.notifyPackageChanged(packageName, request.getAppId());
}
+ for (int userId : firstUserIds) {
+ // Apply restricted settings on potentially dangerous packages. Needs to happen
+ // after appOpsManager is notified of the new package
+ if (request.getPackageSource() == PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE
+ || request.getPackageSource()
+ == PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE) {
+ enableRestrictedSettings(packageName, request.getAppId(), userId);
+ }
+ }
+
// Log current value of "unknown sources" setting
EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
getUnknownSourcesSettings());
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index c260be9..e857e05 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -506,6 +506,9 @@
if (!canAccessProfile(userId, "cannot get shouldHideFromSuggestions")) {
return false;
}
+ if (Flags.archiving() && packageName != null && isPackageArchived(packageName, user)) {
+ return true;
+ }
if (mPackageManagerInternal.filterAppAccess(
packageName, Binder.getCallingUid(), userId)) {
return false;
@@ -758,6 +761,10 @@
}
}
+ private boolean isPackageArchived(@NonNull String packageName, UserHandle user) {
+ return !getApplicationInfoForArchivedApp(packageName, user).isEmpty();
+ }
+
@NonNull
private List<LauncherActivityInfoInternal> generateLauncherActivitiesForArchivedApp(
@Nullable String packageName, UserHandle user) {
@@ -969,11 +976,17 @@
final int callingUid = injectBinderCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
- final PackageInfo info = mPackageManagerInternal.getPackageInfo(packageName,
+ long callingFlag =
PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- callingUid, user.getIdentifier());
- return info != null && info.applicationInfo.enabled;
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+ if (Flags.archiving()) {
+ callingFlag |= PackageManager.MATCH_ARCHIVED_PACKAGES;
+ }
+ final PackageInfo info =
+ mPackageManagerInternal.getPackageInfo(
+ packageName, callingFlag, callingUid, user.getIdentifier());
+ return info != null
+ && (info.applicationInfo.enabled || info.applicationInfo.isArchived);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1439,7 +1452,18 @@
if (!canAccessProfile(user.getIdentifier(), "Cannot check component")) {
return false;
}
-
+ if (Flags.archiving() && component != null && component.getPackageName() != null) {
+ List<LauncherActivityInfoInternal> archiveActivities =
+ generateLauncherActivitiesForArchivedApp(component.getPackageName(), user);
+ if (!archiveActivities.isEmpty()) {
+ for (int i = 0; i < archiveActivities.size(); i++) {
+ if (archiveActivities.get(i).getComponentName().equals(component)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
final int callingUid = injectBinderCallingUid();
final int state = mPackageManagerInternal.getComponentEnabledSetting(component,
callingUid, user.getIdentifier());
diff --git a/services/core/java/com/android/server/pm/MovePackageHelper.java b/services/core/java/com/android/server/pm/MovePackageHelper.java
index 9ad8318..f5f5577 100644
--- a/services/core/java/com/android/server/pm/MovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/MovePackageHelper.java
@@ -196,7 +196,8 @@
// If we're moving app data around, we need all the users unlocked
if (moveCompleteApp) {
for (int userId : installedUserIds) {
- if (StorageManager.isFileEncrypted() && !StorageManager.isUserKeyUnlocked(userId)) {
+ if (StorageManager.isFileEncrypted()
+ && !StorageManager.isCeStorageUnlocked(userId)) {
freezer.close();
throw new PackageManagerException(MOVE_FAILED_LOCKED_USER,
"User " + userId + " must be unlocked");
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6b4ac5b4..c9303f2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3357,7 +3357,7 @@
UserManagerInternal umInternal = mInjector.getUserManagerInternal();
StorageManagerInternal smInternal = mInjector.getLocalService(StorageManagerInternal.class);
final int flags;
- if (StorageManager.isUserKeyUnlocked(userId) && smInternal.isCeStoragePrepared(userId)) {
+ if (StorageManager.isCeStorageUnlocked(userId) && smInternal.isCeStoragePrepared(userId)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
} else if (umInternal.isUserRunning(userId)) {
flags = StorageManager.FLAG_STORAGE_DE;
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index c725cdc..70aa19a 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -183,7 +183,7 @@
StorageManagerInternal.class);
for (UserInfo user : mPm.mUserManager.getUsers(false /* includeDying */)) {
final int flags;
- if (StorageManager.isUserKeyUnlocked(user.id)
+ if (StorageManager.isCeStorageUnlocked(user.id)
&& smInternal.isCeStoragePrepared(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
} else if (umInternal.isUserRunning(user.id)) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index bb55a39..978d8e4 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1427,7 +1427,7 @@
}
final boolean needToShowConfirmCredential = !dontAskCredential
&& mLockPatternUtils.isSecure(userId)
- && (!hasUnifiedChallenge || !StorageManager.isUserKeyUnlocked(userId));
+ && (!hasUnifiedChallenge || !StorageManager.isCeStorageUnlocked(userId));
if (needToShowConfirmCredential) {
if (onlyIfCredentialNotRequired) {
return false;
@@ -7178,9 +7178,9 @@
synchronized (mUserStates) {
state = mUserStates.get(userId, -1);
}
- // Special case, in the stopping/shutdown state user key can still be unlocked
+ // Special case: in the stopping/shutdown state, CE storage can still be unlocked.
if (state == UserState.STATE_STOPPING || state == UserState.STATE_SHUTDOWN) {
- return StorageManager.isUserKeyUnlocked(userId);
+ return StorageManager.isCeStorageUnlocked(userId);
}
return (state == UserState.STATE_RUNNING_UNLOCKING)
|| (state == UserState.STATE_RUNNING_UNLOCKED);
@@ -7197,9 +7197,9 @@
synchronized (mUserStates) {
state = mUserStates.get(userId, -1);
}
- // Special case, in the stopping/shutdown state user key can still be unlocked
+ // Special case: in the stopping/shutdown state, CE storage can still be unlocked.
if (state == UserState.STATE_STOPPING || state == UserState.STATE_SHUTDOWN) {
- return StorageManager.isUserKeyUnlocked(userId);
+ return StorageManager.isCeStorageUnlocked(userId);
}
return state == UserState.STATE_RUNNING_UNLOCKED;
}
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 bc90f5c..aadd03b 100644
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
@@ -19,7 +19,6 @@
import android.annotation.CurrentTimeMillisLong;
import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
-import android.os.BatteryConsumer;
import android.os.UserHandle;
import android.text.format.DateFormat;
import android.util.IndentingPrintWriter;
@@ -66,17 +65,7 @@
aggregatedPowerStatsConfig.getPowerComponentsAggregatedStatsConfigs();
mPowerComponentStats = new PowerComponentAggregatedPowerStats[configs.size()];
for (int i = 0; i < configs.size(); i++) {
- mPowerComponentStats[i] = createPowerComponentAggregatedPowerStats(configs.get(i));
- }
- }
-
- private PowerComponentAggregatedPowerStats createPowerComponentAggregatedPowerStats(
- AggregatedPowerStatsConfig.PowerComponent config) {
- switch (config.getPowerComponentId()) {
- case BatteryConsumer.POWER_COMPONENT_CPU:
- return new CpuAggregatedPowerStats(config);
- default:
- return new PowerComponentAggregatedPowerStats(config);
+ mPowerComponentStats[i] = new PowerComponentAggregatedPowerStats(configs.get(i));
}
}
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 477c228..43fd15d 100644
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
@@ -16,13 +16,16 @@
package com.android.server.power.stats;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.os.BatteryConsumer;
import com.android.internal.os.MultiStateStats;
+import com.android.internal.os.PowerStats;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -73,6 +76,7 @@
private final int mPowerComponentId;
private @TrackedState int[] mTrackedDeviceStates;
private @TrackedState int[] mTrackedUidStates;
+ private AggregatedPowerStatsProcessor mProcessor = NO_OP_PROCESSOR;
PowerComponent(int powerComponentId) {
this.mPowerComponentId = powerComponentId;
@@ -94,6 +98,16 @@
return this;
}
+ /**
+ * Takes an object that should be invoked for every aggregated stats span
+ * 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) {
+ mProcessor = processor;
+ return this;
+ }
+
public int getPowerComponentId() {
return mPowerComponentId;
}
@@ -123,6 +137,11 @@
};
}
+ @NonNull
+ public AggregatedPowerStatsProcessor getProcessor() {
+ return mProcessor;
+ }
+
private boolean isTracked(int[] trackedStates, int state) {
if (trackedStates == null) {
return false;
@@ -153,4 +172,21 @@
public List<PowerComponent> getPowerComponentsAggregatedStatsConfigs() {
return mPowerComponents;
}
+
+ private static final AggregatedPowerStatsProcessor NO_OP_PROCESSOR =
+ new AggregatedPowerStatsProcessor() {
+ @Override
+ public void finish(PowerComponentAggregatedPowerStats stats) {
+ }
+
+ @Override
+ public String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
+ return Arrays.toString(stats);
+ }
+
+ @Override
+ public String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
+ return Arrays.toString(stats);
+ }
+ };
}
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java
new file mode 100644
index 0000000..5fd8ddf
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsProcessor.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2023 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.annotation.Nullable;
+import android.util.Log;
+
+import com.android.internal.os.MultiStateStats;
+import com.android.internal.os.PowerStats;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/*
+ * The power estimation algorithm used by AggregatedPowerStatsProcessor can roughly be
+ * described like this:
+ *
+ * 1. Estimate power usage for each state combination (e.g. power-battery/screen-on) using
+ * a metric such as CPU time-in-state.
+ *
+ * 2. Combine estimates obtain in step 1, aggregating across states that are *not* tracked
+ * per UID.
+ *
+ * 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";
+
+ private static final int INDEX_DOES_NOT_EXIST = -1;
+ private static final double MILLIAMPHOUR_PER_MICROCOULOMB = 1.0 / 1000.0 / 60.0 / 60.0;
+
+ abstract void finish(PowerComponentAggregatedPowerStats stats);
+
+ abstract String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats);
+
+ abstract String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats);
+
+ protected static class PowerEstimationPlan {
+ private final AggregatedPowerStatsConfig.PowerComponent mConfig;
+ public List<DeviceStateEstimation> deviceStateEstimations = new ArrayList<>();
+ public List<CombinedDeviceStateEstimate> combinedDeviceStateEstimations = new ArrayList<>();
+ public List<UidStateEstimate> uidStateEstimates = new ArrayList<>();
+
+ public PowerEstimationPlan(AggregatedPowerStatsConfig.PowerComponent config) {
+ mConfig = config;
+ addDeviceStateEstimations();
+ combineDeviceStateEstimations();
+ addUidStateEstimations();
+ }
+
+ private void addDeviceStateEstimations() {
+ MultiStateStats.States[] config = mConfig.getDeviceStateConfig();
+ int[][] deviceStateCombinations = getAllTrackedStateCombinations(config);
+ for (int[] deviceStateCombination : deviceStateCombinations) {
+ deviceStateEstimations.add(
+ new DeviceStateEstimation(config, deviceStateCombination));
+ }
+ }
+
+ private void combineDeviceStateEstimations() {
+ MultiStateStats.States[] deviceStateConfig = mConfig.getDeviceStateConfig();
+ MultiStateStats.States[] uidStateConfig = mConfig.getUidStateConfig();
+ MultiStateStats.States[] deviceStatesTrackedPerUid =
+ new MultiStateStats.States[deviceStateConfig.length];
+
+ for (int i = 0; i < deviceStateConfig.length; i++) {
+ if (!deviceStateConfig[i].isTracked()) {
+ continue;
+ }
+
+ int index = findTrackedStateByName(uidStateConfig, deviceStateConfig[i].getName());
+ if (index != INDEX_DOES_NOT_EXIST && uidStateConfig[index].isTracked()) {
+ deviceStatesTrackedPerUid[i] = deviceStateConfig[i];
+ }
+ }
+
+ combineDeviceStateEstimationsRecursively(deviceStateConfig, deviceStatesTrackedPerUid,
+ new int[deviceStateConfig.length], 0);
+ }
+
+ private void combineDeviceStateEstimationsRecursively(
+ MultiStateStats.States[] deviceStateConfig,
+ MultiStateStats.States[] deviceStatesTrackedPerUid, int[] stateValues, int state) {
+ if (state >= deviceStateConfig.length) {
+ DeviceStateEstimation dse = getDeviceStateEstimate(stateValues);
+ CombinedDeviceStateEstimate cdse = getCombinedDeviceStateEstimate(
+ deviceStatesTrackedPerUid, stateValues);
+ if (cdse == null) {
+ cdse = new CombinedDeviceStateEstimate(deviceStatesTrackedPerUid, stateValues);
+ combinedDeviceStateEstimations.add(cdse);
+ }
+ cdse.deviceStateEstimations.add(dse);
+ return;
+ }
+
+ if (deviceStateConfig[state].isTracked()) {
+ for (int stateValue = 0;
+ stateValue < deviceStateConfig[state].getLabels().length;
+ stateValue++) {
+ stateValues[state] = stateValue;
+ combineDeviceStateEstimationsRecursively(deviceStateConfig,
+ deviceStatesTrackedPerUid, stateValues, state + 1);
+ }
+ } else {
+ combineDeviceStateEstimationsRecursively(deviceStateConfig,
+ deviceStatesTrackedPerUid, stateValues, state + 1);
+ }
+ }
+
+ private void addUidStateEstimations() {
+ MultiStateStats.States[] deviceStateConfig = mConfig.getDeviceStateConfig();
+ MultiStateStats.States[] uidStateConfig = mConfig.getUidStateConfig();
+ MultiStateStats.States[] uidStatesTrackedForDevice =
+ new MultiStateStats.States[uidStateConfig.length];
+ MultiStateStats.States[] uidStatesNotTrackedForDevice =
+ new MultiStateStats.States[uidStateConfig.length];
+
+ for (int i = 0; i < uidStateConfig.length; i++) {
+ if (!uidStateConfig[i].isTracked()) {
+ continue;
+ }
+
+ int index = findTrackedStateByName(deviceStateConfig, uidStateConfig[i].getName());
+ if (index != INDEX_DOES_NOT_EXIST && deviceStateConfig[index].isTracked()) {
+ uidStatesTrackedForDevice[i] = uidStateConfig[i];
+ } else {
+ uidStatesNotTrackedForDevice[i] = uidStateConfig[i];
+ }
+ }
+
+ @AggregatedPowerStatsConfig.TrackedState
+ int[][] uidStateCombinations = getAllTrackedStateCombinations(uidStateConfig);
+ for (int[] stateValues : uidStateCombinations) {
+ CombinedDeviceStateEstimate combined =
+ getCombinedDeviceStateEstimate(uidStatesTrackedForDevice, stateValues);
+ if (combined == null) {
+ // This is not supposed to be possible
+ Log.wtf(TAG, "Mismatch in UID and combined device states: "
+ + concatLabels(uidStatesTrackedForDevice, stateValues));
+ continue;
+ }
+ UidStateEstimate uidStateEstimate = getUidStateEstimate(combined);
+ if (uidStateEstimate == null) {
+ uidStateEstimate = new UidStateEstimate(combined, uidStatesNotTrackedForDevice);
+ uidStateEstimates.add(uidStateEstimate);
+ }
+ uidStateEstimate.proportionalEstimates.add(
+ new UidStateProportionalEstimate(stateValues));
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Step 1. Compute device-wide power estimates for state combinations:\n");
+ for (DeviceStateEstimation deviceStateEstimation : deviceStateEstimations) {
+ sb.append(" ").append(deviceStateEstimation.id).append("\n");
+ }
+ sb.append("Step 2. Combine device-wide estimates that are untracked per UID:\n");
+ boolean any = false;
+ for (CombinedDeviceStateEstimate cdse : combinedDeviceStateEstimations) {
+ if (cdse.deviceStateEstimations.size() <= 1) {
+ continue;
+ }
+ any = true;
+ sb.append(" ").append(cdse.id).append(": ");
+ for (int i = 0; i < cdse.deviceStateEstimations.size(); i++) {
+ if (i != 0) {
+ sb.append(" + ");
+ }
+ sb.append(cdse.deviceStateEstimations.get(i).id);
+ }
+ sb.append("\n");
+ }
+ if (!any) {
+ sb.append(" N/A\n");
+ }
+ sb.append("Step 3. Proportionally distribute power estimates to UIDs:\n");
+ for (UidStateEstimate uidStateEstimate : uidStateEstimates) {
+ sb.append(" ").append(uidStateEstimate.combinedDeviceStateEstimate.id)
+ .append("\n among: ");
+ for (int i = 0; i < uidStateEstimate.proportionalEstimates.size(); i++) {
+ UidStateProportionalEstimate uspe =
+ uidStateEstimate.proportionalEstimates.get(i);
+ if (i != 0) {
+ sb.append(", ");
+ }
+ sb.append(concatLabels(uidStateEstimate.states, uspe.stateValues));
+ }
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
+
+ @Nullable
+ public DeviceStateEstimation getDeviceStateEstimate(
+ @AggregatedPowerStatsConfig.TrackedState int[] stateValues) {
+ String label = concatLabels(mConfig.getDeviceStateConfig(), stateValues);
+ for (int i = 0; i < deviceStateEstimations.size(); i++) {
+ DeviceStateEstimation deviceStateEstimation = this.deviceStateEstimations.get(i);
+ if (deviceStateEstimation.id.equals(label)) {
+ return deviceStateEstimation;
+ }
+ }
+ return null;
+ }
+
+ public CombinedDeviceStateEstimate getCombinedDeviceStateEstimate(
+ MultiStateStats.States[] deviceStates,
+ @AggregatedPowerStatsConfig.TrackedState int[] stateValues) {
+ String label = concatLabels(deviceStates, stateValues);
+ for (int i = 0; i < combinedDeviceStateEstimations.size(); i++) {
+ CombinedDeviceStateEstimate cdse = combinedDeviceStateEstimations.get(i);
+ if (cdse.id.equals(label)) {
+ return cdse;
+ }
+ }
+ return null;
+ }
+
+ public UidStateEstimate getUidStateEstimate(CombinedDeviceStateEstimate combined) {
+ for (int i = 0; i < uidStateEstimates.size(); i++) {
+ UidStateEstimate uidStateEstimate = uidStateEstimates.get(i);
+ if (uidStateEstimate.combinedDeviceStateEstimate == combined) {
+ return uidStateEstimate;
+ }
+ }
+ return null;
+ }
+
+ public void resetIntermediates() {
+ for (int i = deviceStateEstimations.size() - 1; i >= 0; i--) {
+ deviceStateEstimations.get(i).intermediates = null;
+ }
+ for (int i = deviceStateEstimations.size() - 1; i >= 0; i--) {
+ deviceStateEstimations.get(i).intermediates = null;
+ }
+ for (int i = uidStateEstimates.size() - 1; i >= 0; i--) {
+ UidStateEstimate uidStateEstimate = uidStateEstimates.get(i);
+ List<UidStateProportionalEstimate> proportionalEstimates =
+ uidStateEstimate.proportionalEstimates;
+ for (int j = proportionalEstimates.size() - 1; j >= 0; j--) {
+ proportionalEstimates.get(j).intermediates = null;
+ }
+ }
+ }
+ }
+
+ protected static class DeviceStateEstimation {
+ public final String id;
+ @AggregatedPowerStatsConfig.TrackedState
+ public final int[] stateValues;
+ public Object intermediates;
+
+ public DeviceStateEstimation(MultiStateStats.States[] config,
+ @AggregatedPowerStatsConfig.TrackedState int[] stateValues) {
+ id = concatLabels(config, stateValues);
+ this.stateValues = stateValues;
+ }
+ }
+
+ protected static class CombinedDeviceStateEstimate {
+ public final String id;
+ public List<DeviceStateEstimation> deviceStateEstimations = new ArrayList<>();
+ public Object intermediates;
+
+ public CombinedDeviceStateEstimate(MultiStateStats.States[] config,
+ @AggregatedPowerStatsConfig.TrackedState int[] stateValues) {
+ id = concatLabels(config, stateValues);
+ }
+ }
+
+ protected static class UidStateEstimate {
+ public final MultiStateStats.States[] states;
+ public CombinedDeviceStateEstimate combinedDeviceStateEstimate;
+ public List<UidStateProportionalEstimate> proportionalEstimates = new ArrayList<>();
+
+ public UidStateEstimate(CombinedDeviceStateEstimate combined,
+ MultiStateStats.States[] states) {
+ combinedDeviceStateEstimate = combined;
+ this.states = states;
+ }
+ }
+
+ protected static class UidStateProportionalEstimate {
+ @AggregatedPowerStatsConfig.TrackedState
+ public final int[] stateValues;
+ public Object intermediates;
+
+ protected UidStateProportionalEstimate(
+ @AggregatedPowerStatsConfig.TrackedState int[] stateValues) {
+ this.stateValues = stateValues;
+ }
+ }
+
+ private static int findTrackedStateByName(MultiStateStats.States[] states, String name) {
+ for (int i = 0; i < states.length; i++) {
+ if (states[i].getName().equals(name)) {
+ return i;
+ }
+ }
+ return INDEX_DOES_NOT_EXIST;
+ }
+
+ @NonNull
+ private static String concatLabels(MultiStateStats.States[] config,
+ @AggregatedPowerStatsConfig.TrackedState int[] stateValues) {
+ List<String> labels = new ArrayList<>();
+ for (int state = 0; state < config.length; state++) {
+ if (config[state] != null && config[state].isTracked()) {
+ labels.add(config[state].getName()
+ + "=" + config[state].getLabels()[stateValues[state]]);
+ }
+ }
+ Collections.sort(labels);
+ return labels.toString();
+ }
+
+ @AggregatedPowerStatsConfig.TrackedState
+ private static int[][] getAllTrackedStateCombinations(MultiStateStats.States[] states) {
+ List<int[]> combinations = new ArrayList<>();
+ MultiStateStats.States.forEachTrackedStateCombination(states, stateValues -> {
+ combinations.add(Arrays.copyOf(stateValues, stateValues.length));
+ });
+ return combinations.toArray(new int[combinations.size()][0]);
+ }
+
+ public static double uCtoMah(long chargeUC) {
+ return chargeUC * MILLIAMPHOUR_PER_MICROCOULOMB;
+ }
+}
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 a9c2bc2..a6558e0 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -10937,7 +10937,8 @@
}
mCpuPowerStatsCollector = new CpuPowerStatsCollector(mCpuScalingPolicies, mPowerProfile,
- mHandler, mBatteryStatsConfig.getPowerStatsThrottlePeriodCpu());
+ () -> mBatteryVoltageMv, mHandler,
+ mBatteryStatsConfig.getPowerStatsThrottlePeriodCpu());
mCpuPowerStatsCollector.addConsumer(this::recordPowerStats);
mStartCount++;
@@ -14437,6 +14438,7 @@
final int level, /* not final */ int temp, final int voltageMv, final int chargeUah,
final int chargeFullUah, final long chargeTimeToFullSeconds,
final long elapsedRealtimeMs, final long uptimeMs, final long currentTimeMs) {
+
// Temperature is encoded without the signed bit, so clamp any negative temperatures to 0.
temp = Math.max(0, temp);
@@ -15621,18 +15623,6 @@
}
}
- @GuardedBy("this")
- private void dumpCpuPowerBracketsLocked(PrintWriter pw) {
- pw.println("CPU power brackets; cluster/freq in MHz(avg current in mA):");
- final int bracketCount = mPowerProfile.getCpuPowerBracketCount();
- for (int bracket = 0; bracket < bracketCount; bracket++) {
- pw.print(" ");
- pw.print(bracket);
- pw.print(": ");
- pw.println(mPowerProfile.getCpuPowerBracketDescription(mCpuScalingPolicies, bracket));
- }
- }
-
/**
* Dump EnergyConsumer stats
*/
@@ -16989,8 +16979,10 @@
pw.println();
dumpConstantsLocked(pw);
- pw.println();
- dumpCpuPowerBracketsLocked(pw);
+ if (mCpuPowerStatsCollector != null) {
+ pw.println();
+ mCpuPowerStatsCollector.dumpCpuPowerBracketsLocked(pw);
+ }
pw.println();
dumpEnergyConsumerStatsLocked(pw);
diff --git a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStats.java
deleted file mode 100644
index fbf6928..0000000
--- a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStats.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2023 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;
-
-class CpuAggregatedPowerStats extends PowerComponentAggregatedPowerStats {
- CpuAggregatedPowerStats(AggregatedPowerStatsConfig.PowerComponent config) {
- super(config);
- }
-}
diff --git a/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java
new file mode 100644
index 0000000..f40eef2
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/CpuAggregatedPowerStatsProcessor.java
@@ -0,0 +1,545 @@
+/*
+ * Copyright (C) 2023 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.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.os.CpuScalingPolicies;
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+public class CpuAggregatedPowerStatsProcessor extends AggregatedPowerStatsProcessor {
+ private static final String TAG = "CpuAggregatedPowerStatsProcessor";
+
+ private static final double HOUR_IN_MILLIS = TimeUnit.HOURS.toMillis(1);
+ private static final int UNKNOWN = -1;
+
+ private final CpuScalingPolicies mCpuScalingPolicies;
+ // Number of CPU core clusters
+ private final int mCpuClusterCount;
+ // Total number of CPU scaling steps across all clusters
+ private final int mCpuScalingStepCount;
+ // Map of scaling step to the corresponding core cluster mScalingStepToCluster[step]->cluster
+ private final int[] mScalingStepToCluster;
+ // Average power consumed by the CPU when it is powered up (per power_profile.xml)
+ private final double mPowerMultiplierForCpuActive;
+ // Average power consumed by each cluster when it is powered up (per power_profile.xml)
+ private final double[] mPowerMultipliersByCluster;
+ // Average power consumed by each scaling step when running code (per power_profile.xml)
+ private final double[] mPowerMultipliersByScalingStep;
+ // A map used to combine energy consumers into a smaller set, in case power brackets
+ // are defined in a way that does not allow an unambiguous mapping of energy consumers to
+ // brackets
+ private int[] mEnergyConsumerToCombinedEnergyConsumerMap;
+ // A map of combined energy consumers to the corresponding collections of power brackets.
+ // For example, if there are two CPU_CLUSTER rails and each maps to three brackets,
+ // this map will look like this:
+ // 0 : [0, 1, 2]
+ // 1 : [3, 4, 5]
+ private int[][] mCombinedEnergyConsumerToPowerBracketMap;
+
+ // Cached reference to a PowerStats descriptor. Almost never changes in practice,
+ // helping to avoid reparsing the descriptor for every PowerStats span.
+ private PowerStats.Descriptor mLastUsedDescriptor;
+ // Cached results of parsing of current PowerStats.Descriptor. Only refreshed when
+ // mLastUsedDescriptor changes
+ private CpuPowerStatsCollector.StatsArrayLayout mStatsLayout;
+ // Sequence of steps for power estimation and intermediate results.
+ private PowerEstimationPlan mPlan;
+
+ // Temp array for retrieval of device power stats, to avoid repeated allocations
+ private long[] mTmpDeviceStatsArray;
+ // Temp array for retrieval of UID power stats, to avoid repeated allocations
+ private long[] mTmpUidStatsArray;
+
+ public CpuAggregatedPowerStatsProcessor(PowerProfile powerProfile,
+ CpuScalingPolicies scalingPolicies) {
+ mCpuScalingPolicies = scalingPolicies;
+ mCpuScalingStepCount = scalingPolicies.getScalingStepCount();
+ mScalingStepToCluster = new int[mCpuScalingStepCount];
+ mPowerMultipliersByScalingStep = new double[mCpuScalingStepCount];
+
+ int step = 0;
+ int[] policies = scalingPolicies.getPolicies();
+ mCpuClusterCount = policies.length;
+ mPowerMultipliersByCluster = new double[mCpuClusterCount];
+ for (int cluster = 0; cluster < mCpuClusterCount; cluster++) {
+ int policy = policies[cluster];
+ mPowerMultipliersByCluster[cluster] =
+ powerProfile.getAveragePowerForCpuScalingPolicy(policy) / HOUR_IN_MILLIS;
+ int[] frequencies = scalingPolicies.getFrequencies(policy);
+ for (int i = 0; i < frequencies.length; i++) {
+ mScalingStepToCluster[step] = cluster;
+ mPowerMultipliersByScalingStep[step] =
+ powerProfile.getAveragePowerForCpuScalingStep(policy, i) / HOUR_IN_MILLIS;
+ step++;
+ }
+ }
+ mPowerMultiplierForCpuActive =
+ powerProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE) / HOUR_IN_MILLIS;
+ }
+
+ private void unpackPowerStatsDescriptor(PowerStats.Descriptor descriptor) {
+ if (descriptor.equals(mLastUsedDescriptor)) {
+ return;
+ }
+
+ mLastUsedDescriptor = descriptor;
+ mStatsLayout = new CpuPowerStatsCollector.StatsArrayLayout();
+ mStatsLayout.fromExtras(descriptor.extras);
+
+ mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
+ mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
+ }
+
+ /**
+ * Temporary struct to capture intermediate results of power estimation.
+ */
+ private static final class Intermediates {
+ public long uptime;
+ // Sum of all time-in-step values, combining all time-in-step durations across all cores.
+ public long cumulativeTime;
+ // CPU activity durations per cluster
+ public long[] timeByCluster;
+ // Sums of time-in-step values, aggregated by cluster, combining all cores in the cluster.
+ public long[] cumulativeTimeByCluster;
+ public long[] timeByScalingStep;
+ public double[] powerByCluster;
+ public double[] powerByScalingStep;
+ public long[] powerByEnergyConsumer;
+ }
+
+ /**
+ * Temporary struct to capture intermediate results of power estimation.
+ */
+ private static class DeviceStatsIntermediates {
+ public double power;
+ public long[] timeByBracket;
+ public double[] powerByBracket;
+ }
+
+ @Override
+ public void finish(PowerComponentAggregatedPowerStats stats) {
+ if (stats.getPowerStatsDescriptor() == null) {
+ return;
+ }
+
+ unpackPowerStatsDescriptor(stats.getPowerStatsDescriptor());
+
+ if (mPlan == null) {
+ mPlan = new PowerEstimationPlan(stats.getConfig());
+ if (mStatsLayout.getCpuClusterEnergyConsumerCount() != 0) {
+ initEnergyConsumerToPowerBracketMaps();
+ }
+ }
+
+ Intermediates intermediates = new Intermediates();
+
+ int cpuScalingStepCount = mStatsLayout.getCpuScalingStepCount();
+ if (cpuScalingStepCount != mCpuScalingStepCount) {
+ Log.e(TAG, "Mismatched CPU scaling step count in PowerStats: " + cpuScalingStepCount
+ + ", expected: " + mCpuScalingStepCount);
+ return;
+ }
+
+ int clusterCount = mStatsLayout.getCpuClusterCount();
+ if (clusterCount != mCpuClusterCount) {
+ Log.e(TAG, "Mismatched CPU cluster count in PowerStats: " + clusterCount
+ + ", expected: " + mCpuClusterCount);
+ return;
+ }
+
+ computeTotals(stats, intermediates);
+ if (intermediates.cumulativeTime == 0) {
+ return;
+ }
+
+ estimatePowerByScalingStep(intermediates);
+ estimatePowerByDeviceState(stats, intermediates);
+ 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++) {
+ estimateUidPowerConsumption(stats, uid, mPlan.uidStateEstimates.get(i));
+ }
+ }
+ }
+ mPlan.resetIntermediates();
+ }
+
+ /*
+ * Populate data structures (two maps) needed to use power rail data, aka energy consumers,
+ * to attribute power usage to apps.
+ *
+ * At this point, the algorithm covers only the most basic cases:
+ * - Each cluster is mapped to unique power brackets (possibly multiple for each cluster):
+ * CL_0: [bracket0, bracket1]
+ * CL_1: [bracket3]
+ * In this case, the consumed energy is distributed to the corresponding brackets
+ * proportionally.
+ * - Brackets span multiple clusters:
+ * CL_0: [bracket0, bracket1]
+ * CL_1: [bracket1, bracket2]
+ * CL_2: [bracket3, bracket4]
+ * In this case, we combine energy consumers into groups unambiguously mapped to
+ * brackets. In the above example, consumed energy for CL_0 and CL_1 will be combined
+ * because they both map to the same power bracket (bracket1):
+ * (CL_0+CL_1): [bracket0, bracket1, bracket2]
+ * CL_2: [bracket3, bracket4]
+ */
+ private void initEnergyConsumerToPowerBracketMaps() {
+ int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount();
+ int powerBracketCount = mStatsLayout.getCpuPowerBracketCount();
+
+ mEnergyConsumerToCombinedEnergyConsumerMap = new int[energyConsumerCount];
+ mCombinedEnergyConsumerToPowerBracketMap = new int[energyConsumerCount][];
+
+ int[] policies = mCpuScalingPolicies.getPolicies();
+ if (energyConsumerCount == policies.length) {
+ int[] scalingStepToPowerBracketMap = mStatsLayout.getScalingStepToPowerBracketMap();
+ ArraySet<Integer>[] clusterToBrackets = new ArraySet[policies.length];
+ int step = 0;
+ for (int cluster = 0; cluster < policies.length; cluster++) {
+ int[] freqs = mCpuScalingPolicies.getFrequencies(policies[cluster]);
+ clusterToBrackets[cluster] = new ArraySet<>(freqs.length);
+ for (int j = 0; j < freqs.length; j++) {
+ clusterToBrackets[cluster].add(scalingStepToPowerBracketMap[step++]);
+ }
+ }
+
+ ArraySet<Integer>[] combinedEnergyConsumers = new ArraySet[policies.length];
+ int combinedEnergyConsumersCount = 0;
+
+ for (int cluster = 0; cluster < clusterToBrackets.length; cluster++) {
+ int combineWith = UNKNOWN;
+ for (int i = 0; i < combinedEnergyConsumersCount; i++) {
+ if (containsAny(combinedEnergyConsumers[i], clusterToBrackets[cluster])) {
+ combineWith = i;
+ break;
+ }
+ }
+ if (combineWith != UNKNOWN) {
+ mEnergyConsumerToCombinedEnergyConsumerMap[cluster] = combineWith;
+ combinedEnergyConsumers[combineWith].addAll(clusterToBrackets[cluster]);
+ } else {
+ mEnergyConsumerToCombinedEnergyConsumerMap[cluster] =
+ combinedEnergyConsumersCount;
+ combinedEnergyConsumers[combinedEnergyConsumersCount++] =
+ clusterToBrackets[cluster];
+ }
+ }
+
+ for (int i = combinedEnergyConsumers.length - 1; i >= 0; i--) {
+ mCombinedEnergyConsumerToPowerBracketMap[i] =
+ new int[combinedEnergyConsumers[i].size()];
+ for (int j = combinedEnergyConsumers[i].size() - 1; j >= 0; j--) {
+ mCombinedEnergyConsumerToPowerBracketMap[i][j] =
+ combinedEnergyConsumers[i].valueAt(j);
+ }
+ }
+ } else {
+ // All CPU cluster energy consumers are combined into one, which is
+ // distributed proportionally to all power brackets.
+ int[] map = new int[powerBracketCount];
+ for (int i = 0; i < map.length; i++) {
+ map[i] = i;
+ }
+ mCombinedEnergyConsumerToPowerBracketMap[0] = map;
+ }
+ }
+
+ private static boolean containsAny(ArraySet<Integer> set1, ArraySet<Integer> set2) {
+ for (int i = 0; i < set2.size(); i++) {
+ if (set1.contains(set2.valueAt(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void computeTotals(PowerComponentAggregatedPowerStats stats,
+ Intermediates intermediates) {
+ intermediates.timeByScalingStep = new long[mCpuScalingStepCount];
+ intermediates.timeByCluster = new long[mCpuClusterCount];
+ intermediates.cumulativeTimeByCluster = new long[mCpuClusterCount];
+
+ List<DeviceStateEstimation> deviceStateEstimations = mPlan.deviceStateEstimations;
+ for (int i = deviceStateEstimations.size() - 1; i >= 0; i--) {
+ DeviceStateEstimation deviceStateEstimation = deviceStateEstimations.get(i);
+ if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStateEstimation.stateValues)) {
+ continue;
+ }
+
+ intermediates.uptime += mStatsLayout.getUptime(mTmpDeviceStatsArray);
+
+ for (int cluster = 0; cluster < mCpuClusterCount; cluster++) {
+ intermediates.timeByCluster[cluster] +=
+ mStatsLayout.getTimeByCluster(mTmpDeviceStatsArray, cluster);
+ }
+
+ for (int step = 0; step < mCpuScalingStepCount; step++) {
+ long timeInStep = mStatsLayout.getTimeByScalingStep(mTmpDeviceStatsArray, step);
+ intermediates.cumulativeTime += timeInStep;
+ intermediates.timeByScalingStep[step] += timeInStep;
+ intermediates.cumulativeTimeByCluster[mScalingStepToCluster[step]] += timeInStep;
+ }
+ }
+ }
+
+ private void estimatePowerByScalingStep(Intermediates intermediates) {
+ // CPU consumes some power when it's on - no matter which cores are running.
+ double cpuActivePower = mPowerMultiplierForCpuActive * intermediates.uptime;
+
+ // Additionally, every cluster consumes some power when any of its cores are running
+ intermediates.powerByCluster = new double[mCpuClusterCount];
+ for (int cluster = 0; cluster < mCpuClusterCount; cluster++) {
+ intermediates.powerByCluster[cluster] =
+ mPowerMultipliersByCluster[cluster] * intermediates.timeByCluster[cluster];
+ }
+
+ // Finally, additional power is consumed depending on the frequency scaling
+ intermediates.powerByScalingStep = new double[mCpuScalingStepCount];
+ for (int step = 0; step < mCpuScalingStepCount; step++) {
+ int cluster = mScalingStepToCluster[step];
+
+ double power;
+
+ // Distribute base power proportionally
+ power = cpuActivePower * intermediates.timeByScalingStep[step]
+ / intermediates.cumulativeTime;
+
+ // Distribute per-cluster power proportionally
+ long cumulativeTimeInCluster = intermediates.cumulativeTimeByCluster[cluster];
+ if (cumulativeTimeInCluster != 0) {
+ power += intermediates.powerByCluster[cluster]
+ * intermediates.timeByScalingStep[step]
+ / cumulativeTimeInCluster;
+ }
+
+ power += mPowerMultipliersByScalingStep[step] * intermediates.timeByScalingStep[step];
+
+ intermediates.powerByScalingStep[step] = power;
+ }
+ }
+
+ private void estimatePowerByDeviceState(PowerComponentAggregatedPowerStats stats,
+ Intermediates intermediates) {
+ int cpuScalingStepCount = mStatsLayout.getCpuScalingStepCount();
+ int powerBracketCount = mStatsLayout.getCpuPowerBracketCount();
+ int[] scalingStepToBracketMap = mStatsLayout.getScalingStepToPowerBracketMap();
+ int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount();
+ List<DeviceStateEstimation> deviceStateEstimations = mPlan.deviceStateEstimations;
+ for (int dse = deviceStateEstimations.size() - 1; dse >= 0; dse--) {
+ DeviceStateEstimation deviceStateEstimation = deviceStateEstimations.get(dse);
+ deviceStateEstimation.intermediates = new DeviceStatsIntermediates();
+ DeviceStatsIntermediates deviceStatsIntermediates =
+ (DeviceStatsIntermediates) deviceStateEstimation.intermediates;
+ deviceStatsIntermediates.timeByBracket = new long[powerBracketCount];
+ deviceStatsIntermediates.powerByBracket = new double[powerBracketCount];
+
+ stats.getDeviceStats(mTmpDeviceStatsArray, deviceStateEstimation.stateValues);
+ for (int step = 0; step < cpuScalingStepCount; step++) {
+ if (intermediates.timeByScalingStep[step] == 0) {
+ continue;
+ }
+
+ long timeInStep = mStatsLayout.getTimeByScalingStep(mTmpDeviceStatsArray, step);
+ double stepPower = intermediates.powerByScalingStep[step] * timeInStep
+ / intermediates.timeByScalingStep[step];
+
+ int bracket = scalingStepToBracketMap[step];
+ deviceStatsIntermediates.timeByBracket[bracket] += timeInStep;
+ deviceStatsIntermediates.powerByBracket[bracket] += stepPower;
+ }
+
+ if (energyConsumerCount != 0) {
+ adjustEstimatesUsingEnergyConsumers(intermediates, deviceStatsIntermediates);
+ }
+
+ double power = 0;
+ for (int i = deviceStatsIntermediates.powerByBracket.length - 1; i >= 0; i--) {
+ power += deviceStatsIntermediates.powerByBracket[i];
+ }
+ deviceStatsIntermediates.power = power;
+ mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power);
+ stats.setDeviceStats(deviceStateEstimation.stateValues, mTmpDeviceStatsArray);
+ }
+ }
+
+ private void adjustEstimatesUsingEnergyConsumers(
+ Intermediates intermediates, DeviceStatsIntermediates deviceStatsIntermediates) {
+ int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount();
+ if (energyConsumerCount == 0) {
+ return;
+ }
+
+ if (intermediates.powerByEnergyConsumer == null) {
+ intermediates.powerByEnergyConsumer = new long[energyConsumerCount];
+ } else {
+ Arrays.fill(intermediates.powerByEnergyConsumer, 0);
+ }
+ for (int i = 0; i < energyConsumerCount; i++) {
+ intermediates.powerByEnergyConsumer[mEnergyConsumerToCombinedEnergyConsumerMap[i]] +=
+ mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, i);
+ }
+
+ for (int combinedConsumer = mCombinedEnergyConsumerToPowerBracketMap.length - 1;
+ combinedConsumer >= 0; combinedConsumer--) {
+ int[] combinedEnergyConsumerToPowerBracketMap =
+ mCombinedEnergyConsumerToPowerBracketMap[combinedConsumer];
+ if (combinedEnergyConsumerToPowerBracketMap == null) {
+ continue;
+ }
+
+ double consumedEnergy = uCtoMah(intermediates.powerByEnergyConsumer[combinedConsumer]);
+
+ double totalModeledPower = 0;
+ for (int bracket : combinedEnergyConsumerToPowerBracketMap) {
+ totalModeledPower += deviceStatsIntermediates.powerByBracket[bracket];
+ }
+ if (totalModeledPower == 0) {
+ continue;
+ }
+
+ for (int bracket : combinedEnergyConsumerToPowerBracketMap) {
+ deviceStatsIntermediates.powerByBracket[bracket] =
+ consumedEnergy * deviceStatsIntermediates.powerByBracket[bracket]
+ / totalModeledPower;
+ }
+ }
+ }
+
+ private void combineDeviceStateEstimates() {
+ for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
+ CombinedDeviceStateEstimate cdse = mPlan.combinedDeviceStateEstimations.get(i);
+ DeviceStatsIntermediates cdseIntermediates = new DeviceStatsIntermediates();
+ cdse.intermediates = cdseIntermediates;
+ int bracketCount = mStatsLayout.getCpuPowerBracketCount();
+ cdseIntermediates.timeByBracket = new long[bracketCount];
+ cdseIntermediates.powerByBracket = new double[bracketCount];
+ List<DeviceStateEstimation> deviceStateEstimations = cdse.deviceStateEstimations;
+ for (int j = deviceStateEstimations.size() - 1; j >= 0; j--) {
+ DeviceStateEstimation dse = deviceStateEstimations.get(j);
+ DeviceStatsIntermediates intermediates =
+ (DeviceStatsIntermediates) dse.intermediates;
+ cdseIntermediates.power += intermediates.power;
+ for (int k = 0; k < bracketCount; k++) {
+ cdseIntermediates.timeByBracket[k] += intermediates.timeByBracket[k];
+ cdseIntermediates.powerByBracket[k] += intermediates.powerByBracket[k];
+ }
+ }
+ }
+ }
+
+ private void estimateUidPowerConsumption(PowerComponentAggregatedPowerStats stats, int uid,
+ UidStateEstimate uidStateEstimate) {
+ CombinedDeviceStateEstimate combinedDeviceStateEstimate =
+ uidStateEstimate.combinedDeviceStateEstimate;
+ DeviceStatsIntermediates cdsIntermediates =
+ (DeviceStatsIntermediates) combinedDeviceStateEstimate.intermediates;
+ for (int i = 0; i < uidStateEstimate.proportionalEstimates.size(); i++) {
+ UidStateProportionalEstimate proportionalEstimate =
+ uidStateEstimate.proportionalEstimates.get(i);
+ if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) {
+ continue;
+ }
+
+ double power = 0;
+ for (int bracket = 0; bracket < mStatsLayout.getCpuPowerBracketCount(); bracket++) {
+ if (cdsIntermediates.timeByBracket[bracket] == 0) {
+ continue;
+ }
+
+ long timeInBracket = mStatsLayout.getUidTimeByPowerBracket(mTmpUidStatsArray,
+ bracket);
+ if (timeInBracket == 0) {
+ continue;
+ }
+
+ power += cdsIntermediates.powerByBracket[bracket] * timeInBracket
+ / cdsIntermediates.timeByBracket[bracket];
+ }
+
+ mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+ stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+ }
+ }
+
+ @Override
+ public String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
+ unpackPowerStatsDescriptor(descriptor);
+ StringBuilder sb = new StringBuilder();
+ int cpuScalingStepCount = mStatsLayout.getCpuScalingStepCount();
+ sb.append("steps: [");
+ for (int step = 0; step < cpuScalingStepCount; step++) {
+ if (step != 0) {
+ sb.append(", ");
+ }
+ sb.append(mStatsLayout.getTimeByScalingStep(stats, step));
+ }
+ int clusterCount = mStatsLayout.getCpuClusterCount();
+ sb.append("] clusters: [");
+ for (int cluster = 0; cluster < clusterCount; cluster++) {
+ if (cluster != 0) {
+ sb.append(", ");
+ }
+ sb.append(mStatsLayout.getTimeByCluster(stats, cluster));
+ }
+ sb.append("] uptime: ").append(mStatsLayout.getUptime(stats));
+ int energyConsumerCount = mStatsLayout.getCpuClusterEnergyConsumerCount();
+ if (energyConsumerCount > 0) {
+ sb.append(" energy: [");
+ for (int i = 0; i < energyConsumerCount; i++) {
+ if (i != 0) {
+ sb.append(", ");
+ }
+ sb.append(mStatsLayout.getConsumedEnergy(stats, i));
+ }
+ sb.append("]");
+ }
+ sb.append(" power: ").append(
+ BatteryStats.formatCharge(mStatsLayout.getDevicePowerEstimate(stats)));
+ return sb.toString();
+ }
+
+ @Override
+ public String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
+ unpackPowerStatsDescriptor(descriptor);
+ StringBuilder sb = new StringBuilder();
+ sb.append("[");
+ int powerBracketCount = mStatsLayout.getCpuPowerBracketCount();
+ for (int bracket = 0; bracket < powerBracketCount; bracket++) {
+ if (bracket != 0) {
+ sb.append(", ");
+ }
+ sb.append(mStatsLayout.getUidTimeByPowerBracket(stats, bracket));
+ }
+ sb.append("] power: ").append(
+ BatteryStats.formatCharge(mStatsLayout.getUidPowerEstimate(stats)));
+ return sb.toString();
+ }
+}
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 376ca89..a388932 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
@@ -16,19 +16,39 @@
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.power.PowerStatsInternal;
+import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.Keep;
import com.android.internal.annotations.VisibleForNative;
+import com.android.internal.annotations.VisibleForTesting;
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 com.android.server.LocalServices;
import com.android.server.power.optimization.Flags;
+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.
* <p>
@@ -36,45 +56,350 @@
* constructor. Thus the object is not thread-safe except where noted.
*/
public class CpuPowerStatsCollector extends PowerStatsCollector {
+ private static final String TAG = "CpuPowerStatsCollector";
private static final long NANOS_PER_MILLIS = 1000000;
+ private static final long ENERGY_UNSPECIFIED = -1;
+ private static final int DEFAULT_CPU_POWER_BRACKETS = 3;
+ private static final int DEFAULT_CPU_POWER_BRACKETS_PER_ENERGY_CONSUMER = 2;
+ private static final long POWER_STATS_ENERGY_CONSUMERS_TIMEOUT = 20000;
+ private boolean mIsInitialized;
+ private final CpuScalingPolicies mCpuScalingPolicies;
+ private final PowerProfile mPowerProfile;
private final KernelCpuStatsReader mKernelCpuStatsReader;
- private final int[] mScalingStepToPowerBracketMap;
- private final long[] mTempUidStats;
+ private final Supplier<PowerStatsInternal> mPowerStatsSupplier;
+ private final IntSupplier mVoltageSupplier;
+ private final int mDefaultCpuPowerBrackets;
+ private final int mDefaultCpuPowerBracketsPerEnergyConsumer;
+ private long[] mCpuTimeByScalingStep;
+ private long[] mTempCpuTimeByScalingStep;
+ private long[] mTempUidStats;
private final SparseArray<UidStats> mUidStats = new SparseArray<>();
- private final int mUidStatsSize;
+ private boolean mIsPerUidTimeInStateSupported;
+ private PowerStatsInternal mPowerStatsInternal;
+ private int[] mCpuEnergyConsumerIds;
+ private PowerStats.Descriptor mPowerStatsDescriptor;
// Reusable instance
- private final PowerStats mCpuPowerStats;
+ private PowerStats mCpuPowerStats;
+ private StatsArrayLayout 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 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_DEVICE_POWER_POSITION = "dp";
+ private static final String EXTRA_DEVICE_UPTIME_POSITION = "du";
+ 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_BRACKETS_POSITION = "ub";
+ private static final String EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET = "us";
+ private static final String EXTRA_UID_POWER_POSITION = "up";
+
+ private static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0;
+
+ private int mDeviceStatsArrayLength;
+ private int mUidStatsArrayLength;
+
+ private int mDeviceCpuTimeByScalingStepPosition;
+ private int mDeviceCpuTimeByScalingStepCount;
+ private int mDeviceCpuTimeByClusterPosition;
+ private int mDeviceCpuTimeByClusterCount;
+ private int mDeviceCpuUptimePosition;
+ private int mDeviceEnergyConsumerPosition;
+ private int mDeviceEnergyConsumerCount;
+ private int mDevicePowerEstimatePosition;
+
+ private int mUidPowerBracketsPosition;
+ private int mUidPowerBracketCount;
+ private int[][] mEnergyConsumerToPowerBucketMaps;
+ private int mUidPowerEstimatePosition;
+
+ private int[] mScalingStepToPowerBracketMap;
+
+ public int getDeviceStatsArrayLength() {
+ return mDeviceStatsArrayLength;
+ }
+
+ public int getUidStatsArrayLength() {
+ return mUidStatsArrayLength;
+ }
+
+ /**
+ * Declare that the stats array has a section capturing CPU time per scaling step
+ */
+ public void addDeviceSectionCpuTimeByScalingStep(int scalingStepCount) {
+ mDeviceCpuTimeByScalingStepPosition = mDeviceStatsArrayLength;
+ mDeviceCpuTimeByScalingStepCount = scalingStepCount;
+ mDeviceStatsArrayLength += 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) {
+ mDeviceCpuTimeByClusterCount = clusterCount;
+ mDeviceCpuTimeByClusterPosition = mDeviceStatsArrayLength;
+ mDeviceStatsArrayLength += 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 stats array has a section capturing CPU uptime
+ */
+ public void addDeviceSectionUptime() {
+ mDeviceCpuUptimePosition = mDeviceStatsArrayLength++;
+ }
+
+ /**
+ * Saves the CPU uptime duration in the corresponding <code>stats</code> element.
+ */
+ public void setUptime(long[] stats, long value) {
+ stats[mDeviceCpuUptimePosition] = value;
+ }
+
+ /**
+ * Extracts the CPU uptime duration from the corresponding <code>stats</code> element.
+ */
+ public long getUptime(long[] stats) {
+ return stats[mDeviceCpuUptimePosition];
+ }
+
+ /**
+ * Declares that the stats array has a section capturing EnergyConsumer data from
+ * PowerStatsService.
+ */
+ public void addDeviceSectionEnergyConsumers(int energyConsumerCount) {
+ mDeviceEnergyConsumerPosition = mDeviceStatsArrayLength;
+ mDeviceEnergyConsumerCount = energyConsumerCount;
+ mDeviceStatsArrayLength += energyConsumerCount;
+ }
+
+ public int getCpuClusterEnergyConsumerCount() {
+ 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 = mDeviceStatsArrayLength++;
+ }
+
+ /**
+ * 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 CPU time per power bracket.
+ */
+ public void addUidSectionCpuTimeByPowerBracket(int[] scalingStepToPowerBracketMap) {
+ mScalingStepToPowerBracketMap = scalingStepToPowerBracketMap;
+ mUidPowerBracketsPosition = mUidStatsArrayLength;
+ updatePowerBracketCount();
+ mUidStatsArrayLength += 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];
+ }
+
+ /**
+ * Declare that the UID stats array has a section capturing a power estimate
+ */
+ public void addUidSectionPowerEstimate() {
+ mUidPowerEstimatePosition = mUidStatsArrayLength++;
+ }
+
+ /**
+ * 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_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_DEVICE_UPTIME_POSITION, mDeviceCpuUptimePosition);
+ 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_BRACKETS_POSITION, mUidPowerBracketsPosition);
+ extras.putIntArray(EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET,
+ mScalingStepToPowerBracketMap);
+ extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition);
+ }
+
+ /**
+ * Retrieves elements of the stats array layout from <code>extras</code>
+ */
+ public void fromExtras(PersistableBundle 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);
+ mDeviceCpuUptimePosition = extras.getInt(EXTRA_DEVICE_UPTIME_POSITION);
+ mDeviceEnergyConsumerPosition = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION);
+ mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT);
+ mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION);
+ mUidPowerBracketsPosition = extras.getInt(EXTRA_UID_BRACKETS_POSITION);
+ mScalingStepToPowerBracketMap =
+ extras.getIntArray(EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET);
+ if (mScalingStepToPowerBracketMap == null) {
+ mScalingStepToPowerBracketMap = new int[mDeviceCpuTimeByScalingStepCount];
+ }
+ updatePowerBracketCount();
+ mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION);
+ }
+ }
public CpuPowerStatsCollector(CpuScalingPolicies cpuScalingPolicies, PowerProfile powerProfile,
- Handler handler, long throttlePeriodMs) {
+ IntSupplier voltageSupplier, Handler handler, long throttlePeriodMs) {
this(cpuScalingPolicies, powerProfile, handler, new KernelCpuStatsReader(),
- throttlePeriodMs, Clock.SYSTEM_CLOCK);
+ () -> 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,
- long throttlePeriodMs, Clock clock) {
+ Supplier<PowerStatsInternal> powerStatsSupplier,
+ IntSupplier voltageSupplier, long throttlePeriodMs, Clock clock,
+ int defaultCpuPowerBrackets, int defaultCpuPowerBracketsPerEnergyConsumer) {
super(handler, throttlePeriodMs, clock);
+ mCpuScalingPolicies = cpuScalingPolicies;
+ mPowerProfile = powerProfile;
mKernelCpuStatsReader = kernelCpuStatsReader;
+ mPowerStatsSupplier = powerStatsSupplier;
+ mVoltageSupplier = voltageSupplier;
+ mDefaultCpuPowerBrackets = defaultCpuPowerBrackets;
+ mDefaultCpuPowerBracketsPerEnergyConsumer = defaultCpuPowerBracketsPerEnergyConsumer;
- int scalingStepCount = cpuScalingPolicies.getScalingStepCount();
- mScalingStepToPowerBracketMap = new int[scalingStepCount];
- int index = 0;
- for (int policy : cpuScalingPolicies.getPolicies()) {
- int[] frequencies = cpuScalingPolicies.getFrequencies(policy);
- for (int step = 0; step < frequencies.length; step++) {
- int bracket = powerProfile.getCpuPowerBracketForScalingStep(policy, step);
- mScalingStepToPowerBracketMap[index++] = bracket;
- }
- }
- mUidStatsSize = powerProfile.getCpuPowerBracketCount();
- mTempUidStats = new long[mUidStatsSize];
-
- mCpuPowerStats = new PowerStats(
- new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, 0, mUidStatsSize,
- new PersistableBundle()));
}
/**
@@ -84,43 +409,355 @@
setEnabled(Flags.streamlinedBatteryStats());
}
+ private void ensureInitialized() {
+ if (mIsInitialized) {
+ return;
+ }
+
+ if (!isEnabled()) {
+ return;
+ }
+
+ mIsPerUidTimeInStateSupported = mKernelCpuStatsReader.nativeIsSupportedFeature();
+ mPowerStatsInternal = mPowerStatsSupplier.get();
+
+ if (mPowerStatsInternal != null) {
+ readCpuEnergyConsumerIds();
+ } else {
+ mCpuEnergyConsumerIds = new int[0];
+ }
+
+ int cpuScalingStepCount = mCpuScalingPolicies.getScalingStepCount();
+ mCpuTimeByScalingStep = new long[cpuScalingStepCount];
+ mTempCpuTimeByScalingStep = new long[cpuScalingStepCount];
+ int[] scalingStepToPowerBracketMap = initPowerBrackets();
+
+ mLayout = new StatsArrayLayout();
+ mLayout.addDeviceSectionCpuTimeByScalingStep(cpuScalingStepCount);
+ mLayout.addDeviceSectionCpuTimeByCluster(mCpuScalingPolicies.getPolicies().length);
+ mLayout.addDeviceSectionUptime();
+ mLayout.addDeviceSectionEnergyConsumers(mCpuEnergyConsumerIds.length);
+ mLayout.addDeviceSectionPowerEstimate();
+ mLayout.addUidSectionCpuTimeByPowerBracket(scalingStepToPowerBracketMap);
+ mLayout.addUidSectionPowerEstimate();
+
+ PersistableBundle extras = new PersistableBundle();
+ mLayout.toExtras(extras);
+
+ mPowerStatsDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU,
+ mLayout.getDeviceStatsArrayLength(), mLayout.getUidStatsArrayLength(), extras);
+ mCpuPowerStats = new PowerStats(mPowerStatsDescriptor);
+
+ mTempUidStats = new long[mLayout.getCpuPowerBracketCount()];
+
+ mIsInitialized = true;
+ }
+
+ private void readCpuEnergyConsumerIds() {
+ EnergyConsumer[] energyConsumerInfo = mPowerStatsInternal.getEnergyConsumerInfo();
+ if (energyConsumerInfo == null) {
+ mCpuEnergyConsumerIds = new int[0];
+ 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();
+ } else if (mCpuEnergyConsumerIds.length == 0 || mCpuEnergyConsumerIds.length == 1) {
+ return initDefaultPowerBrackets(mDefaultCpuPowerBrackets);
+ } else if (mCpuScalingPolicies.getPolicies().length == mCpuEnergyConsumerIds.length) {
+ return initPowerBracketsByCluster(mDefaultCpuPowerBracketsPerEnergyConsumer);
+ } else {
+ Slog.i(TAG, "Assigning a single power brackets to each CPU_CLUSTER energy consumer."
+ + " Number of CPU clusters ("
+ + mCpuScalingPolicies.getPolicies().length
+ + ") does not match the number of energy consumers ("
+ + mCpuEnergyConsumerIds.length + "). "
+ + " Using default power bucket assignment.");
+ return initDefaultPowerBrackets(mDefaultCpuPowerBrackets);
+ }
+ }
+
+ private int[] initPowerBracketsFromPowerProfile() {
+ int[] stepToBracketMap = new int[mCpuScalingPolicies.getScalingStepCount()];
+ int index = 0;
+ for (int policy : mCpuScalingPolicies.getPolicies()) {
+ int[] frequencies = mCpuScalingPolicies.getFrequencies(policy);
+ for (int step = 0; step < frequencies.length; step++) {
+ int bracket = mPowerProfile.getCpuPowerBracketForScalingStep(policy, step);
+ stepToBracketMap[index++] = bracket;
+ }
+ }
+ return stepToBracketMap;
+ }
+
+ private int[] initPowerBracketsByCluster(int defaultBracketCountPerCluster) {
+ int[] stepToBracketMap = new int[mCpuScalingPolicies.getScalingStepCount()];
+ int index = 0;
+ int bracketBase = 0;
+ int[] policies = mCpuScalingPolicies.getPolicies();
+ for (int policy : policies) {
+ int[] frequencies = mCpuScalingPolicies.getFrequencies(policy);
+ double[] powerByStep = new double[frequencies.length];
+ for (int step = 0; step < frequencies.length; step++) {
+ powerByStep[step] = mPowerProfile.getAveragePowerForCpuScalingStep(policy, step);
+ }
+
+ int[] policyStepToBracketMap = new int[frequencies.length];
+ mapScalingStepsToDefaultBrackets(policyStepToBracketMap, powerByStep,
+ defaultBracketCountPerCluster);
+ int maxBracket = 0;
+ for (int step = 0; step < frequencies.length; step++) {
+ int bracket = bracketBase + policyStepToBracketMap[step];
+ stepToBracketMap[index++] = bracket;
+ if (bracket > maxBracket) {
+ maxBracket = bracket;
+ }
+ }
+ bracketBase = maxBracket + 1;
+ }
+ return stepToBracketMap;
+ }
+
+ private int[] initDefaultPowerBrackets(int defaultCpuPowerBracketCount) {
+ int[] stepToBracketMap = new int[mCpuScalingPolicies.getScalingStepCount()];
+ double[] powerByStep = new double[mCpuScalingPolicies.getScalingStepCount()];
+ int index = 0;
+ int[] policies = mCpuScalingPolicies.getPolicies();
+ for (int policy : policies) {
+ int[] frequencies = mCpuScalingPolicies.getFrequencies(policy);
+ for (int step = 0; step < frequencies.length; step++) {
+ powerByStep[index++] = mPowerProfile.getAveragePowerForCpuScalingStep(policy, step);
+ }
+ }
+ mapScalingStepsToDefaultBrackets(stepToBracketMap, powerByStep,
+ defaultCpuPowerBracketCount);
+ return stepToBracketMap;
+ }
+
+ private static void mapScalingStepsToDefaultBrackets(int[] stepToBracketMap,
+ double[] powerByStep, int defaultCpuPowerBracketCount) {
+ double minPower = Double.MAX_VALUE;
+ double maxPower = Double.MIN_VALUE;
+ for (final double power : powerByStep) {
+ if (power < minPower) {
+ minPower = power;
+ }
+ if (power > maxPower) {
+ maxPower = power;
+ }
+ }
+ if (powerByStep.length <= defaultCpuPowerBracketCount) {
+ for (int index = 0; index < stepToBracketMap.length; index++) {
+ stepToBracketMap[index] = index;
+ }
+ } else {
+ final double minLogPower = Math.log(minPower);
+ final double logBracket = (Math.log(maxPower) - minLogPower)
+ / defaultCpuPowerBracketCount;
+
+ for (int step = 0; step < powerByStep.length; step++) {
+ int bracket = (int) ((Math.log(powerByStep[step]) - minLogPower) / logBracket);
+ if (bracket >= defaultCpuPowerBracketCount) {
+ bracket = defaultCpuPowerBracketCount - 1;
+ }
+ stepToBracketMap[step] = bracket;
+ }
+ }
+ }
+
+ /**
+ * Prints the definitions of power brackets.
+ */
+ public void dumpCpuPowerBracketsLocked(PrintWriter pw) {
+ ensureInitialized();
+
+ pw.println("CPU power brackets; cluster/freq in MHz(avg current in mA):");
+ for (int bracket = 0; bracket < mLayout.getCpuPowerBracketCount(); bracket++) {
+ pw.print(" ");
+ pw.print(bracket);
+ pw.print(": ");
+ pw.println(getCpuPowerBracketDescription(bracket));
+ }
+ }
+
+ /**
+ * Description of a CPU power bracket: which cluster/frequency combinations are included.
+ */
+ @VisibleForTesting
+ public String getCpuPowerBracketDescription(int powerBracket) {
+ ensureInitialized();
+
+ int[] stepToPowerBracketMap = mLayout.getScalingStepToPowerBracketMap();
+ StringBuilder sb = new StringBuilder();
+ int index = 0;
+ int[] policies = mCpuScalingPolicies.getPolicies();
+ for (int policy : policies) {
+ int[] freqs = mCpuScalingPolicies.getFrequencies(policy);
+ for (int step = 0; step < freqs.length; step++) {
+ if (stepToPowerBracketMap[index] != powerBracket) {
+ index++;
+ continue;
+ }
+
+ if (sb.length() != 0) {
+ sb.append(", ");
+ }
+ if (policies.length > 1) {
+ sb.append(policy).append('/');
+ }
+ sb.append(freqs[step] / 1000);
+ sb.append('(');
+ sb.append(String.format(Locale.US, "%.1f",
+ mPowerProfile.getAveragePowerForCpuScalingStep(policy, step)));
+ sb.append(')');
+
+ index++;
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Returns the descriptor of PowerStats produced by this collector.
+ */
+ @VisibleForTesting
+ public PowerStats.Descriptor getPowerStatsDescriptor() {
+ ensureInitialized();
+
+ return mPowerStatsDescriptor;
+ }
+
@Override
protected PowerStats collectStats() {
+ ensureInitialized();
+
+ if (!mIsPerUidTimeInStateSupported) {
+ return null;
+ }
+
mCpuPowerStats.uidStats.clear();
- long newTimestampNanos = mKernelCpuStatsReader.nativeReadCpuStats(
- this::processUidStats, mScalingStepToPowerBracketMap, mLastUpdateTimestampNanos,
- mTempUidStats);
+ // TODO(b/305120724): additionally retrieve time-in-cluster for each CPU cluster
+ long newTimestampNanos = mKernelCpuStatsReader.nativeReadCpuStats(this::processUidStats,
+ mLayout.getScalingStepToPowerBracketMap(), mLastUpdateTimestampNanos,
+ mTempCpuTimeByScalingStep, mTempUidStats);
+ for (int step = mLayout.getCpuScalingStepCount() - 1; step >= 0; step--) {
+ mLayout.setTimeByScalingStep(mCpuPowerStats.stats, step,
+ mTempCpuTimeByScalingStep[step] - mCpuTimeByScalingStep[step]);
+ mCpuTimeByScalingStep[step] = mTempCpuTimeByScalingStep[step];
+ }
+
mCpuPowerStats.durationMs =
(newTimestampNanos - mLastUpdateTimestampNanos) / NANOS_PER_MILLIS;
mLastUpdateTimestampNanos = newTimestampNanos;
+
+ long uptimeMillis = mClock.uptimeMillis();
+ long uptimeDelta = uptimeMillis - mLastUpdateUptimeMillis;
+ mLastUpdateUptimeMillis = uptimeMillis;
+
+ if (uptimeDelta > mCpuPowerStats.durationMs) {
+ uptimeDelta = mCpuPowerStats.durationMs;
+ }
+ mLayout.setUptime(mCpuPowerStats.stats, uptimeDelta);
+
+ if (mCpuEnergyConsumerIds.length != 0) {
+ collectEnergyConsumers();
+ }
+
return mCpuPowerStats;
}
+ 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;
+
+ 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) {
+ 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;
+ }
+ }
+ }
+ }
+
@VisibleForNative
interface KernelCpuStatsCallback {
@Keep // Called from native
- void processUidStats(int uid, long[] stats);
+ void processUidStats(int uid, long[] timeByPowerBracket);
}
- private void processUidStats(int uid, long[] stats) {
+ private void processUidStats(int uid, long[] timeByPowerBracket) {
+ int powerBracketCount = mLayout.getCpuPowerBracketCount();
+
UidStats uidStats = mUidStats.get(uid);
if (uidStats == null) {
uidStats = new UidStats();
- uidStats.stats = new long[mUidStatsSize];
- uidStats.delta = new long[mUidStatsSize];
+ uidStats.timeByPowerBracket = new long[powerBracketCount];
+ uidStats.stats = new long[mLayout.getUidStatsArrayLength()];
mUidStats.put(uid, uidStats);
}
boolean nonzero = false;
- for (int i = mUidStatsSize - 1; i >= 0; i--) {
- long delta = uidStats.delta[i] = stats[i] - uidStats.stats[i];
+ for (int bracket = powerBracketCount - 1; bracket >= 0; bracket--) {
+ long delta = timeByPowerBracket[bracket] - uidStats.timeByPowerBracket[bracket];
if (delta != 0) {
nonzero = true;
}
- uidStats.stats[i] = stats[i];
+ mLayout.setUidTimeByPowerBracket(uidStats.stats, bracket, delta);
+ uidStats.timeByPowerBracket[bracket] = timeByPowerBracket[bracket];
}
if (nonzero) {
- mCpuPowerStats.uidStats.put(uid, uidStats.delta);
+ mCpuPowerStats.uidStats.put(uid, uidStats.stats);
}
}
@@ -128,13 +765,15 @@
* Native class that retrieves CPU stats from the kernel.
*/
public static class KernelCpuStatsReader {
+ protected native boolean nativeIsSupportedFeature();
+
protected native long nativeReadCpuStats(KernelCpuStatsCallback callback,
int[] scalingStepToPowerBracketMap, long lastUpdateTimestampNanos,
- long[] tempForUidStats);
+ long[] outCpuTimeByScalingStep, long[] tempForUidStats);
}
private static class UidStats {
public long[] stats;
- public long[] delta;
+ public long[] timeByPowerBracket;
}
}
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 05c0a13..2c7843e 100644
--- a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
@@ -16,6 +16,8 @@
package com.android.server.power.stats;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.util.IndentingPrintWriter;
import android.util.SparseArray;
@@ -46,6 +48,8 @@
public final int powerComponentId;
private final MultiStateStats.States[] mDeviceStateConfig;
private final MultiStateStats.States[] mUidStateConfig;
+ @NonNull
+ private final AggregatedPowerStatsConfig.PowerComponent mConfig;
private final int[] mDeviceStates;
private final long[] mDeviceStateTimestamps;
@@ -62,13 +66,20 @@
}
PowerComponentAggregatedPowerStats(AggregatedPowerStatsConfig.PowerComponent config) {
- this.powerComponentId = config.getPowerComponentId();
+ mConfig = config;
+ powerComponentId = config.getPowerComponentId();
mDeviceStateConfig = config.getDeviceStateConfig();
mUidStateConfig = config.getUidStateConfig();
mDeviceStates = new int[mDeviceStateConfig.length];
mDeviceStateTimestamps = new long[mDeviceStateConfig.length];
}
+ @NonNull
+ public AggregatedPowerStatsConfig.PowerComponent getConfig() {
+ return mConfig;
+ }
+
+ @Nullable
public PowerStats.Descriptor getPowerStatsDescriptor() {
return mPowerStatsDescriptor;
}
@@ -108,6 +119,16 @@
}
}
+ void setDeviceStats(@AggregatedPowerStatsConfig.TrackedState int[] states, long[] values) {
+ mDeviceStats.setStats(states, values);
+ }
+
+ void setUidStats(int uid, @AggregatedPowerStatsConfig.TrackedState int[] states,
+ long[] values) {
+ UidStats uidStats = getUidStats(uid);
+ uidStats.stats.setStats(states, values);
+ }
+
boolean isCompatible(PowerStats powerStats) {
return mPowerStatsDescriptor == null || mPowerStatsDescriptor.equals(powerStats.descriptor);
}
@@ -298,7 +319,8 @@
if (mDeviceStats != null) {
ipw.println(mPowerStatsDescriptor.name);
ipw.increaseIndent();
- mDeviceStats.dump(ipw);
+ mDeviceStats.dump(ipw, stats ->
+ mConfig.getProcessor().deviceStatsToString(mPowerStatsDescriptor, stats));
ipw.decreaseIndent();
}
}
@@ -308,7 +330,8 @@
if (uidStats != null && uidStats.stats != null) {
ipw.println(mPowerStatsDescriptor.name);
ipw.increaseIndent();
- uidStats.stats.dump(ipw);
+ uidStats.stats.dump(ipw, stats ->
+ mConfig.getProcessor().uidStatsToString(mPowerStatsDescriptor, stats));
ipw.decreaseIndent();
}
}
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 f374fb7..2f9d567 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
@@ -16,6 +16,7 @@
package com.android.server.power.stats;
import android.os.BatteryStats;
+import android.util.SparseArray;
import com.android.internal.os.BatteryStatsHistory;
import com.android.internal.os.BatteryStatsHistoryIterator;
@@ -30,11 +31,17 @@
public class PowerStatsAggregator {
private final AggregatedPowerStats mStats;
private final BatteryStatsHistory mHistory;
+ private final SparseArray<AggregatedPowerStatsProcessor> mProcessors = new SparseArray<>();
public PowerStatsAggregator(AggregatedPowerStatsConfig aggregatedPowerStatsConfig,
BatteryStatsHistory history) {
mStats = new AggregatedPowerStats(aggregatedPowerStatsConfig);
mHistory = history;
+ for (AggregatedPowerStatsConfig.PowerComponent powerComponentsConfig :
+ aggregatedPowerStatsConfig.getPowerComponentsAggregatedStatsConfigs()) {
+ AggregatedPowerStatsProcessor processor = powerComponentsConfig.getProcessor();
+ mProcessors.put(powerComponentsConfig.getPowerComponentId(), processor);
+ }
}
/**
@@ -100,6 +107,7 @@
if (!mStats.isCompatible(item.powerStats)) {
if (lastTime > baseTime) {
mStats.setDuration(lastTime - baseTime);
+ finish(mStats);
consumer.accept(mStats);
}
mStats.reset();
@@ -112,9 +120,20 @@
}
if (lastTime > baseTime) {
mStats.setDuration(lastTime - baseTime);
+ finish(mStats);
consumer.accept(mStats);
}
mStats.reset(); // to free up memory
}
+
+ private void finish(AggregatedPowerStats stats) {
+ for (int i = 0; i < mProcessors.size(); i++) {
+ PowerComponentAggregatedPowerStats component =
+ stats.getPowerComponentStats(mProcessors.keyAt(i));
+ if (component != null) {
+ mProcessors.valueAt(i).finish(component);
+ }
+ }
+ }
}
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 b49c89f..84cc21e 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
@@ -38,8 +38,9 @@
* except where noted.
*/
public abstract class PowerStatsCollector {
+ private static final int MILLIVOLTS_PER_VOLT = 1000;
private final Handler mHandler;
- private final Clock mClock;
+ protected final Clock mClock;
private final long mThrottlePeriodMs;
private final Runnable mCollectAndDeliverStats = this::collectAndDeliverStats;
private boolean mEnabled;
@@ -100,6 +101,9 @@
@SuppressWarnings("GuardedBy") // Field is volatile
private void collectAndDeliverStats() {
PowerStats stats = collectStats();
+ if (stats == null) {
+ return;
+ }
for (Consumer<PowerStats> consumer : mConsumerList) {
consumer.accept(stats);
}
@@ -180,4 +184,11 @@
mHandler.post(done::open);
done.block();
}
+
+ /** Calculate charge consumption (in microcoulombs) from a given energy and voltage */
+ protected 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;
+ }
}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
index 58619c7..551302e 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
@@ -108,6 +108,9 @@
long currentTimeMillis = mClock.currentTimeMillis();
long currentMonotonicTime = mMonotonicClock.monotonicTime();
long startTime = getLastSavedSpanEndMonotonicTime();
+ if (startTime < 0) {
+ startTime = mBatteryStats.getHistory().getStartTime();
+ }
long endTimeMs = alignToWallClock(startTime + mAggregatedPowerStatsSpanDuration,
mAggregatedPowerStatsSpanDuration, currentMonotonicTime, currentTimeMillis);
while (endTimeMs <= currentMonotonicTime) {
@@ -214,6 +217,7 @@
return mLastSavedSpanEndMonotonicTime;
}
+ mLastSavedSpanEndMonotonicTime = -1;
for (PowerStatsSpan.Metadata metadata : mPowerStatsStore.getTableOfContents()) {
if (metadata.getSections().contains(AggregatedPowerStatsSection.TYPE)) {
for (PowerStatsSpan.TimeFrame timeFrame : metadata.getTimeFrames()) {
diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
index 7b0fe9a..a01bac6 100644
--- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
+++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
@@ -266,10 +266,10 @@
}
/**
- * @return {@code true} iff. {@code userId} is locked on an FBE device.
+ * @return {@code true} iff the credential-encrypted storage for {@code userId} is locked.
*/
@VisibleForTesting
public boolean isUserCredentialLocked(int userId) {
- return StorageManager.isFileEncrypted() && !StorageManager.isUserKeyUnlocked(userId);
+ return StorageManager.isFileEncrypted() && !StorageManager.isCeStorageUnlocked(userId);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 7cccf6b..34bf8ed 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1097,23 +1097,22 @@
"shouldAbortBackgroundActivityStart");
BackgroundActivityStartController balController =
mSupervisor.getBackgroundActivityLaunchController();
- balCode =
+ BackgroundActivityStartController.BalVerdict balVerdict =
balController.checkBackgroundActivityStart(
- callingUid,
- callingPid,
- callingPackage,
- realCallingUid,
- realCallingPid,
- callerApp,
- request.originatingPendingIntent,
- request.backgroundStartPrivileges,
- intent,
- checkedOptions);
- if (balCode != BAL_ALLOW_DEFAULT) {
- request.logMessage.append(" (").append(
- BackgroundActivityStartController.balCodeToString(balCode))
- .append(")");
- }
+ callingUid,
+ callingPid,
+ callingPackage,
+ realCallingUid,
+ realCallingPid,
+ callerApp,
+ request.originatingPendingIntent,
+ request.backgroundStartPrivileges,
+ intent,
+ checkedOptions);
+ balCode = balVerdict.getCode();
+ request.logMessage.append(" (").append(
+ BackgroundActivityStartController.balCodeToString(balCode))
+ .append(")");
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 1e4b258..9b7b8de 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -204,7 +204,7 @@
return checkBackgroundActivityStart(callingUid, callingPid, callingPackage,
realCallingUid, realCallingPid,
callerApp, originatingPendingIntent,
- backgroundStartPrivileges, intent, checkedOptions) == BAL_BLOCK;
+ backgroundStartPrivileges, intent, checkedOptions).blocks();
}
private class BalState {
@@ -291,8 +291,8 @@
return name + "[debugOnly]";
}
- private String dump(@BalCode int mResultIfPiCreatorAllowsBal,
- @BalCode int mResultIfPiSenderAllowsBal) {
+ private String dump(BalVerdict resultIfPiCreatorAllowsBal,
+ BalVerdict resultIfPiSenderAllowsBal) {
return " [callingPackage: " + getDebugPackageName(mCallingPackage, mCallingUid)
+ "; callingUid: " + mCallingUid
+ "; appSwitchState: " + mAppSwitchState
@@ -321,19 +321,64 @@
+ (mCallerApp != null && mCallerApp.hasActivityInVisibleTask())
+ "; realInVisibleTask: "
+ (mRealCallerApp != null && mRealCallerApp.hasActivityInVisibleTask())
- + "; resultIfPiSenderAllowsBal: " + balCodeToString(mResultIfPiSenderAllowsBal)
- + "; resultIfPiCreatorAllowsBal: "
- + balCodeToString(mResultIfPiCreatorAllowsBal)
+ + "; resultIfPiSenderAllowsBal: " + resultIfPiSenderAllowsBal
+ + "; resultIfPiCreatorAllowsBal: " + resultIfPiCreatorAllowsBal
+ "]";
}
}
+ static class BalVerdict {
+
+ static final BalVerdict BLOCK = new BalVerdict(BAL_BLOCK, false, "Blocked");
+ private final @BalCode int mCode;
+ private final boolean mBackground;
+ private final String mMessage;
+ private String mProcessInfo;
+
+ BalVerdict(@BalCode int balCode, boolean background, String message) {
+ this.mBackground = background;
+ this.mCode = balCode;
+ this.mMessage = message;
+ }
+
+ public BalVerdict withProcessInfo(String msg, WindowProcessController process) {
+ mProcessInfo = msg + " (uid=" + process.mUid + ",pid=" + process.getPid() + ")";
+ return this;
+ }
+
+ boolean blocks() {
+ return mCode == BAL_BLOCK;
+ }
+
+ boolean allows() {
+ return !blocks();
+ }
+
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ if (mBackground) {
+ builder.append("Background ");
+ }
+ builder.append("Activity start allowed: " + mMessage + ".");
+ builder.append("BAL Code: ");
+ builder.append(balCodeToString(mCode));
+ if (mProcessInfo != null) {
+ builder.append(" ");
+ builder.append(mProcessInfo);
+ }
+ return builder.toString();
+ }
+
+ public @BalCode int getCode() {
+ return mCode;
+ }
+ }
+
/**
* @return A code denoting which BAL rule allows an activity to be started,
* or {@link #BAL_BLOCK} if the launch should be blocked
*/
- @BalCode
- int checkBackgroundActivityStart(
+ BalVerdict checkBackgroundActivityStart(
int callingUid,
int callingPid,
final String callingPackage,
@@ -362,36 +407,49 @@
// realCallingSdkSandboxUidToAppUid should probably just be used instead (or in addition
// to realCallingUid when calculating resultForRealCaller below.
if (mService.hasActiveVisibleWindow(realCallingSdkSandboxUidToAppUid)) {
- return logStartAllowedAndReturnCode(BAL_ALLOW_SDK_SANDBOX,
- /*background*/ false, state,
+ BalVerdict balVerdict = new BalVerdict(BAL_ALLOW_SDK_SANDBOX, /*background*/ false,
"uid in SDK sandbox has visible (non-toast) window");
+ return statsLog(balVerdict, state);
}
}
- @BalCode int resultForCaller = checkBackgroundActivityStartAllowedByCaller(state);
- @BalCode int resultForRealCaller = callingUid == realCallingUid
+ BalVerdict resultForCaller = checkBackgroundActivityStartAllowedByCaller(state);
+ BalVerdict resultForRealCaller = callingUid == realCallingUid
? resultForCaller // no need to calculate again
: checkBackgroundActivityStartAllowedBySender(state, checkedOptions);
- if (resultForCaller != BAL_BLOCK
+ if (resultForCaller.allows()
&& checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
== ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start explicitly allowed by PI creator. "
+ state.dump(resultForCaller, resultForRealCaller));
}
- return resultForCaller;
+ return statsLog(resultForCaller, state);
}
- if (resultForRealCaller != BAL_BLOCK
+ if (resultForRealCaller.allows()
&& checkedOptions.getPendingIntentBackgroundActivityStartMode()
== ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) {
if (DEBUG_ACTIVITY_STARTS) {
- Slog.i(TAG, "Activity start explicitly allowed by PI sender. "
+ Slog.d(TAG, "Activity start explicitly allowed by PI sender. "
+ state.dump(resultForCaller, resultForRealCaller));
}
- return resultForRealCaller;
+ return statsLog(resultForRealCaller, state);
}
- if (resultForCaller != BAL_BLOCK
+ if (resultForCaller.allows() && resultForRealCaller.allows()
+ && checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+ == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED
+ && checkedOptions.getPendingIntentBackgroundActivityStartMode()
+ == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED) {
+ // Both caller and real caller allow with system defined behavior
+ Slog.wtf(TAG,
+ "With Android 15 BAL hardening this activity start would be blocked"
+ + " (missing opt in by PI creator)! "
+ + state.dump(resultForCaller, resultForRealCaller));
+ // return the realCaller result for backwards compatibility
+ return statsLog(resultForRealCaller, state);
+ }
+ if (resultForCaller.allows()
&& checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
== ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED) {
// Allowed before V by creator
@@ -399,9 +457,9 @@
"With Android 15 BAL hardening this activity start would be blocked"
+ " (missing opt in by PI creator)! "
+ state.dump(resultForCaller, resultForRealCaller));
- return resultForCaller;
+ return statsLog(resultForCaller, state);
}
- if (resultForRealCaller != BAL_BLOCK
+ if (resultForRealCaller.allows()
&& checkedOptions.getPendingIntentBackgroundActivityStartMode()
== ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED) {
// Allowed before U by sender
@@ -410,7 +468,7 @@
"With Android 14 BAL hardening this activity start would be blocked"
+ " (missing opt in by PI sender)! "
+ state.dump(resultForCaller, resultForRealCaller));
- return resultForRealCaller;
+ return statsLog(resultForRealCaller, state);
}
Slog.wtf(TAG, "Without Android 14 BAL hardening this activity start would be allowed"
+ " (missing opt in by PI sender)! "
@@ -436,15 +494,14 @@
state.mRealCallingUidHasAnyVisibleWindow,
(originatingPendingIntent != null));
}
- return BAL_BLOCK;
+ return statsLog(BalVerdict.BLOCK, state);
}
/**
* @return A code denoting which BAL rule allows an activity to be started,
* or {@link #BAL_BLOCK} if the launch should be blocked
*/
- @BalCode
- int checkBackgroundActivityStartAllowedByCaller(BalState state) {
+ BalVerdict checkBackgroundActivityStartAllowedByCaller(BalState state) {
int callingUid = state.mCallingUid;
int callingPid = state.mCallingPid;
final String callingPackage = state.mCallingPackage;
@@ -455,15 +512,15 @@
if (callingUid == Process.ROOT_UID
|| callingAppId == Process.SYSTEM_UID
|| callingAppId == Process.NFC_UID) {
- return logStartAllowedAndReturnCode(
+ return new BalVerdict(
BAL_ALLOW_ALLOWLISTED_UID, /*background*/ false,
- state, "Important callingUid");
+ "Important callingUid");
}
// Always allow home application to start activities.
if (isHomeApp(callingUid, callingPackage)) {
- return logStartAllowedAndReturnCode(BAL_ALLOW_ALLOWLISTED_COMPONENT,
- /*background*/ false, state,
+ return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
+ /*background*/ false,
"Home app");
}
@@ -471,8 +528,8 @@
final WindowState imeWindow =
mService.mRootWindowContainer.getCurrentInputMethodWindow();
if (imeWindow != null && callingAppId == imeWindow.mOwnerUid) {
- return logStartAllowedAndReturnCode(BAL_ALLOW_ALLOWLISTED_COMPONENT,
- /*background*/ false, state,
+ return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
+ /*background*/ false,
"Active ime");
}
@@ -495,8 +552,8 @@
&& callingUidHasAnyVisibleWindow)
|| isCallingUidPersistentSystemProcess;
if (allowCallingUidStartActivity) {
- return logStartAllowedAndReturnCode(BAL_ALLOW_VISIBLE_WINDOW,
- /*background*/ false, state,
+ return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
+ /*background*/ false,
"callingUidHasAnyVisibleWindow = "
+ callingUid
+ ", isCallingUidPersistentSystemProcess = "
@@ -506,30 +563,30 @@
// don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
if (ActivityTaskManagerService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND,
callingPid, callingUid) == PERMISSION_GRANTED) {
- return logStartAllowedAndReturnCode(BAL_ALLOW_PERMISSION,
- /*background*/ true, state,
+ return new BalVerdict(BAL_ALLOW_PERMISSION,
+ /*background*/ true,
"START_ACTIVITIES_FROM_BACKGROUND permission granted");
}
// don't abort if the caller has the same uid as the recents component
if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
- return logStartAllowedAndReturnCode(BAL_ALLOW_ALLOWLISTED_COMPONENT,
- /*background*/ true, state, "Recents Component");
+ return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
+ /*background*/ true, "Recents Component");
}
// don't abort if the callingUid is the device owner
if (mService.isDeviceOwner(callingUid)) {
- return logStartAllowedAndReturnCode(BAL_ALLOW_ALLOWLISTED_COMPONENT,
- /*background*/ true, state, "Device Owner");
+ return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
+ /*background*/ true, "Device Owner");
}
// don't abort if the callingUid is a affiliated profile owner
if (mService.isAffiliatedProfileOwner(callingUid)) {
- return logStartAllowedAndReturnCode(BAL_ALLOW_ALLOWLISTED_COMPONENT,
- /*background*/ true, state, "Affiliated Profile Owner");
+ return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
+ /*background*/ true, "Affiliated Profile Owner");
}
// don't abort if the callingUid has companion device
final int callingUserId = UserHandle.getUserId(callingUid);
if (mService.isAssociatedCompanionApp(callingUserId, callingUid)) {
- return logStartAllowedAndReturnCode(BAL_ALLOW_ALLOWLISTED_COMPONENT,
- /*background*/ true, state, "Companion App");
+ return new BalVerdict(BAL_ALLOW_ALLOWLISTED_COMPONENT,
+ /*background*/ true, "Companion App");
}
// don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
@@ -538,16 +595,15 @@
"Background activity start for "
+ callingPackage
+ " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
- return logStartAllowedAndReturnCode(BAL_ALLOW_SAW_PERMISSION,
- /*background*/ true, state, "SYSTEM_ALERT_WINDOW permission is granted");
+ return new BalVerdict(BAL_ALLOW_SAW_PERMISSION,
+ /*background*/ true, "SYSTEM_ALERT_WINDOW permission is granted");
}
// don't abort if the callingUid and callingPackage have the
// OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION appop
if (isSystemExemptFlagEnabled() && mService.getAppOpsManager().checkOpNoThrow(
AppOpsManager.OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION,
callingUid, callingPackage) == AppOpsManager.MODE_ALLOWED) {
- return logStartAllowedAndReturnCode(BAL_ALLOW_PERMISSION,
- /*background*/ true, state,
+ return new BalVerdict(BAL_ALLOW_PERMISSION, /*background*/ true,
"OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION appop is granted");
}
@@ -555,48 +611,48 @@
// That's the case for PendingIntent-based starts, since the creator's process might not be
// up and alive.
// Don't abort if the callerApp or other processes of that uid are allowed in any way.
- @BalCode int callerAppAllowsBal = checkProcessAllowsBal(callerApp, state);
- if (callerAppAllowsBal != BAL_BLOCK) {
+ BalVerdict callerAppAllowsBal = checkProcessAllowsBal(callerApp, state);
+ if (callerAppAllowsBal.allows()) {
return callerAppAllowsBal;
}
// If we are here, it means all exemptions based on the creator failed
- return BAL_BLOCK;
+ return BalVerdict.BLOCK;
}
/**
* @return A code denoting which BAL rule allows an activity to be started,
* or {@link #BAL_BLOCK} if the launch should be blocked
*/
- @BalCode
- int checkBackgroundActivityStartAllowedBySender(
+ BalVerdict checkBackgroundActivityStartAllowedBySender(
BalState state,
ActivityOptions checkedOptions) {
int realCallingUid = state.mRealCallingUid;
+ BackgroundStartPrivileges backgroundStartPrivileges = state.mBackgroundStartPrivileges;
if (PendingIntentRecord.isPendingIntentBalAllowedByPermission(checkedOptions)
&& ActivityManager.checkComponentPermission(
android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
realCallingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
- return logStartAllowedAndReturnCode(BAL_ALLOW_PENDING_INTENT,
- /*background*/ false, state,
+ return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+ /*background*/ false,
"realCallingUid has BAL permission. realCallingUid: " + realCallingUid);
}
// don't abort if the realCallingUid has a visible window
// TODO(b/171459802): We should check appSwitchAllowed also
if (state.mRealCallingUidHasAnyVisibleWindow) {
- return logStartAllowedAndReturnCode(BAL_ALLOW_PENDING_INTENT,
- /*background*/ false, state,
+ return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+ /*background*/ false,
"realCallingUid has visible (non-toast) window. realCallingUid: "
+ realCallingUid);
}
// if the realCallingUid is a persistent system process, abort if the IntentSender
// wasn't allowed to start an activity
if (state.mIsRealCallingUidPersistentSystemProcess
- && state.mBackgroundStartPrivileges.allowsBackgroundActivityStarts()) {
- return logStartAllowedAndReturnCode(BAL_ALLOW_PENDING_INTENT,
- /*background*/ false, state,
+ && backgroundStartPrivileges.allowsBackgroundActivityStarts()) {
+ return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+ /*background*/ false,
"realCallingUid is persistent system process AND intent "
+ "sender allowed (allowBackgroundActivityStart = true). "
+ "realCallingUid: " + realCallingUid);
@@ -604,21 +660,21 @@
// don't abort if the realCallingUid is an associated companion app
if (mService.isAssociatedCompanionApp(
UserHandle.getUserId(realCallingUid), realCallingUid)) {
- return logStartAllowedAndReturnCode(BAL_ALLOW_PENDING_INTENT,
- /*background*/ false, state,
+ return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
+ /*background*/ false,
"realCallingUid is a companion app. "
+ "realCallingUid: " + realCallingUid);
}
// don't abort if the callerApp or other processes of that uid are allowed in any way
- @BalCode int realCallerAppAllowsBal =
+ BalVerdict realCallerAppAllowsBal =
checkProcessAllowsBal(state.mRealCallerApp, state);
- if (realCallerAppAllowsBal != BAL_BLOCK) {
+ if (realCallerAppAllowsBal.allows()) {
return realCallerAppAllowsBal;
}
// If we are here, it means all exemptions based on PI sender failed
- return BAL_BLOCK;
+ return BalVerdict.BLOCK;
}
/**
@@ -628,18 +684,16 @@
* String, int, boolean, boolean, boolean, long, long, long)} for details on the
* exceptions.
*/
- private @BalCode int checkProcessAllowsBal(WindowProcessController app, BalState state) {
+ private BalVerdict checkProcessAllowsBal(WindowProcessController app,
+ BalState state) {
if (app == null) {
- return BAL_BLOCK;
+ return BalVerdict.BLOCK;
}
// first check the original calling process
- final @BalCode int balAllowedForCaller = app
+ final BalVerdict balAllowedForCaller = app
.areBackgroundActivityStartsAllowed(state.mAppSwitchState);
- if (balAllowedForCaller != BAL_BLOCK) {
- return logStartAllowedAndReturnCode(balAllowedForCaller,
- /*background*/ true, state,
- "callerApp process (pid = " + app.getPid()
- + ", uid = " + app.mUid + ") is allowed");
+ if (balAllowedForCaller.allows()) {
+ return balAllowedForCaller.withProcessInfo("callerApp process", app);
} else {
// only if that one wasn't allowed, check the other ones
final ArraySet<WindowProcessController> uidProcesses =
@@ -647,18 +701,17 @@
if (uidProcesses != null) {
for (int i = uidProcesses.size() - 1; i >= 0; i--) {
final WindowProcessController proc = uidProcesses.valueAt(i);
- int balAllowedForUid = proc.areBackgroundActivityStartsAllowed(
- state.mAppSwitchState);
- if (proc != app && balAllowedForUid != BAL_BLOCK) {
- return logStartAllowedAndReturnCode(balAllowedForUid,
- /*background*/ true, state,
- "process" + proc.getPid() + " from uid " + app.mUid
- + " is allowed");
+ if (proc != app) {
+ BalVerdict balAllowedForUid = proc.areBackgroundActivityStartsAllowed(
+ state.mAppSwitchState);
+ if (balAllowedForUid.allows()) {
+ return balAllowedForCaller.withProcessInfo("process", proc);
+ }
}
}
}
}
- return BAL_BLOCK;
+ return BalVerdict.BLOCK;
}
/**
@@ -1156,36 +1209,6 @@
return joiner.toString();
}
- static @BalCode int logStartAllowedAndReturnCode(@BalCode int code,
- boolean background, int callingUid, int realCallingUid, Intent intent, int pid,
- String msg) {
- return logStartAllowedAndReturnCode(code, background, callingUid, realCallingUid,
- intent, DEBUG_ACTIVITY_STARTS ? ("[Process(" + pid + ")]" + msg) : "");
- }
-
- private static @BalCode int logStartAllowedAndReturnCode(@BalCode int code,
- boolean background, BalState state, String msg) {
- return logStartAllowedAndReturnCode(code, background, state.mCallingUid,
- state.mRealCallingUid, state.mIntent, msg);
- }
-
- private static @BalCode int logStartAllowedAndReturnCode(@BalCode int code,
- boolean background, int callingUid, int realCallingUid, Intent intent, String msg) {
- statsLogBalAllowed(code, callingUid, realCallingUid, intent);
- if (DEBUG_ACTIVITY_STARTS) {
- StringBuilder builder = new StringBuilder();
- if (background) {
- builder.append("Background ");
- }
- builder.append("Activity start allowed: " + msg + ". callingUid: "
- + callingUid + ". ");
- builder.append("BAL Code: ");
- builder.append(balCodeToString(code));
- Slog.i(TAG, builder.toString());
- }
- return code;
- }
-
private static boolean isSystemExemptFlagEnabled() {
return DeviceConfig.getBoolean(
NAMESPACE_WINDOW_MANAGER,
@@ -1193,8 +1216,12 @@
/* defaultValue= */ true);
}
- private static void statsLogBalAllowed(
- @BalCode int code, int callingUid, int realCallingUid, Intent intent) {
+ private static BalVerdict statsLog(BalVerdict finalVerdict, BalState state) {
+ @BalCode int code = finalVerdict.getCode();
+ int callingUid = state.mCallingUid;
+ int realCallingUid = state.mRealCallingUid;
+ Intent intent = state.mIntent;
+
if (code == BAL_ALLOW_PENDING_INTENT
&& (callingUid == Process.SYSTEM_UID || realCallingUid == Process.SYSTEM_UID)) {
String activityName =
@@ -1214,6 +1241,7 @@
callingUid,
realCallingUid);
}
+ return finalVerdict;
}
/**
diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
index 527edc1..e849589 100644
--- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
+++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
@@ -27,7 +27,6 @@
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_GRACE_PERIOD;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_PERMISSION;
import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW;
-import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK;
import static java.util.Objects.requireNonNull;
@@ -49,6 +48,7 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -96,37 +96,33 @@
mBackgroundActivityStartCallback = callback;
}
- @BackgroundActivityStartController.BalCode
- int areBackgroundActivityStartsAllowed(int pid, int uid, String packageName,
+ BalVerdict areBackgroundActivityStartsAllowed(
+ int pid, int uid, String packageName,
int appSwitchState, boolean isCheckingForFgsStart,
boolean hasActivityInVisibleTask, boolean hasBackgroundActivityStartPrivileges,
long lastStopAppSwitchesTime, long lastActivityLaunchTime,
long lastActivityFinishTime) {
// Allow if the proc is instrumenting with background activity starts privs.
if (hasBackgroundActivityStartPrivileges) {
- return BackgroundActivityStartController.logStartAllowedAndReturnCode(
- BAL_ALLOW_PERMISSION, /*background*/ true, uid, uid, /*intent*/ null,
- pid, "Activity start allowed: process instrumenting with background "
- + "activity starts privileges");
+ return new BalVerdict(BAL_ALLOW_PERMISSION, /*background*/ true,
+ "Activity start allowed: process instrumenting with background "
+ + "activity starts privileges");
}
// Allow if the flag was explicitly set.
if (isBackgroundStartAllowedByToken(uid, packageName, isCheckingForFgsStart)) {
- return BackgroundActivityStartController.logStartAllowedAndReturnCode(
- BAL_ALLOW_PERMISSION, /*background*/ true, uid, uid, /*intent*/ null,
- pid, "Activity start allowed: process allowed by token");
+ return new BalVerdict(BAL_ALLOW_PERMISSION, /*background*/ true,
+ "Activity start allowed: process allowed by token");
}
// Allow if the caller is bound by a UID that's currently foreground.
if (isBoundByForegroundUid()) {
- return BackgroundActivityStartController.logStartAllowedAndReturnCode(
- BAL_ALLOW_VISIBLE_WINDOW, /*background*/ false, uid, uid, /*intent*/ null,
- pid, "Activity start allowed: process bound by foreground uid");
+ return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, /*background*/ false,
+ "Activity start allowed: process bound by foreground uid");
}
// Allow if the caller has an activity in any foreground task.
if (hasActivityInVisibleTask
&& (appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY)) {
- return BackgroundActivityStartController.logStartAllowedAndReturnCode(
- BAL_ALLOW_FOREGROUND, /*background*/ false, uid, uid, /*intent*/ null,
- pid, "Activity start allowed: process has activity in foreground task");
+ return new BalVerdict(BAL_ALLOW_FOREGROUND, /*background*/ false,
+ "Activity start allowed: process has activity in foreground task");
}
// If app switching is not allowed, we ignore all the start activity grace period
@@ -141,9 +137,8 @@
// let app to be able to start background activity even it's in grace period.
if (lastActivityLaunchTime > lastStopAppSwitchesTime
|| lastActivityFinishTime > lastStopAppSwitchesTime) {
- return BackgroundActivityStartController.logStartAllowedAndReturnCode(
- BAL_ALLOW_GRACE_PERIOD, /*background*/ true, uid, uid, /*intent*/ null,
- pid, "Activity start allowed: within "
+ return new BalVerdict(BAL_ALLOW_GRACE_PERIOD, /*background*/ true,
+ "Activity start allowed: within "
+ ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period");
}
if (DEBUG_ACTIVITY_STARTS) {
@@ -154,7 +149,7 @@
}
}
- return BAL_BLOCK;
+ return BalVerdict.BLOCK;
}
/**
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e7893da..f314900 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4716,6 +4716,17 @@
scheduleAnimation();
mWmService.mH.post(() -> InputMethodManagerInternal.get().onImeParentChanged());
+ } else if (mImeControlTarget != null && mImeControlTarget == mImeLayeringTarget) {
+ // Even if the IME surface parent is not changed, the layer target belonging to the
+ // parent may have changes. Then attempt to reassign if the IME control target is
+ // possible to be the relative layer.
+ final SurfaceControl lastRelativeLayer = mImeWindowsContainer.getLastRelativeLayer();
+ if (lastRelativeLayer != mImeLayeringTarget.mSurfaceControl) {
+ assignRelativeLayerForIme(getSyncTransaction(), false /* forceUpdate */);
+ if (lastRelativeLayer != mImeWindowsContainer.getLastRelativeLayer()) {
+ scheduleAnimation();
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index d2f6d16..fa104bb 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3643,7 +3643,7 @@
*/
TaskFragmentParentInfo getTaskFragmentParentInfo() {
return new TaskFragmentParentInfo(getConfiguration(), getDisplayId(),
- shouldBeVisible(null /* starting */));
+ shouldBeVisible(null /* starting */), hasNonFinishingDirectActivity());
}
@Override
@@ -6062,6 +6062,11 @@
// Non-root task position changed.
mRootWindowContainer.invalidateTaskLayers();
}
+
+ if (child.asActivityRecord() != null) {
+ // Send for TaskFragmentParentInfo#hasDirectActivity change.
+ sendTaskFragmentParentInfoChangedIfNeeded();
+ }
}
void reparent(TaskDisplayArea newParent, boolean onTop) {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 82d3424..2fc531a 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1049,6 +1049,10 @@
return getActivity(ActivityRecord::canBeTopRunning);
}
+ /**
+ * Reports non-finishing activity count including this TaskFragment's child embedded
+ * TaskFragments' children activities.
+ */
int getNonFinishingActivityCount() {
final int[] runningActivityCount = new int[1];
forAllActivities(a -> {
@@ -1059,6 +1063,20 @@
return runningActivityCount[0];
}
+ /**
+ * Returns {@code true} if there's any non-finishing direct children activity, which is not
+ * embedded in TaskFragments
+ */
+ boolean hasNonFinishingDirectActivity() {
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final ActivityRecord activity = getChildAt(i).asActivityRecord();
+ if (activity != null && !activity.finishing) {
+ return true;
+ }
+ }
+ return false;
+ }
+
boolean isTopActivityFocusable() {
final ActivityRecord r = topRunningActivity();
return r != null ? r.isFocusable()
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index ff766be..34ae370 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -157,6 +157,15 @@
*/
private final ArrayMap<IBinder, Integer> mDeferredTransitions = new ArrayMap<>();
+ /**
+ * Map from {@link TaskFragmentTransaction#getTransactionToken()} to a
+ * {@link Transition.ReadyCondition} that is waiting for the {@link TaskFragmentTransaction}
+ * to complete.
+ * @see #onTransactionHandled
+ */
+ private final ArrayMap<IBinder, Transition.ReadyCondition> mInFlightTransactions =
+ new ArrayMap<>();
+
TaskFragmentOrganizerState(@NonNull ITaskFragmentOrganizer organizer, int pid, int uid,
boolean isSystemOrganizer) {
mOrganizer = organizer;
@@ -173,7 +182,7 @@
@Override
public void binderDied() {
synchronized (mGlobalLock) {
- removeOrganizer(mOrganizer);
+ removeOrganizer(mOrganizer, "client died");
}
}
@@ -195,7 +204,7 @@
mOrganizedTaskFragments.remove(taskFragment);
}
- void dispose() {
+ void dispose(@NonNull String reason) {
boolean wasVisible = false;
for (int i = mOrganizedTaskFragments.size() - 1; i >= 0; i--) {
final TaskFragment taskFragment = mOrganizedTaskFragments.get(i);
@@ -236,6 +245,10 @@
// Cleanup any running transaction to unblock the current transition.
onTransactionFinished(mDeferredTransitions.keyAt(i));
}
+ for (int i = mInFlightTransactions.size() - 1; i >= 0; i--) {
+ // Cleanup any in-flight transactions to unblock the transition.
+ mInFlightTransactions.valueAt(i).meetAlternate("disposed(" + reason + ")");
+ }
mOrganizer.asBinder().unlinkToDeath(this, 0 /* flags */);
}
@@ -398,11 +411,6 @@
Slog.d(TAG, "Exception sending TaskFragmentTransaction", e);
return;
}
- onTransactionStarted(transaction.getTransactionToken());
- }
-
- /** Called when the transaction is sent to the organizer. */
- void onTransactionStarted(@NonNull IBinder transactionToken) {
if (!mWindowOrganizerController.getTransitionController().isCollecting()) {
return;
}
@@ -410,9 +418,13 @@
.getCollectingTransitionId();
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Defer transition id=%d for TaskFragmentTransaction=%s", transitionId,
- transactionToken);
- mDeferredTransitions.put(transactionToken, transitionId);
+ transaction.getTransactionToken());
+ mDeferredTransitions.put(transaction.getTransactionToken(), transitionId);
mWindowOrganizerController.getTransitionController().deferTransitionReady();
+ final Transition.ReadyCondition transactionApplied = new Transition.ReadyCondition(
+ "task-fragment transaction", transaction);
+ mWindowOrganizerController.getTransitionController().waitFor(transactionApplied);
+ mInFlightTransactions.put(transaction.getTransactionToken(), transactionApplied);
}
/** Called when the transaction is finished. */
@@ -496,7 +508,7 @@
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
"Unregister task fragment organizer=%s uid=%d pid=%d",
organizer.asBinder(), uid, pid);
- removeOrganizer(organizer);
+ removeOrganizer(organizer, "unregistered");
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -564,6 +576,11 @@
: null;
if (state != null) {
state.onTransactionFinished(transactionToken);
+ final Transition.ReadyCondition condition =
+ state.mInFlightTransactions.remove(transactionToken);
+ if (condition != null) {
+ condition.meet();
+ }
}
}
}
@@ -777,7 +794,8 @@
return mTaskFragmentOrganizerState.containsKey(organizer.asBinder());
}
- private void removeOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
+ private void removeOrganizer(@NonNull ITaskFragmentOrganizer organizer,
+ @NonNull String reason) {
final TaskFragmentOrganizerState state = mTaskFragmentOrganizerState.get(
organizer.asBinder());
if (state == null) {
@@ -788,7 +806,7 @@
// event dispatch as result of surface placement.
mPendingTaskFragmentEvents.remove(organizer.asBinder());
// remove all of the children of the organized TaskFragment
- state.dispose();
+ state.dispose(reason);
mTaskFragmentOrganizerState.remove(organizer.asBinder());
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 4a0f44b..8f884d2f 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2706,6 +2706,10 @@
return mLastLayer;
}
+ SurfaceControl getLastRelativeLayer() {
+ return mLastRelativeToLayer;
+ }
+
protected void setRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) {
if (mSurfaceFreezer.hasLeash()) {
// When the freezer has created animation leash parent for the window, set the layer
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index e769a27..a74a707 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -42,7 +42,6 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK;
import static com.android.server.wm.WindowManagerService.MY_PID;
import static java.util.Objects.requireNonNull;
@@ -631,22 +630,23 @@
*/
@HotPath(caller = HotPath.START_SERVICE)
public boolean areBackgroundFgsStartsAllowed() {
- return areBackgroundActivityStartsAllowed(mAtm.getBalAppSwitchesState(),
- true /* isCheckingForFgsStart */) != BAL_BLOCK;
+ return areBackgroundActivityStartsAllowed(
+ mAtm.getBalAppSwitchesState(),
+ true /* isCheckingForFgsStart */).allows();
}
- @BackgroundActivityStartController.BalCode
- int areBackgroundActivityStartsAllowed(int appSwitchState) {
- return areBackgroundActivityStartsAllowed(appSwitchState,
+ BackgroundActivityStartController.BalVerdict areBackgroundActivityStartsAllowed(
+ int appSwitchState) {
+ return areBackgroundActivityStartsAllowed(
+ appSwitchState,
false /* isCheckingForFgsStart */);
}
- @BackgroundActivityStartController.BalCode
- private int areBackgroundActivityStartsAllowed(int appSwitchState,
- boolean isCheckingForFgsStart) {
- return mBgLaunchController.areBackgroundActivityStartsAllowed(mPid, mUid, mInfo.packageName,
- appSwitchState, isCheckingForFgsStart, hasActivityInVisibleTask(),
- mInstrumentingWithBackgroundActivityStartPrivileges,
+ private BackgroundActivityStartController.BalVerdict areBackgroundActivityStartsAllowed(
+ int appSwitchState, boolean isCheckingForFgsStart) {
+ return mBgLaunchController.areBackgroundActivityStartsAllowed(mPid, mUid,
+ mInfo.packageName, appSwitchState, isCheckingForFgsStart,
+ hasActivityInVisibleTask(), mInstrumentingWithBackgroundActivityStartPrivileges,
mAtm.getLastStopAppSwitchesTime(),
mLastActivityLaunchTime, mLastActivityFinishTime);
}
diff --git a/services/core/jni/com_android_server_power_stats_CpuPowerStatsCollector.cpp b/services/core/jni/com_android_server_power_stats_CpuPowerStatsCollector.cpp
index a6084ea..cac13eb 100644
--- a/services/core/jni/com_android_server_power_stats_CpuPowerStatsCollector.cpp
+++ b/services/core/jni/com_android_server_power_stats_CpuPowerStatsCollector.cpp
@@ -34,9 +34,12 @@
static constexpr uint64_t NSEC_PER_MSEC = 1000000;
-static int extractUidStats(JNIEnv *env, std::vector<std::vector<uint64_t>> ×,
- ScopedIntArrayRO &scopedScalingStepToPowerBracketMap,
- jlongArray tempForUidStats);
+static int flatten(JNIEnv *env, const std::vector<std::vector<uint64_t>> ×,
+ jlongArray outArray);
+
+static int combineByBracket(JNIEnv *env, const std::vector<std::vector<uint64_t>> ×,
+ ScopedIntArrayRO &scopedScalingStepToPowerBracketMap,
+ jlongArray outBrackets);
static bool initialized = false;
static jclass class_KernelCpuStatsCallback;
@@ -62,25 +65,43 @@
return OK;
}
+static jboolean nativeIsSupportedFeature(JNIEnv *env) {
+ if (!android::bpf::startTrackingUidTimes()) {
+ return false;
+ }
+ auto totalByScalingStep = android::bpf::getTotalCpuFreqTimes();
+ return totalByScalingStep.has_value();
+}
+
static jlong nativeReadCpuStats(JNIEnv *env, [[maybe_unused]] jobject zis, jobject callback,
jintArray scalingStepToPowerBracketMap,
- jlong lastUpdateTimestampNanos, jlongArray tempForUidStats) {
+ jlong lastUpdateTimestampNanos, jlongArray cpuTimeByScalingStep,
+ jlongArray tempForUidStats) {
+ ScopedIntArrayRO scopedScalingStepToPowerBracketMap(env, scalingStepToPowerBracketMap);
+
if (!initialized) {
if (init(env) == EXCEPTION) {
return 0L;
}
}
+ auto totalByScalingStep = android::bpf::getTotalCpuFreqTimes();
+ if (!totalByScalingStep) {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException", "Unsupported kernel feature");
+ return EXCEPTION;
+ }
+
+ if (flatten(env, *totalByScalingStep, cpuTimeByScalingStep) == EXCEPTION) {
+ return 0L;
+ }
+
uint64_t newLastUpdate = lastUpdateTimestampNanos;
auto data = android::bpf::getUidsUpdatedCpuFreqTimes(&newLastUpdate);
if (!data.has_value()) return lastUpdateTimestampNanos;
- ScopedIntArrayRO scopedScalingStepToPowerBracketMap(env, scalingStepToPowerBracketMap);
-
for (auto &[uid, times] : *data) {
- int status =
- extractUidStats(env, times, scopedScalingStepToPowerBracketMap, tempForUidStats);
- if (status == EXCEPTION) {
+ if (combineByBracket(env, times, scopedScalingStepToPowerBracketMap, tempForUidStats) ==
+ EXCEPTION) {
return 0L;
}
env->CallVoidMethod(callback, method_KernelCpuStatsCallback_processUidStats, (jint)uid,
@@ -89,13 +110,34 @@
return newLastUpdate;
}
-static int extractUidStats(JNIEnv *env, std::vector<std::vector<uint64_t>> ×,
- ScopedIntArrayRO &scopedScalingStepToPowerBracketMap,
- jlongArray tempForUidStats) {
- ScopedLongArrayRW scopedTempForStats(env, tempForUidStats);
- uint64_t *arrayForStats = reinterpret_cast<uint64_t *>(scopedTempForStats.get());
- const uint8_t statsSize = scopedTempForStats.size();
- memset(arrayForStats, 0, statsSize * sizeof(uint64_t));
+static int flatten(JNIEnv *env, const std::vector<std::vector<uint64_t>> ×,
+ jlongArray outArray) {
+ ScopedLongArrayRW scopedOutArray(env, outArray);
+ const uint8_t scalingStepCount = scopedOutArray.size();
+ uint64_t *out = reinterpret_cast<uint64_t *>(scopedOutArray.get());
+ uint32_t scalingStep = 0;
+ for (const auto &subVec : times) {
+ for (uint32_t i = 0; i < subVec.size(); ++i) {
+ if (scalingStep >= scalingStepCount) {
+ jniThrowExceptionFmt(env, "java/lang/IndexOutOfBoundsException",
+ "Array is too short, size=%u, scalingStep=%u",
+ scalingStepCount, scalingStep);
+ return EXCEPTION;
+ }
+ out[scalingStep] = subVec[i] / NSEC_PER_MSEC;
+ scalingStep++;
+ }
+ }
+ return OK;
+}
+
+static int combineByBracket(JNIEnv *env, const std::vector<std::vector<uint64_t>> ×,
+ ScopedIntArrayRO &scopedScalingStepToPowerBracketMap,
+ jlongArray outBrackets) {
+ ScopedLongArrayRW scopedOutBrackets(env, outBrackets);
+ uint64_t *brackets = reinterpret_cast<uint64_t *>(scopedOutBrackets.get());
+ const uint8_t statsSize = scopedOutBrackets.size();
+ memset(brackets, 0, statsSize * sizeof(uint64_t));
const uint8_t scalingStepCount = scopedScalingStepToPowerBracketMap.size();
uint32_t scalingStep = 0;
@@ -108,14 +150,14 @@
scalingStepCount, scalingStep);
return EXCEPTION;
}
- uint32_t bucket = scopedScalingStepToPowerBracketMap[scalingStep];
- if (bucket >= statsSize) {
+ uint32_t bracket = scopedScalingStepToPowerBracketMap[scalingStep];
+ if (bracket >= statsSize) {
jniThrowExceptionFmt(env, "java/lang/IndexOutOfBoundsException",
- "UidStats array is too short, length=%u, bucket[%u]=%u",
- statsSize, scalingStep, bucket);
+ "Bracket array is too short, length=%u, bracket[%u]=%u",
+ statsSize, scalingStep, bracket);
return EXCEPTION;
}
- arrayForStats[bucket] += subVec[i] / NSEC_PER_MSEC;
+ brackets[bracket] += subVec[i] / NSEC_PER_MSEC;
scalingStep++;
}
}
@@ -123,7 +165,8 @@
}
static const JNINativeMethod method_table[] = {
- {"nativeReadCpuStats", "(L" JAVA_CLASS_KERNEL_CPU_STATS_CALLBACK ";[IJ[J)J",
+ {"nativeIsSupportedFeature", "()Z", (void *)nativeIsSupportedFeature},
+ {"nativeReadCpuStats", "(L" JAVA_CLASS_KERNEL_CPU_STATS_CALLBACK ";[IJ[J[J)J",
(void *)nativeReadCpuStats},
};
diff --git a/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
index ca57f51..8b5d7f0 100644
--- a/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
+++ b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
@@ -31,6 +31,7 @@
import android.app.smartspace.SmartspaceConfig;
import android.app.smartspace.SmartspaceSessionId;
import android.app.smartspace.SmartspaceTargetEvent;
+import android.app.smartspace.flags.Flags;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
@@ -165,7 +166,8 @@
}
Context ctx = getContext();
if (!(ctx.checkCallingPermission(MANAGE_SMARTSPACE) == PERMISSION_GRANTED
- || ctx.checkCallingPermission(ACCESS_SMARTSPACE) == PERMISSION_GRANTED
+ || (Flags.accessSmartspace()
+ && ctx.checkCallingPermission(ACCESS_SMARTSPACE) == PERMISSION_GRANTED)
|| mServiceNameResolver.isTemporary(userId)
|| mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid()))) {
diff --git a/services/tests/powerstatstests/res/xml/power_profile_test.xml b/services/tests/powerstatstests/res/xml/power_profile_test.xml
new file mode 100644
index 0000000..ecd8861
--- /dev/null
+++ b/services/tests/powerstatstests/res/xml/power_profile_test.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<device name="Android">
+ <!-- This is the battery capacity in mAh -->
+ <item name="battery.capacity">3000</item>
+
+ <!-- Power consumption when CPU is suspended -->
+ <item name="cpu.suspend">5</item>
+ <!-- Additional power consumption when CPU is in a kernel idle loop -->
+ <item name="cpu.idle">1.11</item>
+ <!-- Additional power consumption by CPU excluding cluster and core when running -->
+ <item name="cpu.active">2.55</item>
+
+ <!-- Additional power consumption of CPU policy0 itself when running on related cores -->
+ <item name="cpu.scaling_policy_power.policy0">2.11</item>
+ <!-- Additional power consumption of CPU policy4 itself when running on related cores -->
+ <item name="cpu.scaling_policy_power.policy4">2.22</item>
+
+ <!-- Additional power used by a CPU related to policy3 when running at different
+ speeds. -->
+ <array name="cpu.scaling_step_power.policy0">
+ <value>10</value> <!-- 300 MHz CPU speed -->
+ <value>20</value> <!-- 1000 MHz CPU speed -->
+ <value>30</value> <!-- 1900 MHz CPU speed -->
+ </array>
+ <!-- Additional power used by a CPU related to policy3 when running at different
+ speeds. -->
+ <array name="cpu.scaling_step_power.policy4">
+ <value>25</value> <!-- 300 MHz CPU speed -->
+ <value>35</value> <!-- 1000 MHz CPU speed -->
+ <value>50</value> <!-- 2500 MHz CPU speed -->
+ <value>60</value> <!-- 3000 MHz CPU speed -->
+ </array>
+
+ <!-- Power used by display unit in ambient display mode, including back lighting-->
+ <item name="ambient.on">0.5</item>
+ <!-- Additional power used when screen is turned on at minimum brightness -->
+ <item name="screen.on">100</item>
+ <!-- Additional power used when screen is at maximum brightness, compared to
+ screen at minimum brightness -->
+ <item name="screen.full">800</item>
+
+ <!-- Average power used by the camera flash module when on -->
+ <item name="camera.flashlight">500</item>
+ <!-- Average power use by the camera subsystem for a typical camera
+ application. Intended as a rough estimate for an application running a
+ preview and capturing approximately 10 full-resolution pictures per
+ minute. -->
+ <item name="camera.avg">600</item>
+
+ <!-- Additional power used by the audio hardware, probably due to DSP -->
+ <item name="audio">100.0</item>
+
+ <!-- Additional power used by the video hardware, probably due to DSP -->
+ <item name="video">150.0</item> <!-- ~50mA -->
+
+ <!-- Additional power used when GPS is acquiring a signal -->
+ <item name="gps.on">10</item>
+
+ <!-- Additional power used when cellular radio is transmitting/receiving -->
+ <item name="radio.active">60</item>
+ <!-- Additional power used when cellular radio is paging the tower -->
+ <item name="radio.scanning">3</item>
+ <!-- Additional power used when the cellular radio is on. Multi-value entry,
+ one per signal strength (no signal, weak, moderate, strong) -->
+ <array name="radio.on"> <!-- Strength 0 to BINS-1 -->
+ <value>6</value> <!-- none -->
+ <value>5</value> <!-- poor -->
+ <value>4</value> <!-- moderate -->
+ <value>3</value> <!-- good -->
+ <value>3</value> <!-- great -->
+ </array>
+
+ <!-- Cellular modem related values. These constants are deprecated, but still supported and
+ need to be tested -->
+ <item name="modem.controller.sleep">123</item>
+ <item name="modem.controller.idle">456</item>
+ <item name="modem.controller.rx">789</item>
+ <array name="modem.controller.tx"> <!-- Strength 0 to 4 -->
+ <value>10</value>
+ <value>20</value>
+ <value>30</value>
+ <value>40</value>
+ <value>50</value>
+ </array>
+</device>
\ No newline at end of file
diff --git a/core/tests/coretests/res/xml/power_profile_test_power_brackets.xml b/services/tests/powerstatstests/res/xml/power_profile_test_power_brackets.xml
similarity index 100%
rename from core/tests/coretests/res/xml/power_profile_test_power_brackets.xml
rename to services/tests/powerstatstests/res/xml/power_profile_test_power_brackets.xml
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatePowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatePowerStatsProcessorTest.java
new file mode 100644
index 0000000..48e2dd7
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatePowerStatsProcessorTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2023 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.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 android.os.BatteryConsumer;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.MultiStateStats;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class AggregatePowerStatsProcessorTest {
+
+ @Test
+ public void createPowerEstimationPlan_allDeviceStatesPresentInUidStates() {
+ AggregatedPowerStatsConfig.PowerComponent config =
+ new AggregatedPowerStatsConfig.PowerComponent(BatteryConsumer.POWER_COMPONENT_ANY)
+ .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+ .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE);
+
+ AggregatedPowerStatsProcessor.PowerEstimationPlan plan =
+ new AggregatedPowerStatsProcessor.PowerEstimationPlan(config);
+ assertThat(deviceStateEstimatesToStrings(plan))
+ .containsExactly("[0, 0]", "[0, 1]", "[1, 0]", "[1, 1]");
+ assertThat(combinedDeviceStatsToStrings(plan))
+ .containsExactly("[[0, 0]]", "[[0, 1]]", "[[1, 0]]", "[[1, 1]]");
+ assertThat(uidStateEstimatesToStrings(plan, config))
+ .containsExactly(
+ "[[0, 0]]: [ps]: [[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 0, 3], [0, 0, 4]]",
+ "[[0, 1]]: [ps]: [[0, 1, 0], [0, 1, 1], [0, 1, 2], [0, 1, 3], [0, 1, 4]]",
+ "[[1, 0]]: [ps]: [[1, 0, 0], [1, 0, 1], [1, 0, 2], [1, 0, 3], [1, 0, 4]]",
+ "[[1, 1]]: [ps]: [[1, 1, 0], [1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 1, 4]]");
+ }
+
+ @Test
+ public void createPowerEstimationPlan_combineDeviceStats() {
+ AggregatedPowerStatsConfig.PowerComponent config =
+ new AggregatedPowerStatsConfig.PowerComponent(BatteryConsumer.POWER_COMPONENT_ANY)
+ .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+ .trackUidStates(STATE_POWER, STATE_PROCESS_STATE);
+
+ AggregatedPowerStatsProcessor.PowerEstimationPlan plan =
+ new AggregatedPowerStatsProcessor.PowerEstimationPlan(config);
+
+ assertThat(deviceStateEstimatesToStrings(plan))
+ .containsExactly("[0, 0]", "[0, 1]", "[1, 0]", "[1, 1]");
+ assertThat(combinedDeviceStatsToStrings(plan))
+ .containsExactly(
+ "[[0, 0], [0, 1]]",
+ "[[1, 0], [1, 1]]");
+ assertThat(uidStateEstimatesToStrings(plan, config))
+ .containsExactly(
+ "[[0, 0], [0, 1]]: [ps]: [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4]]",
+ "[[1, 0], [1, 1]]: [ps]: [[1, 0], [1, 1], [1, 2], [1, 3], [1, 4]]");
+ }
+
+ private static List<String> deviceStateEstimatesToStrings(
+ AggregatedPowerStatsProcessor.PowerEstimationPlan plan) {
+ return plan.deviceStateEstimations.stream()
+ .map(dse -> dse.stateValues).map(Arrays::toString).toList();
+ }
+
+ private static List<String> combinedDeviceStatsToStrings(
+ AggregatedPowerStatsProcessor.PowerEstimationPlan plan) {
+ return plan.combinedDeviceStateEstimations.stream()
+ .map(cds -> cds.deviceStateEstimations)
+ .map(dses -> dses.stream()
+ .map(dse -> dse.stateValues).map(Arrays::toString).toList())
+ .map(Object::toString)
+ .toList();
+ }
+
+ private static List<String> uidStateEstimatesToStrings(
+ AggregatedPowerStatsProcessor.PowerEstimationPlan plan,
+ AggregatedPowerStatsConfig.PowerComponent config) {
+ MultiStateStats.States[] uidStateConfig = config.getUidStateConfig();
+ return plan.uidStateEstimates.stream()
+ .map(use ->
+ use.combinedDeviceStateEstimate.deviceStateEstimations.stream()
+ .map(dse -> dse.stateValues).map(Arrays::toString).toList()
+ + ": "
+ + Arrays.stream(use.states)
+ .filter(Objects::nonNull)
+ .map(MultiStateStats.States::getName).toList()
+ + ": "
+ + use.proportionalEstimates.stream()
+ .map(pe -> trackedStatesToString(uidStateConfig, pe.stateValues))
+ .toList())
+ .toList();
+ }
+
+ private static Object trackedStatesToString(MultiStateStats.States[] states,
+ int[] stateValues) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[");
+ boolean first = true;
+ for (int i = 0; i < states.length; i++) {
+ if (!states[i].isTracked()) {
+ continue;
+ }
+
+ if (!first) {
+ sb.append(", ");
+ }
+ first = false;
+ sb.append(stateValues[i]);
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+}
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 3579fce..0b10954 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
@@ -142,6 +142,22 @@
return this;
}
+ /**
+ * Mocks the CPU bracket count
+ */
+ public BatteryUsageStatsRule setCpuPowerBracketCount(int count) {
+ when(mPowerProfile.getCpuPowerBracketCount()).thenReturn(count);
+ return this;
+ }
+
+ /**
+ * Mocks the CPU bracket for the given CPU scaling policy and step
+ */
+ public BatteryUsageStatsRule setCpuPowerBracket(int policy, int step, int bracket) {
+ when(mPowerProfile.getCpuPowerBracketForScalingStep(policy, step)).thenReturn(bracket);
+ return this;
+ }
+
public BatteryUsageStatsRule setAveragePowerForOrdinal(String group, int ordinal,
double value) {
when(mPowerProfile.getAveragePowerForOrdinal(group, ordinal)).thenReturn(value);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java
new file mode 100644
index 0000000..79084cc
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuAggregatedPowerStatsProcessorTest.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2023 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.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
+
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
+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.junit.Assert.fail;
+
+import android.os.BatteryConsumer;
+import android.os.PersistableBundle;
+import android.util.LongArray;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.MultiStateStats;
+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 java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CpuAggregatedPowerStatsProcessorTest {
+ @Rule
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+ .setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720)
+ .setCpuScalingPolicy(0, new int[]{0, 1}, new int[]{100, 200})
+ .setCpuScalingPolicy(2, new int[]{2, 3}, new int[]{300})
+ .setAveragePowerForCpuScalingPolicy(0, 360)
+ .setAveragePowerForCpuScalingPolicy(2, 480)
+ .setAveragePowerForCpuScalingStep(0, 0, 300)
+ .setAveragePowerForCpuScalingStep(0, 1, 400)
+ .setAveragePowerForCpuScalingStep(2, 0, 500)
+ .setCpuPowerBracketCount(3)
+ .setCpuPowerBracket(0, 0, 0)
+ .setCpuPowerBracket(0, 1, 1)
+ .setCpuPowerBracket(2, 0, 2);
+
+ private AggregatedPowerStatsConfig.PowerComponent mConfig;
+ private CpuAggregatedPowerStatsProcessor mProcessor;
+ private MockPowerComponentAggregatedPowerStats mStats;
+
+ @Before
+ public void setup() {
+ mConfig = new AggregatedPowerStatsConfig.PowerComponent(BatteryConsumer.POWER_COMPONENT_CPU)
+ .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+ .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE);
+
+ mProcessor = new CpuAggregatedPowerStatsProcessor(
+ mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies());
+ }
+
+ @Test
+ public void powerProfileModel() {
+ mStats = new MockPowerComponentAggregatedPowerStats(mConfig, false);
+ mStats.setDeviceStats(
+ states(POWER_STATE_BATTERY, SCREEN_STATE_ON),
+ concat(
+ values(3500, 4500, 3000), // scaling steps
+ values(2000, 1000), // clusters
+ values(5000)), // uptime
+ 3.113732);
+ mStats.setDeviceStats(
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON),
+ concat(
+ values(6000, 6500, 4000),
+ values(5000, 3000),
+ values(7000)),
+ 4.607245);
+ mStats.setDeviceStats(
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER),
+ concat(
+ values(9000, 10000, 7000),
+ values(8000, 6000),
+ values(20000)),
+ 7.331799);
+ mStats.setUidStats(24,
+ states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND),
+ values(400, 1500, 2000), 1.206947);
+ mStats.setUidStats(42,
+ states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND),
+ values(900, 1000, 1500), 1.016182);
+ mStats.setUidStats(42,
+ states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_BACKGROUND),
+ values(600, 500, 300), 0.385042);
+ mStats.setUidStats(42,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED),
+ values(1500, 2000, 1000), 1.252578);
+
+ mProcessor.finish(mStats);
+
+ mStats.verifyPowerEstimates();
+ }
+
+ @Test
+ public void energyConsumerModel() {
+ mStats = new MockPowerComponentAggregatedPowerStats(mConfig, true);
+ mStats.setDeviceStats(
+ states(POWER_STATE_BATTERY, SCREEN_STATE_ON),
+ concat(
+ values(3500, 4500, 3000), // scaling steps
+ values(2000, 1000), // clusters
+ values(5000), // uptime
+ values(5_000_000L, 6_000_000L)), // energy, uC
+ 3.055555);
+ mStats.setDeviceStats(
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON),
+ concat(
+ values(6000, 6500, 4000),
+ values(5000, 3000),
+ values(7000),
+ values(5_000_000L, 6_000_000L)), // same as above
+ 3.055555); // same as above - WAI
+ mStats.setDeviceStats(
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER),
+ concat(
+ values(9000, 10000, 7000),
+ values(8000, 6000),
+ values(20000),
+ values(8_000_000L, 18_000_000L)),
+ 7.222222);
+ mStats.setUidStats(24,
+ states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND),
+ values(400, 1500, 2000), 1.449078);
+ mStats.setUidStats(42,
+ states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND),
+ values(900, 1000, 1500), 1.161902);
+ mStats.setUidStats(42,
+ states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_BACKGROUND),
+ values(600, 500, 300), 0.355406);
+ mStats.setUidStats(42,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED),
+ values(1500, 2000, 1000), 0.80773);
+
+ mProcessor.finish(mStats);
+
+ mStats.verifyPowerEstimates();
+ }
+
+ private int[] states(int... states) {
+ return states;
+ }
+
+ private long[] values(long... values) {
+ return values;
+ }
+
+ private long[] concat(long[]... arrays) {
+ LongArray all = new LongArray();
+ for (long[] array : arrays) {
+ for (long value : array) {
+ all.add(value);
+ }
+ }
+ return all.toArray();
+ }
+
+ private static class MockPowerComponentAggregatedPowerStats extends
+ PowerComponentAggregatedPowerStats {
+ private final CpuPowerStatsCollector.StatsArrayLayout mStatsLayout;
+ private final PowerStats.Descriptor mDescriptor;
+ private HashMap<String, long[]> mDeviceStats = new HashMap<>();
+ private HashMap<String, long[]> mUidStats = new HashMap<>();
+ private HashSet<Integer> mUids = new HashSet<>();
+ private HashMap<String, Double> mExpectedDevicePower = new HashMap<>();
+ private HashMap<String, Double> mExpectedUidPower = new HashMap<>();
+
+ MockPowerComponentAggregatedPowerStats(AggregatedPowerStatsConfig.PowerComponent config,
+ boolean useEnergyConsumers) {
+ super(config);
+ mStatsLayout = new CpuPowerStatsCollector.StatsArrayLayout();
+ mStatsLayout.addDeviceSectionCpuTimeByScalingStep(3);
+ mStatsLayout.addDeviceSectionCpuTimeByCluster(2);
+ mStatsLayout.addDeviceSectionUptime();
+ if (useEnergyConsumers) {
+ mStatsLayout.addDeviceSectionEnergyConsumers(2);
+ }
+ mStatsLayout.addDeviceSectionPowerEstimate();
+ mStatsLayout.addUidSectionCpuTimeByPowerBracket(new int[]{0, 1, 2});
+ mStatsLayout.addUidSectionPowerEstimate();
+
+ PersistableBundle extras = new PersistableBundle();
+ mStatsLayout.toExtras(extras);
+ mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU,
+ mStatsLayout.getDeviceStatsArrayLength(), mStatsLayout.getUidStatsArrayLength(),
+ extras);
+ }
+
+ @Override
+ public PowerStats.Descriptor getPowerStatsDescriptor() {
+ return mDescriptor;
+ }
+
+ @Override
+ boolean getDeviceStats(long[] outValues, int[] deviceStates) {
+ long[] values = getDeviceStats(deviceStates);
+ System.arraycopy(values, 0, outValues, 0, values.length);
+ return true;
+ }
+
+ private long[] getDeviceStats(int[] deviceStates) {
+ String key = statesToString(getConfig().getDeviceStateConfig(), deviceStates);
+ long[] values = mDeviceStats.get(key);
+ return values == null ? new long[mDescriptor.statsArrayLength] : values;
+ }
+
+ void setDeviceStats(int[] states, long[] values, double expectedPowerEstimate) {
+ setDeviceStats(states, values);
+ mExpectedDevicePower.put(statesToString(getConfig().getDeviceStateConfig(), states),
+ expectedPowerEstimate);
+ }
+
+ @Override
+ void setDeviceStats(int[] states, long[] values) {
+ String key = statesToString(getConfig().getDeviceStateConfig(), states);
+ mDeviceStats.put(key, Arrays.copyOf(values, mDescriptor.statsArrayLength));
+ }
+
+ @Override
+ boolean getUidStats(long[] outValues, int uid, int[] uidStates) {
+ long[] values = getUidStats(uid, uidStates);
+ assertThat(values).isNotNull();
+ System.arraycopy(values, 0, outValues, 0, values.length);
+ return true;
+ }
+
+ private long[] getUidStats(int uid, int[] uidStates) {
+ String key = uid + " " + statesToString(getConfig().getUidStateConfig(), uidStates);
+ long[] values = mUidStats.get(key);
+ return values == null ? new long[mDescriptor.uidStatsArrayLength] : values;
+ }
+
+ void setUidStats(int uid, int[] states, long[] values, double expectedPowerEstimate) {
+ setUidStats(uid, states, values);
+ mExpectedUidPower.put(
+ uid + " " + statesToString(getConfig().getUidStateConfig(), states),
+ expectedPowerEstimate);
+ }
+
+ @Override
+ void setUidStats(int uid, int[] states, long[] values) {
+ mUids.add(uid);
+ String key = uid + " " + statesToString(getConfig().getUidStateConfig(), states);
+ mUidStats.put(key, Arrays.copyOf(values, mDescriptor.uidStatsArrayLength));
+ }
+
+ @Override
+ void collectUids(Collection<Integer> uids) {
+ uids.addAll(mUids);
+ }
+
+ void verifyPowerEstimates() {
+ StringBuilder mismatches = new StringBuilder();
+ for (Map.Entry<String, Double> entry : mExpectedDevicePower.entrySet()) {
+ String key = entry.getKey();
+ double expected = mExpectedDevicePower.get(key);
+ double actual = mStatsLayout.getDevicePowerEstimate(mDeviceStats.get(key));
+ if (Math.abs(expected - actual) > 0.005) {
+ mismatches.append(key + " expected: " + expected + " actual: " + actual + "\n");
+ }
+ }
+ for (Map.Entry<String, Double> entry : mExpectedUidPower.entrySet()) {
+ String key = entry.getKey();
+ double expected = mExpectedUidPower.get(key);
+ double actual = mStatsLayout.getUidPowerEstimate(mUidStats.get(key));
+ if (Math.abs(expected - actual) > 0.005) {
+ mismatches.append(key + " expected: " + expected + " actual: " + actual + "\n");
+ }
+ }
+ if (!mismatches.isEmpty()) {
+ fail("Unexpected power estimations:\n" + mismatches);
+ }
+ }
+
+ private String statesToString(MultiStateStats.States[] config, int[] states) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < states.length; i++) {
+ sb.append(config[i].getName()).append("=").append(states[i]).append(" ");
+ }
+ return sb.toString();
+ }
+ }
+}
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 f2ee6db..bc211df 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
@@ -20,16 +20,26 @@
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.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.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.CpuScalingPolicies;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.PowerStats;
@@ -40,85 +50,266 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class CpuPowerStatsCollectorTest {
+ private Context mContext;
private final MockClock mMockClock = new MockClock();
private final HandlerThread mHandlerThread = new HandlerThread("test");
private Handler mHandler;
- private CpuPowerStatsCollector mCollector;
private PowerStats mCollectedStats;
- @Mock
private PowerProfile mPowerProfile;
@Mock
private CpuPowerStatsCollector.KernelCpuStatsReader mMockKernelCpuStatsReader;
+ @Mock
+ private PowerStatsInternal mPowerStatsInternal;
+ private CpuScalingPolicies mCpuScalingPolicies;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+ mContext = InstrumentationRegistry.getContext();
mHandlerThread.start();
mHandler = mHandlerThread.getThreadHandler();
- when(mPowerProfile.getCpuPowerBracketCount()).thenReturn(2);
- when(mPowerProfile.getCpuPowerBracketForScalingStep(0, 0)).thenReturn(0);
- when(mPowerProfile.getCpuPowerBracketForScalingStep(0, 1)).thenReturn(1);
- mCollector = new CpuPowerStatsCollector(new CpuScalingPolicies(
- new SparseArray<>() {{
- put(0, new int[]{0});
- }},
- new SparseArray<>() {{
- put(0, new int[]{1, 12});
- }}),
- mPowerProfile, mHandler, mMockKernelCpuStatsReader, 60_000, mMockClock);
- mCollector.addConsumer(stats -> mCollectedStats = stats);
- mCollector.setEnabled(true);
+ when(mMockKernelCpuStatsReader.nativeIsSupportedFeature()).thenReturn(true);
}
@Test
- public void collectStats() {
- mockKernelCpuStats(new SparseArray<>() {{
- put(42, new long[]{100, 200});
- put(99, new long[]{300, 600});
- }}, 0, 1234);
+ public void powerBrackets_specifiedInPowerProfile() {
+ mPowerProfile = new PowerProfile(mContext);
+ mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test_power_brackets);
+ mCpuScalingPolicies = new CpuScalingPolicies(
+ new SparseArray<>() {{
+ put(0, new int[]{0});
+ put(4, new int[]{4});
+ }},
+ new SparseArray<>() {{
+ put(0, new int[]{100});
+ put(4, new int[]{400, 500});
+ }});
+
+ CpuPowerStatsCollector collector = createCollector(8, 0);
+
+ assertThat(getScalingStepToPowerBracketMap(collector))
+ .isEqualTo(new int[]{1, 1, 0});
+ }
+
+ @Test
+ public void powerBrackets_default_noEnergyConsumers() {
+ mPowerProfile = new PowerProfile(mContext);
+ mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
+ mockCpuScalingPolicies(2);
+
+ CpuPowerStatsCollector collector = createCollector(3, 0);
+
+ assertThat(new String[]{
+ collector.getCpuPowerBracketDescription(0),
+ collector.getCpuPowerBracketDescription(1),
+ collector.getCpuPowerBracketDescription(2)})
+ .isEqualTo(new String[]{
+ "0/300(10.0)",
+ "0/1000(20.0), 0/2000(30.0), 4/300(25.0)",
+ "4/1000(35.0), 4/2500(50.0), 4/3000(60.0)"});
+ assertThat(getScalingStepToPowerBracketMap(collector))
+ .isEqualTo(new int[]{0, 1, 1, 1, 2, 2, 2});
+ }
+
+ @Test
+ public void powerBrackets_moreBracketsThanStates() {
+ mPowerProfile = new PowerProfile(mContext);
+ mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
+ mockCpuScalingPolicies(2);
+
+ CpuPowerStatsCollector collector = createCollector(8, 0);
+
+ assertThat(getScalingStepToPowerBracketMap(collector))
+ .isEqualTo(new int[]{0, 1, 2, 3, 4, 5, 6});
+ }
+
+ @Test
+ public void powerBrackets_energyConsumers() throws Exception {
+ mPowerProfile = new PowerProfile(mContext);
+ mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
+ mockCpuScalingPolicies(2);
+ mockEnergyConsumers();
+
+ CpuPowerStatsCollector collector = createCollector(8, 2);
+
+ assertThat(getScalingStepToPowerBracketMap(collector))
+ .isEqualTo(new int[]{0, 1, 1, 2, 2, 3, 3});
+ }
+
+ @Test
+ public void powerStatsDescriptor() throws Exception {
+ mPowerProfile = new PowerProfile(mContext);
+ mPowerProfile.forceInitForTesting(mContext, R.xml.power_profile_test);
+ mockCpuScalingPolicies(2);
+ mockEnergyConsumers();
+
+ CpuPowerStatsCollector collector = createCollector(8, 2);
+ PowerStats.Descriptor descriptor = collector.getPowerStatsDescriptor();
+ assertThat(descriptor.powerComponentId).isEqualTo(BatteryConsumer.POWER_COMPONENT_CPU);
+ assertThat(descriptor.name).isEqualTo("cpu");
+ assertThat(descriptor.statsArrayLength).isEqualTo(13);
+ assertThat(descriptor.uidStatsArrayLength).isEqualTo(5);
+ CpuPowerStatsCollector.StatsArrayLayout layout =
+ new CpuPowerStatsCollector.StatsArrayLayout();
+ layout.fromExtras(descriptor.extras);
+
+ long[] deviceStats = new long[descriptor.statsArrayLength];
+ layout.setTimeByScalingStep(deviceStats, 2, 42);
+ layout.setConsumedEnergy(deviceStats, 1, 43);
+ layout.setUptime(deviceStats, 44);
+ layout.setDevicePowerEstimate(deviceStats, 45);
+
+ long[] uidStats = new long[descriptor.uidStatsArrayLength];
+ layout.setUidTimeByPowerBracket(uidStats, 3, 46);
+ layout.setUidPowerEstimate(uidStats, 47);
+
+ assertThat(layout.getCpuScalingStepCount()).isEqualTo(7);
+ assertThat(layout.getTimeByScalingStep(deviceStats, 2)).isEqualTo(42);
+
+ assertThat(layout.getCpuClusterEnergyConsumerCount()).isEqualTo(2);
+ assertThat(layout.getConsumedEnergy(deviceStats, 1)).isEqualTo(43);
+
+ assertThat(layout.getUptime(deviceStats)).isEqualTo(44);
+
+ assertThat(layout.getDevicePowerEstimate(deviceStats)).isEqualTo(45);
+
+ assertThat(layout.getScalingStepToPowerBracketMap()).isEqualTo(
+ new int[]{0, 1, 1, 2, 2, 3, 3});
+ assertThat(layout.getCpuPowerBracketCount()).isEqualTo(4);
+
+ assertThat(layout.getUidTimeByPowerBracket(uidStats, 3)).isEqualTo(46);
+ assertThat(layout.getUidPowerEstimate(uidStats)).isEqualTo(47);
+ }
+
+ @Test
+ public void collectStats() throws Exception {
+ mockCpuScalingPolicies(1);
+ mockPowerProfile();
+ mockEnergyConsumers();
+
+ CpuPowerStatsCollector collector = createCollector(8, 0);
+ CpuPowerStatsCollector.StatsArrayLayout layout =
+ new CpuPowerStatsCollector.StatsArrayLayout();
+ layout.fromExtras(collector.getPowerStatsDescriptor().extras);
+
+ mockKernelCpuStats(new long[]{1111, 2222, 3333},
+ new SparseArray<>() {{
+ put(42, new long[]{100, 200});
+ put(99, new long[]{300, 600});
+ }}, 0, 1234);
mMockClock.uptime = 1000;
- mCollector.forceSchedule();
+ collector.forceSchedule();
waitForIdle();
assertThat(mCollectedStats.durationMs).isEqualTo(1234);
- assertThat(mCollectedStats.uidStats.get(42)).isEqualTo(new long[]{100, 200});
- assertThat(mCollectedStats.uidStats.get(99)).isEqualTo(new long[]{300, 600});
- mockKernelCpuStats(new SparseArray<>() {{
- put(42, new long[]{123, 234});
- put(99, new long[]{345, 678});
- }}, 1234, 3421);
+ assertThat(layout.getCpuScalingStepCount()).isEqualTo(3);
+ assertThat(layout.getTimeByScalingStep(mCollectedStats.stats, 0)).isEqualTo(1111);
+ assertThat(layout.getTimeByScalingStep(mCollectedStats.stats, 1)).isEqualTo(2222);
+ assertThat(layout.getTimeByScalingStep(mCollectedStats.stats, 2)).isEqualTo(3333);
+
+ assertThat(layout.getConsumedEnergy(mCollectedStats.stats, 0)).isEqualTo(0);
+ assertThat(layout.getConsumedEnergy(mCollectedStats.stats, 1)).isEqualTo(0);
+
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(42), 0))
+ .isEqualTo(100);
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(42), 1))
+ .isEqualTo(200);
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(99), 0))
+ .isEqualTo(300);
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(99), 1))
+ .isEqualTo(600);
+
+ mockKernelCpuStats(new long[]{5555, 4444, 3333},
+ new SparseArray<>() {{
+ put(42, new long[]{123, 234});
+ put(99, new long[]{345, 678});
+ }}, 1234, 3421);
mMockClock.uptime = 2000;
- mCollector.forceSchedule();
+ collector.forceSchedule();
waitForIdle();
assertThat(mCollectedStats.durationMs).isEqualTo(3421 - 1234);
- assertThat(mCollectedStats.uidStats.get(42)).isEqualTo(new long[]{23, 34});
- assertThat(mCollectedStats.uidStats.get(99)).isEqualTo(new long[]{45, 78});
+
+ assertThat(layout.getTimeByScalingStep(mCollectedStats.stats, 0)).isEqualTo(4444);
+ assertThat(layout.getTimeByScalingStep(mCollectedStats.stats, 1)).isEqualTo(2222);
+ assertThat(layout.getTimeByScalingStep(mCollectedStats.stats, 2)).isEqualTo(0);
+
+ // 500 * 1000 / 3500
+ assertThat(layout.getConsumedEnergy(mCollectedStats.stats, 0)).isEqualTo(143);
+ // 700 * 1000 / 3500
+ assertThat(layout.getConsumedEnergy(mCollectedStats.stats, 1)).isEqualTo(200);
+
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(42), 0))
+ .isEqualTo(23);
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(42), 1))
+ .isEqualTo(34);
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(99), 0))
+ .isEqualTo(45);
+ assertThat(layout.getUidTimeByPowerBracket(mCollectedStats.uidStats.get(99), 1))
+ .isEqualTo(78);
}
- private void mockKernelCpuStats(SparseArray<long[]> uidToCpuStats,
+ private void mockCpuScalingPolicies(int clusterCount) {
+ SparseArray<int[]> cpus = new SparseArray<>();
+ SparseArray<int[]> freqs = new SparseArray<>();
+ cpus.put(0, new int[]{0, 1, 2, 3});
+ freqs.put(0, new int[]{300000, 1000000, 2000000});
+ if (clusterCount == 2) {
+ cpus.put(4, new int[]{4, 5});
+ freqs.put(4, new int[]{300000, 1000000, 2500000, 3000000});
+ }
+ mCpuScalingPolicies = new CpuScalingPolicies(cpus, freqs);
+ }
+
+ private void mockPowerProfile() {
+ mPowerProfile = mock(PowerProfile.class);
+ when(mPowerProfile.getCpuPowerBracketCount()).thenReturn(2);
+ when(mPowerProfile.getCpuPowerBracketForScalingStep(0, 0)).thenReturn(0);
+ when(mPowerProfile.getCpuPowerBracketForScalingStep(0, 1)).thenReturn(1);
+ when(mPowerProfile.getCpuPowerBracketForScalingStep(0, 2)).thenReturn(1);
+ }
+
+ private CpuPowerStatsCollector createCollector(int defaultCpuPowerBrackets,
+ int defaultCpuPowerBracketsPerEnergyConsumer) {
+ CpuPowerStatsCollector collector = new CpuPowerStatsCollector(mCpuScalingPolicies,
+ mPowerProfile, mHandler, mMockKernelCpuStatsReader, () -> mPowerStatsInternal,
+ () -> 3500, 60_000, mMockClock, defaultCpuPowerBrackets,
+ defaultCpuPowerBracketsPerEnergyConsumer);
+ collector.addConsumer(stats -> mCollectedStats = stats);
+ collector.setEnabled(true);
+ return collector;
+ }
+
+ private void mockKernelCpuStats(long[] deviceStats, SparseArray<long[]> uidToCpuStats,
long expectedLastUpdateTimestampMs, long newLastUpdateTimestampMs) {
when(mMockKernelCpuStatsReader.nativeReadCpuStats(
any(CpuPowerStatsCollector.KernelCpuStatsCallback.class),
- any(int[].class), anyLong(), any(long[].class)))
+ any(int[].class), anyLong(), any(long[].class), any(long[].class)))
.thenAnswer(invocation -> {
CpuPowerStatsCollector.KernelCpuStatsCallback callback =
invocation.getArgument(0);
int[] powerBucketIndexes = invocation.getArgument(1);
long lastTimestamp = invocation.getArgument(2);
- long[] tempStats = invocation.getArgument(3);
+ long[] cpuTimeByScalingStep = invocation.getArgument(3);
+ long[] tempStats = invocation.getArgument(4);
- assertThat(powerBucketIndexes).isEqualTo(new int[]{0, 1});
+ assertThat(powerBucketIndexes).isEqualTo(new int[]{0, 1, 1});
assertThat(lastTimestamp / 1000000L).isEqualTo(expectedLastUpdateTimestampMs);
assertThat(tempStats).hasLength(2);
+ System.arraycopy(deviceStats, 0, cpuTimeByScalingStep, 0,
+ cpuTimeByScalingStep.length);
+
for (int i = 0; i < uidToCpuStats.size(); i++) {
int uid = uidToCpuStats.keyAt(i);
long[] cpuStats = uidToCpuStats.valueAt(i);
@@ -129,6 +320,67 @@
});
}
+ @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 static int[] getScalingStepToPowerBracketMap(CpuPowerStatsCollector collector) {
+ CpuPowerStatsCollector.StatsArrayLayout layout =
+ new CpuPowerStatsCollector.StatsArrayLayout();
+ layout.fromExtras(collector.getPowerStatsDescriptor().extras);
+ return layout.getScalingStepToPowerBracketMap();
+ }
+
private void waitForIdle() {
ConditionVariable done = new ConditionVariable();
mHandler.post(done::open);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
index 30a73181..eb03a6c 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
@@ -180,7 +180,7 @@
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true);
- multiStateStats.dump(pw);
+ multiStateStats.dump(pw, Arrays::toString);
assertThat(sw.toString()).isEqualTo(
"plugged-in fg [25, 50]\n"
+ "on-battery fg [25, 50]\n"
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index 71007f5..52a5d8f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -156,6 +156,7 @@
mUserState.setTargetAssignedToAccessibilityButton(COMPONENT_NAME.flattenToString());
mUserState.setTouchExplorationEnabledLocked(true);
mUserState.setMagnificationSingleFingerTripleTapEnabledLocked(true);
+ mUserState.setMagnificationTwoFingerTripleTapEnabledLocked(true);
mUserState.setAutoclickEnabledLocked(true);
mUserState.setUserNonInteractiveUiTimeoutLocked(30);
mUserState.setUserInteractiveUiTimeoutLocked(30);
@@ -178,6 +179,7 @@
assertNull(mUserState.getTargetAssignedToAccessibilityButton());
assertFalse(mUserState.isTouchExplorationEnabledLocked());
assertFalse(mUserState.isMagnificationSingleFingerTripleTapEnabledLocked());
+ assertFalse(mUserState.isMagnificationTwoFingerTripleTapEnabledLocked());
assertFalse(mUserState.isAutoclickEnabledLocked());
assertEquals(0, mUserState.getUserNonInteractiveUiTimeoutLocked());
assertEquals(0, mUserState.getUserInteractiveUiTimeoutLocked());
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index 4e6dd06..ece3dfe 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -674,6 +674,17 @@
}
@Test
+ public void notifyPermissionRequestCancelled_forwardsToLogger() {
+ int hostUid = 123;
+ mService =
+ new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector);
+
+ mService.notifyPermissionRequestCancelled(hostUid);
+
+ verify(mMediaProjectionMetricsLogger).logProjectionPermissionRequestCancelled(hostUid);
+ }
+
+ @Test
public void notifyAppSelectorDisplayed_forwardsToLogger() {
int hostUid = 456;
mService =
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
index 410604f..ad1cd6e 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
@@ -18,6 +18,7 @@
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CANCELLED;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED;
import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_PERMISSION_REQUEST_DISPLAYED;
@@ -452,6 +453,63 @@
MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED);
}
+ @Test
+ public void logProjectionPermissionRequestCancelled_logsStateChangedAtomId() {
+ mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+
+ verifyStateChangedAtomIdLogged();
+ }
+
+ @Test
+ public void logProjectionPermissionRequestCancelled_logsExistingSessionId() {
+ int existingSessionId = 456;
+ when(mSessionIdGenerator.getCurrentSessionId()).thenReturn(existingSessionId);
+
+ mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+
+ verifySessionIdLogged(existingSessionId);
+ }
+
+ @Test
+ public void logProjectionPermissionRequestCancelled_logsStateCancelled() {
+ mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+
+ verifyStateLogged(MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CANCELLED);
+ }
+
+ @Test
+ public void logProjectionPermissionRequestCancelled_logsPreviousState() {
+ mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
+ verifyPreviousStateLogged(
+ MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN);
+
+ mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+ verifyPreviousStateLogged(
+ MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED);
+ }
+
+ @Test
+ public void logProjectionPermissionRequestCancelled_logsHostUid() {
+ mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+
+ verifyHostUidLogged(TEST_HOST_UID);
+ }
+
+ @Test
+ public void logProjectionPermissionRequestCancelled_logsUnknownTargetUid() {
+ mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+
+ verifyTargetUidLogged(-2);
+ }
+
+ @Test
+ public void logProjectionPermissionRequestCancelled_logsUnknownCreationSource() {
+ mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
+
+ verifyCreationSourceLogged(
+ MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN);
+ }
+
private void verifyStateChangedAtomIdLogged() {
verify(mFrameworkStatsLogWrapper)
.write(
diff --git a/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java
index fc27edc..a4d50f0 100644
--- a/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java
@@ -94,27 +94,31 @@
public void testBugreportFileManagerFileExists() {
Pair<Integer, String> callingInfo = new Pair<>(mCallingUid, mCallingPackage);
mBugreportFileManager.addBugreportFileForCaller(
- callingInfo, mBugreportFile);
+ callingInfo, mBugreportFile, /* keepOnRetrieval= */ false);
assertThrows(IllegalArgumentException.class, () ->
mBugreportFileManager.ensureCallerPreviouslyGeneratedFile(
- callingInfo, "unknown-file.zip"));
+ mContext, callingInfo, Process.myUserHandle().getIdentifier(),
+ "unknown-file.zip"));
// No exception should be thrown.
- mBugreportFileManager.ensureCallerPreviouslyGeneratedFile(callingInfo, mBugreportFile);
+ mBugreportFileManager.ensureCallerPreviouslyGeneratedFile(
+ mContext, callingInfo, mContext.getUserId(), mBugreportFile);
}
@Test
public void testBugreportFileManagerMultipleFiles() {
Pair<Integer, String> callingInfo = new Pair<>(mCallingUid, mCallingPackage);
mBugreportFileManager.addBugreportFileForCaller(
- callingInfo, mBugreportFile);
+ callingInfo, mBugreportFile, /* keepOnRetrieval= */ false);
mBugreportFileManager.addBugreportFileForCaller(
- callingInfo, mBugreportFile2);
+ callingInfo, mBugreportFile2, /* keepOnRetrieval= */ false);
// No exception should be thrown.
- mBugreportFileManager.ensureCallerPreviouslyGeneratedFile(callingInfo, mBugreportFile);
- mBugreportFileManager.ensureCallerPreviouslyGeneratedFile(callingInfo, mBugreportFile2);
+ mBugreportFileManager.ensureCallerPreviouslyGeneratedFile(
+ mContext, callingInfo, mContext.getUserId(), mBugreportFile);
+ mBugreportFileManager.ensureCallerPreviouslyGeneratedFile(
+ mContext, callingInfo, mContext.getUserId(), mBugreportFile2);
}
@Test
@@ -122,7 +126,8 @@
Pair<Integer, String> callingInfo = new Pair<>(mCallingUid, mCallingPackage);
assertThrows(IllegalArgumentException.class,
() -> mBugreportFileManager.ensureCallerPreviouslyGeneratedFile(
- callingInfo, "test-file.zip"));
+ mContext, callingInfo, Process.myUserHandle().getIdentifier(),
+ "test-file.zip"));
}
@Test
@@ -130,7 +135,8 @@
CountDownLatch latch = new CountDownLatch(1);
Listener listener = new Listener(latch);
mService.retrieveBugreport(Binder.getCallingUid(), mContext.getPackageName(),
- new FileDescriptor(), mBugreportFile, listener);
+ mContext.getUserId(), new FileDescriptor(), mBugreportFile,
+ /* keepOnRetrieval= */ false, listener);
assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue();
assertThat(listener.getErrorCode()).isEqualTo(
BugreportCallback.BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 6235b3b..c57b051 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -1742,6 +1742,7 @@
private void testApplyTransaction_reorder_failsIfNotSystemOrganizer_common(
@TaskFragmentOperation.OperationType int opType) {
final Task task = createTask(mDisplayContent);
+ doNothing().when(task).sendTaskFragmentParentInfoChangedIfNeeded();
// Create a non-embedded Activity at the bottom.
final ActivityRecord bottomActivity = new ActivityBuilder(mAtm)
.setTask(task)
@@ -1934,7 +1935,7 @@
/** Setups the mock Task as the parent of the given TaskFragment. */
private static void setupMockParent(TaskFragment taskFragment, Task mockParent) {
doReturn(mockParent).when(taskFragment).getTask();
- doReturn(new TaskFragmentParentInfo(new Configuration(), DEFAULT_DISPLAY, true))
+ doReturn(new TaskFragmentParentInfo(new Configuration(), DEFAULT_DISPLAY, true, true))
.when(mockParent).getTaskFragmentParentInfo();
// Task needs to be visible
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 0c58069..435a835 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -1568,6 +1568,7 @@
final TaskFragment fragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
final ActivityRecord embeddedActivity = fragment.getTopMostActivity();
+ doNothing().when(task).sendTaskFragmentParentInfoChangedIfNeeded();
task.moveActivityToFront(activity);
assertEquals("Activity must be moved to front", activity, task.getTopMostActivity());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 3ec6f42..973ab84 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -448,7 +448,6 @@
mDisplayContent.updateImeParent();
// Ime should on top of the popup IME layering target window.
- mDisplayContent.assignChildLayers(mTransaction);
assertWindowHigher(mImeWindow, popupImeTargetWin);
}
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index ecd7039..d30078d 100755
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -29,6 +29,7 @@
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsVideoCallProvider;
+import com.android.internal.telephony.flags.Flags;
import com.android.internal.telephony.util.TelephonyUtils;
import java.util.ArrayList;
@@ -545,6 +546,11 @@
try {
iSession.setListener(mIImsCallSessionListenerProxy);
} catch (RemoteException e) {
+ if (Flags.ignoreAlreadyTerminatedIncomingCallBeforeRegisteringListener()) {
+ // Registering listener failed, so other operations are not allowed.
+ Log.e(TAG, "ImsCallSession : " + e);
+ mClosed = true;
+ }
}
} else {
mClosed = true;
diff --git a/tests/notification/Android.bp b/tests/notification/Android.bp
deleted file mode 100644
index 1c1b5a2..0000000
--- a/tests/notification/Android.bp
+++ /dev/null
@@ -1,16 +0,0 @@
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_base_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test {
- name: "NotificationTests",
- // Include all test java files.
- srcs: ["src/**/*.java"],
- libs: ["android.test.runner.stubs"],
- sdk_version: "21",
-}
diff --git a/tests/notification/AndroidManifest.xml b/tests/notification/AndroidManifest.xml
deleted file mode 100644
index 7cee00a7..0000000
--- a/tests/notification/AndroidManifest.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.frameworks.tests.notification"
- >
- <application>
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation
- android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.frameworks.tests.notification"
- android:label="Frameworks Notification Tests" />
-</manifest>
diff --git a/tests/notification/OWNERS b/tests/notification/OWNERS
deleted file mode 100644
index 396fd12..0000000
--- a/tests/notification/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /services/core/java/com/android/server/notification/OWNERS
diff --git a/tests/notification/res/drawable-nodpi/arubin_hed.jpeg b/tests/notification/res/drawable-nodpi/arubin_hed.jpeg
deleted file mode 100644
index c6d8ae9..0000000
--- a/tests/notification/res/drawable-nodpi/arubin_hed.jpeg
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-nodpi/bucket.png b/tests/notification/res/drawable-nodpi/bucket.png
deleted file mode 100644
index c865649..0000000
--- a/tests/notification/res/drawable-nodpi/bucket.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-nodpi/matias_hed.jpg b/tests/notification/res/drawable-nodpi/matias_hed.jpg
deleted file mode 100644
index 8cc3081..0000000
--- a/tests/notification/res/drawable-nodpi/matias_hed.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-nodpi/page_hed.jpg b/tests/notification/res/drawable-nodpi/page_hed.jpg
deleted file mode 100644
index ea950c8..0000000
--- a/tests/notification/res/drawable-nodpi/page_hed.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-nodpi/romainguy_hed.jpg b/tests/notification/res/drawable-nodpi/romainguy_hed.jpg
deleted file mode 100644
index 5b7643e..0000000
--- a/tests/notification/res/drawable-nodpi/romainguy_hed.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-nodpi/romainguy_rockaway.jpg b/tests/notification/res/drawable-nodpi/romainguy_rockaway.jpg
deleted file mode 100644
index 68473ba..0000000
--- a/tests/notification/res/drawable-nodpi/romainguy_rockaway.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/add.png b/tests/notification/res/drawable-xhdpi/add.png
deleted file mode 100644
index 7226b3d..0000000
--- a/tests/notification/res/drawable-xhdpi/add.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/ic_dial_action_call.png b/tests/notification/res/drawable-xhdpi/ic_dial_action_call.png
deleted file mode 100644
index ca20a91..0000000
--- a/tests/notification/res/drawable-xhdpi/ic_dial_action_call.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/ic_end_call.png b/tests/notification/res/drawable-xhdpi/ic_end_call.png
deleted file mode 100644
index c464a6d..0000000
--- a/tests/notification/res/drawable-xhdpi/ic_end_call.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/ic_media_next.png b/tests/notification/res/drawable-xhdpi/ic_media_next.png
deleted file mode 100644
index 4def965..0000000
--- a/tests/notification/res/drawable-xhdpi/ic_media_next.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/ic_menu_upload.png b/tests/notification/res/drawable-xhdpi/ic_menu_upload.png
deleted file mode 100644
index f1438ed..0000000
--- a/tests/notification/res/drawable-xhdpi/ic_menu_upload.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/icon.png b/tests/notification/res/drawable-xhdpi/icon.png
deleted file mode 100644
index 189e85b..0000000
--- a/tests/notification/res/drawable-xhdpi/icon.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_alarm.png b/tests/notification/res/drawable-xhdpi/stat_notify_alarm.png
deleted file mode 100644
index 658d04f..0000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_alarm.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_calendar.png b/tests/notification/res/drawable-xhdpi/stat_notify_calendar.png
deleted file mode 100644
index 5ae7782..0000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_calendar.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_email.png b/tests/notification/res/drawable-xhdpi/stat_notify_email.png
deleted file mode 100644
index 23c4672..0000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_email.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_missed_call.png b/tests/notification/res/drawable-xhdpi/stat_notify_missed_call.png
deleted file mode 100644
index 8719eff..0000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_missed_call.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_sms.png b/tests/notification/res/drawable-xhdpi/stat_notify_sms.png
deleted file mode 100644
index 323cb3d..0000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_sms.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_snooze.png b/tests/notification/res/drawable-xhdpi/stat_notify_snooze.png
deleted file mode 100644
index 26dcda35..0000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_snooze.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_snooze_longer.png b/tests/notification/res/drawable-xhdpi/stat_notify_snooze_longer.png
deleted file mode 100644
index b8b2f8a..0000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_snooze_longer.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_talk_text.png b/tests/notification/res/drawable-xhdpi/stat_notify_talk_text.png
deleted file mode 100644
index 12cae9f..0000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_talk_text.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_sys_phone_call.png b/tests/notification/res/drawable-xhdpi/stat_sys_phone_call.png
deleted file mode 100644
index db42b7c..0000000
--- a/tests/notification/res/drawable-xhdpi/stat_sys_phone_call.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/layout/full_screen.xml b/tests/notification/res/layout/full_screen.xml
deleted file mode 100644
index 6ff7552..0000000
--- a/tests/notification/res/layout/full_screen.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <ImageView
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:src="@drawable/page_hed"
- android:onClick="dismiss"
- />
-</FrameLayout>
\ No newline at end of file
diff --git a/tests/notification/res/layout/main.xml b/tests/notification/res/layout/main.xml
deleted file mode 100644
index f5a740f..0000000
--- a/tests/notification/res/layout/main.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <LinearLayout android:id="@+id/linearLayout1" android:orientation="vertical" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_width="match_parent" android:layout_margin="35dp">
- <Button android:id="@+id/button1" android:text="@string/post_button_label" android:layout_height="wrap_content" android:layout_width="match_parent" android:onClick="doPost"></Button>
- <Button android:id="@+id/button2" android:text="@string/remove_button_label" android:layout_height="wrap_content" android:layout_width="match_parent" android:onClick="doRemove"></Button>
- </LinearLayout>
-</FrameLayout>
diff --git a/tests/notification/res/values/dimens.xml b/tests/notification/res/values/dimens.xml
deleted file mode 100644
index 21e7bc3..0000000
--- a/tests/notification/res/values/dimens.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources>
- <!-- The width of the big icons in notifications. -->
- <dimen name="notification_large_icon_width">64dp</dimen>
- <!-- The width of the big icons in notifications. -->
- <dimen name="notification_large_icon_height">64dp</dimen>
-</resources>
diff --git a/tests/notification/res/values/strings.xml b/tests/notification/res/values/strings.xml
deleted file mode 100644
index 80bf103..0000000
--- a/tests/notification/res/values/strings.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <string name="hello">Hello World, NotificationShowcaseActivity!</string>
- <string name="app_name">NotificationShowcase</string>
- <string name="post_button_label">Post Notifications</string>
- <string name="remove_button_label">Remove Notifications</string>
- <string name="answered">call answered</string>
- <string name="ignored">call ignored</string>
- <string name="full_screen_name">Full Screen Activity</string>
-</resources>
diff --git a/tests/notification/src/com/android/frameworks/tests/notification/NotificationTests.java b/tests/notification/src/com/android/frameworks/tests/notification/NotificationTests.java
deleted file mode 100644
index 5d639f6..0000000
--- a/tests/notification/src/com/android/frameworks/tests/notification/NotificationTests.java
+++ /dev/null
@@ -1,494 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Typeface;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Parcel;
-import android.os.SystemClock;
-import android.provider.ContactsContract;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.Suppress;
-import android.text.SpannableStringBuilder;
-import android.text.TextUtils;
-import android.text.style.StyleSpan;
-import android.util.Log;
-import android.view.View;
-import android.widget.Toast;
-
-import java.lang.reflect.Method;
-import java.lang.InterruptedException;
-import java.lang.NoSuchMethodError;
-import java.lang.NoSuchMethodException;
-import java.util.ArrayList;
-
-import com.android.frameworks.tests.notification.R;
-
-public class NotificationTests extends AndroidTestCase {
- private static final String TAG = "NOTEST";
- public static void L(String msg, Object... args) {
- Log.v(TAG, (args == null || args.length == 0) ? msg : String.format(msg, args));
- }
-
- public static final String ACTION_CREATE = "create";
- public static final int NOTIFICATION_ID = 31338;
-
- public static final boolean SHOW_PHONE_CALL = false;
- public static final boolean SHOW_INBOX = true;
- public static final boolean SHOW_BIG_TEXT = true;
- public static final boolean SHOW_BIG_PICTURE = true;
- public static final boolean SHOW_MEDIA = true;
- public static final boolean SHOW_STOPWATCH = false;
- public static final boolean SHOW_SOCIAL = false;
- public static final boolean SHOW_CALENDAR = false;
- public static final boolean SHOW_PROGRESS = false;
-
- private static Bitmap getBitmap(Context context, int resId) {
- int largeIconWidth = (int) context.getResources()
- .getDimension(R.dimen.notification_large_icon_width);
- int largeIconHeight = (int) context.getResources()
- .getDimension(R.dimen.notification_large_icon_height);
- Drawable d = context.getResources().getDrawable(resId);
- Bitmap b = Bitmap.createBitmap(largeIconWidth, largeIconHeight, Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(b);
- d.setBounds(0, 0, largeIconWidth, largeIconHeight);
- d.draw(c);
- return b;
- }
-
- private static PendingIntent makeEmailIntent(Context context, String who) {
- final Intent intent = new Intent(android.content.Intent.ACTION_SENDTO,
- Uri.parse("mailto:" + who));
- return PendingIntent.getActivity(
- context, 0, intent,
- PendingIntent.FLAG_CANCEL_CURRENT);
- }
-
- static final String[] LINES = new String[] {
- "Uh oh",
- "Getting kicked out of this room",
- "I'll be back in 5-10 minutes.",
- "And now \u2026 I have to find my shoes. \uD83D\uDC63",
- "\uD83D\uDC5F \uD83D\uDC5F",
- "\uD83D\uDC60 \uD83D\uDC60",
- };
- static final int MAX_LINES = 5;
- public static Notification makeBigTextNotification(Context context, int update, int id,
- long when) {
- String personUri = null;
- /*
- Cursor c = null;
- try {
- String[] projection = new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.LOOKUP_KEY };
- String selections = ContactsContract.Contacts.DISPLAY_NAME + " = 'Mike Cleron'";
- final ContentResolver contentResolver = context.getContentResolver();
- c = contentResolver.query(ContactsContract.Contacts.CONTENT_URI,
- projection, selections, null, null);
- if (c != null && c.getCount() > 0) {
- c.moveToFirst();
- int lookupIdx = c.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY);
- int idIdx = c.getColumnIndex(ContactsContract.Contacts._ID);
- String lookupKey = c.getString(lookupIdx);
- long contactId = c.getLong(idIdx);
- Uri lookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
- personUri = lookupUri.toString();
- }
- } finally {
- if (c != null) {
- c.close();
- }
- }
- if (TextUtils.isEmpty(personUri)) {
- Log.w(TAG, "failed to find contact for Mike Cleron");
- } else {
- Log.w(TAG, "Mike Cleron is " + personUri);
- }
- */
-
- StringBuilder longSmsText = new StringBuilder();
- int end = 2 + update;
- if (end > LINES.length) {
- end = LINES.length;
- }
- final int start = Math.max(0, end - MAX_LINES);
- for (int i=start; i<end; i++) {
- if (i >= LINES.length) break;
- if (i > start) longSmsText.append("\n");
- longSmsText.append(LINES[i]);
- }
- if (update > 2) {
- when = System.currentTimeMillis();
- }
- Notification.BigTextStyle bigTextStyle = new Notification.BigTextStyle()
- .bigText(longSmsText);
- Notification bigText = new Notification.Builder(context)
- .setContentTitle("Mike Cleron")
- .setContentIntent(ToastService.getPendingIntent(context, "Clicked on bigText"))
- .setContentText(longSmsText)
- //.setTicker("Mike Cleron: " + longSmsText)
- .setWhen(when)
- .setLargeIcon(getBitmap(context, R.drawable.bucket))
- .setPriority(Notification.PRIORITY_HIGH)
- .setNumber(update)
- .setSmallIcon(R.drawable.stat_notify_talk_text)
- .setStyle(bigTextStyle)
- .setDefaults(Notification.DEFAULT_SOUND)
- .addPerson(personUri)
- .build();
- return bigText;
- }
-
- public static Notification makeUploadNotification(Context context, int progress, long when) {
- Notification.Builder uploadNotification = new Notification.Builder(context)
- .setContentTitle("File Upload")
- .setContentText("foo.txt")
- .setPriority(Notification.PRIORITY_MIN)
- .setContentIntent(ToastService.getPendingIntent(context, "Clicked on Upload"))
- .setWhen(when)
- .setSmallIcon(R.drawable.ic_menu_upload)
- .setProgress(100, Math.min(progress, 100), false);
- return uploadNotification.build();
- }
-
- static SpannableStringBuilder BOLD(CharSequence str) {
- final SpannableStringBuilder ssb = new SpannableStringBuilder(str);
- ssb.setSpan(new StyleSpan(Typeface.BOLD), 0, ssb.length(), 0);
- return ssb;
- }
-
- public static class ToastService extends IntentService {
-
- private static final String TAG = "ToastService";
-
- private static final String ACTION_TOAST = "toast";
-
- private Handler handler;
-
- public ToastService() {
- super(TAG);
- }
- public ToastService(String name) {
- super(name);
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- handler = new Handler();
- return super.onStartCommand(intent, flags, startId);
- }
-
- @Override
- protected void onHandleIntent(Intent intent) {
- Log.v(TAG, "clicked a thing! intent=" + intent.toString());
- if (intent.hasExtra("text")) {
- final String text = intent.getStringExtra("text");
- handler.post(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(ToastService.this, text, Toast.LENGTH_LONG).show();
- Log.v(TAG, "toast " + text);
- }
- });
- }
- }
-
- public static PendingIntent getPendingIntent(Context context, String text) {
- Intent toastIntent = new Intent(context, ToastService.class);
- toastIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- toastIntent.setAction(ACTION_TOAST + ":" + text); // one per toast message
- toastIntent.putExtra("text", text);
- PendingIntent pi = PendingIntent.getService(
- context, 58, toastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- return pi;
- }
- }
-
- public static void sleepIfYouCan(int ms) {
- try {
- Thread.sleep(ms);
- } catch (InterruptedException e) {}
- }
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- }
-
- public static String summarize(Notification n) {
- return String.format("<notif title=\"%s\" icon=0x%08x view=%s>",
- n.extras.get(Notification.EXTRA_TITLE),
- n.icon,
- String.valueOf(n.contentView));
- }
-
- public void testCreate() throws Exception {
- ArrayList<Notification> mNotifications = new ArrayList<Notification>();
- NotificationManager noMa =
- (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-
- L("Constructing notifications...");
- if (SHOW_BIG_TEXT) {
- int bigtextId = mNotifications.size();
- final long time = SystemClock.currentThreadTimeMillis();
- final Notification n = makeBigTextNotification(mContext, 0, bigtextId, System.currentTimeMillis());
- L(" %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time);
- mNotifications.add(n);
- }
-
- int uploadId = mNotifications.size();
- long uploadWhen = System.currentTimeMillis();
-
- if (SHOW_PROGRESS) {
- mNotifications.add(makeUploadNotification(mContext, 0, uploadWhen));
- }
-
- if (SHOW_PHONE_CALL) {
- int phoneId = mNotifications.size();
- final PendingIntent fullscreenIntent
- = FullScreenActivity.getPendingIntent(mContext, phoneId);
- final long time = SystemClock.currentThreadTimeMillis();
- Notification phoneCall = new Notification.Builder(mContext)
- .setContentTitle("Incoming call")
- .setContentText("Matias Duarte")
- .setLargeIcon(getBitmap(mContext, R.drawable.matias_hed))
- .setSmallIcon(R.drawable.stat_sys_phone_call)
- .setDefaults(Notification.DEFAULT_SOUND)
- .setPriority(Notification.PRIORITY_MAX)
- .setContentIntent(fullscreenIntent)
- .setFullScreenIntent(fullscreenIntent, true)
- .addAction(R.drawable.ic_dial_action_call, "Answer",
- ToastService.getPendingIntent(mContext, "Clicked on Answer"))
- .addAction(R.drawable.ic_end_call, "Ignore",
- ToastService.getPendingIntent(mContext, "Clicked on Ignore"))
- .setOngoing(true)
- .addPerson(Uri.fromParts("tel", "1 (617) 555-1212", null).toString())
- .build();
- L(" %s: create=%dms", phoneCall.toString(), SystemClock.currentThreadTimeMillis() - time);
- mNotifications.add(phoneCall);
- }
-
- if (SHOW_STOPWATCH) {
- final long time = SystemClock.currentThreadTimeMillis();
- final Notification n = new Notification.Builder(mContext)
- .setContentTitle("Stopwatch PRO")
- .setContentText("Counting up")
- .setContentIntent(ToastService.getPendingIntent(mContext, "Clicked on Stopwatch"))
- .setSmallIcon(R.drawable.stat_notify_alarm)
- .setUsesChronometer(true)
- .build();
- L(" %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time);
- mNotifications.add(n);
- }
-
- if (SHOW_CALENDAR) {
- final long time = SystemClock.currentThreadTimeMillis();
- final Notification n = new Notification.Builder(mContext)
- .setContentTitle("J Planning")
- .setContentText("The Botcave")
- .setWhen(System.currentTimeMillis())
- .setSmallIcon(R.drawable.stat_notify_calendar)
- .setContentIntent(ToastService.getPendingIntent(mContext, "Clicked on calendar event"))
- .setContentInfo("7PM")
- .addAction(R.drawable.stat_notify_snooze, "+10 min",
- ToastService.getPendingIntent(mContext, "snoozed 10 min"))
- .addAction(R.drawable.stat_notify_snooze_longer, "+1 hour",
- ToastService.getPendingIntent(mContext, "snoozed 1 hr"))
- .addAction(R.drawable.stat_notify_email, "Email",
- ToastService.getPendingIntent(mContext,
- "Congratulations, you just destroyed someone's inbox zero"))
- .build();
- L(" %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time);
- mNotifications.add(n);
- }
-
- if (SHOW_BIG_PICTURE) {
- BitmapDrawable d =
- (BitmapDrawable) mContext.getResources().getDrawable(R.drawable.romainguy_rockaway);
- final long time = SystemClock.currentThreadTimeMillis();
- final Notification n = new Notification.Builder(mContext)
- .setContentTitle("Romain Guy")
- .setContentText("I was lucky to find a Canon 5D Mk III at a local Bay Area "
- + "store last week but I had not been able to try it in the field "
- + "until tonight. After a few days of rain the sky finally cleared "
- + "up. Rockaway Beach did not disappoint and I was finally able to "
- + "see what my new camera feels like when shooting landscapes.")
- .setSmallIcon(android.R.drawable.stat_notify_chat)
- .setContentIntent(
- ToastService.getPendingIntent(mContext, "Clicked picture"))
- .setLargeIcon(getBitmap(mContext, R.drawable.romainguy_hed))
- .addAction(R.drawable.add, "Add to Gallery",
- ToastService.getPendingIntent(mContext, "Added"))
- .setStyle(new Notification.BigPictureStyle()
- .bigPicture(d.getBitmap()))
- .build();
- L(" %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time);
- mNotifications.add(n);
- }
-
- if (SHOW_INBOX) {
- final long time = SystemClock.currentThreadTimeMillis();
- final Notification n = new Notification.Builder(mContext)
- .setContentTitle("New mail")
- .setContentText("3 new messages")
- .setSubText("example@gmail.com")
- .setContentIntent(ToastService.getPendingIntent(mContext, "Clicked on Mail"))
- .setSmallIcon(R.drawable.stat_notify_email)
- .setStyle(new Notification.InboxStyle()
- .setSummaryText("example@gmail.com")
- .addLine(BOLD("Alice:").append(" hey there!"))
- .addLine(BOLD("Bob:").append(" hi there!"))
- .addLine(BOLD("Charlie:").append(" Iz IN UR EMAILZ!!"))
- ).build();
- L(" %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time);
- mNotifications.add(n);
- }
-
- if (SHOW_SOCIAL) {
- final long time = SystemClock.currentThreadTimeMillis();
- final Notification n = new Notification.Builder(mContext)
- .setContentTitle("Social Network")
- .setContentText("You were mentioned in a post")
- .setContentInfo("example@gmail.com")
- .setContentIntent(ToastService.getPendingIntent(mContext, "Clicked on Social"))
- .setSmallIcon(android.R.drawable.stat_notify_chat)
- .setPriority(Notification.PRIORITY_LOW)
- .build();
- L(" %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time);
- mNotifications.add(n);
- }
-
- L("Posting notifications...");
- for (int i=0; i<mNotifications.size(); i++) {
- final int count = 4;
- for (int j=0; j<count; j++) {
- long time = SystemClock.currentThreadTimeMillis();
- final Notification n = mNotifications.get(i);
- noMa.notify(NOTIFICATION_ID + i, n);
- time = SystemClock.currentThreadTimeMillis() - time;
- L(" %s: notify=%dms (%d/%d)", summarize(n), time,
- j + 1, count);
- sleepIfYouCan(150);
- }
- }
-
- sleepIfYouCan(1000);
-
- L("Canceling notifications...");
- for (int i=0; i<mNotifications.size(); i++) {
- final Notification n = mNotifications.get(i);
- long time = SystemClock.currentThreadTimeMillis();
- noMa.cancel(NOTIFICATION_ID + i);
- time = SystemClock.currentThreadTimeMillis() - time;
- L(" %s: cancel=%dms", summarize(n), time);
- }
-
- sleepIfYouCan(500);
-
- L("Parceling notifications...");
- // we want to be able to use this test on older OSes that do not have getOpenAshmemSize
- Method getOpenAshmemSize = null;
- try {
- getOpenAshmemSize = Parcel.class.getMethod("getOpenAshmemSize");
- } catch (NoSuchMethodException ex) {
- }
- for (int i=0; i<mNotifications.size(); i++) {
- Parcel p = Parcel.obtain();
- {
- final Notification n = mNotifications.get(i);
- long time = SystemClock.currentThreadTimeMillis();
- n.writeToParcel(p, 0);
- time = SystemClock.currentThreadTimeMillis() - time;
- L(" %s: write parcel=%dms size=%d ashmem=%s",
- summarize(n), time, p.dataPosition(),
- (getOpenAshmemSize != null)
- ? getOpenAshmemSize.invoke(p)
- : "???");
- p.setDataPosition(0);
- }
-
- long time = SystemClock.currentThreadTimeMillis();
- final Notification n2 = Notification.CREATOR.createFromParcel(p);
- time = SystemClock.currentThreadTimeMillis() - time;
- L(" %s: parcel read=%dms", summarize(n2), time);
-
- time = SystemClock.currentThreadTimeMillis();
- noMa.notify(NOTIFICATION_ID + i, n2);
- time = SystemClock.currentThreadTimeMillis() - time;
- L(" %s: notify=%dms", summarize(n2), time);
- }
-
- sleepIfYouCan(500);
-
- L("Canceling notifications...");
- for (int i=0; i<mNotifications.size(); i++) {
- long time = SystemClock.currentThreadTimeMillis();
- final Notification n = mNotifications.get(i);
- noMa.cancel(NOTIFICATION_ID + i);
- time = SystemClock.currentThreadTimeMillis() - time;
- L(" %s: cancel=%dms", summarize(n), time);
- }
-
-
-// if (SHOW_PROGRESS) {
-// ProgressService.startProgressUpdater(this, uploadId, uploadWhen, 0);
-// }
- }
-
- public static class FullScreenActivity extends Activity {
- public static final String EXTRA_ID = "id";
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.full_screen);
- final Intent intent = getIntent();
- if (intent != null && intent.hasExtra(EXTRA_ID)) {
- final int id = intent.getIntExtra(EXTRA_ID, -1);
- if (id >= 0) {
- NotificationManager noMa =
- (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- noMa.cancel(NOTIFICATION_ID + id);
- }
- }
- }
-
- public void dismiss(View v) {
- finish();
- }
-
- public static PendingIntent getPendingIntent(Context context, int id) {
- Intent fullScreenIntent = new Intent(context, FullScreenActivity.class);
- fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
- fullScreenIntent.putExtra(EXTRA_ID, id);
- PendingIntent pi = PendingIntent.getActivity(
- context, 22, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- return pi;
- }
- }
-}
-
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 1b1e93bd..a08f385 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -323,6 +323,7 @@
"should only be used together with the --static-lib flag.",
&options_.merge_only);
AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_);
+ AddOptionalFlagList("--feature-flags", "Placeholder, to be implemented.", &feature_flags_args_);
}
int Action(const std::vector<std::string>& args) override;
@@ -347,6 +348,7 @@
std::optional<std::string> stable_id_file_path_;
std::vector<std::string> split_args_;
std::optional<std::string> trace_folder_;
+ std::vector<std::string> feature_flags_args_;
};
}// namespace aapt