Merge "Add a separated flag for Volume Panel large screen" into main
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index cc7d97a..2ff1f76 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -14204,7 +14204,8 @@
method @Deprecated public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
- method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @RequiresPermission(allOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isInSelfManagedCall(@NonNull String, @NonNull android.os.UserHandle, boolean);
+ method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @RequiresPermission(allOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isInSelfManagedCall(@NonNull String, @NonNull android.os.UserHandle);
+ method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @RequiresPermission(allOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isInSelfManagedCall(@NonNull String, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle);
field public static final String ACTION_CURRENT_TTY_MODE_CHANGED = "android.telecom.action.CURRENT_TTY_MODE_CHANGED";
diff --git a/core/java/Android.bp b/core/java/Android.bp
index eba500d..34b8a87 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -126,6 +126,7 @@
srcs: [
"android/os/IExternalVibrationController.aidl",
"android/os/IExternalVibratorService.aidl",
+ "android/os/ExternalVibrationScale.aidl",
],
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 16c7753..39900a0 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -74,6 +74,7 @@
import android.os.UserManager;
import android.permission.PermissionGroupUsage;
import android.permission.PermissionUsageHelper;
+import android.permission.flags.Flags;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -99,7 +100,6 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Parcelling;
import com.android.internal.util.Preconditions;
-import com.android.media.flags.Flags;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -2077,7 +2077,8 @@
* @hide
*/
@SystemApi
- @FlaggedApi(Flags.FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL)
+ @FlaggedApi(com.android.media.flags.Flags
+ .FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL)
public static final String OPSTR_MEDIA_ROUTING_CONTROL = "android:media_routing_control";
/**
@@ -2243,7 +2244,7 @@
*
* @hide
*/
- @FlaggedApi(android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @FlaggedApi(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
@SystemApi
public static final String OPSTR_ACCESS_RESTRICTED_SETTINGS =
"android:access_restricted_settings";
@@ -3432,7 +3433,8 @@
}
}
- public static final @android.annotation.NonNull Creator<PackageOps> CREATOR = new Creator<PackageOps>() {
+ public static final @android.annotation.NonNull Creator<PackageOps> CREATOR =
+ new Creator<PackageOps>() {
@Override public PackageOps createFromParcel(Parcel source) {
return new PackageOps(source);
}
@@ -7410,7 +7412,7 @@
* @param userId User id of the app whose Op changed.
* @param persistentDeviceId persistent device id whose Op changed.
*/
- @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+ @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
default void onOpChanged(@NonNull String op, @NonNull String packageName, int userId,
@NonNull String persistentDeviceId) {
if (Objects.equals(persistentDeviceId,
@@ -7480,7 +7482,7 @@
* @param attributionFlags the attribution flags for this operation.
* @param attributionChainId the unique id of the attribution chain this op is a part of.
*/
- @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+ @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
default void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName,
@Nullable String attributionTag, int virtualDeviceId, boolean active,
@AttributionFlags int attributionFlags, int attributionChainId) {
@@ -7534,7 +7536,7 @@
* @param flags The flags of this op
* @param result The result of the note.
*/
- @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+ @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
default void onOpNoted(@NonNull String op, int uid, @NonNull String packageName,
@Nullable String attributionTag, int virtualDeviceId, @OpFlags int flags,
@Mode int result) {
@@ -8361,14 +8363,26 @@
String attributionTag, int virtualDeviceId, boolean active,
@AttributionFlags int attributionFlags, int attributionChainId) {
executor.execute(() -> {
- if (callback instanceof OnOpActiveChangedInternalListener) {
- ((OnOpActiveChangedInternalListener) callback).onOpActiveChanged(op,
- uid, packageName, virtualDeviceId, active);
- }
- if (sAppOpInfos[op].name != null) {
- callback.onOpActiveChanged(sAppOpInfos[op].name, uid, packageName,
- attributionTag, virtualDeviceId, active, attributionFlags,
- attributionChainId);
+ if (Flags.deviceAwarePermissionApisEnabled()) {
+ if (callback instanceof OnOpActiveChangedInternalListener) {
+ ((OnOpActiveChangedInternalListener) callback).onOpActiveChanged(op,
+ uid, packageName, virtualDeviceId, active);
+ }
+ if (sAppOpInfos[op].name != null) {
+ callback.onOpActiveChanged(sAppOpInfos[op].name, uid, packageName,
+ attributionTag, virtualDeviceId, active, attributionFlags,
+ attributionChainId);
+ }
+ } else {
+ if (callback instanceof OnOpActiveChangedInternalListener) {
+ ((OnOpActiveChangedInternalListener) callback).onOpActiveChanged(op,
+ uid, packageName, active);
+ }
+ if (sAppOpInfos[op].name != null) {
+ callback.onOpActiveChanged(sAppOpInfos[op].name, uid, packageName,
+ attributionTag, active, attributionFlags,
+ attributionChainId);
+ }
}
});
}
@@ -8613,9 +8627,13 @@
try {
executor.execute(() -> {
if (sAppOpInfos[op].name != null) {
- listener.onOpNoted(sAppOpInfos[op].name, uid, packageName,
- attributionTag, virtualDeviceId,
- flags, mode);
+ if (Flags.deviceAwarePermissionApisEnabled()) {
+ listener.onOpNoted(sAppOpInfos[op].name, uid, packageName,
+ attributionTag, virtualDeviceId, flags, mode);
+ } else {
+ listener.onOpNoted(sAppOpInfos[op].name, uid, packageName,
+ attributionTag, flags, mode);
+ }
}
});
} finally {
diff --git a/core/java/android/os/ExternalVibrationScale.aidl b/core/java/android/os/ExternalVibrationScale.aidl
new file mode 100644
index 0000000..cf6f8ed
--- /dev/null
+++ b/core/java/android/os/ExternalVibrationScale.aidl
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * ExternalVibrationScale holds the vibration scale level and adaptive haptics scale. These
+ * can be used to scale external vibrations.
+ *
+ * @hide
+ */
+parcelable ExternalVibrationScale {
+ @Backing(type="int")
+ enum ScaleLevel {
+ SCALE_MUTE = -100,
+ SCALE_VERY_LOW = -2,
+ SCALE_LOW = -1,
+ SCALE_NONE = 0,
+ SCALE_HIGH = 1,
+ SCALE_VERY_HIGH = 2
+ }
+
+ /**
+ * The scale level that will be applied to external vibrations.
+ */
+ ScaleLevel scaleLevel = ScaleLevel.SCALE_NONE;
+
+ /**
+ * The adaptive haptics scale that will be applied to external vibrations.
+ */
+ float adaptiveHapticsScale = 1f;
+}
diff --git a/core/java/android/os/IExternalVibratorService.aidl b/core/java/android/os/IExternalVibratorService.aidl
index 666171f..a9df15a 100644
--- a/core/java/android/os/IExternalVibratorService.aidl
+++ b/core/java/android/os/IExternalVibratorService.aidl
@@ -17,6 +17,7 @@
package android.os;
import android.os.ExternalVibration;
+import android.os.ExternalVibrationScale;
/**
* The communication channel by which an external system that wants to control the system
@@ -32,29 +33,24 @@
* {@hide}
*/
interface IExternalVibratorService {
- const int SCALE_MUTE = -100;
- const int SCALE_VERY_LOW = -2;
- const int SCALE_LOW = -1;
- const int SCALE_NONE = 0;
- const int SCALE_HIGH = 1;
- const int SCALE_VERY_HIGH = 2;
-
/**
* A method called by the external system to start a vibration.
*
- * If this returns {@code SCALE_MUTE}, then the vibration should <em>not</em> play. If this
- * returns any other scale level, then any currently playing vibration controlled by the
- * requesting system must be muted and this vibration can begin playback.
+ * This returns an {@link ExternalVibrationScale} which includes the vibration scale level and
+ * the adaptive haptics scale.
+ *
+ * If the returned scale level is {@link ExternalVibrationScale.ScaleLevel#SCALE_MUTE}, then
+ * the vibration should <em>not</em> play. If it returns any other scale level, then
+ * any currently playing vibration controlled by the requesting system must be muted and this
+ * vibration can begin playback.
*
* Note that the IExternalVibratorService implementation will not call mute on any currently
* playing external vibrations in order to avoid re-entrancy with the system on the other side.
*
- * @param vibration An ExternalVibration
- *
- * @return {@code SCALE_MUTE} if the external vibration should not play, and any other scale
- * level if it should.
+ * @param vib The external vibration starting.
+ * @return {@link ExternalVibrationScale} including scale level and adaptive haptics scale.
*/
- int onExternalVibrationStart(in ExternalVibration vib);
+ ExternalVibrationScale onExternalVibrationStart(in ExternalVibration vib);
/**
* A method called by the external system when a vibration no longer wants to play.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ec4d587..50adc40 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12481,6 +12481,24 @@
public static void setLocationProviderEnabled(ContentResolver cr,
String provider, boolean enabled) {
}
+
+ /**
+ * List of system components that support restore in a V-> U OS downgrade but do not have
+ * RestoreAnyVersion set to true. Value set before system restore.
+ * This setting is not B&Rd
+ * List is stored as a comma-separated string of package names e.g. "a,b,c"
+ * @hide
+ */
+ public static final String V_TO_U_RESTORE_ALLOWLIST = "v_to_u_restore_allowlist";
+
+ /**
+ * List of system components that have RestoreAnyVersion set to true but do not support
+ * restore in a V-> U OS downgrade. Value set before system restore.
+ * This setting is not B&Rd
+ * List is stored as a comma-separated string of package names e.g. "a,b,c"
+ * @hide
+ */
+ public static final String V_TO_U_RESTORE_DENYLIST = "v_to_u_restore_denylist";
}
/**
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 021bbf7..896b3f4 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -196,8 +196,6 @@
private Canvas mCanvas;
private int mSaveCount;
- @Surface.FrameRateCompatibility int mFrameRateCompatibility;
-
private final Object[] mNativeWindowLock = new Object[0];
// Set by native code, do not write!
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 06dc275..596c52d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -24,6 +24,7 @@
import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
+import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
@@ -2368,6 +2369,9 @@
private static boolean sToolkitSetFrameRateReadOnlyFlagValue;
private static boolean sToolkitMetricsForFrameRateDecisionFlagValue;
+ // Used to set frame rate compatibility.
+ @Surface.FrameRateCompatibility int mFrameRateCompatibility =
+ FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
static {
EMPTY_STATE_SET = StateSet.get(0);
@@ -10889,8 +10893,11 @@
structure.setAutofillId(new AutofillId(getAutofillId(),
AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId())));
}
- structure.setCredentialManagerRequest(getCredentialManagerRequest(),
- getCredentialManagerCallback());
+ if (getViewCredentialHandler() != null) {
+ structure.setCredentialManagerRequest(
+ getViewCredentialHandler().getRequest(),
+ getViewCredentialHandler().getCallback());
+ }
CharSequence cname = info.getClassName();
structure.setClassName(cname != null ? cname.toString() : null);
structure.setContentDescription(info.getContentDescription());
@@ -33538,7 +33545,8 @@
frameRateCateogry = FRAME_RATE_CATEGORY_HIGH;
}
} else {
- viewRootImpl.votePreferredFrameRate(mPreferredFrameRate);
+ viewRootImpl.votePreferredFrameRate(mPreferredFrameRate,
+ mFrameRateCompatibility);
return;
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1e79786..94260b2 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -30,6 +30,7 @@
import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
+import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
import static android.view.View.PFLAG_DRAW_ANIMATION;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
@@ -1027,6 +1028,13 @@
// as needed and can be useful for power saving.
// Should not enable the dVRR feature if the value is false.
private boolean mIsFrameRatePowerSavingsBalanced = true;
+ // Used to check if there is a conflict between different frame rate voting.
+ // Take 24 and 30 as an example, 24 is not a divisor of 30.
+ // We consider there is a conflict.
+ private boolean mIsFrameRateConflicted = false;
+ // Used to set frame rate compatibility.
+ @Surface.FrameRateCompatibility int mFrameRateCompatibility =
+ FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
// time for touch boost period.
private static final int FRAME_RATE_TOUCH_BOOST_TIME = 3000;
// time for checking idle status periodically.
@@ -4110,6 +4118,7 @@
? mFrameRateCategoryLowCount - 1 : mFrameRateCategoryLowCount;
mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
mPreferredFrameRate = -1;
+ mIsFrameRateConflicted = false;
}
private void createSyncIfNeeded() {
@@ -6508,6 +6517,7 @@
break;
case MSG_FRAME_RATE_SETTING:
mPreferredFrameRate = 0;
+ mFrameRateCompatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
setPreferredFrameRate(mPreferredFrameRate);
break;
}
@@ -12286,9 +12296,15 @@
if (!shouldSetFrameRateCategory()) {
return;
}
+ int categoryFromConflictedFrameRates = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+ if (mIsFrameRateConflicted) {
+ categoryFromConflictedFrameRates = mPreferredFrameRate > 60
+ ? FRAME_RATE_CATEGORY_HIGH : FRAME_RATE_CATEGORY_NORMAL;
+ }
int frameRateCategory = mIsTouchBoosting
- ? FRAME_RATE_CATEGORY_HIGH_HINT : preferredFrameRateCategory;
+ ? FRAME_RATE_CATEGORY_HIGH_HINT
+ : Math.max(preferredFrameRateCategory, categoryFromConflictedFrameRates);
// FRAME_RATE_CATEGORY_HIGH has a higher precedence than FRAME_RATE_CATEGORY_HIGH_HINT
// For now, FRAME_RATE_CATEGORY_HIGH_HINT is used for boosting with user interaction.
@@ -12338,7 +12354,7 @@
+ preferredFrameRate);
}
mFrameRateTransaction.setFrameRate(mSurfaceControl, preferredFrameRate,
- Surface.FRAME_RATE_COMPATIBILITY_DEFAULT).applyAsyncUnsafe();
+ mFrameRateCompatibility).applyAsyncUnsafe();
mLastPreferredFrameRate = preferredFrameRate;
}
} catch (Exception e) {
@@ -12361,7 +12377,8 @@
private boolean shouldSetFrameRate() {
// use toolkitSetFrameRate flag to gate the change
- return mSurface.isValid() && mPreferredFrameRate > 0 && shouldEnableDvrr();
+ return mSurface.isValid() && mPreferredFrameRate > 0
+ && shouldEnableDvrr() && !mIsFrameRateConflicted;
}
private boolean shouldTouchBoost(int motionEventAction, int windowType) {
@@ -12404,29 +12421,45 @@
}
/**
- * Allow Views to vote for the preferred frame rate
+ * Allow Views to vote for the preferred frame rate and compatibility.
* When determining the preferred frame rate value,
* we follow this logic: If no preferred frame rate has been set yet,
* we assign the value of frameRate as the preferred frame rate.
- * If either the current or the new preferred frame rate exceeds 60 Hz,
- * we select the higher value between them.
- * However, if both values are 60 Hz or lower, we set the preferred frame rate
- * to 60 Hz to maintain optimal performance.
+ * IF there are multiple frame rates are voted:
+ * 1. There is a frame rate is a multiple of all other frame rates.
+ * We choose this frmae rate to be the one to be set.
+ * 2. There is no frame rate can be a multiple of others
+ * We set category to HIGH if the maximum frame rate is greater than 60.
+ * Otherwise, we set category to NORMAL.
+ *
+ * Use FRAME_RATE_COMPATIBILITY_GTE for velocity and FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
+ * for TextureView video play and user requested frame rate.
*
* @param frameRate the preferred frame rate of a View
+ * @param frameRateCompatibility the preferred frame rate compatibility of a View
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
- public void votePreferredFrameRate(float frameRate) {
+ public void votePreferredFrameRate(float frameRate, int frameRateCompatibility) {
if (frameRate <= 0) {
return;
}
+ if (mPreferredFrameRate > 0 && mPreferredFrameRate % frameRate != 0
+ && frameRate % mPreferredFrameRate != 0) {
+ mIsFrameRateConflicted = true;
+ mFrameRateCompatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+ }
+ if (frameRate > mPreferredFrameRate) {
+ mFrameRateCompatibility = frameRateCompatibility;
+ }
mPreferredFrameRate = Math.max(mPreferredFrameRate, frameRate);
-
mHasInvalidation = true;
- mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
- mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING,
- FRAME_RATE_SETTING_REEVALUATE_TIME);
+
+ if (!mIsFrameRateConflicted) {
+ mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
+ mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING,
+ FRAME_RATE_SETTING_REEVALUATE_TIME);
+ }
}
/**
@@ -12454,6 +12487,14 @@
}
/**
+ * Get the value of mFrameRateCompatibility
+ */
+ @VisibleForTesting
+ public int getFrameRateCompatibility() {
+ return mFrameRateCompatibility;
+ }
+
+ /**
* Get the value of mIsFrameRateBoosting
*/
@VisibleForTesting
@@ -12503,6 +12544,15 @@
}
/**
+ * Get the value of mIsFrameRateConflicted
+ * Can be used to checked if there is a conflict of frame rate votes.
+ */
+ @VisibleForTesting
+ public boolean isFrameRateConflicted() {
+ return mIsFrameRateConflicted;
+ }
+
+ /**
* Set the value of mIsFrameRatePowerSavingsBalanced
* Can be used to checked if toolkit dVRR feature is enabled.
*/
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index 55986e7..8d3920f 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -78,3 +78,11 @@
bug: "300979854"
is_fixed_read_only: true
}
+
+flag {
+ name: "predictive_back_ime"
+ namespace: "input_method"
+ description: "Predictive back animation for IMEs"
+ bug: "322836622"
+ is_fixed_read_only: true
+}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 038c00e..64cbe7f 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -24,6 +24,8 @@
import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
+import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE;
import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
@@ -705,17 +707,51 @@
public void votePreferredFrameRate_voteFrameRate_aggregate() {
View view = new View(sContext);
attachViewToWindow(view);
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
sInstrumentation.runOnMainSync(() -> {
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
- viewRootImpl.votePreferredFrameRate(24);
+ assertEquals(viewRootImpl.getFrameRateCompatibility(),
+ FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+ assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+ viewRootImpl.votePreferredFrameRate(24, FRAME_RATE_COMPATIBILITY_GTE);
assertEquals(viewRootImpl.getPreferredFrameRate(), 24, 0.1);
- viewRootImpl.votePreferredFrameRate(30);
+ assertEquals(viewRootImpl.getFrameRateCompatibility(),
+ FRAME_RATE_COMPATIBILITY_GTE);
+ assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+ viewRootImpl.votePreferredFrameRate(30, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
assertEquals(viewRootImpl.getPreferredFrameRate(), 30, 0.1);
- viewRootImpl.votePreferredFrameRate(60);
+ // If there is a conflict, then set compatibility to
+ // FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
+ assertEquals(viewRootImpl.getFrameRateCompatibility(),
+ FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+ // Should be true since there is a conflict between 24 and 30.
+ assertEquals(viewRootImpl.isFrameRateConflicted(), true);
+ view.invalidate();
+ });
+ sInstrumentation.waitForIdleSync();
+
+ sInstrumentation.runOnMainSync(() -> {
+ assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+ viewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_GTE);
assertEquals(viewRootImpl.getPreferredFrameRate(), 60, 0.1);
- viewRootImpl.votePreferredFrameRate(120);
+ assertEquals(viewRootImpl.getFrameRateCompatibility(),
+ FRAME_RATE_COMPATIBILITY_GTE);
+ assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+ viewRootImpl.votePreferredFrameRate(120, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
assertEquals(viewRootImpl.getPreferredFrameRate(), 120, 0.1);
+ assertEquals(viewRootImpl.getFrameRateCompatibility(),
+ FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+ // Should be false since 60 is a divisor of 120.
+ assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+ viewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_GTE);
+ assertEquals(viewRootImpl.getPreferredFrameRate(), 120, 0.1);
+ // compatibility should be remained the same (FRAME_RATE_COMPATIBILITY_FIXED_SOURCE)
+ // since the frame rate 60 is smaller than 120.
+ assertEquals(viewRootImpl.getFrameRateCompatibility(),
+ FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+ // Should be false since 60 is a divisor of 120.
+ assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+
});
}
@@ -842,14 +878,26 @@
sInstrumentation.runOnMainSync(() -> {
assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
- viewRootImpl.votePreferredFrameRate(24);
+ assertEquals(viewRootImpl.getFrameRateCompatibility(),
+ FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+ assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+ viewRootImpl.votePreferredFrameRate(24, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
assertEquals(viewRootImpl.getPreferredFrameRate(), 24, 0.1);
+ assertEquals(viewRootImpl.getFrameRateCompatibility(),
+ FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+ assertEquals(viewRootImpl.isFrameRateConflicted(), false);
view.invalidate();
assertEquals(viewRootImpl.getPreferredFrameRate(), 24, 0.1);
+ assertEquals(viewRootImpl.getFrameRateCompatibility(),
+ FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+ assertEquals(viewRootImpl.isFrameRateConflicted(), false);
});
Thread.sleep(delay);
assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
+ assertEquals(viewRootImpl.getFrameRateCompatibility(),
+ FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+ assertEquals(viewRootImpl.isFrameRateConflicted(), false);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index e0f0556..369258e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -683,6 +683,17 @@
mDataRepository.removeBubblesForUser(removedUserId, parentUserId);
}
+ /** Called when sensitive notification state has changed */
+ public void onSensitiveNotificationProtectionStateChanged(
+ boolean sensitiveNotificationProtectionActive) {
+ if (mStackView != null) {
+ mStackView.onSensitiveNotificationProtectionStateChanged(
+ sensitiveNotificationProtectionActive);
+ ProtoLog.d(WM_SHELL_BUBBLES, "onSensitiveNotificationProtectionStateChanged=%b",
+ sensitiveNotificationProtectionActive);
+ }
+ }
+
/** Whether bubbles are showing in the bubble bar. */
public boolean isShowingAsBubbleBar() {
return canShowAsBubbleBar() && mBubbleStateListener != null;
@@ -2583,6 +2594,14 @@
mMainExecutor.execute(
() -> BubbleController.this.onNotificationPanelExpandedChanged(expanded));
}
+
+ @Override
+ public void onSensitiveNotificationProtectionStateChanged(
+ boolean sensitiveNotificationProtectionActive) {
+ mMainExecutor.execute(
+ () -> BubbleController.this.onSensitiveNotificationProtectionStateChanged(
+ sensitiveNotificationProtectionActive));
+ }
}
/**
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 b23fd52..e7da034 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
@@ -291,6 +291,11 @@
*/
private boolean mRemovingLastBubbleWhileExpanded = false;
+ /**
+ * Whether sensitive notification protection should disable flyout
+ */
+ private boolean mSensitiveNotificationProtectionActive = false;
+
/** Animator for animating the expanded view's alpha (including the TaskView inside it). */
private final ValueAnimator mExpandedViewAlphaAnimator = ValueAnimator.ofFloat(0f, 1f);
@@ -2199,6 +2204,11 @@
}
}
+ void onSensitiveNotificationProtectionStateChanged(
+ boolean sensitiveNotificationProtectionActive) {
+ mSensitiveNotificationProtectionActive = sensitiveNotificationProtectionActive;
+ }
+
/**
* Asks the BubbleController to hide the IME from anywhere, whether it's focused on Bubbles or
* not.
@@ -2842,6 +2852,7 @@
|| isExpanded()
|| mIsExpansionAnimating
|| mIsGestureInProgress
+ || mSensitiveNotificationProtectionActive
|| mBubbleToExpandAfterFlyoutCollapse != null
|| bubbleView == null) {
if (bubbleView != null && mFlyout.getVisibility() != VISIBLE) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 28af0ca..26077cf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -286,6 +286,16 @@
void onUserRemoved(int removedUserId);
/**
+ * Called when the Sensitive notification protection state has changed, such as when media
+ * projection starts and stops.
+ *
+ * @param sensitiveNotificationProtectionActive {@code true} if notifications should be
+ * protected
+ */
+ void onSensitiveNotificationProtectionStateChanged(
+ boolean sensitiveNotificationProtectionActive);
+
+ /**
* A listener to be notified of bubble state changes, used by launcher to render bubbles in
* its process.
*/
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index e4f3e2d..abd84de 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -155,6 +155,7 @@
host: {
static_libs: [
"libandroidfw",
+ "libhostgraphics",
"libutils",
],
},
@@ -501,6 +502,17 @@
],
header_libs: ["android_graphics_jni_headers"],
export_header_lib_headers: ["android_graphics_jni_headers"],
+ target: {
+ android: {
+ export_include_dirs: ["platform/android"],
+ },
+ host: {
+ export_include_dirs: ["platform/host"],
+ },
+ windows: {
+ enabled: true,
+ },
+ },
}
cc_defaults {
@@ -538,6 +550,7 @@
"utils/Blur.cpp",
"utils/Color.cpp",
"utils/LinearAllocator.cpp",
+ "utils/StringUtils.cpp",
"utils/TypefaceUtils.cpp",
"utils/VectorDrawableUtils.cpp",
"AnimationContext.cpp",
@@ -552,6 +565,7 @@
"Mesh.cpp",
"MemoryPolicy.cpp",
"PathParser.cpp",
+ "ProfileData.cpp",
"Properties.cpp",
"PropertyValuesAnimatorSet.cpp",
"PropertyValuesHolder.cpp",
@@ -569,12 +583,13 @@
export_proto_headers: true,
},
+ header_libs: ["libandroid_headers_private"],
+
target: {
android: {
- header_libs: [
- "libandroid_headers_private",
- "libtonemap_headers",
- ],
+ header_libs: ["libtonemap_headers"],
+
+ local_include_dirs: ["platform/android"],
srcs: [
"hwui/AnimatedImageThread.cpp",
@@ -605,7 +620,6 @@
"thread/CommonPool.cpp",
"utils/GLUtils.cpp",
"utils/NdkUtils.cpp",
- "utils/StringUtils.cpp",
"AutoBackendTextureRelease.cpp",
"DeferredLayerUpdater.cpp",
"DeviceInfo.cpp",
@@ -617,7 +631,6 @@
"FrameMetricsReporter.cpp",
"Layer.cpp",
"LayerUpdateQueue.cpp",
- "ProfileData.cpp",
"ProfileDataContainer.cpp",
"Readback.cpp",
"TreeInfo.cpp",
@@ -628,6 +641,21 @@
// Allow implicit fallthroughs in HardwareBitmapUploader.cpp until they are fixed.
cflags: ["-Wno-implicit-fallthrough"],
},
+ host: {
+ header_libs: ["libnativebase_headers"],
+
+ local_include_dirs: ["platform/host"],
+
+ srcs: [
+ "platform/host/renderthread/CacheManager.cpp",
+ "platform/host/renderthread/RenderThread.cpp",
+ "platform/host/ProfileDataContainer.cpp",
+ "platform/host/Readback.cpp",
+ "platform/host/WebViewFunctorManager.cpp",
+ ],
+
+ cflags: ["-Wno-unused-private-field"],
+ },
},
}
@@ -663,6 +691,7 @@
header_libs: ["libandroid_headers_private"],
target: {
android: {
+ local_include_dirs: ["platform/android"],
shared_libs: [
"libgui",
"libui",
diff --git a/libs/hwui/ProfileData.cpp b/libs/hwui/ProfileData.cpp
index 3d0ca0a..7be9541 100644
--- a/libs/hwui/ProfileData.cpp
+++ b/libs/hwui/ProfileData.cpp
@@ -110,6 +110,7 @@
}
void ProfileData::dump(int fd) const {
+#ifdef __ANDROID__
dprintf(fd, "\nStats since: %" PRIu64 "ns", mStatStartTime);
dprintf(fd, "\nTotal frames rendered: %u", mTotalFrameCount);
dprintf(fd, "\nJanky frames: %u (%.2f%%)", mJankFrameCount,
@@ -138,6 +139,7 @@
dprintf(fd, " %ums=%u", entry.renderTimeMs, entry.frameCount);
});
dprintf(fd, "\n");
+#endif
}
uint32_t ProfileData::findPercentile(int percentile) const {
diff --git a/libs/hwui/thread/ThreadBase.h b/libs/hwui/platform/android/thread/ThreadBase.h
similarity index 98%
rename from libs/hwui/thread/ThreadBase.h
rename to libs/hwui/platform/android/thread/ThreadBase.h
index 0289d3f..2f3581f 100644
--- a/libs/hwui/thread/ThreadBase.h
+++ b/libs/hwui/platform/android/thread/ThreadBase.h
@@ -17,14 +17,14 @@
#ifndef HWUI_THREADBASE_H
#define HWUI_THREADBASE_H
-#include "WorkQueue.h"
-#include "utils/Macros.h"
-
#include <utils/Looper.h>
#include <utils/Thread.h>
#include <algorithm>
+#include "thread/WorkQueue.h"
+#include "utils/Macros.h"
+
namespace android::uirenderer {
class ThreadBase : public Thread {
diff --git a/libs/hwui/platform/host/ProfileDataContainer.cpp b/libs/hwui/platform/host/ProfileDataContainer.cpp
new file mode 100644
index 0000000..9ed1b02
--- /dev/null
+++ b/libs/hwui/platform/host/ProfileDataContainer.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ProfileDataContainer.h"
+
+#include <log/log.h>
+
+namespace android {
+namespace uirenderer {
+
+void ProfileDataContainer::freeData() REQUIRES(mJankDataMutex) {
+ delete mData;
+ mIsMapped = false;
+ mData = nullptr;
+}
+
+void ProfileDataContainer::rotateStorage() {
+ std::lock_guard lock(mJankDataMutex);
+ mData->reset();
+}
+
+void ProfileDataContainer::switchStorageToAshmem(int ashmemfd) {
+ ALOGE("Ashmem is not supported for non-Android configurations");
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/platform/host/Readback.cpp b/libs/hwui/platform/host/Readback.cpp
new file mode 100644
index 0000000..b024ec0
--- /dev/null
+++ b/libs/hwui/platform/host/Readback.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Readback.h"
+
+using namespace android::uirenderer::renderthread;
+
+namespace android {
+namespace uirenderer {
+
+void Readback::copySurfaceInto(ANativeWindow* window, const std::shared_ptr<CopyRequest>& request) {
+}
+
+CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
+ return CopyResult::UnknownError;
+}
+
+CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
+ return CopyResult::UnknownError;
+}
+
+CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, SkBitmap* bitmap) {
+ return CopyResult::UnknownError;
+}
+
+CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, const Rect& srcRect,
+ SkBitmap* bitmap) {
+ return CopyResult::UnknownError;
+}
+
+bool Readback::copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
+ SkBitmap* bitmap) {
+ return false;
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/platform/host/WebViewFunctorManager.cpp b/libs/hwui/platform/host/WebViewFunctorManager.cpp
new file mode 100644
index 0000000..1d16655
--- /dev/null
+++ b/libs/hwui/platform/host/WebViewFunctorManager.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "WebViewFunctorManager.h"
+
+namespace android::uirenderer {
+
+WebViewFunctor::WebViewFunctor(void* data, const WebViewFunctorCallbacks& callbacks,
+ RenderMode functorMode)
+ : mData(data) {}
+
+WebViewFunctor::~WebViewFunctor() {}
+
+void WebViewFunctor::sync(const WebViewSyncData& syncData) const {}
+
+void WebViewFunctor::onRemovedFromTree() {}
+
+bool WebViewFunctor::prepareRootSurfaceControl() {
+ return true;
+}
+
+void WebViewFunctor::drawGl(const DrawGlInfo& drawInfo) {}
+
+void WebViewFunctor::initVk(const VkFunctorInitParams& params) {}
+
+void WebViewFunctor::drawVk(const VkFunctorDrawParams& params) {}
+
+void WebViewFunctor::postDrawVk() {}
+
+void WebViewFunctor::destroyContext() {}
+
+void WebViewFunctor::removeOverlays() {}
+
+ASurfaceControl* WebViewFunctor::getSurfaceControl() {
+ return mSurfaceControl;
+}
+
+void WebViewFunctor::mergeTransaction(ASurfaceTransaction* transaction) {}
+
+void WebViewFunctor::reparentSurfaceControl(ASurfaceControl* parent) {}
+
+WebViewFunctorManager& WebViewFunctorManager::instance() {
+ static WebViewFunctorManager sInstance;
+ return sInstance;
+}
+
+int WebViewFunctorManager::createFunctor(void* data, const WebViewFunctorCallbacks& callbacks,
+ RenderMode functorMode) {
+ return 0;
+}
+
+void WebViewFunctorManager::releaseFunctor(int functor) {}
+
+void WebViewFunctorManager::onContextDestroyed() {}
+
+void WebViewFunctorManager::destroyFunctor(int functor) {}
+
+sp<WebViewFunctor::Handle> WebViewFunctorManager::handleFor(int functor) {
+ return nullptr;
+}
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/platform/host/renderthread/CacheManager.cpp b/libs/hwui/platform/host/renderthread/CacheManager.cpp
new file mode 100644
index 0000000..b03f400
--- /dev/null
+++ b/libs/hwui/platform/host/renderthread/CacheManager.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "renderthread/CacheManager.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+CacheManager::CacheManager(RenderThread& thread)
+ : mRenderThread(thread), mMemoryPolicy(loadMemoryPolicy()) {}
+
+void CacheManager::setupCacheLimits() {}
+
+void CacheManager::destroy() {}
+
+void CacheManager::trimMemory(TrimLevel mode) {}
+
+void CacheManager::trimCaches(CacheTrimLevel mode) {}
+
+void CacheManager::trimStaleResources() {}
+
+void CacheManager::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {}
+
+void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) {}
+
+void CacheManager::onFrameCompleted() {}
+
+void CacheManager::onThreadIdle() {}
+
+void CacheManager::scheduleDestroyContext() {}
+
+void CacheManager::cancelDestroyContext() {}
+
+bool CacheManager::areAllContextsStopped() {
+ return false;
+}
+
+void CacheManager::checkUiHidden() {}
+
+void CacheManager::registerCanvasContext(CanvasContext* context) {}
+
+void CacheManager::unregisterCanvasContext(CanvasContext* context) {}
+
+void CacheManager::onContextStopped(CanvasContext* context) {}
+
+void CacheManager::notifyNextFrameSize(int width, int height) {}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/platform/host/renderthread/RenderThread.cpp b/libs/hwui/platform/host/renderthread/RenderThread.cpp
new file mode 100644
index 0000000..6f08b59
--- /dev/null
+++ b/libs/hwui/platform/host/renderthread/RenderThread.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "renderthread/RenderThread.h"
+
+#include "Readback.h"
+#include "renderthread/VulkanManager.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+static bool gHasRenderThreadInstance = false;
+static JVMAttachHook gOnStartHook = nullptr;
+
+ASurfaceControlFunctions::ASurfaceControlFunctions() {}
+
+bool RenderThread::hasInstance() {
+ return gHasRenderThreadInstance;
+}
+
+void RenderThread::setOnStartHook(JVMAttachHook onStartHook) {
+ LOG_ALWAYS_FATAL_IF(hasInstance(), "can't set an onStartHook after we've started...");
+ gOnStartHook = onStartHook;
+}
+
+JVMAttachHook RenderThread::getOnStartHook() {
+ return gOnStartHook;
+}
+
+RenderThread& RenderThread::getInstance() {
+ [[clang::no_destroy]] static sp<RenderThread> sInstance = []() {
+ sp<RenderThread> thread = sp<RenderThread>::make();
+ thread->start("RenderThread");
+ return thread;
+ }();
+ gHasRenderThreadInstance = true;
+ return *sInstance;
+}
+
+RenderThread::RenderThread()
+ : ThreadBase()
+ , mVsyncSource(nullptr)
+ , mVsyncRequested(false)
+ , mFrameCallbackTaskPending(false)
+ , mRenderState(nullptr)
+ , mEglManager(nullptr)
+ , mFunctorManager(WebViewFunctorManager::instance())
+ , mGlobalProfileData(mJankDataMutex) {
+ Properties::load();
+}
+
+RenderThread::~RenderThread() {}
+
+void RenderThread::initThreadLocals() {
+ mCacheManager = new CacheManager(*this);
+}
+
+void RenderThread::requireGlContext() {}
+
+void RenderThread::requireVkContext() {}
+
+void RenderThread::initGrContextOptions(GrContextOptions& options) {}
+
+void RenderThread::destroyRenderingContext() {}
+
+VulkanManager& RenderThread::vulkanManager() {
+ return *mVkManager;
+}
+
+void RenderThread::dumpGraphicsMemory(int fd, bool includeProfileData) {}
+
+void RenderThread::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {}
+
+Readback& RenderThread::readback() {
+ if (!mReadback) {
+ mReadback = new Readback(*this);
+ }
+
+ return *mReadback;
+}
+
+void RenderThread::setGrContext(sk_sp<GrDirectContext> context) {}
+
+sk_sp<GrDirectContext> RenderThread::requireGrContext() {
+ return mGrContext;
+}
+
+bool RenderThread::threadLoop() {
+ if (gOnStartHook) {
+ gOnStartHook("RenderThread");
+ }
+ initThreadLocals();
+
+ while (true) {
+ waitForWork();
+ processQueue();
+ mCacheManager->onThreadIdle();
+ }
+
+ return false;
+}
+
+void RenderThread::postFrameCallback(IFrameCallback* callback) {}
+
+bool RenderThread::removeFrameCallback(IFrameCallback* callback) {
+ return false;
+}
+
+void RenderThread::pushBackFrameCallback(IFrameCallback* callback) {}
+
+sk_sp<Bitmap> RenderThread::allocateHardwareBitmap(SkBitmap& skBitmap) {
+ return nullptr;
+}
+
+bool RenderThread::isCurrent() {
+ return true;
+}
+
+void RenderThread::preload() {}
+
+void RenderThread::trimMemory(TrimLevel level) {}
+
+void RenderThread::trimCaches(CacheTrimLevel level) {}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/thread/ThreadBase.h b/libs/hwui/platform/host/thread/ThreadBase.h
similarity index 65%
copy from libs/hwui/thread/ThreadBase.h
copy to libs/hwui/platform/host/thread/ThreadBase.h
index 0289d3f..d709430 100644
--- a/libs/hwui/thread/ThreadBase.h
+++ b/libs/hwui/platform/host/thread/ThreadBase.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,31 +17,24 @@
#ifndef HWUI_THREADBASE_H
#define HWUI_THREADBASE_H
-#include "WorkQueue.h"
-#include "utils/Macros.h"
-
-#include <utils/Looper.h>
#include <utils/Thread.h>
#include <algorithm>
+#include "thread/WorkQueue.h"
+#include "utils/Macros.h"
+
namespace android::uirenderer {
class ThreadBase : public Thread {
PREVENT_COPY_AND_ASSIGN(ThreadBase);
public:
- ThreadBase()
- : Thread(false)
- , mLooper(new Looper(false))
- , mQueue([this]() { mLooper->wake(); }, mLock) {}
+ ThreadBase() : Thread(false), mQueue([this]() { mCondition.notify_all(); }, mLock) {}
WorkQueue& queue() { return mQueue; }
- void requestExit() {
- Thread::requestExit();
- mLooper->wake();
- }
+ void requestExit() { Thread::requestExit(); }
void start(const char* name = "ThreadBase") { Thread::run(name); }
@@ -51,37 +44,31 @@
protected:
void waitForWork() {
- nsecs_t nextWakeup;
- {
- std::unique_lock lock{mLock};
- nextWakeup = mQueue.nextWakeup(lock);
- }
- int timeout = -1;
+ std::unique_lock lock{mLock};
+ nsecs_t nextWakeup = mQueue.nextWakeup(lock);
+ std::chrono::nanoseconds duration = std::chrono::nanoseconds::max();
if (nextWakeup < std::numeric_limits<nsecs_t>::max()) {
- timeout = ns2ms(nextWakeup - WorkQueue::clock::now());
+ int timeout = nextWakeup - WorkQueue::clock::now();
if (timeout < 0) timeout = 0;
+ duration = std::chrono::nanoseconds(timeout);
}
- int result = mLooper->pollOnce(timeout);
- LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR, "RenderThread Looper POLL_ERROR!");
+ mCondition.wait_for(lock, duration);
}
void processQueue() { mQueue.process(); }
virtual bool threadLoop() override {
- Looper::setForThread(mLooper);
while (!exitPending()) {
waitForWork();
processQueue();
}
- Looper::setForThread(nullptr);
return false;
}
- sp<Looper> mLooper;
-
private:
WorkQueue mQueue;
std::mutex mLock;
+ std::condition_variable mCondition;
};
} // namespace android::uirenderer
diff --git a/libs/hwui/private/hwui/WebViewFunctor.h b/libs/hwui/private/hwui/WebViewFunctor.h
index 22ae59e..493c943 100644
--- a/libs/hwui/private/hwui/WebViewFunctor.h
+++ b/libs/hwui/private/hwui/WebViewFunctor.h
@@ -17,15 +17,7 @@
#ifndef FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
#define FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
-#ifdef __ANDROID__ // Layoutlib does not support surface control
#include <android/surface_control.h>
-#else
-// To avoid ifdefs around overlay implementation all over the place we typedef these to void *. They
-// won't be used.
-typedef void* ASurfaceControl;
-typedef void* ASurfaceTransaction;
-#endif
-
#include <cutils/compiler.h>
#include <private/hwui/DrawGlInfo.h>
#include <private/hwui/DrawVkInfo.h>
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 623ee4e..a024aeb 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -17,11 +17,12 @@
#include "RenderThread.h"
#include <GrContextOptions.h>
-#include <include/gpu/ganesh/gl/GrGLDirectContext.h>
#include <android-base/properties.h>
#include <dlfcn.h>
#include <gl/GrGLInterface.h>
#include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/gl/GrGLDirectContext.h>
+#include <private/android/choreographer.h>
#include <sys/resource.h>
#include <ui/FatVector.h>
#include <utils/Condition.h>
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 79e57de..045d26f 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -20,10 +20,7 @@
#include <GrDirectContext.h>
#include <SkBitmap.h>
#include <cutils/compiler.h>
-#include <private/android/choreographer.h>
#include <surface_control_private.h>
-#include <thread/ThreadBase.h>
-#include <utils/Looper.h>
#include <utils/Thread.h>
#include <memory>
diff --git a/packages/SettingsLib/res/drawable/ic_bt_le_audio_sharing.xml b/packages/SettingsLib/res/drawable/ic_bt_le_audio_sharing.xml
new file mode 100644
index 0000000..6186773
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_bt_le_audio_sharing.xml
@@ -0,0 +1,87 @@
+<!--
+ ~ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#000000"
+ android:pathData="M16.984,24H7.279L12.131,15.508L16.984,24ZM10.481,22.144H13.781L12.131,19.257L10.481,22.144Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M12.131,14.295C13.471,14.295 14.558,13.209 14.558,11.869C14.558,10.529 13.471,9.442 12.131,9.442C10.791,9.442 9.705,10.529 9.705,11.869C9.705,13.209 10.791,14.295 12.131,14.295Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M4.573,21.368C4.052,20.943 3.967,20.179 4.379,19.657C4.804,19.136 5.568,19.051 6.09,19.463C6.611,19.876 6.696,20.64 6.284,21.174C6.041,21.465 5.689,21.623 5.338,21.623C5.071,21.623 4.804,21.538 4.573,21.368Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M17.991,21.162C17.579,20.628 17.663,19.876 18.185,19.451C18.707,19.039 19.471,19.124 19.896,19.646C20.308,20.167 20.223,20.931 19.702,21.344C19.471,21.526 19.204,21.611 18.949,21.611C18.586,21.611 18.234,21.453 17.991,21.162Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M1.213,17.145C0.91,16.551 1.165,15.823 1.771,15.532C2.378,15.241 3.093,15.495 3.397,16.09C3.688,16.697 3.433,17.424 2.827,17.715C2.657,17.8 2.475,17.837 2.305,17.837C1.844,17.837 1.419,17.582 1.213,17.145Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M21.449,17.691C20.842,17.4 20.588,16.684 20.879,16.077C21.17,15.471 21.898,15.216 22.504,15.507C23.099,15.798 23.354,16.526 23.062,17.133C22.856,17.557 22.419,17.812 21.971,17.812C21.789,17.812 21.619,17.776 21.449,17.691Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M0,11.892C0,11.225 0.546,10.679 1.213,10.679C1.88,10.679 2.426,11.212 2.426,11.892C2.426,12.559 1.88,13.105 1.213,13.105C0.546,13.105 0,12.559 0,11.892Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M21.837,11.869C21.837,11.857 21.837,11.845 21.837,11.833C21.824,11.153 22.37,10.62 23.05,10.607C23.717,10.607 24.251,11.153 24.263,11.821C24.263,11.833 24.263,11.845 24.263,11.845C24.263,11.857 24.263,11.869 24.263,11.869C24.263,12.536 23.717,13.082 23.05,13.082C22.382,13.082 21.837,12.536 21.837,11.869Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M1.759,8.242C1.152,7.963 0.898,7.235 1.189,6.628C1.48,6.022 2.196,5.767 2.802,6.058C3.409,6.349 3.664,7.077 3.372,7.684C3.166,8.108 2.729,8.363 2.281,8.363C2.099,8.363 1.929,8.327 1.759,8.242Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M20.866,7.622C20.563,7.028 20.818,6.3 21.424,6.009C22.019,5.706 22.747,5.96 23.038,6.567C23.038,6.567 23.038,6.567 23.05,6.567C23.341,7.161 23.087,7.889 22.48,8.181C22.31,8.265 22.128,8.302 21.958,8.302C21.509,8.302 21.073,8.059 20.866,7.622Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M4.355,4.104C3.931,3.582 4.016,2.818 4.537,2.406C5.071,1.981 5.823,2.066 6.248,2.588C6.672,3.109 6.588,3.874 6.066,4.298C5.835,4.48 5.569,4.565 5.302,4.565C4.95,4.565 4.598,4.407 4.355,4.104Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M18.161,4.262C17.627,3.838 17.542,3.073 17.955,2.552C18.379,2.03 19.132,1.945 19.666,2.358C20.187,2.77 20.272,3.534 19.86,4.068C19.617,4.359 19.265,4.517 18.913,4.517C18.646,4.517 18.379,4.432 18.161,4.262Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M8.492,1.497C8.334,0.854 8.747,0.199 9.402,0.041C10.057,-0.105 10.7,0.308 10.858,0.963C11.003,1.606 10.591,2.261 9.948,2.407C9.851,2.431 9.754,2.443 9.669,2.443C9.123,2.443 8.613,2.067 8.492,1.497Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M14.267,2.395C13.599,2.249 13.199,1.606 13.345,0.951C13.49,0.296 14.133,-0.116 14.788,0.029C15.443,0.175 15.856,0.83 15.71,1.485C15.589,2.043 15.08,2.431 14.534,2.431C14.437,2.431 14.352,2.419 14.267,2.395Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M7,17.037C6.527,16.564 6.527,15.8 7,15.326C7.473,14.841 8.237,14.841 8.71,15.314C9.196,15.787 9.196,16.552 8.723,17.025C8.48,17.267 8.177,17.389 7.861,17.389C7.546,17.389 7.242,17.267 7,17.037Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M15.565,17.012C15.092,16.539 15.092,15.762 15.565,15.289C16.038,14.816 16.814,14.816 17.288,15.289C17.761,15.762 17.761,16.539 17.288,17.012C17.045,17.243 16.742,17.364 16.426,17.364C16.111,17.364 15.807,17.243 15.565,17.012Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M4.853,11.917C4.853,11.237 5.386,10.691 6.054,10.691C6.721,10.691 7.279,11.225 7.279,11.892C7.279,12.56 6.745,13.106 6.078,13.118C5.398,13.118 4.853,12.584 4.853,11.917Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M16.984,11.868C16.984,11.856 16.984,11.844 16.984,11.832C16.984,11.832 16.984,11.82 16.984,11.807C16.972,11.14 17.506,10.582 18.185,10.582C18.852,10.57 19.398,11.116 19.41,11.783C19.41,11.795 19.41,11.82 19.41,11.832C19.41,11.844 19.41,11.856 19.41,11.868C19.41,12.535 18.865,13.081 18.197,13.081C17.53,13.081 16.984,12.535 16.984,11.868Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M6.952,8.471C6.478,7.997 6.478,7.233 6.952,6.76C6.952,6.76 6.952,6.76 6.939,6.76C7.413,6.275 8.189,6.275 8.662,6.748C9.135,7.221 9.147,7.985 8.674,8.458C8.432,8.701 8.116,8.822 7.813,8.822C7.497,8.822 7.194,8.701 6.952,8.471Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M15.529,8.399C15.043,7.938 15.043,7.161 15.504,6.688C15.977,6.203 16.742,6.203 17.227,6.664C17.7,7.137 17.712,7.901 17.239,8.387C17.009,8.629 16.693,8.751 16.378,8.751C16.075,8.751 15.759,8.629 15.529,8.399Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M10.87,5.815C10.858,5.148 11.392,4.59 12.071,4.59C12.738,4.578 13.284,5.124 13.284,5.791C13.296,6.458 12.762,7.016 12.083,7.016C11.416,7.016 10.87,6.483 10.87,5.815Z"/>
+</vector>
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index b58187d..28cdc6d 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -807,7 +807,9 @@
Settings.Secure.UI_TRANSLATION_ENABLED,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_EDGE_HAPTIC_ENABLED,
Settings.Secure.DND_CONFIGS_MIGRATED,
- Settings.Secure.NAVIGATION_MODE_RESTORE);
+ Settings.Secure.NAVIGATION_MODE_RESTORE,
+ Settings.Secure.V_TO_U_RESTORE_ALLOWLIST,
+ Settings.Secure.V_TO_U_RESTORE_DENYLIST);
@Test
public void systemSettingsBackedUpOrDenied() {
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 71f9ba27..a69a2a6 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -377,6 +377,13 @@
}
flag {
+ name: "screenshot_action_dismiss_system_windows"
+ namespace: "systemui"
+ description: "Dismiss existing system windows when starting action from screenshot UI"
+ bug: "309933761"
+}
+
+flag {
name: "run_fingerprint_detect_on_dismissible_keyguard"
namespace: "systemui"
description: "Run fingerprint detect instead of authenticate if the keyguard is dismissible."
@@ -529,3 +536,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "bind_keyguard_media_visibility"
+ namespace: "systemui"
+ description: "Binds Keyguard Media Controller Visibility to MediaContainerView"
+ bug: "298213983"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt
index 535c2d3..e862f0c 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt
@@ -17,7 +17,6 @@
import android.view.View
import androidx.annotation.VisibleForTesting
-import java.util.Random
/** Plays [TurbulenceNoiseView] in ease-in, main (no easing), and ease-out order. */
class TurbulenceNoiseController(private val turbulenceNoiseView: TurbulenceNoiseView) {
@@ -37,8 +36,6 @@
}
}
- private val random = Random()
-
/** Current state of the animation. */
@VisibleForTesting
var state: AnimationState = AnimationState.NOT_PLAYING
@@ -95,12 +92,7 @@
}
state = AnimationState.EASE_IN
- // Add offset to avoid repetitive noise.
- turbulenceNoiseView.playEaseIn(
- offsetX = random.nextFloat(),
- offsetY = random.nextFloat(),
- this::playMainAnimation
- )
+ turbulenceNoiseView.playEaseIn(this::playMainAnimation)
}
private fun playMainAnimation() {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt
index c59bc10..5e72e3b 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt
@@ -109,7 +109,7 @@
/** Plays the turbulence noise with linear ease-in. */
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
- fun playEaseIn(offsetX: Float = 0f, offsetY: Float = 0f, onAnimationEnd: Runnable? = null) {
+ fun playEaseIn(onAnimationEnd: Runnable? = null) {
if (noiseConfig == null) {
return
}
@@ -129,8 +129,8 @@
val progress = updateListener.animatedValue as Float
shader.setNoiseMove(
- offsetX + initialX + timeInSec * config.noiseMoveSpeedX,
- offsetY + initialY + timeInSec * config.noiseMoveSpeedY,
+ initialX + timeInSec * config.noiseMoveSpeedX,
+ initialY + timeInSec * config.noiseMoveSpeedY,
initialZ + timeInSec * config.noiseMoveSpeedZ
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
index 8bd0d45..97d5b41 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
@@ -35,7 +35,6 @@
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
-import com.android.systemui.keyguard.ui.viewmodel.AodAlphaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
@@ -55,7 +54,6 @@
private val vibratorHelper: VibratorHelper,
private val indicationController: KeyguardIndicationController,
private val indicationAreaViewModel: KeyguardIndicationAreaViewModel,
- private val alphaViewModel: AodAlphaViewModel,
) {
/**
* Renders a single lockscreen shortcut.
@@ -104,7 +102,6 @@
content {
IndicationArea(
indicationAreaViewModel = indicationAreaViewModel,
- alphaViewModel = alphaViewModel,
indicationController = indicationController,
)
}
@@ -183,7 +180,6 @@
@Composable
private fun IndicationArea(
indicationAreaViewModel: KeyguardIndicationAreaViewModel,
- alphaViewModel: AodAlphaViewModel,
indicationController: KeyguardIndicationController,
modifier: Modifier = Modifier,
) {
@@ -196,7 +192,6 @@
KeyguardIndicationAreaBinder.bind(
view = view,
viewModel = indicationAreaViewModel,
- aodAlphaViewModel = alphaViewModel,
indicationController = indicationController,
)
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 4156d83..ce96d75 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -18,7 +18,9 @@
package com.android.systemui.communal.domain.interactor
import android.app.smartspace.SmartspaceTarget
+import android.appwidget.AppWidgetProviderInfo
import android.content.pm.UserInfo
+import android.os.UserHandle
import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
import android.widget.RemoteViews
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -51,6 +53,8 @@
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository
import com.android.systemui.testKosmos
@@ -96,6 +100,7 @@
private lateinit var communalPrefsRepository: FakeCommunalPrefsRepository
private lateinit var editWidgetsActivityStarter: EditWidgetsActivityStarter
private lateinit var sceneInteractor: SceneInteractor
+ private lateinit var userTracker: FakeUserTracker
private lateinit var underTest: CommunalInteractor
@@ -113,6 +118,7 @@
editWidgetsActivityStarter = kosmos.editWidgetsActivityStarter
communalPrefsRepository = kosmos.fakeCommunalPrefsRepository
sceneInteractor = kosmos.sceneInteractor
+ userTracker = kosmos.fakeUserTracker
whenever(mainUser.isMain).thenReturn(true)
whenever(secondaryUser.isMain).thenReturn(false)
@@ -207,25 +213,19 @@
keyguardRepository.setKeyguardOccluded(false)
tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
- // Widgets are available.
- val widgets =
- listOf(
- CommunalWidgetContentModel(
- appWidgetId = 0,
- priority = 30,
- providerInfo = mock(),
- ),
- CommunalWidgetContentModel(
- appWidgetId = 1,
- priority = 20,
- providerInfo = mock(),
- ),
- CommunalWidgetContentModel(
- appWidgetId = 2,
- priority = 10,
- providerInfo = mock(),
- ),
- )
+ val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
+ userRepository.setUserInfos(userInfos)
+ userTracker.set(
+ userInfos = userInfos,
+ selectedUserIndex = 0,
+ )
+ runCurrent()
+
+ // Widgets available.
+ val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
+ val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id)
+ val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
+ val widgets = listOf(widget1, widget2, widget3)
widgetRepository.setCommunalWidgets(widgets)
val widgetContent by collectLastValue(underTest.widgetContent)
@@ -752,6 +752,38 @@
verify(editWidgetsActivityStarter).startActivity(widgetKey)
}
+ @Test
+ fun filterWidgets_whenUserProfileRemoved() =
+ testScope.runTest {
+ // Keyguard showing, and tutorial completed.
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+ // Only main user exists.
+ val userInfos = listOf(MAIN_USER_INFO)
+ userRepository.setUserInfos(userInfos)
+ userTracker.set(
+ userInfos = userInfos,
+ selectedUserIndex = 0,
+ )
+ runCurrent()
+
+ val widgetContent by collectLastValue(underTest.widgetContent)
+ // Given three widgets, and one of them is associated with pre-existing work profile.
+ val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
+ val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id)
+ val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
+ val widgets = listOf(widget1, widget2, widget3)
+ widgetRepository.setCommunalWidgets(widgets)
+
+ // One widget is filtered out and the remaining two link to main user id.
+ assertThat(checkNotNull(widgetContent).size).isEqualTo(2)
+ widgetContent!!.forEachIndexed { _, model ->
+ assertThat(model.providerInfo.profile?.identifier).isEqualTo(MAIN_USER_INFO.id)
+ }
+ }
+
private fun smartspaceTimer(id: String, timestamp: Long = 0L): SmartspaceTarget {
val timer = mock(SmartspaceTarget::class.java)
whenever(timer.smartspaceTargetId).thenReturn(id)
@@ -760,4 +792,17 @@
whenever(timer.creationTimeMillis).thenReturn(timestamp)
return timer
}
+
+ private fun createWidgetForUser(appWidgetId: Int, userId: Int): CommunalWidgetContentModel =
+ mock<CommunalWidgetContentModel> {
+ whenever(this.appWidgetId).thenReturn(appWidgetId)
+ val providerInfo = mock<AppWidgetProviderInfo>()
+ whenever(providerInfo.profile).thenReturn(UserHandle(userId))
+ whenever(this.providerInfo).thenReturn(providerInfo)
+ }
+
+ private companion object {
+ val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+ val USER_INFO_WORK = UserInfo(10, "work", UserInfo.FLAG_PROFILE)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index 352bacc..5ee88cb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -17,6 +17,9 @@
package com.android.systemui.communal.view.viewmodel
import android.app.smartspace.SmartspaceTarget
+import android.appwidget.AppWidgetProviderInfo
+import android.content.pm.UserInfo
+import android.os.UserHandle
import android.provider.Settings
import android.widget.RemoteViews
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -39,6 +42,7 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository
import com.android.systemui.testKosmos
@@ -59,6 +63,7 @@
class CommunalEditModeViewModelTest : SysuiTestCase() {
@Mock private lateinit var mediaHost: MediaHost
@Mock private lateinit var uiEventLogger: UiEventLogger
+ @Mock private lateinit var providerInfo: AppWidgetProviderInfo
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
@@ -78,6 +83,11 @@
widgetRepository = kosmos.fakeCommunalWidgetRepository
smartspaceRepository = kosmos.fakeSmartspaceRepository
mediaRepository = kosmos.fakeCommunalMediaRepository
+ kosmos.fakeUserTracker.set(
+ userInfos = listOf(MAIN_USER_INFO),
+ selectedUserIndex = 0,
+ )
+ whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id))
underTest =
CommunalEditModeViewModel(
@@ -100,12 +110,12 @@
CommunalWidgetContentModel(
appWidgetId = 0,
priority = 30,
- providerInfo = mock(),
+ providerInfo = providerInfo,
),
CommunalWidgetContentModel(
appWidgetId = 1,
priority = 20,
- providerInfo = mock(),
+ providerInfo = providerInfo,
),
)
widgetRepository.setCommunalWidgets(widgets)
@@ -156,12 +166,12 @@
CommunalWidgetContentModel(
appWidgetId = 0,
priority = 30,
- providerInfo = mock(),
+ providerInfo = providerInfo,
),
CommunalWidgetContentModel(
appWidgetId = 1,
priority = 20,
- providerInfo = mock(),
+ providerInfo = providerInfo,
),
)
widgetRepository.setCommunalWidgets(widgets)
@@ -205,4 +215,8 @@
underTest.onReorderWidgetCancel()
verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_CANCEL)
}
+
+ private companion object {
+ val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index cc322d0..1e523dd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -17,7 +17,9 @@
package com.android.systemui.communal.view.viewmodel
import android.app.smartspace.SmartspaceTarget
+import android.appwidget.AppWidgetProviderInfo
import android.content.pm.UserInfo
+import android.os.UserHandle
import android.provider.Settings
import android.widget.RemoteViews
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -45,13 +47,13 @@
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -71,6 +73,7 @@
class CommunalViewModelTest : SysuiTestCase() {
@Mock private lateinit var mediaHost: MediaHost
@Mock private lateinit var user: UserInfo
+ @Mock private lateinit var providerInfo: AppWidgetProviderInfo
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
@@ -98,6 +101,12 @@
kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
+ kosmos.fakeUserTracker.set(
+ userInfos = listOf(MAIN_USER_INFO),
+ selectedUserIndex = 0,
+ )
+ whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id))
+
underTest =
CommunalViewModel(
testScope,
@@ -147,12 +156,12 @@
CommunalWidgetContentModel(
appWidgetId = 0,
priority = 30,
- providerInfo = mock(),
+ providerInfo = providerInfo,
),
CommunalWidgetContentModel(
appWidgetId = 1,
priority = 20,
- providerInfo = mock(),
+ providerInfo = providerInfo,
),
)
widgetRepository.setCommunalWidgets(widgets)
@@ -225,4 +234,8 @@
userRepository.setUserInfos(listOf(user))
userRepository.setSelectedUserInfo(user)
}
+
+ private companion object {
+ val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
index 8488843..2c9d72c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
@@ -16,7 +16,9 @@
package com.android.systemui.communal.widgets
+import android.appwidget.AppWidgetProviderInfo
import android.content.pm.UserInfo
+import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
@@ -32,6 +34,7 @@
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
+import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.mock
@@ -65,7 +68,7 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO))
+ kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO, USER_INFO_WORK))
kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
@@ -76,6 +79,7 @@
CommunalAppWidgetHostStartable(
appWidgetHost,
kosmos.communalInteractor,
+ kosmos.fakeUserTracker,
kosmos.applicationCoroutineScope,
kosmos.testDispatcher,
)
@@ -170,6 +174,46 @@
}
}
+ @Test
+ fun removeWidgetsForDeletedProfile_whenCommunalIsAvailable() =
+ with(kosmos) {
+ testScope.runTest {
+ // Communal is available and work profile is configured.
+ setCommunalAvailable(true)
+ kosmos.fakeUserTracker.set(
+ userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK),
+ selectedUserIndex = 0,
+ )
+ val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
+ val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id)
+ val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
+ val widgets = listOf(widget1, widget2, widget3)
+ fakeCommunalWidgetRepository.setCommunalWidgets(widgets)
+
+ underTest.start()
+ runCurrent()
+
+ val communalWidgets by
+ collectLastValue(fakeCommunalWidgetRepository.communalWidgets)
+ assertThat(communalWidgets).containsExactly(widget1, widget2, widget3)
+
+ // Unlock the device and remove work profile.
+ fakeKeyguardRepository.setKeyguardShowing(false)
+ kosmos.fakeUserTracker.set(
+ userInfos = listOf(MAIN_USER_INFO),
+ selectedUserIndex = 0,
+ )
+ runCurrent()
+
+ // Communal becomes available.
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ runCurrent()
+
+ // Widget created for work profile is removed.
+ assertThat(communalWidgets).containsExactly(widget2, widget3)
+ }
+ }
+
private suspend fun setCommunalAvailable(available: Boolean) =
with(kosmos) {
fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
@@ -179,7 +223,16 @@
fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, settingsValue, MAIN_USER_INFO.id)
}
+ private fun createWidgetForUser(appWidgetId: Int, userId: Int): CommunalWidgetContentModel =
+ mock<CommunalWidgetContentModel> {
+ whenever(this.appWidgetId).thenReturn(appWidgetId)
+ val providerInfo = mock<AppWidgetProviderInfo>()
+ whenever(providerInfo.profile).thenReturn(UserHandle(userId))
+ whenever(this.providerInfo).thenReturn(providerInfo)
+ }
+
private companion object {
val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+ val USER_INFO_WORK = UserInfo(10, "work", UserInfo.FLAG_PROFILE)
}
}
diff --git a/packages/SystemUI/res/layout/screen_share_dialog.xml b/packages/SystemUI/res/layout/screen_share_dialog.xml
index ae81d29..2616e8a 100644
--- a/packages/SystemUI/res/layout/screen_share_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_share_dialog.xml
@@ -72,7 +72,7 @@
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="@dimen/screenrecord_buttons_margin_top">
- <TextView
+ <Button
android:id="@android:id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -83,7 +83,7 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
- <TextView
+ <Button
android:id="@android:id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 495f20f2..53ad344 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3129,6 +3129,8 @@
<!-- [CHAR LIMIT=25] Long label used by Note Task Shortcut -->
<string name="note_task_shortcut_long_label">Note-taking, <xliff:g id="note_taking_app" example="Note-taking App">%1$s</xliff:g></string>
+ <!-- [CHAR LIMIT=NONE] Output switch chip text during broadcasting -->
+ <string name="audio_sharing_description">Sharing audio</string>
<!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, media app is broadcasting -->
<string name="broadcasting_description_is_broadcasting">Broadcasting</string>
<!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, title -->
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogDelegate.java
index 00bbb20..6af0fa0 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogDelegate.java
@@ -40,6 +40,7 @@
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.media.MediaOutputConstants;
import com.android.systemui.broadcast.BroadcastSender;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.media.controls.util.MediaDataUtils;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.res.R;
@@ -74,7 +75,7 @@
private final SystemUIDialog.Factory mSystemUIDialogFactory;
private final String mCurrentBroadcastApp;
private final String mOutputPackageName;
- private final Executor mExecutor;
+ private final Executor mBgExecutor;
private boolean mShouldLaunchLeBroadcastDialog;
private Button mSwitchBroadcast;
@@ -159,7 +160,7 @@
MediaOutputDialogFactory mediaOutputDialogFactory,
@Nullable LocalBluetoothManager localBluetoothManager,
UiEventLogger uiEventLogger,
- Executor executor,
+ @Background Executor bgExecutor,
BroadcastSender broadcastSender,
SystemUIDialog.Factory systemUIDialogFactory,
@Assisted(CURRENT_BROADCAST_APP) String currentBroadcastApp,
@@ -171,7 +172,7 @@
mCurrentBroadcastApp = currentBroadcastApp;
mOutputPackageName = outputPkgName;
mUiEventLogger = uiEventLogger;
- mExecutor = executor;
+ mBgExecutor = bgExecutor;
mBroadcastSender = broadcastSender;
if (DEBUG) {
@@ -187,7 +188,7 @@
@Override
public void onStart(SystemUIDialog dialog) {
mDialogs.add(dialog);
- registerBroadcastCallBack(mExecutor, mBroadcastCallback);
+ registerBroadcastCallBack(mBgExecutor, mBroadcastCallback);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
index 2af49cf..b269967 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
@@ -143,7 +143,7 @@
mTextPreview.getViewTreeObserver().addOnPreDrawListener(() -> {
int availableHeight = mTextPreview.getHeight()
- (mTextPreview.getPaddingTop() + mTextPreview.getPaddingBottom());
- mTextPreview.setMaxLines(availableHeight / mTextPreview.getLineHeight());
+ mTextPreview.setMaxLines(Math.max(availableHeight / mTextPreview.getLineHeight(), 1));
return true;
});
super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index d0044a4..5397837 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -29,6 +29,7 @@
import com.android.systemui.communal.shared.model.CommunalContentSize.HALF
import com.android.systemui.communal.shared.model.CommunalContentSize.THIRD
import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
import com.android.systemui.communal.widgets.CommunalAppWidgetHost
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
@@ -45,6 +46,7 @@
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.settings.UserTracker
import com.android.systemui.smartspace.data.repository.SmartspaceRepository
import com.android.systemui.util.kotlin.BooleanFlowOperators.and
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
@@ -59,6 +61,7 @@
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
@@ -82,6 +85,7 @@
communalSettingsInteractor: CommunalSettingsInteractor,
private val appWidgetHost: CommunalAppWidgetHost,
private val editWidgetsActivityStarter: EditWidgetsActivityStarter,
+ private val userTracker: UserTracker,
sceneInteractor: SceneInteractor,
sceneContainerFlags: SceneContainerFlags,
@CommunalLog logBuffer: LogBuffer,
@@ -262,10 +266,16 @@
fun updateWidgetOrder(widgetIdToPriorityMap: Map<Int, Int>) =
widgetRepository.updateWidgetOrder(widgetIdToPriorityMap)
+ /** All widgets present in db. */
+ val communalWidgets: Flow<List<CommunalWidgetContentModel>> =
+ isCommunalAvailable.flatMapLatest { available ->
+ if (!available) emptyFlow() else widgetRepository.communalWidgets
+ }
+
/** A list of widget content to be displayed in the communal hub. */
val widgetContent: Flow<List<CommunalContentModel.Widget>> =
widgetRepository.communalWidgets.map { widgets ->
- widgets.map Widget@{ widget ->
+ filterWidgetsByExistingUsers(widgets).map Widget@{ widget ->
return@Widget CommunalContentModel.Widget(
appWidgetId = widget.appWidgetId,
providerInfo = widget.providerInfo,
@@ -345,6 +355,19 @@
return@combine ongoingContent
}
+ /**
+ * Filter and retain widgets associated with an existing user, safeguarding against displaying
+ * stale data following user deletion.
+ */
+ private fun filterWidgetsByExistingUsers(
+ list: List<CommunalWidgetContentModel>,
+ ): List<CommunalWidgetContentModel> {
+ val currentUserIds = userTracker.userProfiles.map { it.id }.toSet()
+ return list.filter { widget ->
+ currentUserIds.contains(widget.providerInfo.profile?.identifier)
+ }
+ }
+
companion object {
/**
* The user activity timeout which should be used when the communal hub is opened. A value
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
index 4ddd768..8390d62 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
@@ -18,11 +18,14 @@
import com.android.systemui.CoreStartable
import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.settings.UserTracker
import com.android.systemui.util.kotlin.BooleanFlowOperators.or
import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
@@ -37,6 +40,7 @@
constructor(
private val appWidgetHost: CommunalAppWidgetHost,
private val communalInteractor: CommunalInteractor,
+ private val userTracker: UserTracker,
@Background private val bgScope: CoroutineScope,
@Main private val uiDispatcher: CoroutineDispatcher
) : CoreStartable {
@@ -47,6 +51,14 @@
.pairwise(false)
.filter { (previous, new) -> previous != new }
.onEach { (_, shouldListen) -> updateAppWidgetHostActive(shouldListen) }
+ .sample(communalInteractor.communalWidgets, ::Pair)
+ .onEach { (withPrev, widgets) ->
+ val (_, isActive) = withPrev
+ // The validation is performed once the hub becomes active.
+ if (isActive) {
+ validateWidgetsAndDeleteOrphaned(widgets)
+ }
+ }
.launchIn(bgScope)
appWidgetHost.appWidgetIdToRemove
@@ -63,4 +75,15 @@
appWidgetHost.stopListening()
}
}
+
+ /**
+ * Ensure the existence of all associated users for widgets, and remove widgets belonging to
+ * users who have been deleted.
+ */
+ private fun validateWidgetsAndDeleteOrphaned(widgets: List<CommunalWidgetContentModel>) {
+ val currentUserIds = userTracker.userProfiles.map { it.id }.toSet()
+ widgets
+ .filter { widget -> !currentUserIds.contains(widget.providerInfo.profile?.identifier) }
+ .onEach { widget -> communalInteractor.deleteWidget(id = widget.appWidgetId) }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index 0f038e1..bc07b95 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -36,6 +36,7 @@
import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.controls.settings.ControlsSettingsRepository
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.VibratorHelper
@@ -47,16 +48,16 @@
@SysUISingleton
class ControlActionCoordinatorImpl @Inject constructor(
- private val context: Context,
- private val bgExecutor: DelayableExecutor,
- @Main private val uiExecutor: DelayableExecutor,
- private val activityStarter: ActivityStarter,
- private val broadcastSender: BroadcastSender,
- private val keyguardStateController: KeyguardStateController,
- private val taskViewFactory: Optional<TaskViewFactory>,
- private val controlsMetricsLogger: ControlsMetricsLogger,
- private val vibrator: VibratorHelper,
- private val controlsSettingsRepository: ControlsSettingsRepository,
+ private val context: Context,
+ @Background private val bgExecutor: DelayableExecutor,
+ @Main private val uiExecutor: DelayableExecutor,
+ private val activityStarter: ActivityStarter,
+ private val broadcastSender: BroadcastSender,
+ private val keyguardStateController: KeyguardStateController,
+ private val taskViewFactory: Optional<TaskViewFactory>,
+ private val controlsMetricsLogger: ControlsMetricsLogger,
+ private val vibrator: VibratorHelper,
+ private val controlsSettingsRepository: ControlsSettingsRepository,
) : ControlActionCoordinator {
private var dialog: Dialog? = null
private var pendingAction: Action? = null
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index e893177..1157d97 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -126,6 +126,7 @@
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.PolicyModule;
+import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.dagger.SmartRepliesInflationModule;
import com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule;
@@ -358,6 +359,7 @@
VisualInterruptionDecisionProvider visualInterruptionDecisionProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
+ SensitiveNotificationProtectionController sensitiveNotificationProtectionController,
CommonNotifCollection notifCollection,
NotifPipeline notifPipeline,
SysUiState sysUiState,
@@ -376,6 +378,7 @@
visualInterruptionDecisionProvider,
zenModeController,
notifUserManager,
+ sensitiveNotificationProtectionController,
notifCollection,
notifPipeline,
sysUiState,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index e35c5a6..301942f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -170,7 +170,6 @@
KeyguardIndicationAreaBinder.bind(
notificationShadeWindowView.requireViewById(R.id.keyguard_indication_area),
keyguardIndicationAreaViewModel,
- aodAlphaViewModel,
indicationController,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
index 7c1368a..841f52d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
@@ -23,7 +23,7 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.Flags.keyguardBottomAreaRefactor
-import com.android.systemui.keyguard.ui.viewmodel.AodAlphaViewModel
+import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
@@ -51,7 +51,6 @@
fun bind(
view: ViewGroup,
viewModel: KeyguardIndicationAreaViewModel,
- aodAlphaViewModel: AodAlphaViewModel,
indicationController: KeyguardIndicationController,
): DisposableHandle {
indicationController.setIndicationArea(view)
@@ -68,30 +67,10 @@
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
- if (keyguardBottomAreaRefactor()) {
- aodAlphaViewModel.alpha.collect { alpha ->
- view.apply {
- this.importantForAccessibility =
- if (alpha == 0f) {
- View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
- } else {
- View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
- }
- this.alpha = alpha
- }
- }
- } else {
- viewModel.alpha.collect { alpha ->
- view.apply {
- this.importantForAccessibility =
- if (alpha == 0f) {
- View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
- } else {
- View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
- }
- this.alpha = alpha
- }
- }
+ // Do not independently apply alpha, as [KeyguardRootViewModel] should work
+ // for this and all its children
+ if (!(migrateClocksToBlueprint() || keyguardBottomAreaRefactor())) {
+ viewModel.alpha.collect { alpha -> view.alpha = alpha }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt
index 78099d9..a53c6d7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt
@@ -50,6 +50,15 @@
)
}
+ override fun setAlpha(alpha: Float) {
+ super.setAlpha(alpha)
+
+ if (alpha == 0f) {
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ } else {
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
+ }
+ }
private fun indicationTopRow(): KeyguardIndicationTextView {
return KeyguardIndicationTextView(context, attrs).apply {
id = R.id.keyguard_indication_text
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
index ea05c1d..3361343 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
@@ -25,7 +25,6 @@
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
-import com.android.systemui.keyguard.ui.viewmodel.AodAlphaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.res.R
import com.android.systemui.statusbar.KeyguardIndicationController
@@ -37,7 +36,6 @@
constructor(
private val context: Context,
private val keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel,
- private val aodAlphaViewModel: AodAlphaViewModel,
private val indicationController: KeyguardIndicationController,
) : KeyguardSection() {
private val indicationAreaViewId = R.id.keyguard_indication_area
@@ -56,7 +54,6 @@
KeyguardIndicationAreaBinder.bind(
constraintLayout.requireViewById(R.id.keyguard_indication_area),
keyguardIndicationAreaViewModel,
- aodAlphaViewModel,
indicationController,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
index 42d68ba..f4d70a5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
@@ -30,6 +30,8 @@
import androidx.annotation.WorkerThread
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.settingslib.flags.Flags.enableLeAudioSharing
+import com.android.settingslib.flags.Flags.legacyLeAudioSharing
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.media.PhoneMediaDevice
@@ -332,14 +334,28 @@
@WorkerThread
private fun updateCurrent() {
if (isLeAudioBroadcastEnabled()) {
- current =
- MediaDeviceData(
- /* enabled */ true,
- /* icon */ context.getDrawable(R.drawable.settings_input_antenna),
- /* name */ broadcastDescription,
- /* intent */ null,
- /* showBroadcastButton */ showBroadcastButton = true
- )
+ if (enableLeAudioSharing()) {
+ current =
+ MediaDeviceData(
+ enabled = false,
+ icon =
+ context.getDrawable(
+ com.android.settingslib.R.drawable.ic_bt_le_audio_sharing
+ ),
+ name = context.getString(R.string.audio_sharing_description),
+ intent = null,
+ showBroadcastButton = false
+ )
+ } else {
+ current =
+ MediaDeviceData(
+ /* enabled */ true,
+ /* icon */ context.getDrawable(R.drawable.settings_input_antenna),
+ /* name */ broadcastDescription,
+ /* intent */ null,
+ /* showBroadcastButton */ showBroadcastButton = true
+ )
+ }
} else {
val aboutToConnect = aboutToConnectDeviceOverride
if (
@@ -420,6 +436,7 @@
@WorkerThread
private fun isLeAudioBroadcastEnabled(): Boolean {
+ if (!enableLeAudioSharing() && !legacyLeAudioSharing()) return false
val localBluetoothManager = localBluetoothManager.get()
if (localBluetoothManager != null) {
val profileManager = localBluetoothManager.profileManager
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
index 9206af2..ba7d410 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
@@ -300,10 +300,17 @@
private fun setVisibility(view: ViewGroup?, newVisibility: Int) {
val currentMediaContainer = view ?: return
- val previousVisibility = currentMediaContainer.visibility
- currentMediaContainer.visibility = newVisibility
- if (previousVisibility != newVisibility && currentMediaContainer is MediaContainerView) {
- visibilityChangedListener?.invoke(newVisibility == View.VISIBLE)
+ val isVisible = newVisibility == View.VISIBLE
+
+ if (currentMediaContainer is MediaContainerView) {
+ val previousVisibility = currentMediaContainer.visibility
+
+ currentMediaContainer.setKeyguardVisibility(isVisible)
+ if (previousVisibility != newVisibility) {
+ visibilityChangedListener?.invoke(isVisible)
+ }
+ } else {
+ currentMediaContainer.visibility = newVisibility
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index e8ad4d3..4e940f1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -260,7 +260,6 @@
private TurbulenceNoiseController mTurbulenceNoiseController;
private LoadingEffect mLoadingEffect;
private final GlobalSettings mGlobalSettings;
- private final Random mRandom = new Random();
private TurbulenceNoiseAnimationConfig mTurbulenceNoiseAnimationConfig;
private boolean mWasPlaying = false;
private boolean mButtonClicked = false;
@@ -1294,13 +1293,14 @@
mMediaViewHolder.getTurbulenceNoiseView();
int width = targetView.getWidth();
int height = targetView.getHeight();
+ Random random = new Random();
return new TurbulenceNoiseAnimationConfig(
/* gridCount= */ 2.14f,
TurbulenceNoiseAnimationConfig.DEFAULT_LUMINOSITY_MULTIPLIER,
- /* noiseOffsetX= */ mRandom.nextFloat(),
- /* noiseOffsetY= */ mRandom.nextFloat(),
- /* noiseOffsetZ= */ mRandom.nextFloat(),
+ /* noiseOffsetX= */ random.nextFloat(),
+ /* noiseOffsetY= */ random.nextFloat(),
+ /* noiseOffsetZ= */ random.nextFloat(),
/* noiseMoveSpeedX= */ 0.42f,
/* noiseMoveSpeedY= */ 0f,
TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_SPEED_Z,
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 416eae1..4f062af 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -72,7 +72,7 @@
context: Context,
logger: MediaTttReceiverLogger,
windowManager: WindowManager,
- mainExecutor: DelayableExecutor,
+ @Main mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
dumpManager: DumpManager,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
index 72a5c46..c1b2037 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
@@ -84,6 +84,10 @@
*/
val qsHeight: Int
+ /** Compatibility for use by LockscreenShadeTransitionController. Matches default from [QS] */
+ val isQsFullyCollapsed: Boolean
+ get() = true
+
sealed interface State {
val isVisible: Boolean
@@ -165,6 +169,10 @@
override val qsHeight: Int
get() = qsImpl.value?.qsHeight ?: 0
+ // If value is null, there's no QS and therefore it's fully collapsed.
+ override val isQsFullyCollapsed: Boolean
+ get() = qsImpl.value?.isFullyCollapsed ?: true
+
// Same config changes as in FragmentHostManager
private val interestingChanges =
InterestingConfigChanges(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
index bee3152..fb5339d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
@@ -31,10 +31,13 @@
import android.view.WindowManagerGlobal
import com.android.app.tracing.coroutines.launch
import com.android.internal.infra.ServiceConnector
+import com.android.systemui.Flags.screenshotActionDismissSystemWindows
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.settings.DisplayTracker
+import com.android.systemui.shared.system.ActivityManagerWrapper
+import com.android.systemui.statusbar.phone.CentralSurfaces
import javax.inject.Inject
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineDispatcher
@@ -46,9 +49,11 @@
@Inject
constructor(
private val context: Context,
+ private val activityManagerWrapper: ActivityManagerWrapper,
@Application private val applicationScope: CoroutineScope,
@Main private val mainDispatcher: CoroutineDispatcher,
private val displayTracker: DisplayTracker,
+ private val keyguardController: ScreenshotKeyguardController,
) {
/**
* Execute the given intent with startActivity while performing operations for screenshot action
@@ -74,7 +79,14 @@
user: UserHandle,
overrideTransition: Boolean,
) {
- dismissKeyguard()
+ if (screenshotActionDismissSystemWindows()) {
+ keyguardController.dismiss()
+ activityManagerWrapper.closeSystemWindows(
+ CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT
+ )
+ } else {
+ dismissKeyguard()
+ }
if (user == myUserHandle()) {
withContext(mainDispatcher) { context.startActivity(intent, options) }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotKeyguardController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotKeyguardController.kt
new file mode 100644
index 0000000..7696bbe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotKeyguardController.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.screenshot
+
+import android.content.Context
+import android.content.Intent
+import com.android.internal.infra.ServiceConnector
+import javax.inject.Inject
+import kotlinx.coroutines.CompletableDeferred
+
+open class ScreenshotKeyguardController @Inject constructor(context: Context) {
+ private val proxyConnector: ServiceConnector<IScreenshotProxy> =
+ ServiceConnector.Impl(
+ context,
+ Intent(context, ScreenshotProxyService::class.java),
+ Context.BIND_AUTO_CREATE or Context.BIND_WAIVE_PRIORITY or Context.BIND_NOT_VISIBLE,
+ context.userId,
+ IScreenshotProxy.Stub::asInterface
+ )
+
+ suspend fun dismiss() {
+ val completion = CompletableDeferred<Unit>()
+ val onDoneBinder =
+ object : IOnDoneCallback.Stub() {
+ override fun onDone(success: Boolean) {
+ completion.complete(Unit)
+ }
+ }
+ proxyConnector.post { it.dismissKeyguard(onDoneBinder) }
+ completion.await()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
index 86f6523..d5ab306 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
@@ -17,15 +17,16 @@
import android.content.Intent
import android.os.IBinder
+import android.os.RemoteException
import android.util.Log
import androidx.lifecycle.LifecycleService
import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.coroutines.launch
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.shade.ShadeExpansionStateManager
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
-import com.android.app.tracing.coroutines.launch
import kotlinx.coroutines.withContext
/** Provides state from the main SystemUI process on behalf of the Screenshot process. */
@@ -56,7 +57,13 @@
private suspend fun executeAfterDismissing(callback: IOnDoneCallback) =
withContext(mMainDispatcher) {
activityStarter.executeRunnableDismissingKeyguard(
- Runnable { callback.onDone(true) },
+ {
+ try {
+ callback.onDone(true)
+ } catch (e: RemoteException) {
+ Log.w(TAG, "Failed to complete callback transaction", e)
+ }
+ },
null,
true /* dismissShade */,
true /* afterKeyguardGone */,
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index f2fa0ef..125f7fc 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -121,7 +121,6 @@
@GuardedBy("callbacks")
private val callbacks: MutableList<DataItem> = ArrayList()
- private var beforeUserSwitchingJob: Job? = null
private var userSwitchingJob: Job? = null
private var afterUserSwitchingJob: Job? = null
@@ -194,14 +193,7 @@
private fun registerUserSwitchObserver() {
iActivityManager.registerUserSwitchObserver(object : UserSwitchObserver() {
override fun onBeforeUserSwitching(newUserId: Int) {
- if (isBackgroundUserSwitchEnabled) {
- beforeUserSwitchingJob?.cancel()
- beforeUserSwitchingJob = appScope.launch(backgroundContext) {
- handleBeforeUserSwitching(newUserId)
- }
- } else {
- handleBeforeUserSwitching(newUserId)
- }
+ handleBeforeUserSwitching(newUserId)
}
override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
@@ -233,15 +225,24 @@
@WorkerThread
protected open fun handleBeforeUserSwitching(newUserId: Int) {
- Assert.isNotMainThread()
setUserIdInternal(newUserId)
val list = synchronized(callbacks) {
callbacks.toList()
}
+ val latch = CountDownLatch(list.size)
list.forEach {
- it.callback.get()?.onBeforeUserSwitching(newUserId)
+ val callback = it.callback.get()
+ if (callback != null) {
+ it.executor.execute {
+ callback.onBeforeUserSwitching(newUserId)
+ latch.countDown()
+ }
+ } else {
+ latch.countDown()
+ }
}
+ latch.await()
}
@WorkerThread
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt
index 4d0552e..adca3f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt
@@ -21,9 +21,9 @@
import android.util.MathUtils
import androidx.annotation.FloatRange
import androidx.annotation.Px
-import com.android.systemui.res.R
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QS
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.SplitShadeStateController
import dagger.assisted.Assisted
@@ -38,7 +38,7 @@
context: Context,
configurationController: ConfigurationController,
dumpManager: DumpManager,
- @Assisted private val qsProvider: () -> QS,
+ @Assisted private val qsProvider: () -> QS?,
splitShadeStateController: SplitShadeStateController
) :
AbstractLockscreenShadeTransitionController(
@@ -48,7 +48,7 @@
splitShadeStateController
) {
- private val qs: QS
+ private val qs: QS?
get() = qsProvider()
/**
@@ -135,7 +135,7 @@
/* amount= */ MathUtils.saturate(qsDragDownAmount / qsSquishTransitionDistance)
)
isTransitioningToFullShade = dragDownAmount > 0.0f
- qs.setTransitionToFullShadeProgress(
+ qs?.setTransitionToFullShadeProgress(
isTransitioningToFullShade,
qsTransitionFraction,
qsSquishTransitionFraction
@@ -163,6 +163,6 @@
@AssistedFactory
fun interface Factory {
- fun create(qsProvider: () -> QS): LockscreenShadeQsTransitionController
+ fun create(qsProvider: () -> QS?): LockscreenShadeQsTransitionController
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index a59d753..4ee8349 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -31,6 +31,7 @@
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QS
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeLockscreenInteractor
import com.android.systemui.shade.data.repository.ShadeRepository
@@ -84,6 +85,7 @@
private val splitShadeStateController: SplitShadeStateController,
private val shadeLockscreenInteractorLazy: Lazy<ShadeLockscreenInteractor>,
naturalScrollingSettingObserver: NaturalScrollingSettingObserver,
+ private val lazyQSSceneAdapter: Lazy<QSSceneAdapter>,
) : Dumpable {
private var pulseHeight: Float = 0f
@@ -93,7 +95,11 @@
private var useSplitShade: Boolean = false
private lateinit var nsslController: NotificationStackScrollLayoutController
lateinit var centralSurfaces: CentralSurfaces
- lateinit var qS: QS
+
+ // When in scene container mode, this will be null. In that case, we use the adapter if needed
+ var qS: QS? = null
+ private val isQsFullyCollapsed: Boolean
+ get() = qS?.isFullyCollapsed ?: lazyQSSceneAdapter.get().isQsFullyCollapsed
/** A handler that handles the next keyguard dismiss animation. */
private var animationHandlerOnKeyguardDismiss: ((Long) -> Unit)? = null
@@ -286,7 +292,8 @@
/** @return true if the interaction is accepted, false if it should be cancelled */
internal fun canDragDown(): Boolean {
return (statusBarStateController.state == StatusBarState.KEYGUARD ||
- nsslController.isInLockedDownShade()) && (qS.isFullyCollapsed || useSplitShade)
+ nsslController.isInLockedDownShade()) &&
+ (isQsFullyCollapsed || useSplitShade)
}
/** Called by the touch helper when when a gesture has completed all the way and released. */
@@ -410,7 +417,7 @@
get() =
(statusBarStateController.getState() == StatusBarState.KEYGUARD &&
!keyguardBypassController.bypassEnabled &&
- (qS.isFullyCollapsed || useSplitShade))
+ (isQsFullyCollapsed || useSplitShade))
/** The amount in pixels that the user has dragged down. */
internal var dragDownAmount = 0f
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
index e47c914..612a365 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
@@ -27,7 +27,7 @@
private val context: Context,
private val scrimController: ScrimController,
private val statusBarStateController: SysuiStatusBarStateController,
- @Assisted private val qSProvider: () -> QS,
+ @Assisted private val qSProvider: () -> QS?,
@Assisted private val nsslControllerProvider: () -> NotificationStackScrollLayoutController
) : LockScreenShadeOverScroller {
@@ -37,7 +37,7 @@
private var maxOverScrollAmount = 0
private var previousOverscrollAmount = 0
- private val qS: QS
+ private val qS: QS?
get() = qSProvider()
private val nsslController: NotificationStackScrollLayoutController
@@ -90,7 +90,7 @@
}
private fun applyOverscroll(overscrollAmount: Int) {
- qS.setOverScrollAmount(overscrollAmount)
+ qS?.setOverScrollAmount(overscrollAmount)
scrimController.setNotificationsOverScrollAmount(overscrollAmount)
nsslController.setOverScrollAmount(overscrollAmount)
}
@@ -109,7 +109,7 @@
val animator = ValueAnimator.ofInt(previousOverscrollAmount, 0)
animator.addUpdateListener {
val overScrollAmount = it.animatedValue as Int
- qS.setOverScrollAmount(overScrollAmount)
+ qS?.setOverScrollAmount(overScrollAmount)
scrimController.setNotificationsOverScrollAmount(overScrollAmount)
nsslController.setOverScrollAmount(overScrollAmount)
}
@@ -143,7 +143,7 @@
@AssistedFactory
fun interface Factory {
fun create(
- qSProvider: () -> QS,
+ qSProvider: () -> QS?,
nsslControllerProvider: () -> NotificationStackScrollLayoutController
): SplitShadeLockScreenOverScroller
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 8189fe0..dfe6cd5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -25,6 +25,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -92,7 +93,7 @@
@Inject
public VisualStabilityCoordinator(
- DelayableExecutor delayableExecutor,
+ @Background DelayableExecutor delayableExecutor,
DumpManager dumpManager,
HeadsUpManager headsUpManager,
ShadeAnimationInteractor shadeAnimationInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
index 76e5fd3..a5f42bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -45,10 +45,12 @@
* icons and keeping the icon assets themselves up to date as notifications change.
*
* TODO: Much of this code was copied whole-sale in order to get it out of NotificationEntry.
- * Long-term, it should probably live somewhere in the content inflation pipeline.
+ * Long-term, it should probably live somewhere in the content inflation pipeline.
*/
@SysUISingleton
-class IconManager @Inject constructor(
+class IconManager
+@Inject
+constructor(
private val notifCollection: CommonNotifCollection,
private val launcherApps: LauncherApps,
private val iconBuilder: IconBuilder
@@ -59,30 +61,30 @@
notifCollection.addCollectionListener(entryListener)
}
- private val entryListener = object : NotifCollectionListener {
- override fun onEntryInit(entry: NotificationEntry) {
- entry.addOnSensitivityChangedListener(sensitivityListener)
+ private val entryListener =
+ object : NotifCollectionListener {
+ override fun onEntryInit(entry: NotificationEntry) {
+ entry.addOnSensitivityChangedListener(sensitivityListener)
+ }
+
+ override fun onEntryCleanUp(entry: NotificationEntry) {
+ entry.removeOnSensitivityChangedListener(sensitivityListener)
+ }
+
+ override fun onRankingApplied() {
+ // rankings affect whether a conversation is important, which can change the icons
+ recalculateForImportantConversationChange()
+ }
}
- override fun onEntryCleanUp(entry: NotificationEntry) {
- entry.removeOnSensitivityChangedListener(sensitivityListener)
- }
-
- override fun onRankingApplied() {
- // rankings affect whether a conversation is important, which can change the icons
- recalculateForImportantConversationChange()
- }
- }
-
- private val sensitivityListener = NotificationEntry.OnSensitivityChangedListener {
- entry -> updateIconsSafe(entry)
- }
+ private val sensitivityListener =
+ NotificationEntry.OnSensitivityChangedListener { entry -> updateIconsSafe(entry) }
private fun recalculateForImportantConversationChange() {
for (entry in notifCollection.allNotifs) {
val isImportant = isImportantConversation(entry)
- if (entry.icons.areIconsAvailable &&
- isImportant != entry.icons.isImportantConversation
+ if (
+ entry.icons.areIconsAvailable && isImportant != entry.icons.isImportantConversation
) {
updateIconsSafe(entry)
}
@@ -97,34 +99,35 @@
* @throws InflationException Exception if required icons are not valid or specified
*/
@Throws(InflationException::class)
- fun createIcons(entry: NotificationEntry) = traceSection("IconManager.createIcons") {
- // Construct the status bar icon view.
- val sbIcon = iconBuilder.createIconView(entry)
- sbIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
+ fun createIcons(entry: NotificationEntry) =
+ traceSection("IconManager.createIcons") {
+ // Construct the status bar icon view.
+ val sbIcon = iconBuilder.createIconView(entry)
+ sbIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
- // Construct the shelf icon view.
- val shelfIcon = iconBuilder.createIconView(entry)
- shelfIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
- shelfIcon.visibility = View.INVISIBLE
+ // Construct the shelf icon view.
+ val shelfIcon = iconBuilder.createIconView(entry)
+ shelfIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
+ shelfIcon.visibility = View.INVISIBLE
- // Construct the aod icon view.
- val aodIcon = iconBuilder.createIconView(entry)
- aodIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
- aodIcon.setIncreasedSize(true)
+ // Construct the aod icon view.
+ val aodIcon = iconBuilder.createIconView(entry)
+ aodIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
+ aodIcon.setIncreasedSize(true)
- // Set the icon views' icons
- val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry)
+ // Set the icon views' icons
+ val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry)
- try {
- setIcon(entry, normalIconDescriptor, sbIcon)
- setIcon(entry, sensitiveIconDescriptor, shelfIcon)
- setIcon(entry, sensitiveIconDescriptor, aodIcon)
- entry.icons = IconPack.buildPack(sbIcon, shelfIcon, aodIcon, entry.icons)
- } catch (e: InflationException) {
- entry.icons = IconPack.buildEmptyPack(entry.icons)
- throw e
+ try {
+ setIcon(entry, normalIconDescriptor, sbIcon)
+ setIcon(entry, sensitiveIconDescriptor, shelfIcon)
+ setIcon(entry, sensitiveIconDescriptor, aodIcon)
+ entry.icons = IconPack.buildPack(sbIcon, shelfIcon, aodIcon, entry.icons)
+ } catch (e: InflationException) {
+ entry.icons = IconPack.buildEmptyPack(entry.icons)
+ throw e
+ }
}
- }
/**
* Update the notification icons.
@@ -133,33 +136,33 @@
* @throws InflationException Exception if required icons are not valid or specified
*/
@Throws(InflationException::class)
- fun updateIcons(entry: NotificationEntry) = traceSection("IconManager.updateIcons") {
- if (!entry.icons.areIconsAvailable) {
- return@traceSection
- }
- entry.icons.smallIconDescriptor = null
- entry.icons.peopleAvatarDescriptor = null
+ fun updateIcons(entry: NotificationEntry) =
+ traceSection("IconManager.updateIcons") {
+ if (!entry.icons.areIconsAvailable) {
+ return@traceSection
+ }
+ entry.icons.smallIconDescriptor = null
+ entry.icons.peopleAvatarDescriptor = null
- val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry)
- val notificationContentDescription = entry.sbn.notification?.let {
- iconBuilder.getIconContentDescription(it)
- }
+ val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry)
+ val notificationContentDescription =
+ entry.sbn.notification?.let { iconBuilder.getIconContentDescription(it) }
- entry.icons.statusBarIcon?.let {
- it.setNotification(entry.sbn, notificationContentDescription)
- setIcon(entry, normalIconDescriptor, it)
- }
+ entry.icons.statusBarIcon?.let {
+ it.setNotification(entry.sbn, notificationContentDescription)
+ setIcon(entry, normalIconDescriptor, it)
+ }
- entry.icons.shelfIcon?.let {
- it.setNotification(entry.sbn, notificationContentDescription)
- setIcon(entry, normalIconDescriptor, it)
- }
+ entry.icons.shelfIcon?.let {
+ it.setNotification(entry.sbn, notificationContentDescription)
+ setIcon(entry, sensitiveIconDescriptor, it)
+ }
- entry.icons.aodIcon?.let {
- it.setNotification(entry.sbn, notificationContentDescription)
- setIcon(entry, sensitiveIconDescriptor, it)
+ entry.icons.aodIcon?.let {
+ it.setNotification(entry.sbn, notificationContentDescription)
+ setIcon(entry, sensitiveIconDescriptor, it)
+ }
}
- }
private fun updateIconsSafe(entry: NotificationEntry) {
try {
@@ -173,11 +176,12 @@
@Throws(InflationException::class)
private fun getIconDescriptors(entry: NotificationEntry): Pair<StatusBarIcon, StatusBarIcon> {
val iconDescriptor = getIconDescriptor(entry, redact = false)
- val sensitiveDescriptor = if (entry.isSensitive) {
- getIconDescriptor(entry, redact = true)
- } else {
- iconDescriptor
- }
+ val sensitiveDescriptor =
+ if (entry.isSensitive) {
+ getIconDescriptor(entry, redact = true)
+ } else {
+ iconDescriptor
+ }
return Pair(iconDescriptor, sensitiveDescriptor)
}
@@ -197,14 +201,15 @@
}
val icon =
- (if (showPeopleAvatar) {
- createPeopleAvatar(entry)
- } else {
- n.smallIcon
- }) ?: throw InflationException(
- "No icon in notification from " + entry.sbn.packageName)
+ (if (showPeopleAvatar) {
+ createPeopleAvatar(entry)
+ } else {
+ n.smallIcon
+ })
+ ?: throw InflationException("No icon in notification from " + entry.sbn.packageName)
- val ic = StatusBarIcon(
+ val ic =
+ StatusBarIcon(
entry.sbn.user,
entry.sbn.packageName,
icon,
@@ -282,8 +287,8 @@
/**
* Determines if this icon shows a conversation based on the sensitivity of the icon, its
- * context and the user's indicated sensitivity preference. If we're using a fall back icon
- * of the small icon, we don't consider this to be showing a conversation
+ * context and the user's indicated sensitivity preference. If we're using a fall back icon of
+ * the small icon, we don't consider this to be showing a conversation
*
* @param iconView The icon that shows the conversation.
*/
@@ -293,19 +298,20 @@
iconDescriptor: StatusBarIcon
): Boolean {
val usedInSensitiveContext =
- iconView === entry.icons.shelfIcon || iconView === entry.icons.aodIcon
+ iconView === entry.icons.shelfIcon || iconView === entry.icons.aodIcon
val isSmallIcon = iconDescriptor.icon.equals(entry.sbn.notification.smallIcon)
- return isImportantConversation(entry) && !isSmallIcon &&
- (!usedInSensitiveContext || !entry.isSensitive)
+ return isImportantConversation(entry) &&
+ !isSmallIcon &&
+ (!usedInSensitiveContext || !entry.isSensitive)
}
private fun isImportantConversation(entry: NotificationEntry): Boolean {
// Also verify that the Notification is MessagingStyle, since we're going to access
// MessagingStyle-specific data (EXTRA_MESSAGES, EXTRA_MESSAGING_PERSON).
return entry.ranking.channel != null &&
- entry.ranking.channel.isImportantConversation &&
- entry.sbn.notification.isStyle(MessagingStyle::class.java) &&
- entry.key !in unimportantConversationKeys
+ entry.ranking.channel.isImportantConversation &&
+ entry.sbn.notification.isStyle(MessagingStyle::class.java) &&
+ entry.key !in unimportantConversationKeys
}
override fun setUnimportantConversations(keys: Collection<String>) {
@@ -323,8 +329,8 @@
interface ConversationIconManager {
/**
* Sets the complete current set of notification keys which should (for the purposes of icon
- * presentation) be considered unimportant. This tells the icon manager to remove the avatar
- * of a group from which the priority notification has been removed.
+ * presentation) be considered unimportant. This tells the icon manager to remove the avatar of
+ * a group from which the priority notification has been removed.
*/
fun setUnimportantConversations(keys: Collection<String>)
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
index bae5baa..5551ab4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
@@ -22,6 +22,8 @@
import android.graphics.Path
import android.graphics.RectF
import android.util.AttributeSet
+import android.util.Log
+import com.android.systemui.Flags
import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.row.ExpandableView
@@ -87,4 +89,63 @@
) {
// No animation, it doesn't need it, this would be local
}
+
+ override fun setVisibility(visibility: Int) {
+ if (Flags.bindKeyguardMediaVisibility()) {
+ if (isVisibilityValid(visibility)) {
+ super.setVisibility(visibility)
+ }
+ } else {
+ super.setVisibility(visibility)
+ }
+
+ assertMediaContainerVisibility(visibility)
+ }
+
+ /**
+ * visibility should be aligned with MediaContainerView visibility on the keyguard.
+ */
+ private fun isVisibilityValid(visibility: Int): Boolean {
+ val currentViewState = viewState as? MediaContainerViewState ?: return true
+ val shouldBeGone = !currentViewState.shouldBeVisible
+ return if (shouldBeGone) visibility == GONE else visibility != GONE
+ }
+
+ /**
+ * b/298213983
+ * MediaContainerView's visibility is changed to VISIBLE when it should be GONE.
+ * This method check this state and logs.
+ */
+ private fun assertMediaContainerVisibility(visibility: Int) {
+ val currentViewState = viewState
+
+ if (currentViewState is MediaContainerViewState) {
+ if (!currentViewState.shouldBeVisible && visibility == VISIBLE) {
+ Log.wtf("MediaContainerView", "MediaContainerView should be GONE " +
+ "but its visibility changed to VISIBLE")
+ }
+ }
+ }
+
+ fun setKeyguardVisibility(isVisible: Boolean) {
+ val currentViewState = viewState
+ if (currentViewState is MediaContainerViewState) {
+ currentViewState.shouldBeVisible = isVisible
+ }
+
+ visibility = if (isVisible) VISIBLE else GONE
+ }
+
+ override fun createExpandableViewState(): ExpandableViewState = MediaContainerViewState()
+
+ class MediaContainerViewState : ExpandableViewState() {
+ var shouldBeVisible: Boolean = false
+
+ override fun copyFrom(viewState: ViewState) {
+ super.copyFrom(viewState)
+ if (viewState is MediaContainerViewState) {
+ shouldBeVisible = viewState.shouldBeVisible
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 634de7a..1ef9a8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -853,7 +853,7 @@
}
}
if (row.isHeadsUpAnimatingAway()) {
- if (NotificationsImprovedHunAnimation.isEnabled()) {
+ if (NotificationsImprovedHunAnimation.isEnabled() && !ambientState.isDozing()) {
if (shouldHunAppearFromBottom(ambientState, childState)) {
// move to the bottom of the screen
childState.setYTranslation(
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 b772158..db15144 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -1375,7 +1375,10 @@
|| !mKeyguardStateController.canDismissLockScreen()
|| mKeyguardViewMediator.isAnySimPinSecure()
|| (mQsController.getExpanded() && trackingTouch)
- || mShadeSurface.getBarState() == StatusBarState.SHADE_LOCKED) {
+ || mShadeSurface.getBarState() == StatusBarState.SHADE_LOCKED
+ // This last one causes a race condition when the shade resets. Don't send a 0
+ // and let StatusBarStateController process a keyguard state change instead
+ || 1f - fraction == 0f) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 4fd33ba..5610ed9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -55,6 +55,7 @@
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.power.domain.interactor.PowerInteractor;
@@ -138,7 +139,7 @@
Context context,
@DisplayId int displayId,
Handler mainThreadHandler,
- Executor uiBgExecutor,
+ @Background Executor uiBgExecutor,
NotificationVisibilityProvider visibilityProvider,
HeadsUpManager headsUpManager,
ActivityStarter activityStarter,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
index f73d089..3e3ea85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
@@ -315,8 +315,8 @@
// TTL for satellite polling is one hour
const val POLLING_INTERVAL_MS: Long = 1000 * 60 * 60
- // Let the system boot up (5s) and stabilize before we check for system support
- const val MIN_UPTIME: Long = 1000 * 5
+ // Let the system boot up and stabilize before we check for system support
+ const val MIN_UPTIME: Long = 1000 * 60
private const val TAG = "DeviceBasedSatelliteRepo"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
index a078dd5..2ad4d36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
@@ -23,6 +23,7 @@
import android.content.Context
import android.content.Intent
import android.net.Uri
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.res.R
import com.android.systemui.util.concurrency.DelayableExecutor
import javax.inject.Inject
@@ -34,7 +35,7 @@
class BatteryStateNotifier @Inject constructor(
val controller: BatteryController,
val noMan: NotificationManager,
- val delayableExecutor: DelayableExecutor,
+ @Background val delayableExecutor: DelayableExecutor,
val context: Context
) : BatteryController.BatteryStateChangeCallback {
var stateUnknown = false
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
index 6124f63..2cad844 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
@@ -124,15 +124,6 @@
}
/**
- * Provide a Background-Thread Executor by default.
- */
- @Provides
- @SysUISingleton
- public static Executor provideExecutor(@Background Looper looper) {
- return new ExecutorImpl(looper);
- }
-
- /**
* Provide a BroadcastRunning Executor (for sending and receiving broadcasts).
*/
@Provides
@@ -174,15 +165,6 @@
}
/**
- * Provide a Background-Thread Executor by default.
- */
- @Provides
- @SysUISingleton
- public static DelayableExecutor provideDelayableExecutor(@Background Looper looper) {
- return new ExecutorImpl(looper);
- }
-
- /**
* Provide a Background-Thread Executor.
*/
@Provides
@@ -193,15 +175,6 @@
}
/**
- * Provide a Background-Thread Executor by default.
- */
- @Provides
- @SysUISingleton
- public static RepeatableExecutor provideRepeatableExecutor(@Background DelayableExecutor exec) {
- return new RepeatableExecutorImpl(exec);
- }
-
- /**
* Provide a Background-Thread Executor.
*/
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
index 9b72eb7..5979f3e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
@@ -28,6 +28,7 @@
import androidx.annotation.NonNull;
import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.SystemClock;
@@ -94,10 +95,12 @@
}
};
+ // TODO: b/326449074 - Ensure the DelayableExecutor is on the correct thread, and update the
+ // qualifier (to @Main) or name (to bgExecutor) to be consistent with that.
@Inject
public PersistentConnectionManager(
SystemClock clock,
- DelayableExecutor mainExecutor,
+ @Background DelayableExecutor mainExecutor,
DumpManager dumpManager,
@Named(DUMPSYS_NAME) String dumpsysName,
@Named(SERVICE_CONNECTION) ObservableServiceConnection<T> serviceConnection,
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 139d190..65dede8 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -25,6 +25,7 @@
import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+import static com.android.server.notification.Flags.screenshareNotificationHiding;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
@@ -69,6 +70,7 @@
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.wm.shell.bubbles.Bubble;
import com.android.wm.shell.bubbles.BubbleEntry;
@@ -102,6 +104,7 @@
private final NotificationVisibilityProvider mVisibilityProvider;
private final VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider;
private final NotificationLockscreenUserManager mNotifUserManager;
+ private final SensitiveNotificationProtectionController mSensitiveNotifProtectionController;
private final CommonNotifCollection mCommonNotifCollection;
private final NotifPipeline mNotifPipeline;
private final NotifPipelineFlags mNotifPipelineFlags;
@@ -111,6 +114,7 @@
// TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
private final List<NotifCallback> mCallbacks = new ArrayList<>();
private final StatusBarWindowCallback mStatusBarWindowCallback;
+ private final Runnable mSensitiveStateChangedListener;
private boolean mPanelExpanded;
/**
@@ -130,6 +134,7 @@
VisualInterruptionDecisionProvider visualInterruptionDecisionProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
+ SensitiveNotificationProtectionController sensitiveNotificationProtectionController,
CommonNotifCollection notifCollection,
NotifPipeline notifPipeline,
SysUiState sysUiState,
@@ -149,6 +154,7 @@
visualInterruptionDecisionProvider,
zenModeController,
notifUserManager,
+ sensitiveNotificationProtectionController,
notifCollection,
notifPipeline,
sysUiState,
@@ -173,6 +179,7 @@
VisualInterruptionDecisionProvider visualInterruptionDecisionProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
+ SensitiveNotificationProtectionController sensitiveNotificationProtectionController,
CommonNotifCollection notifCollection,
NotifPipeline notifPipeline,
SysUiState sysUiState,
@@ -188,6 +195,7 @@
mVisibilityProvider = visibilityProvider;
mVisualInterruptionDecisionProvider = visualInterruptionDecisionProvider;
mNotifUserManager = notifUserManager;
+ mSensitiveNotifProtectionController = sensitiveNotificationProtectionController;
mCommonNotifCollection = notifCollection;
mNotifPipeline = notifPipeline;
mNotifPipelineFlags = notifPipelineFlags;
@@ -251,6 +259,22 @@
};
notificationShadeWindowController.registerCallback(mStatusBarWindowCallback);
+ mSensitiveStateChangedListener = new Runnable() {
+ @Override
+ public void run() {
+ if (!screenshareNotificationHiding()) {
+ return;
+ }
+ bubbles.onSensitiveNotificationProtectionStateChanged(
+ mSensitiveNotifProtectionController.isSensitiveStateActive());
+ }
+ };
+
+ if (screenshareNotificationHiding()) {
+ mSensitiveNotifProtectionController
+ .registerSensitiveStateListener(mSensitiveStateChangedListener);
+ }
+
mSysuiProxy = new Bubbles.SysuiProxy() {
@Override
public void isNotificationPanelExpand(Consumer<Boolean> callback) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
index 1205dce..711f90f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
@@ -23,7 +23,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
-import com.android.systemui.keyguard.ui.viewmodel.AodAlphaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.res.R
import com.android.systemui.statusbar.KeyguardIndicationController
@@ -40,7 +39,6 @@
class DefaultIndicationAreaSectionTest : SysuiTestCase() {
@Mock private lateinit var keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel
- @Mock private lateinit var aodAlphaViewModel: AodAlphaViewModel
@Mock private lateinit var indicationController: KeyguardIndicationController
private lateinit var underTest: DefaultIndicationAreaSection
@@ -52,7 +50,6 @@
DefaultIndicationAreaSection(
context,
keyguardIndicationAreaViewModel,
- aodAlphaViewModel,
indicationController,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
index 14fe182..7f3d79f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
@@ -27,12 +27,16 @@
import android.media.session.MediaController
import android.media.session.MediaController.PlaybackInfo
import android.media.session.MediaSession
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
+import com.android.settingslib.flags.Flags
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.media.PhoneMediaDevice
@@ -83,6 +87,7 @@
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
public class MediaDeviceManagerTest : SysuiTestCase() {
+ @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
private lateinit var manager: MediaDeviceManager
@Mock private lateinit var controllerFactory: MediaControllerFactory
@@ -668,7 +673,28 @@
}
@Test
- fun onBroadcastStarted_currentMediaDeviceDataIsBroadcasting() {
+ fun onBroadcastStarted_flagOff_currentMediaDeviceDataIsBroadcasting() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ val broadcastCallback = setupBroadcastCallback()
+ setupLeAudioConfiguration(true)
+ setupBroadcastPackage(BROADCAST_APP_NAME)
+ broadcastCallback.onBroadcastStarted(1, 1)
+
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+
+ val data = captureDeviceData(KEY)
+ assertThat(data.showBroadcastButton).isFalse()
+ assertThat(data.enabled).isTrue()
+ assertThat(data.name).isEqualTo(DEVICE_NAME)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+ @DisableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ fun onBroadcastStarted_legacy_currentMediaDeviceDataIsBroadcasting() {
val broadcastCallback = setupBroadcastCallback()
setupLeAudioConfiguration(true)
setupBroadcastPackage(BROADCAST_APP_NAME)
@@ -686,7 +712,9 @@
}
@Test
- fun onBroadcastStarted_currentMediaDeviceDataIsNotBroadcasting() {
+ @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+ @DisableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ fun onBroadcastStarted_legacy_currentMediaDeviceDataIsNotBroadcasting() {
val broadcastCallback = setupBroadcastCallback()
setupLeAudioConfiguration(true)
setupBroadcastPackage(NORMAL_APP_NAME)
@@ -703,6 +731,62 @@
}
@Test
+ @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+ @DisableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ fun onBroadcastStopped_legacy_bluetoothLeBroadcastIsDisabledAndBroadcastingButtonIsGone() {
+ val broadcastCallback = setupBroadcastCallback()
+ setupLeAudioConfiguration(false)
+ broadcastCallback.onBroadcastStopped(1, 1)
+
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+
+ val data = captureDeviceData(KEY)
+ assertThat(data.showBroadcastButton).isFalse()
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+ @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ fun onBroadcastStarted_currentMediaDeviceDataIsBroadcasting() {
+ val broadcastCallback = setupBroadcastCallback()
+ setupLeAudioConfiguration(true)
+ setupBroadcastPackage(BROADCAST_APP_NAME)
+ broadcastCallback.onBroadcastStarted(1, 1)
+
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+
+ val data = captureDeviceData(KEY)
+ assertThat(data.showBroadcastButton).isFalse()
+ assertThat(data.enabled).isFalse()
+ assertThat(data.name).isEqualTo(context.getString(R.string.audio_sharing_description))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+ @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ fun onBroadcastStarted_currentMediaDeviceDataIsNotBroadcasting() {
+ val broadcastCallback = setupBroadcastCallback()
+ setupLeAudioConfiguration(true)
+ setupBroadcastPackage(NORMAL_APP_NAME)
+ broadcastCallback.onBroadcastStarted(1, 1)
+
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+
+ val data = captureDeviceData(KEY)
+ assertThat(data.showBroadcastButton).isFalse()
+ assertThat(data.enabled).isFalse()
+ assertThat(data.name).isEqualTo(context.getString(R.string.audio_sharing_description))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+ @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
fun onBroadcastStopped_bluetoothLeBroadcastIsDisabledAndBroadcastingButtonIsGone() {
val broadcastCallback = setupBroadcastCallback()
setupLeAudioConfiguration(false)
@@ -714,6 +798,8 @@
val data = captureDeviceData(KEY)
assertThat(data.showBroadcastButton).isFalse()
+ assertThat(data.name?.equals(context.getString(R.string.audio_sharing_description)))
+ .isFalse()
}
private fun captureCallback(): LocalMediaManager.DeviceCallback {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
new file mode 100644
index 0000000..0c32470
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.screenshot
+
+import android.content.Intent
+import android.os.Process.myUserHandle
+import android.platform.test.annotations.EnableFlags
+import android.testing.AndroidTestingRunner
+import android.testing.TestableContext
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.DisplayTracker
+import com.android.systemui.shared.system.ActivityManagerWrapper
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.util.mockito.mock
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestCoroutineScheduler
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+
+@RunWith(AndroidTestingRunner::class)
+class ActionIntentExecutorTest : SysuiTestCase() {
+
+ private val scheduler = TestCoroutineScheduler()
+ private val mainDispatcher = StandardTestDispatcher(scheduler)
+ private val testScope = TestScope(mainDispatcher)
+ private val testableContext = TestableContext(mContext)
+
+ private val activityManagerWrapper = mock<ActivityManagerWrapper>()
+ private val displayTracker = mock<DisplayTracker>()
+ private val keyguardController = mock<ScreenshotKeyguardController>()
+
+ private val actionIntentExecutor =
+ ActionIntentExecutor(
+ testableContext,
+ activityManagerWrapper,
+ testScope,
+ mainDispatcher,
+ displayTracker,
+ keyguardController,
+ )
+
+ @Test
+ @EnableFlags(Flags.FLAG_SCREENSHOT_ACTION_DISMISS_SYSTEM_WINDOWS)
+ fun launchIntent_callsCloseSystemWindows() =
+ testScope.runTest {
+ val intent = Intent(Intent.ACTION_EDIT).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK }
+ val userHandle = myUserHandle()
+
+ actionIntentExecutor.launchIntent(intent, null, userHandle, false)
+ scheduler.advanceUntilIdle()
+
+ verify(activityManagerWrapper)
+ .closeSystemWindows(CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index 032ec74..774aa51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -371,7 +371,6 @@
val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
- captor.value.onBeforeUserSwitching(newID)
captor.value.onUserSwitching(newID, userSwitchingReply)
assertThat(callback.calledOnUserChanging).isEqualTo(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt
index 0b4de34..402d9aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt
@@ -18,12 +18,13 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QS
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
+import com.android.systemui.util.mockito.mock
import com.google.common.truth.Expect
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -43,13 +44,15 @@
@get:Rule val expect: Expect = Expect.create()
@Mock private lateinit var dumpManager: DumpManager
- @Mock private lateinit var qS: QS
+ private var qS: QS? = null
private lateinit var controller: LockscreenShadeQsTransitionController
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ qS = mock()
+
setTransitionDistance(TRANSITION_DISTANCE)
setTransitionDelay(TRANSITION_DELAY)
setSquishTransitionDistance(SQUISH_TRANSITION_DISTANCE)
@@ -220,7 +223,7 @@
controller.dragDownAmount = rawDragAmount
- verify(qS)
+ verify(qS!!)
.setTransitionToFullShadeProgress(
/* isTransitioningToFullShade= */ true,
/* transitionFraction= */ controller.qsTransitionFraction,
@@ -228,6 +231,15 @@
)
}
+ @Test
+ fun nullQS_onDragAmountChanged_doesNotCrash() {
+ qS = null
+
+ val rawDragAmount = 200f
+
+ controller.dragDownAmount = rawDragAmount
+ }
+
private fun setTransitionDistance(value: Int) {
overrideResource(R.dimen.lockscreen_shade_qs_transition_distance, value)
configurationController.notifyConfigurationChanged()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 91701b1..86116a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -18,6 +18,7 @@
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.plugins.qs.QS
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeLockscreenInteractor
import com.android.systemui.shade.data.repository.FakeShadeRepository
@@ -82,6 +83,8 @@
private val testScope
get() = testComponent.testScope
+ private val qsSceneAdapter = FakeQSSceneAdapter({ mock() })
+
lateinit var row: ExpandableNotificationRow
@Mock lateinit var centralSurfaces: CentralSurfaces
@@ -189,6 +192,7 @@
splitShadeStateController = ResourcesSplitShadeStateController(),
shadeLockscreenInteractorLazy = {shadeLockscreenInteractor},
naturalScrollingSettingObserver = naturalScrollingSettingObserver,
+ lazyQSSceneAdapter = { qsSceneAdapter }
)
transitionController.addCallback(transitionControllerCallback)
@@ -567,6 +571,16 @@
verify(shadeLockscreenInteractor).setKeyguardStatusBarAlpha(-1f)
}
+ @Test
+ fun nullQs_canDragDownFromAdapter() {
+ transitionController.qS = null
+
+ qsSceneAdapter.isQsFullyCollapsed = true
+ assertTrue("Can't drag down on keyguard", transitionController.canDragDown())
+ qsSceneAdapter.isQsFullyCollapsed = false
+ assertFalse("Can drag down when QS is expanded", transitionController.canDragDown())
+ }
+
private fun enableSplitShade() {
setSplitShadeEnabled(true)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
index 81d5c4d..700fb1e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
@@ -9,6 +9,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.util.mockito.mock
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -31,7 +32,7 @@
@Mock private lateinit var scrimController: ScrimController
@Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
- @Mock private lateinit var qS: QS
+ private var qS: QS? = null
@Mock private lateinit var nsslController: NotificationStackScrollLayoutController
@Mock private lateinit var dumpManager: DumpManager
@@ -40,6 +41,7 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ qS = mock()
whenever(nsslController.height).thenReturn(1800)
@@ -92,7 +94,7 @@
setDragAmount(1000f)
whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
setDragAmount(999f)
- reset(qS, scrimController, nsslController)
+ reset(qS!!, scrimController, nsslController)
setDragAmount(998f)
setDragAmount(997f)
@@ -100,8 +102,15 @@
verifyNoMoreOverScrollChanges()
}
+ @Test
+ fun qsNull_applyOverscroll_doesNotCrash() {
+ qS = null
+
+ setDragAmount(100f)
+ }
+
private fun verifyOverScrollPerformed() {
- verify(qS).setOverScrollAmount(intThat { it > 0 })
+ verify(qS!!).setOverScrollAmount(intThat { it > 0 })
verify(scrimController).setNotificationsOverScrollAmount(intThat { it > 0 })
verify(nsslController).setOverScrollAmount(intThat { it > 0 })
}
@@ -109,7 +118,7 @@
private fun verifyOverScrollResetToZero() {
// Might be more than once as the animator might have multiple values close to zero that
// round down to zero.
- verify(qS, atLeast(1)).setOverScrollAmount(0)
+ verify(qS!!, atLeast(1)).setOverScrollAmount(0)
verify(scrimController, atLeast(1)).setNotificationsOverScrollAmount(0)
verify(nsslController, atLeast(1)).setOverScrollAmount(0)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
index 8b99811..a12806b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
@@ -167,6 +167,20 @@
}
@Test
+ fun testUpdateIcons_sensitiveImportantConversation() {
+ val entry =
+ notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false)
+ entry?.setSensitive(true, true)
+ entry?.channel?.isImportantConversation = true
+ entry?.let { iconManager.createIcons(it) }
+ // Updating the icons after creation shouldn't break anything
+ entry?.let { iconManager.updateIcons(it) }
+ assertThat(entry?.icons?.statusBarIcon?.sourceIcon).isEqualTo(shortcutIc)
+ assertThat(entry?.icons?.shelfIcon?.sourceIcon).isEqualTo(smallIc)
+ assertThat(entry?.icons?.aodIcon?.sourceIcon).isEqualTo(smallIc)
+ }
+
+ @Test
fun testUpdateIcons_sensitivityChange() {
val entry =
notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 91a9da3..995da81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -222,6 +222,26 @@
}
@Test
+ @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
+ fun resetViewStates_hunAnimatingAwayWhileDozing_yTranslationIsInset() {
+ whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
+
+ ambientState.isDozing = true
+
+ resetViewStates_hunYTranslationIs(stackScrollAlgorithm.mHeadsUpInset)
+ }
+
+ @Test
+ @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
+ fun resetViewStates_hunAnimatingAwayWhileDozing_hasStackMargin_changesHunYTranslation() {
+ whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
+
+ ambientState.isDozing = true
+
+ resetViewStates_stackMargin_changesHunYTranslation()
+ }
+
+ @Test
fun resetViewStates_hunsOverlapping_bottomHunClipped() {
val topHun = mockExpandableNotificationRow()
val bottomHun = mockExpandableNotificationRow()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt
index 203096a..08b49f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt
@@ -166,4 +166,28 @@
assertThat(config.color).isEqualTo(expectedColor)
}
}
+
+ @Test
+ fun play_initializesShader() {
+ val expectedNoiseOffset = floatArrayOf(0.1f, 0.2f, 0.3f)
+ val config =
+ TurbulenceNoiseAnimationConfig(
+ noiseOffsetX = expectedNoiseOffset[0],
+ noiseOffsetY = expectedNoiseOffset[1],
+ noiseOffsetZ = expectedNoiseOffset[2]
+ )
+ val turbulenceNoiseView = TurbulenceNoiseView(context, null)
+ val turbulenceNoiseController = TurbulenceNoiseController(turbulenceNoiseView)
+
+ fakeExecutor.execute {
+ turbulenceNoiseController.play(SIMPLEX_NOISE, config)
+
+ assertThat(turbulenceNoiseView.noiseConfig).isNotNull()
+ val shader = turbulenceNoiseView.turbulenceNoiseShader!!
+ assertThat(shader).isNotNull()
+ assertThat(shader.noiseOffsetX).isEqualTo(expectedNoiseOffset[0])
+ assertThat(shader.noiseOffsetY).isEqualTo(expectedNoiseOffset[1])
+ assertThat(shader.noiseOffsetZ).isEqualTo(expectedNoiseOffset[2])
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index b25ac24..a930860 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -26,6 +26,7 @@
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING;
import static com.google.common.truth.Truth.assertThat;
@@ -46,6 +47,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
@@ -73,6 +75,8 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.service.dreams.IDreamManager;
import android.service.notification.NotificationListenerService;
import android.service.notification.ZenModeConfig;
@@ -161,6 +165,7 @@
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
+import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository;
@@ -257,6 +262,8 @@
private NotificationShadeWindowView mNotificationShadeWindowView;
@Mock
private AuthController mAuthController;
+ @Mock
+ private SensitiveNotificationProtectionController mSensitiveNotificationProtectionController;
private SysUiState mSysUiState;
private boolean mSysUiStateBubblesExpanded;
@@ -272,6 +279,8 @@
private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor;
@Captor
private ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateControllerCallbackCaptor;
+ @Captor
+ private ArgumentCaptor<Runnable> mSensitiveStateChangedListener;
private BubblesManager mBubblesManager;
private TestableBubbleController mBubbleController;
@@ -594,6 +603,7 @@
interruptionDecisionProvider,
mZenModeController,
mLockscreenUserManager,
+ mSensitiveNotificationProtectionController,
mCommonNotifCollection,
mNotifPipeline,
mSysUiState,
@@ -2203,6 +2213,33 @@
assertThat(mBubbleController.getLayerView().isExpanded()).isFalse();
}
+ @DisableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
+ @Test
+ public void doesNotRegisterSensitiveStateListener() {
+ verifyZeroInteractions(mSensitiveNotificationProtectionController);
+ }
+
+ @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
+ @Test
+ public void registerSensitiveStateListener() {
+ verify(mSensitiveNotificationProtectionController).registerSensitiveStateListener(any());
+ }
+
+ @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
+ @Test
+ public void onSensitiveNotificationProtectionStateChanged() {
+ verify(mSensitiveNotificationProtectionController, atLeastOnce())
+ .registerSensitiveStateListener(mSensitiveStateChangedListener.capture());
+
+ when(mSensitiveNotificationProtectionController.isSensitiveStateActive()).thenReturn(true);
+ mSensitiveStateChangedListener.getValue().run();
+ verify(mBubbleController).onSensitiveNotificationProtectionStateChanged(true);
+
+ when(mSensitiveNotificationProtectionController.isSensitiveStateActive()).thenReturn(false);
+ mSensitiveStateChangedListener.getValue().run();
+ verify(mBubbleController).onSensitiveNotificationProtectionStateChanged(false);
+ }
+
/** Creates a bubble using the userId and package. */
private Bubble createBubble(int userId, String pkg) {
final UserHandle userHandle = new UserHandle(userId);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index 6af08d3..f74cf71 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -31,6 +31,7 @@
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
+import com.android.systemui.settings.userTracker
import com.android.systemui.smartspace.data.repository.smartspaceRepository
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.mock
@@ -46,6 +47,7 @@
appWidgetHost = mock(),
keyguardInteractor = keyguardInteractor,
editWidgetsActivityStarter = editWidgetsActivityStarter,
+ userTracker = userTracker,
logBuffer = logcatLogBuffer("CommunalInteractor"),
tableLogBuffer = mock(),
communalSettingsInteractor = communalSettingsInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
index b1581d1..4d902fa 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
@@ -41,6 +41,8 @@
private val _navBarPadding = MutableStateFlow<Int>(0)
val navBarPadding = _navBarPadding.asStateFlow()
+ override var isQsFullyCollapsed: Boolean = true
+
override suspend fun inflate(context: Context) {
_view.value = inflateDelegate(context)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterKosmos.kt
new file mode 100644
index 0000000..00ab0b5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterKosmos.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.ui.adapter
+
+import android.view.View
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+var Kosmos.fakeQSSceneAdapter by Kosmos.Fixture { FakeQSSceneAdapter({ mock<View>() }) }
+
+val Kosmos.qsSceneAdapter: QSSceneAdapter by Kosmos.Fixture { fakeQSSceneAdapter }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
index e5072f1..e4a3896 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
@@ -26,6 +26,7 @@
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.media.controls.ui.controller.mediaHierarchyManager
import com.android.systemui.plugins.activityStarter
+import com.android.systemui.qs.ui.adapter.qsSceneAdapter
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.domain.interactor.shadeLockscreenInteractor
@@ -61,5 +62,6 @@
splitShadeStateController = splitShadeStateController,
shadeLockscreenInteractorLazy = { shadeLockscreenInteractor },
naturalScrollingSettingObserver = naturalScrollingSettingObserver,
+ lazyQSSceneAdapter = { qsSceneAdapter }
)
}
diff --git a/services/backup/flags.aconfig b/services/backup/flags.aconfig
index 71f2b9e..e9f959f 100644
--- a/services/backup/flags.aconfig
+++ b/services/backup/flags.aconfig
@@ -35,6 +35,15 @@
}
flag {
+ name: "enable_v_to_u_restore_for_system_components_in_allowlist"
+ namespace: "onboarding"
+ description: "Enables system components to opt in to support restore in V to U downgrade "
+ "scenario without opting in for restoreAnyVersion."
+ bug: "324233962"
+ is_fixed_read_only: true
+}
+
+flag {
name: "enable_increase_datatypes_for_agent_logging"
namespace: "onboarding"
description: "Increase the number of a supported datatypes that an agent can define for its "
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index 9f0deea..6e98e68 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -177,6 +177,10 @@
return mHasMetadata;
}
+ public int getSourceSdk() {
+ return mStoredSdkVersion;
+ }
+
public Metadata getRestoredMetadata(String packageName) {
if (mRestoredSignatures == null) {
Slog.w(TAG, "getRestoredMetadata() before metadata read!");
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index d85dd87..e666442 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -44,6 +44,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
+import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.os.ParcelFileDescriptor;
@@ -51,6 +52,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.EventLog;
import android.util.Slog;
@@ -82,6 +84,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -158,6 +161,12 @@
// When finished call listener
private final OnTaskFinishedListener mListener;
+ // List of packages that support V-> U downgrade but do not have RestoreAnyVersion set to true.
+ private List<String> mVToUAllowlist;
+
+ // List of packages that have RestoreAnyVersion set to true but do not support V-> U downgrade.
+ private List<String> mVToUDenylist;
+
// Key/value: bookkeeping about staged data and files for agent access
private File mBackupDataName;
private File mStageName;
@@ -172,7 +181,8 @@
@VisibleForTesting
PerformUnifiedRestoreTask(
UserBackupManagerService backupManagerService,
- TransportConnection transportConnection) {
+ TransportConnection transportConnection,
+ String vToUAllowlist, String vToUDenyList) {
mListener = null;
mAgentTimeoutParameters = null;
mOperationStorage = null;
@@ -183,6 +193,8 @@
mBackupEligibilityRules = null;
this.backupManagerService = backupManagerService;
mBackupManagerMonitorEventSender = new BackupManagerMonitorEventSender(/* monitor= */ null);
+ mVToUAllowlist = createVToUList(vToUAllowlist);
+ mVToUDenylist = createVToUList(vToUDenyList);
}
// This task can assume that the wakelock is properly held for it and doesn't have to worry
@@ -223,6 +235,18 @@
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
mBackupEligibilityRules = backupEligibilityRules;
+ mVToUAllowlist =
+ createVToUList(
+ Settings.Secure.getStringForUser(
+ backupManagerService.getContext().getContentResolver(),
+ Settings.Secure.V_TO_U_RESTORE_ALLOWLIST,
+ mUserId));
+ mVToUDenylist =
+ createVToUList(
+ Settings.Secure.getStringForUser(
+ backupManagerService.getContext().getContentResolver(),
+ Settings.Secure.V_TO_U_RESTORE_DENYLIST,
+ mUserId));
if (targetPackage != null) {
// Single package restore
@@ -636,60 +660,29 @@
// Data is from a "newer" version of the app than we have currently
// installed. If the app has not declared that it is prepared to
// handle this case, we do not attempt the restore.
- if ((mCurrentPackage.applicationInfo.flags
- & ApplicationInfo.FLAG_RESTORE_ANY_VERSION)
- == 0) {
- String message =
- "Source version "
- + metaInfo.versionCode
- + " > installed version "
- + mCurrentPackage.getLongVersionCode();
- Slog.w(TAG, "Package " + pkgName + ": " + message);
- Bundle monitoringExtras =
- mBackupManagerMonitorEventSender.putMonitoringExtra(
- null,
- BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION,
- metaInfo.versionCode);
- monitoringExtras =
- mBackupManagerMonitorEventSender.putMonitoringExtra(
- monitoringExtras,
- BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY,
- false);
- monitoringExtras = addRestoreOperationTypeToEvent(monitoringExtras);
- mBackupManagerMonitorEventSender.monitorEvent(
- BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER,
- mCurrentPackage,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
- monitoringExtras);
- EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, message);
- nextState = UnifiedRestoreState.RUNNING_QUEUE;
- return;
- } else {
- if (DEBUG) {
- Slog.v(
- TAG,
- "Source version "
- + metaInfo.versionCode
- + " > installed version "
- + mCurrentPackage.getLongVersionCode()
- + " but restoreAnyVersion");
+ if (mIsSystemRestore
+ && isVToUDowngrade(mPmAgent.getSourceSdk(), android.os.Build.VERSION.SDK_INT)) {
+ if (isPackageEligibleForVToURestore(mCurrentPackage)) {
+ Slog.i(TAG, "Package " + pkgName
+ + " is eligible for V to U downgrade scenario");
+ } else {
+ String message = "Package not eligible for V to U downgrade scenario";
+ Slog.i(TAG, pkgName + " : " + message);
+ EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, message);
+ nextState = UnifiedRestoreState.RUNNING_QUEUE;
+ return;
}
- Bundle monitoringExtras =
- mBackupManagerMonitorEventSender.putMonitoringExtra(
- null,
- BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION,
- metaInfo.versionCode);
- monitoringExtras =
- mBackupManagerMonitorEventSender.putMonitoringExtra(
- monitoringExtras,
- BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY,
- true);
- monitoringExtras = addRestoreOperationTypeToEvent(monitoringExtras);
- mBackupManagerMonitorEventSender.monitorEvent(
- BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER,
- mCurrentPackage,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
- monitoringExtras);
+ } else {
+ if ((mCurrentPackage.applicationInfo.flags
+ & ApplicationInfo.FLAG_RESTORE_ANY_VERSION)
+ == 0) {
+ // Downgrade scenario with RestoreAnyVersion flag off
+ logDowngradeScenario(/* isRestoreAnyVersion */ false, metaInfo);
+ nextState = UnifiedRestoreState.RUNNING_QUEUE;
+ return;
+ } else {
+ logDowngradeScenario(/* isRestoreAnyVersion */ true, metaInfo);
+ }
}
}
@@ -1673,4 +1666,86 @@
return mBackupManagerMonitorEventSender.putMonitoringExtra(
extras, BackupManagerMonitor.EXTRA_LOG_OPERATION_TYPE, RESTORE);
}
+
+ // checks the sdk of the target/source device for a B&R operation.
+ // system components can opt in/out of V->U restore via allowlists. All other apps are
+ // not impacted
+ @SuppressWarnings("AndroidFrameworkCompatChange")
+ @VisibleForTesting
+ protected boolean isVToUDowngrade(int sourceSdk, int targetSdk) {
+ // We assume that if the source sdk is greater than U then the source is V.
+ return Flags.enableVToURestoreForSystemComponentsInAllowlist()
+ && (sourceSdk > Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ && (targetSdk == Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+ }
+
+ @VisibleForTesting
+ protected List<String> createVToUList(@Nullable String listString) {
+ // The allowlist/denylist is stored as a comma-separated list of package names
+ List<String> list = new ArrayList<>();
+ if (listString != null) {
+ list = Arrays.asList(listString.split(","));
+ }
+ return list;
+ }
+
+ @VisibleForTesting
+ protected boolean isPackageEligibleForVToURestore(PackageInfo mCurrentPackage) {
+ // A package is eligible for V to U downgrade restore if either:
+ // - The package has restoreAnyVersion set to false and is part of the V to U allowlist
+ // (and not in the denylist)
+ // - The package has restoreAnyVersion set to true and is not part of the denylist
+ if (mVToUDenylist.contains(mCurrentPackage.packageName)){
+ return false;
+ } else if ((mCurrentPackage.applicationInfo.flags
+ & ApplicationInfo.FLAG_RESTORE_ANY_VERSION)
+ == 0) {
+ // package has restoreAnyVersion set to false
+ return mVToUAllowlist.contains(mCurrentPackage.packageName);
+ } else {
+ // package has restoreAnyVersion set to true and is nor in denylist
+ return true;
+ }
+ }
+
+ private void logDowngradeScenario(boolean isRestoreAnyVersion, Metadata metaInfo) {
+ Bundle monitoringExtras =
+ mBackupManagerMonitorEventSender.putMonitoringExtra(
+ null,
+ BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION,
+ metaInfo.versionCode);
+ String message;
+ if (isRestoreAnyVersion) {
+ monitoringExtras =
+ mBackupManagerMonitorEventSender.putMonitoringExtra(
+ monitoringExtras,
+ BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY,
+ true);
+ message = "Source version "
+ + metaInfo.versionCode
+ + " > installed version "
+ + mCurrentPackage.getLongVersionCode()
+ + " but restoreAnyVersion";
+ } else {
+ monitoringExtras =
+ mBackupManagerMonitorEventSender.putMonitoringExtra(
+ monitoringExtras,
+ BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY,
+ false);
+ message = "Source version "
+ + metaInfo.versionCode
+ + " > installed version "
+ + mCurrentPackage.getLongVersionCode();
+ EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, mCurrentPackage.packageName,
+ message);
+ }
+ Slog.i(TAG, "Package " + mCurrentPackage.packageName + ": " + message);
+ monitoringExtras = addRestoreOperationTypeToEvent(monitoringExtras);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
+ }
+
}
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 767f54d..966fe5b 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -120,6 +120,7 @@
static final String[] sDeviceConfigAconfigScopes = new String[] {
"accessibility",
"android_core_networking",
+ "android_stylus",
"aoc",
"app_widgets",
"arc_next",
diff --git a/services/core/java/com/android/server/display/BrightnessThrottler.java b/services/core/java/com/android/server/display/BrightnessThrottler.java
index bba5ba3..631e751 100644
--- a/services/core/java/com/android/server/display/BrightnessThrottler.java
+++ b/services/core/java/com/android/server/display/BrightnessThrottler.java
@@ -37,9 +37,11 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.utils.DebugUtils;
import com.android.server.display.utils.DeviceConfigParsingUtils;
+import com.android.server.display.utils.SensorUtils;
import java.io.PrintWriter;
import java.util.HashMap;
@@ -79,7 +81,7 @@
// Maps the throttling ID to the data. Sourced from DisplayDeviceConfig.
@NonNull
- private HashMap<String, ThermalBrightnessThrottlingData> mDdcThermalThrottlingDataMap;
+ private Map<String, ThermalBrightnessThrottlingData> mDdcThermalThrottlingDataMap;
// Current throttling data being used.
// Null if we do not support throttling.
@@ -97,6 +99,10 @@
// The brightness throttling configuration that should be used.
private String mThermalBrightnessThrottlingDataId;
+ // Temperature Sensor to be monitored for throttling.
+ @NonNull
+ private SensorData mTempSensor;
+
// This is a collection of brightness throttling data that has been written as overrides from
// the DeviceConfig. This will always take priority over the display device config data.
// We need to store the data for every display device, so we do not need to update this each
@@ -121,17 +127,19 @@
BrightnessThrottler(Handler handler, Runnable throttlingChangeCallback, String uniqueDisplayId,
String throttlingDataId,
- @NonNull HashMap<String, ThermalBrightnessThrottlingData>
- thermalBrightnessThrottlingDataMap) {
- this(new Injector(), handler, handler, throttlingChangeCallback,
- uniqueDisplayId, throttlingDataId, thermalBrightnessThrottlingDataMap);
+ @NonNull DisplayDeviceConfig displayDeviceConfig) {
+ this(new Injector(), handler, handler, throttlingChangeCallback, uniqueDisplayId,
+ throttlingDataId,
+ displayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId(),
+ displayDeviceConfig.getTempSensor());
}
@VisibleForTesting
BrightnessThrottler(Injector injector, Handler handler, Handler deviceConfigHandler,
Runnable throttlingChangeCallback, String uniqueDisplayId, String throttlingDataId,
- @NonNull HashMap<String, ThermalBrightnessThrottlingData>
- thermalBrightnessThrottlingDataMap) {
+ @NonNull Map<String, ThermalBrightnessThrottlingData>
+ thermalBrightnessThrottlingDataMap,
+ @NonNull SensorData tempSensor) {
mInjector = injector;
mHandler = handler;
@@ -147,7 +155,7 @@
mDdcThermalThrottlingDataMap = thermalBrightnessThrottlingDataMap;
loadThermalBrightnessThrottlingDataFromDeviceConfig();
loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(mDdcThermalThrottlingDataMap,
- mThermalBrightnessThrottlingDataId, mUniqueDisplayId);
+ tempSensor, mThermalBrightnessThrottlingDataId, mUniqueDisplayId);
}
boolean deviceSupportsThrottling() {
@@ -180,12 +188,14 @@
}
void loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
- HashMap<String, ThermalBrightnessThrottlingData> ddcThrottlingDataMap,
+ Map<String, ThermalBrightnessThrottlingData> ddcThrottlingDataMap,
+ SensorData tempSensor,
String brightnessThrottlingDataId,
String uniqueDisplayId) {
mDdcThermalThrottlingDataMap = ddcThrottlingDataMap;
mThermalBrightnessThrottlingDataId = brightnessThrottlingDataId;
mUniqueDisplayId = uniqueDisplayId;
+ mTempSensor = tempSensor;
resetThermalThrottlingData();
}
@@ -310,7 +320,7 @@
}
if (deviceSupportsThrottling()) {
- mSkinThermalStatusObserver.startObserving();
+ mSkinThermalStatusObserver.startObserving(mTempSensor);
}
}
@@ -357,6 +367,7 @@
private final class SkinThermalStatusObserver extends IThermalEventListener.Stub {
private final Injector mInjector;
private final Handler mHandler;
+ private SensorData mObserverTempSensor;
private IThermalService mThermalService;
private boolean mStarted;
@@ -371,28 +382,51 @@
if (DEBUG) {
Slog.d(TAG, "New thermal throttling status = " + temp.getStatus());
}
+
+ if (mObserverTempSensor.name != null
+ && !mObserverTempSensor.name.equals(temp.getName())) {
+ Slog.i(TAG, "Skipping thermal throttling notification as monitored sensor: "
+ + mObserverTempSensor.name
+ + " != notified sensor: "
+ + temp.getName());
+ return;
+ }
mHandler.post(() -> {
final @Temperature.ThrottlingStatus int status = temp.getStatus();
thermalStatusChanged(status);
});
}
- void startObserving() {
- if (mStarted) {
+ void startObserving(SensorData tempSensor) {
+ if (!mStarted || mObserverTempSensor == null) {
+ mObserverTempSensor = tempSensor;
+ registerThermalListener();
+ return;
+ }
+
+ String curType = mObserverTempSensor.type;
+ mObserverTempSensor = tempSensor;
+ if (curType.equals(tempSensor.type)) {
if (DEBUG) {
Slog.d(TAG, "Thermal status observer already started");
}
return;
}
+ stopObserving();
+ registerThermalListener();
+ }
+
+ void registerThermalListener() {
mThermalService = mInjector.getThermalService();
if (mThermalService == null) {
Slog.e(TAG, "Could not observe thermal status. Service not available");
return;
}
+ int temperatureType = SensorUtils.getSensorTemperatureType(mObserverTempSensor);
try {
// We get a callback immediately upon registering so there's no need to query
// for the current value.
- mThermalService.registerThermalEventListenerWithType(this, Temperature.TYPE_SKIN);
+ mThermalService.registerThermalEventListenerWithType(this, temperatureType);
mStarted = true;
} catch (RemoteException e) {
Slog.e(TAG, "Failed to register thermal status listener", e);
@@ -418,6 +452,7 @@
void dump(PrintWriter writer) {
writer.println(" SkinThermalStatusObserver:");
writer.println(" mStarted: " + mStarted);
+ writer.println(" mObserverTempSensor: " + mObserverTempSensor);
if (mThermalService != null) {
writer.println(" ThermalService available");
} else {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index d1374a5..9b2dcc5 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -384,6 +384,10 @@
* </point>
* </supportedModes>
* </proxSensor>
+ * <tempSensor>
+ * <type>DISPLAY</type>
+ * <name>VIRTUAL-SKIN-DISPLAY</name>
+ * </tempSensor>
*
* <ambientLightHorizonLong>10001</ambientLightHorizonLong>
* <ambientLightHorizonShort>2001</ambientLightHorizonShort>
@@ -625,6 +629,12 @@
@Nullable
private SensorData mProximitySensor;
+ // The details of the temperature sensor associated with this display.
+ // Throttling will be based on thermal status of this sensor.
+ // For empty values default back to sensor of TYPE_SKIN.
+ @NonNull
+ private SensorData mTempSensor;
+
private final List<RefreshRateLimitation> mRefreshRateLimitations =
new ArrayList<>(2 /*initialCapacity*/);
@@ -821,10 +831,10 @@
private String mLowBlockingZoneThermalMapId = null;
private String mHighBlockingZoneThermalMapId = null;
- private final HashMap<String, ThermalBrightnessThrottlingData>
+ private final Map<String, ThermalBrightnessThrottlingData>
mThermalBrightnessThrottlingDataMapByThrottlingId = new HashMap<>();
- private final HashMap<String, PowerThrottlingData>
+ private final Map<String, PowerThrottlingData>
mPowerThrottlingDataMapByThrottlingId = new HashMap<>();
private final Map<String, SparseArray<SurfaceControl.RefreshRateRange>>
@@ -1489,6 +1499,13 @@
return mProximitySensor;
}
+ /**
+ * @return temperature sensor data associated with the display.
+ */
+ public SensorData getTempSensor() {
+ return mTempSensor;
+ }
+
boolean isAutoBrightnessAvailable() {
return mAutoBrightnessAvailable;
}
@@ -1539,7 +1556,7 @@
/**
* @return brightness throttling configuration data for this display, for each throttling id.
*/
- public HashMap<String, ThermalBrightnessThrottlingData>
+ public Map<String, ThermalBrightnessThrottlingData>
getThermalBrightnessThrottlingDataMapByThrottlingId() {
return mThermalBrightnessThrottlingDataMapByThrottlingId;
}
@@ -1558,7 +1575,7 @@
/**
* @return power throttling configuration data for this display, for each throttling id.
**/
- public HashMap<String, PowerThrottlingData>
+ public Map<String, PowerThrottlingData>
getPowerThrottlingDataMapByThrottlingId() {
return mPowerThrottlingDataMapByThrottlingId;
}
@@ -1871,6 +1888,7 @@
+ "mAmbientLightSensor=" + mAmbientLightSensor
+ ", mScreenOffBrightnessSensor=" + mScreenOffBrightnessSensor
+ ", mProximitySensor=" + mProximitySensor
+ + ", mTempSensor=" + mTempSensor
+ ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
+ ", mDensityMapping= " + mDensityMapping
+ ", mAutoBrightnessBrighteningLightDebounce= "
@@ -1972,6 +1990,7 @@
mContext.getResources());
mScreenOffBrightnessSensor = SensorData.loadScreenOffBrightnessSensorConfig(config);
mProximitySensor = SensorData.loadProxSensorConfig(config);
+ mTempSensor = SensorData.loadTempSensorConfig(mFlags, config);
loadAmbientHorizonFromDdc(config);
loadBrightnessChangeThresholds(config);
loadAutoBrightnessConfigValues(config);
@@ -1999,6 +2018,7 @@
loadBrightnessRampsFromConfigXml();
mAmbientLightSensor = SensorData.loadAmbientLightSensorConfig(mContext.getResources());
mProximitySensor = SensorData.loadSensorUnspecifiedConfig();
+ mTempSensor = SensorData.loadTempSensorUnspecifiedConfig();
loadBrightnessChangeThresholdsFromXml();
loadAutoBrightnessConfigsFromConfigXml();
loadAutoBrightnessAvailableFromConfigXml();
@@ -2026,6 +2046,7 @@
setSimpleMappingStrategyValues();
mAmbientLightSensor = SensorData.loadAmbientLightSensorConfig(mContext.getResources());
mProximitySensor = SensorData.loadSensorUnspecifiedConfig();
+ mTempSensor = SensorData.loadTempSensorUnspecifiedConfig();
loadAutoBrightnessAvailableFromConfigXml();
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index d5863a7..3965d55 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -861,6 +861,7 @@
mThermalBrightnessThrottlingDataId = thermalBrightnessThrottlingDataId;
mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
config.getThermalBrightnessThrottlingDataMapByThrottlingId(),
+ config.getTempSensor(),
mThermalBrightnessThrottlingDataId,
mUniqueDisplayId);
}
@@ -923,6 +924,7 @@
mBrightnessRangeController.loadFromConfig(hbmMetadata, token, info, mDisplayDeviceConfig);
mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId(),
+ mDisplayDeviceConfig.getTempSensor(),
mThermalBrightnessThrottlingDataId, mUniqueDisplayId);
}
@@ -1996,7 +1998,7 @@
postBrightnessChangeRunnable();
}, mUniqueDisplayId,
mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId,
- ddConfig.getThermalBrightnessThrottlingDataMapByThrottlingId());
+ ddConfig);
}
private void blockScreenOn() {
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index bc5fcb4..18e8fab 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -38,6 +38,7 @@
import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.feature.DisplayManagerFlags;
@@ -336,5 +337,10 @@
public float getBrightnessWearBedtimeModeCap() {
return mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode();
}
+
+ @NonNull
+ public SensorData getTempSensor() {
+ return mDisplayDeviceConfig.getTempSensor();
+ }
}
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
index 944a8a6..4498258 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
@@ -35,8 +35,10 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.utils.DeviceConfigParsingUtils;
+import com.android.server.display.utils.SensorUtils;
import java.io.PrintWriter;
import java.util.List;
@@ -49,9 +51,8 @@
BrightnessClamper<BrightnessThermalClamper.ThermalData> {
private static final String TAG = "BrightnessThermalClamper";
-
- @Nullable
- private final IThermalService mThermalService;
+ @NonNull
+ private final ThermalStatusObserver mThermalStatusObserver;
@NonNull
private final DeviceConfigParameterProvider mConfigParameterProvider;
// data from DeviceConfig, for all displays, for all dataSets
@@ -66,7 +67,6 @@
// otherwise mDataFromDeviceConfig
@Nullable
private ThermalBrightnessThrottlingData mThermalThrottlingDataActive = null;
- private boolean mStarted = false;
@Nullable
private String mUniqueDisplayId = null;
@Nullable
@@ -74,14 +74,6 @@
@Temperature.ThrottlingStatus
private int mThrottlingStatus = Temperature.THROTTLING_NONE;
- private final IThermalEventListener mThermalEventListener = new IThermalEventListener.Stub() {
- @Override
- public void notifyThrottling(Temperature temperature) {
- @Temperature.ThrottlingStatus int status = temperature.getStatus();
- mHandler.post(() -> thermalStatusChanged(status));
- }
- };
-
private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> {
try {
int status = DeviceConfigParsingUtils.parseThermalStatus(key);
@@ -105,12 +97,11 @@
BrightnessThermalClamper(Injector injector, Handler handler,
ClamperChangeListener listener, ThermalData thermalData) {
super(handler, listener);
- mThermalService = injector.getThermalService();
mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
+ mThermalStatusObserver = new ThermalStatusObserver(injector, handler);
mHandler.post(() -> {
setDisplayData(thermalData);
loadOverrideData();
- start();
});
}
@@ -139,32 +130,19 @@
@Override
void stop() {
- if (!mStarted) {
- return;
- }
- try {
- mThermalService.unregisterThermalEventListener(mThermalEventListener);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to unregister thermal status listener", e);
- }
- mStarted = false;
+ mThermalStatusObserver.stopObserving();
}
@Override
void dump(PrintWriter writer) {
writer.println("BrightnessThermalClamper:");
- writer.println(" mStarted: " + mStarted);
- if (mThermalService != null) {
- writer.println(" ThermalService available");
- } else {
- writer.println(" ThermalService not available");
- }
writer.println(" mThrottlingStatus: " + mThrottlingStatus);
writer.println(" mUniqueDisplayId: " + mUniqueDisplayId);
writer.println(" mDataId: " + mDataId);
writer.println(" mDataOverride: " + mThermalThrottlingDataOverride);
writer.println(" mDataFromDeviceConfig: " + mThermalThrottlingDataFromDeviceConfig);
writer.println(" mDataActive: " + mThermalThrottlingDataActive);
+ mThermalStatusObserver.dump(writer);
super.dump(writer);
}
@@ -193,6 +171,7 @@
Slog.wtf(TAG,
"Thermal throttling data is missing for thermalThrottlingDataId=" + mDataId);
}
+ mThermalStatusObserver.registerSensor(data.getTempSensor());
}
private void recalculateBrightnessCap() {
@@ -226,19 +205,91 @@
}
}
- private void start() {
- if (mThermalService == null) {
- Slog.e(TAG, "Could not observe thermal status. Service not available");
- return;
+
+ private final class ThermalStatusObserver extends IThermalEventListener.Stub {
+ private final Injector mInjector;
+ private final Handler mHandler;
+ private IThermalService mThermalService;
+ private boolean mStarted;
+ private SensorData mObserverTempSensor;
+
+ ThermalStatusObserver(Injector injector, Handler handler) {
+ mInjector = injector;
+ mHandler = handler;
+ mStarted = false;
}
- try {
- // We get a callback immediately upon registering so there's no need to query
- // for the current value.
- mThermalService.registerThermalEventListenerWithType(mThermalEventListener,
- Temperature.TYPE_SKIN);
- mStarted = true;
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to register thermal status listener", e);
+
+ void registerSensor(SensorData tempSensor) {
+ if (!mStarted || mObserverTempSensor == null) {
+ mObserverTempSensor = tempSensor;
+ registerThermalListener();
+ return;
+ }
+
+ String curType = mObserverTempSensor.type;
+ mObserverTempSensor = tempSensor;
+ if (curType.equals(tempSensor.type)) {
+ Slog.d(TAG, "Thermal status observer already started");
+ return;
+ }
+ stopObserving();
+ registerThermalListener();
+ }
+
+ void registerThermalListener() {
+ mThermalService = mInjector.getThermalService();
+ if (mThermalService == null) {
+ Slog.e(TAG, "Could not observe thermal status. Service not available");
+ return;
+ }
+ int temperatureType = SensorUtils.getSensorTemperatureType(mObserverTempSensor);
+ try {
+ // We get a callback immediately upon registering so there's no need to query
+ // for the current value.
+ mThermalService.registerThermalEventListenerWithType(this, temperatureType);
+ mStarted = true;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register thermal status listener", e);
+ }
+ }
+
+ @Override
+ public void notifyThrottling(Temperature temp) {
+ Slog.d(TAG, "New thermal throttling status = " + temp.getStatus());
+ if (mObserverTempSensor.name != null
+ && !mObserverTempSensor.name.equals(temp.getName())) {
+ Slog.i(TAG, "Skipping thermal throttling notification as monitored sensor: "
+ + mObserverTempSensor.name
+ + " != notified sensor: "
+ + temp.getName());
+ return;
+ }
+ @Temperature.ThrottlingStatus int status = temp.getStatus();
+ mHandler.post(() -> thermalStatusChanged(status));
+ }
+
+ void stopObserving() {
+ if (!mStarted) {
+ return;
+ }
+ try {
+ mThermalService.unregisterThermalEventListener(this);
+ mStarted = false;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to unregister thermal status listener", e);
+ }
+ mThermalService = null;
+ }
+
+ void dump(PrintWriter writer) {
+ writer.println(" ThermalStatusObserver:");
+ writer.println(" mStarted: " + mStarted);
+ writer.println(" mObserverTempSensor: " + mObserverTempSensor);
+ if (mThermalService != null) {
+ writer.println(" ThermalService available");
+ } else {
+ writer.println(" ThermalService not available");
+ }
}
}
@@ -251,6 +302,9 @@
@Nullable
ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData();
+
+ @NonNull
+ SensorData getTempSensor();
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/display/config/SensorData.java b/services/core/java/com/android/server/display/config/SensorData.java
index 3bb35bf..8e716f8 100644
--- a/services/core/java/com/android/server/display/config/SensorData.java
+++ b/services/core/java/com/android/server/display/config/SensorData.java
@@ -22,6 +22,7 @@
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.feature.DisplayManagerFlags;
import java.util.ArrayList;
import java.util.Collections;
@@ -32,6 +33,9 @@
*/
public class SensorData {
+ public static final String TEMPERATURE_TYPE_DISPLAY = "DISPLAY";
+ public static final String TEMPERATURE_TYPE_SKIN = "SKIN";
+
@Nullable
public final String type;
@Nullable
@@ -143,6 +147,32 @@
}
/**
+ * Loads temperature sensor data for no config case. (Type: SKIN, Name: null)
+ */
+ public static SensorData loadTempSensorUnspecifiedConfig() {
+ return new SensorData(TEMPERATURE_TYPE_SKIN, null);
+ }
+
+ /**
+ * Loads temperature sensor data from given display config.
+ * If empty or null config given default to (Type: SKIN, Name: null)
+ */
+ public static SensorData loadTempSensorConfig(DisplayManagerFlags flags,
+ DisplayConfiguration config) {
+ SensorDetails sensorDetails = config.getTempSensor();
+ if (!flags.isSensorBasedBrightnessThrottlingEnabled() || sensorDetails == null) {
+ return new SensorData(TEMPERATURE_TYPE_SKIN, null);
+ }
+ String name = sensorDetails.getName();
+ String type = sensorDetails.getType();
+ if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
+ type = TEMPERATURE_TYPE_SKIN;
+ name = null;
+ }
+ return new SensorData(type, name);
+ }
+
+ /**
* Loads sensor unspecified config, this means system should use default sensor.
* See also {@link com.android.server.display.utils.SensorUtils}
*/
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 1ae2559..516d4b1 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -121,6 +121,11 @@
Flags::refreshRateVotingTelemetry
);
+ private final FlagState mSensorBasedBrightnessThrottling = new FlagState(
+ Flags.FLAG_SENSOR_BASED_BRIGHTNESS_THROTTLING,
+ Flags::sensorBasedBrightnessThrottling
+ );
+
/**
* @return {@code true} if 'port' is allowed in display layout configuration file.
*/
@@ -247,6 +252,10 @@
return mRefreshRateVotingTelemetry.isEnabled();
}
+ public boolean isSensorBasedBrightnessThrottlingEnabled() {
+ return mSensorBasedBrightnessThrottling.isEnabled();
+ }
+
/**
* dumps all flagstates
* @param pw printWriter
@@ -270,6 +279,7 @@
pw.println(" " + mAutoBrightnessModesFlagState);
pw.println(" " + mFastHdrTransitions);
pw.println(" " + mRefreshRateVotingTelemetry);
+ pw.println(" " + mSensorBasedBrightnessThrottling);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index c2f52b5..63ab3a9 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -184,3 +184,11 @@
bug: "310029108"
is_fixed_read_only: true
}
+
+flag {
+ name: "sensor_based_brightness_throttling"
+ namespace: "display_manager"
+ description: "Feature flag for enabling brightness throttling using sensor from config."
+ bug: "294900859"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/display/utils/SensorUtils.java b/services/core/java/com/android/server/display/utils/SensorUtils.java
index 8b9fe108..c63473a 100644
--- a/services/core/java/com/android/server/display/utils/SensorUtils.java
+++ b/services/core/java/com/android/server/display/utils/SensorUtils.java
@@ -16,9 +16,11 @@
package com.android.server.display.utils;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.Sensor;
import android.hardware.SensorManager;
+import android.os.Temperature;
import android.text.TextUtils;
import com.android.server.display.config.SensorData;
@@ -70,4 +72,17 @@
return null;
}
+ /**
+ * Convert string temperature type to its corresponding integer value.
+ */
+ public static int getSensorTemperatureType(@NonNull SensorData tempSensor) {
+ if (tempSensor.type.equalsIgnoreCase(SensorData.TEMPERATURE_TYPE_DISPLAY)) {
+ return Temperature.TYPE_DISPLAY;
+ } else if (tempSensor.type.equalsIgnoreCase(SensorData.TEMPERATURE_TYPE_SKIN)) {
+ return Temperature.TYPE_SKIN;
+ }
+ throw new IllegalArgumentException(
+ "tempSensor doesn't support type: " + tempSensor.type);
+ }
+
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index fc7b873..3a7ac0b 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -8206,7 +8206,7 @@
try {
return mTelecomManager.isInManagedCall()
|| mTelecomManager.isInSelfManagedCall(pkg,
- UserHandle.getUserHandleForUid(uid), /* hasCrossUserAccess */ true);
+ /* hasCrossUserAccess */ true);
} catch (IllegalStateException ise) {
// Telecom is not ready (this is likely early boot), so there are no calls.
return false;
diff --git a/services/core/java/com/android/server/search/SearchManagerService.java b/services/core/java/com/android/server/search/SearchManagerService.java
index 7091c47..ecfc040 100644
--- a/services/core/java/com/android/server/search/SearchManagerService.java
+++ b/services/core/java/com/android/server/search/SearchManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.search;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.ISearchManager;
import android.app.SearchManager;
import android.app.SearchableInfo;
@@ -24,6 +25,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
import android.os.Binder;
@@ -32,6 +34,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
@@ -47,6 +50,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -71,11 +75,6 @@
}
@Override
- public void onUserUnlocking(@NonNull TargetUser user) {
- mService.mHandler.post(() -> mService.onUnlockUser(user.getUserIdentifier()));
- }
-
- @Override
public void onUserStopped(@NonNull TargetUser user) {
mService.onCleanupUser(user.getUserIdentifier());
}
@@ -102,10 +101,6 @@
}
private Searchables getSearchables(int userId) {
- return getSearchables(userId, false);
- }
-
- private Searchables getSearchables(int userId, boolean forceUpdate) {
final long token = Binder.clearCallingIdentity();
try {
final UserManager um = mContext.getSystemService(UserManager.class);
@@ -122,21 +117,11 @@
Searchables searchables = mSearchables.get(userId);
if (searchables == null) {
searchables = new Searchables(mContext, userId);
- searchables.updateSearchableList();
- mSearchables.append(userId, searchables);
- } else if (forceUpdate) {
- searchables.updateSearchableList();
+ mSearchables.put(userId, searchables);
}
- return searchables;
- }
- }
- private void onUnlockUser(int userId) {
- try {
- getSearchables(userId, true);
- } catch (IllegalStateException ignored) {
- // We're just trying to warm a cache, so we don't mind if the user
- // was stopped or destroyed before we got here.
+ searchables.updateSearchableListIfNeeded();
+ return searchables;
}
}
@@ -150,28 +135,110 @@
* Refreshes the "searchables" list when packages are added/removed.
*/
class MyPackageMonitor extends PackageMonitor {
+ /**
+ * Packages that are appeared, disappeared, or modified for whatever reason.
+ */
+ private final ArrayList<String> mChangedPackages = new ArrayList<>();
+
+ /**
+ * {@code true} if one or more packages that contain {@link SearchableInfo} appeared.
+ */
+ private boolean mSearchablePackageAppeared = false;
@Override
- public void onSomePackagesChanged() {
- updateSearchables();
+ public void onBeginPackageChanges() {
+ clearPackageChangeState();
}
@Override
- public void onPackageModified(String pkg) {
- updateSearchables();
+ public void onPackageAppeared(String packageName, int reason) {
+ if (!mSearchablePackageAppeared) {
+ // Check if the new appeared package contains SearchableInfo.
+ mSearchablePackageAppeared =
+ hasSearchableForPackage(packageName, getChangingUserId());
+ }
+ mChangedPackages.add(packageName);
}
- private void updateSearchables() {
- final int changingUserId = getChangingUserId();
+ @Override
+ public void onPackageDisappeared(String packageName, int reason) {
+ mChangedPackages.add(packageName);
+ }
+
+ @Override
+ public void onPackageModified(String packageName) {
+ mChangedPackages.add(packageName);
+ }
+
+ @Override
+ public void onFinishPackageChanges() {
+ onFinishPackageChangesInternal();
+ clearPackageChangeState();
+ }
+
+ private void clearPackageChangeState() {
+ mChangedPackages.clear();
+ mSearchablePackageAppeared = false;
+ }
+
+ private boolean hasSearchableForPackage(String packageName, int userId) {
+ final List<ResolveInfo> searchList = querySearchableActivities(mContext,
+ new Intent(Intent.ACTION_SEARCH).setPackage(packageName), userId);
+ if (!searchList.isEmpty()) {
+ return true;
+ }
+
+ final List<ResolveInfo> webSearchList = querySearchableActivities(mContext,
+ new Intent(Intent.ACTION_WEB_SEARCH).setPackage(packageName), userId);
+ if (!webSearchList.isEmpty()) {
+ return true;
+ }
+
+ final List<ResolveInfo> globalSearchList = querySearchableActivities(mContext,
+ new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH).setPackage(packageName),
+ userId);
+ return !globalSearchList.isEmpty();
+ }
+
+ private boolean shouldRebuildSearchableList(@UserIdInt int changingUserId) {
+ // This method is guaranteed to be called only on getRegisteredHandler()
+ if (mSearchablePackageAppeared) {
+ return true;
+ }
+
+ ArraySet<String> knownSearchablePackageNames = new ArraySet<>();
synchronized (mSearchables) {
- // Update list of searchable activities
- for (int i = 0; i < mSearchables.size(); i++) {
- if (changingUserId == mSearchables.keyAt(i)) {
- mSearchables.valueAt(i).updateSearchableList();
- break;
- }
+ Searchables searchables = mSearchables.get(changingUserId);
+ if (searchables != null) {
+ knownSearchablePackageNames = searchables.getKnownSearchablePackageNames();
}
}
+
+ final int numOfPackages = mChangedPackages.size();
+ for (int i = 0; i < numOfPackages; i++) {
+ final String packageName = mChangedPackages.get(i);
+ if (knownSearchablePackageNames.contains(packageName)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private void onFinishPackageChangesInternal() {
+ final int changingUserId = getChangingUserId();
+ if (!shouldRebuildSearchableList(changingUserId)) {
+ return;
+ }
+
+ synchronized (mSearchables) {
+ // Invalidate the searchable list.
+ Searchables searchables = mSearchables.get(changingUserId);
+ if (searchables != null) {
+ searchables.invalidateSearchableList();
+ }
+ }
+
// Inform all listeners that the list of searchables has been updated.
Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
@@ -180,6 +247,17 @@
}
}
+ @NonNull
+ static List<ResolveInfo> querySearchableActivities(Context context, Intent searchIntent,
+ @UserIdInt int userId) {
+ final List<ResolveInfo> activities = context.getPackageManager()
+ .queryIntentActivitiesAsUser(searchIntent, PackageManager.GET_META_DATA
+ | PackageManager.MATCH_INSTANT
+ | PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
+ return activities;
+ }
+
+
class GlobalSearchProviderObserver extends ContentObserver {
private final ContentResolver mResolver;
@@ -196,7 +274,7 @@
public void onChange(boolean selfChange) {
synchronized (mSearchables) {
for (int i = 0; i < mSearchables.size(); i++) {
- mSearchables.valueAt(i).updateSearchableList();
+ mSearchables.valueAt(i).invalidateSearchableList();
}
}
Intent intent = new Intent(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
diff --git a/services/core/java/com/android/server/search/Searchables.java b/services/core/java/com/android/server/search/Searchables.java
index 7b39775..dc67339 100644
--- a/services/core/java/com/android/server/search/Searchables.java
+++ b/services/core/java/com/android/server/search/Searchables.java
@@ -35,8 +35,10 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
import java.io.FileDescriptor;
@@ -62,7 +64,6 @@
private static final String MD_SEARCHABLE_SYSTEM_SEARCH = "*";
private Context mContext;
-
private HashMap<ComponentName, SearchableInfo> mSearchablesMap = null;
private ArrayList<SearchableInfo> mSearchablesList = null;
private ArrayList<SearchableInfo> mSearchablesInGlobalSearchList = null;
@@ -81,6 +82,12 @@
final private IPackageManager mPm;
// User for which this Searchables caches information
private int mUserId;
+ @GuardedBy("this")
+ private boolean mRebuildSearchables = true;
+
+ // Package names that are known to contain {@link SearchableInfo}
+ @GuardedBy("this")
+ private ArraySet<String> mKnownSearchablePackageNames = new ArraySet<>();
/**
*
@@ -224,7 +231,14 @@
*
* TODO: sort the list somehow? UI choice.
*/
- public void updateSearchableList() {
+ public void updateSearchableListIfNeeded() {
+ synchronized (this) {
+ if (!mRebuildSearchables) {
+ // The searchable list is valid, no need to rebuild.
+ return;
+ }
+ }
+
// These will become the new values at the end of the method
HashMap<ComponentName, SearchableInfo> newSearchablesMap
= new HashMap<ComponentName, SearchableInfo>();
@@ -232,6 +246,7 @@
= new ArrayList<SearchableInfo>();
ArrayList<SearchableInfo> newSearchablesInGlobalSearchList
= new ArrayList<SearchableInfo>();
+ ArraySet<String> newKnownSearchablePackageNames = new ArraySet<>();
// Use intent resolver to generate list of ACTION_SEARCH & ACTION_WEB_SEARCH receivers.
List<ResolveInfo> searchList;
@@ -264,6 +279,7 @@
mUserId);
if (searchable != null) {
newSearchablesList.add(searchable);
+ newKnownSearchablePackageNames.add(ai.packageName);
newSearchablesMap.put(searchable.getSearchActivity(), searchable);
if (searchable.shouldIncludeInGlobalSearch()) {
newSearchablesInGlobalSearchList.add(searchable);
@@ -286,16 +302,41 @@
synchronized (this) {
mSearchablesMap = newSearchablesMap;
mSearchablesList = newSearchablesList;
+ mKnownSearchablePackageNames = newKnownSearchablePackageNames;
mSearchablesInGlobalSearchList = newSearchablesInGlobalSearchList;
mGlobalSearchActivities = newGlobalSearchActivities;
mCurrentGlobalSearchActivity = newGlobalSearchActivity;
mWebSearchActivity = newWebSearchActivity;
+ for (ResolveInfo globalSearchActivity: mGlobalSearchActivities) {
+ mKnownSearchablePackageNames.add(
+ globalSearchActivity.getComponentInfo().packageName);
+ }
+ if (mCurrentGlobalSearchActivity != null) {
+ mKnownSearchablePackageNames.add(
+ mCurrentGlobalSearchActivity.getPackageName());
+ }
+ if (mWebSearchActivity != null) {
+ mKnownSearchablePackageNames.add(mWebSearchActivity.getPackageName());
+ }
+
+ mRebuildSearchables = false;
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
+ synchronized ArraySet<String> getKnownSearchablePackageNames() {
+ return mKnownSearchablePackageNames;
+ }
+
+ synchronized void invalidateSearchableList() {
+ mRebuildSearchables = true;
+
+ // Don't rebuild the searchable list, it will be rebuilt
+ // when the next updateSearchableList gets called.
+ }
+
/**
* Returns a sorted list of installed search providers as per
* the following heuristics:
@@ -532,6 +573,8 @@
pw.print(" "); pw.println(info.getSuggestAuthority());
}
}
+
+ pw.println("mRebuildSearchables = " + mRebuildSearchables);
}
}
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java
index 7163319..5d17884 100644
--- a/services/core/java/com/android/server/vibrator/VibrationScaler.java
+++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java
@@ -19,7 +19,7 @@
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.vibrator.V1_0.EffectStrength;
-import android.os.IExternalVibratorService;
+import android.os.ExternalVibrationScale;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
@@ -37,11 +37,13 @@
// Scale levels. Each level, except MUTE, is defined as the delta between the current setting
// and the default intensity for that type of vibration (i.e. current - default).
- private static final int SCALE_VERY_LOW = IExternalVibratorService.SCALE_VERY_LOW; // -2
- private static final int SCALE_LOW = IExternalVibratorService.SCALE_LOW; // -1
- private static final int SCALE_NONE = IExternalVibratorService.SCALE_NONE; // 0
- private static final int SCALE_HIGH = IExternalVibratorService.SCALE_HIGH; // 1
- private static final int SCALE_VERY_HIGH = IExternalVibratorService.SCALE_VERY_HIGH; // 2
+ private static final int SCALE_VERY_LOW =
+ ExternalVibrationScale.ScaleLevel.SCALE_VERY_LOW; // -2
+ private static final int SCALE_LOW = ExternalVibrationScale.ScaleLevel.SCALE_LOW; // -1
+ private static final int SCALE_NONE = ExternalVibrationScale.ScaleLevel.SCALE_NONE; // 0
+ private static final int SCALE_HIGH = ExternalVibrationScale.ScaleLevel.SCALE_HIGH; // 1
+ private static final int SCALE_VERY_HIGH =
+ ExternalVibrationScale.ScaleLevel.SCALE_VERY_HIGH; // 2
// Scale factors for each level.
private static final float SCALE_FACTOR_VERY_LOW = 0.6f;
@@ -83,9 +85,9 @@
* Calculates the scale to be applied to external vibration with given usage.
*
* @param usageHint one of VibrationAttributes.USAGE_*
- * @return one of IExternalVibratorService.SCALE_*
+ * @return one of ExternalVibrationScale.ScaleLevel.SCALE_*
*/
- public int getExternalVibrationScale(int usageHint) {
+ public int getExternalVibrationScaleLevel(int usageHint) {
int defaultIntensity = mSettingsController.getDefaultIntensity(usageHint);
int currentIntensity = mSettingsController.getCurrentIntensity(usageHint);
@@ -107,6 +109,22 @@
}
/**
+ * Returns the adaptive haptics scale that should be applied to the vibrations with
+ * the given usage. When no adaptive scales are available for the usages, then returns 1
+ * indicating no scaling will be applied
+ *
+ * @param usageHint one of VibrationAttributes.USAGE_*
+ * @return The adaptive haptics scale.
+ */
+ public float getAdaptiveHapticsScale(int usageHint) {
+ if (shouldApplyAdaptiveHapticsScale(usageHint)) {
+ return mAdaptiveHapticsScales.get(usageHint);
+ }
+
+ return 1f; // no scaling
+ }
+
+ /**
* Scale a {@link VibrationEffect} based on the given usage hint for this vibration.
*
* @param effect the effect to be scaled
@@ -152,9 +170,7 @@
}
// If adaptive haptics scaling is available for this usage, apply it to the segment.
- if (Flags.adaptiveHapticsEnabled()
- && mAdaptiveHapticsScales.size() > 0
- && mAdaptiveHapticsScales.contains(usageHint)) {
+ if (shouldApplyAdaptiveHapticsScale(usageHint)) {
float adaptiveScale = mAdaptiveHapticsScales.get(usageHint);
segment = segment.scaleLinearly(adaptiveScale);
}
@@ -224,6 +240,10 @@
mAdaptiveHapticsScales.clear();
}
+ private boolean shouldApplyAdaptiveHapticsScale(int usageHint) {
+ return Flags.adaptiveHapticsEnabled() && mAdaptiveHapticsScales.contains(usageHint);
+ }
+
/** Mapping of Vibrator.VIBRATION_INTENSITY_* values to {@link EffectStrength}. */
private static int intensityToEffectStrength(int intensity) {
switch (intensity) {
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index be5d158..78e0ebb 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.vibrator;
+import static android.os.ExternalVibrationScale.ScaleLevel.SCALE_MUTE;
import static android.os.VibrationEffect.VibrationParameter.targetAmplitude;
import static android.os.VibrationEffect.VibrationParameter.targetFrequency;
@@ -35,6 +36,7 @@
import android.os.Build;
import android.os.CombinedVibration;
import android.os.ExternalVibration;
+import android.os.ExternalVibrationScale;
import android.os.Handler;
import android.os.IBinder;
import android.os.IExternalVibratorService;
@@ -277,7 +279,7 @@
context.registerReceiver(mIntentReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
- if (ServiceManager.isDeclared(VIBRATOR_CONTROL_SERVICE)) {
+ if (injector.isServiceDeclared(VIBRATOR_CONTROL_SERVICE)) {
injector.addService(VIBRATOR_CONTROL_SERVICE, mVibratorControlService);
}
@@ -1427,6 +1429,10 @@
VibratorControllerHolder createVibratorControllerHolder() {
return new VibratorControllerHolder();
}
+
+ boolean isServiceDeclared(String name) {
+ return ServiceManager.isDeclared(name);
+ }
}
/**
@@ -1594,7 +1600,7 @@
IBinder.DeathRecipient {
public final ExternalVibration externalVibration;
- public int scale;
+ public ExternalVibrationScale scale = new ExternalVibrationScale();
private Vibration.Status mStatus;
@@ -1605,7 +1611,6 @@
// instead of using DEVICE_ID_INVALID here and relying on the UID checks.
Context.DEVICE_ID_INVALID, externalVibration.getPackage(), null));
this.externalVibration = externalVibration;
- this.scale = IExternalVibratorService.SCALE_NONE;
mStatus = Vibration.Status.RUNNING;
}
@@ -1658,7 +1663,7 @@
public Vibration.DebugInfo getDebugInfo() {
return new Vibration.DebugInfo(mStatus, stats, /* playedEffect= */ null,
- /* originalEffect= */ null, scale, callerInfo);
+ /* originalEffect= */ null, scale.scaleLevel, callerInfo);
}
public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) {
@@ -1988,11 +1993,17 @@
/** Implementation of {@link IExternalVibratorService} to be triggered on external control. */
@VisibleForTesting
final class ExternalVibratorService extends IExternalVibratorService.Stub {
+ private static final ExternalVibrationScale SCALE_MUTE = new ExternalVibrationScale();
+
+ static {
+ SCALE_MUTE.scaleLevel = ExternalVibrationScale.ScaleLevel.SCALE_MUTE;
+ }
@Override
- public int onExternalVibrationStart(ExternalVibration vib) {
+ public ExternalVibrationScale onExternalVibrationStart(ExternalVibration vib) {
+
if (!hasExternalControlCapability()) {
- return IExternalVibratorService.SCALE_MUTE;
+ return SCALE_MUTE;
}
if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE,
vib.getUid(), -1 /*owningUid*/, true /*exported*/)
@@ -2000,7 +2011,7 @@
Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid()
+ " tried to play externally controlled vibration"
+ " without VIBRATE permission, ignoring.");
- return IExternalVibratorService.SCALE_MUTE;
+ return SCALE_MUTE;
}
// Create Vibration.Stats as close to the received request as possible, for tracking.
@@ -2033,7 +2044,7 @@
}
if (vibrationEndInfo != null) {
- vibHolder.scale = IExternalVibratorService.SCALE_MUTE;
+ vibHolder.scale = SCALE_MUTE;
// Failed to start the vibration, end it and report metrics right away.
endVibrationAndWriteStatsLocked(vibHolder, vibrationEndInfo);
return vibHolder.scale;
@@ -2074,7 +2085,10 @@
}
mCurrentExternalVibration = vibHolder;
vibHolder.linkToDeath();
- vibHolder.scale = mVibrationScaler.getExternalVibrationScale(attrs.getUsage());
+ vibHolder.scale.scaleLevel = mVibrationScaler.getExternalVibrationScaleLevel(
+ attrs.getUsage());
+ vibHolder.scale.adaptiveHapticsScale = mVibrationScaler.getAdaptiveHapticsScale(
+ attrs.getUsage());
}
if (waitForCompletion) {
@@ -2086,7 +2100,7 @@
new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_CANCELLING),
/* continueExternalControl= */ false);
}
- return IExternalVibratorService.SCALE_MUTE;
+ return SCALE_MUTE;
}
}
if (!alreadyUnderExternalControl) {
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
index b3c8b0b..27c80c4 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
@@ -107,15 +107,20 @@
mContext = context;
mSystemInterface = systemInterface;
WebViewProviderInfo[] webviewProviders = getWebViewPackages();
+
+ WebViewProviderInfo defaultProvider = null;
for (WebViewProviderInfo provider : webviewProviders) {
if (provider.availableByDefault) {
- mDefaultProvider = provider;
+ defaultProvider = provider;
break;
}
}
- // This should be unreachable because the config parser enforces that there is at least one
- // availableByDefault provider.
- throw new AndroidRuntimeException("No available by default WebView Provider.");
+ if (defaultProvider == null) {
+ // This should be unreachable because the config parser enforces that there is at least
+ // one availableByDefault provider.
+ throw new AndroidRuntimeException("No available by default WebView Provider.");
+ }
+ mDefaultProvider = defaultProvider;
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 56024f7..bc6f93f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1535,11 +1535,12 @@
// Picture-in-picture mode changes also trigger a multi-window mode change as well, so
// update that here in order. Set the last reported MW state to the same as the PiP
// state since we haven't yet actually resized the task (these callbacks need to
- // precede the configuration change from the resize.
+ // precede the configuration change from the resize.)
mLastReportedPictureInPictureMode = inPictureInPictureMode;
mLastReportedMultiWindowMode = inPictureInPictureMode;
ensureActivityConfiguration(true /* ignoreVisibility */);
- if (inPictureInPictureMode && findMainWindow() == null) {
+ if (inPictureInPictureMode && findMainWindow() == null
+ && task.topRunningActivity() == this) {
// Prevent malicious app entering PiP without valid WindowState, which can in turn
// result a non-touchable PiP window since the InputConsumer for PiP requires it.
EventLog.writeEvent(0x534e4554, "265293293", -1, "");
@@ -3549,7 +3550,7 @@
IBinder callerToken = new Binder();
if (android.security.Flags.contentUriPermissionApis()) {
try {
- resultTo.computeCallerInfo(callerToken, intent, this.getUid(),
+ resultTo.computeCallerInfo(callerToken, resultData, this.getUid(),
mAtmService.getPackageManager().getNameForUid(this.getUid()),
/* isShareIdentityEnabled */ false);
// Result callers cannot share their identity via
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index a469165..b38a2f9 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -117,6 +117,9 @@
<xs:element type="sensorDetails" name="proxSensor">
<xs:annotation name="final"/>
</xs:element>
+ <xs:element type="sensorDetails" name="tempSensor">
+ <xs:annotation name="final"/>
+ </xs:element>
<!-- Length of the ambient light horizon used to calculate the long & short term
estimates of ambient light in milliseconds.-->
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 79ea274..b329db4 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -133,6 +133,7 @@
method public final java.math.BigDecimal getScreenBrightnessRampSlowIncreaseIdle();
method public final com.android.server.display.config.SensorDetails getScreenOffBrightnessSensor();
method public final com.android.server.display.config.IntegerArray getScreenOffBrightnessSensorValueToLux();
+ method public final com.android.server.display.config.SensorDetails getTempSensor();
method @NonNull public final com.android.server.display.config.ThermalThrottling getThermalThrottling();
method public final com.android.server.display.config.UsiVersion getUsiVersion();
method public final void setAmbientBrightnessChangeThresholds(@NonNull com.android.server.display.config.Thresholds);
@@ -167,6 +168,7 @@
method public final void setScreenBrightnessRampSlowIncreaseIdle(java.math.BigDecimal);
method public final void setScreenOffBrightnessSensor(com.android.server.display.config.SensorDetails);
method public final void setScreenOffBrightnessSensorValueToLux(com.android.server.display.config.IntegerArray);
+ method public final void setTempSensor(com.android.server.display.config.SensorDetails);
method public final void setThermalThrottling(@NonNull com.android.server.display.config.ThermalThrottling);
method public final void setUsiVersion(com.android.server.display.config.UsiVersion);
}
diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp
index b7af58c..3bce9b5 100644
--- a/services/tests/InputMethodSystemServerTests/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/Android.bp
@@ -69,7 +69,7 @@
}
android_ravenwood_test {
- name: "FrameworksInputMethodSystemServerTests_host",
+ name: "FrameworksInputMethodSystemServerTestsRavenwood",
static_libs: [
"androidx.annotation_annotation",
"androidx.test.rules",
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java
index 8faaf59..05c243f 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java
@@ -43,6 +43,7 @@
import com.android.server.display.BrightnessThrottler.Injector;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.config.SensorData;
import com.android.server.display.mode.DisplayModeDirectorTest;
import org.junit.Before;
@@ -56,6 +57,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -292,6 +294,53 @@
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
}
+
+ @Test
+ public void testThermalThrottlingWithDisplaySensor() throws Exception {
+ final ThrottlingLevel level =
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.25f);
+ List<ThrottlingLevel> levels = new ArrayList<>(List.of(level));
+ final ThermalBrightnessThrottlingData data = ThermalBrightnessThrottlingData.create(levels);
+ final SensorData tempSensor = new SensorData("DISPLAY", "VIRTUAL-SKIN-DISPLAY");
+ final BrightnessThrottler throttler =
+ createThrottlerSupportedWithTempSensor(data, tempSensor);
+ assertTrue(throttler.deviceSupportsThrottling());
+
+ verify(mThermalServiceMock)
+ .registerThermalEventListenerWithType(
+ mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_DISPLAY));
+ final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
+
+ // Set VIRTUAL-SKIN-DISPLAY tatus too low to verify no throttling.
+ listener.notifyThrottling(getDisplayTempWithName(tempSensor.name, level.thermalStatus - 1));
+ mTestLooper.dispatchAll();
+ assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+ assertFalse(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
+
+ // Verify when skin sensor throttled, no brightness throttling triggered.
+ listener.notifyThrottling(getSkinTemp(level.thermalStatus + 1));
+ mTestLooper.dispatchAll();
+ assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+ assertFalse(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
+
+ // Verify when display sensor of another name throttled, no brightness throttling triggered.
+ listener.notifyThrottling(getDisplayTempWithName("ANOTHER-NAME", level.thermalStatus + 1));
+ mTestLooper.dispatchAll();
+ assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+ assertFalse(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
+
+ // Verify when display sensor of current name throttled, brightness throttling triggered.
+ listener.notifyThrottling(getDisplayTempWithName(tempSensor.name, level.thermalStatus + 1));
+ mTestLooper.dispatchAll();
+ assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
+ assertTrue(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+ throttler.getBrightnessMaxReason());
+ }
+
@Test public void testUpdateThermalThrottlingData() throws Exception {
// Initialise brightness throttling levels
// Ensure that they are overridden by setting the data through device config.
@@ -476,18 +525,30 @@
return new BrightnessThrottler(mInjectorMock, mHandler, mHandler,
/* throttlingChangeCallback= */ () -> {}, /* uniqueDisplayId= */ null,
/* thermalThrottlingDataId= */ null,
- /* thermalThrottlingDataMap= */ new HashMap<>(1));
+ /* thermalThrottlingDataMap= */ new HashMap<>(1),
+ /* tempSensor= */ null);
}
private BrightnessThrottler createThrottlerSupported(ThermalBrightnessThrottlingData data) {
+ SensorData tempSensor = SensorData.loadTempSensorUnspecifiedConfig();
+ return createThrottlerSupportedWithTempSensor(data, tempSensor);
+ }
+ private BrightnessThrottler createThrottlerSupportedWithTempSensor(
+ ThermalBrightnessThrottlingData data, SensorData tempSensor) {
assertNotNull(data);
- HashMap<String, ThermalBrightnessThrottlingData> throttlingDataMap = new HashMap<>(1);
+ Map<String, ThermalBrightnessThrottlingData> throttlingDataMap = new HashMap<>(1);
throttlingDataMap.put("default", data);
return new BrightnessThrottler(mInjectorMock, mHandler, BackgroundThread.getHandler(),
- () -> {}, "123", "default", throttlingDataMap);
+ () -> {}, "123", "default", throttlingDataMap, tempSensor);
}
private Temperature getSkinTemp(@ThrottlingStatus int status) {
return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status);
}
+
+ private Temperature getDisplayTempWithName(
+ String sensorName, @ThrottlingStatus int status) {
+ assertNotNull(sensorName);
+ return new Temperature(30.0f, Temperature.TYPE_DISPLAY, sensorName, status);
+ }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index b29fc88..2867041 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -20,6 +20,7 @@
import static com.android.internal.display.BrightnessSynchronizer.brightnessIntToFloat;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
+import static com.android.server.display.config.SensorData.TEMPERATURE_TYPE_SKIN;
import static com.android.server.display.config.SensorData.SupportedMode;
import static com.android.server.display.utils.DeviceConfigParsingUtils.ambientBrightnessThresholdsIntToFloat;
import static com.android.server.display.utils.DeviceConfigParsingUtils.displayBrightnessThresholdsIntToFloat;
@@ -106,6 +107,7 @@
MockitoAnnotations.initMocks(this);
when(mContext.getResources()).thenReturn(mResources);
when(mFlags.areAutoBrightnessModesEnabled()).thenReturn(true);
+ when(mFlags.isSensorBasedBrightnessThrottlingEnabled()).thenReturn(true);
mockDeviceConfigs();
}
@@ -143,6 +145,8 @@
assertEquals("", mDisplayDeviceConfig.getAmbientLightSensor().name);
assertNull(mDisplayDeviceConfig.getProximitySensor().type);
assertNull(mDisplayDeviceConfig.getProximitySensor().name);
+ assertEquals(TEMPERATURE_TYPE_SKIN, mDisplayDeviceConfig.getTempSensor().type);
+ assertNull(mDisplayDeviceConfig.getTempSensor().name);
assertTrue(mDisplayDeviceConfig.isAutoBrightnessAvailable());
}
@@ -592,6 +596,13 @@
}
@Test
+ public void testTempSensorFromDisplayConfig() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile();
+ assertEquals("DISPLAY", mDisplayDeviceConfig.getTempSensor().type);
+ assertEquals("VIRTUAL-SKIN-DISPLAY", mDisplayDeviceConfig.getTempSensor().name);
+ }
+
+ @Test
public void testBlockingZoneThresholdsFromDisplayConfig() throws IOException {
setupDisplayDeviceConfigFromDisplayConfigFile();
@@ -1356,6 +1367,10 @@
+ "<name>Test Binned Brightness Sensor</name>\n"
+ "</screenOffBrightnessSensor>\n"
+ proxSensor
+ + "<tempSensor>\n"
+ + "<type>DISPLAY</type>\n"
+ + "<name>VIRTUAL-SKIN-DISPLAY</name>\n"
+ + "</tempSensor>\n"
+ "<ambientBrightnessChangeThresholds>\n"
+ "<brighteningThresholds>\n"
+ "<minimum>10</minimum>\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java
index 37d0f62..34f352e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java
@@ -37,10 +37,14 @@
import com.android.server.display.DisplayDeviceConfig;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.testutils.FakeDeviceConfigInterface;
import com.android.server.testutils.TestHandler;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -50,9 +54,6 @@
import java.util.List;
-import junitparams.JUnitParamsRunner;
-import junitparams.Parameters;
-
@RunWith(JUnitParamsRunner.class)
public class BrightnessThermalClamperTest {
@@ -125,13 +126,13 @@
public void testNotifyThrottlingAfterOnDisplayChange(List<ThrottlingLevel> throttlingLevels,
@Temperature.ThrottlingStatus int throttlingStatus,
boolean expectedActive, float expectedBrightness) throws RemoteException {
- IThermalEventListener thermalEventListener = captureThermalEventListener();
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
mClamper.onDisplayChanged(new TestThermalData(throttlingLevels));
mTestHandler.flush();
assertFalse(mClamper.isActive());
assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
- thermalEventListener.notifyThrottling(createTemperature(throttlingStatus));
+ thermalEventListener.notifyThrottling(createSkinTemperature(throttlingStatus));
mTestHandler.flush();
assertEquals(expectedActive, mClamper.isActive());
assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
@@ -139,11 +140,11 @@
@Test
@Parameters(method = "testThrottlingData")
- public void testOnDisplayChangeAfterNotifyThrottlng(List<ThrottlingLevel> throttlingLevels,
+ public void testOnDisplayChangeAfterNotifyThrottling(List<ThrottlingLevel> throttlingLevels,
@Temperature.ThrottlingStatus int throttlingStatus,
boolean expectedActive, float expectedBrightness) throws RemoteException {
- IThermalEventListener thermalEventListener = captureThermalEventListener();
- thermalEventListener.notifyThrottling(createTemperature(throttlingStatus));
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+ thermalEventListener.notifyThrottling(createSkinTemperature(throttlingStatus));
mTestHandler.flush();
assertFalse(mClamper.isActive());
assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
@@ -156,8 +157,8 @@
@Test
public void testOverrideData() throws RemoteException {
- IThermalEventListener thermalEventListener = captureThermalEventListener();
- thermalEventListener.notifyThrottling(createTemperature(Temperature.THROTTLING_SEVERE));
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+ thermalEventListener.notifyThrottling(createSkinTemperature(Temperature.THROTTLING_SEVERE));
mTestHandler.flush();
assertFalse(mClamper.isActive());
assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
@@ -183,15 +184,60 @@
assertEquals(0.4f, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
}
- private IThermalEventListener captureThermalEventListener() throws RemoteException {
+ @Test
+ public void testDisplaySensorBasedThrottling() throws RemoteException {
+ final int severity = PowerManager.THERMAL_STATUS_SEVERE;
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+ // Update config to listen to display type sensor.
+ final SensorData tempSensor = new SensorData("DISPLAY", "VIRTUAL-SKIN-DISPLAY");
+ final TestThermalData thermalData =
+ new TestThermalData(
+ DISPLAY_ID,
+ DisplayDeviceConfig.DEFAULT_ID,
+ List.of(new ThrottlingLevel(severity, 0.5f)),
+ tempSensor);
+ mClamper.onDisplayChanged(thermalData);
+ mTestHandler.flush();
+ verify(mMockThermalService).unregisterThermalEventListener(thermalEventListener);
+ thermalEventListener = captureThermalEventListener(Temperature.TYPE_DISPLAY);
+ assertFalse(mClamper.isActive());
+
+ // Verify no throttling triggered when any other sensor notification received.
+ thermalEventListener.notifyThrottling(createSkinTemperature(severity));
+ mTestHandler.flush();
+ assertFalse(mClamper.isActive());
+
+ thermalEventListener.notifyThrottling(createDisplayTemperature("OTHER-SENSOR", severity));
+ mTestHandler.flush();
+ assertFalse(mClamper.isActive());
+
+ assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
+
+ // Verify throttling triggered when display sensor of given name throttled.
+ thermalEventListener.notifyThrottling(createDisplayTemperature(tempSensor.name, severity));
+ mTestHandler.flush();
+ assertTrue(mClamper.isActive());
+ assertEquals(0.5f, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
+ }
+
+ private IThermalEventListener captureSkinThermalEventListener() throws RemoteException {
+ return captureThermalEventListener(Temperature.TYPE_SKIN);
+ }
+
+ private IThermalEventListener captureThermalEventListener(int type) throws RemoteException {
ArgumentCaptor<IThermalEventListener> captor = ArgumentCaptor.forClass(
IThermalEventListener.class);
verify(mMockThermalService).registerThermalEventListenerWithType(captor.capture(), eq(
- Temperature.TYPE_SKIN));
+ type));
return captor.getValue();
}
- private Temperature createTemperature(@Temperature.ThrottlingStatus int status) {
+ private Temperature createDisplayTemperature(
+ @NonNull String sensorName, @Temperature.ThrottlingStatus int status) {
+ return new Temperature(100, Temperature.TYPE_DISPLAY, sensorName, status);
+ }
+
+ private Temperature createSkinTemperature(@Temperature.ThrottlingStatus int status) {
return new Temperature(100, Temperature.TYPE_SKIN, "test_temperature", status);
}
@@ -217,19 +263,26 @@
private final String mUniqueDisplayId;
private final String mDataId;
private final ThermalBrightnessThrottlingData mData;
+ private final SensorData mTempSensor;
private TestThermalData() {
- this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, null);
+ this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, null,
+ SensorData.loadTempSensorUnspecifiedConfig());
}
private TestThermalData(List<ThrottlingLevel> data) {
- this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, data);
+ this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, data,
+ SensorData.loadTempSensorUnspecifiedConfig());
}
- private TestThermalData(String uniqueDisplayId, String dataId, List<ThrottlingLevel> data) {
+
+ private TestThermalData(String uniqueDisplayId, String dataId, List<ThrottlingLevel> data,
+ SensorData tempSensor) {
mUniqueDisplayId = uniqueDisplayId;
mDataId = dataId;
mData = ThermalBrightnessThrottlingData.create(data);
+ mTempSensor = tempSensor;
}
+
@NonNull
@Override
public String getUniqueDisplayId() {
@@ -247,5 +300,11 @@
public ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData() {
return mData;
}
+
+ @NonNull
+ @Override
+ public SensorData getTempSensor() {
+ return mTempSensor;
+ }
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
index 940469f..414532b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
@@ -29,16 +29,20 @@
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupTransport;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
+import android.os.Build;
import android.os.Message;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.modules.utils.testing.TestableDeviceConfig;
+import com.android.server.backup.Flags;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.BackupHandler;
import com.android.server.backup.transport.BackupTransportClient;
@@ -56,10 +60,12 @@
import org.mockito.stubbing.Answer;
import java.util.ArrayDeque;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
@@ -74,10 +80,17 @@
private static final String SYSTEM_PACKAGE_NAME = "android";
private static final String NON_SYSTEM_PACKAGE_NAME = "package";
- @Mock private BackupDataInput mBackupDataInput;
- @Mock private BackupDataOutput mBackupDataOutput;
- @Mock private UserBackupManagerService mBackupManagerService;
- @Mock private TransportConnection mTransportConnection;
+ private static final String V_TO_U_ALLOWLIST = "pkg1";
+ private static final String V_TO_U_DENYLIST = "pkg2";
+
+ @Mock
+ private BackupDataInput mBackupDataInput;
+ @Mock
+ private BackupDataOutput mBackupDataOutput;
+ @Mock
+ private UserBackupManagerService mBackupManagerService;
+ @Mock
+ private TransportConnection mTransportConnection;
private Set<String> mExcludedkeys = new HashSet<>();
private Map<String, String> mBackupData = new HashMap<>();
@@ -91,6 +104,10 @@
public TestableDeviceConfig.TestableDeviceConfigRule mDeviceConfigRule =
new TestableDeviceConfig.TestableDeviceConfigRule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+
private Context mContext;
@Before
@@ -118,7 +135,8 @@
return null;
});
- mRestoreTask = new PerformUnifiedRestoreTask(mBackupManagerService, mTransportConnection);
+ mRestoreTask = new PerformUnifiedRestoreTask(mBackupManagerService, mTransportConnection,
+ V_TO_U_ALLOWLIST, V_TO_U_DENYLIST);
}
private void populateTestData() {
@@ -235,6 +253,122 @@
== UnifiedRestoreState.FINAL);
}
+ @Test
+ public void testCreateVToUList_listSettingIsNull_returnEmptyList() {
+ List<String> expectedEmptyList = new ArrayList<>();
+
+ List<String> list = mRestoreTask.createVToUList(null);
+
+ assertEquals(list, expectedEmptyList);
+ }
+
+ @Test
+ public void testCreateVToUList_listIsNotNull_returnCorrectList() {
+ List<String> expectedList = Arrays.asList("a", "b", "c");
+ String listString = "a,b,c";
+
+ List<String> list = mRestoreTask.createVToUList(listString);
+
+ assertEquals(list, expectedList);
+ }
+
+ @Test
+ public void testIsVToUDowngrade_vToUFlagIsOffAndTargetIsUSourceIsV_returnFalse() {
+ mSetFlagsRule.disableFlags(
+ Flags.FLAG_ENABLE_V_TO_U_RESTORE_FOR_SYSTEM_COMPONENTS_IN_ALLOWLIST);
+
+ boolean isVToUDowngrade = mRestoreTask.isVToUDowngrade(
+ Build.VERSION_CODES.VANILLA_ICE_CREAM, Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+
+ assertFalse(isVToUDowngrade);
+ }
+
+ @Test
+ public void testIsVToUDowngrade_vToUFlagIsOnAndTargetIsUSourceIsV_returnTrue() {
+ mSetFlagsRule.enableFlags(
+ Flags.FLAG_ENABLE_V_TO_U_RESTORE_FOR_SYSTEM_COMPONENTS_IN_ALLOWLIST);
+
+ boolean isVToUDowngrade = mRestoreTask.isVToUDowngrade(
+ Build.VERSION_CODES.VANILLA_ICE_CREAM, Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+
+ assertTrue(isVToUDowngrade);
+ }
+
+ @Test
+ public void testIsVToUDowngrade_vToUFlagIsOnAndSourceIsNotV_returnFalse() {
+ mSetFlagsRule.enableFlags(
+ Flags.FLAG_ENABLE_V_TO_U_RESTORE_FOR_SYSTEM_COMPONENTS_IN_ALLOWLIST);
+
+ boolean isVToUDowngrade = mRestoreTask.isVToUDowngrade(Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+
+ assertFalse(isVToUDowngrade);
+ }
+
+ @Test
+ public void testIsVToUDowngrade_vToUFlagIsOnAndTargetIsNotU_returnFalse() {
+ mSetFlagsRule.enableFlags(
+ Flags.FLAG_ENABLE_V_TO_U_RESTORE_FOR_SYSTEM_COMPONENTS_IN_ALLOWLIST);
+
+ boolean isVToUDowngrade = mRestoreTask.isVToUDowngrade(
+ Build.VERSION_CODES.VANILLA_ICE_CREAM, Build.VERSION_CODES.VANILLA_ICE_CREAM);
+
+ assertFalse(isVToUDowngrade);
+ }
+
+
+ @Test
+ public void testIsEligibleForVToUDowngrade_pkgIsNotOnAllowlist_returnFalse() {
+ PackageInfo testPackageInfo = new PackageInfo();
+ testPackageInfo.packageName = "pkg";
+ testPackageInfo.applicationInfo = new ApplicationInfo();
+ // restoreAnyVersion flag is off
+ testPackageInfo.applicationInfo.flags = 0;
+
+ boolean eligibilityCriteria = mRestoreTask.isPackageEligibleForVToURestore(testPackageInfo);
+
+ assertFalse(eligibilityCriteria);
+ }
+
+ @Test
+ public void testIsEligibleForVToUDowngrade_pkgIsOnAllowlist_returnTrue() {
+ PackageInfo testPackageInfo = new PackageInfo();
+ testPackageInfo.packageName = "pkg1";
+ testPackageInfo.applicationInfo = new ApplicationInfo();
+ // restoreAnyVersion flag is off
+ testPackageInfo.applicationInfo.flags = 0;
+
+ boolean eligibilityCriteria = mRestoreTask.isPackageEligibleForVToURestore(testPackageInfo);
+
+ assertTrue(eligibilityCriteria);
+ }
+
+ @Test
+ public void testIsEligibleForVToUDowngrade_pkgIsNotOnDenyList_returnTrue() {
+ PackageInfo testPackageInfo = new PackageInfo();
+ testPackageInfo.packageName = "pkg";
+ testPackageInfo.applicationInfo = new ApplicationInfo();
+ // restoreAnyVersion flag is on
+ testPackageInfo.applicationInfo.flags = ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
+
+ boolean eligibilityCriteria = mRestoreTask.isPackageEligibleForVToURestore(testPackageInfo);
+
+ assertTrue(eligibilityCriteria);
+ }
+
+ @Test
+ public void testIsEligibleForVToUDowngrade_pkgIsOnDenyList_returnFalse() {
+ PackageInfo testPackageInfo = new PackageInfo();
+ testPackageInfo.packageName = "pkg2";
+ testPackageInfo.applicationInfo = new ApplicationInfo();
+ // restoreAnyVersion flag is on
+ testPackageInfo.applicationInfo.flags = ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
+
+ boolean eligibilityCriteria = mRestoreTask.isPackageEligibleForVToURestore(testPackageInfo);
+
+ assertFalse(eligibilityCriteria);
+ }
+
private void setupForRestoreKeyValueState(int transportStatus)
throws RemoteException, TransportNotAvailableException {
// Mock BackupHandler to do nothing when executeNextState() is called
diff --git a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
index f5c6795..771a765 100644
--- a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
+++ b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
@@ -92,7 +92,7 @@
public void testNonSearchable() {
// test basic array & hashmap
Searchables searchables = new Searchables(mContext, 0);
- searchables.updateSearchableList();
+ searchables.updateSearchableListIfNeeded();
// confirm that we return null for non-searchy activities
ComponentName nonActivity = new ComponentName("com.android.frameworks.servicestests",
@@ -121,7 +121,7 @@
doReturn(true).when(mPackageManagerInternal).canAccessComponent(anyInt(), any(), anyInt());
Searchables searchables = new Searchables(mContext, 0);
- searchables.updateSearchableList();
+ searchables.updateSearchableListIfNeeded();
// tests with "real" searchables (deprecate, this should be a unit test)
ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
int count = searchablesList.size();
@@ -139,7 +139,7 @@
doReturn(false).when(mPackageManagerInternal).canAccessComponent(anyInt(), any(), anyInt());
Searchables searchables = new Searchables(mContext, 0);
- searchables.updateSearchableList();
+ searchables.updateSearchableListIfNeeded();
ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
assertNotNull(searchablesList);
MoreAsserts.assertEmpty(searchablesList);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index cff7f46..715c9d4 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -12008,7 +12008,7 @@
// style + self managed call - bypasses block
when(mTelecomManager.isInSelfManagedCall(
- r.getSbn().getPackageName(), r.getUser(), true)).thenReturn(true);
+ r.getSbn().getPackageName(), true)).thenReturn(true);
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
@@ -12091,7 +12091,7 @@
// style + self managed call - bypasses block
mService.clearNotifications();
reset(mUsageStats);
- when(mTelecomManager.isInSelfManagedCall(r.getSbn().getPackageName(), r.getUser(), true))
+ when(mTelecomManager.isInSelfManagedCall(r.getSbn().getPackageName(), true))
.thenReturn(true);
mService.addEnqueuedNotification(r);
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
index b431888..3e59878 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
@@ -35,8 +35,8 @@
import android.content.ContentResolver;
import android.content.ContextWrapper;
import android.content.pm.PackageManagerInternal;
+import android.os.ExternalVibrationScale;
import android.os.Handler;
-import android.os.IExternalVibratorService;
import android.os.PowerManagerInternal;
import android.os.UserHandle;
import android.os.VibrationAttributes;
@@ -49,6 +49,7 @@
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibrationEffectSegment;
+import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -119,29 +120,65 @@
public void testGetExternalVibrationScale() {
setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
- assertEquals(IExternalVibratorService.SCALE_VERY_HIGH,
- mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_VERY_HIGH,
+ mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);
- assertEquals(IExternalVibratorService.SCALE_HIGH,
- mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_HIGH,
+ mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_LOW);
- assertEquals(IExternalVibratorService.SCALE_NONE,
- mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_NONE,
+ mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_MEDIUM);
- assertEquals(IExternalVibratorService.SCALE_LOW,
- mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_LOW,
+ mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_HIGH);
- assertEquals(IExternalVibratorService.SCALE_VERY_LOW,
- mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_VERY_LOW,
+ mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
// Vibration setting being bypassed will use default setting and not scale.
- assertEquals(IExternalVibratorService.SCALE_NONE,
- mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_NONE,
+ mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+ public void testAdaptiveHapticsScale_withAdaptiveHapticsAvailable() {
+ setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW);
+ setDefaultIntensity(USAGE_RINGTONE, Vibrator.VIBRATION_INTENSITY_LOW);
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
+
+ mVibrationScaler.updateAdaptiveHapticsScale(USAGE_TOUCH, 0.5f);
+ mVibrationScaler.updateAdaptiveHapticsScale(USAGE_RINGTONE, 0.2f);
+
+ assertEquals(0.5f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_TOUCH));
+ assertEquals(0.2f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_RINGTONE));
+ assertEquals(1f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_NOTIFICATION));
+
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
+ // Vibration setting being bypassed will apply adaptive haptics scales.
+ assertEquals(0.2f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_RINGTONE));
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+ public void testAdaptiveHapticsScale_flagDisabled_adaptiveHapticScaleAlwaysNone() {
+ setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW);
+ setDefaultIntensity(USAGE_RINGTONE, Vibrator.VIBRATION_INTENSITY_LOW);
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
+
+ mVibrationScaler.updateAdaptiveHapticsScale(USAGE_TOUCH, 0.5f);
+ mVibrationScaler.updateAdaptiveHapticsScale(USAGE_RINGTONE, 0.2f);
+
+ assertEquals(1f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_TOUCH));
+ assertEquals(1f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_RINGTONE));
+ assertEquals(1f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_NOTIFICATION));
}
@Test
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
index 2823223..417fbd0 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -35,7 +35,6 @@
import android.content.ComponentName;
import android.content.pm.PackageManagerInternal;
import android.frameworks.vibrator.ScaleParam;
-import android.frameworks.vibrator.VibrationParam;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -55,8 +54,6 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
-import java.util.ArrayList;
-import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@@ -135,7 +132,7 @@
vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
mVibratorControlService.onRequestVibrationParamsComplete(token,
- generateVibrationParams(vibrationScales));
+ VibrationParamGenerator.generateVibrationParams(vibrationScales));
verify(mMockVibrationScaler).updateAdaptiveHapticsScale(USAGE_ALARM, 0.7f);
verify(mMockVibrationScaler).updateAdaptiveHapticsScale(USAGE_NOTIFICATION, 0.4f);
@@ -162,7 +159,7 @@
vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
mVibratorControlService.onRequestVibrationParamsComplete(new Binder(),
- generateVibrationParams(vibrationScales));
+ VibrationParamGenerator.generateVibrationParams(vibrationScales));
verifyZeroInteractions(mMockVibrationScaler);
}
@@ -175,7 +172,8 @@
vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
- mVibratorControlService.setVibrationParams(generateVibrationParams(vibrationScales),
+ mVibratorControlService.setVibrationParams(
+ VibrationParamGenerator.generateVibrationParams(vibrationScales),
mFakeVibratorController);
verify(mMockVibrationScaler).updateAdaptiveHapticsScale(USAGE_ALARM, 0.7f);
@@ -193,7 +191,8 @@
vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
- mVibratorControlService.setVibrationParams(generateVibrationParams(vibrationScales),
+ mVibratorControlService.setVibrationParams(
+ VibrationParamGenerator.generateVibrationParams(vibrationScales),
mFakeVibratorController);
verifyZeroInteractions(mMockVibrationScaler);
@@ -268,28 +267,6 @@
}
}
- private VibrationParam[] generateVibrationParams(SparseArray<Float> vibrationScales) {
- List<VibrationParam> vibrationParamList = new ArrayList<>();
- for (int i = 0; i < vibrationScales.size(); i++) {
- int type = vibrationScales.keyAt(i);
- float scale = vibrationScales.valueAt(i);
-
- vibrationParamList.add(generateVibrationParam(type, scale));
- }
-
- return vibrationParamList.toArray(new VibrationParam[0]);
- }
-
- private VibrationParam generateVibrationParam(int type, float scale) {
- ScaleParam scaleParam = new ScaleParam();
- scaleParam.typesMask = type;
- scaleParam.scale = scale;
- VibrationParam vibrationParam = new VibrationParam();
- vibrationParam.setScale(scaleParam);
-
- return vibrationParam;
- }
-
private int buildVibrationTypesMask(int... types) {
int typesMask = 0;
for (int type : types) {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index e7571ef..ed89ccf 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -50,6 +50,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.res.Resources;
+import android.frameworks.vibrator.ScaleParam;
import android.hardware.input.IInputManager;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerGlobal;
@@ -59,16 +60,17 @@
import android.media.AudioManager;
import android.os.CombinedVibration;
import android.os.ExternalVibration;
+import android.os.ExternalVibrationScale;
import android.os.Handler;
import android.os.IBinder;
import android.os.IExternalVibrationController;
-import android.os.IExternalVibratorService;
import android.os.IVibratorStateListener;
import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.VibrationAttributes;
@@ -82,6 +84,10 @@
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibrationEffectSegment;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.util.SparseArray;
@@ -153,6 +159,8 @@
public MockitoRule rule = MockitoJUnit.rule();
@Rule
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -185,8 +193,10 @@
private Context mContextSpy;
private TestLooper mTestLooper;
private FakeVibrator mVibrator;
+ private FakeVibratorController mFakeVibratorController;
private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
private VibratorManagerService.ExternalVibratorService mExternalVibratorService;
+ private VibratorControlService mVibratorControlService;
private VibrationConfig mVibrationConfig;
private InputManagerGlobal.TestSession mInputManagerGlobalSession;
private InputManager mInputManager;
@@ -197,6 +207,7 @@
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock);
mVibrationConfig = new VibrationConfig(mContextSpy.getResources());
+ mFakeVibratorController = new FakeVibratorController();
ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
@@ -310,6 +321,8 @@
if (service instanceof VibratorManagerService.ExternalVibratorService) {
mExternalVibratorService =
(VibratorManagerService.ExternalVibratorService) service;
+ } else if (service instanceof VibratorControlService) {
+ mVibratorControlService = (VibratorControlService) service;
}
}
@@ -321,9 +334,13 @@
VibratorControllerHolder createVibratorControllerHolder() {
VibratorControllerHolder holder = new VibratorControllerHolder();
- holder.setVibratorController(new FakeVibratorController());
+ holder.setVibratorController(mFakeVibratorController);
return holder;
}
+
+ boolean isServiceDeclared(String name) {
+ return true;
+ }
});
return mService;
}
@@ -1108,12 +1125,13 @@
ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
AUDIO_ALARM_ATTRS,
controller, firstToken);
- int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ ExternalVibrationScale scale =
+ mExternalVibratorService.onExternalVibrationStart(externalVibration);
vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
RINGTONE_ATTRS);
- assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
// The external vibration should have been cancelled
verify(controller).mute();
assertEquals(Arrays.asList(false, true, false),
@@ -1708,13 +1726,14 @@
ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
AUDIO_ALARM_ATTRS,
mock(IExternalVibrationController.class), binderToken);
- int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
- assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
+ externalVibration);
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
when(mVirtualDeviceManagerInternalMock.isAppRunningOnAnyVirtualDevice(UID))
.thenReturn(true);
scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
- assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
}
@Test
@@ -1727,10 +1746,11 @@
ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
AUDIO_ALARM_ATTRS,
mock(IExternalVibrationController.class), binderToken);
- int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
+ externalVibration);
mExternalVibratorService.onExternalVibrationStop(externalVibration);
- assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
assertEquals(Arrays.asList(false, true, false),
mVibratorProviders.get(1).getExternalControlStates());
@@ -1753,17 +1773,19 @@
ExternalVibration firstVibration = new ExternalVibration(UID, PACKAGE_NAME,
AUDIO_ALARM_ATTRS,
firstController, firstToken);
- int firstScale = mExternalVibratorService.onExternalVibrationStart(firstVibration);
+ ExternalVibrationScale firstScale =
+ mExternalVibratorService.onExternalVibrationStart(firstVibration);
AudioAttributes ringtoneAudioAttrs = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
.build();
ExternalVibration secondVibration = new ExternalVibration(UID, PACKAGE_NAME,
ringtoneAudioAttrs, secondController, secondToken);
- int secondScale = mExternalVibratorService.onExternalVibrationStart(secondVibration);
+ ExternalVibrationScale secondScale =
+ mExternalVibratorService.onExternalVibrationStart(secondVibration);
- assertNotEquals(IExternalVibratorService.SCALE_MUTE, firstScale);
- assertNotEquals(IExternalVibratorService.SCALE_MUTE, secondScale);
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, firstScale.scaleLevel);
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, secondScale.scaleLevel);
verify(firstController).mute();
verify(secondController, never()).mute();
// Set external control called only once.
@@ -1799,8 +1821,9 @@
ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
AUDIO_ALARM_ATTRS,
mock(IExternalVibrationController.class));
- int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
- assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ ExternalVibrationScale scale =
+ mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
// Vibration is cancelled.
assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
@@ -1825,9 +1848,10 @@
ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
AUDIO_ALARM_ATTRS,
mock(IExternalVibrationController.class));
- int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ ExternalVibrationScale scale =
+ mExternalVibratorService.onExternalVibrationStart(externalVibration);
// External vibration is ignored.
- assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
// Vibration is not cancelled.
assertFalse(waitUntil(s -> !s.isVibrating(1), service, CLEANUP_TIMEOUT_MILLIS));
@@ -1852,8 +1876,9 @@
ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
AUDIO_ALARM_ATTRS, mock(IExternalVibrationController.class));
- int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
- assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ ExternalVibrationScale scale =
+ mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
// Vibration is cancelled.
assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
@@ -1879,9 +1904,10 @@
ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
AUDIO_NOTIFICATION_ATTRS,
mock(IExternalVibrationController.class));
- int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ ExternalVibrationScale scale =
+ mExternalVibratorService.onExternalVibrationStart(externalVibration);
// New vibration is ignored.
- assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
// Vibration is not cancelled.
assertFalse(waitUntil(s -> !s.isVibrating(1), service, CLEANUP_TIMEOUT_MILLIS));
@@ -1901,18 +1927,19 @@
setRingerMode(AudioManager.RINGER_MODE_SILENT);
createSystemReadyService();
- int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
- assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ ExternalVibrationScale scale =
+ mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
createSystemReadyService();
scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
- assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
createSystemReadyService();
scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
- assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
}
@Test
@@ -1935,14 +1962,14 @@
ExternalVibration vib = new ExternalVibration(UID, PACKAGE_NAME, audioAttrs,
mock(IExternalVibrationController.class));
- int scale = mExternalVibratorService.onExternalVibrationStart(vib);
- assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(vib);
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
mExternalVibratorService.onExternalVibrationStop(vib);
scale = mExternalVibratorService.onExternalVibrationStart(
new ExternalVibration(UID, PACKAGE_NAME, flaggedAudioAttrs,
mock(IExternalVibrationController.class)));
- assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
}
@Test
@@ -1956,10 +1983,91 @@
.build();
createSystemReadyService();
- int scale = mExternalVibratorService.onExternalVibrationStart(
- new ExternalVibration(/* uid= */ 123, PACKAGE_NAME, flaggedAudioAttrs,
- mock(IExternalVibrationController.class)));
- assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ ExternalVibrationScale scale =
+ mExternalVibratorService.onExternalVibrationStart(
+ new ExternalVibration(/* uid= */ 123, PACKAGE_NAME, flaggedAudioAttrs,
+ mock(IExternalVibrationController.class)));
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+ public void onExternalVibration_withAdaptiveHaptics_returnsCorrectAdaptiveScales()
+ throws RemoteException {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL,
+ IVibrator.CAP_AMPLITUDE_CONTROL);
+ createSystemReadyService();
+
+ SparseArray<Float> vibrationScales = new SparseArray<>();
+ vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
+ vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
+
+ mVibratorControlService.setVibrationParams(
+ VibrationParamGenerator.generateVibrationParams(vibrationScales),
+ mFakeVibratorController);
+ ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ AUDIO_ALARM_ATTRS,
+ mock(IExternalVibrationController.class));
+ ExternalVibrationScale scale =
+ mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ mExternalVibratorService.onExternalVibrationStop(externalVibration);
+
+ assertEquals(scale.adaptiveHapticsScale, 0.7f, 0);
+
+ externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ AUDIO_NOTIFICATION_ATTRS,
+ mock(IExternalVibrationController.class));
+ scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ mExternalVibratorService.onExternalVibrationStop(externalVibration);
+
+ assertEquals(scale.adaptiveHapticsScale, 0.4f, 0);
+
+ AudioAttributes ringtoneAudioAttrs = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
+ .build();
+ externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ ringtoneAudioAttrs,
+ mock(IExternalVibrationController.class));
+ scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+
+ assertEquals(scale.adaptiveHapticsScale, 1f, 0);
+ }
+
+ @Test
+ @RequiresFlagsDisabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+ public void onExternalVibration_withAdaptiveHapticsFlagDisabled_alwaysReturnScaleNone()
+ throws RemoteException {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL,
+ IVibrator.CAP_AMPLITUDE_CONTROL);
+ createSystemReadyService();
+
+ SparseArray<Float> vibrationScales = new SparseArray<>();
+ vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
+ vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
+
+ mVibratorControlService.setVibrationParams(
+ VibrationParamGenerator.generateVibrationParams(vibrationScales),
+ mFakeVibratorController);
+ ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ AUDIO_ALARM_ATTRS,
+ mock(IExternalVibrationController.class));
+ ExternalVibrationScale scale =
+ mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ mExternalVibratorService.onExternalVibrationStop(externalVibration);
+
+ assertEquals(scale.adaptiveHapticsScale, 1f, 0);
+
+ mVibratorControlService.setVibrationParams(
+ VibrationParamGenerator.generateVibrationParams(vibrationScales),
+ mFakeVibratorController);
+ externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ AUDIO_NOTIFICATION_ATTRS,
+ mock(IExternalVibrationController.class));
+ scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+
+ assertEquals(scale.adaptiveHapticsScale, 1f, 0);
}
@Test
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/VibrationParamGenerator.java b/services/tests/vibrator/utils/com/android/server/vibrator/VibrationParamGenerator.java
new file mode 100644
index 0000000..a606388
--- /dev/null
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/VibrationParamGenerator.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.frameworks.vibrator.ScaleParam;
+import android.frameworks.vibrator.VibrationParam;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A helper class that can be used to generate arrays of {@link VibrationParam}.
+ */
+public final class VibrationParamGenerator {
+ /**
+ * Generates an array of {@link VibrationParam}.
+ */
+ public static VibrationParam[] generateVibrationParams(SparseArray<Float> vibrationScales) {
+ List<VibrationParam> vibrationParamList = new ArrayList<>();
+ for (int i = 0; i < vibrationScales.size(); i++) {
+ int type = vibrationScales.keyAt(i);
+ float scale = vibrationScales.valueAt(i);
+
+ vibrationParamList.add(generateVibrationParam(type, scale));
+ }
+
+ return vibrationParamList.toArray(new VibrationParam[0]);
+ }
+
+ private static VibrationParam generateVibrationParam(int type, float scale) {
+ ScaleParam scaleParam = new ScaleParam();
+ scaleParam.typesMask = type;
+ scaleParam.scale = scale;
+ VibrationParam vibrationParam = new VibrationParam();
+ vibrationParam.setScale(scaleParam);
+
+ return vibrationParam;
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java
index d84620b..ead36f1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java
@@ -25,6 +25,7 @@
import static org.mockito.Mockito.verify;
import android.platform.test.annotations.Presubmit;
+import android.server.wm.BuildUtils;
import android.view.SurfaceControl;
import android.window.SurfaceSyncGroup;
@@ -370,6 +371,7 @@
assertEquals(0, finishedLatch.getCount());
}
+ @Test
public void testSurfaceSyncGroupTimeout() throws InterruptedException {
final CountDownLatch finishedLatch = new CountDownLatch(1);
SurfaceSyncGroup syncGroup = new SurfaceSyncGroup(TAG);
@@ -386,7 +388,7 @@
// Never finish syncTarget2 so it forces the timeout. Timeout is 1 second so wait a little
// over 1 second to make sure it completes.
- finishedLatch.await(1100, TimeUnit.MILLISECONDS);
+ finishedLatch.await(1100L * BuildUtils.HW_TIMEOUT_MULTIPLIER, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch.getCount());
}
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 889f842..c217780 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -2582,7 +2582,6 @@
if (anyPackagesAppearing()) {
initRecognizer(userHandle);
}
- return;
}
if (curInteractor != null) {
@@ -2631,15 +2630,16 @@
}
}
- // There is no interactor, so just deal with a simple recognizer.
- int change = isPackageDisappearing(curRecognizer.getPackageName());
- if (change == PACKAGE_PERMANENT_CHANGE
- || change == PACKAGE_TEMPORARY_CHANGE) {
- setCurRecognizer(findAvailRecognizer(null, userHandle), userHandle);
+ if (curRecognizer != null) {
+ int change = isPackageDisappearing(curRecognizer.getPackageName());
+ if (change == PACKAGE_PERMANENT_CHANGE
+ || change == PACKAGE_TEMPORARY_CHANGE) {
+ setCurRecognizer(findAvailRecognizer(null, userHandle), userHandle);
- } else if (isPackageModified(curRecognizer.getPackageName())) {
- setCurRecognizer(findAvailRecognizer(curRecognizer.getPackageName(),
- userHandle), userHandle);
+ } else if (isPackageModified(curRecognizer.getPackageName())) {
+ setCurRecognizer(findAvailRecognizer(curRecognizer.getPackageName(),
+ userHandle), userHandle);
+ }
}
}
}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 08c76af..9792cdd 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -2797,13 +2797,10 @@
* calls for a given {@code packageName} and {@code userHandle}.
*
* @param packageName the package name of the app to check calls for.
- * @param userHandle the user handle on which to check for calls.
- * @param detectForAllUsers indicates if calls should be detected across all users. If it is
- * set to {@code true}, and the caller has the ability to interact
- * across users, the userHandle parameter is disregarded.
+ * @param userHandle the user handle to check calls for.
* @return {@code true} if there are ongoing calls, {@code false} otherwise.
- * @throws SecurityException if detectForAllUsers is true or userHandle is not the calling user
- * and the caller does not grant the ability to interact across users.
+ * @throws SecurityException if the userHandle is not the calling user and the caller does not
+ * grant the ability to interact across users.
* @hide
*/
@SystemApi
@@ -2811,11 +2808,45 @@
@RequiresPermission(allOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
public boolean isInSelfManagedCall(@NonNull String packageName,
- @NonNull UserHandle userHandle, boolean detectForAllUsers) {
+ @NonNull UserHandle userHandle) {
ITelecomService service = getTelecomService();
if (service != null) {
try {
return service.isInSelfManagedCall(packageName, userHandle,
+ mContext.getOpPackageName(), false);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException isInSelfManagedCall: " + e);
+ e.rethrowFromSystemServer();
+ return false;
+ }
+ } else {
+ throw new IllegalStateException("Telecom service is not present");
+ }
+ }
+
+ /**
+ * Determines whether there are any ongoing {@link PhoneAccount#CAPABILITY_SELF_MANAGED}
+ * calls for a given {@code packageName} amongst all users, given that detectForAllUsers is true
+ * and the caller has the ability to interact across users. If detectForAllUsers isn't enabled,
+ * the calls will be checked against the caller.
+ *
+ * @param packageName the package name of the app to check calls for.
+ * @param detectForAllUsers indicates if calls should be detected across all users.
+ * @return {@code true} if there are ongoing calls, {@code false} otherwise.
+ * @throws SecurityException if detectForAllUsers is true and the caller does not grant the
+ * ability to interact across users.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
+ @RequiresPermission(allOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+ public boolean isInSelfManagedCall(@NonNull String packageName,
+ boolean detectForAllUsers) {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.isInSelfManagedCall(packageName, null,
mContext.getOpPackageName(), detectForAllUsers);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException isInSelfManagedCall: " + e);
diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java
index 5af2c34..5524541 100644
--- a/telephony/java/android/service/euicc/EuiccService.java
+++ b/telephony/java/android/service/euicc/EuiccService.java
@@ -856,10 +856,22 @@
int slotId, IGetAvailableMemoryInBytesCallback callback) {
mExecutor.execute(
() -> {
- long availableMemoryInBytes =
- EuiccService.this.onGetAvailableMemoryInBytes(slotId);
+ long availableMemoryInBytes = EuiccManager.EUICC_MEMORY_FIELD_UNAVAILABLE;
+ String unsupportedOperationMessage = "";
try {
- callback.onSuccess(availableMemoryInBytes);
+ availableMemoryInBytes =
+ EuiccService.this.onGetAvailableMemoryInBytes(slotId);
+ } catch (UnsupportedOperationException e) {
+ unsupportedOperationMessage = e.getMessage();
+ }
+
+ try {
+ if (!unsupportedOperationMessage.isEmpty()) {
+ callback.onUnsupportedOperationException(
+ unsupportedOperationMessage);
+ } else {
+ callback.onSuccess(availableMemoryInBytes);
+ }
} catch (RemoteException e) {
// Can't communicate with the phone process; ignore.
}
diff --git a/telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl b/telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl
index bd6d19b..e550e77 100644
--- a/telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl
+++ b/telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl
@@ -19,4 +19,5 @@
/** @hide */
oneway interface IGetAvailableMemoryInBytesCallback {
void onSuccess(long availableMemoryInBytes);
+ void onUnsupportedOperationException(String message);
}