Merge "Style the Magnification Window - Window Mode Drag" into udc-dev
diff --git a/OWNERS b/OWNERS
index 09a721f..7bea58b 100644
--- a/OWNERS
+++ b/OWNERS
@@ -35,3 +35,5 @@
per-file framework-jarjar-rules.txt = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION}
per-file TestProtoLibraries.bp = file:platform/platform_testing:/libraries/health/OWNERS
per-file TestProtoLibraries.bp = file:platform/tools/tradefederation:/OWNERS
+
+per-file ZYGOTE_OWNERS = file:/ZYGOTE_OWNERS
diff --git a/ZYGOTE_OWNERS b/ZYGOTE_OWNERS
index 90a185b..f6d15e0 100644
--- a/ZYGOTE_OWNERS
+++ b/ZYGOTE_OWNERS
@@ -1,4 +1,3 @@
-calin@google.com
chriswailes@google.com
maco@google.com
narayan@google.com
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 79a2659..5de1172 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -5277,6 +5277,8 @@
pw.print(" mScreenLocked="); pw.println(mScreenLocked);
pw.print(" mNetworkConnected="); pw.println(mNetworkConnected);
pw.print(" mCharging="); pw.println(mCharging);
+ pw.print(" activeEmergencyCall=");
+ pw.println(mEmergencyCallListener.isEmergencyCallActive());
if (mConstraints.size() != 0) {
pw.println(" mConstraints={");
for (int i = 0; i < mConstraints.size(); i++) {
diff --git a/core/api/current.txt b/core/api/current.txt
index 259f1e9..a665bcd 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -4492,8 +4492,8 @@
method public void openOptionsMenu();
method public void overrideActivityTransition(int, @AnimRes int, @AnimRes int);
method public void overrideActivityTransition(int, @AnimRes int, @AnimRes int, @ColorInt int);
- method public void overridePendingTransition(int, int);
- method public void overridePendingTransition(int, int, int);
+ method @Deprecated public void overridePendingTransition(int, int);
+ method @Deprecated public void overridePendingTransition(int, int, int);
method public void postponeEnterTransition();
method public void recreate();
method public void registerActivityLifecycleCallbacks(@NonNull android.app.Application.ActivityLifecycleCallbacks);
@@ -10333,7 +10333,7 @@
field public static final int BIND_AUTO_CREATE = 1; // 0x1
field public static final int BIND_DEBUG_UNBIND = 2; // 0x2
field public static final int BIND_EXTERNAL_SERVICE = -2147483648; // 0x80000000
- field public static final long BIND_EXTERNAL_SERVICE_LONG = -9223372036854775808L; // 0x8000000000000000L
+ field public static final long BIND_EXTERNAL_SERVICE_LONG = 4611686018427387904L; // 0x4000000000000000L
field public static final int BIND_IMPORTANT = 64; // 0x40
field public static final int BIND_INCLUDE_CAPABILITIES = 4096; // 0x1000
field public static final int BIND_NOT_FOREGROUND = 4; // 0x4
@@ -10441,7 +10441,6 @@
}
public static final class Context.BindServiceFlags {
- method public long getValue();
method @NonNull public static android.content.Context.BindServiceFlags of(long);
}
@@ -19593,7 +19592,7 @@
public final class VirtualDisplayConfig implements android.os.Parcelable {
method public int describeContents();
method public int getDensityDpi();
- method @NonNull public java.util.List<java.lang.String> getDisplayCategories();
+ method @NonNull public java.util.Set<java.lang.String> getDisplayCategories();
method public int getFlags();
method public int getHeight();
method @NonNull public String getName();
@@ -19608,7 +19607,7 @@
ctor public VirtualDisplayConfig.Builder(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int);
method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder addDisplayCategory(@NonNull String);
method @NonNull public android.hardware.display.VirtualDisplayConfig build();
- method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDisplayCategories(@NonNull java.util.List<java.lang.String>);
+ method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDisplayCategories(@NonNull java.util.Set<java.lang.String>);
method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setFlags(int);
method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setRequestedRefreshRate(@FloatRange(from=0.0f) float);
method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setSurface(@Nullable android.view.Surface);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 05f31a5..76a47e4 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3362,7 +3362,7 @@
}
public static final class VirtualSensorConfig.Builder {
- ctor public VirtualSensorConfig.Builder(int, @NonNull String);
+ ctor public VirtualSensorConfig.Builder(@IntRange(from=1) int, @NonNull String);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig build();
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setDirectChannelTypesSupported(int);
method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setHighestDirectReportRateLevel(int);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d9c7a27..125e727 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6621,7 +6621,9 @@
* the incoming activity. Use 0 for no animation.
* @param exitAnim A resource ID of the animation resource to use for
* the outgoing activity. Use 0 for no animation.
+ * @deprecated Use {@link #overrideActivityTransition(int, int, int)}} instead.
*/
+ @Deprecated
public void overridePendingTransition(int enterAnim, int exitAnim) {
overridePendingTransition(enterAnim, exitAnim, 0);
}
@@ -6644,7 +6646,9 @@
* the outgoing activity. Use 0 for no animation.
* @param backgroundColor The background color to use for the background during the animation if
* the animation requires a background. Set to 0 to not override the default color.
+ * @deprecated Use {@link #overrideActivityTransition(int, int, int, int)}} instead.
*/
+ @Deprecated
public void overridePendingTransition(int enterAnim, int exitAnim, int backgroundColor) {
ActivityClient.getInstance().overridePendingTransition(mToken, getPackageName(), enterAnim,
exitAnim, backgroundColor);
diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java
index f4cd60d..97cc706 100644
--- a/core/java/android/app/LocaleConfig.java
+++ b/core/java/android/app/LocaleConfig.java
@@ -58,10 +58,10 @@
*
* @attr ref android.R.styleable#LocaleConfig_Locale_name
* @attr ref android.R.styleable#AndroidManifestApplication_localeConfig
- *
- * <p>For more information about the LocaleConfig overridden by the application, see
- * TODO(b/261528306): add link to guide
*/
+// Add following to last Note: when guide is written:
+// For more information about the LocaleConfig overridden by the application, see TODO(b/261528306):
+// add link to guide
public class LocaleConfig implements Parcelable {
private static final String TAG = "LocaleConfig";
public static final String TAG_LOCALE_CONFIG = "locale-config";
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 6cc4c8a..90681cb 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -545,12 +545,13 @@
@VirtualDisplayFlag int flags,
@Nullable @CallbackExecutor Executor executor,
@Nullable VirtualDisplay.Callback callback) {
- VirtualDisplayConfig config = new VirtualDisplayConfig.Builder(
+ VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
getVirtualDisplayName(), width, height, densityDpi)
- .setSurface(surface)
- .setFlags(flags)
- .build();
- return createVirtualDisplay(config, executor, callback);
+ .setFlags(flags);
+ if (surface != null) {
+ builder.setSurface(surface);
+ }
+ return createVirtualDisplay(builder.build(), executor, callback);
}
/**
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
index ffbdff8..401e754 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
@@ -17,6 +17,7 @@
package android.companion.virtual.sensor;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -167,7 +168,10 @@
* @param name The name of the sensor. Must be unique among all sensors with the same type
* that belong to the same virtual device.
*/
- public Builder(int type, @NonNull String name) {
+ public Builder(@IntRange(from = 1) int type, @NonNull String name) {
+ if (type <= 0) {
+ throw new IllegalArgumentException("Virtual sensor type must be positive");
+ }
mType = type;
mName = Objects.requireNonNull(name);
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a412560..36f7ff5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -316,10 +316,12 @@
BIND_ALLOW_ACTIVITY_STARTS,
BIND_INCLUDE_CAPABILITIES,
BIND_SHARED_ISOLATED_PROCESS,
- // Intentionally not included, because it'd cause sign-extension.
+ // Intentionally not include BIND_EXTERNAL_SERVICE, because it'd cause sign-extension.
// This would allow Android Studio to show a warning, if someone tries to use
// BIND_EXTERNAL_SERVICE BindServiceFlags.
- BIND_EXTERNAL_SERVICE_LONG
+ BIND_EXTERNAL_SERVICE_LONG,
+ // Make sure no flag uses the sign bit (most significant bit) of the long integer,
+ // to avoid future confusion.
})
@Retention(RetentionPolicy.SOURCE)
public @interface BindServiceFlagsLongBits {}
@@ -338,6 +340,7 @@
/**
* @return Return flags in 64 bits long integer.
+ * @hide
*/
public long getValue() {
return mValue;
@@ -678,13 +681,11 @@
*/
public static final int BIND_EXTERNAL_SERVICE = 0x80000000;
-
/**
* Works in the same way as {@link #BIND_EXTERNAL_SERVICE}, but it's defined as a (@code long)
* value that is compatible to {@link BindServiceFlags}.
*/
- public static final long BIND_EXTERNAL_SERVICE_LONG = 0x8000_0000_0000_0000L;
-
+ public static final long BIND_EXTERNAL_SERVICE_LONG = 1L << 62;
/**
* These bind flags reduce the strength of the binding such that we shouldn't
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index a6a6215..cb988df 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2935,13 +2935,14 @@
* <li>The {@link InstallSourceInfo#getUpdateOwnerPackageName() update owner}
* of an existing version of the app (in other words, this install session is
* an app update) if the update ownership enforcement is enabled.</li>
- * <li>The {@link InstallSourceInfo#getInstallingPackageName() installer of
- * record} of an existing version of the app (in other words, this install
+ * <li>The
+ * {@link InstallSourceInfo#getInstallingPackageName() installer of record}
+ * of an existing version of the app (in other words, this install
* session is an app update) if the update ownership enforcement isn't
* enabled.</li>
* <li>Updating itself.</li>
* </ul>
- * </li>>
+ * </li>
* <li>The installer declares the
* {@link android.Manifest.permission#UPDATE_PACKAGES_WITHOUT_USER_ACTION
* UPDATE_PACKAGES_WITHOUT_USER_ACTION} permission.</li>
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 50dd7a0..6ae71d2 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1409,9 +1409,10 @@
* @param hdrConversionMode The {@link HdrConversionMode} to set.
* Note, {@code HdrConversionMode.preferredHdrOutputType} is only applicable when
* {@code HdrConversionMode.conversionMode} is {@link HdrConversionMode#HDR_CONVERSION_FORCE}.
+ * If {@code HdrConversionMode.preferredHdrOutputType} is not set in case when
+ * {@code HdrConversionMode.conversionMode} is {@link HdrConversionMode#HDR_CONVERSION_FORCE},
+ * it means that preferred output type is SDR.
*
- * @throws IllegalArgumentException if hdrConversionMode.preferredHdrOutputType is not set
- * when hdrConversionMode.conversionMode is {@link HdrConversionMode#HDR_CONVERSION_FORCE}.
* @throws IllegalArgumentException if hdrConversionMode.preferredHdrOutputType is set but
* hdrConversionMode.conversionMode is not {@link HdrConversionMode#HDR_CONVERSION_FORCE}.
*
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 6b56a06..067ae4d 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -27,13 +27,13 @@
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.ArraySet;
import android.view.Display;
import android.view.Surface;
-import java.util.ArrayList;
import java.util.Collections;
-import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* Holds configuration used to create {@link VirtualDisplay} instances.
@@ -51,8 +51,8 @@
private final Surface mSurface;
private final String mUniqueId;
private final int mDisplayIdToMirror;
- private final boolean mWindowManagerMirroring;
- private ArrayList<String> mDisplayCategories = null;
+ private final boolean mWindowManagerMirroringEnabled;
+ private ArraySet<String> mDisplayCategories = null;
private final float mRequestedRefreshRate;
private VirtualDisplayConfig(
@@ -64,8 +64,8 @@
@Nullable Surface surface,
@Nullable String uniqueId,
int displayIdToMirror,
- boolean windowManagerMirroring,
- @NonNull ArrayList<String> displayCategories,
+ boolean windowManagerMirroringEnabled,
+ @NonNull ArraySet<String> displayCategories,
float requestedRefreshRate) {
mName = name;
mWidth = width;
@@ -75,7 +75,7 @@
mSurface = surface;
mUniqueId = uniqueId;
mDisplayIdToMirror = displayIdToMirror;
- mWindowManagerMirroring = windowManagerMirroring;
+ mWindowManagerMirroringEnabled = windowManagerMirroringEnabled;
mDisplayCategories = displayCategories;
mRequestedRefreshRate = requestedRefreshRate;
}
@@ -151,8 +151,8 @@
* if DisplayManager should record contents instead.
* @hide
*/
- public boolean isWindowManagerMirroring() {
- return mWindowManagerMirroring;
+ public boolean isWindowManagerMirroringEnabled() {
+ return mWindowManagerMirroringEnabled;
}
/**
@@ -161,8 +161,8 @@
* @see Builder#setDisplayCategories
*/
@NonNull
- public List<String> getDisplayCategories() {
- return Collections.unmodifiableList(mDisplayCategories);
+ public Set<String> getDisplayCategories() {
+ return Collections.unmodifiableSet(mDisplayCategories);
}
/**
@@ -185,8 +185,8 @@
dest.writeTypedObject(mSurface, flags);
dest.writeString8(mUniqueId);
dest.writeInt(mDisplayIdToMirror);
- dest.writeBoolean(mWindowManagerMirroring);
- dest.writeStringList(mDisplayCategories);
+ dest.writeBoolean(mWindowManagerMirroringEnabled);
+ dest.writeArraySet(mDisplayCategories);
dest.writeFloat(mRequestedRefreshRate);
}
@@ -210,7 +210,7 @@
&& Objects.equals(mSurface, that.mSurface)
&& Objects.equals(mUniqueId, that.mUniqueId)
&& mDisplayIdToMirror == that.mDisplayIdToMirror
- && mWindowManagerMirroring == that.mWindowManagerMirroring
+ && mWindowManagerMirroringEnabled == that.mWindowManagerMirroringEnabled
&& Objects.equals(mDisplayCategories, that.mDisplayCategories)
&& mRequestedRefreshRate == that.mRequestedRefreshRate;
}
@@ -219,7 +219,7 @@
public int hashCode() {
int hashCode = Objects.hash(
mName, mWidth, mHeight, mDensityDpi, mFlags, mSurface, mUniqueId,
- mDisplayIdToMirror, mWindowManagerMirroring, mDisplayCategories,
+ mDisplayIdToMirror, mWindowManagerMirroringEnabled, mDisplayCategories,
mRequestedRefreshRate);
return hashCode;
}
@@ -236,7 +236,7 @@
+ " mSurface=" + mSurface
+ " mUniqueId=" + mUniqueId
+ " mDisplayIdToMirror=" + mDisplayIdToMirror
- + " mWindowManagerMirroring=" + mWindowManagerMirroring
+ + " mWindowManagerMirroringEnabled=" + mWindowManagerMirroringEnabled
+ " mDisplayCategories=" + mDisplayCategories
+ " mRequestedRefreshRate=" + mRequestedRefreshRate
+ ")";
@@ -251,9 +251,8 @@
mSurface = in.readTypedObject(Surface.CREATOR);
mUniqueId = in.readString8();
mDisplayIdToMirror = in.readInt();
- mWindowManagerMirroring = in.readBoolean();
- mDisplayCategories = new ArrayList<>();
- in.readStringList(mDisplayCategories);
+ mWindowManagerMirroringEnabled = in.readBoolean();
+ mDisplayCategories = (ArraySet<String>) in.readArraySet(null);
mRequestedRefreshRate = in.readFloat();
}
@@ -283,8 +282,8 @@
private Surface mSurface = null;
private String mUniqueId = null;
private int mDisplayIdToMirror = DEFAULT_DISPLAY;
- private boolean mWindowManagerMirroring = false;
- private ArrayList<String> mDisplayCategories = new ArrayList<>();
+ private boolean mWindowManagerMirroringEnabled = false;
+ private ArraySet<String> mDisplayCategories = new ArraySet<>();
private float mRequestedRefreshRate = 0.0f;
/**
@@ -370,8 +369,8 @@
* @hide
*/
@NonNull
- public Builder setWindowManagerMirroring(boolean windowManagerMirroring) {
- mWindowManagerMirroring = windowManagerMirroring;
+ public Builder setWindowManagerMirroringEnabled(boolean windowManagerMirroringEnabled) {
+ mWindowManagerMirroringEnabled = windowManagerMirroringEnabled;
return this;
}
@@ -383,7 +382,7 @@
* {@link android.content.pm.ActivityInfo#requiredDisplayCategory}.
*/
@NonNull
- public Builder setDisplayCategories(@NonNull List<String> displayCategories) {
+ public Builder setDisplayCategories(@NonNull Set<String> displayCategories) {
mDisplayCategories.clear();
mDisplayCategories.addAll(Objects.requireNonNull(displayCategories));
return this;
@@ -435,7 +434,7 @@
mSurface,
mUniqueId,
mDisplayIdToMirror,
- mWindowManagerMirroring,
+ mWindowManagerMirroringEnabled,
mDisplayCategories,
mRequestedRefreshRate);
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 26bc5d12..259012f 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -60,7 +60,6 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -181,9 +180,6 @@
private final ArrayMap<IBinder, IWallpaperEngineWrapper> mActiveEngines = new ArrayMap<>();
- private Handler mBackgroundHandler;
- private HandlerThread mBackgroundThread;
-
static final class WallpaperCommand {
String action;
int x;
@@ -202,6 +198,14 @@
*/
public class Engine {
IWallpaperEngineWrapper mIWallpaperEngine;
+ final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4);
+ final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4);
+
+ // 2D matrix [x][y] to represent a page of a portion of a window
+ EngineWindowPage[] mWindowPages = new EngineWindowPage[0];
+ Bitmap mLastScreenshot;
+ int mLastWindowPage = -1;
+ private boolean mResetWindowPages;
// Copies from mIWallpaperEngine.
HandlerCaller mCaller;
@@ -262,27 +266,11 @@
final Object mLock = new Object();
boolean mOffsetMessageEnqueued;
-
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
float mPendingXOffset;
float mPendingYOffset;
float mPendingXOffsetStep;
float mPendingYOffsetStep;
-
- /**
- * local color extraction related fields
- * to be used by the background thread only (except the atomic boolean)
- */
- final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4);
- final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4);
- private long mLastProcessLocalColorsTimestamp;
- private AtomicBoolean mProcessLocalColorsPending = new AtomicBoolean(false);
- private int mPixelCopyCount = 0;
- // 2D matrix [x][y] to represent a page of a portion of a window
- EngineWindowPage[] mWindowPages = new EngineWindowPage[0];
- Bitmap mLastScreenshot;
- private boolean mResetWindowPages;
-
boolean mPendingSync;
MotionEvent mPendingMove;
boolean mIsInAmbientMode;
@@ -291,8 +279,12 @@
private long mLastColorInvalidation;
private final Runnable mNotifyColorsChanged = this::notifyColorsChanged;
+ // used to throttle processLocalColors
+ private long mLastProcessLocalColorsTimestamp;
+ private AtomicBoolean mProcessLocalColorsPending = new AtomicBoolean(false);
private final Supplier<Long> mClockFunction;
private final Handler mHandler;
+
private Display mDisplay;
private Context mDisplayContext;
private int mDisplayState;
@@ -862,7 +854,7 @@
+ "was not established.");
}
mResetWindowPages = true;
- processLocalColors();
+ processLocalColors(mPendingXOffset, mPendingXOffsetStep);
} catch (RemoteException e) {
Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e);
}
@@ -1400,7 +1392,7 @@
resetWindowPages();
mSession.finishDrawing(mWindow, null /* postDrawTransaction */,
Integer.MAX_VALUE);
- processLocalColors();
+ processLocalColors(mPendingXOffset, mPendingXOffsetStep);
}
reposition();
reportEngineShown(shouldWaitForEngineShown());
@@ -1544,7 +1536,7 @@
if (!mDestroyed) {
mVisible = visible;
reportVisibility(false);
- if (mReportedVisible) processLocalColors();
+ if (mReportedVisible) processLocalColors(mPendingXOffset, mPendingXOffsetStep);
} else {
AnimationHandler.requestAnimatorsEnabled(visible, this);
}
@@ -1647,41 +1639,31 @@
}
// setup local color extraction data
- processLocalColors();
+ processLocalColors(xOffset, xOffsetStep);
}
/**
* Thread-safe util to call {@link #processLocalColorsInternal} with a minimum interval of
* {@link #PROCESS_LOCAL_COLORS_INTERVAL_MS} between two calls.
*/
- private void processLocalColors() {
+ private void processLocalColors(float xOffset, float xOffsetStep) {
if (mProcessLocalColorsPending.compareAndSet(false, true)) {
final long now = mClockFunction.get();
final long timeSinceLastColorProcess = now - mLastProcessLocalColorsTimestamp;
final long timeToWait = Math.max(0,
PROCESS_LOCAL_COLORS_INTERVAL_MS - timeSinceLastColorProcess);
- mBackgroundHandler.postDelayed(() -> {
+ mHandler.postDelayed(() -> {
mLastProcessLocalColorsTimestamp = now + timeToWait;
mProcessLocalColorsPending.set(false);
- processLocalColorsInternal();
+ processLocalColorsInternal(xOffset, xOffsetStep);
}, timeToWait);
}
}
- private void processLocalColorsInternal() {
+ private void processLocalColorsInternal(float xOffset, float xOffsetStep) {
// implemented by the wallpaper
if (supportsLocalColorExtraction()) return;
- assertBackgroundThread();
- float xOffset;
- float xOffsetStep;
- float wallpaperDimAmount;
- synchronized (mLock) {
- xOffset = mPendingXOffset;
- xOffsetStep = mPendingXOffsetStep;
- wallpaperDimAmount = mWallpaperDimAmount;
- }
-
if (DEBUG) {
Log.d(TAG, "processLocalColors " + xOffset + " of step "
+ xOffsetStep);
@@ -1744,7 +1726,7 @@
xPage = mWindowPages.length - 1;
}
current = mWindowPages[xPage];
- updatePage(current, xPage, xPages, wallpaperDimAmount);
+ updatePage(current, xPage, xPages, finalXOffsetStep);
Trace.endSection();
}
@@ -1764,23 +1746,16 @@
}
}
- /**
- * Must be called with the surface lock held.
- * Must not be called if the surface is not valid.
- * Will unlock the surface when done using it.
- */
void updatePage(EngineWindowPage currentPage, int pageIndx, int numPages,
- float wallpaperDimAmount) {
-
- assertBackgroundThread();
-
+ float xOffsetStep) {
// in case the clock is zero, we start with negative time
long current = SystemClock.elapsedRealtime() - DEFAULT_UPDATE_SCREENSHOT_DURATION;
long lapsed = current - currentPage.getLastUpdateTime();
// Always update the page when the last update time is <= 0
// This is important especially when the device first boots
- if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) return;
-
+ if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) {
+ return;
+ }
Surface surface = mSurfaceHolder.getSurface();
if (!surface.isValid()) return;
boolean widthIsLarger = mSurfaceSize.x > mSurfaceSize.y;
@@ -1796,42 +1771,33 @@
Bitmap screenShot = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
final Bitmap finalScreenShot = screenShot;
- final String pixelCopySectionName = "WallpaperService#pixelCopy";
- final int pixelCopyCount = mPixelCopyCount++;
- Trace.beginAsyncSection(pixelCopySectionName, pixelCopyCount);
- try {
- PixelCopy.request(surface, screenShot, (res) -> {
- Trace.endAsyncSection(pixelCopySectionName, pixelCopyCount);
- if (DEBUG) Log.d(TAG, "result of pixel copy is " + res);
- if (res != PixelCopy.SUCCESS) {
- Bitmap lastBitmap = currentPage.getBitmap();
- // assign the last bitmap taken for now
- currentPage.setBitmap(mLastScreenshot);
- Bitmap lastScreenshot = mLastScreenshot;
- if (lastScreenshot != null && !lastScreenshot.isRecycled()
- && !Objects.equals(lastBitmap, lastScreenshot)) {
- updatePageColors(currentPage, pageIndx, numPages, wallpaperDimAmount);
- }
- } else {
- mLastScreenshot = finalScreenShot;
- // going to hold this lock for a while
- currentPage.setBitmap(finalScreenShot);
- currentPage.setLastUpdateTime(current);
- updatePageColors(currentPage, pageIndx, numPages, wallpaperDimAmount);
+ Trace.beginSection("WallpaperService#pixelCopy");
+ PixelCopy.request(surface, screenShot, (res) -> {
+ Trace.endSection();
+ if (DEBUG) Log.d(TAG, "result of pixel copy is " + res);
+ if (res != PixelCopy.SUCCESS) {
+ Bitmap lastBitmap = currentPage.getBitmap();
+ // assign the last bitmap taken for now
+ currentPage.setBitmap(mLastScreenshot);
+ Bitmap lastScreenshot = mLastScreenshot;
+ if (lastScreenshot != null && !lastScreenshot.isRecycled()
+ && !Objects.equals(lastBitmap, lastScreenshot)) {
+ updatePageColors(currentPage, pageIndx, numPages, xOffsetStep);
}
- }, mBackgroundHandler);
- } catch (IllegalArgumentException e) {
- // this can potentially happen if the surface is invalidated right between the
- // surface.isValid() check and the PixelCopy operation.
- // in this case, stop: we'll compute colors on the next processLocalColors call.
- Log.i(TAG, "Cancelling processLocalColors: exception caught during PixelCopy");
- }
+ } else {
+ mLastScreenshot = finalScreenShot;
+ // going to hold this lock for a while
+ currentPage.setBitmap(finalScreenShot);
+ currentPage.setLastUpdateTime(current);
+ updatePageColors(currentPage, pageIndx, numPages, xOffsetStep);
+ }
+ }, mHandler);
+
}
// locked by the passed page
- private void updatePageColors(
- EngineWindowPage page, int pageIndx, int numPages, float wallpaperDimAmount) {
+ private void updatePageColors(EngineWindowPage page, int pageIndx, int numPages,
+ float xOffsetStep) {
if (page.getBitmap() == null) return;
- assertBackgroundThread();
Trace.beginSection("WallpaperService#updatePageColors");
if (DEBUG) {
Log.d(TAG, "updatePageColorsLocked for page " + pageIndx + " with areas "
@@ -1853,7 +1819,7 @@
Log.e(TAG, "Error creating page local color bitmap", e);
continue;
}
- WallpaperColors color = WallpaperColors.fromBitmap(target, wallpaperDimAmount);
+ WallpaperColors color = WallpaperColors.fromBitmap(target, mWallpaperDimAmount);
target.recycle();
WallpaperColors currentColor = page.getColors(area);
@@ -1870,26 +1836,17 @@
+ " local color callback for area" + area + " for page " + pageIndx
+ " of " + numPages);
}
- mHandler.post(() -> {
- try {
- mConnection.onLocalWallpaperColorsChanged(area, color,
- mDisplayContext.getDisplayId());
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e);
- }
- });
+ try {
+ mConnection.onLocalWallpaperColorsChanged(area, color,
+ mDisplayContext.getDisplayId());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e);
+ }
}
}
Trace.endSection();
}
- private void assertBackgroundThread() {
- if (!mBackgroundHandler.getLooper().isCurrentThread()) {
- throw new IllegalStateException(
- "ProcessLocalColors should be called from the background thread");
- }
- }
-
private RectF generateSubRect(RectF in, int pageInx, int numPages) {
float minLeft = (float) (pageInx) / (float) (numPages);
float maxRight = (float) (pageInx + 1) / (float) (numPages);
@@ -1914,6 +1871,7 @@
if (supportsLocalColorExtraction()) return;
if (!mResetWindowPages) return;
mResetWindowPages = false;
+ mLastWindowPage = -1;
for (int i = 0; i < mWindowPages.length; i++) {
mWindowPages[i].setLastUpdateTime(0L);
}
@@ -1939,10 +1897,12 @@
if (DEBUG) {
Log.d(TAG, "addLocalColorsAreas adding local color areas " + regions);
}
- mBackgroundHandler.post(() -> {
+ mHandler.post(() -> {
mLocalColorsToAdd.addAll(regions);
- processLocalColors();
+ processLocalColors(mPendingXOffset, mPendingYOffset);
});
+
+
}
/**
@@ -1952,7 +1912,7 @@
*/
public void removeLocalColorsAreas(@NonNull List<RectF> regions) {
if (supportsLocalColorExtraction()) return;
- mBackgroundHandler.post(() -> {
+ mHandler.post(() -> {
float step = mPendingXOffsetStep;
mLocalColorsToAdd.removeAll(regions);
mLocalColorAreas.removeAll(regions);
@@ -2618,9 +2578,6 @@
@Override
public void onCreate() {
Trace.beginSection("WPMS.onCreate");
- mBackgroundThread = new HandlerThread("DefaultWallpaperLocalColorExtractor");
- mBackgroundThread.start();
- mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
super.onCreate();
Trace.endSection();
}
@@ -2633,7 +2590,6 @@
engineWrapper.destroy();
}
mActiveEngines.clear();
- mBackgroundThread.quitSafely();
Trace.endSection();
}
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 07ac597..b4675e0 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -81,7 +81,10 @@
// GC'd while the native peer of the receiver is using them.
private MessageQueue mMessageQueue;
+ private final VsyncEventData mVsyncEventData = new VsyncEventData();
+
private static native long nativeInit(WeakReference<DisplayEventReceiver> receiver,
+ WeakReference<VsyncEventData> vsyncEventData,
MessageQueue messageQueue, int vsyncSource, int eventRegistration, long layerHandle);
private static native long nativeGetDisplayEventReceiverFinalizer();
@FastNative
@@ -124,7 +127,9 @@
}
mMessageQueue = looper.getQueue();
- mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
+ mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this),
+ new WeakReference<VsyncEventData>(mVsyncEventData),
+ mMessageQueue,
vsyncSource, eventRegistration, layerHandle);
mFreeNativeResources = sNativeAllocationRegistry.registerNativeAllocation(this,
mReceiverPtr);
@@ -142,9 +147,6 @@
}
static final class VsyncEventData {
-
- static final FrameTimeline[] INVALID_FRAME_TIMELINES =
- {new FrameTimeline(FrameInfo.INVALID_VSYNC_ID, Long.MAX_VALUE, Long.MAX_VALUE)};
// The amount of frame timeline choices.
// Must be in sync with VsyncEventData::kFrameTimelinesLength in
// frameworks/native/libs/gui/include/gui/VsyncEventData.h. If they do not match, a runtime
@@ -152,6 +154,10 @@
static final int FRAME_TIMELINES_LENGTH = 7;
public static class FrameTimeline {
+ FrameTimeline() {}
+
+ // Called from native code.
+ @SuppressWarnings("unused")
FrameTimeline(long vsyncId, long expectedPresentationTime, long deadline) {
this.vsyncId = vsyncId;
this.expectedPresentationTime = expectedPresentationTime;
@@ -160,14 +166,14 @@
// The frame timeline vsync id, used to correlate a frame
// produced by HWUI with the timeline data stored in Surface Flinger.
- public final long vsyncId;
+ public long vsyncId = FrameInfo.INVALID_VSYNC_ID;
// The frame timestamp for when the frame is expected to be presented.
- public final long expectedPresentationTime;
+ public long expectedPresentationTime = Long.MAX_VALUE;
// The frame deadline timestamp in {@link System#nanoTime()} timebase that it is
// allotted for the frame to be completed.
- public final long deadline;
+ public long deadline = Long.MAX_VALUE;
}
/**
@@ -175,11 +181,18 @@
* {@link FrameInfo#VSYNC} to the current vsync in case Choreographer callback was heavily
* delayed by the app.
*/
- public final long frameInterval;
+ public long frameInterval = -1;
public final FrameTimeline[] frameTimelines;
- public final int preferredFrameTimelineIndex;
+ public int preferredFrameTimelineIndex = 0;
+
+ VsyncEventData() {
+ frameTimelines = new FrameTimeline[FRAME_TIMELINES_LENGTH];
+ for (int i = 0; i < frameTimelines.length; i++) {
+ frameTimelines[i] = new FrameTimeline();
+ }
+ }
// Called from native code.
@SuppressWarnings("unused")
@@ -190,12 +203,6 @@
this.frameInterval = frameInterval;
}
- VsyncEventData() {
- this.frameInterval = -1;
- this.frameTimelines = INVALID_FRAME_TIMELINES;
- this.preferredFrameTimelineIndex = 0;
- }
-
public FrameTimeline preferredFrameTimeline() {
return frameTimelines[preferredFrameTimelineIndex];
}
@@ -299,9 +306,8 @@
// Called from native code.
@SuppressWarnings("unused")
- private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame,
- VsyncEventData vsyncEventData) {
- onVsync(timestampNanos, physicalDisplayId, frame, vsyncEventData);
+ private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
+ onVsync(timestampNanos, physicalDisplayId, frame, mVsyncEventData);
}
// Called from native code.
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index dfb11bc..f863678 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -454,11 +454,6 @@
*/
int TRANSIT_WAKE = 11;
/**
- * The screen is turning off. This is used as a message to stop all animations.
- * @hide
- */
- int TRANSIT_SLEEP = 12;
- /**
* The first slot for custom transition types. Callers (like Shell) can make use of custom
* transition types for dealing with special cases. These types are effectively ignored by
* Core and will just be passed along as part of TransitionInfo objects. An example is
@@ -467,7 +462,7 @@
* implementation.
* @hide
*/
- int TRANSIT_FIRST_CUSTOM = 13;
+ int TRANSIT_FIRST_CUSTOM = 12;
/**
* @hide
@@ -485,7 +480,6 @@
TRANSIT_KEYGUARD_UNOCCLUDE,
TRANSIT_PIP,
TRANSIT_WAKE,
- TRANSIT_SLEEP,
TRANSIT_FIRST_CUSTOM
})
@Retention(RetentionPolicy.SOURCE)
@@ -1443,7 +1437,6 @@
case TRANSIT_KEYGUARD_UNOCCLUDE: return "KEYGUARD_UNOCCLUDE";
case TRANSIT_PIP: return "PIP";
case TRANSIT_WAKE: return "WAKE";
- case TRANSIT_SLEEP: return "SLEEP";
case TRANSIT_FIRST_CUSTOM: return "FIRST_CUSTOM";
default:
if (type > TRANSIT_FIRST_CUSTOM) {
diff --git a/core/java/com/android/internal/app/procstats/UidState.java b/core/java/com/android/internal/app/procstats/UidState.java
index 8761b74..4911346 100644
--- a/core/java/com/android/internal/app/procstats/UidState.java
+++ b/core/java/com/android/internal/app/procstats/UidState.java
@@ -150,6 +150,7 @@
public void resetSafely(long now) {
mDurations.resetTable();
mStartTime = now;
+ mProcesses.removeIf(p -> !p.isInUse());
}
/**
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index b09a9c3..739055e 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -50,12 +50,22 @@
struct {
jclass clazz;
+
jmethodID init;
+
+ jfieldID vsyncId;
+ jfieldID expectedPresentationTime;
+ jfieldID deadline;
} frameTimelineClassInfo;
struct {
jclass clazz;
+
jmethodID init;
+
+ jfieldID frameInterval;
+ jfieldID preferredFrameTimelineIndex;
+ jfieldID frameTimelines;
} vsyncEventDataClassInfo;
} gDisplayEventReceiverClassInfo;
@@ -63,7 +73,7 @@
class NativeDisplayEventReceiver : public DisplayEventDispatcher {
public:
- NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak,
+ NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak, jobject vsyncEventDataWeak,
const sp<MessageQueue>& messageQueue, jint vsyncSource,
jint eventRegistration, jlong layerHandle);
@@ -74,6 +84,7 @@
private:
jobject mReceiverWeakGlobal;
+ jobject mVsyncEventDataWeakGlobal;
sp<MessageQueue> mMessageQueue;
void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
@@ -87,6 +98,7 @@
};
NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak,
+ jobject vsyncEventDataWeak,
const sp<MessageQueue>& messageQueue,
jint vsyncSource, jint eventRegistration,
jlong layerHandle)
@@ -98,6 +110,7 @@
reinterpret_cast<IBinder*>(layerHandle))
: nullptr),
mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
+ mVsyncEventDataWeakGlobal(env->NewGlobalRef(vsyncEventDataWeak)),
mMessageQueue(messageQueue) {
ALOGV("receiver %p ~ Initializing display event receiver.", this);
}
@@ -144,12 +157,39 @@
JNIEnv* env = AndroidRuntime::getJNIEnv();
ScopedLocalRef<jobject> receiverObj(env, GetReferent(env, mReceiverWeakGlobal));
- if (receiverObj.get()) {
+ ScopedLocalRef<jobject> vsyncEventDataObj(env, GetReferent(env, mVsyncEventDataWeakGlobal));
+ if (receiverObj.get() && vsyncEventDataObj.get()) {
ALOGV("receiver %p ~ Invoking vsync handler.", this);
- jobject javaVsyncEventData = createJavaVsyncEventData(env, vsyncEventData);
+ env->SetIntField(vsyncEventDataObj.get(),
+ gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo
+ .preferredFrameTimelineIndex,
+ vsyncEventData.preferredFrameTimelineIndex);
+ env->SetLongField(vsyncEventDataObj.get(),
+ gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.frameInterval,
+ vsyncEventData.frameInterval);
+
+ jobjectArray frameTimelinesObj = reinterpret_cast<jobjectArray>(
+ env->GetObjectField(vsyncEventDataObj.get(),
+ gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo
+ .frameTimelines));
+ for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+ VsyncEventData::FrameTimeline& frameTimeline = vsyncEventData.frameTimelines[i];
+ jobject frameTimelineObj = env->GetObjectArrayElement(frameTimelinesObj, i);
+ env->SetLongField(frameTimelineObj,
+ gDisplayEventReceiverClassInfo.frameTimelineClassInfo.vsyncId,
+ frameTimeline.vsyncId);
+ env->SetLongField(frameTimelineObj,
+ gDisplayEventReceiverClassInfo.frameTimelineClassInfo
+ .expectedPresentationTime,
+ frameTimeline.expectedPresentationTime);
+ env->SetLongField(frameTimelineObj,
+ gDisplayEventReceiverClassInfo.frameTimelineClassInfo.deadline,
+ frameTimeline.deadlineTimestamp);
+ }
+
env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync,
- timestamp, displayId.value, count, javaVsyncEventData);
+ timestamp, displayId.value, count);
ALOGV("receiver %p ~ Returned from vsync handler.", this);
}
@@ -217,8 +257,9 @@
mMessageQueue->raiseAndClearException(env, "dispatchModeChanged");
}
-static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj,
- jint vsyncSource, jint eventRegistration, jlong layerHandle) {
+static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject vsyncEventDataWeak,
+ jobject messageQueueObj, jint vsyncSource, jint eventRegistration,
+ jlong layerHandle) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
@@ -226,8 +267,8 @@
}
sp<NativeDisplayEventReceiver> receiver =
- new NativeDisplayEventReceiver(env, receiverWeak, messageQueue, vsyncSource,
- eventRegistration, layerHandle);
+ new NativeDisplayEventReceiver(env, receiverWeak, vsyncEventDataWeak, messageQueue,
+ vsyncSource, eventRegistration, layerHandle);
status_t status = receiver->initialize();
if (status) {
String8 message;
@@ -274,7 +315,9 @@
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
- {"nativeInit", "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;IIJ)J",
+ {"nativeInit",
+ "(Ljava/lang/ref/WeakReference;Ljava/lang/ref/WeakReference;Landroid/os/"
+ "MessageQueue;IIJ)J",
(void*)nativeInit},
{"nativeGetDisplayEventReceiverFinalizer", "()J",
(void*)nativeGetDisplayEventReceiverFinalizer},
@@ -291,8 +334,7 @@
gDisplayEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
gDisplayEventReceiverClassInfo.dispatchVsync =
- GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync",
- "(JJILandroid/view/DisplayEventReceiver$VsyncEventData;)V");
+ GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJI)V");
gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
gDisplayEventReceiverClassInfo.dispatchModeChanged =
@@ -318,6 +360,15 @@
gDisplayEventReceiverClassInfo.frameTimelineClassInfo.init =
GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz,
"<init>", "(JJJ)V");
+ gDisplayEventReceiverClassInfo.frameTimelineClassInfo.vsyncId =
+ GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz,
+ "vsyncId", "J");
+ gDisplayEventReceiverClassInfo.frameTimelineClassInfo.expectedPresentationTime =
+ GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz,
+ "expectedPresentationTime", "J");
+ gDisplayEventReceiverClassInfo.frameTimelineClassInfo.deadline =
+ GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz,
+ "deadline", "J");
jclass vsyncEventDataClazz =
FindClassOrDie(env, "android/view/DisplayEventReceiver$VsyncEventData");
@@ -329,6 +380,17 @@
"([Landroid/view/"
"DisplayEventReceiver$VsyncEventData$FrameTimeline;IJ)V");
+ gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.preferredFrameTimelineIndex =
+ GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz,
+ "preferredFrameTimelineIndex", "I");
+ gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.frameInterval =
+ GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz,
+ "frameInterval", "J");
+ gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.frameTimelines =
+ GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz,
+ "frameTimelines",
+ "[Landroid/view/DisplayEventReceiver$VsyncEventData$FrameTimeline;");
+
return res;
}
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index ab7b0ab..ed612a0 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -142,6 +142,7 @@
optional int64 finish_clock_time_ms = 4;
}
repeated BroadcastSummary historical_broadcasts_summary = 6;
+ repeated BroadcastRecordProto pending_broadcasts = 7;
}
message MemInfoDumpProto {
@@ -439,6 +440,7 @@
optional int32 id = 1;
optional .android.app.NotificationProto notification = 2;
+ optional int32 foregroundServiceType = 3;
}
optional Foreground foreground = 13;
diff --git a/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java b/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java
index 16ed3ef..17b064c 100644
--- a/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java
+++ b/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java
@@ -26,6 +26,7 @@
import static org.testng.Assert.assertThrows;
+import android.hardware.Sensor;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
@@ -67,6 +68,24 @@
}
@Test
+ public void virtualSensorConfig_invalidName_throwsException() {
+ assertThrows(
+ NullPointerException.class,
+ () -> new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, null));
+ }
+
+ @Test
+ public void virtualSensorConfig_invalidType_throwsException() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new VirtualSensorConfig.Builder(Sensor.TYPE_ALL, SENSOR_NAME));
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new VirtualSensorConfig.Builder(0, SENSOR_NAME));
+ }
+
+ @Test
public void hardwareBufferDirectChannelTypeSupported_throwsException() {
assertThrows(
IllegalArgumentException.class,
diff --git a/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java
index 9b9a84b..35b3267 100644
--- a/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java
@@ -156,4 +156,19 @@
eq(0),
eq(APP_1_PROCESS_NAME));
}
+
+ @SmallTest
+ public void testSafelyResetClearsProcessInUidState() throws Exception {
+ ProcessStats processStats = new ProcessStats();
+ ProcessState processState =
+ processStats.getProcessStateLocked(
+ APP_1_PACKAGE_NAME, APP_1_UID, APP_1_VERSION, APP_1_PROCESS_NAME);
+ processState.makeActive();
+ UidState uidState = processStats.mUidStates.get(APP_1_UID);
+ assertTrue(uidState.isInUse());
+ processState.makeInactive();
+ uidState.resetSafely(NOW_MS);
+ processState.makeActive();
+ assertFalse(uidState.isInUse());
+ }
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 913eaf2..05e1772 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1297,12 +1297,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-894942237": {
- "message": "Force Playing Transition: %d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_WINDOW_TRANSITIONS",
- "at": "com\/android\/server\/wm\/Transition.java"
- },
"-883738232": {
"message": "Adding more than one toast window for UID at a time.",
"level": "WARN",
@@ -4249,6 +4243,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
+ "1878927091": {
+ "message": "prepareSurface: No changes in animation for %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ANIM",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
"1891501279": {
"message": "cancelAnimation(): reason=%s",
"level": "DEBUG",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 2252587..70d3b35 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -204,7 +204,7 @@
// and exit, since exit itself can trigger a number of changes that update the stages.
private boolean mShouldUpdateRecents;
private boolean mExitSplitScreenOnHide;
- private boolean mIsSplitEntering;
+ private boolean mIsDividerRemoteAnimating;
private boolean mIsDropEntering;
private boolean mIsExiting;
@@ -881,7 +881,7 @@
// Set false to avoid record new bounds with old task still on top;
mShouldUpdateRecents = false;
- mIsSplitEntering = true;
+ mIsDividerRemoteAnimating = true;
if (mSplitRequest == null) {
mSplitRequest = new SplitRequest(mainTaskId,
mainPendingIntent != null ? mainPendingIntent.getIntent() : null,
@@ -974,7 +974,7 @@
}
private void onRemoteAnimationFinishedOrCancelled(WindowContainerTransaction evictWct) {
- mIsSplitEntering = false;
+ mIsDividerRemoteAnimating = false;
mShouldUpdateRecents = true;
mSplitRequest = null;
// If any stage has no child after animation finished, it means that split will display
@@ -1247,7 +1247,7 @@
}
});
mShouldUpdateRecents = false;
- mIsSplitEntering = false;
+ mIsDividerRemoteAnimating = false;
mSplitLayout.getInvisibleBounds(mTempRect1);
if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) {
@@ -1590,7 +1590,7 @@
&& !ENABLE_SHELL_TRANSITIONS) {
// Clear the divider remote animating flag as the divider will be re-rendered to apply
// the new rotation config.
- mIsSplitEntering = false;
+ mIsDividerRemoteAnimating = false;
mSplitLayout.update(null /* t */);
onLayoutSizeChanged(mSplitLayout);
}
@@ -1640,9 +1640,9 @@
}
void onChildTaskAppeared(StageListenerImpl stageListener, int taskId) {
+ // Handle entering split screen while there is a split pair running in the background.
if (stageListener == mSideStageListener && !isSplitScreenVisible() && isSplitActive()
- && !mIsSplitEntering) {
- // Handle entring split case here if split already running background.
+ && mSplitRequest == null) {
if (mIsDropEntering) {
mSplitLayout.resetDividerPosition();
} else {
@@ -1734,7 +1734,7 @@
mDividerVisible = visible;
sendSplitVisibilityChanged();
- if (mIsSplitEntering) {
+ if (mIsDividerRemoteAnimating) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
" Skip animating divider bar due to it's remote animating.");
return;
@@ -1754,7 +1754,7 @@
" Skip animating divider bar due to divider leash not ready.");
return;
}
- if (mIsSplitEntering) {
+ if (mIsDividerRemoteAnimating) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
" Skip animating divider bar due to it's remote animating.");
return;
@@ -1822,7 +1822,8 @@
mSplitLayout.flingDividerToDismiss(
mSideStagePosition != SPLIT_POSITION_BOTTOM_OR_RIGHT,
EXIT_REASON_APP_FINISHED);
- } else if (!isSplitScreenVisible() && !mIsSplitEntering) {
+ } else if (!isSplitScreenVisible() && mSplitRequest == null) {
+ // Dismiss split screen in the background once any sides of the split become empty.
exitSplitScreen(null /* childrenToTop */, EXIT_REASON_APP_FINISHED);
}
} else if (isSideStage && hasChildren && !mMainStage.isActive()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 75112b6..d1565d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -450,9 +450,7 @@
// Already done, so no need to end it.
return;
}
- if (mixed.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) {
- // queue since no actual animation.
- } else if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
+ if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
if (mixed.mAnimType == MixedTransition.ANIM_TYPE_GOING_HOME) {
boolean ended = mSplitHandler.end();
// If split couldn't end (because it is remote), then don't end everything else
@@ -466,12 +464,8 @@
} else {
mPipHandler.end();
}
- } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
- mPipHandler.end();
- if (mixed.mLeftoversHandler != null) {
- mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
- finishCallback);
- }
+ } else if (mixed.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) {
+ // queue
} else {
throw new IllegalStateException("Playing a mixed transition with unknown type? "
+ mixed.mType);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java
deleted file mode 100644
index 0386ec3..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.transition;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.IBinder;
-import android.util.Log;
-import android.view.SurfaceControl;
-import android.window.TransitionInfo;
-import android.window.TransitionRequestInfo;
-import android.window.WindowContainerTransaction;
-
-import java.util.ArrayList;
-
-/**
- * A Simple handler that tracks SLEEP transitions. We track them specially since we (ab)use these
- * as sentinels for fast-forwarding through animations when the screen is off.
- *
- * There should only be one SleepHandler and it is used explicitly by {@link Transitions} so we
- * don't register it like a normal handler.
- */
-class SleepHandler implements Transitions.TransitionHandler {
- final ArrayList<IBinder> mSleepTransitions = new ArrayList<>();
-
- @Override
- public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction startTransaction,
- @NonNull SurfaceControl.Transaction finishTransaction,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {
- startTransaction.apply();
- finishCallback.onTransitionFinished(null, null);
- mSleepTransitions.remove(transition);
- return true;
- }
-
- @Override
- @Nullable
- public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
- @NonNull TransitionRequestInfo request) {
- mSleepTransitions.add(transition);
- return new WindowContainerTransaction();
- }
-
- @Override
- public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
- @Nullable SurfaceControl.Transaction finishTransaction) {
- Log.e(Transitions.TAG, "Sleep transition was consumed. This doesn't make sense");
- mSleepTransitions.remove(transition);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
index 8d29901..bcc37ba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -143,8 +143,12 @@
Animation a = null;
if (animAttr != 0) {
if (overrideType == ANIM_FROM_STYLE && !isTask) {
- a = loadCustomActivityTransition(animAttr, options, enter, transitionAnimation);
- if (a == null) {
+ final TransitionInfo.AnimationOptions.CustomActivityTransition customTransition =
+ getCustomActivityTransition(animAttr, options);
+ if (customTransition != null) {
+ a = loadCustomActivityTransition(
+ customTransition, options, enter, transitionAnimation);
+ } else {
a = transitionAnimation
.loadAnimationAttr(options.getPackageName(), options.getAnimations(),
animAttr, translucent);
@@ -161,10 +165,8 @@
return a;
}
- static Animation loadCustomActivityTransition(int animAttr,
- TransitionInfo.AnimationOptions options, boolean enter,
- TransitionAnimation transitionAnimation) {
- Animation a = null;
+ static TransitionInfo.AnimationOptions.CustomActivityTransition getCustomActivityTransition(
+ int animAttr, TransitionInfo.AnimationOptions options) {
boolean isOpen = false;
switch (animAttr) {
case R.styleable.WindowAnimation_activityOpenEnterAnimation:
@@ -178,17 +180,19 @@
return null;
}
- final TransitionInfo.AnimationOptions.CustomActivityTransition transitionAnim =
- options.getCustomActivityTransition(isOpen);
- if (transitionAnim != null) {
- a = transitionAnimation.loadAppTransitionAnimation(options.getPackageName(),
- enter ? transitionAnim.getCustomEnterResId()
- : transitionAnim.getCustomExitResId());
- if (a != null && transitionAnim.getCustomBackgroundColor() != 0) {
- a.setBackdropColor(transitionAnim.getCustomBackgroundColor());
- }
- }
+ return options.getCustomActivityTransition(isOpen);
+ }
+ static Animation loadCustomActivityTransition(
+ @NonNull TransitionInfo.AnimationOptions.CustomActivityTransition transitionAnim,
+ TransitionInfo.AnimationOptions options, boolean enter,
+ TransitionAnimation transitionAnimation) {
+ final Animation a = transitionAnimation.loadAppTransitionAnimation(options.getPackageName(),
+ enter ? transitionAnim.getCustomEnterResId()
+ : transitionAnim.getCustomExitResId());
+ if (a != null && transitionAnim.getCustomBackgroundColor() != 0) {
+ a.setBackdropColor(transitionAnim.getCustomBackgroundColor());
+ }
return a;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 155990a..b39b953 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -21,7 +21,6 @@
import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OPEN;
-import static android.view.WindowManager.TRANSIT_SLEEP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.fixScale;
@@ -125,7 +124,6 @@
private final DisplayController mDisplayController;
private final ShellController mShellController;
private final ShellTransitionImpl mImpl = new ShellTransitionImpl();
- private final SleepHandler mSleepHandler = new SleepHandler();
private boolean mIsRegistered = false;
@@ -139,14 +137,6 @@
private float mTransitionAnimationScaleSetting = 1.0f;
- /**
- * How much time we allow for an animation to finish itself on sleep. If it takes longer, we
- * will force-finish it (on this end) which may leave it in a bad state but won't hang the
- * device. This needs to be pretty small because it is an allowance for each queued animation,
- * however it can't be too small since there is some potential IPC involved.
- */
- private static final int SLEEP_ALLOWANCE_MS = 120;
-
private static final class ActiveTransition {
IBinder mToken;
TransitionHandler mHandler;
@@ -488,29 +478,11 @@
+ Arrays.toString(mActiveTransitions.stream().map(
activeTransition -> activeTransition.mToken).toArray()));
}
- final ActiveTransition active = mActiveTransitions.get(activeIdx);
for (int i = 0; i < mObservers.size(); ++i) {
mObservers.get(i).onTransitionReady(transitionToken, info, t, finishT);
}
- if (info.getType() == TRANSIT_SLEEP) {
- if (activeIdx > 0) {
- active.mInfo = info;
- active.mStartT = t;
- active.mFinishT = finishT;
- if (!info.getRootLeash().isValid()) {
- // Shell has some debug settings which makes calling binders with invalid
- // surfaces crash, so replace it with a "real" one.
- info.setRootLeash(new SurfaceControl.Builder().setName("Invalid")
- .setContainerLayer().build(), 0, 0);
- }
- // Sleep starts a process of forcing all prior transitions to finish immediately
- finishForSleep(null /* forceFinish */);
- return;
- }
- }
-
// Allow to notify keyguard un-occluding state to KeyguardService, which can happen while
// screen-off, so there might no visibility change involved.
if (!info.getRootLeash().isValid() && info.getType() != TRANSIT_KEYGUARD_UNOCCLUDE) {
@@ -555,6 +527,7 @@
return;
}
+ final ActiveTransition active = mActiveTransitions.get(activeIdx);
active.mInfo = info;
active.mStartT = t;
active.mFinishT = finishT;
@@ -830,30 +803,23 @@
}
final ActiveTransition active = new ActiveTransition();
WindowContainerTransaction wct = null;
-
- // If we have sleep, we use a special handler and we try to finish everything ASAP.
- if (request.getType() == TRANSIT_SLEEP) {
- mSleepHandler.handleRequest(transitionToken, request);
- active.mHandler = mSleepHandler;
- } else {
- for (int i = mHandlers.size() - 1; i >= 0; --i) {
- wct = mHandlers.get(i).handleRequest(transitionToken, request);
- if (wct != null) {
- active.mHandler = mHandlers.get(i);
- break;
- }
+ for (int i = mHandlers.size() - 1; i >= 0; --i) {
+ wct = mHandlers.get(i).handleRequest(transitionToken, request);
+ if (wct != null) {
+ active.mHandler = mHandlers.get(i);
+ break;
}
- if (request.getDisplayChange() != null) {
- TransitionRequestInfo.DisplayChange change = request.getDisplayChange();
- if (change.getEndRotation() != change.getStartRotation()) {
- // Is a rotation, so dispatch to all displayChange listeners
- if (wct == null) {
- wct = new WindowContainerTransaction();
- }
- mDisplayController.getChangeController().dispatchOnDisplayChange(wct,
- change.getDisplayId(), change.getStartRotation(),
- change.getEndRotation(), null /* newDisplayAreaInfo */);
+ }
+ if (request.getDisplayChange() != null) {
+ TransitionRequestInfo.DisplayChange change = request.getDisplayChange();
+ if (change.getEndRotation() != change.getStartRotation()) {
+ // Is a rotation, so dispatch to all displayChange listeners
+ if (wct == null) {
+ wct = new WindowContainerTransaction();
}
+ mDisplayController.getChangeController().dispatchOnDisplayChange(wct,
+ change.getDisplayId(), change.getStartRotation(), change.getEndRotation(),
+ null /* newDisplayAreaInfo */);
}
}
mOrganizer.startTransition(transitionToken, wct != null && wct.isEmpty() ? null : wct);
@@ -880,56 +846,6 @@
}
/**
- * Finish running animations (almost) immediately when a SLEEP transition comes in. We use this
- * as both a way to reduce unnecessary work (animations not visible while screen off) and as a
- * failsafe to unblock "stuck" animations (in particular remote animations).
- *
- * This works by "merging" the sleep transition into the currently-playing transition (even if
- * its out-of-order) -- turning SLEEP into a signal. If the playing transition doesn't finish
- * within `SLEEP_ALLOWANCE_MS` from this merge attempt, this will then finish it directly (and
- * send an abort/consumed message).
- *
- * This is then repeated until there are no more pending sleep transitions.
- *
- * @param forceFinish When non-null, this is the transition that we last sent the SLEEP merge
- * signal to -- so it will be force-finished if it's still running.
- */
- private void finishForSleep(@Nullable IBinder forceFinish) {
- if (mActiveTransitions.isEmpty() || mSleepHandler.mSleepTransitions.isEmpty()) {
- return;
- }
- if (forceFinish != null && mActiveTransitions.get(0).mToken == forceFinish) {
- Log.e(TAG, "Forcing transition to finish due to sleep timeout: "
- + mActiveTransitions.get(0).mToken);
- onFinish(mActiveTransitions.get(0).mToken, null, null, true);
- }
- final SurfaceControl.Transaction dummyT = new SurfaceControl.Transaction();
- while (!mActiveTransitions.isEmpty() && !mSleepHandler.mSleepTransitions.isEmpty()) {
- final ActiveTransition playing = mActiveTransitions.get(0);
- int sleepIdx = findActiveTransition(mSleepHandler.mSleepTransitions.get(0));
- if (sleepIdx >= 0) {
- // Try to signal that we are sleeping by attempting to merge the sleep transition
- // into the playing one.
- final ActiveTransition nextSleep = mActiveTransitions.get(sleepIdx);
- playing.mHandler.mergeAnimation(nextSleep.mToken, nextSleep.mInfo, dummyT,
- playing.mToken, (wct, cb) -> {});
- } else {
- Log.e(TAG, "Couldn't find sleep transition in active list: "
- + mSleepHandler.mSleepTransitions.get(0));
- }
- // it's possible to complete immediately. If that happens, just repeat the signal
- // loop until we either finish everything or start playing an animation that isn't
- // finishing immediately.
- if (!mActiveTransitions.isEmpty() && mActiveTransitions.get(0) == playing) {
- // Give it a (very) short amount of time to process it before forcing.
- mMainExecutor.executeDelayed(
- () -> finishForSleep(playing.mToken), SLEEP_ALLOWANCE_MS);
- break;
- }
- }
- }
-
- /**
* Interface for a callback that must be called after a TransitionHandler finishes playing an
* animation.
*/
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index 9e9012e..d70e8b3 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -191,7 +191,7 @@
} else {
session = ContentRecordingSession.createTaskSession(launchCookie);
}
- virtualDisplayConfig.setWindowManagerMirroring(true);
+ virtualDisplayConfig.setWindowManagerMirroringEnabled(true);
final DisplayManager dm = mContext.getSystemService(DisplayManager.class);
final VirtualDisplay virtualDisplay = dm.createVirtualDisplay(this,
virtualDisplayConfig.build(), callback, handler, windowContext);
diff --git a/media/java/android/media/projection/OWNERS b/media/java/android/media/projection/OWNERS
index 96532d0..2273f81 100644
--- a/media/java/android/media/projection/OWNERS
+++ b/media/java/android/media/projection/OWNERS
@@ -1,3 +1,4 @@
michaelwr@google.com
santoscordon@google.com
chaviw@google.com
+nmusgrave@google.com
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 7e9443b..c39a6db 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -32,6 +32,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.NullPointerException;
import java.util.concurrent.Executor;
/**
@@ -271,7 +272,12 @@
mExecutor.execute(() -> {
synchronized (mCallbackLock) {
if (mCallback != null) {
- mCallback.onFilterStatusChanged(this, status);
+ try {
+ mCallback.onFilterStatusChanged(this, status);
+ }
+ catch (NullPointerException e) {
+ Log.d(TAG, "catch exception:" + e);
+ }
}
}
});
@@ -285,7 +291,12 @@
mExecutor.execute(() -> {
synchronized (mCallbackLock) {
if (mCallback != null) {
- mCallback.onFilterEvent(this, events);
+ try {
+ mCallback.onFilterEvent(this, events);
+ }
+ catch (NullPointerException e) {
+ Log.d(TAG, "catch exception:" + e);
+ }
} else {
for (FilterEvent event : events) {
if (event instanceof MediaEvent) {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index a2d1e4d..b32fe3f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -42,11 +42,13 @@
import com.android.credentialmanager.createflow.EnabledProviderInfo
import com.android.credentialmanager.createflow.RequestDisplayInfo
import com.android.credentialmanager.getflow.GetCredentialUiState
+import com.android.credentialmanager.getflow.findAutoSelectEntry
import androidx.credentials.CreateCredentialRequest.DisplayInfo
import androidx.credentials.CreatePublicKeyCredentialRequest
import androidx.credentials.CreatePasswordRequest
import androidx.credentials.GetPasswordOption
import androidx.credentials.GetPublicKeyCredentialOption
+import com.android.credentialmanager.common.ProviderActivityState
import java.time.Instant
@@ -128,10 +130,20 @@
getCredentialUiState = null,
)
}
- RequestInfo.TYPE_GET -> UiState(
- createCredentialUiState = null,
- getCredentialUiState = getCredentialInitialUiState(originName)!!,
- )
+ RequestInfo.TYPE_GET -> {
+ val getCredentialInitialUiState = getCredentialInitialUiState(originName)!!
+ val autoSelectEntry =
+ findAutoSelectEntry(getCredentialInitialUiState.providerDisplayInfo)
+ UiState(
+ createCredentialUiState = null,
+ getCredentialUiState = getCredentialInitialUiState,
+ selectedEntry = autoSelectEntry,
+ providerActivityState =
+ if (autoSelectEntry == null) ProviderActivityState.NOT_APPLICABLE
+ else ProviderActivityState.READY_TO_LAUNCH,
+ isAutoSelectFlow = autoSelectEntry != null,
+ )
+ }
else -> throw IllegalStateException("Unrecognized request type: ${requestInfo.type}")
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index a5c74943..ca30c53 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -44,6 +44,9 @@
val selectedEntry: BaseEntry? = null,
val providerActivityState: ProviderActivityState = ProviderActivityState.NOT_APPLICABLE,
val dialogState: DialogState = DialogState.ACTIVE,
+ // True if the UI has one and onely one auto selectable entry. Its provider activiey will be
+ // launched immediately, and canceling it will cancel the whole UI flow.
+ val isAutoSelectFlow: Boolean = false,
)
class CredentialSelectorViewModel(
@@ -96,13 +99,20 @@
val resultCode = providerActivityResult.resultCode
val resultData = providerActivityResult.data
if (resultCode == Activity.RESULT_CANCELED) {
- // Re-display the CredMan UI if the user canceled from the provider UI.
- Log.d(Constants.LOG_TAG, "The provider activity was cancelled," +
- " re-displaying our UI.")
- uiState = uiState.copy(
- selectedEntry = null,
- providerActivityState = ProviderActivityState.NOT_APPLICABLE,
- )
+ // Re-display the CredMan UI if the user canceled from the provider UI, or cancel
+ // the UI if this is the auto select flow.
+ if (uiState.isAutoSelectFlow) {
+ Log.d(Constants.LOG_TAG, "The auto selected provider activity was cancelled," +
+ " ending the credential manager activity.")
+ onUserCancel()
+ } else {
+ Log.d(Constants.LOG_TAG, "The provider activity was cancelled," +
+ " re-displaying our UI.")
+ uiState = uiState.copy(
+ selectedEntry = null,
+ providerActivityState = ProviderActivityState.NOT_APPLICABLE,
+ )
+ }
} else {
if (entry != null) {
Log.d(
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index 96e2d3f..e61633f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -226,7 +226,10 @@
userName = credentialEntry.username.toString(),
displayName = credentialEntry.displayName?.toString(),
icon = credentialEntry.icon.loadDrawable(context),
+ shouldTintIcon = credentialEntry.isDefaultIcon ?: false,
lastUsedTimeMillis = credentialEntry.lastUsedTime,
+ isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
+ credentialEntry.autoSelectAllowedFromOption,
))
}
is PublicKeyCredentialEntry -> {
@@ -242,7 +245,10 @@
userName = credentialEntry.username.toString(),
displayName = credentialEntry.displayName?.toString(),
icon = credentialEntry.icon.loadDrawable(context),
+ shouldTintIcon = credentialEntry.isDefaultIcon,
lastUsedTimeMillis = credentialEntry.lastUsedTime,
+ isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
+ credentialEntry.autoSelectAllowedFromOption,
))
}
is CustomCredentialEntry -> {
@@ -258,7 +264,10 @@
userName = credentialEntry.title.toString(),
displayName = credentialEntry.subtitle?.toString(),
icon = credentialEntry.icon.loadDrawable(context),
+ shouldTintIcon = credentialEntry.isDefaultIcon,
lastUsedTimeMillis = credentialEntry.lastUsedTime,
+ isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
+ credentialEntry.autoSelectAllowedFromOption,
))
}
else -> Log.d(
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt b/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt
index 75b12ff..82d6952 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt
@@ -155,21 +155,23 @@
userName: String,
userDisplayName: String?,
lastUsedTime: Instant?,
+ isAutoSelectAllowed: Boolean = false,
): Entry {
- val intent = Intent("com.androidauth.androidvault.CONFIRM_PASSWORD")
- .setPackage("com.androidauth.androidvault")
- intent.putExtra("provider_extra_sample", "testprovider")
- val pendingIntent = PendingIntent.getActivity(
- context, 1,
- intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
- or PendingIntent.FLAG_ONE_SHOT)
+ val intent = Intent(Settings.ACTION_SYNC_SETTINGS)
+ val pendingIntent =
+ PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
+ val candidateQueryData = Bundle()
+ candidateQueryData.putBoolean(
+ "androidx.credentials.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED",
+ isAutoSelectAllowed
)
val passkeyEntry = PublicKeyCredentialEntry.Builder(
context,
userName,
pendingIntent,
- BeginGetPublicKeyCredentialOption(Bundle(), "id", "requestjson")
- ).setDisplayName(userDisplayName).setLastUsedTime(lastUsedTime).build()
+ BeginGetPublicKeyCredentialOption(candidateQueryData, "id", "requestjson")
+ ).setDisplayName(userDisplayName).setLastUsedTime(lastUsedTime)
+ .setAutoSelectAllowed(isAutoSelectAllowed).build()
return Entry(key, subkey, passkeyEntry.slice, Intent())
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
index 9550268..c0c29bb 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
@@ -74,6 +74,7 @@
passwordValue: String? = null,
/** If true, draws a trailing lock icon. */
isLockedAuthEntry: Boolean = false,
+ enforceOneLine: Boolean = false,
) {
val iconPadding = Modifier.wrapContentSize().padding(
// Horizontal padding should be 16dp, but the suggestion chip itself
@@ -93,12 +94,17 @@
// has 8dp horizontal elements padding
horizontal = 8.dp, vertical = 16.dp,
),
+ // Make sure the trailing icon and text column are centered vertically.
verticalAlignment = Alignment.CenterVertically,
) {
- Column(modifier = Modifier.wrapContentSize()) {
- SmallTitleText(entryHeadlineText)
+ // Apply weight so that the trailing icon can always show.
+ Column(modifier = Modifier.wrapContentHeight().fillMaxWidth().weight(1f)) {
+ SmallTitleText(text = entryHeadlineText, enforceOneLine = enforceOneLine)
if (passwordValue != null) {
- Row(modifier = Modifier.fillMaxWidth()) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
val visualTransformation = remember { PasswordVisualTransformation() }
val originalPassword by remember {
mutableStateOf(passwordValue)
@@ -110,9 +116,10 @@
).text.text
)
}
- BodySmallText(displayedPassword.value)
+ BodySmallText(
+ text = displayedPassword.value, enforceOneLine = enforceOneLine)
ToggleVisibilityButton(
- modifier = Modifier.padding(start = 5.dp).size(24.dp),
+ modifier = Modifier.padding(start = 12.dp, top = 5.dp).size(24.dp),
onToggle = {
if (it) {
displayedPassword.value = originalPassword
@@ -125,14 +132,14 @@
)
}
} else if (entrySecondLineText != null) {
- BodySmallText(entrySecondLineText)
+ BodySmallText(text = entrySecondLineText, enforceOneLine = enforceOneLine)
}
if (entryThirdLineText != null) {
- BodySmallText(entryThirdLineText)
+ BodySmallText(text = entryThirdLineText, enforceOneLine = enforceOneLine)
}
}
if (isLockedAuthEntry) {
- Box(modifier = Modifier.wrapContentSize()) {
+ Box(modifier = Modifier.wrapContentSize().padding(start = 16.dp)) {
Icon(
imageVector = Icons.Outlined.Lock,
// Decorative purpose only.
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt
index 8af729e..22871bcb 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt
@@ -23,6 +23,7 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
/**
* The headline for a screen. E.g. "Create a passkey for X", "Choose a saved sign-in for X".
@@ -57,12 +58,14 @@
* Body-small typography; on-surface-variant color.
*/
@Composable
-fun BodySmallText(text: String, modifier: Modifier = Modifier) {
+fun BodySmallText(text: String, modifier: Modifier = Modifier, enforceOneLine: Boolean = false) {
Text(
modifier = modifier.wrapContentSize(),
text = text,
color = MaterialTheme.colorScheme.onSurfaceVariant,
style = MaterialTheme.typography.bodySmall,
+ overflow = TextOverflow.Ellipsis,
+ maxLines = if (enforceOneLine) 1 else Int.MAX_VALUE
)
}
@@ -83,12 +86,14 @@
* Title-small typography; on-surface color.
*/
@Composable
-fun SmallTitleText(text: String, modifier: Modifier = Modifier) {
+fun SmallTitleText(text: String, modifier: Modifier = Modifier, enforceOneLine: Boolean = false) {
Text(
modifier = modifier.wrapContentSize(),
text = text,
color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.titleSmall,
+ overflow = TextOverflow.Ellipsis,
+ maxLines = if (enforceOneLine) 1 else Int.MAX_VALUE
)
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index b83c593..9fe7899 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -1,4 +1,18 @@
-@file:OptIn(ExperimentalMaterial3Api::class)
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package com.android.credentialmanager.createflow
@@ -15,7 +29,6 @@
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material3.Divider
-import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.NewReleases
@@ -637,6 +650,7 @@
// This subtitle would never be null for create password
requestDisplayInfo.subtitle ?: ""
else null,
+ enforceOneLine = true,
)
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index ea56f46..ab947ae 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -182,12 +182,14 @@
CredentialEntryRow(
credentialEntryInfo = it.sortedCredentialEntryList.first(),
onEntrySelected = onEntrySelected,
+ enforceOneLine = true,
)
}
authenticationEntryList.forEach {
AuthenticationEntryRow(
authenticationEntryInfo = it,
onEntrySelected = onEntrySelected,
+ enforceOneLine = true,
)
}
} else if (usernameForCredentialSize < 4) {
@@ -195,12 +197,14 @@
CredentialEntryRow(
credentialEntryInfo = it.sortedCredentialEntryList.first(),
onEntrySelected = onEntrySelected,
+ enforceOneLine = true,
)
}
authenticationEntryList.take(4 - usernameForCredentialSize).forEach {
AuthenticationEntryRow(
authenticationEntryInfo = it,
onEntrySelected = onEntrySelected,
+ enforceOneLine = true,
)
}
} else {
@@ -208,6 +212,7 @@
CredentialEntryRow(
credentialEntryInfo = it.sortedCredentialEntryList.first(),
onEntrySelected = onEntrySelected,
+ enforceOneLine = true,
)
}
}
@@ -402,10 +407,12 @@
fun CredentialEntryRow(
credentialEntryInfo: CredentialEntryInfo,
onEntrySelected: (BaseEntry) -> Unit,
+ enforceOneLine: Boolean = false,
) {
Entry(
onClick = { onEntrySelected(credentialEntryInfo) },
iconImageBitmap = credentialEntryInfo.icon?.toBitmap()?.asImageBitmap(),
+ shouldApplyIconImageBitmapTint = credentialEntryInfo.shouldTintIcon,
// Fall back to iconPainter if iconImageBitmap isn't available
iconPainter =
if (credentialEntryInfo.icon == null) painterResource(R.drawable.ic_other_sign_in_24)
@@ -425,6 +432,7 @@
separator = stringResource(R.string.get_dialog_sign_in_type_username_separator)
)
},
+ enforceOneLine = enforceOneLine,
)
}
@@ -432,6 +440,7 @@
fun AuthenticationEntryRow(
authenticationEntryInfo: AuthenticationEntryInfo,
onEntrySelected: (BaseEntry) -> Unit,
+ enforceOneLine: Boolean = false,
) {
Entry(
onClick = { onEntrySelected(authenticationEntryInfo) },
@@ -443,6 +452,7 @@
else R.string.locked_credential_entry_label_subtext_tap_to_unlock
),
isLockedAuthEntry = !authenticationEntryInfo.isUnlockedAndEmpty,
+ enforceOneLine = enforceOneLine,
)
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index 56bc19a..263a632 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -41,6 +41,23 @@
!state.requestDisplayInfo.preferImmediatelyAvailableCredentials)
}
+internal fun findAutoSelectEntry(providerDisplayInfo: ProviderDisplayInfo): CredentialEntryInfo? {
+ if (providerDisplayInfo.authenticationEntryList.isNotEmpty()) {
+ return null
+ }
+ if (providerDisplayInfo.sortedUserNameToCredentialEntryList.size == 1) {
+ val entryList = providerDisplayInfo.sortedUserNameToCredentialEntryList.firstOrNull()
+ ?: return null
+ if (entryList.sortedCredentialEntryList.size == 1) {
+ val entry = entryList.sortedCredentialEntryList.firstOrNull() ?: return null
+ if (entry.isAutoSelectable) {
+ return entry
+ }
+ }
+ }
+ return null
+}
+
data class ProviderInfo(
/**
* Unique id (component name) of this provider.
@@ -81,7 +98,9 @@
val userName: String,
val displayName: String?,
val icon: Drawable?,
+ val shouldTintIcon: Boolean,
val lastUsedTimeMillis: Instant?,
+ val isAutoSelectable: Boolean,
) : BaseEntry(
providerId,
entryKey,
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
index 140c10d..f358417 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
+++ b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
@@ -18,6 +18,15 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+// This filegroup is used by menu tests.
+filegroup {
+ name: "AccessibilityMenuSource",
+ srcs: [
+ "src/**/AccessibilityMenuService.java",
+ "src/**/A11yMenuShortcut.java",
+ ],
+}
+
android_app {
name: "AccessibilityMenu",
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
index 39e5a8c..a902c5b 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/shortcutItem"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="@dimen/grid_item_padding"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
index c1f2aa8..8ca64d2 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
@@ -16,6 +16,7 @@
package com.android.systemui.accessibility.accessibilitymenu;
+import android.Manifest;
import android.accessibilityservice.AccessibilityButtonController;
import android.accessibilityservice.AccessibilityService;
import android.content.BroadcastReceiver;
@@ -51,8 +52,12 @@
/** @hide */
public class AccessibilityMenuService extends AccessibilityService
implements View.OnTouchListener {
- private static final String TAG = "A11yMenuService";
+ public static final String PACKAGE_NAME = AccessibilityMenuService.class.getPackageName();
+ public static final String INTENT_TOGGLE_MENU = ".toggle_menu";
+ public static final String INTENT_HIDE_MENU = ".hide_menu";
+
+ private static final String TAG = "A11yMenuService";
private static final long BUFFER_MILLISECONDS_TO_PREVENT_UPDATE_FAILURE = 100L;
private static final int BRIGHTNESS_UP_INCREMENT_GAMMA =
@@ -74,7 +79,8 @@
// TODO(b/136716947): Support multi-display once a11y framework side is ready.
private DisplayManager mDisplayManager;
- final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() {
+ private final DisplayManager.DisplayListener mDisplayListener =
+ new DisplayManager.DisplayListener() {
int mRotation;
@Override
@@ -95,13 +101,20 @@
}
};
- final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mHideMenuReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mA11yMenuLayout.hideMenu();
}
};
+ private final BroadcastReceiver mToggleMenuReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mA11yMenuLayout.toggleVisibility();
+ }
+ };
+
/**
* Update a11y menu layout when large button setting is changed.
*/
@@ -172,7 +185,19 @@
protected void onServiceConnected() {
mA11yMenuLayout = new A11yMenuOverlayLayout(this);
- registerReceiver(mBroadcastReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
+ IntentFilter hideMenuFilter = new IntentFilter();
+ hideMenuFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ hideMenuFilter.addAction(PACKAGE_NAME + INTENT_HIDE_MENU);
+
+ // Including WRITE_SECURE_SETTINGS enforces that we only listen to apps
+ // with the restricted WRITE_SECURE_SETTINGS permission who broadcast this intent.
+ registerReceiver(mHideMenuReceiver, hideMenuFilter,
+ Manifest.permission.WRITE_SECURE_SETTINGS, null,
+ Context.RECEIVER_EXPORTED);
+ registerReceiver(mToggleMenuReceiver,
+ new IntentFilter(PACKAGE_NAME + INTENT_TOGGLE_MENU),
+ Manifest.permission.WRITE_SECURE_SETTINGS, null,
+ Context.RECEIVER_EXPORTED);
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
mPrefs.registerOnSharedPreferenceChangeListener(mSharedPreferenceChangeListener);
@@ -260,7 +285,8 @@
* @param increment The increment amount in gamma-space
*/
private void adjustBrightness(int increment) {
- BrightnessInfo info = getDisplay().getBrightnessInfo();
+ Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ BrightnessInfo info = display.getBrightnessInfo();
int gamma = BrightnessUtils.convertLinearToGammaFloat(
info.brightness,
info.brightnessMinimum,
@@ -275,7 +301,7 @@
info.brightnessMinimum,
info.brightnessMaximum
);
- mDisplayManager.setBrightness(getDisplayId(), brightness);
+ mDisplayManager.setBrightness(display.getDisplayId(), brightness);
mA11yMenuLayout.showSnackbar(
getString(R.string.brightness_percentage_label,
(gamma / (BrightnessUtils.GAMMA_SPACE_MAX / 100))));
@@ -310,7 +336,8 @@
@Override
public boolean onUnbind(Intent intent) {
- unregisterReceiver(mBroadcastReceiver);
+ unregisterReceiver(mHideMenuReceiver);
+ unregisterReceiver(mToggleMenuReceiver);
mPrefs.unregisterOnSharedPreferenceChangeListener(mSharedPreferenceChangeListener);
sInitialized = false;
return super.onUnbind(intent);
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
index 6f0fe37..6ae65cb 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
@@ -21,6 +21,7 @@
import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.TextView;
@@ -146,6 +147,15 @@
shortcutIconButton.setBackground(
mShortcutDrawableUtils.createAdaptiveIconDrawable(shortcutItem.imageColor));
+
+ shortcutIconButton.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(
+ View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.setUniqueId(host.getTag().toString());
+ }
+ });
}
}
}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp
new file mode 100644
index 0000000..1757dda
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp
@@ -0,0 +1,43 @@
+//
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "AccessibilityMenuServiceTests",
+ certificate: "platform",
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.runner",
+ "androidx.test.ext.junit",
+ "compatibility-device-util-axt",
+ "platform-test-annotations",
+ "truth-prebuilt",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ":AccessibilityMenuSource",
+ ],
+ platform_apis: true,
+ test_suites: ["device-tests"],
+ instrumentation_for: "AccessibilityMenu",
+}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidManifest.xml b/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidManifest.xml
new file mode 100644
index 0000000..7be6ca7
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.systemui.accessibility.accessibilitymenu.tests">
+
+ <!-- Needed to write to Settings.Secure to enable and disable the service under test. -->
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS"/>
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.systemui.accessibility.accessibilitymenu.tests"
+ android:label="AccessibilityMenu Test Cases">
+ </instrumentation>
+</manifest>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidTest.xml b/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidTest.xml
new file mode 100644
index 0000000..39bee53
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs AccessibilityMenu Test Cases.">
+ <option name="test-tag" value="AccessibilityMenuServiceTests" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="AccessibilityMenuServiceTests.apk" />
+ <option name="aapt-version" value="AAPT2" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.systemui.accessibility.accessibilitymenu.tests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/TEST_MAPPING b/packages/SystemUI/accessibility/accessibilitymenu/tests/TEST_MAPPING
new file mode 100644
index 0000000..2bd52b5
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+ "presubmit": [
+ {
+ "name": "AccessibilityMenuServiceTests",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "android.support.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
new file mode 100644
index 0000000..529a70c
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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.accessibility.accessibilitymenu.tests;
+
+import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_HIDE_MENU;
+import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_TOGGLE_MENU;
+import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.PACKAGE_NAME;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManager;
+import android.provider.Settings;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.TestUtils;
+import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut.ShortcutId;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityMenuServiceTest {
+ private static final String TAG = "A11yMenuServiceTest";
+
+ private static final int TIMEOUT_SERVICE_STATUS_CHANGE_S = 5;
+ private static final int TIMEOUT_UI_CHANGE_S = 5;
+
+ private static Instrumentation sInstrumentation;
+ private static UiAutomation sUiAutomation;
+
+ private static AccessibilityManager sAccessibilityManager;
+
+ @BeforeClass
+ public static void classSetup() throws Throwable {
+ final String serviceName = PACKAGE_NAME + "/.AccessibilityMenuService";
+ sInstrumentation = InstrumentationRegistry.getInstrumentation();
+ sUiAutomation = sInstrumentation.getUiAutomation(
+ UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
+ final Context context = sInstrumentation.getContext();
+ sAccessibilityManager = context.getSystemService(AccessibilityManager.class);
+
+ // Disable all a11yServices if any are active.
+ if (!sAccessibilityManager.getEnabledAccessibilityServiceList(
+ AccessibilityServiceInfo.FEEDBACK_ALL_MASK).isEmpty()) {
+ Settings.Secure.putString(context.getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
+ TestUtils.waitUntil("Failed to disable all services",
+ TIMEOUT_SERVICE_STATUS_CHANGE_S,
+ () -> sAccessibilityManager.getEnabledAccessibilityServiceList(
+ AccessibilityServiceInfo.FEEDBACK_ALL_MASK).isEmpty());
+ }
+
+ // Enable a11yMenu service.
+ Settings.Secure.putString(context.getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, serviceName);
+
+ TestUtils.waitUntil("Failed to enable service",
+ TIMEOUT_SERVICE_STATUS_CHANGE_S,
+ () -> sAccessibilityManager.getEnabledAccessibilityServiceList(
+ AccessibilityServiceInfo.FEEDBACK_ALL_MASK).stream().filter(
+ info -> info.getId().contains(serviceName)).count() == 1);
+ }
+
+ @AfterClass
+ public static void classTeardown() throws Throwable {
+ Settings.Secure.putString(sInstrumentation.getTargetContext().getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
+ }
+
+ private boolean isMenuVisible() {
+ return sUiAutomation.getRootInActiveWindow() != null
+ && sUiAutomation.getRootInActiveWindow().getPackageName().toString().equals(
+ PACKAGE_NAME);
+ }
+
+ private void openMenu() throws Throwable {
+ if (isMenuVisible()) {
+ return;
+ }
+ Intent intent = new Intent(PACKAGE_NAME + INTENT_TOGGLE_MENU);
+ sInstrumentation.getContext().sendBroadcast(intent);
+ TestUtils.waitUntil("Timed out before menu could appear.",
+ TIMEOUT_UI_CHANGE_S, () -> isMenuVisible());
+ }
+
+ private void closeMenu() throws Throwable {
+ if (!isMenuVisible()) {
+ return;
+ }
+ Intent intent = new Intent(PACKAGE_NAME + INTENT_HIDE_MENU);
+ sInstrumentation.getContext().sendBroadcast(intent);
+ TestUtils.waitUntil("Timed out before menu could close.",
+ TIMEOUT_UI_CHANGE_S, () -> !isMenuVisible());
+ }
+
+ private List<AccessibilityNodeInfo> getGridButtonList() {
+ return sUiAutomation.getRootInActiveWindow()
+ .findAccessibilityNodeInfosByViewId(PACKAGE_NAME + ":id/shortcutIconBtn");
+ }
+
+ private AccessibilityNodeInfo findGridButtonInfo(
+ List<AccessibilityNodeInfo> buttons, String text) {
+ for (AccessibilityNodeInfo button: buttons) {
+ if (button.getUniqueId().equals(text)) {
+ return button;
+ }
+ }
+ return null;
+ }
+
+ @Test
+ public void testAdjustBrightness() throws Throwable {
+ openMenu();
+
+ Context context = sInstrumentation.getTargetContext();
+ DisplayManager displayManager = context.getSystemService(
+ DisplayManager.class);
+ float resetBrightness = displayManager.getBrightness(context.getDisplayId());
+
+ List<AccessibilityNodeInfo> buttons = getGridButtonList();
+ AccessibilityNodeInfo brightnessUpButton = findGridButtonInfo(buttons,
+ String.valueOf(ShortcutId.ID_BRIGHTNESS_UP_VALUE.ordinal()));
+ AccessibilityNodeInfo brightnessDownButton = findGridButtonInfo(buttons,
+ String.valueOf(ShortcutId.ID_BRIGHTNESS_DOWN_VALUE.ordinal()));
+
+ int clickId = AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.getId();
+ BrightnessInfo brightnessInfo = displayManager.getDisplay(
+ context.getDisplayId()).getBrightnessInfo();
+
+ try {
+ displayManager.setBrightness(context.getDisplayId(), brightnessInfo.brightnessMinimum);
+ TestUtils.waitUntil("Could not change to minimum brightness",
+ TIMEOUT_UI_CHANGE_S,
+ () -> displayManager.getBrightness(context.getDisplayId())
+ == brightnessInfo.brightnessMinimum);
+ brightnessUpButton.performAction(clickId);
+ TestUtils.waitUntil("Did not detect an increase in brightness.",
+ TIMEOUT_UI_CHANGE_S,
+ () -> displayManager.getBrightness(context.getDisplayId())
+ > brightnessInfo.brightnessMinimum);
+
+ displayManager.setBrightness(context.getDisplayId(), brightnessInfo.brightnessMaximum);
+ TestUtils.waitUntil("Could not change to maximum brightness",
+ TIMEOUT_UI_CHANGE_S,
+ () -> displayManager.getBrightness(context.getDisplayId())
+ == brightnessInfo.brightnessMaximum);
+ brightnessDownButton.performAction(clickId);
+ TestUtils.waitUntil("Did not detect a decrease in brightness.",
+ TIMEOUT_UI_CHANGE_S,
+ () -> displayManager.getBrightness(context.getDisplayId())
+ < brightnessInfo.brightnessMaximum);
+ } finally {
+ displayManager.setBrightness(context.getDisplayId(), resetBrightness);
+ closeMenu();
+ }
+ }
+}
diff --git a/packages/SystemUI/animation/.gitignore b/packages/SystemUI/animation/.gitignore
new file mode 100644
index 0000000..f9a33db
--- /dev/null
+++ b/packages/SystemUI/animation/.gitignore
@@ -0,0 +1,9 @@
+.idea/
+.gradle/
+gradle/
+build/
+gradlew*
+local.properties
+*.iml
+android.properties
+buildSrc
\ No newline at end of file
diff --git a/packages/SystemUI/animation/build.gradle b/packages/SystemUI/animation/build.gradle
new file mode 100644
index 0000000..939455f
--- /dev/null
+++ b/packages/SystemUI/animation/build.gradle
@@ -0,0 +1,37 @@
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+// TODO: Pull out surfaceeffects outside of src and have separate build files there.
+android {
+ sourceSets {
+ main {
+ java.srcDirs = ["${SYS_UI_DIR}/animation/src/com/android/systemui/surfaceeffects/"]
+ manifest.srcFile "${SYS_UI_DIR}/animation/AndroidManifest.xml"
+ }
+ }
+
+ compileSdk 33
+
+ defaultConfig {
+ minSdk 33
+ targetSdk 33
+ }
+
+ lintOptions {
+ abortOnError false
+ }
+ tasks.lint.enabled = false
+ tasks.withType(JavaCompile) {
+ options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ freeCompilerArgs = ["-Xjvm-default=all"]
+ }
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0"
+ implementation 'androidx.core:core-animation:1.0.0-alpha02'
+ implementation 'androidx.core:core-ktx:1.9.0'
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/DemotingTestWithoutBugDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/DemotingTestWithoutBugDetector.kt
new file mode 100644
index 0000000..f64ea45
--- /dev/null
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/DemotingTestWithoutBugDetector.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.internal.systemui.lint
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import org.jetbrains.uast.UAnnotation
+import org.jetbrains.uast.UElement
+
+class DemotingTestWithoutBugDetector : Detector(), SourceCodeScanner {
+ override fun getApplicableUastTypes(): List<Class<out UElement>> {
+ return listOf(UAnnotation::class.java)
+ }
+
+ override fun createUastHandler(context: JavaContext): UElementHandler {
+ return object : UElementHandler() {
+ override fun visitAnnotation(node: UAnnotation) {
+ if (node.qualifiedName !in DEMOTING_ANNOTATION) {
+ return
+ }
+ val bugId = node.findAttributeValue("bugId")!!.evaluate() as Int
+ if (bugId <= 0) {
+ val location = context.getLocation(node)
+ val message = "Please attach a bug id to track demoted test"
+ context.report(ISSUE, node, location, message)
+ }
+ }
+ }
+ }
+
+ companion object {
+ val DEMOTING_ANNOTATION =
+ listOf("androidx.test.filters.FlakyTest", "android.platform.test.annotations.FlakyTest")
+
+ @JvmField
+ val ISSUE: Issue =
+ Issue.create(
+ id = "DemotingTestWithoutBug",
+ briefDescription = "Demoting a test without attaching a bug.",
+ explanation =
+ """
+ Annotations (`@FlakyTest`) demote tests to an unmonitored \
+ test suite. Please set the `bugId` field in such annotations to track \
+ the test status.
+ """,
+ category = Category.TESTING,
+ priority = 8,
+ severity = Severity.WARNING,
+ implementation =
+ Implementation(
+ DemotingTestWithoutBugDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+ }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
index 84f7050..387b67d 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
@@ -39,7 +39,8 @@
RegisterReceiverViaContextDetector.ISSUE,
SoftwareBitmapDetector.ISSUE,
NonInjectedServiceDetector.ISSUE,
- StaticSettingsProviderDetector.ISSUE
+ StaticSettingsProviderDetector.ISSUE,
+ DemotingTestWithoutBugDetector.ISSUE
)
override val api: Int
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/DemotingTestWithoutBugDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/DemotingTestWithoutBugDetectorTest.kt
new file mode 100644
index 0000000..557c300
--- /dev/null
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/DemotingTestWithoutBugDetectorTest.kt
@@ -0,0 +1,151 @@
+/*
+ * 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.internal.systemui.lint
+
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestFiles
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+
+class DemotingTestWithoutBugDetectorTest : SystemUILintDetectorTest() {
+
+ override fun getDetector(): Detector = DemotingTestWithoutBugDetector()
+ override fun getIssues(): List<Issue> = listOf(DemotingTestWithoutBugDetector.ISSUE)
+
+ @Test
+ fun testMarkFlaky_withBugId() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import androidx.test.filters.FlakyTest;
+
+ @FlakyTest(bugId = 123)
+ public class TestClass {
+ public void testCase() {}
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(DemotingTestWithoutBugDetector.ISSUE)
+ .run()
+ .expectClean()
+
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.platform.test.annotations.FlakyTest;
+
+ @FlakyTest(bugId = 123)
+ public class TestClass {
+ public void testCase() {}
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(DemotingTestWithoutBugDetector.ISSUE)
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun testMarkFlaky_withoutBugId() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import androidx.test.filters.FlakyTest;
+
+ @FlakyTest
+ public class TestClass {
+ public void testCase() {}
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(DemotingTestWithoutBugDetector.ISSUE)
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass.java:4: Warning: Please attach a bug id to track demoted test [DemotingTestWithoutBug]
+ @FlakyTest
+ ~~~~~~~~~~
+ 0 errors, 1 warnings
+ """
+ )
+
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.platform.test.annotations.FlakyTest;
+
+ @FlakyTest
+ public class TestClass {
+ public void testCase() {}
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(DemotingTestWithoutBugDetector.ISSUE)
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass.java:4: Warning: Please attach a bug id to track demoted test [DemotingTestWithoutBug]
+ @FlakyTest
+ ~~~~~~~~~~
+ 0 errors, 1 warnings
+ """
+ )
+ }
+
+ private val filtersFlakyTestStub: TestFile =
+ java(
+ """
+ package androidx.test.filters;
+
+ public @interface FlakyTest {
+ int bugId() default -1;
+ }
+ """
+ )
+ private val annotationsFlakyTestStub: TestFile =
+ java(
+ """
+ package android.platform.test.annotations;
+
+ public @interface FlakyTest {
+ int bugId() default -1;
+ }
+ """
+ )
+ private val stubs = arrayOf(filtersFlakyTestStub, annotationsFlakyTestStub)
+}
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index b6a78f5..caf3233 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -82,7 +82,7 @@
<!-- The vertical margin between the date and the owner info. -->
<!-- The translation for disappearing security views after having solved them. -->
- <dimen name="disappear_y_translation">-32dp</dimen>
+ <dimen name="disappear_y_translation">-50dp</dimen>
<!-- Dimens for animation for the Bouncer PIN view -->
<dimen name="pin_view_trans_y_entry">120dp</dimen>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 6f7d66d..fca55b1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -20,7 +20,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
-import static android.view.WindowManager.TRANSIT_SLEEP;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
@@ -47,7 +46,6 @@
import com.android.wm.shell.util.TransitionUtil;
import java.util.ArrayList;
-import java.util.HashMap;
/**
* Helper class to build {@link RemoteTransition} objects
@@ -207,12 +205,6 @@
@SuppressLint("NewApi")
boolean merge(TransitionInfo info, SurfaceControl.Transaction t) {
- if (info.getType() == TRANSIT_SLEEP) {
- // A sleep event means we need to stop animations immediately, so cancel here.
- mListener.onAnimationCanceled(new HashMap<>());
- finish(mWillFinishToHome, false /* userLeaveHint */);
- return false;
- }
ArrayList<TransitionInfo.Change> openingTasks = null;
ArrayList<TransitionInfo.Change> closingTasks = null;
mAppearedTargets = null;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 2949616..66d5d09 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -39,7 +39,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Activity;
@@ -1068,13 +1067,10 @@
int yTranslation = mResources.getDimensionPixelSize(R.dimen.disappear_y_translation);
- AnimatorSet anims = new AnimatorSet();
ObjectAnimator yAnim = ObjectAnimator.ofFloat(mView, View.TRANSLATION_Y, yTranslation);
- ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mView, View.ALPHA, 0f);
-
- anims.setInterpolator(Interpolators.STANDARD_ACCELERATE);
- anims.playTogether(alphaAnim, yAnim);
- anims.start();
+ yAnim.setInterpolator(Interpolators.STANDARD_ACCELERATE);
+ yAnim.setDuration(500);
+ yAnim.start();
}
private void setupUserSwitcher() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index f1abdc6..06258b2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -637,12 +637,17 @@
public void startAppearAnimation() {
if (mCurrentSecurityMode != SecurityMode.None) {
- mView.setAlpha(1f);
+ setAlpha(1f);
mView.startAppearAnimation(mCurrentSecurityMode);
getCurrentSecurityController().startAppearAnimation();
}
}
+ /** Set the alpha of the security container view */
+ public void setAlpha(float alpha) {
+ mView.setAlpha(alpha);
+ }
+
public boolean startDisappearAnimation(Runnable onFinishRunnable) {
boolean didRunAnimation = false;
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index a5beb4e..3cf26b3 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -23,15 +23,16 @@
import com.android.systemui.keyguard.WorkLockActivity;
import com.android.systemui.people.PeopleSpaceActivity;
import com.android.systemui.people.widget.LaunchConversationActivity;
-import com.android.systemui.screenshot.AppClipsActivity;
-import com.android.systemui.screenshot.AppClipsTrampolineActivity;
import com.android.systemui.screenshot.LongScreenshotActivity;
+import com.android.systemui.screenshot.appclips.AppClipsActivity;
+import com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity;
import com.android.systemui.sensorprivacy.SensorUseStartedActivity;
import com.android.systemui.sensorprivacy.television.TvSensorPrivacyChangedActivity;
import com.android.systemui.sensorprivacy.television.TvUnblockSensorActivity;
import com.android.systemui.settings.brightness.BrightnessDialog;
import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity;
import com.android.systemui.tuner.TunerActivity;
+import com.android.systemui.usb.UsbAccessoryUriActivity;
import com.android.systemui.usb.UsbConfirmActivity;
import com.android.systemui.usb.UsbDebuggingActivity;
import com.android.systemui.usb.UsbDebuggingSecondaryUserActivity;
@@ -97,6 +98,12 @@
@ClassKey(UsbConfirmActivity.class)
public abstract Activity bindUsbConfirmActivity(UsbConfirmActivity activity);
+ /** Inject into UsbAccessoryUriActivity. */
+ @Binds
+ @IntoMap
+ @ClassKey(UsbAccessoryUriActivity.class)
+ public abstract Activity bindUsbAccessoryUriActivity(UsbAccessoryUriActivity activity);
+
/** Inject into CreateUserActivity. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
index 73c2289..a7b3bbc 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
@@ -254,7 +254,10 @@
mCurrentScrimController = mScrimManager.getCurrentController();
session.registerCallback(() -> {
- mVelocityTracker.recycle();
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
mScrimManager.removeCallback(mScrimManagerCallback);
mCapture = null;
mNotificationShadeWindowController.setForcePluginOpen(false, this);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 3608b91..5817415 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -461,7 +461,7 @@
@Keep
@JvmField
val ENABLE_PIP_SIZE_LARGE_SCREEN =
- sysPropBooleanFlag(1114, "persist.wm.debug.enable_pip_size_large_screen", default = false)
+ sysPropBooleanFlag(1114, "persist.wm.debug.enable_pip_size_large_screen", default = true)
// TODO(b/265998256): Tracking bug
@Keep
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt
index b0f9c4e..d078688 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt
@@ -21,6 +21,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.keyboard.backlight.ui.KeyboardBacklightDialogCoordinator
import javax.inject.Inject
/** A [CoreStartable] that launches components interested in physical keyboard interaction. */
@@ -28,11 +29,12 @@
class PhysicalKeyboardCoreStartable
@Inject
constructor(
+ private val keyboardBacklightDialogCoordinator: KeyboardBacklightDialogCoordinator,
private val featureFlags: FeatureFlags,
) : CoreStartable {
override fun start() {
if (featureFlags.isEnabled(Flags.KEYBOARD_BACKLIGHT_INDICATOR)) {
- // TODO(b/268645743) start listening for keyboard backlight brightness
+ keyboardBacklightDialogCoordinator.startListening()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractor.kt
new file mode 100644
index 0000000..65e70b3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractor.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.keyboard.backlight.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyboard.data.repository.KeyboardRepository
+import com.android.systemui.keyboard.shared.model.BacklightModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+
+/** Allows listening to changes to keyboard backlight level */
+@SysUISingleton
+class KeyboardBacklightInteractor
+@Inject
+constructor(
+ private val keyboardRepository: KeyboardRepository,
+) {
+
+ /** Emits current backlight level as [BacklightModel] or null if keyboard is not connected */
+ val backlight: Flow<BacklightModel?> =
+ keyboardRepository.keyboardConnected.flatMapLatest { connected ->
+ if (connected) keyboardRepository.backlight else flowOf(null)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt
new file mode 100644
index 0000000..85d0379
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.keyboard.backlight.ui
+
+import android.content.Context
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyboard.backlight.ui.view.KeyboardBacklightDialog
+import com.android.systemui.keyboard.backlight.ui.viewmodel.BacklightDialogViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * Based on the state produced from [BacklightDialogViewModel] shows or hides keyboard backlight
+ * indicator
+ */
+@SysUISingleton
+class KeyboardBacklightDialogCoordinator
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ private val context: Context,
+ private val viewModel: BacklightDialogViewModel,
+) {
+
+ var dialog: KeyboardBacklightDialog? = null
+
+ fun startListening() {
+ applicationScope.launch {
+ viewModel.dialogContent.collect { dialogViewModel ->
+ if (dialogViewModel != null) {
+ if (dialog == null) {
+ dialog = KeyboardBacklightDialog(context, dialogViewModel)
+ // pass viewModel and show
+ }
+ } else {
+ dialog?.dismiss()
+ dialog = null
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
new file mode 100644
index 0000000..b68a2a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyboard.backlight.ui.view
+
+import android.app.Dialog
+import android.content.Context
+import android.os.Bundle
+import com.android.systemui.keyboard.backlight.ui.viewmodel.BacklightDialogContentViewModel
+
+class KeyboardBacklightDialog(context: Context, val viewModel: BacklightDialogContentViewModel) :
+ Dialog(context) {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ // TODO(b/268650355) Implement the dialog
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogContentViewModel.kt
similarity index 68%
copy from packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt
copy to packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogContentViewModel.kt
index ea15a9f..3ef0ca3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogContentViewModel.kt
@@ -15,10 +15,6 @@
*
*/
-package com.android.systemui.keyboard.data.model
+package com.android.systemui.keyboard.backlight.ui.viewmodel
-/**
- * Model for current state of keyboard backlight brightness. [level] indicates current level of
- * backlight brightness and [maxLevel] its max possible value.
- */
-data class BacklightModel(val level: Int, val maxLevel: Int)
+data class BacklightDialogContentViewModel(val currentValue: Int, val maxValue: Int)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModel.kt
new file mode 100644
index 0000000..86abca5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModel.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.keyboard.backlight.ui.viewmodel
+
+import android.view.accessibility.AccessibilityManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyboard.backlight.domain.interactor.KeyboardBacklightInteractor
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
+import javax.inject.Inject
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.map
+
+/**
+ * Responsible for dialog visibility and content - emits [BacklightDialogContentViewModel] if dialog
+ * should be shown and hidden otherwise
+ */
+@SysUISingleton
+class BacklightDialogViewModel
+@Inject
+constructor(
+ interactor: KeyboardBacklightInteractor,
+ private val accessibilityManagerWrapper: AccessibilityManagerWrapper,
+) {
+
+ private val timeoutMillis: Long
+ get() =
+ accessibilityManagerWrapper
+ .getRecommendedTimeoutMillis(
+ DEFAULT_DIALOG_TIMEOUT_MILLIS,
+ AccessibilityManager.FLAG_CONTENT_ICONS
+ )
+ .toLong()
+
+ val dialogContent: Flow<BacklightDialogContentViewModel?> =
+ interactor.backlight
+ .filterNotNull()
+ .map { BacklightDialogContentViewModel(it.level, it.maxLevel) }
+ .timeout(timeoutMillis, emitAfterTimeout = null)
+
+ private fun <T> Flow<T>.timeout(timeoutMillis: Long, emitAfterTimeout: T): Flow<T> {
+ return flatMapLatest {
+ flow {
+ emit(it)
+ delay(timeoutMillis)
+ emit(emitAfterTimeout)
+ }
+ }
+ }
+
+ private companion object {
+ const val DEFAULT_DIALOG_TIMEOUT_MILLIS = 3000
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt
index dd5c5d3..b86083a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt
@@ -25,7 +25,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.keyboard.data.model.BacklightModel
+import com.android.systemui.keyboard.shared.model.BacklightModel
import java.util.concurrent.Executor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shared/model/BacklightModel.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt
rename to packages/SystemUI/src/com/android/systemui/keyboard/shared/model/BacklightModel.kt
index ea15a9f..4a32f79 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/data/model/BacklightModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shared/model/BacklightModel.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.systemui.keyboard.data.model
+package com.android.systemui.keyboard.shared.model
/**
* Model for current state of keyboard backlight brightness. [level] indicates current level of
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 2ad1ab7..288f0cc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -966,13 +966,24 @@
public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
+ if (!handleOnAnimationStart(
+ transit, apps, wallpapers, nonApps, finishedCallback)) {
+ // Usually we rely on animation completion to synchronize occluded status,
+ // but there was no animation to play, so just update it now.
+ setOccluded(true /* isOccluded */, false /* animate */);
+ }
+ }
+
+ private boolean handleOnAnimationStart(int transit, RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
if (apps == null || apps.length == 0 || apps[0] == null) {
if (DEBUG) {
Log.d(TAG, "No apps provided to the OccludeByDream runner; "
+ "skipping occluding animation.");
}
finishedCallback.onAnimationFinished();
- return;
+ return false;
}
final RemoteAnimationTarget primary = apps[0];
@@ -982,7 +993,7 @@
Log.w(TAG, "The occluding app isn't Dream; "
+ "finishing up. Please check that the config is correct.");
finishedCallback.onAnimationFinished();
- return;
+ return false;
}
final SyncRtSurfaceTransactionApplier applier =
@@ -1031,6 +1042,7 @@
mOccludeByDreamAnimator.start();
});
+ return true;
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 47ef0fa..cb89106 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -46,6 +46,8 @@
import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule;
import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule;
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceModule;
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger;
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLoggerImpl;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
@@ -64,6 +66,8 @@
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
+import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.CoroutineScope;
/**
* Dagger Module providing keyguard.
@@ -153,4 +157,10 @@
public ViewMediatorCallback providesViewMediatorCallback(KeyguardViewMediator viewMediator) {
return viewMediator.getViewMediatorCallback();
}
+
+ /** */
+ @Provides
+ public KeyguardQuickAffordancesMetricsLogger providesKeyguardQuickAffordancesMetricsLogger() {
+ return new KeyguardQuickAffordancesMetricsLoggerImpl();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index a3b3d0f..76f20d25 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -80,6 +80,9 @@
*/
val isKeyguardShowing: Flow<Boolean>
+ /** Is the keyguard in a unlocked state? */
+ val isKeyguardUnlocked: Flow<Boolean>
+
/** Is an activity showing over the keyguard? */
val isKeyguardOccluded: Flow<Boolean>
@@ -278,6 +281,31 @@
}
.distinctUntilChanged()
+ override val isKeyguardUnlocked: Flow<Boolean> =
+ conflatedCallbackFlow {
+ val callback =
+ object : KeyguardStateController.Callback {
+ override fun onUnlockedChanged() {
+ trySendWithFailureLogging(
+ keyguardStateController.isUnlocked,
+ TAG,
+ "updated isKeyguardUnlocked"
+ )
+ }
+ }
+
+ keyguardStateController.addCallback(callback)
+ // Adding the callback does not send an initial update.
+ trySendWithFailureLogging(
+ keyguardStateController.isUnlocked,
+ TAG,
+ "initial isKeyguardUnlocked"
+ )
+
+ awaitClose { keyguardStateController.removeCallback(callback) }
+ }
+ .distinctUntilChanged()
+
override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow {
val callback =
object : KeyguardStateController.Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index 0c4bca6..100bc59 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -68,8 +68,11 @@
/**
* Begin a transition from one state to another. Transitions are interruptible, and will issue a
* [TransitionStep] with state = [TransitionState.CANCELED] before beginning the next one.
+ *
+ * When canceled, there are two options: to continue from the current position of the prior
+ * transition, or to reset the position. When [resetIfCanceled] == true, it will do the latter.
*/
- fun startTransition(info: TransitionInfo): UUID?
+ fun startTransition(info: TransitionInfo, resetIfCanceled: Boolean = false): UUID?
/**
* Allows manual control of a transition. When calling [startTransition], the consumer must pass
@@ -130,7 +133,10 @@
)
}
- override fun startTransition(info: TransitionInfo): UUID? {
+ override fun startTransition(
+ info: TransitionInfo,
+ resetIfCanceled: Boolean,
+ ): UUID? {
if (lastStep.from == info.from && lastStep.to == info.to) {
Log.i(TAG, "Duplicate call to start the transition, rejecting: $info")
return null
@@ -138,7 +144,11 @@
val startingValue =
if (lastStep.transitionState != TransitionState.FINISHED) {
Log.i(TAG, "Transition still active: $lastStep, canceling")
- lastStep.value
+ if (resetIfCanceled) {
+ 0f
+ } else {
+ lastStep.value
+ }
} else {
0f
}
@@ -227,10 +237,7 @@
}
private fun trace(step: TransitionStep, isManual: Boolean) {
- if (
- step.transitionState != TransitionState.STARTED &&
- step.transitionState != TransitionState.FINISHED
- ) {
+ if (step.transitionState == TransitionState.RUNNING) {
return
}
val traceName =
@@ -243,7 +250,10 @@
val traceCookie = traceName.hashCode()
if (step.transitionState == TransitionState.STARTED) {
Trace.beginAsyncSection(traceName, traceCookie)
- } else if (step.transitionState == TransitionState.FINISHED) {
+ } else if (
+ step.transitionState == TransitionState.FINISHED ||
+ step.transitionState == TransitionState.CANCELED
+ ) {
Trace.endAsyncSection(traceName, traceCookie)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 8715d1f..3beac0b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -34,7 +34,6 @@
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
@@ -57,14 +56,7 @@
private fun listenForDreamingToLockscreen() {
scope.launch {
- // Dependending on the dream, either dream state or occluded change will change first,
- // so listen for both
- combine(keyguardInteractor.isAbleToDream, keyguardInteractor.isKeyguardOccluded) {
- isAbleToDream,
- isKeyguardOccluded ->
- isAbleToDream && isKeyguardOccluded
- }
- .distinctUntilChanged()
+ keyguardInteractor.isAbleToDream
.sample(
combine(
keyguardInteractor.dozeTransitionModel,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index d01f489..911861d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -130,55 +130,59 @@
shadeRepository.shadeModel
.sample(
combine(
- keyguardTransitionInteractor.finishedKeyguardState,
+ keyguardTransitionInteractor.startedKeyguardTransitionStep,
keyguardInteractor.statusBarState,
- ::Pair
+ keyguardInteractor.isKeyguardUnlocked,
+ ::toTriple
),
- ::toTriple
+ ::toQuad
)
- .collect { (shadeModel, keyguardState, statusBarState) ->
+ .collect { (shadeModel, keyguardState, statusBarState, isKeyguardUnlocked) ->
val id = transitionId
if (id != null) {
- // An existing `id` means a transition is started, and calls to
- // `updateTransition` will control it until FINISHED or CANCELED
- var nextState =
- if (shadeModel.expansionAmount == 0f) {
- TransitionState.FINISHED
- } else if (shadeModel.expansionAmount == 1f) {
- TransitionState.CANCELED
- } else {
- TransitionState.RUNNING
- }
- keyguardTransitionRepository.updateTransition(
- id,
- 1f - shadeModel.expansionAmount,
- nextState,
- )
-
- if (
- nextState == TransitionState.CANCELED ||
- nextState == TransitionState.FINISHED
- ) {
- transitionId = null
- }
-
- // If canceled, just put the state back
- if (nextState == TransitionState.CANCELED) {
- keyguardTransitionRepository.startTransition(
- TransitionInfo(
- ownerName = name,
- from = KeyguardState.PRIMARY_BOUNCER,
- to = KeyguardState.LOCKSCREEN,
- animator = getAnimator(0.milliseconds)
- )
+ if (keyguardState.to == KeyguardState.PRIMARY_BOUNCER) {
+ // An existing `id` means a transition is started, and calls to
+ // `updateTransition` will control it until FINISHED or CANCELED
+ var nextState =
+ if (shadeModel.expansionAmount == 0f) {
+ TransitionState.FINISHED
+ } else if (shadeModel.expansionAmount == 1f) {
+ TransitionState.CANCELED
+ } else {
+ TransitionState.RUNNING
+ }
+ keyguardTransitionRepository.updateTransition(
+ id,
+ 1f - shadeModel.expansionAmount,
+ nextState,
)
+
+ if (
+ nextState == TransitionState.CANCELED ||
+ nextState == TransitionState.FINISHED
+ ) {
+ transitionId = null
+ }
+
+ // If canceled, just put the state back
+ if (nextState == TransitionState.CANCELED) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ ownerName = name,
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.LOCKSCREEN,
+ animator = getAnimator(0.milliseconds)
+ )
+ )
+ }
}
} else {
// TODO (b/251849525): Remove statusbarstate check when that state is
// integrated into KeyguardTransitionRepository
if (
- keyguardState == KeyguardState.LOCKSCREEN &&
+ keyguardState.to == KeyguardState.LOCKSCREEN &&
shadeModel.isUserDragging &&
+ !isKeyguardUnlocked &&
statusBarState == KEYGUARD
) {
transitionId =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index b59b413..94961cb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -17,6 +17,9 @@
package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Password
+import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -26,6 +29,8 @@
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
@@ -37,7 +42,8 @@
@Application private val scope: CoroutineScope,
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
- private val keyguardTransitionInteractor: KeyguardTransitionInteractor
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ private val keyguardSecurityModel: KeyguardSecurityModel,
) : TransitionInteractor(FromPrimaryBouncerTransitionInteractor::class.simpleName!!) {
override fun start() {
@@ -93,31 +99,47 @@
private fun listenForPrimaryBouncerToGone() {
scope.launch {
keyguardInteractor.isKeyguardGoingAway
- .sample(keyguardTransitionInteractor.finishedKeyguardState) { a, b -> Pair(a, b) }
- .collect { pair ->
- val (isKeyguardGoingAway, keyguardState) = pair
- if (isKeyguardGoingAway && keyguardState == KeyguardState.PRIMARY_BOUNCER) {
+ .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
+ .collect { (isKeyguardGoingAway, lastStartedTransitionStep) ->
+ if (
+ isKeyguardGoingAway &&
+ lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER
+ ) {
+ val securityMode =
+ keyguardSecurityModel.getSecurityMode(
+ KeyguardUpdateMonitor.getCurrentUser()
+ )
+ // IME for password requires a slightly faster animation
+ val duration =
+ if (securityMode == Password) {
+ TO_GONE_SHORT_DURATION
+ } else {
+ TO_GONE_DURATION
+ }
keyguardTransitionRepository.startTransition(
TransitionInfo(
ownerName = name,
from = KeyguardState.PRIMARY_BOUNCER,
to = KeyguardState.GONE,
- animator = getAnimator(),
- )
+ animator = getAnimator(duration),
+ ),
+ resetIfCanceled = true,
)
}
}
}
}
- private fun getAnimator(): ValueAnimator {
+ private fun getAnimator(duration: Duration = DEFAULT_DURATION): ValueAnimator {
return ValueAnimator().apply {
setInterpolator(Interpolators.LINEAR)
- setDuration(TRANSITION_DURATION_MS)
+ setDuration(duration.inWholeMilliseconds)
}
}
companion object {
- private const val TRANSITION_DURATION_MS = 300L
+ private val DEFAULT_DURATION = 300.milliseconds
+ val TO_GONE_DURATION = 250.milliseconds
+ val TO_GONE_SHORT_DURATION = 200.milliseconds
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index d25aff0a..ec99049 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -33,7 +33,9 @@
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isWakingOrStartingToWake
import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
@@ -95,6 +97,9 @@
awaitClose { commandQueue.removeCallback(callback) }
}
+ /** The device wake/sleep state */
+ val wakefulnessModel: Flow<WakefulnessModel> = repository.wakefulness
+
/**
* Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means
* that doze mode is not running and DREAMING is ok to commence.
@@ -109,6 +114,12 @@
isDreaming && isDozeOff(dozeTransitionModel.to)
}
)
+ .sample(
+ wakefulnessModel,
+ { isAbleToDream, wakefulnessModel ->
+ isAbleToDream && isWakingOrStartingToWake(wakefulnessModel)
+ }
+ )
.flatMapLatest { isAbleToDream ->
flow {
delay(50)
@@ -119,6 +130,8 @@
/** Whether the keyguard is showing or not. */
val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing
+ /** Whether the keyguard is unlocked or not. */
+ val isKeyguardUnlocked: Flow<Boolean> = repository.isKeyguardUnlocked
/** Whether the keyguard is occluded (covered by an activity). */
val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded
/** Whether the keyguard is going away. */
@@ -127,8 +140,6 @@
val primaryBouncerShowing: Flow<Boolean> = bouncerRepository.primaryBouncerVisible
/** Whether the alternate bouncer is showing or not. */
val alternateBouncerShowing: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
- /** The device wake/sleep state */
- val wakefulnessModel: Flow<WakefulnessModel> = repository.wakefulness
/** Observable for the [StatusBarState] */
val statusBarState: Flow<StatusBarState> = repository.statusBarState
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index bc3c720..1735b5d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -37,6 +37,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
@@ -68,6 +69,7 @@
private val featureFlags: FeatureFlags,
private val repository: Lazy<KeyguardQuickAffordanceRepository>,
private val launchAnimator: DialogLaunchAnimator,
+ private val logger: KeyguardQuickAffordancesMetricsLogger,
private val devicePolicyManager: DevicePolicyManager,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) {
@@ -122,10 +124,12 @@
* @param configKey The configuration key corresponding to the [KeyguardQuickAffordanceModel] of
* the affordance that was clicked
* @param expandable An optional [Expandable] for the activity- or dialog-launch animation
+ * @param slotId The id of the lockscreen slot that the affordance is in
*/
fun onQuickAffordanceTriggered(
configKey: String,
expandable: Expandable?,
+ slotId: String,
) {
@Suppress("UNCHECKED_CAST")
val config =
@@ -139,6 +143,7 @@
Log.e(TAG, "Affordance config with key of \"$configKey\" not found!")
return
}
+ logger.logOnShortcutTriggered(slotId, configKey)
when (val result = config.onTriggered(expandable)) {
is KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity ->
@@ -191,6 +196,7 @@
affordanceIds = selections,
)
+ logger.logOnShortcutSelected(slotId, affordanceId)
return true
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index 51b0277..e650b9f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -61,7 +61,15 @@
}
scope.launch {
- keyguardInteractor.isDreaming.collect { logger.log(TAG, VERBOSE, "isDreaming", it) }
+ keyguardInteractor.isAbleToDream.collect {
+ logger.log(TAG, VERBOSE, "isAbleToDream", it)
+ }
+ }
+
+ scope.launch {
+ keyguardInteractor.isKeyguardOccluded.collect {
+ logger.log(TAG, VERBOSE, "isOccluded", it)
+ }
}
scope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 1b7da5b..3c0ec35 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -78,6 +78,10 @@
val occludedToLockscreenTransition: Flow<TransitionStep> =
repository.transition(OCCLUDED, LOCKSCREEN)
+ /** PRIMARY_BOUNCER->GONE transition information. */
+ val primaryBouncerToGoneTransition: Flow<TransitionStep> =
+ repository.transition(PRIMARY_BOUNCER, GONE)
+
/**
* AOD<->LOCKSCREEN transition information, mapped to dozeAmount range of AOD (1f) <->
* Lockscreen (0f).
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordancesMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordancesMetricsLogger.kt
new file mode 100644
index 0000000..0b0225a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordancesMetricsLogger.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.shared.quickaffordance
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shared.system.SysUiStatsLog
+
+interface KeyguardQuickAffordancesMetricsLogger {
+
+ /**
+ * Logs shortcut Triggered
+ * @param slotId The id of the lockscreen slot that the affordance is in
+ * @param affordanceId The id of the lockscreen affordance
+ */
+ fun logOnShortcutTriggered(slotId: String, affordanceId: String)
+
+ /**
+ * Logs shortcut Selected
+ * @param slotId The id of the lockscreen slot that the affordance is in
+ * @param affordanceId The id of the lockscreen affordance
+ */
+ fun logOnShortcutSelected(slotId: String, affordanceId: String)
+
+}
+
+@SysUISingleton
+class KeyguardQuickAffordancesMetricsLoggerImpl : KeyguardQuickAffordancesMetricsLogger {
+
+ override fun logOnShortcutTriggered(slotId: String, affordanceId: String) {
+ SysUiStatsLog.write(
+ SysUiStatsLog.LOCKSCREEN_SHORTCUT_TRIGGERED,
+ slotId,
+ affordanceId,
+ )
+ }
+
+ override fun logOnShortcutSelected(slotId: String, affordanceId: String) {
+ SysUiStatsLog.write(
+ SysUiStatsLog.LOCKSCREEN_SHORTCUT_SELECTED,
+ slotId,
+ affordanceId,
+ )
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
index ca1e27c..38b9d50 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
@@ -47,6 +47,7 @@
duration: Duration,
onStep: (Float) -> Float,
startTime: Duration = 0.milliseconds,
+ onStart: (() -> Unit)? = null,
onCancel: (() -> Float)? = null,
onFinish: (() -> Float)? = null,
interpolator: Interpolator = LINEAR,
@@ -73,6 +74,7 @@
// the ViewModels of the last update
STARTED -> {
isComplete = false
+ onStart?.invoke()
max(0f, min(1f, value))
}
// Always send a final value of 1. Because of rounding, [value] may never be
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index 2a9060f6..d63636c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -502,6 +502,7 @@
KeyguardQuickAffordanceViewModel.OnClickedParameters(
configKey = configKey,
expandable = Expandable.fromView(view),
+ slotId = viewModel.slotId,
)
)
}
@@ -568,6 +569,7 @@
KeyguardQuickAffordanceViewModel.OnClickedParameters(
configKey = viewModel.configKey,
expandable = Expandable.fromView(view),
+ slotId = viewModel.slotId,
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
index 7db567b..2337ffc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -31,6 +31,7 @@
import com.android.systemui.keyguard.data.BouncerViewDelegate
import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.ActivityStarter
import kotlinx.coroutines.awaitCancellation
@@ -44,6 +45,7 @@
fun bind(
view: ViewGroup,
viewModel: KeyguardBouncerViewModel,
+ primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel,
componentFactory: KeyguardBouncerComponent.Factory
) {
// Builds the KeyguardSecurityContainerController from bouncer view group.
@@ -145,6 +147,12 @@
}
launch {
+ primaryBouncerToGoneTransitionViewModel.bouncerAlpha.collect { alpha ->
+ securityContainerController.setAlpha(alpha)
+ }
+ }
+
+ launch {
viewModel.bouncerExpansionAmount
.filter { it == EXPANSION_VISIBLE }
.collect {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
index ab9e6a4..a8e3464 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
@@ -175,7 +175,8 @@
areQuickAffordancesFullyOpaque,
selectedPreviewSlotId,
) { model, animateReveal, isFullyOpaque, selectedPreviewSlotId ->
- val isSelected = selectedPreviewSlotId == position.toSlotId()
+ val slotId = position.toSlotId()
+ val isSelected = selectedPreviewSlotId == slotId
model.toViewModel(
animateReveal = !previewMode.isInPreviewMode && animateReveal,
isClickable = isFullyOpaque && !previewMode.isInPreviewMode,
@@ -187,7 +188,8 @@
previewMode.isInPreviewMode &&
previewMode.shouldHighlightSelectedAffordance &&
!isSelected,
- forceInactive = previewMode.isInPreviewMode
+ forceInactive = previewMode.isInPreviewMode,
+ slotId = slotId,
)
}
.distinctUntilChanged()
@@ -200,6 +202,7 @@
isSelected: Boolean,
isDimmed: Boolean,
forceInactive: Boolean,
+ slotId: String,
): KeyguardQuickAffordanceViewModel {
return when (this) {
is KeyguardQuickAffordanceModel.Visible ->
@@ -212,6 +215,7 @@
quickAffordanceInteractor.onQuickAffordanceTriggered(
configKey = parameters.configKey,
expandable = parameters.expandable,
+ slotId = parameters.slotId,
)
},
isClickable = isClickable,
@@ -219,8 +223,11 @@
isSelected = isSelected,
useLongPress = quickAffordanceInteractor.useLongPress,
isDimmed = isDimmed,
+ slotId = slotId,
)
- is KeyguardQuickAffordanceModel.Hidden -> KeyguardQuickAffordanceViewModel()
+ is KeyguardQuickAffordanceModel.Hidden -> KeyguardQuickAffordanceViewModel(
+ slotId = slotId,
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
index cb68a82..38d1db6d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
@@ -32,9 +32,11 @@
val isSelected: Boolean = false,
val useLongPress: Boolean = false,
val isDimmed: Boolean = false,
+ val slotId: String,
) {
data class OnClickedParameters(
val configKey: String,
val expandable: Expandable?,
+ val slotId: String,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
new file mode 100644
index 0000000..92038e2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down PRIMARY_BOUNCER->GONE transition into discrete steps for corresponding views to
+ * consume.
+ */
+@SysUISingleton
+class PrimaryBouncerToGoneTransitionViewModel
+@Inject
+constructor(
+ private val interactor: KeyguardTransitionInteractor,
+ private val statusBarStateController: SysuiStatusBarStateController,
+) {
+ private val transitionAnimation =
+ KeyguardTransitionAnimationFlow(
+ transitionDuration = TO_GONE_DURATION,
+ transitionFlow = interactor.primaryBouncerToGoneTransition,
+ )
+
+ private var leaveShadeOpen: Boolean = false
+
+ /** Bouncer container alpha */
+ val bouncerAlpha: Flow<Float> =
+ transitionAnimation.createFlow(
+ duration = 200.milliseconds,
+ onStep = { 1f - it },
+ )
+
+ /** Scrim behind alpha */
+ val scrimBehindAlpha: Flow<Float> =
+ transitionAnimation.createFlow(
+ duration = TO_GONE_DURATION,
+ interpolator = EMPHASIZED_ACCELERATE,
+ onStart = { leaveShadeOpen = statusBarStateController.leaveOpenOnKeyguardHide() },
+ onStep = {
+ if (leaveShadeOpen) {
+ 1f
+ } else {
+ 1f - it
+ }
+ },
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
index acc537a..2fa8f9a 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
@@ -17,7 +17,7 @@
import com.android.systemui.notetask.quickaffordance.NoteTaskQuickAffordanceConfig
import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity
-import com.android.systemui.screenshot.AppClipsTrampolineActivity
+import com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity
/**
* Supported entry points for [NoteTaskController.showNoteTask].
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
index 8ced4646..5c59532 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
@@ -17,8 +17,10 @@
package com.android.systemui.notetask.shortcut
import android.app.Activity
+import android.app.role.RoleManager
import android.content.Intent
import android.os.Bundle
+import android.os.PersistableBundle
import androidx.activity.ComponentActivity
import androidx.annotation.DrawableRes
import androidx.core.content.pm.ShortcutInfoCompat
@@ -36,7 +38,11 @@
* href="https://developer.android.com/develop/ui/views/launch/shortcuts/creating-shortcuts#custom-pinned">Creating
* a custom shortcut activity</a>
*/
-class CreateNoteTaskShortcutActivity @Inject constructor() : ComponentActivity() {
+class CreateNoteTaskShortcutActivity
+@Inject
+constructor(
+ private val roleManager: RoleManager,
+) : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -59,12 +65,19 @@
intent: Intent,
@DrawableRes iconResource: Int,
): Intent {
+ val extras = PersistableBundle()
+
+ roleManager.getRoleHoldersAsUser(RoleManager.ROLE_NOTES, user).firstOrNull()?.let { name ->
+ extras.putString(EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE, name)
+ }
+
val shortcutInfo =
ShortcutInfoCompat.Builder(this, id)
.setIntent(intent)
.setShortLabel(shortLabel)
.setLongLived(true)
.setIcon(IconCompat.createWithResource(this, iconResource))
+ .setExtras(extras)
.build()
return ShortcutManagerCompat.createShortcutResultIntent(
@@ -75,5 +88,16 @@
private companion object {
private const val SHORTCUT_ID = "note-task-shortcut-id"
+
+ /**
+ * Shortcut extra which can point to a package name and can be used to indicate an alternate
+ * badge info. Launcher only reads this if the shortcut comes from a system app.
+ *
+ * Duplicated from [com.android.launcher3.icons.IconCache].
+ *
+ * @see com.android.launcher3.icons.IconCache.EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE
+ */
+ private const val EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE =
+ "extra_shortcut_badge_override_package"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 25ff308b..019ca52 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -631,7 +631,9 @@
final NavigationBarView navBarView =
mNavBarControllerLazy.get().getNavigationBarView(mContext.getDisplayId());
final NotificationPanelViewController panelController =
- mCentralSurfacesOptionalLazy.get().get().getNotificationPanelViewController();
+ mCentralSurfacesOptionalLazy.get()
+ .map(CentralSurfaces::getNotificationPanelViewController)
+ .orElse(null);
if (SysUiState.DEBUG) {
Log.d(TAG_OPS, "Updating sysui state flags: navBarFragment=" + navBarFragment
+ " navBarView=" + navBarView + " panelController=" + panelController);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
index c8c1337..7cfe232 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
@@ -57,7 +57,8 @@
import javax.inject.Inject;
-class ImageExporter {
+/** A class to help with exporting screenshot to storage. */
+public class ImageExporter {
private static final String TAG = LogConfig.logTag(ImageExporter.class);
static final Duration PENDING_ENTRY_TTL = Duration.ofHours(24);
@@ -90,7 +91,7 @@
private final FeatureFlags mFlags;
@Inject
- ImageExporter(ContentResolver resolver, FeatureFlags flags) {
+ public ImageExporter(ContentResolver resolver, FeatureFlags flags) {
mResolver = resolver;
mFlags = flags;
}
@@ -148,7 +149,7 @@
*
* @return a listenable future result
*/
- ListenableFuture<Result> export(Executor executor, UUID requestId, Bitmap bitmap,
+ public ListenableFuture<Result> export(Executor executor, UUID requestId, Bitmap bitmap,
UserHandle owner) {
return export(executor, requestId, bitmap, ZonedDateTime.now(), owner);
}
@@ -181,13 +182,14 @@
);
}
- static class Result {
- Uri uri;
- UUID requestId;
- String fileName;
- long timestamp;
- CompressFormat format;
- boolean published;
+ /** The result returned by the task exporting screenshots to storage. */
+ public static class Result {
+ public Uri uri;
+ public UUID requestId;
+ public String fileName;
+ public long timestamp;
+ public CompressFormat format;
+ public boolean published;
@Override
public String toString() {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
index fc94aed..7a62bae 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
@@ -93,13 +93,7 @@
@UiEvent(doc = "User has discarded the result of a long screenshot")
SCREENSHOT_LONG_SCREENSHOT_EXIT(911),
@UiEvent(doc = "A screenshot has been taken and saved to work profile")
- SCREENSHOT_SAVED_TO_WORK_PROFILE(1240),
- @UiEvent(doc = "Notes application triggered the screenshot for notes")
- SCREENSHOT_FOR_NOTE_TRIGGERED(1308),
- @UiEvent(doc = "User accepted the screenshot to be sent to the notes app")
- SCREENSHOT_FOR_NOTE_ACCEPTED(1309),
- @UiEvent(doc = "User cancelled the screenshot for notes app flow")
- SCREENSHOT_FOR_NOTE_CANCELLED(1310);
+ SCREENSHOT_SAVED_TO_WORK_PROFILE(1240);
private final int mId;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
index 3133924..4756cc8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.appclips;
-import static com.android.systemui.screenshot.AppClipsTrampolineActivity.ACTION_FINISH_FROM_TRAMPOLINE;
-import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_CALLING_PACKAGE_NAME;
-import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_RESULT_RECEIVER;
-import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_SCREENSHOT_URI;
-import static com.android.systemui.screenshot.AppClipsTrampolineActivity.PERMISSION_SELF;
-import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_ACCEPTED;
-import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_CANCELLED;
+import static com.android.systemui.screenshot.appclips.AppClipsEvent.SCREENSHOT_FOR_NOTE_ACCEPTED;
+import static com.android.systemui.screenshot.appclips.AppClipsEvent.SCREENSHOT_FOR_NOTE_CANCELLED;
+import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity.ACTION_FINISH_FROM_TRAMPOLINE;
+import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity.EXTRA_CALLING_PACKAGE_NAME;
+import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity.EXTRA_RESULT_RECEIVER;
+import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity.EXTRA_SCREENSHOT_URI;
+import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity.PERMISSION_SELF;
import android.app.Activity;
import android.content.BroadcastReceiver;
@@ -52,6 +52,8 @@
import com.android.internal.logging.UiEventLogger.UiEventEnum;
import com.android.settingslib.Utils;
import com.android.systemui.R;
+import com.android.systemui.screenshot.CropView;
+import com.android.systemui.screenshot.MagnifierView;
import com.android.systemui.settings.UserTracker;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsCrossProcessHelper.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsCrossProcessHelper.java
index 65fb4c9..e1619dc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsCrossProcessHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsCrossProcessHelper.java
@@ -32,12 +32,12 @@
/** An intermediary singleton object to help communicating with the cross process service. */
@SysUISingleton
-public class AppClipsCrossProcessHelper {
+class AppClipsCrossProcessHelper {
private final ServiceConnector<IAppClipsScreenshotHelperService> mProxyConnector;
@Inject
- public AppClipsCrossProcessHelper(@Application Context context) {
+ AppClipsCrossProcessHelper(@Application Context context) {
mProxyConnector = new ServiceConnector.Impl<IAppClipsScreenshotHelperService>(context,
new Intent(context, AppClipsScreenshotHelperService.class),
Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY
@@ -52,7 +52,7 @@
* pass around but not a {@link Bitmap}.
*/
@Nullable
- public Bitmap takeScreenshot() {
+ Bitmap takeScreenshot() {
try {
AndroidFuture<ScreenshotHardwareBufferInternal> future =
mProxyConnector.postForResult(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsEvent.java
new file mode 100644
index 0000000..7a085b9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsEvent.java
@@ -0,0 +1,40 @@
+/*
+ * 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.appclips;
+
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+
+enum AppClipsEvent implements UiEventLogger.UiEventEnum {
+
+ @UiEvent(doc = "Notes application triggered the screenshot for notes")
+ SCREENSHOT_FOR_NOTE_TRIGGERED(1308),
+ @UiEvent(doc = "User accepted the screenshot to be sent to the notes app")
+ SCREENSHOT_FOR_NOTE_ACCEPTED(1309),
+ @UiEvent(doc = "User cancelled the screenshot for notes app flow")
+ SCREENSHOT_FOR_NOTE_CANCELLED(1310);
+
+ private final int mId;
+
+ AppClipsEvent(int id) {
+ mId = id;
+ }
+
+ @Override
+ public int getId() {
+ return mId;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperService.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperService.java
index 6f8c365..83ff020 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperService.java
@@ -24,7 +24,6 @@
import androidx.annotation.Nullable;
-import com.android.systemui.screenshot.AppClipsActivity;
import com.android.wm.shell.bubbles.Bubbles;
import java.util.Optional;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java
index eda38e4..3cb1a34 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.appclips;
import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_BLOCKED_BY_ADMIN;
import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED;
@@ -24,7 +24,7 @@
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
import static com.android.systemui.flags.Flags.SCREENSHOT_APP_CLIPS;
-import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_TRIGGERED;
+import static com.android.systemui.screenshot.appclips.AppClipsEvent.SCREENSHOT_FOR_NOTE_TRIGGERED;
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
index b2910fd..4cbca28a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
@@ -14,12 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.appclips;
import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED;
-import static androidx.annotation.VisibleForTesting.PACKAGE_PRIVATE;
-
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.HardwareRenderer;
@@ -31,7 +29,6 @@
import android.os.Process;
import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
@@ -39,11 +36,10 @@
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.screenshot.appclips.AppClipsCrossProcessHelper;
+import com.android.systemui.screenshot.ImageExporter;
import com.google.common.util.concurrent.ListenableFuture;
-import java.time.ZonedDateTime;
import java.util.UUID;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
@@ -52,8 +48,7 @@
import javax.inject.Inject;
/** A {@link ViewModel} to help with the App Clips screenshot flow. */
-@VisibleForTesting(otherwise = PACKAGE_PRIVATE)
-public final class AppClipsViewModel extends ViewModel {
+final class AppClipsViewModel extends ViewModel {
private final AppClipsCrossProcessHelper mAppClipsCrossProcessHelper;
private final ImageExporter mImageExporter;
@@ -80,8 +75,7 @@
}
/** Grabs a screenshot and updates the {@link Bitmap} set in screenshot {@link LiveData}. */
- @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
- public void performScreenshot() {
+ void performScreenshot() {
mBgExecutor.execute(() -> {
Bitmap screenshot = mAppClipsCrossProcessHelper.takeScreenshot();
mMainExecutor.execute(() -> {
@@ -95,14 +89,12 @@
}
/** Returns a {@link LiveData} that holds the captured screenshot. */
- @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
- public LiveData<Bitmap> getScreenshot() {
+ LiveData<Bitmap> getScreenshot() {
return mScreenshotLiveData;
}
/** Returns a {@link LiveData} that holds the {@link Uri} where screenshot is saved. */
- @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
- public LiveData<Uri> getResultLiveData() {
+ LiveData<Uri> getResultLiveData() {
return mResultLiveData;
}
@@ -110,8 +102,7 @@
* Returns a {@link LiveData} that holds the error codes for
* {@link Intent#EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE}.
*/
- @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
- public LiveData<Integer> getErrorLiveData() {
+ LiveData<Integer> getErrorLiveData() {
return mErrorLiveData;
}
@@ -119,8 +110,7 @@
* Saves the provided {@link Drawable} to storage then informs the result {@link Uri} to
* {@link LiveData}.
*/
- @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
- public void saveScreenshotThenFinish(Drawable screenshotDrawable, Rect bounds) {
+ void saveScreenshotThenFinish(Drawable screenshotDrawable, Rect bounds) {
mBgExecutor.execute(() -> {
// Render the screenshot bitmap in background.
Bitmap screenshotBitmap = renderBitmap(screenshotDrawable, bounds);
@@ -128,7 +118,7 @@
// Export and save the screenshot in background.
// TODO(b/267310185): Save to work profile UserHandle.
ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export(
- mBgExecutor, UUID.randomUUID(), screenshotBitmap, ZonedDateTime.now(),
+ mBgExecutor, UUID.randomUUID(), screenshotBitmap,
Process.myUserHandle());
// Get the result and update state on main thread.
@@ -160,8 +150,7 @@
}
/** Helper factory to help with injecting {@link AppClipsViewModel}. */
- @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
- public static final class Factory implements ViewModelProvider.Factory {
+ static final class Factory implements ViewModelProvider.Factory {
private final AppClipsCrossProcessHelper mAppClipsCrossProcessHelper;
private final ImageExporter mImageExporter;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/ScreenshotHardwareBufferInternal.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/ScreenshotHardwareBufferInternal.java
index 3b107f1..1e53ebb 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/ScreenshotHardwareBufferInternal.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/ScreenshotHardwareBufferInternal.java
@@ -28,7 +28,7 @@
* An internal version of {@link ScreenshotHardwareBuffer} that helps with parceling the information
* necessary for creating a {@link Bitmap}.
*/
-public class ScreenshotHardwareBufferInternal implements Parcelable {
+class ScreenshotHardwareBufferInternal implements Parcelable {
public static final Creator<ScreenshotHardwareBufferInternal> CREATOR =
new Creator<>() {
@@ -45,7 +45,7 @@
private final HardwareBuffer mHardwareBuffer;
private final ParcelableColorSpace mParcelableColorSpace;
- public ScreenshotHardwareBufferInternal(
+ ScreenshotHardwareBufferInternal(
ScreenshotHardwareBuffer screenshotHardwareBuffer) {
mHardwareBuffer = screenshotHardwareBuffer.getHardwareBuffer();
mParcelableColorSpace = new ParcelableColorSpace(
@@ -65,7 +65,7 @@
* {@link Bitmap#wrapHardwareBuffer(HardwareBuffer, ColorSpace)} and
* {@link HardwareBuffer#close()} for more information.
*/
- public Bitmap createBitmapThenCloseBuffer() {
+ Bitmap createBitmapThenCloseBuffer() {
Bitmap bitmap = Bitmap.wrapHardwareBuffer(mHardwareBuffer,
mParcelableColorSpace.getColorSpace());
mHardwareBuffer.close();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 87350b46..c130b39 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -45,6 +45,7 @@
import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.keyguard.ui.binder.KeyguardBouncerViewBinder;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationInsetsController;
@@ -133,7 +134,8 @@
KeyguardBouncerViewModel keyguardBouncerViewModel,
KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory,
AlternateBouncerInteractor alternateBouncerInteractor,
- KeyguardTransitionInteractor keyguardTransitionInteractor
+ KeyguardTransitionInteractor keyguardTransitionInteractor,
+ PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel
) {
mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
@@ -160,6 +162,7 @@
KeyguardBouncerViewBinder.bind(
mView.findViewById(R.id.keyguard_bouncer_container),
keyguardBouncerViewModel,
+ primaryBouncerToGoneTransitionViewModel,
keyguardBouncerComponentFactory);
collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(),
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 d6dc671..664d61a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -3747,6 +3747,12 @@
@Override
public void notifyBiometricAuthModeChanged() {
mDozeServiceHost.updateDozing();
+ if (mBiometricUnlockController.getMode()
+ == BiometricUnlockController.MODE_DISMISS_BOUNCER) {
+ // Don't update the scrim controller at this time, in favor of the transition repository
+ // updating the scrim
+ return;
+ }
updateScrimController();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 753032c..3268032 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -59,7 +59,7 @@
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.fragment.StatusBarIconBlocklistKt;
-import com.android.systemui.statusbar.phone.fragment.StatusBarSystemEventAnimator;
+import com.android.systemui.statusbar.phone.fragment.StatusBarSystemEventDefaultAnimator;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -75,6 +75,8 @@
import javax.inject.Inject;
+import kotlin.Unit;
+
/** View Controller for {@link com.android.systemui.statusbar.phone.KeyguardStatusBarView}. */
public class KeyguardStatusBarViewController extends ViewController<KeyguardStatusBarView> {
private static final String TAG = "KeyguardStatusBarViewController";
@@ -123,7 +125,8 @@
public void onDensityOrFontScaleChanged() {
mView.loadDimens();
// The animator is dependent on resources for offsets
- mSystemEventAnimator = new StatusBarSystemEventAnimator(mView, getResources());
+ mSystemEventAnimator =
+ getSystemEventAnimator(mSystemEventAnimator.isAnimationRunning());
}
@Override
@@ -248,7 +251,8 @@
private int mStatusBarState;
private boolean mDozing;
private boolean mShowingKeyguardHeadsUp;
- private StatusBarSystemEventAnimator mSystemEventAnimator;
+ private StatusBarSystemEventDefaultAnimator mSystemEventAnimator;
+ private float mSystemEventAnimatorAlpha = 1;
/**
* The alpha value to be set on the View. If -1, this value is to be ignored.
@@ -324,7 +328,7 @@
mView.setKeyguardUserAvatarEnabled(
!mStatusBarUserChipViewModel.getChipEnabled());
- mSystemEventAnimator = new StatusBarSystemEventAnimator(mView, r);
+ mSystemEventAnimator = getSystemEventAnimator(/* isAnimationRunning */ false);
mDisableStateTracker = new DisableStateTracker(
/* mask1= */ DISABLE_SYSTEM_INFO,
@@ -480,6 +484,10 @@
* (1.0f - mKeyguardHeadsUpShowingAmount);
}
+ if (mSystemEventAnimator.isAnimationRunning()) {
+ newAlpha = Math.min(newAlpha, mSystemEventAnimatorAlpha);
+ }
+
boolean hideForBypass =
mFirstBypassAttempt && mKeyguardUpdateMonitor.shouldListenForFace()
|| mDelayShowingKeyguardStatusBar;
@@ -488,7 +496,7 @@
&& !mDozing
&& !hideForBypass
&& !mDisableStateTracker.isDisabled()
- ? View.VISIBLE : View.INVISIBLE;
+ ? View.VISIBLE : View.INVISIBLE;
updateViewState(newAlpha, newVisibility);
}
@@ -614,4 +622,15 @@
updateBlockedIcons();
}
};
+
+ private StatusBarSystemEventDefaultAnimator getSystemEventAnimator(boolean isAnimationRunning) {
+ return new StatusBarSystemEventDefaultAnimator(getResources(), (alpha) -> {
+ mSystemEventAnimatorAlpha = alpha;
+ updateViewState();
+ return Unit.INSTANCE;
+ }, (translationX) -> {
+ mView.setTranslationX(translationX);
+ return Unit.INSTANCE;
+ }, isAnimationRunning);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index fb8bf52..9fb942c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+
import static java.lang.Float.isNaN;
import android.animation.Animator;
@@ -53,9 +55,14 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
+import com.android.systemui.keyguard.shared.model.TransitionState;
+import com.android.systemui.keyguard.shared.model.TransitionStep;
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.stack.ViewState;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -71,6 +78,8 @@
import javax.inject.Inject;
+import kotlinx.coroutines.CoroutineDispatcher;
+
/**
* Controls both the scrim behind the notifications and in front of the notifications (when a
* security method gets shown).
@@ -197,6 +206,7 @@
private final ScreenOffAnimationController mScreenOffAnimationController;
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private final SysuiStatusBarStateController mStatusBarStateController;
private GradientColors mColors;
private boolean mNeedsDrawableColorUpdate;
@@ -251,6 +261,20 @@
private boolean mWakeLockHeld;
private boolean mKeyguardOccluded;
+ private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+ private CoroutineDispatcher mMainDispatcher;
+ private boolean mIsBouncerToGoneTransitionRunning = false;
+ private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
+ private final Consumer<Float> mScrimAlphaConsumer =
+ (Float alpha) -> {
+ mScrimInFront.setViewAlpha(mInFrontAlpha);
+ mNotificationsScrim.setViewAlpha(mNotificationsAlpha);
+ mBehindAlpha = alpha;
+ mScrimBehind.setViewAlpha(alpha);
+ };
+
+ Consumer<TransitionStep> mPrimaryBouncerToGoneTransition;
+
@Inject
public ScrimController(
LightBarController lightBarController,
@@ -265,13 +289,18 @@
@Main Executor mainExecutor,
ScreenOffAnimationController screenOffAnimationController,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
- StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
+ KeyguardTransitionInteractor keyguardTransitionInteractor,
+ SysuiStatusBarStateController sysuiStatusBarStateController,
+ @Main CoroutineDispatcher mainDispatcher) {
mScrimStateListener = lightBarController::setScrimState;
mDefaultScrimAlpha = BUSY_SCRIM_ALPHA;
mKeyguardStateController = keyguardStateController;
mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mStatusBarStateController = sysuiStatusBarStateController;
mKeyguardVisibilityCallback = new KeyguardVisibilityCallback();
mHandler = handler;
mMainExecutor = mainExecutor;
@@ -304,6 +333,9 @@
}
});
mColors = new GradientColors();
+ mPrimaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel;
+ mKeyguardTransitionInteractor = keyguardTransitionInteractor;
+ mMainDispatcher = mainDispatcher;
}
/**
@@ -343,6 +375,33 @@
for (ScrimState state : ScrimState.values()) {
state.prepare(state);
}
+
+ // Directly control transition to UNLOCKED scrim state from PRIMARY_BOUNCER, and make sure
+ // to report back that keyguard has faded away. This fixes cases where the scrim state was
+ // rapidly switching on unlock, due to shifts in state in CentralSurfacesImpl
+ mPrimaryBouncerToGoneTransition =
+ (TransitionStep step) -> {
+ TransitionState state = step.getTransitionState();
+
+ mIsBouncerToGoneTransitionRunning = state == TransitionState.RUNNING;
+
+ if (state == TransitionState.STARTED) {
+ setExpansionAffectsAlpha(false);
+ transitionTo(ScrimState.UNLOCKED);
+ }
+
+ if (state == TransitionState.FINISHED || state == TransitionState.CANCELED) {
+ setExpansionAffectsAlpha(true);
+ if (mKeyguardStateController.isKeyguardFadingAway()) {
+ mStatusBarKeyguardViewManager.onKeyguardFadedAway();
+ }
+ }
+ };
+
+ collectFlow(behindScrim, mKeyguardTransitionInteractor.getPrimaryBouncerToGoneTransition(),
+ mPrimaryBouncerToGoneTransition, mMainDispatcher);
+ collectFlow(behindScrim, mPrimaryBouncerToGoneTransitionViewModel.getScrimBehindAlpha(),
+ mScrimAlphaConsumer, mMainDispatcher);
}
// TODO(b/270984686) recompute scrim height accurately, based on shade contents.
@@ -372,6 +431,11 @@
}
public void transitionTo(ScrimState state, Callback callback) {
+ if (mIsBouncerToGoneTransitionRunning) {
+ Log.i(TAG, "Skipping transition to: " + state
+ + " while mIsBouncerToGoneTransitionRunning");
+ return;
+ }
if (state == mState) {
// Call the callback anyway, unless it's already enqueued
if (callback != null && mCallback != callback) {
@@ -1049,7 +1113,8 @@
mBehindAlpha = 1;
}
// Prevent notification scrim flicker when transitioning away from keyguard.
- if (mKeyguardStateController.isKeyguardGoingAway()) {
+ if (mKeyguardStateController.isKeyguardGoingAway()
+ && !mStatusBarStateController.leaveOpenOnKeyguardHide()) {
mNotificationsAlpha = 0;
}
@@ -1138,7 +1203,9 @@
Trace.traceCounter(Trace.TRACE_TAG_APP, getScrimName(scrimView) + "_tint",
Color.alpha(tint));
scrimView.setTint(tint);
- scrimView.setViewAlpha(alpha);
+ if (!mIsBouncerToGoneTransitionRunning) {
+ scrimView.setViewAlpha(alpha);
+ }
} else {
scrim.setAlpha(alpha);
}
@@ -1486,6 +1553,9 @@
}
public void setKeyguardOccluded(boolean keyguardOccluded) {
+ if (mKeyguardOccluded == keyguardOccluded) {
+ return;
+ }
mKeyguardOccluded = keyguardOccluded;
updateScrims();
}
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 726b234..edfc95f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -16,11 +16,13 @@
package com.android.systemui.statusbar.phone;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
import static android.service.notification.NotificationListenerService.REASON_CLICK;
import static com.android.systemui.statusbar.phone.CentralSurfaces.getActivityOptions;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.PendingIntent;
@@ -579,8 +581,14 @@
EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
entry.getKey());
mCentralSurfaces.wakeUpForFullScreenIntent();
- fullScreenIntent.send();
+
+ ActivityOptions options = ActivityOptions.makeBasic();
+ options.setPendingIntentBackgroundActivityStartMode(
+ MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+ fullScreenIntent.sendAndReturnResult(null, 0, null, null, null, null,
+ options.toBundle());
entry.notifyFullScreenIntentLaunched();
+
mMetricsLogger.count("note_fullscreen", 1);
String activityName;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
index c04ea36..5903fa3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
@@ -26,19 +26,39 @@
import com.android.systemui.statusbar.events.STATUS_BAR_X_MOVE_OUT
import com.android.systemui.statusbar.events.SystemStatusAnimationCallback
import com.android.systemui.util.animation.AnimationUtil.Companion.frames
+import com.android.systemui.util.doOnCancel
+import com.android.systemui.util.doOnEnd
+
+/**
+ * An implementation of [StatusBarSystemEventDefaultAnimator], applying the onAlphaChanged and
+ * onTranslationXChanged callbacks directly to the provided animatedView.
+ */
+class StatusBarSystemEventAnimator @JvmOverloads constructor(
+ val animatedView: View,
+ resources: Resources,
+ isAnimationRunning: Boolean = false
+) : StatusBarSystemEventDefaultAnimator(
+ resources = resources,
+ onAlphaChanged = animatedView::setAlpha,
+ onTranslationXChanged = animatedView::setTranslationX,
+ isAnimationRunning = isAnimationRunning
+)
/**
* Tied directly to [SystemStatusAnimationScheduler]. Any StatusBar-like thing (keyguard, collapsed
- * status bar fragment), can just feed this an animatable view to get the default system status
- * animation.
+ * status bar fragment), can use this Animator to get the default system status animation. It simply
+ * needs to implement the onAlphaChanged and onTranslationXChanged callbacks.
*
* This animator relies on resources, and should be recreated whenever resources are updated. While
* this class could be used directly as the animation callback, it's probably best to forward calls
* to it so that it can be recreated at any moment without needing to remove/add callback.
*/
-class StatusBarSystemEventAnimator(
- val animatedView: View,
- resources: Resources
+
+open class StatusBarSystemEventDefaultAnimator @JvmOverloads constructor(
+ resources: Resources,
+ private val onAlphaChanged: (Float) -> Unit,
+ private val onTranslationXChanged: (Float) -> Unit,
+ var isAnimationRunning: Boolean = false
) : SystemStatusAnimationCallback {
private val translationXIn: Int = resources.getDimensionPixelSize(
R.dimen.ongoing_appops_chip_animation_in_status_bar_translation_x)
@@ -46,18 +66,19 @@
R.dimen.ongoing_appops_chip_animation_out_status_bar_translation_x)
override fun onSystemEventAnimationBegin(): Animator {
+ isAnimationRunning = true
val moveOut = ValueAnimator.ofFloat(0f, 1f).apply {
duration = 23.frames
interpolator = STATUS_BAR_X_MOVE_OUT
addUpdateListener {
- animatedView.translationX = -(translationXIn * animatedValue as Float)
+ onTranslationXChanged(-(translationXIn * animatedValue as Float))
}
}
val alphaOut = ValueAnimator.ofFloat(1f, 0f).apply {
duration = 8.frames
interpolator = null
addUpdateListener {
- animatedView.alpha = animatedValue as Float
+ onAlphaChanged(animatedValue as Float)
}
}
@@ -67,13 +88,13 @@
}
override fun onSystemEventAnimationFinish(hasPersistentDot: Boolean): Animator {
- animatedView.translationX = translationXOut.toFloat()
+ onTranslationXChanged(translationXOut.toFloat())
val moveIn = ValueAnimator.ofFloat(1f, 0f).apply {
duration = 23.frames
startDelay = 7.frames
interpolator = STATUS_BAR_X_MOVE_IN
addUpdateListener {
- animatedView.translationX = translationXOut * animatedValue as Float
+ onTranslationXChanged(translationXOut * animatedValue as Float)
}
}
val alphaIn = ValueAnimator.ofFloat(0f, 1f).apply {
@@ -81,13 +102,14 @@
startDelay = 11.frames
interpolator = null
addUpdateListener {
- animatedView.alpha = animatedValue as Float
+ onAlphaChanged(animatedValue as Float)
}
}
val animatorSet = AnimatorSet()
animatorSet.playTogether(moveIn, alphaIn)
-
+ animatorSet.doOnEnd { isAnimationRunning = false }
+ animatorSet.doOnCancel { isAnimationRunning = false }
return animatorSet
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
index 030c54f..9952cfd 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
@@ -62,8 +62,6 @@
BluetoothAdapter.OnMetadataChangedListener {
private val stylusCallbacks: CopyOnWriteArrayList<StylusCallback> = CopyOnWriteArrayList()
- private val stylusBatteryCallbacks: CopyOnWriteArrayList<StylusBatteryCallback> =
- CopyOnWriteArrayList()
// This map should only be accessed on the handler
private val inputDeviceAddressMap: MutableMap<Int, String?> = ArrayMap()
@@ -106,14 +104,6 @@
stylusCallbacks.remove(callback)
}
- fun registerBatteryCallback(callback: StylusBatteryCallback) {
- stylusBatteryCallbacks.add(callback)
- }
-
- fun unregisterBatteryCallback(callback: StylusBatteryCallback) {
- stylusBatteryCallbacks.remove(callback)
- }
-
override fun onInputDeviceAdded(deviceId: Int) {
if (!hasStarted) return
@@ -195,7 +185,7 @@
"${device.address}: $isCharging"
}
- executeStylusBatteryCallbacks { cb ->
+ executeStylusCallbacks { cb ->
cb.onStylusBluetoothChargingStateChanged(inputDeviceId, device, isCharging)
}
}
@@ -221,7 +211,7 @@
onStylusUsed()
}
- executeStylusBatteryCallbacks { cb ->
+ executeStylusCallbacks { cb ->
cb.onStylusUsiBatteryStateChanged(deviceId, eventTimeMillis, batteryState)
}
}
@@ -329,10 +319,6 @@
stylusCallbacks.forEach(run)
}
- private fun executeStylusBatteryCallbacks(run: (cb: StylusBatteryCallback) -> Unit) {
- stylusBatteryCallbacks.forEach(run)
- }
-
private fun registerBatteryListener(deviceId: Int) {
try {
inputManager.addInputDeviceBatteryListener(deviceId, executor, this)
@@ -378,13 +364,6 @@
fun onStylusBluetoothConnected(deviceId: Int, btAddress: String) {}
fun onStylusBluetoothDisconnected(deviceId: Int, btAddress: String) {}
fun onStylusFirstUsed() {}
- }
-
- /**
- * Callback interface to receive stylus battery events from the StylusManager. All callbacks are
- * runs on the same background handler.
- */
- interface StylusBatteryCallback {
fun onStylusBluetoothChargingStateChanged(
inputDeviceId: Int,
btDevice: BluetoothDevice,
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt
index 27cafb1..3667392 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt
@@ -37,7 +37,7 @@
private val inputManager: InputManager,
private val stylusUsiPowerUi: StylusUsiPowerUI,
private val featureFlags: FeatureFlags,
-) : CoreStartable, StylusManager.StylusCallback, StylusManager.StylusBatteryCallback {
+) : CoreStartable, StylusManager.StylusCallback {
override fun onStylusAdded(deviceId: Int) {
// On some devices, the addition of a new internal stylus indicates the use of a
@@ -74,7 +74,6 @@
stylusUsiPowerUi.init()
stylusManager.registerCallback(this)
- stylusManager.registerBatteryCallback(this)
stylusManager.startListener()
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
index d5d3efd..3a7ac9c 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
@@ -31,6 +31,9 @@
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+
+import javax.inject.Inject;
/**
* If the attached USB accessory has a URL associated with it, and that URL is valid,
@@ -46,13 +49,27 @@
private UsbAccessory mAccessory;
private Uri mUri;
+ private final DeviceProvisionedController mDeviceProvisionedController;
+
+ @Inject
+ UsbAccessoryUriActivity(DeviceProvisionedController deviceProvisionedController) {
+ mDeviceProvisionedController = deviceProvisionedController;
+ }
+
@Override
public void onCreate(Bundle icicle) {
- getWindow().addSystemFlags(
+ getWindow().addSystemFlags(
WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
- super.onCreate(icicle);
+ super.onCreate(icicle);
- Intent intent = getIntent();
+ // Don't show this dialog during Setup Wizard
+ if (!mDeviceProvisionedController.isDeviceProvisioned()) {
+ Log.e(TAG, "device not provisioned");
+ finish();
+ return;
+ }
+
+ Intent intent = getIntent();
mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
String uriString = intent.getStringExtra("uri");
mUri = (uriString == null ? null : Uri.parse(uriString));
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 81ae6e8..c72853e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
@@ -115,6 +115,17 @@
}
/**
+ * Provide a Long running Executor.
+ */
+ @Provides
+ @SysUISingleton
+ @LongRunning
+ public static DelayableExecutor provideLongRunningDelayableExecutor(
+ @LongRunning Looper looper) {
+ return new ExecutorImpl(looper);
+ }
+
+ /**
* Provide a Background-Thread Executor.
*/
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index 76a01b9..54b3030 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -41,7 +41,7 @@
import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.LongRunning;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -71,17 +71,16 @@
private HandlerThread mWorker;
// used for most tasks (call canvas.drawBitmap, load/unload the bitmap)
- @Background
- private final DelayableExecutor mBackgroundExecutor;
+ @LongRunning
+ private final DelayableExecutor mLongExecutor;
// wait at least this duration before unloading the bitmap
private static final int DELAY_UNLOAD_BITMAP = 2000;
@Inject
- public ImageWallpaper(@Background DelayableExecutor backgroundExecutor,
- UserTracker userTracker) {
+ public ImageWallpaper(@LongRunning DelayableExecutor longExecutor, UserTracker userTracker) {
super();
- mBackgroundExecutor = backgroundExecutor;
+ mLongExecutor = longExecutor;
mUserTracker = userTracker;
}
@@ -131,7 +130,7 @@
setFixedSizeAllowed(true);
setShowForAllUsers(true);
mWallpaperLocalColorExtractor = new WallpaperLocalColorExtractor(
- mBackgroundExecutor,
+ mLongExecutor,
new WallpaperLocalColorExtractor.WallpaperLocalColorExtractorCallback() {
@Override
public void onColorsProcessed(List<RectF> regions,
@@ -230,7 +229,7 @@
}
private void drawFrame() {
- mBackgroundExecutor.execute(this::drawFrameSynchronized);
+ mLongExecutor.execute(this::drawFrameSynchronized);
}
private void drawFrameSynchronized() {
@@ -285,7 +284,7 @@
}
private void unloadBitmapIfNotUsed() {
- mBackgroundExecutor.execute(this::unloadBitmapIfNotUsedSynchronized);
+ mLongExecutor.execute(this::unloadBitmapIfNotUsedSynchronized);
}
private void unloadBitmapIfNotUsedSynchronized() {
@@ -381,7 +380,7 @@
* - the mini bitmap from color extractor is recomputed
* - the DELAY_UNLOAD_BITMAP has passed
*/
- mBackgroundExecutor.executeDelayed(
+ mLongExecutor.executeDelayed(
this::unloadBitmapIfNotUsedSynchronized, DELAY_UNLOAD_BITMAP);
}
// even if the bitmap cannot be loaded, call reportEngineShown
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java b/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java
index 988fd71..1e8446f 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractor.java
@@ -29,7 +29,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
-import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.LongRunning;
import com.android.systemui.util.Assert;
import java.io.FileDescriptor;
@@ -66,8 +66,8 @@
private final List<RectF> mPendingRegions = new ArrayList<>();
private final Set<RectF> mProcessedRegions = new ArraySet<>();
- @Background
- private final Executor mBackgroundExecutor;
+ @LongRunning
+ private final Executor mLongExecutor;
private final WallpaperLocalColorExtractorCallback mWallpaperLocalColorExtractorCallback;
@@ -101,13 +101,13 @@
/**
* Creates a new color extractor.
- * @param backgroundExecutor the executor on which the color extraction will be performed
+ * @param longExecutor the executor on which the color extraction will be performed
* @param wallpaperLocalColorExtractorCallback an interface to handle the callbacks from
* the color extractor.
*/
- public WallpaperLocalColorExtractor(@Background Executor backgroundExecutor,
+ public WallpaperLocalColorExtractor(@LongRunning Executor longExecutor,
WallpaperLocalColorExtractorCallback wallpaperLocalColorExtractorCallback) {
- mBackgroundExecutor = backgroundExecutor;
+ mLongExecutor = longExecutor;
mWallpaperLocalColorExtractorCallback = wallpaperLocalColorExtractorCallback;
}
@@ -117,7 +117,7 @@
* not recomputed.
*/
public void setDisplayDimensions(int displayWidth, int displayHeight) {
- mBackgroundExecutor.execute(() ->
+ mLongExecutor.execute(() ->
setDisplayDimensionsSynchronized(displayWidth, displayHeight));
}
@@ -144,7 +144,7 @@
* @param bitmap the new wallpaper
*/
public void onBitmapChanged(@NonNull Bitmap bitmap) {
- mBackgroundExecutor.execute(() -> onBitmapChangedSynchronized(bitmap));
+ mLongExecutor.execute(() -> onBitmapChangedSynchronized(bitmap));
}
private void onBitmapChangedSynchronized(@NonNull Bitmap bitmap) {
@@ -167,7 +167,7 @@
* @param pages the total number of pages of the launcher
*/
public void onPageChanged(int pages) {
- mBackgroundExecutor.execute(() -> onPageChangedSynchronized(pages));
+ mLongExecutor.execute(() -> onPageChangedSynchronized(pages));
}
private void onPageChangedSynchronized(int pages) {
@@ -194,7 +194,7 @@
*/
public void addLocalColorsAreas(@NonNull List<RectF> regions) {
if (regions.size() > 0) {
- mBackgroundExecutor.execute(() -> addLocalColorsAreasSynchronized(regions));
+ mLongExecutor.execute(() -> addLocalColorsAreasSynchronized(regions));
} else {
Log.w(TAG, "Attempt to add colors with an empty list");
}
@@ -218,7 +218,7 @@
* @param regions The areas of interest in our wallpaper (in screen pixel coordinates)
*/
public void removeLocalColorAreas(@NonNull List<RectF> regions) {
- mBackgroundExecutor.execute(() -> removeLocalColorAreasSynchronized(regions));
+ mLongExecutor.execute(() -> removeLocalColorAreasSynchronized(regions));
}
private void removeLocalColorAreasSynchronized(@NonNull List<RectF> regions) {
@@ -236,7 +236,7 @@
* Clean up the memory (in particular, the mini bitmap) used by this class.
*/
public void cleanUp() {
- mBackgroundExecutor.execute(this::cleanUpSynchronized);
+ mLongExecutor.execute(this::cleanUpSynchronized);
}
private void cleanUpSynchronized() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
index 3a168d4..d6dbd73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
@@ -450,6 +450,15 @@
swipeToPosition(0f, Direction.DOWN, 0);
}
+ @Test
+ public void testTouchSessionOnRemovedCalledTwice() {
+ mTouchHandler.onSessionStart(mTouchSession);
+ ArgumentCaptor<DreamTouchHandler.TouchSession.Callback> onRemovedCallbackCaptor =
+ ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.Callback.class);
+ verify(mTouchSession).registerCallback(onRemovedCallbackCaptor.capture());
+ onRemovedCallbackCaptor.getValue().onRemoved();
+ onRemovedCallbackCaptor.getValue().onRemoved();
+ }
private void swipeToPosition(float percent, Direction direction, float velocityY) {
Mockito.clearInvocations(mTouchSession);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractorTest.kt
new file mode 100644
index 0000000..ec94cde
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractorTest.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.keyboard.backlight.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
+import com.android.systemui.keyboard.shared.model.BacklightModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyboardBacklightInteractorTest : SysuiTestCase() {
+
+ private val keyboardRepository = FakeKeyboardRepository()
+ private lateinit var underTest: KeyboardBacklightInteractor
+
+ @Before
+ fun setUp() {
+ underTest = KeyboardBacklightInteractor(keyboardRepository)
+ }
+
+ @Test
+ fun emitsNull_whenKeyboardJustConnected() = runTest {
+ val latest by collectLastValue(underTest.backlight)
+ keyboardRepository.setKeyboardConnected(true)
+
+ assertThat(latest).isNull()
+ }
+
+ @Test
+ fun emitsBacklight_whenKeyboardConnectedAndBacklightChanged() = runTest {
+ keyboardRepository.setKeyboardConnected(true)
+ keyboardRepository.setBacklight(BacklightModel(1, 5))
+
+ assertThat(underTest.backlight.first()).isEqualTo(BacklightModel(1, 5))
+ }
+
+ @Test
+ fun emitsNull_afterKeyboardDisconnecting() = runTest {
+ val latest by collectLastValue(underTest.backlight)
+ keyboardRepository.setKeyboardConnected(true)
+ keyboardRepository.setBacklight(BacklightModel(1, 5))
+
+ keyboardRepository.setKeyboardConnected(false)
+
+ assertThat(latest).isNull()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModelTest.kt
new file mode 100644
index 0000000..ec05d10
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModelTest.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.keyboard.backlight.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyboard.backlight.domain.interactor.KeyboardBacklightInteractor
+import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
+import com.android.systemui.keyboard.shared.model.BacklightModel
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class BacklightDialogViewModelTest : SysuiTestCase() {
+
+ private val keyboardRepository = FakeKeyboardRepository()
+ private lateinit var underTest: BacklightDialogViewModel
+ @Mock private lateinit var accessibilityManagerWrapper: AccessibilityManagerWrapper
+ private val timeoutMillis = 3000L
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(accessibilityManagerWrapper.getRecommendedTimeoutMillis(any(), any()))
+ .thenReturn(timeoutMillis.toInt())
+ underTest =
+ BacklightDialogViewModel(
+ KeyboardBacklightInteractor(keyboardRepository),
+ accessibilityManagerWrapper
+ )
+ keyboardRepository.setKeyboardConnected(true)
+ }
+
+ @Test
+ fun emitsViewModel_whenBacklightChanged() = runTest {
+ keyboardRepository.setBacklight(BacklightModel(1, 5))
+
+ assertThat(underTest.dialogContent.first()).isEqualTo(BacklightDialogContentViewModel(1, 5))
+ }
+
+ @Test
+ fun emitsNull_afterTimeout() = runTest {
+ val latest by collectLastValue(underTest.dialogContent)
+ keyboardRepository.setBacklight(BacklightModel(1, 5))
+
+ assertThat(latest).isEqualTo(BacklightDialogContentViewModel(1, 5))
+ advanceTimeBy(timeoutMillis + 1)
+ assertThat(latest).isNull()
+ }
+
+ @Test
+ fun emitsNull_after5secDelay_fromLastBacklightChange() = runTest {
+ val latest by collectLastValue(underTest.dialogContent)
+ keyboardRepository.setKeyboardConnected(true)
+
+ keyboardRepository.setBacklight(BacklightModel(1, 5))
+ assertThat(latest).isEqualTo(BacklightDialogContentViewModel(1, 5))
+
+ advanceTimeBy(timeoutMillis * 2 / 3)
+ // timeout yet to pass, no new emission
+ keyboardRepository.setBacklight(BacklightModel(2, 5))
+ assertThat(latest).isEqualTo(BacklightDialogContentViewModel(2, 5))
+
+ advanceTimeBy(timeoutMillis * 2 / 3)
+ // timeout refreshed because of last `setBacklight`, still content present
+ assertThat(latest).isEqualTo(BacklightDialogContentViewModel(2, 5))
+
+ advanceTimeBy(timeoutMillis * 2 / 3)
+ // finally timeout reached and null emitted
+ assertThat(latest).isNull()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index a4e5bca..984f4be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -45,6 +45,7 @@
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRenderer
import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRendererFactory
import com.android.systemui.keyguard.ui.preview.KeyguardRemotePreviewManager
@@ -91,6 +92,7 @@
@Mock private lateinit var launchAnimator: DialogLaunchAnimator
@Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
+ @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
private lateinit var underTest: CustomizationProvider
private lateinit var testScope: TestScope
@@ -184,6 +186,7 @@
featureFlags = featureFlags,
repository = { quickAffordanceRepository },
launchAnimator = launchAnimator,
+ logger = logger,
devicePolicyManager = devicePolicyManager,
backgroundDispatcher = testDispatcher,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 0469e77..0e6f8d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -219,6 +219,29 @@
}
@Test
+ fun isKeyguardUnlocked() =
+ runTest(UnconfinedTestDispatcher()) {
+ whenever(keyguardStateController.isUnlocked).thenReturn(false)
+ var latest: Boolean? = null
+ val job = underTest.isKeyguardUnlocked.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isFalse()
+
+ val captor = argumentCaptor<KeyguardStateController.Callback>()
+ verify(keyguardStateController).addCallback(captor.capture())
+
+ whenever(keyguardStateController.isUnlocked).thenReturn(true)
+ captor.value.onUnlockedChanged()
+ assertThat(latest).isTrue()
+
+ whenever(keyguardStateController.isUnlocked).thenReturn(false)
+ captor.value.onUnlockedChanged()
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
fun isDozing() =
runTest(UnconfinedTestDispatcher()) {
var latest: Boolean? = null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 84ec125..46c623a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -42,6 +42,7 @@
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.UserFileManager
@@ -225,6 +226,7 @@
@Mock private lateinit var launchAnimator: DialogLaunchAnimator
@Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
+ @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
private lateinit var underTest: KeyguardQuickAffordanceInteractor
private lateinit var testScope: TestScope
@@ -331,6 +333,7 @@
featureFlags = featureFlags,
repository = { quickAffordanceRepository },
launchAnimator = launchAnimator,
+ logger = logger,
devicePolicyManager = devicePolicyManager,
backgroundDispatcher = testDispatcher,
)
@@ -360,10 +363,11 @@
KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
}
- underTest.onQuickAffordanceTriggered(
- configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
- expandable = expandable,
- )
+ underTest.onQuickAffordanceTriggered(
+ configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
+ expandable = expandable,
+ slotId = "",
+ )
if (startActivity) {
if (needsToUnlockFirst) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 62c9e5f..cd579db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -45,6 +45,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
@@ -80,6 +81,7 @@
@Mock private lateinit var launchAnimator: DialogLaunchAnimator
@Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
+ @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
private lateinit var underTest: KeyguardQuickAffordanceInteractor
@@ -186,6 +188,7 @@
featureFlags = featureFlags,
repository = { quickAffordanceRepository },
launchAnimator = launchAnimator,
+ logger = logger,
devicePolicyManager = devicePolicyManager,
backgroundDispatcher = testDispatcher,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index ae7a928..fe9098f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -19,6 +19,8 @@
import android.animation.ValueAnimator
import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Interpolators
import com.android.systemui.flags.FakeFeatureFlags
@@ -40,6 +42,7 @@
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.cancelChildren
@@ -51,6 +54,8 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
@@ -77,6 +82,7 @@
// Used to verify transition requests for test output
@Mock private lateinit var mockTransitionRepository: KeyguardTransitionRepository
@Mock private lateinit var commandQueue: CommandQueue
+ @Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel
private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor
private lateinit var fromDreamingTransitionInteractor: FromDreamingTransitionInteractor
@@ -102,6 +108,8 @@
transitionRepository = KeyguardTransitionRepositoryImpl()
runner = KeyguardTransitionRunner(transitionRepository)
+ whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(PIN)
+
val featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
fromLockscreenTransitionInteractor =
FromLockscreenTransitionInteractor(
@@ -173,16 +181,17 @@
keyguardInteractor = createKeyguardInteractor(featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
+ keyguardSecurityModel = keyguardSecurityModel,
)
fromPrimaryBouncerTransitionInteractor.start()
}
@Test
- fun `DREAMING to LOCKSCREEN - dreaming state changes first`() =
+ fun `DREAMING to LOCKSCREEN`() =
testScope.runTest {
- // GIVEN a device is dreaming and occluded
+ // GIVEN a device is dreaming
keyguardRepository.setDreamingWithOverlay(true)
- keyguardRepository.setKeyguardOccluded(true)
+ keyguardRepository.setWakefulnessModel(startingToWake())
runCurrent()
// GIVEN a prior transition has run to DREAMING
@@ -215,56 +224,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
- }
- // THEN a transition to BOUNCER should occur
- assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor")
- assertThat(info.from).isEqualTo(KeyguardState.DREAMING)
- assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
- assertThat(info.animator).isNotNull()
-
- coroutineContext.cancelChildren()
- }
-
- @Test
- fun `DREAMING to LOCKSCREEN - occluded state changes first`() =
- testScope.runTest {
- // GIVEN a device is dreaming and occluded
- keyguardRepository.setDreamingWithOverlay(true)
- keyguardRepository.setKeyguardOccluded(true)
- runCurrent()
-
- // GIVEN a prior transition has run to DREAMING
- runner.startTransition(
- testScope,
- TransitionInfo(
- ownerName = "",
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.DREAMING,
- animator =
- ValueAnimator().apply {
- duration = 10
- interpolator = Interpolators.LINEAR
- },
- )
- )
- runCurrent()
- reset(mockTransitionRepository)
-
- // WHEN doze is complete
- keyguardRepository.setDozeTransitionModel(
- DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
- )
- // AND occluded has stopped
- keyguardRepository.setKeyguardOccluded(false)
- advanceUntilIdle()
- // AND then dreaming has stopped
- keyguardRepository.setDreamingWithOverlay(false)
- advanceUntilIdle()
-
- val info =
- withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to BOUNCER should occur
assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor")
@@ -304,7 +264,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to PRIMARY_BOUNCER should occur
assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
@@ -345,7 +305,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
@@ -386,7 +346,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
@@ -427,7 +387,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
@@ -468,7 +428,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
@@ -505,7 +465,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor")
@@ -542,7 +502,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor")
@@ -583,7 +543,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -624,7 +584,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to AOD should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -661,7 +621,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to AOD should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -677,6 +637,7 @@
testScope.runTest {
// GIVEN a device that is not dreaming or dozing
keyguardRepository.setDreamingWithOverlay(false)
+ keyguardRepository.setWakefulnessModel(startingToWake())
keyguardRepository.setDozeTransitionModel(
DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
)
@@ -704,7 +665,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DREAMING should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -741,7 +702,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to PRIMARY_BOUNCER should occur
assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -784,7 +745,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to AOD should occur
assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -828,7 +789,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -870,7 +831,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to LOCKSCREEN should occur
assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -912,7 +873,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to AOD should occur
assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
@@ -954,7 +915,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
@@ -995,7 +956,7 @@
val info =
withArgCaptor<TransitionInfo> {
- verify(mockTransitionRepository).startTransition(capture())
+ verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to LOCKSCREEN should occur
assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index c727b3a..bfc09d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -45,6 +45,7 @@
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
@@ -89,6 +90,7 @@
@Mock private lateinit var launchAnimator: DialogLaunchAnimator
@Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
+ @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
private lateinit var underTest: KeyguardBottomAreaViewModel
@@ -208,6 +210,7 @@
featureFlags = featureFlags,
repository = { quickAffordanceRepository },
launchAnimator = launchAnimator,
+ logger = logger,
devicePolicyManager = devicePolicyManager,
backgroundDispatcher = testDispatcher,
),
@@ -230,6 +233,7 @@
icon = mock(),
canShowWhileLocked = false,
intent = Intent("action"),
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
)
val configKey =
setUpQuickAffordanceModel(
@@ -260,6 +264,7 @@
icon = mock(),
canShowWhileLocked = false,
intent = Intent("action"),
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
)
val configKey =
setUpQuickAffordanceModel(
@@ -272,6 +277,7 @@
testConfig =
TestConfig(
isVisible = false,
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
),
configKey = configKey,
)
@@ -299,6 +305,7 @@
icon = icon,
canShowWhileLocked = false,
intent = Intent("action"),
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
),
)
@@ -313,6 +320,7 @@
canShowWhileLocked = false,
intent = Intent("action"),
isSelected = true,
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
),
configKey = configKey,
)
@@ -341,6 +349,7 @@
icon = icon,
canShowWhileLocked = false,
intent = Intent("action"),
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
),
)
val configKey =
@@ -354,6 +363,7 @@
icon = icon,
canShowWhileLocked = false,
intent = Intent("action"),
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId(),
),
)
@@ -368,6 +378,7 @@
canShowWhileLocked = false,
intent = Intent("action"),
isDimmed = true,
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId(),
),
configKey = configKey,
)
@@ -387,6 +398,7 @@
canShowWhileLocked = false,
intent =
null, // This will cause it to tell the system that the click was handled.
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId(),
)
val configKey =
setUpQuickAffordanceModel(
@@ -409,6 +421,7 @@
val config =
TestConfig(
isVisible = false,
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
)
val configKey =
setUpQuickAffordanceModel(
@@ -434,6 +447,7 @@
icon = mock(),
canShowWhileLocked = false,
intent = Intent("action"),
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
)
setUpQuickAffordanceModel(
@@ -513,6 +527,7 @@
isClickable = true,
icon = mock(),
canShowWhileLocked = true,
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
)
)
assertThat(value()).isTrue()
@@ -524,6 +539,7 @@
isClickable = true,
icon = mock(),
canShowWhileLocked = false,
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId(),
)
)
assertThat(value()).isTrue()
@@ -532,6 +548,7 @@
testConfig =
TestConfig(
isVisible = false,
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
)
)
assertThat(value()).isTrue()
@@ -540,6 +557,7 @@
testConfig =
TestConfig(
isVisible = false,
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId(),
)
)
assertThat(value()).isFalse()
@@ -594,6 +612,7 @@
icon = mock(),
canShowWhileLocked = false,
intent = Intent("action"),
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
)
val configKey =
setUpQuickAffordanceModel(
@@ -626,6 +645,7 @@
icon = mock(),
canShowWhileLocked = false,
intent = Intent("action"),
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
)
val configKey =
setUpQuickAffordanceModel(
@@ -656,6 +676,7 @@
icon = mock(),
canShowWhileLocked = false,
intent = Intent("action"),
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
)
val configKey =
setUpQuickAffordanceModel(
@@ -684,6 +705,7 @@
icon = mock(),
canShowWhileLocked = false,
intent = Intent("action"),
+ slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
)
val configKey =
setUpQuickAffordanceModel(
@@ -748,12 +770,14 @@
assertThat(viewModel.isActivated).isEqualTo(testConfig.isActivated)
assertThat(viewModel.isSelected).isEqualTo(testConfig.isSelected)
assertThat(viewModel.isDimmed).isEqualTo(testConfig.isDimmed)
+ assertThat(viewModel.slotId).isEqualTo(testConfig.slotId)
if (testConfig.isVisible) {
assertThat(viewModel.icon).isEqualTo(testConfig.icon)
viewModel.onClicked.invoke(
KeyguardQuickAffordanceViewModel.OnClickedParameters(
configKey = configKey,
expandable = expandable,
+ slotId = viewModel.slotId,
)
)
if (testConfig.intent != null) {
@@ -775,6 +799,7 @@
val intent: Intent? = null,
val isSelected: Boolean = false,
val isDimmed: Boolean = false,
+ val slotId: String = ""
) {
init {
check(!isVisible || icon != null) { "Must supply non-null icon if visible!" }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
new file mode 100644
index 0000000..2a91799
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
@@ -0,0 +1,109 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.util.mockito.whenever
+import com.google.common.collect.Range
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() {
+ private lateinit var underTest: PrimaryBouncerToGoneTransitionViewModel
+ private lateinit var repository: FakeKeyguardTransitionRepository
+ @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ repository = FakeKeyguardTransitionRepository()
+ val interactor = KeyguardTransitionInteractor(repository)
+ underTest = PrimaryBouncerToGoneTransitionViewModel(interactor, statusBarStateController)
+ }
+
+ @Test
+ fun scrimBehindAlpha_leaveShadeOpen() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Float>()
+
+ val job = underTest.scrimBehindAlpha.onEach { values.add(it) }.launchIn(this)
+
+ whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(true)
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.6f))
+ repository.sendTransitionStep(step(1f))
+
+ assertThat(values.size).isEqualTo(4)
+ values.forEach { assertThat(it).isEqualTo(1f) }
+
+ job.cancel()
+ }
+
+ @Test
+ fun scrimBehindAlpha_doNotLeaveShadeOpen() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Float>()
+
+ val job = underTest.scrimBehindAlpha.onEach { values.add(it) }.launchIn(this)
+
+ whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(false)
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0.3f))
+ repository.sendTransitionStep(step(0.6f))
+ repository.sendTransitionStep(step(1f))
+
+ assertThat(values.size).isEqualTo(4)
+ values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
+ assertThat(values[3]).isEqualTo(0f)
+
+ job.cancel()
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.GONE,
+ value = value,
+ transitionState = state,
+ ownerName = "PrimaryBouncerToGoneTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
index 515e1ee..3c08d58 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
@@ -18,14 +18,14 @@
import static android.app.Activity.RESULT_OK;
-import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_ACCEPTED;
-import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_CANCELLED;
+import static com.android.systemui.screenshot.appclips.AppClipsEvent.SCREENSHOT_FOR_NOTE_ACCEPTED;
+import static com.android.systemui.screenshot.appclips.AppClipsEvent.SCREENSHOT_FOR_NOTE_CANCELLED;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -34,36 +34,35 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ApplicationInfoFlags;
import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.ResultReceiver;
+import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
import android.widget.ImageView;
-import androidx.lifecycle.MutableLiveData;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.intercepting.SingleActivityFactory;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.screenshot.AppClipsActivity;
-import com.android.systemui.screenshot.AppClipsTrampolineActivity;
-import com.android.systemui.screenshot.AppClipsViewModel;
+import com.android.systemui.screenshot.ImageExporter;
import com.android.systemui.settings.UserTracker;
+import com.google.common.util.concurrent.Futures;
+
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.UUID;
+import java.util.concurrent.Executor;
import java.util.function.BiConsumer;
@RunWith(AndroidTestingRunner.class)
@@ -78,18 +77,16 @@
private static final String TEST_CALLING_PACKAGE = "test-calling-package";
@Mock
- private AppClipsViewModel.Factory mViewModelFactory;
+ private AppClipsCrossProcessHelper mAppClipsCrossProcessHelper;
+ @Mock
+ private ImageExporter mImageExporter;
@Mock
private PackageManager mPackageManager;
@Mock
private UserTracker mUserTracker;
@Mock
private UiEventLogger mUiEventLogger;
- @Mock
- private AppClipsViewModel mViewModel;
- private MutableLiveData<Bitmap> mScreenshotLiveData;
- private MutableLiveData<Uri> mResultLiveData;
private AppClipsActivity mActivity;
// Using the deprecated ActivityTestRule and SingleActivityFactory to help with injecting mocks.
@@ -97,8 +94,11 @@
new SingleActivityFactory<>(AppClipsActivityTestable.class) {
@Override
protected AppClipsActivityTestable create(Intent unUsed) {
- return new AppClipsActivityTestable(mViewModelFactory, mPackageManager,
- mUserTracker, mUiEventLogger);
+ return new AppClipsActivityTestable(
+ new AppClipsViewModel.Factory(mAppClipsCrossProcessHelper,
+ mImageExporter, getContext().getMainExecutor(),
+ directExecutor()), mPackageManager, mUserTracker,
+ mUiEventLogger);
}
};
@@ -110,29 +110,17 @@
public void setUp() throws PackageManager.NameNotFoundException {
MockitoAnnotations.initMocks(this);
- mScreenshotLiveData = new MutableLiveData<>();
- mResultLiveData = new MutableLiveData<>();
- MutableLiveData<Integer> errorLiveData = new MutableLiveData<>();
-
- when(mViewModelFactory.create(any(Class.class))).thenReturn(mViewModel);
- when(mViewModel.getScreenshot()).thenReturn(mScreenshotLiveData);
- when(mViewModel.getResultLiveData()).thenReturn(mResultLiveData);
- when(mViewModel.getErrorLiveData()).thenReturn(errorLiveData);
when(mUserTracker.getUserId()).thenReturn(TEST_USER_ID);
-
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.uid = TEST_UID;
when(mPackageManager.getApplicationInfoAsUser(eq(TEST_CALLING_PACKAGE),
any(ApplicationInfoFlags.class), eq(TEST_USER_ID))).thenReturn(applicationInfo);
- doAnswer(invocation -> {
- runOnMainThread(() -> mScreenshotLiveData.setValue(TEST_BITMAP));
- return null;
- }).when(mViewModel).performScreenshot();
- doAnswer(invocation -> {
- runOnMainThread(() -> mResultLiveData.setValue(TEST_URI));
- return null;
- }).when(mViewModel).saveScreenshotThenFinish(any(Drawable.class), any(Rect.class));
+ when(mAppClipsCrossProcessHelper.takeScreenshot()).thenReturn(TEST_BITMAP);
+ ImageExporter.Result result = new ImageExporter.Result();
+ result.uri = TEST_URI;
+ when(mImageExporter.export(any(Executor.class), any(UUID.class), any(Bitmap.class),
+ any(UserHandle.class))).thenReturn(Futures.immediateFuture(result));
}
@After
@@ -140,7 +128,6 @@
mActivityRule.finishActivity();
}
- @Ignore("b/269403503")
@Test
public void appClipsLaunched_screenshotDisplayed() {
launchActivity();
@@ -148,7 +135,6 @@
assertThat(((ImageView) mActivity.findViewById(R.id.preview)).getDrawable()).isNotNull();
}
- @Ignore("b/269403503")
@Test
public void screenshotDisplayed_userConsented_screenshotExportedSuccessfully() {
ResultReceiver resultReceiver = createResultReceiver((resultCode, data) -> {
@@ -168,7 +154,6 @@
verify(mUiEventLogger).log(SCREENSHOT_FOR_NOTE_ACCEPTED, TEST_UID, TEST_CALLING_PACKAGE);
}
- @Ignore("b/269403503")
@Test
public void screenshotDisplayed_userDeclined() {
ResultReceiver resultReceiver = createResultReceiver((resultCode, data) -> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java
index e40c49b..ad06dcc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java
@@ -25,7 +25,8 @@
import static android.content.Intent.EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE;
import static com.android.systemui.flags.Flags.SCREENSHOT_APP_CLIPS;
-import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_SCREENSHOT_URI;
+import static com.android.systemui.screenshot.appclips.AppClipsEvent.SCREENSHOT_FOR_NOTE_TRIGGERED;
+import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity.EXTRA_SCREENSHOT_URI;
import static com.google.common.truth.Truth.assertThat;
@@ -59,8 +60,6 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.notetask.NoteTaskController;
-import com.android.systemui.screenshot.AppClipsTrampolineActivity;
-import com.android.systemui.screenshot.ScreenshotEvent;
import com.android.systemui.settings.UserTracker;
import com.android.wm.shell.bubbles.Bubbles;
@@ -262,8 +261,7 @@
mActivityRule.launchActivity(mActivityIntent);
waitForIdleSync();
- verify(mUiEventLogger).log(ScreenshotEvent.SCREENSHOT_FOR_NOTE_TRIGGERED, TEST_UID,
- TEST_CALLING_PACKAGE);
+ verify(mUiEventLogger).log(SCREENSHOT_FOR_NOTE_TRIGGERED, TEST_UID, TEST_CALLING_PACKAGE);
}
private void mockToSatisfyAllPrerequisites() throws NameNotFoundException {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java
index d5af7ce1..e7c3c05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.appclips;
import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED;
@@ -36,7 +36,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.screenshot.appclips.AppClipsCrossProcessHelper;
+import com.android.systemui.screenshot.ImageExporter;
import com.google.common.util.concurrent.Futures;
@@ -46,7 +46,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.time.ZonedDateTime;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
@@ -62,7 +61,7 @@
@Mock private AppClipsCrossProcessHelper mAppClipsCrossProcessHelper;
@Mock private ImageExporter mImageExporter;
- private com.android.systemui.screenshot.AppClipsViewModel mViewModel;
+ private AppClipsViewModel mViewModel;
@Before
public void setUp() {
@@ -99,8 +98,8 @@
@Test
public void saveScreenshot_throwsError_shouldUpdateErrorWithFailed() {
- when(mImageExporter.export(any(Executor.class), any(UUID.class), eq(null), any(
- ZonedDateTime.class), any(UserHandle.class))).thenReturn(
+ when(mImageExporter.export(any(Executor.class), any(UUID.class), eq(null),
+ any(UserHandle.class))).thenReturn(
Futures.immediateFailedFuture(new ExecutionException(new Throwable())));
mViewModel.saveScreenshotThenFinish(FAKE_DRAWABLE, FAKE_RECT);
@@ -113,9 +112,9 @@
@Test
public void saveScreenshot_failsSilently_shouldUpdateErrorWithFailed() {
- when(mImageExporter.export(any(Executor.class), any(UUID.class), eq(null), any(
- ZonedDateTime.class), any(UserHandle.class))).thenReturn(
- Futures.immediateFuture(new ImageExporter.Result()));
+ when(mImageExporter.export(any(Executor.class), any(UUID.class), eq(null),
+ any(UserHandle.class))).thenReturn(
+ Futures.immediateFuture(new ImageExporter.Result()));
mViewModel.saveScreenshotThenFinish(FAKE_DRAWABLE, FAKE_RECT);
waitForIdleSync();
@@ -129,9 +128,8 @@
public void saveScreenshot_succeeds_shouldUpdateResultWithUri() {
ImageExporter.Result result = new ImageExporter.Result();
result.uri = FAKE_URI;
- when(mImageExporter.export(any(Executor.class), any(UUID.class), eq(null), any(
- ZonedDateTime.class), any(UserHandle.class))).thenReturn(
- Futures.immediateFuture(result));
+ when(mImageExporter.export(any(Executor.class), any(UUID.class), eq(null),
+ any(UserHandle.class))).thenReturn(Futures.immediateFuture(result));
mViewModel.saveScreenshotThenFinish(FAKE_DRAWABLE, FAKE_RECT);
waitForIdleSync();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 0a401b0..82a5743 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -33,6 +33,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.NotificationInsetsController
@@ -65,48 +66,32 @@
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
- @Mock
- private lateinit var view: NotificationShadeWindowView
- @Mock
- private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
- @Mock
- private lateinit var centralSurfaces: CentralSurfaces
- @Mock
- private lateinit var dockManager: DockManager
- @Mock
- private lateinit var notificationPanelViewController: NotificationPanelViewController
- @Mock
- private lateinit var notificationShadeDepthController: NotificationShadeDepthController
- @Mock
- private lateinit var notificationShadeWindowController: NotificationShadeWindowController
- @Mock
- private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
- @Mock
- private lateinit var ambientState: AmbientState
- @Mock
- private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel
- @Mock
- private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
- @Mock
- private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
- @Mock
- private lateinit var statusBarWindowStateController: StatusBarWindowStateController
+ @Mock private lateinit var view: NotificationShadeWindowView
+ @Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
+ @Mock private lateinit var centralSurfaces: CentralSurfaces
+ @Mock private lateinit var dockManager: DockManager
+ @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController
+ @Mock private lateinit var notificationShadeDepthController: NotificationShadeDepthController
+ @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+ @Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
+ @Mock private lateinit var ambientState: AmbientState
+ @Mock private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel
+ @Mock private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
+ @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
+ @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController
@Mock
private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
- @Mock
- private lateinit var lockIconViewController: LockIconViewController
- @Mock
- private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
- @Mock
- private lateinit var pulsingGestureListener: PulsingGestureListener
- @Mock
- private lateinit var notificationInsetsController: NotificationInsetsController
- @Mock
- private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+ @Mock private lateinit var lockIconViewController: LockIconViewController
+ @Mock private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
+ @Mock private lateinit var pulsingGestureListener: PulsingGestureListener
+ @Mock private lateinit var notificationInsetsController: NotificationInsetsController
+ @Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
@Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
@Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
@Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
@Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+ @Mock
+ lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel
private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
private lateinit var interactionEventHandler: InteractionEventHandler
@@ -118,43 +103,44 @@
MockitoAnnotations.initMocks(this)
whenever(view.bottom).thenReturn(VIEW_BOTTOM)
whenever(view.findViewById<ViewGroup>(R.id.keyguard_bouncer_container))
- .thenReturn(mock(ViewGroup::class.java))
+ .thenReturn(mock(ViewGroup::class.java))
whenever(keyguardBouncerComponentFactory.create(any(ViewGroup::class.java)))
- .thenReturn(keyguardBouncerComponent)
+ .thenReturn(keyguardBouncerComponent)
whenever(keyguardBouncerComponent.securityContainerController)
- .thenReturn(keyguardSecurityContainerController)
+ .thenReturn(keyguardSecurityContainerController)
whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
- .thenReturn(emptyFlow<TransitionStep>())
- underTest = NotificationShadeWindowViewController(
- lockscreenShadeTransitionController,
- FalsingCollectorFake(),
- sysuiStatusBarStateController,
- dockManager,
- notificationShadeDepthController,
- view,
- notificationPanelViewController,
- ShadeExpansionStateManager(),
- stackScrollLayoutController,
- statusBarKeyguardViewManager,
- statusBarWindowStateController,
- lockIconViewController,
- centralSurfaces,
- notificationShadeWindowController,
- keyguardUnlockAnimationController,
- notificationInsetsController,
- ambientState,
- pulsingGestureListener,
- keyguardBouncerViewModel,
- keyguardBouncerComponentFactory,
- alternateBouncerInteractor,
- keyguardTransitionInteractor,
- )
+ .thenReturn(emptyFlow<TransitionStep>())
+ underTest =
+ NotificationShadeWindowViewController(
+ lockscreenShadeTransitionController,
+ FalsingCollectorFake(),
+ sysuiStatusBarStateController,
+ dockManager,
+ notificationShadeDepthController,
+ view,
+ notificationPanelViewController,
+ ShadeExpansionStateManager(),
+ stackScrollLayoutController,
+ statusBarKeyguardViewManager,
+ statusBarWindowStateController,
+ lockIconViewController,
+ centralSurfaces,
+ notificationShadeWindowController,
+ keyguardUnlockAnimationController,
+ notificationInsetsController,
+ ambientState,
+ pulsingGestureListener,
+ keyguardBouncerViewModel,
+ keyguardBouncerComponentFactory,
+ alternateBouncerInteractor,
+ keyguardTransitionInteractor,
+ primaryBouncerToGoneTransitionViewModel,
+ )
underTest.setupExpandedStatusBar()
- interactionEventHandlerCaptor =
- ArgumentCaptor.forClass(InteractionEventHandler::class.java)
+ interactionEventHandlerCaptor = ArgumentCaptor.forClass(InteractionEventHandler::class.java)
verify(view).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
- interactionEventHandler = interactionEventHandlerCaptor.value
+ interactionEventHandler = interactionEventHandlerCaptor.value
}
// Note: So far, these tests only cover interactions with the status bar view controller. More
@@ -184,14 +170,11 @@
@Test
fun handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSb() {
underTest.setStatusBarViewController(phoneStatusBarViewController)
- val downEvBelow = MotionEvent.obtain(
- 0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0
- )
+ val downEvBelow =
+ MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
interactionEventHandler.handleDispatchTouchEvent(downEvBelow)
- val nextEvent = MotionEvent.obtain(
- 0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0
- )
+ val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0)
whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
index 5d71979..faa6221 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
@@ -46,6 +46,7 @@
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationInsetsController;
@@ -101,6 +102,7 @@
@Mock private NotificationInsetsController mNotificationInsetsController;
@Mock private AlternateBouncerInteractor mAlternateBouncerInteractor;
@Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+ @Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
@Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler>
mInteractionEventHandlerCaptor;
@@ -150,7 +152,8 @@
mKeyguardBouncerViewModel,
mKeyguardBouncerComponentFactory,
mAlternateBouncerInteractor,
- mKeyguardTransitionInteractor
+ mKeyguardTransitionInteractor,
+ mPrimaryBouncerToGoneTransitionViewModel
);
mController.setupExpandedStatusBar();
mController.setDragDownHelper(mDragDownHelper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index dc5a047..e1fba81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -24,6 +24,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
@@ -58,8 +60,14 @@
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
+import com.android.systemui.keyguard.shared.model.KeyguardState;
+import com.android.systemui.keyguard.shared.model.TransitionState;
+import com.android.systemui.keyguard.shared.model.TransitionStep;
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.scrim.ScrimView;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.FakeConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -85,8 +93,10 @@
import java.util.HashSet;
import java.util.Map;
+import kotlinx.coroutines.CoroutineDispatcher;
+
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class ScrimControllerTest extends SysuiTestCase {
@@ -115,6 +125,11 @@
@Mock private DockManager mDockManager;
@Mock private ScreenOffAnimationController mScreenOffAnimationController;
@Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
+ @Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
+ @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+ @Mock private CoroutineDispatcher mMainDispatcher;
+ @Mock private SysuiStatusBarStateController mSysuiStatusBarStateController;
+
// TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The
// event-dispatch-on-registration pattern caused some of these unit tests to fail.)
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -225,13 +240,22 @@
when(mDelayedWakeLockBuilder.build()).thenReturn(mWakeLock);
when(mDockManager.isDocked()).thenReturn(false);
+ when(mKeyguardTransitionInteractor.getPrimaryBouncerToGoneTransition())
+ .thenReturn(emptyFlow());
+ when(mPrimaryBouncerToGoneTransitionViewModel.getScrimBehindAlpha())
+ .thenReturn(emptyFlow());
+
mScrimController = new ScrimController(mLightBarController,
mDozeParameters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder,
new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor,
mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()),
mScreenOffAnimationController,
mKeyguardUnlockAnimationController,
- mStatusBarKeyguardViewManager);
+ mStatusBarKeyguardViewManager,
+ mPrimaryBouncerToGoneTransitionViewModel,
+ mKeyguardTransitionInteractor,
+ mSysuiStatusBarStateController,
+ mMainDispatcher);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
mScrimController.setAnimatorListener(mAnimatorListener);
@@ -861,7 +885,11 @@
mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()),
mScreenOffAnimationController,
mKeyguardUnlockAnimationController,
- mStatusBarKeyguardViewManager);
+ mStatusBarKeyguardViewManager,
+ mPrimaryBouncerToGoneTransitionViewModel,
+ mKeyguardTransitionInteractor,
+ mSysuiStatusBarStateController,
+ mMainDispatcher);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
mScrimController.setAnimatorListener(mAnimatorListener);
@@ -1629,6 +1657,28 @@
assertScrimAlpha(mScrimBehind, 0);
}
+ @Test
+ public void ignoreTransitionRequestWhileKeyguardTransitionRunning() {
+ mScrimController.transitionTo(ScrimState.UNLOCKED);
+ mScrimController.mPrimaryBouncerToGoneTransition.accept(
+ new TransitionStep(KeyguardState.PRIMARY_BOUNCER, KeyguardState.GONE, 0f,
+ TransitionState.RUNNING, "ScrimControllerTest"));
+
+ // This request should not happen
+ mScrimController.transitionTo(ScrimState.BOUNCER);
+ assertThat(mScrimController.getState()).isEqualTo(ScrimState.UNLOCKED);
+ }
+
+ @Test
+ public void primaryBouncerToGoneOnFinishCallsKeyguardFadedAway() {
+ when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(true);
+ mScrimController.mPrimaryBouncerToGoneTransition.accept(
+ new TransitionStep(KeyguardState.PRIMARY_BOUNCER, KeyguardState.GONE, 0f,
+ TransitionState.FINISHED, "ScrimControllerTest"));
+
+ verify(mStatusBarKeyguardViewManager).onKeyguardFadedAway();
+ }
+
private void assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion) {
mScrimController.setRawPanelExpansionFraction(expansion);
finishAnimationsImmediately();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt
index f8bf4b9..4525ad2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt
@@ -65,8 +65,6 @@
@Mock lateinit var uiEventLogger: UiEventLogger
@Mock lateinit var stylusCallback: StylusManager.StylusCallback
@Mock lateinit var otherStylusCallback: StylusManager.StylusCallback
- @Mock lateinit var stylusBatteryCallback: StylusManager.StylusBatteryCallback
- @Mock lateinit var otherStylusBatteryCallback: StylusManager.StylusBatteryCallback
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var stylusManager: StylusManager
@@ -123,7 +121,6 @@
stylusManager.startListener()
stylusManager.registerCallback(stylusCallback)
- stylusManager.registerBatteryCallback(stylusBatteryCallback)
clearInvocations(inputManager)
}
@@ -434,23 +431,6 @@
}
@Test
- fun onMetadataChanged_multipleRegisteredBatteryCallbacks_executesAll() {
- stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
- stylusManager.registerBatteryCallback(otherStylusBatteryCallback)
-
- stylusManager.onMetadataChanged(
- bluetoothDevice,
- BluetoothDevice.METADATA_MAIN_CHARGING,
- "true".toByteArray()
- )
-
- verify(stylusBatteryCallback, times(1))
- .onStylusBluetoothChargingStateChanged(BT_STYLUS_DEVICE_ID, bluetoothDevice, true)
- verify(otherStylusBatteryCallback, times(1))
- .onStylusBluetoothChargingStateChanged(BT_STYLUS_DEVICE_ID, bluetoothDevice, true)
- }
-
- @Test
fun onMetadataChanged_chargingStateTrue_executesBatteryCallbacks() {
stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
@@ -460,7 +440,7 @@
"true".toByteArray()
)
- verify(stylusBatteryCallback, times(1))
+ verify(stylusCallback, times(1))
.onStylusBluetoothChargingStateChanged(BT_STYLUS_DEVICE_ID, bluetoothDevice, true)
}
@@ -474,7 +454,7 @@
"false".toByteArray()
)
- verify(stylusBatteryCallback, times(1))
+ verify(stylusCallback, times(1))
.onStylusBluetoothChargingStateChanged(BT_STYLUS_DEVICE_ID, bluetoothDevice, false)
}
@@ -486,7 +466,7 @@
"true".toByteArray()
)
- verifyNoMoreInteractions(stylusBatteryCallback)
+ verifyNoMoreInteractions(stylusCallback)
}
@Test
@@ -499,8 +479,7 @@
"true".toByteArray()
)
- verify(stylusBatteryCallback, never())
- .onStylusBluetoothChargingStateChanged(any(), any(), any())
+ verify(stylusCallback, never()).onStylusBluetoothChargingStateChanged(any(), any(), any())
}
@Test
@@ -614,7 +593,7 @@
fun onBatteryStateChanged_executesBatteryCallbacks() {
stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
- verify(stylusBatteryCallback, times(1))
+ verify(stylusCallback, times(1))
.onStylusUsiBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
index 82b80f5..3db0ecc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
@@ -96,7 +96,6 @@
startable.start()
verify(stylusManager, times(1)).registerCallback(startable)
- verify(stylusManager, times(1)).registerBatteryCallback(startable)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
index 85cfef7..fd368eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
@@ -16,22 +16,24 @@
package com.android.systemui.unfold.updates
+import android.content.Context
+import android.hardware.display.DisplayManager
+import android.os.Looper
import android.testing.AndroidTestingRunner
-import android.view.IRotationWatcher
-import android.view.IWindowManager
+import android.view.Display
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.utils.os.FakeHandler
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Captor
import org.mockito.Mock
+import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
@@ -42,19 +44,23 @@
private lateinit var rotationChangeProvider: RotationChangeProvider
- @Mock lateinit var windowManagerInterface: IWindowManager
+ @Mock lateinit var displayManager: DisplayManager
@Mock lateinit var listener: RotationListener
- @Captor lateinit var rotationWatcher: ArgumentCaptor<IRotationWatcher>
- private val fakeExecutor = FakeExecutor(FakeSystemClock())
+ @Mock lateinit var display: Display
+ @Captor lateinit var displayListener: ArgumentCaptor<DisplayManager.DisplayListener>
+ private val fakeHandler = FakeHandler(Looper.getMainLooper())
+
+ private lateinit var spyContext: Context
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- rotationChangeProvider =
- RotationChangeProvider(windowManagerInterface, context, fakeExecutor)
+ spyContext = spy(context)
+ whenever(spyContext.display).thenReturn(display)
+ rotationChangeProvider = RotationChangeProvider(displayManager, spyContext, fakeHandler)
rotationChangeProvider.addCallback(listener)
- fakeExecutor.runAllReady()
- verify(windowManagerInterface).watchRotation(rotationWatcher.capture(), anyInt())
+ fakeHandler.dispatchQueuedMessages()
+ verify(displayManager).registerDisplayListener(displayListener.capture(), any())
}
@Test
@@ -70,15 +76,16 @@
verify(listener).onRotationChanged(42)
rotationChangeProvider.removeCallback(listener)
- fakeExecutor.runAllReady()
+ fakeHandler.dispatchQueuedMessages()
sendRotationUpdate(43)
- verify(windowManagerInterface).removeRotationWatcher(any())
+ verify(displayManager).unregisterDisplayListener(any())
verifyNoMoreInteractions(listener)
}
private fun sendRotationUpdate(newRotation: Int) {
- rotationWatcher.value.onRotationChanged(newRotation)
- fakeExecutor.runAllReady()
+ whenever(display.rotation).thenReturn(newRotation)
+ displayListener.allValues.forEach { it.onDisplayChanged(display.displayId) }
+ fakeHandler.dispatchQueuedMessages()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
index 31cce4f..468c5a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
@@ -88,7 +88,7 @@
@Mock
private Bitmap mWallpaperBitmap;
FakeSystemClock mFakeSystemClock = new FakeSystemClock();
- FakeExecutor mFakeBackgroundExecutor = new FakeExecutor(mFakeSystemClock);
+ FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
@Before
public void setUp() throws Exception {
@@ -125,7 +125,7 @@
@Test
public void testBitmapWallpaper_normal() {
- // Will use a image wallpaper with dimensions DISPLAY_WIDTH x DISPLAY_WIDTH.
+ // Will use an image wallpaper with dimensions DISPLAY_WIDTH x DISPLAY_WIDTH.
// Then we expect the surface size will be also DISPLAY_WIDTH x DISPLAY_WIDTH.
int bitmapSide = DISPLAY_WIDTH;
testSurfaceHelper(
@@ -137,7 +137,7 @@
@Test
public void testBitmapWallpaper_low_resolution() {
- // Will use a image wallpaper with dimensions BMP_WIDTH x BMP_HEIGHT.
+ // Will use an image wallpaper with dimensions BMP_WIDTH x BMP_HEIGHT.
// Then we expect the surface size will be also BMP_WIDTH x BMP_HEIGHT.
testSurfaceHelper(LOW_BMP_WIDTH /* bitmapWidth */,
LOW_BMP_HEIGHT /* bitmapHeight */,
@@ -161,13 +161,13 @@
ImageWallpaper.CanvasEngine spyEngine = getSpyEngine();
spyEngine.onCreate(mSurfaceHolder);
spyEngine.onSurfaceRedrawNeeded(mSurfaceHolder);
- assertThat(mFakeBackgroundExecutor.numPending()).isAtLeast(1);
+ assertThat(mFakeExecutor.numPending()).isAtLeast(1);
int n = 0;
- while (mFakeBackgroundExecutor.numPending() >= 1) {
+ while (mFakeExecutor.numPending() >= 1) {
n++;
assertThat(n).isAtMost(10);
- mFakeBackgroundExecutor.runNextReady();
+ mFakeExecutor.runNextReady();
mFakeSystemClock.advanceTime(1000);
}
@@ -176,7 +176,7 @@
}
private ImageWallpaper createImageWallpaper() {
- return new ImageWallpaper(mFakeBackgroundExecutor, mUserTracker) {
+ return new ImageWallpaper(mFakeExecutor, mUserTracker) {
@Override
public Engine onCreateEngine() {
return new CanvasEngine() {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/data/repository/FakeKeyboardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/data/repository/FakeKeyboardRepository.kt
new file mode 100644
index 0000000..4e43546
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/data/repository/FakeKeyboardRepository.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.keyboard.data.repository
+
+import com.android.systemui.keyboard.shared.model.BacklightModel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.filterNotNull
+
+class FakeKeyboardRepository : KeyboardRepository {
+
+ private val _keyboardConnected = MutableStateFlow(false)
+ override val keyboardConnected: Flow<Boolean> = _keyboardConnected
+
+ private val _backlightState: MutableStateFlow<BacklightModel?> = MutableStateFlow(null)
+ // filtering to make sure backlight doesn't have default initial value
+ override val backlight: Flow<BacklightModel> = _backlightState.filterNotNull()
+
+ fun setBacklight(state: BacklightModel) {
+ _backlightState.value = state
+ }
+
+ fun setKeyboardConnected(connected: Boolean) {
+ _keyboardConnected.value = connected
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 1a371c7..194ed02 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -47,6 +47,9 @@
private val _isKeyguardShowing = MutableStateFlow(false)
override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing
+ private val _isKeyguardUnlocked = MutableStateFlow(false)
+ override val isKeyguardUnlocked: Flow<Boolean> = _isKeyguardUnlocked
+
private val _isKeyguardOccluded = MutableStateFlow(false)
override val isKeyguardOccluded: Flow<Boolean> = _isKeyguardOccluded
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index eac1bd1..16442bb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -37,7 +37,7 @@
_transitions.emit(step)
}
- override fun startTransition(info: TransitionInfo): UUID? {
+ override fun startTransition(info: TransitionInfo, resetIfCanceled: Boolean): UUID? {
return null
}
diff --git a/packages/SystemUI/unfold/Android.bp b/packages/SystemUI/unfold/Android.bp
index 180b611..2e0a946 100644
--- a/packages/SystemUI/unfold/Android.bp
+++ b/packages/SystemUI/unfold/Android.bp
@@ -35,6 +35,7 @@
],
kotlincflags: ["-Xjvm-default=enable"],
java_version: "1.8",
+ sdk_version: "current",
min_sdk_version: "current",
plugins: ["dagger2-compiler"],
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
index 068347c..a079668 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
@@ -19,8 +19,8 @@
import android.content.ContentResolver
import android.content.Context
import android.hardware.SensorManager
+import android.hardware.display.DisplayManager
import android.os.Handler
-import android.view.IWindowManager
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.dagger.UnfoldMain
import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
@@ -61,7 +61,7 @@
@BindsInstance @UnfoldMain executor: Executor,
@BindsInstance @UnfoldSingleThreadBg singleThreadBgExecutor: Executor,
@BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String,
- @BindsInstance windowManager: IWindowManager,
+ @BindsInstance displayManager: DisplayManager,
@BindsInstance contentResolver: ContentResolver = context.contentResolver
): UnfoldSharedComponent
}
@@ -84,8 +84,9 @@
@BindsInstance context: Context,
@BindsInstance config: UnfoldTransitionConfig,
@BindsInstance @UnfoldMain executor: Executor,
+ @BindsInstance @UnfoldMain handler: Handler,
@BindsInstance @UnfoldSingleThreadBg singleThreadBgExecutor: Executor,
- @BindsInstance windowManager: IWindowManager,
+ @BindsInstance displayManager: DisplayManager,
@BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String,
): RemoteUnfoldSharedComponent
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index 8eb79df..1839919 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -19,8 +19,8 @@
import android.content.Context
import android.hardware.SensorManager
+import android.hardware.display.DisplayManager
import android.os.Handler
-import android.view.IWindowManager
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.updates.FoldProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
@@ -47,7 +47,7 @@
mainExecutor: Executor,
singleThreadBgExecutor: Executor,
tracingTagPrefix: String,
- windowManager: IWindowManager,
+ displayManager: DisplayManager,
): UnfoldSharedComponent =
DaggerUnfoldSharedComponent.factory()
.create(
@@ -61,7 +61,7 @@
mainExecutor,
singleThreadBgExecutor,
tracingTagPrefix,
- windowManager,
+ displayManager,
)
/**
@@ -73,16 +73,18 @@
context: Context,
config: UnfoldTransitionConfig,
mainExecutor: Executor,
+ mainHandler: Handler,
singleThreadBgExecutor: Executor,
tracingTagPrefix: String,
- windowManager: IWindowManager,
+ displayManager: DisplayManager,
): RemoteUnfoldSharedComponent =
DaggerRemoteUnfoldSharedComponent.factory()
.create(
context,
config,
mainExecutor,
+ mainHandler,
singleThreadBgExecutor,
- windowManager,
+ displayManager,
tracingTagPrefix,
)
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
index d19b414..28e4936 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
@@ -16,7 +16,6 @@
package com.android.systemui.unfold.progress
import android.os.Trace
-import android.os.Trace.TRACE_TAG_APP
import android.util.Log
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.FloatPropertyCompat
@@ -110,7 +109,7 @@
if (DEBUG) {
Log.d(TAG, "onFoldUpdate = ${update.name()}")
- Trace.traceCounter(Trace.TRACE_TAG_APP, "fold_update", update)
+ Trace.setCounter("fold_update", update.toLong())
}
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 82fd225..d653fc7 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -119,7 +119,7 @@
"lastHingeAngle: $lastHingeAngle, " +
"lastHingeAngleBeforeTransition: $lastHingeAngleBeforeTransition"
)
- Trace.traceCounter(Trace.TRACE_TAG_APP, "hinge_angle", angle.toInt())
+ Trace.setCounter( "hinge_angle", angle.toLong())
}
val currentDirection =
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
index 0cf8224..ce8f1a1 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
@@ -17,36 +17,32 @@
package com.android.systemui.unfold.updates
import android.content.Context
+import android.hardware.display.DisplayManager
+import android.os.Handler
import android.os.RemoteException
-import android.view.IRotationWatcher
-import android.view.IWindowManager
-import android.view.Surface.Rotation
import com.android.systemui.unfold.dagger.UnfoldMain
import com.android.systemui.unfold.util.CallbackController
-import java.util.concurrent.Executor
import javax.inject.Inject
/**
- * Allows to subscribe to rotation changes.
- *
- * This is needed as rotation updates from [IWindowManager] are received in a binder thread, while
- * most of the times we want them in the main one. Updates are provided for the display associated
+ * Allows to subscribe to rotation changes. Updates are provided for the display associated
* to [context].
*/
class RotationChangeProvider
@Inject
constructor(
- private val windowManagerInterface: IWindowManager,
+ private val displayManager: DisplayManager,
private val context: Context,
- @UnfoldMain private val mainExecutor: Executor,
+ @UnfoldMain private val mainHandler: Handler,
) : CallbackController<RotationChangeProvider.RotationListener> {
private val listeners = mutableListOf<RotationListener>()
- private val rotationWatcher = RotationWatcher()
+ private val displayListener = RotationDisplayListener()
+ private var lastRotation: Int? = null
override fun addCallback(listener: RotationListener) {
- mainExecutor.execute {
+ mainHandler.post {
if (listeners.isEmpty()) {
subscribeToRotation()
}
@@ -55,17 +51,18 @@
}
override fun removeCallback(listener: RotationListener) {
- mainExecutor.execute {
+ mainHandler.post {
listeners -= listener
if (listeners.isEmpty()) {
unsubscribeToRotation()
+ lastRotation = null
}
}
}
private fun subscribeToRotation() {
try {
- windowManagerInterface.watchRotation(rotationWatcher, context.displayId)
+ displayManager.registerDisplayListener(displayListener, mainHandler)
} catch (e: RemoteException) {
throw e.rethrowFromSystemServer()
}
@@ -73,7 +70,7 @@
private fun unsubscribeToRotation() {
try {
- windowManagerInterface.removeRotationWatcher(rotationWatcher)
+ displayManager.unregisterDisplayListener(displayListener)
} catch (e: RemoteException) {
throw e.rethrowFromSystemServer()
}
@@ -82,12 +79,25 @@
/** Gets notified of rotation changes. */
fun interface RotationListener {
/** Called once rotation changes. */
- fun onRotationChanged(@Rotation newRotation: Int)
+ fun onRotationChanged(newRotation: Int)
}
- private inner class RotationWatcher : IRotationWatcher.Stub() {
- override fun onRotationChanged(rotation: Int) {
- mainExecutor.execute { listeners.forEach { it.onRotationChanged(rotation) } }
+ private inner class RotationDisplayListener : DisplayManager.DisplayListener {
+
+ override fun onDisplayChanged(displayId: Int) {
+ val display = context.display ?: return
+
+ if (displayId == display.displayId) {
+ val currentRotation = display.rotation
+ if (lastRotation == null || lastRotation != currentRotation) {
+ listeners.forEach { it.onRotationChanged(currentRotation) }
+ lastRotation = currentRotation
+ }
+ }
}
+
+ override fun onDisplayAdded(displayId: Int) {}
+
+ override fun onDisplayRemoved(displayId: Int) {}
}
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
index 06ca153..ce5c5f9 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
@@ -79,10 +79,9 @@
companion object {
fun ContentResolver.areAnimationsEnabled(): Boolean {
val animationScale =
- Settings.Global.getStringForUser(
+ Settings.Global.getString(
this,
Settings.Global.ANIMATOR_DURATION_SCALE,
- this.userId
)
?.toFloatOrNull()
?: 1f
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 1c46028..34033e2 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -134,7 +134,7 @@
private final ArraySet<RunningAppsChangedListener> mRunningAppsChangedListeners =
new ArraySet<>();
@Nullable private final SecureWindowCallback mSecureWindowCallback;
- @Nullable private final List<String> mDisplayCategories;
+ @Nullable private final Set<String> mDisplayCategories;
private final boolean mShowTasksInHostDeviceRecents;
@@ -178,7 +178,7 @@
@NonNull ActivityBlockedCallback activityBlockedCallback,
@NonNull SecureWindowCallback secureWindowCallback,
@NonNull IntentListenerCallback intentListenerCallback,
- @NonNull List<String> displayCategories,
+ @NonNull Set<String> displayCategories,
boolean showTasksInHostDeviceRecents) {
super();
mAllowedUsers = allowedUsers;
diff --git a/services/companion/java/com/android/server/companion/virtual/SensorController.java b/services/companion/java/com/android/server/companion/virtual/SensorController.java
index 864fe0f..7df0d86 100644
--- a/services/companion/java/com/android/server/companion/virtual/SensorController.java
+++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java
@@ -99,6 +99,9 @@
private int createSensorInternal(IBinder sensorToken, VirtualSensorConfig config)
throws SensorCreationException {
+ if (config.getType() <= 0) {
+ throw new SensorCreationException("Received an invalid virtual sensor type.");
+ }
final int handle = mSensorManagerInternal.createRuntimeSensor(mVirtualDeviceId,
config.getType(), config.getName(),
config.getVendor() == null ? "" : config.getVendor(), config.getFlags(),
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 2d010cf..b338d89 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -97,6 +97,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.function.Consumer;
@@ -830,7 +831,7 @@
}
private GenericWindowPolicyController createWindowPolicyController(
- @NonNull List<String> displayCategories) {
+ @NonNull Set<String> displayCategories) {
final GenericWindowPolicyController gwpc =
new GenericWindowPolicyController(FLAG_SECURE,
SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
diff --git a/services/core/java/com/android/server/LogMteState.java b/services/core/java/com/android/server/LogMteState.java
new file mode 100644
index 0000000..410dd83
--- /dev/null
+++ b/services/core/java/com/android/server/LogMteState.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.StatsManager;
+import android.content.Context;
+import android.util.StatsEvent;
+
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.Zygote;
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.util.List;
+
+public class LogMteState {
+ public static void register(Context context) {
+ context.getSystemService(StatsManager.class)
+ .setPullAtomCallback(
+ FrameworkStatsLog.MTE_STATE,
+ null, // use default PullAtomMetadata values
+ BackgroundThread.getExecutor(),
+ new StatsManager.StatsPullAtomCallback() {
+ @Override
+ public int onPullAtom(int atomTag, List<StatsEvent> data) {
+ if (atomTag != FrameworkStatsLog.MTE_STATE) {
+ throw new UnsupportedOperationException(
+ "Unknown tagId=" + atomTag);
+ }
+ data.add(
+ FrameworkStatsLog.buildStatsEvent(
+ FrameworkStatsLog.MTE_STATE,
+ Zygote.nativeSupportsMemoryTagging()
+ ? FrameworkStatsLog.MTE_STATE__STATE__ON
+ : FrameworkStatsLog.MTE_STATE__STATE__OFF));
+ return StatsManager.PULL_SUCCESS;
+ }
+ });
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index f90a3ce..24c9e0f 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2092,6 +2092,17 @@
r.appInfo.uid, r.intent.getIntent(), r, r.userId,
BackgroundStartPrivileges.NONE,
false /* isBindService */);
+ if (r.mAllowStartForeground == REASON_DENIED) {
+ Slog.w(TAG_SERVICE, "FGS type change to/from SHORT_SERVICE: "
+ + " BFSL DENIED.");
+ } else {
+ if (DEBUG_SHORT_SERVICE) {
+ Slog.w(TAG_SERVICE, "FGS type change to/from SHORT_SERVICE: "
+ + " BFSL Allowed: "
+ + PowerExemptionManager.reasonCodeToString(
+ r.mAllowStartForeground));
+ }
+ }
final boolean fgsStartAllowed =
!isBgFgsRestrictionEnabledForService
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index e3f00ded..9e95e5f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -995,7 +995,7 @@
private static final String KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION =
"enable_wait_for_finish_attach_application";
- private static final boolean DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION = true;
+ private static final boolean DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION = false;
/** @see #KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION */
public volatile boolean mEnableWaitForFinishAttachApplication =
diff --git a/services/core/java/com/android/server/am/BroadcastHistory.java b/services/core/java/com/android/server/am/BroadcastHistory.java
index 6ac0e8b..34658ca 100644
--- a/services/core/java/com/android/server/am/BroadcastHistory.java
+++ b/services/core/java/com/android/server/am/BroadcastHistory.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Intent;
import android.os.Bundle;
import android.util.TimeUtils;
@@ -26,6 +27,7 @@
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Date;
/**
@@ -48,6 +50,11 @@
}
/**
+ * List of broadcasts which are being delivered or yet to be delivered.
+ */
+ private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<>();
+
+ /**
* Historical data of past broadcasts, for debugging. This is a ring buffer
* whose last element is at mHistoryNext.
*/
@@ -70,7 +77,16 @@
final long[] mSummaryHistoryDispatchTime;
final long[] mSummaryHistoryFinishTime;
- public void addBroadcastToHistoryLocked(BroadcastRecord original) {
+ void onBroadcastEnqueuedLocked(@NonNull BroadcastRecord r) {
+ mPendingBroadcasts.add(r);
+ }
+
+ void onBroadcastFinishedLocked(@NonNull BroadcastRecord r) {
+ mPendingBroadcasts.remove(r);
+ addBroadcastToHistoryLocked(r);
+ }
+
+ public void addBroadcastToHistoryLocked(@NonNull BroadcastRecord original) {
// Note sometimes (only for sticky broadcasts?) we reuse BroadcastRecords,
// So don't change the incoming record directly.
final BroadcastRecord historyRecord = original.maybeStripForHistory();
@@ -93,7 +109,12 @@
}
@NeverCompile
- public void dumpDebug(ProtoOutputStream proto) {
+ public void dumpDebug(@NonNull ProtoOutputStream proto) {
+ for (int i = 0; i < mPendingBroadcasts.size(); ++i) {
+ final BroadcastRecord r = mPendingBroadcasts.get(i);
+ r.dumpDebug(proto, BroadcastQueueProto.PENDING_BROADCASTS);
+ }
+
int lastIndex = mHistoryNext;
int ringIndex = lastIndex;
do {
@@ -127,8 +148,20 @@
}
@NeverCompile
- public boolean dumpLocked(PrintWriter pw, String dumpPackage, String queueName,
- SimpleDateFormat sdf, boolean dumpAll, boolean needSep) {
+ public boolean dumpLocked(@NonNull PrintWriter pw, @Nullable String dumpPackage,
+ @NonNull String queueName, @NonNull SimpleDateFormat sdf,
+ boolean dumpAll, boolean needSep) {
+ pw.println(" Pending broadcasts:");
+ if (mPendingBroadcasts.isEmpty()) {
+ pw.println(" <empty>");
+ } else {
+ for (int idx = mPendingBroadcasts.size() - 1; idx >= 0; --idx) {
+ final BroadcastRecord r = mPendingBroadcasts.get(idx);
+ pw.print(" Broadcast #"); pw.print(idx); pw.println(":");
+ r.dump(pw, " ", sdf);
+ }
+ }
+
int i;
boolean printed = false;
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 84c03e5..32e5fd1 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -1145,8 +1145,11 @@
pw.print(" because ");
pw.print(reasonToString(mRunnableAtReason));
pw.println();
- pw.print("mProcessCached="); pw.println(mProcessCached);
+
pw.increaseIndent();
+ dumpProcessState(pw);
+ dumpBroadcastCounts(pw);
+
if (mActive != null) {
dumpRecord("ACTIVE", now, pw, mActive, mActiveIndex);
}
@@ -1167,6 +1170,49 @@
}
@NeverCompile
+ private void dumpProcessState(@NonNull IndentingPrintWriter pw) {
+ final StringBuilder sb = new StringBuilder();
+ if (mProcessCached) {
+ sb.append("CACHED");
+ }
+ if (mProcessInstrumented) {
+ if (sb.length() > 0) sb.append("|");
+ sb.append("INSTR");
+ }
+ if (mProcessPersistent) {
+ if (sb.length() > 0) sb.append("|");
+ sb.append("PER");
+ }
+ if (sb.length() > 0) {
+ pw.print("state:"); pw.println(sb);
+ }
+ if (runningOomAdjusted) {
+ pw.print("runningOomAdjusted:"); pw.println(runningOomAdjusted);
+ }
+ }
+
+ @NeverCompile
+ private void dumpBroadcastCounts(@NonNull IndentingPrintWriter pw) {
+ pw.print("e:"); pw.print(mCountEnqueued);
+ pw.print(" d:"); pw.print(mCountDeferred);
+ pw.print(" f:"); pw.print(mCountForeground);
+ pw.print(" fd:"); pw.print(mCountForegroundDeferred);
+ pw.print(" o:"); pw.print(mCountOrdered);
+ pw.print(" a:"); pw.print(mCountAlarm);
+ pw.print(" p:"); pw.print(mCountPrioritized);
+ pw.print(" pd:"); pw.print(mCountPrioritizedDeferred);
+ pw.print(" int:"); pw.print(mCountInteractive);
+ pw.print(" rt:"); pw.print(mCountResultTo);
+ pw.print(" ins:"); pw.print(mCountInstrumented);
+ pw.print(" m:"); pw.print(mCountManifest);
+
+ pw.print(" csi:"); pw.print(mActiveCountSinceIdle);
+ pw.print(" ccu:"); pw.print(mActiveCountConsecutiveUrgent);
+ pw.print(" ccn:"); pw.print(mActiveCountConsecutiveNormal);
+ pw.println();
+ }
+
+ @NeverCompile
private void dumpRecord(@Nullable String flavor, @UptimeMillisLong long now,
@NonNull IndentingPrintWriter pw, @NonNull BroadcastRecord record, int recordIndex) {
TimeUtils.formatDuration(record.enqueueTime, now, pw);
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 841b61e..81ca267 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -90,7 +90,6 @@
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
@@ -602,6 +601,7 @@
r.enqueueTime = SystemClock.uptimeMillis();
r.enqueueRealTime = SystemClock.elapsedRealtime();
r.enqueueClockTime = System.currentTimeMillis();
+ mHistory.onBroadcastEnqueuedLocked(r);
ArraySet<BroadcastRecord> replacedBroadcasts = mReplacedBroadcastsCache.getAndSet(null);
if (replacedBroadcasts == null) {
@@ -825,8 +825,9 @@
if (app != null && app.isInFullBackup()) {
return "isInFullBackup";
}
- if (mSkipPolicy.shouldSkip(r, receiver)) {
- return "mSkipPolicy";
+ final String skipReason = mSkipPolicy.shouldSkipMessage(r, receiver);
+ if (skipReason != null) {
+ return skipReason;
}
final Intent receiverIntent = r.getReceiverIntent(receiver);
if (receiverIntent == null) {
@@ -1100,7 +1101,8 @@
*/
private void setDeliveryState(@Nullable BroadcastProcessQueue queue,
@Nullable ProcessRecord app, @NonNull BroadcastRecord r, int index,
- @NonNull Object receiver, @DeliveryState int newDeliveryState, String reason) {
+ @NonNull Object receiver, @DeliveryState int newDeliveryState,
+ @NonNull String reason) {
final int cookie = traceBegin("setDeliveryState");
final int oldDeliveryState = getDeliveryState(r, index);
boolean checkFinished = false;
@@ -1108,7 +1110,7 @@
// Only apply state when we haven't already reached a terminal state;
// this is how we ignore racing timeout messages
if (!isDeliveryStateTerminal(oldDeliveryState)) {
- r.setDeliveryState(index, newDeliveryState);
+ r.setDeliveryState(index, newDeliveryState, reason);
if (oldDeliveryState == BroadcastRecord.DELIVERY_DEFERRED) {
r.deferredCount--;
} else if (newDeliveryState == BroadcastRecord.DELIVERY_DEFERRED) {
@@ -1659,7 +1661,7 @@
mService.notifyBroadcastFinishedLocked(r);
r.finishTime = SystemClock.uptimeMillis();
r.nextReceiver = r.receivers.size();
- mHistory.addBroadcastToHistoryLocked(r);
+ mHistory.onBroadcastFinishedLocked(r);
BroadcastQueueImpl.logBootCompletedBroadcastCompletionLatencyIfPossible(r);
@@ -1833,8 +1835,9 @@
if (dumpConstants) {
mConstants.dump(ipw);
}
+
if (dumpHistory) {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
needSep = mHistory.dumpLocked(ipw, dumpPackage, mQueueName, sdf, dumpAll, needSep);
}
return needSep;
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index f793c50..59f33dd 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -99,6 +99,7 @@
final @Nullable BroadcastOptions options; // BroadcastOptions supplied by caller
final @NonNull List<Object> receivers; // contains BroadcastFilter and ResolveInfo
final @DeliveryState int[] delivery; // delivery state of each receiver
+ final @NonNull String[] deliveryReasons; // reasons for delivery state of each receiver
final boolean[] deferredUntilActive; // whether each receiver is infinitely deferred
final int[] blockedUntilTerminalCount; // blocked until count of each receiver
@Nullable ProcessRecord resultToApp; // who receives final result if non-null
@@ -298,7 +299,7 @@
pw.print(" initialSticky="); pw.println(initialSticky);
}
if (nextReceiver != 0) {
- pw.print(prefix); pw.print("nextReceiver="); pw.print(nextReceiver);
+ pw.print(prefix); pw.print("nextReceiver="); pw.println(nextReceiver);
}
if (curFilter != null) {
pw.print(prefix); pw.print("curFilter="); pw.println(curFilter);
@@ -328,6 +329,7 @@
}
pw.print(prefix); pw.print("state="); pw.print(state); pw.println(stateStr);
}
+ pw.print(prefix); pw.print("terminalCount="); pw.println(terminalCount);
final int N = receivers != null ? receivers.size() : 0;
String p2 = prefix + " ";
PrintWriterPrinter printer = new PrintWriterPrinter(pw);
@@ -346,6 +348,7 @@
TimeUtils.formatDuration(terminalTime[i] - scheduledTime[i], pw);
pw.print(' ');
}
+ pw.print("("); pw.print(blockedUntilTerminalCount[i]); pw.print(") ");
pw.print("#"); pw.print(i); pw.print(": ");
if (o instanceof BroadcastFilter) {
pw.println(o);
@@ -356,6 +359,9 @@
} else {
pw.println(o);
}
+ if (deliveryReasons[i] != null) {
+ pw.print(p2); pw.print("reason: "); pw.println(deliveryReasons[i]);
+ }
}
}
@@ -393,6 +399,7 @@
options = _options;
receivers = (_receivers != null) ? _receivers : EMPTY_RECEIVERS;
delivery = new int[_receivers != null ? _receivers.size() : 0];
+ deliveryReasons = new String[delivery.length];
deferUntilActive = options != null ? options.isDeferUntilActive() : false;
deferredUntilActive = new boolean[deferUntilActive ? delivery.length : 0];
blockedUntilTerminalCount = calculateBlockedUntilTerminalCount(receivers, _serialized);
@@ -448,6 +455,7 @@
options = from.options;
receivers = from.receivers;
delivery = from.delivery;
+ deliveryReasons = from.deliveryReasons;
deferUntilActive = from.deferUntilActive;
deferredUntilActive = from.deferredUntilActive;
blockedUntilTerminalCount = from.blockedUntilTerminalCount;
@@ -609,8 +617,10 @@
* Update the delivery state of the given {@link #receivers} index.
* Automatically updates any time measurements related to state changes.
*/
- void setDeliveryState(int index, @DeliveryState int deliveryState) {
+ void setDeliveryState(int index, @DeliveryState int deliveryState,
+ @NonNull String reason) {
delivery[index] = deliveryState;
+ deliveryReasons[index] = reason;
if (deferUntilActive) deferredUntilActive[index] = false;
switch (deliveryState) {
case DELIVERY_DELIVERED:
@@ -977,7 +987,8 @@
if (label == null) {
label = intent.toString();
}
- mCachedToShortString = label + "/u" + userId;
+ mCachedToShortString = Integer.toHexString(System.identityHashCode(this))
+ + ":" + label + "/u" + userId;
}
return mCachedToShortString;
}
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index ae8ceab..6180117 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -468,6 +468,8 @@
long fgToken = proto.start(ServiceRecordProto.FOREGROUND);
proto.write(ServiceRecordProto.Foreground.ID, foregroundId);
foregroundNoti.dumpDebug(proto, ServiceRecordProto.Foreground.NOTIFICATION);
+ proto.write(ServiceRecordProto.Foreground.FOREGROUND_SERVICE_TYPE,
+ foregroundServiceType);
proto.end(fgToken);
}
ProtoUtils.toDuration(proto, ServiceRecordProto.CREATE_REAL_TIME, createRealTime, nowReal);
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
index 2d6966a..bb8d3f4 100644
--- a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
@@ -213,8 +213,6 @@
@Override
public void updateUidProcState(int uid, int procState, int capability) {
- mEventLog.logUpdateUidProcState(uid, procState, capability);
-
int uidState = processStateToUidState(procState);
int prevUidState = mUidStates.get(uid, AppOpsManager.MIN_PRIORITY_UID_STATE);
@@ -226,6 +224,10 @@
&& (uidState != prevUidState || capability != prevCapability))
|| (pendingStateCommitTime != 0
&& (uidState != pendingUidState || capability != pendingCapability))) {
+
+ // If this process update results in a capability or uid state change, log it. It's
+ // not interesting otherwise.
+ mEventLog.logUpdateUidProcState(uid, procState, capability);
mPendingUidStates.put(uid, uidState);
mPendingCapability.put(uid, capability);
@@ -389,10 +391,8 @@
private static class EventLog {
- // These seems a bit too verbose and not as useful, turning off for now.
- // DCE should be able to remove most associated code.
// Memory usage: 16 * size bytes
- private static final int UPDATE_UID_PROC_STATE_LOG_MAX_SIZE = 0;
+ private static final int UPDATE_UID_PROC_STATE_LOG_MAX_SIZE = 200;
// Memory usage: 20 * size bytes
private static final int COMMIT_UID_STATE_LOG_MAX_SIZE = 200;
// Memory usage: 24 * size bytes
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 21cc172..55d2921 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2126,16 +2126,24 @@
autoHdrOutputTypes = getEnabledAutoHdrTypesLocked();
}
+ int conversionMode = hdrConversionMode.getConversionMode();
+ int preferredHdrType = hdrConversionMode.getPreferredHdrOutputType();
// If the HDR conversion is disabled by an app through WindowManager.LayoutParams, then
// set HDR conversion mode to HDR_CONVERSION_PASSTHROUGH.
if (mOverrideHdrConversionMode == null) {
- mSystemPreferredHdrOutputType =
- mInjector.setHdrConversionMode(hdrConversionMode.getConversionMode(),
- hdrConversionMode.getPreferredHdrOutputType(), autoHdrOutputTypes);
+ // HDR_CONVERSION_FORCE with HDR_TYPE_INVALID is used to represent forcing SDR type.
+ // But, internally SDR is selected by using passthrough mode.
+ if (conversionMode == HdrConversionMode.HDR_CONVERSION_FORCE
+ && preferredHdrType == Display.HdrCapabilities.HDR_TYPE_INVALID) {
+ conversionMode = HdrConversionMode.HDR_CONVERSION_PASSTHROUGH;
+ }
} else {
- mInjector.setHdrConversionMode(mOverrideHdrConversionMode.getConversionMode(),
- mOverrideHdrConversionMode.getPreferredHdrOutputType(), null);
+ conversionMode = mOverrideHdrConversionMode.getConversionMode();
+ preferredHdrType = mOverrideHdrConversionMode.getPreferredHdrOutputType();
+ autoHdrOutputTypes = null;
}
+ mSystemPreferredHdrOutputType = mInjector.setHdrConversionMode(
+ conversionMode, preferredHdrType, autoHdrOutputTypes);
}
}
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 364d53b..eda15ae 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -285,7 +285,7 @@
mUniqueIndex = uniqueIndex;
mIsDisplayOn = surface != null;
mDisplayIdToMirror = virtualDisplayConfig.getDisplayIdToMirror();
- mIsWindowManagerMirroring = virtualDisplayConfig.isWindowManagerMirroring();
+ mIsWindowManagerMirroring = virtualDisplayConfig.isWindowManagerMirroringEnabled();
}
@Override
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index e0253fc..088740e 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -150,6 +150,10 @@
@Override
public void onInputDeviceAdded(int deviceId) {
onInputDeviceChanged(deviceId);
+ if (useNewSettingsUi()) {
+ // Force native callback to set up keyboard layout overlay for newly added keyboards
+ reloadKeyboardLayouts();
+ }
}
@Override
@@ -283,7 +287,8 @@
public KeyboardLayout[] getKeyboardLayoutsForInputDevice(
final InputDeviceIdentifier identifier) {
if (useNewSettingsUi()) {
- return new KeyboardLayout[0];
+ // Provide all supported keyboard layouts since Ime info is not provided
+ return getKeyboardLayouts();
}
final String[] enabledLayoutDescriptors =
getEnabledKeyboardLayoutsForInputDevice(identifier);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 4f28432..cc41207 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -3101,6 +3101,16 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, printWriter)) return;
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ dumpInternal(printWriter);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private void dumpInternal(PrintWriter printWriter) {
IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
pw.println("Current lock settings service state:");
diff --git a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
new file mode 100644
index 0000000..182aa6f
--- /dev/null
+++ b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
+import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO;
+import static android.media.MediaRoute2Info.FEATURE_LOCAL_PLAYBACK;
+import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
+import static android.media.MediaRoute2Info.TYPE_DOCK;
+import static android.media.MediaRoute2Info.TYPE_HDMI;
+import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
+import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
+import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.AudioRoutesInfo;
+import android.media.IAudioRoutesObserver;
+import android.media.IAudioService;
+import android.media.MediaRoute2Info;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+
+
+/* package */ final class AudioPoliciesDeviceRouteController implements DeviceRouteController {
+
+ private static final String TAG = "APDeviceRoutesController";
+
+ private static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE";
+
+ @NonNull
+ private final Context mContext;
+ @NonNull
+ private final AudioManager mAudioManager;
+ @NonNull
+ private final IAudioService mAudioService;
+
+ @NonNull
+ private final OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
+ @NonNull
+ private final AudioRoutesObserver mAudioRoutesObserver = new AudioRoutesObserver();
+
+ private int mDeviceVolume;
+
+ @NonNull
+ private MediaRoute2Info mDeviceRoute;
+ @Nullable
+ private MediaRoute2Info mSelectedRoute;
+
+ @VisibleForTesting
+ /* package */ AudioPoliciesDeviceRouteController(@NonNull Context context,
+ @NonNull AudioManager audioManager,
+ @NonNull IAudioService audioService,
+ @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) {
+ Objects.requireNonNull(context);
+ Objects.requireNonNull(audioManager);
+ Objects.requireNonNull(audioService);
+ Objects.requireNonNull(onDeviceRouteChangedListener);
+
+ mContext = context;
+ mOnDeviceRouteChangedListener = onDeviceRouteChangedListener;
+
+ mAudioManager = audioManager;
+ mAudioService = audioService;
+
+ AudioRoutesInfo newAudioRoutes = null;
+ try {
+ newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Cannot connect to audio service to start listen to routes", e);
+ }
+
+ mDeviceRoute = createRouteFromAudioInfo(newAudioRoutes);
+ }
+
+ @Override
+ public synchronized boolean selectRoute(@Nullable Integer type) {
+ if (type == null) {
+ mSelectedRoute = null;
+ return true;
+ }
+
+ if (!isDeviceRouteType(type)) {
+ return false;
+ }
+
+ mSelectedRoute = createRouteFromAudioInfo(type);
+ return true;
+ }
+
+ @Override
+ @NonNull
+ public synchronized MediaRoute2Info getDeviceRoute() {
+ if (mSelectedRoute != null) {
+ return mSelectedRoute;
+ }
+ return mDeviceRoute;
+ }
+
+ @Override
+ public synchronized boolean updateVolume(int volume) {
+ if (mDeviceVolume == volume) {
+ return false;
+ }
+
+ mDeviceVolume = volume;
+
+ if (mSelectedRoute != null) {
+ mSelectedRoute = new MediaRoute2Info.Builder(mSelectedRoute)
+ .setVolume(volume)
+ .build();
+ }
+
+ mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute)
+ .setVolume(volume)
+ .build();
+
+ return true;
+ }
+
+ @NonNull
+ private MediaRoute2Info createRouteFromAudioInfo(@Nullable AudioRoutesInfo newRoutes) {
+ int type = TYPE_BUILTIN_SPEAKER;
+
+ if (newRoutes != null) {
+ if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) {
+ type = TYPE_WIRED_HEADPHONES;
+ } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) {
+ type = TYPE_WIRED_HEADSET;
+ } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
+ type = TYPE_DOCK;
+ } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) {
+ type = TYPE_HDMI;
+ } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) {
+ type = TYPE_USB_DEVICE;
+ }
+ }
+
+ return createRouteFromAudioInfo(type);
+ }
+
+ @NonNull
+ private MediaRoute2Info createRouteFromAudioInfo(@MediaRoute2Info.Type int type) {
+ int name = R.string.default_audio_route_name;
+
+ switch (type) {
+ case TYPE_WIRED_HEADPHONES:
+ case TYPE_WIRED_HEADSET:
+ name = R.string.default_audio_route_name_headphones;
+ break;
+ case TYPE_DOCK:
+ name = R.string.default_audio_route_name_dock_speakers;
+ break;
+ case TYPE_HDMI:
+ name = R.string.default_audio_route_name_external_device;
+ break;
+ case TYPE_USB_DEVICE:
+ name = R.string.default_audio_route_name_usb;
+ break;
+ }
+
+ synchronized (this) {
+ return new MediaRoute2Info.Builder(
+ DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString())
+ .setVolumeHandling(mAudioManager.isVolumeFixed()
+ ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
+ : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
+ .setVolume(mDeviceVolume)
+ .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
+ .setType(type)
+ .addFeature(FEATURE_LIVE_AUDIO)
+ .addFeature(FEATURE_LIVE_VIDEO)
+ .addFeature(FEATURE_LOCAL_PLAYBACK)
+ .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
+ .build();
+ }
+ }
+
+ /**
+ * Checks if the given type is a device route.
+ *
+ * <p>Device route means a route which is either built-in or wired to the current device.
+ *
+ * @param type specifies the type of the device.
+ * @return {@code true} if the device is wired or built-in and {@code false} otherwise.
+ */
+ private boolean isDeviceRouteType(@MediaRoute2Info.Type int type) {
+ switch (type) {
+ case TYPE_BUILTIN_SPEAKER:
+ case TYPE_WIRED_HEADPHONES:
+ case TYPE_WIRED_HEADSET:
+ case TYPE_DOCK:
+ case TYPE_HDMI:
+ case TYPE_USB_DEVICE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private class AudioRoutesObserver extends IAudioRoutesObserver.Stub {
+
+ @Override
+ public void dispatchAudioRoutesChanged(AudioRoutesInfo newAudioRoutes) {
+ boolean isDeviceRouteChanged;
+ MediaRoute2Info deviceRoute = createRouteFromAudioInfo(newAudioRoutes);
+
+ synchronized (AudioPoliciesDeviceRouteController.this) {
+ mDeviceRoute = deviceRoute;
+ isDeviceRouteChanged = mSelectedRoute == null;
+ }
+
+ if (isDeviceRouteChanged) {
+ mOnDeviceRouteChangedListener.onDeviceRouteChanged(deviceRoute);
+ }
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/media/DeviceRouteController.java b/services/core/java/com/android/server/media/DeviceRouteController.java
index 8bd6416..d7893ee 100644
--- a/services/core/java/com/android/server/media/DeviceRouteController.java
+++ b/services/core/java/com/android/server/media/DeviceRouteController.java
@@ -16,32 +16,14 @@
package com.android.server.media;
-import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
-import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO;
-import static android.media.MediaRoute2Info.FEATURE_LOCAL_PLAYBACK;
-import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
-import static android.media.MediaRoute2Info.TYPE_DOCK;
-import static android.media.MediaRoute2Info.TYPE_HDMI;
-import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
-import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
-import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.media.AudioManager;
-import android.media.AudioRoutesInfo;
import android.media.IAudioRoutesObserver;
import android.media.IAudioService;
import android.media.MediaRoute2Info;
-import android.os.RemoteException;
import android.os.ServiceManager;
-import android.util.Slog;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.Objects;
/**
* Controls device routes.
@@ -49,145 +31,65 @@
* <p>A device route is a system wired route, for example, built-in speaker, wired
* headsets and headphones, dock, hdmi, or usb devices.
*
- * <p>Thread safe.
- *
* @see SystemMediaRoute2Provider
*/
-/* package */ final class DeviceRouteController {
+/* package */ interface DeviceRouteController {
- private static final String TAG = "WiredRoutesController";
-
- private static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE";
-
- @NonNull
- private final Context mContext;
- @NonNull
- private final AudioManager mAudioManager;
- @NonNull
- private final IAudioService mAudioService;
-
- @NonNull
- private final OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
- @NonNull
- private final AudioRoutesObserver mAudioRoutesObserver = new AudioRoutesObserver();
-
- private int mDeviceVolume;
- private MediaRoute2Info mDeviceRoute;
-
+ /**
+ * Returns a new instance of {@link DeviceRouteController}.
+ */
/* package */ static DeviceRouteController createInstance(@NonNull Context context,
@NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) {
AudioManager audioManager = context.getSystemService(AudioManager.class);
IAudioService audioService = IAudioService.Stub.asInterface(
ServiceManager.getService(Context.AUDIO_SERVICE));
- return new DeviceRouteController(context,
+ return new LegacyDeviceRouteController(context,
audioManager,
audioService,
onDeviceRouteChangedListener);
}
- @VisibleForTesting
- /* package */ DeviceRouteController(@NonNull Context context,
- @NonNull AudioManager audioManager,
- @NonNull IAudioService audioService,
- @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) {
- Objects.requireNonNull(context);
- Objects.requireNonNull(audioManager);
- Objects.requireNonNull(audioService);
- Objects.requireNonNull(onDeviceRouteChangedListener);
+ /**
+ * Select the route with the given built-in or wired {@link MediaRoute2Info.Type}.
+ *
+ * <p>If the type is {@code null} then unselects the route and falls back to the default device
+ * route observed from
+ * {@link com.android.server.audio.AudioService#startWatchingRoutes(IAudioRoutesObserver)}.
+ *
+ * @param type device type. May be {@code null} to unselect currently selected route.
+ * @return whether the selection succeeds. If the selection fails the state of the controller
+ * remains intact.
+ */
+ boolean selectRoute(@Nullable @MediaRoute2Info.Type Integer type);
- mContext = context;
- mOnDeviceRouteChangedListener = onDeviceRouteChangedListener;
-
- mAudioManager = audioManager;
- mAudioService = audioService;
-
- AudioRoutesInfo newAudioRoutes = null;
- try {
- newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
- } catch (RemoteException e) {
- Slog.w(TAG, "Cannot connect to audio service to start listen to routes", e);
- }
-
- mDeviceRoute = createRouteFromAudioInfo(newAudioRoutes);
- }
-
+ /**
+ * Returns currently selected device (built-in or wired) route.
+ *
+ * @return non-null device route.
+ */
@NonNull
- /* package */ synchronized MediaRoute2Info getDeviceRoute() {
- return mDeviceRoute;
- }
+ MediaRoute2Info getDeviceRoute();
- /* package */ synchronized boolean updateVolume(int volume) {
- if (mDeviceVolume == volume) {
- return false;
- }
+ /**
+ * Updates device route volume.
+ *
+ * @param volume specifies a volume for the device route or 0 for unknown.
+ * @return {@code true} if updated successfully and {@code false} otherwise.
+ */
+ boolean updateVolume(int volume);
- mDeviceVolume = volume;
- mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute)
- .setVolume(volume)
- .build();
+ /**
+ * Interface for receiving events when device route has changed.
+ */
+ interface OnDeviceRouteChangedListener {
- return true;
- }
-
- private MediaRoute2Info createRouteFromAudioInfo(@Nullable AudioRoutesInfo newRoutes) {
- int name = R.string.default_audio_route_name;
- int type = TYPE_BUILTIN_SPEAKER;
-
- if (newRoutes != null) {
- if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) {
- type = TYPE_WIRED_HEADPHONES;
- name = com.android.internal.R.string.default_audio_route_name_headphones;
- } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) {
- type = TYPE_WIRED_HEADSET;
- name = com.android.internal.R.string.default_audio_route_name_headphones;
- } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
- type = TYPE_DOCK;
- name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
- } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) {
- type = TYPE_HDMI;
- name = com.android.internal.R.string.default_audio_route_name_external_device;
- } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) {
- type = TYPE_USB_DEVICE;
- name = com.android.internal.R.string.default_audio_route_name_usb;
- }
- }
-
- synchronized (this) {
- return new MediaRoute2Info.Builder(
- DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString())
- .setVolumeHandling(mAudioManager.isVolumeFixed()
- ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
- : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
- .setVolume(mDeviceVolume)
- .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
- .setType(type)
- .addFeature(FEATURE_LIVE_AUDIO)
- .addFeature(FEATURE_LIVE_VIDEO)
- .addFeature(FEATURE_LOCAL_PLAYBACK)
- .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
- .build();
- }
- }
-
- private void notifyDeviceRouteUpdate(@NonNull MediaRoute2Info deviceRoute) {
- mOnDeviceRouteChangedListener.onDeviceRouteChanged(deviceRoute);
- }
-
- /* package */ interface OnDeviceRouteChangedListener {
+ /**
+ * Called when device route has changed.
+ *
+ * @param deviceRoute non-null device route.
+ */
void onDeviceRouteChanged(@NonNull MediaRoute2Info deviceRoute);
}
- private class AudioRoutesObserver extends IAudioRoutesObserver.Stub {
-
- @Override
- public void dispatchAudioRoutesChanged(AudioRoutesInfo newAudioRoutes) {
- MediaRoute2Info deviceRoute = createRouteFromAudioInfo(newAudioRoutes);
- synchronized (DeviceRouteController.this) {
- mDeviceRoute = deviceRoute;
- }
- notifyDeviceRouteUpdate(deviceRoute);
- }
- }
-
}
diff --git a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
new file mode 100644
index 0000000..971d11f
--- /dev/null
+++ b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
+import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO;
+import static android.media.MediaRoute2Info.FEATURE_LOCAL_PLAYBACK;
+import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
+import static android.media.MediaRoute2Info.TYPE_DOCK;
+import static android.media.MediaRoute2Info.TYPE_HDMI;
+import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
+import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
+import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.AudioRoutesInfo;
+import android.media.IAudioRoutesObserver;
+import android.media.IAudioService;
+import android.media.MediaRoute2Info;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+
+/**
+ * Controls device routes.
+ *
+ * <p>A device route is a system wired route, for example, built-in speaker, wired
+ * headsets and headphones, dock, hdmi, or usb devices.
+ *
+ * <p>Thread safe.
+ *
+ * @see SystemMediaRoute2Provider
+ */
+/* package */ final class LegacyDeviceRouteController implements DeviceRouteController {
+
+ private static final String TAG = "LDeviceRouteController";
+
+ private static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE";
+
+ @NonNull
+ private final Context mContext;
+ @NonNull
+ private final AudioManager mAudioManager;
+ @NonNull
+ private final IAudioService mAudioService;
+
+ @NonNull
+ private final OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
+ @NonNull
+ private final AudioRoutesObserver mAudioRoutesObserver = new AudioRoutesObserver();
+
+ private int mDeviceVolume;
+ private MediaRoute2Info mDeviceRoute;
+
+ @VisibleForTesting
+ /* package */ LegacyDeviceRouteController(@NonNull Context context,
+ @NonNull AudioManager audioManager,
+ @NonNull IAudioService audioService,
+ @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) {
+ Objects.requireNonNull(context);
+ Objects.requireNonNull(audioManager);
+ Objects.requireNonNull(audioService);
+ Objects.requireNonNull(onDeviceRouteChangedListener);
+
+ mContext = context;
+ mOnDeviceRouteChangedListener = onDeviceRouteChangedListener;
+
+ mAudioManager = audioManager;
+ mAudioService = audioService;
+
+ AudioRoutesInfo newAudioRoutes = null;
+ try {
+ newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Cannot connect to audio service to start listen to routes", e);
+ }
+
+ mDeviceRoute = createRouteFromAudioInfo(newAudioRoutes);
+ }
+
+ @Override
+ public boolean selectRoute(@Nullable Integer type) {
+ // No-op as the controller does not support selection from the outside of the class.
+ return false;
+ }
+
+ @Override
+ @NonNull
+ public synchronized MediaRoute2Info getDeviceRoute() {
+ return mDeviceRoute;
+ }
+
+ @Override
+ public synchronized boolean updateVolume(int volume) {
+ if (mDeviceVolume == volume) {
+ return false;
+ }
+
+ mDeviceVolume = volume;
+ mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute)
+ .setVolume(volume)
+ .build();
+
+ return true;
+ }
+
+ private MediaRoute2Info createRouteFromAudioInfo(@Nullable AudioRoutesInfo newRoutes) {
+ int name = R.string.default_audio_route_name;
+ int type = TYPE_BUILTIN_SPEAKER;
+
+ if (newRoutes != null) {
+ if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) {
+ type = TYPE_WIRED_HEADPHONES;
+ name = com.android.internal.R.string.default_audio_route_name_headphones;
+ } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) {
+ type = TYPE_WIRED_HEADSET;
+ name = com.android.internal.R.string.default_audio_route_name_headphones;
+ } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
+ type = TYPE_DOCK;
+ name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
+ } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) {
+ type = TYPE_HDMI;
+ name = com.android.internal.R.string.default_audio_route_name_external_device;
+ } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) {
+ type = TYPE_USB_DEVICE;
+ name = com.android.internal.R.string.default_audio_route_name_usb;
+ }
+ }
+
+ synchronized (this) {
+ return new MediaRoute2Info.Builder(
+ DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString())
+ .setVolumeHandling(mAudioManager.isVolumeFixed()
+ ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
+ : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
+ .setVolume(mDeviceVolume)
+ .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
+ .setType(type)
+ .addFeature(FEATURE_LIVE_AUDIO)
+ .addFeature(FEATURE_LIVE_VIDEO)
+ .addFeature(FEATURE_LOCAL_PLAYBACK)
+ .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
+ .build();
+ }
+ }
+
+ private void notifyDeviceRouteUpdate(@NonNull MediaRoute2Info deviceRoute) {
+ mOnDeviceRouteChangedListener.onDeviceRouteChanged(deviceRoute);
+ }
+
+ private class AudioRoutesObserver extends IAudioRoutesObserver.Stub {
+
+ @Override
+ public void dispatchAudioRoutesChanged(AudioRoutesInfo newAudioRoutes) {
+ MediaRoute2Info deviceRoute = createRouteFromAudioInfo(newAudioRoutes);
+ synchronized (LegacyDeviceRouteController.this) {
+ mDeviceRoute = deviceRoute;
+ }
+ notifyDeviceRouteUpdate(deviceRoute);
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 53e841d..73440b7 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -892,6 +892,7 @@
return allowedComponents;
}
+ @NonNull
protected List<String> getAllowedPackages(int userId) {
final List<String> allowedPackages = new ArrayList<>();
synchronized (mApproved) {
@@ -1181,25 +1182,6 @@
return installed;
}
- protected Set<String> getAllowedPackages() {
- final Set<String> allowedPackages = new ArraySet<>();
- synchronized (mApproved) {
- for (int k = 0; k < mApproved.size(); k++) {
- ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.valueAt(k);
- for (int i = 0; i < allowedByType.size(); i++) {
- final ArraySet<String> allowed = allowedByType.valueAt(i);
- for (int j = 0; j < allowed.size(); j++) {
- String pkgName = getPackageName(allowed.valueAt(j));
- if (!TextUtils.isEmpty(pkgName)) {
- allowedPackages.add(pkgName);
- }
- }
- }
- }
- }
- return allowedPackages;
- }
-
private void trimApprovedListsAccordingToInstalledServices(int userId) {
synchronized (mApproved) {
final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0d39457..53b03d5 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2709,16 +2709,18 @@
}
private void sendRegisteredOnlyBroadcast(String action) {
- Intent intent = new Intent(action);
- getContext().sendBroadcastAsUser(intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
- UserHandle.ALL, null);
+ int[] userIds = mUmInternal.getProfileIds(mAmi.getCurrentUserId(), true);
+ Intent intent = new Intent(action).addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ for (int userId : userIds) {
+ getContext().sendBroadcastAsUser(intent, UserHandle.of(userId), null);
+ }
// explicitly send the broadcast to all DND packages, even if they aren't currently running
- intent.setFlags(0);
- final Set<String> dndApprovedPackages = mConditionProviders.getAllowedPackages();
- for (String pkg : dndApprovedPackages) {
- intent.setPackage(pkg);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
+ for (int userId : userIds) {
+ for (String pkg : mConditionProviders.getAllowedPackages(userId)) {
+ Intent pkgIntent = new Intent(action).setPackage(pkg).setFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ getContext().sendBroadcastAsUser(pkgIntent, UserHandle.of(userId));
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 9329f06..0d417e4 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -136,9 +136,7 @@
}
/**
- * @param isolated indicates if this object should <em>not</em> connect to
- * the real {@code installd}. All remote calls will be ignored
- * unless you extend this class and intercept them.
+ * @param isolated Make the installer isolated. See {@link isIsolated}.
*/
public Installer(Context context, boolean isolated) {
super(context);
@@ -153,6 +151,15 @@
mWarnIfHeld = warnIfHeld;
}
+ /**
+ * Returns true if the installer is isolated, i.e. if this object should <em>not</em> connect to
+ * the real {@code installd}. All remote calls will be ignored unless you extend this class and
+ * intercept them.
+ */
+ public boolean isIsolated() {
+ return mIsolated;
+ }
+
@Override
public void onStart() {
if (mIsolated) {
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 767c0a7..6a2ddc8 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import static com.android.server.pm.DexOptHelper.useArtService;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
@@ -301,6 +302,15 @@
throws InstallerException {
final StringBuilder builder = new StringBuilder();
+ if (useArtService()) {
+ if ((dexFlags & DEXOPT_SECONDARY_DEX) != 0) {
+ // installd may change the reference profile in place for secondary dex
+ // files, which isn't safe with the lock free approach in ART Service.
+ throw new IllegalArgumentException(
+ "Invalid OTA dexopt call for secondary dex");
+ }
+ }
+
// The current version. For v10, see b/115993344.
builder.append("10 ");
@@ -353,7 +363,6 @@
PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
collectingInstaller, mPackageManagerService.mInstallLock, mContext);
- // TODO(b/251903639): Allow this use of legacy dexopt code even when ART Service is enabled.
try {
optimizer.performDexOpt(pkg, pkgSetting, null /* ISAs */,
null /* CompilerStats.PackageStats */,
@@ -362,9 +371,19 @@
new DexoptOptions(pkg.getPackageName(), compilationReason,
DexoptOptions.DEXOPT_BOOT_COMPLETE));
} catch (LegacyDexoptDisabledException e) {
- throw new RuntimeException(e);
+ // OTA is still allowed to use the legacy dexopt code even when ART Service is enabled.
+ // The installer is isolated and won't call into installd, and the dexopt() method is
+ // overridden to only collect the command above. Hence we shouldn't go into any code
+ // path where this exception is thrown.
+ Slog.wtf(TAG, e);
}
+ // ART Service compat note: These commands are consumed by the otapreopt binary, which uses
+ // the same legacy dexopt code as installd to invoke dex2oat. It provides output path
+ // implementations (see calculate_odex_file_path and create_cache_path in
+ // frameworks/native/cmds/installd/otapreopt.cpp) to write to different odex files than
+ // those used by ART Service in its ordinary operations, so it doesn't interfere with ART
+ // Service even when dalvik.vm.useartservice is true.
return commands;
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 0a90e7a3..8a4080f 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -18,6 +18,7 @@
import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
+import static com.android.server.pm.DexOptHelper.useArtService;
import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
@@ -329,8 +330,22 @@
String profileName = ArtManager.getProfileName(
i == 0 ? null : pkg.getSplitNames()[i - 1]);
- final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary()
- || packageUseInfo.isUsedByOtherApps(path);
+
+ final boolean isUsedByOtherApps;
+ if (options.isDexoptAsSharedLibrary()) {
+ isUsedByOtherApps = true;
+ } else if (useArtService()) {
+ // We get here when collecting dexopt commands in OTA preopt, even when ART Service
+ // is in use. packageUseInfo isn't useful in that case since the legacy dex use
+ // database hasn't been updated. So we'd have to query ART Service instead, but it
+ // doesn't provide that API. Just cop-out and bypass the cloud profile handling.
+ // That means such apps will get preopted wrong, and we'll leave it to a later
+ // background dexopt after reboot instead.
+ isUsedByOtherApps = false;
+ } else {
+ isUsedByOtherApps = packageUseInfo.isUsedByOtherApps(path);
+ }
+
String compilerFilter = getRealCompilerFilter(pkg, options.getCompilerFilter());
// If the app is used by other apps, we must not use the existing profile because it
// may contain user data, unless the profile is newly created on install.
@@ -446,6 +461,14 @@
private boolean prepareCloudProfile(AndroidPackage pkg, String profileName, String path,
@Nullable String dexMetadataPath) throws LegacyDexoptDisabledException {
if (dexMetadataPath != null) {
+ if (mInstaller.isIsolated()) {
+ // If the installer is isolated, the two calls to it below will return immediately,
+ // so this only short-circuits that a bit. We need to do it to avoid the
+ // LegacyDexoptDisabledException getting thrown first, when we get here during OTA
+ // preopt and ART Service is enabled.
+ return true;
+ }
+
try {
// Make sure we don't keep any existing contents.
mInstaller.deleteReferenceProfile(pkg.getPackageName(), profileName);
@@ -879,7 +902,12 @@
private int getDexoptNeeded(String packageName, String path, String isa, String compilerFilter,
String classLoaderContext, int profileAnalysisResult, boolean downgrade,
int dexoptFlags, String oatDir) throws LegacyDexoptDisabledException {
- Installer.checkLegacyDexoptDisabled();
+ // Allow calls from OtaDexoptService even when ART Service is in use. The installer is
+ // isolated in that case so later calls to it won't call into installd anyway.
+ if (!mInstaller.isIsolated()) {
+ Installer.checkLegacyDexoptDisabled();
+ }
+
final boolean shouldBePublic = (dexoptFlags & DEXOPT_PUBLIC) != 0;
final boolean isProfileGuidedFilter = (dexoptFlags & DEXOPT_PROFILE_GUIDED) != 0;
boolean newProfile = profileAnalysisResult == PROFILE_ANALYSIS_OPTIMIZE;
@@ -948,6 +976,8 @@
*/
private int analyseProfiles(AndroidPackage pkg, int uid, String profileName,
String compilerFilter) throws LegacyDexoptDisabledException {
+ Installer.checkLegacyDexoptDisabled();
+
// Check if we are allowed to merge and if the compiler filter is profile guided.
if (!isProfileGuidedCompilerFilter(compilerFilter)) {
return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 45dc49d..37a59da 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2977,7 +2977,7 @@
}
break;
case KeyEvent.KEYCODE_H:
- if (down && event.isMetaPressed()) {
+ if (event.isMetaPressed()) {
return handleHomeShortcuts(displayId, focusedToken, event);
}
break;
@@ -3018,11 +3018,6 @@
if (down && event.isMetaPressed() && event.isCtrlPressed() && repeatCount == 0) {
enterStageSplitFromRunningApp(true /* leftOrTop */);
return key_consumed;
- } else if (!down && event.isMetaPressed()) {
- boolean backKeyHandled = backKeyPress();
- if (backKeyHandled) {
- return key_consumed;
- }
}
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
@@ -3031,14 +3026,6 @@
return key_consumed;
}
break;
- case KeyEvent.KEYCODE_GRAVE:
- if (!down && event.isMetaPressed()) {
- boolean backKeyHandled = backKeyPress();
- if (backKeyHandled) {
- return key_consumed;
- }
- }
- break;
case KeyEvent.KEYCODE_SLASH:
if (down && repeatCount == 0 && event.isMetaPressed() && !keyguardOn) {
toggleKeyboardShortcutsMenu(event.getDeviceId());
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index f11c864..bc23020 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -12688,8 +12688,8 @@
energy = info.getControllerEnergyUsed();
if (!info.getUidTraffic().isEmpty()) {
for (UidTraffic traffic : info.getUidTraffic()) {
- uidRxBytes.incrementValue(traffic.getUid(), traffic.getRxBytes());
- uidTxBytes.incrementValue(traffic.getUid(), traffic.getTxBytes());
+ uidRxBytes.put(traffic.getUid(), traffic.getRxBytes());
+ uidTxBytes.put(traffic.getUid(), traffic.getTxBytes());
}
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 7a73359..c9eef38 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -923,6 +923,8 @@
if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId,
null /* options */);
+ mWindowManagerInternal.setWallpaperShowWhenLocked(
+ mToken, (wallpaper.mWhich & FLAG_LOCK) != 0);
final DisplayData wpdData =
mWallpaperDisplayHelper.getDisplayDataOrCreate(mDisplayId);
try {
@@ -1415,12 +1417,13 @@
try {
if (connector.mEngine != null) {
connector.mEngine.setWallpaperFlags(which);
+ mWindowManagerInternal.setWallpaperShowWhenLocked(
+ connector.mToken, (which & FLAG_LOCK) != 0);
}
} catch (RemoteException e) {
Slog.e(TAG, "Failed to update wallpaper engine flags", e);
}
- }
- );
+ });
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index f73c68a..939cf1a 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -888,8 +888,11 @@
} else {
int animAttr = mapOpenCloseTransitTypes(transit, enter);
if (animAttr != 0) {
- a = loadCustomActivityAnimation(animAttr, enter, container);
- if (a == null) {
+ final CustomAppTransition customAppTransition =
+ getCustomAppTransition(animAttr, container);
+ if (customAppTransition != null) {
+ a = loadCustomActivityAnimation(customAppTransition, enter, container);
+ } else {
if (canCustomizeAppTransition) {
a = loadAnimationAttr(lp, animAttr, transit);
} else {
@@ -911,7 +914,7 @@
return a;
}
- Animation loadCustomActivityAnimation(int animAttr, boolean enter, WindowContainer container) {
+ CustomAppTransition getCustomAppTransition(int animAttr, WindowContainer container) {
ActivityRecord customAnimationSource = container.asActivityRecord();
if (customAnimationSource == null) {
return null;
@@ -927,31 +930,28 @@
return null;
}
}
- final CustomAppTransition custom;
switch (animAttr) {
case WindowAnimation_activityOpenEnterAnimation:
case WindowAnimation_activityOpenExitAnimation:
- custom = customAnimationSource.getCustomAnimation(true /* open */);
- break;
+ return customAnimationSource.getCustomAnimation(true /* open */);
case WindowAnimation_activityCloseEnterAnimation:
case WindowAnimation_activityCloseExitAnimation:
- custom = customAnimationSource.getCustomAnimation(false /* open */);
- break;
- default:
- return null;
- }
- if (custom != null) {
- final Animation a = mTransitionAnimation.loadAppTransitionAnimation(
- customAnimationSource.packageName, enter
- ? custom.mEnterAnim : custom.mExitAnim);
- if (a != null && custom.mBackgroundColor != 0) {
- a.setBackdropColor(custom.mBackgroundColor);
- a.setShowBackdrop(true);
- }
- return a;
+ return customAnimationSource.getCustomAnimation(false /* open */);
}
return null;
}
+ private Animation loadCustomActivityAnimation(@NonNull CustomAppTransition custom,
+ boolean enter, WindowContainer container) {
+ final ActivityRecord customAnimationSource = container.asActivityRecord();
+ final Animation a = mTransitionAnimation.loadAppTransitionAnimation(
+ customAnimationSource.packageName, enter
+ ? custom.mEnterAnim : custom.mExitAnim);
+ if (a != null && custom.mBackgroundColor != 0) {
+ a.setBackdropColor(custom.mBackgroundColor);
+ a.setShowBackdrop(true);
+ }
+ return a;
+ }
int getAppRootTaskClipMode() {
return mNextAppTransitionRequests.contains(TRANSIT_RELAUNCH)
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index 0dc6e0f..5c9c813 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -364,6 +364,7 @@
/** Hides the window immediately until it is drawn in new rotation. */
void hideImmediately(WindowToken windowToken) {
+ if (isTargetToken(windowToken)) return;
final boolean original = mHideImmediately;
mHideImmediately = true;
final Operation op = new Operation(Operation.ACTION_FADE);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 947edde..87f5703b 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4158,13 +4158,13 @@
/** @see WindowManagerInternal#onToggleImeRequested */
void onShowImeRequested() {
- if (mImeLayeringTarget == null || mInputMethodWindow == null) {
+ if (mInputMethodWindow == null) {
return;
}
// If IME window will be shown on the rotated activity, share the transformed state to
// IME window so it can compute rotated frame with rotated configuration.
- if (mImeLayeringTarget.mToken.isFixedRotationTransforming()) {
- mInputMethodWindow.mToken.linkFixedRotationTransform(mImeLayeringTarget.mToken);
+ if (mFixedRotationLaunchingApp != null) {
+ mInputMethodWindow.mToken.linkFixedRotationTransform(mFixedRotationLaunchingApp);
// Hide the window until the rotation is done to avoid intermediate artifacts if the
// parent surface of IME container is changed.
if (mAsyncRotationController != null) {
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 210a7d9..c1f2b2b 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -580,6 +580,13 @@
// Notification shade has control anyways, no reason to force anything.
return focusedWin;
}
+ if (focusedWin != null) {
+ final InsetsSourceProvider provider = focusedWin.getControllableInsetProvider();
+ if (provider != null && provider.getSource().getType() == Type.navigationBars()) {
+ // Navigation bar has control if it is focused.
+ return focusedWin;
+ }
+ }
if (mPolicy.isForceShowNavigationBarEnabled() && focusedWin != null
&& focusedWin.getActivityType() == ACTIVITY_TYPE_STANDARD) {
// When "force show navigation bar" is enabled, it means both force visible is true, and
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index e147219..b3b56f2 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -33,7 +33,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_PIP;
-import static android.view.WindowManager.TRANSIT_SLEEP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_WAKE;
@@ -2330,7 +2329,6 @@
}
void applySleepTokens(boolean applyToRootTasks) {
- boolean builtSleepTransition = false;
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
// Set the sleeping state of the display.
final DisplayContent display = getChildAt(displayNdx);
@@ -2340,30 +2338,6 @@
}
display.setIsSleeping(displayShouldSleep);
- if (display.mTransitionController.isShellTransitionsEnabled() && !builtSleepTransition
- // Only care if there are actual sleep tokens.
- && displayShouldSleep && !display.mAllSleepTokens.isEmpty()) {
- builtSleepTransition = true;
- // We don't actually care about collecting anything here. We really just want
- // this as a signal to the transition-player.
- final Transition transition = new Transition(TRANSIT_SLEEP, 0 /* flags */,
- display.mTransitionController, mWmService.mSyncEngine);
- final Runnable sendSleepTransition = () -> {
- display.mTransitionController.requestStartTransition(transition,
- null /* trigger */, null /* remote */, null /* display */);
- // Force playing immediately so that unrelated ops can't be collected.
- transition.playNow();
- };
- if (display.mTransitionController.isCollecting()) {
- mWmService.mSyncEngine.queueSyncSet(
- () -> display.mTransitionController.moveToCollecting(transition),
- sendSleepTransition);
- } else {
- display.mTransitionController.moveToCollecting(transition);
- sendSleepTransition.run();
- }
- }
-
if (!applyToRootTasks) {
continue;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 879323e..a30ab11 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -218,9 +218,6 @@
final TransitionController.Logger mLogger = new TransitionController.Logger();
- /** Whether this transition was forced to play early (eg for a SLEEP signal). */
- private boolean mForcePlaying = false;
-
/**
* {@code false} if this transition runs purely in WMCore (meaning Shell is completely unaware
* of it). Currently, this happens before the display is ready since nothing can be seen yet.
@@ -1010,25 +1007,6 @@
mController.dispatchLegacyAppTransitionCancelled();
}
- /** Immediately moves this to playing even if it isn't started yet. */
- void playNow() {
- if (mState == STATE_PLAYING) return;
- ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Force Playing Transition: %d",
- mSyncId);
- mForcePlaying = true;
- setAllReady();
- if (mState == STATE_COLLECTING) {
- start();
- }
- // Don't wait for actual surface-placement. We don't want anything else collected in this
- // transition.
- mSyncEngine.onSurfacePlacement();
- }
-
- boolean isForcePlaying() {
- return mForcePlaying;
- }
-
void setRemoteTransition(RemoteTransition remoteTransition) {
mRemoteTransition = remoteTransition;
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 16541c1..2b848d5 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -37,6 +37,7 @@
import static com.android.server.wm.WindowManagerService.H.WALLPAPER_DRAW_PENDING_TIMEOUT;
import android.annotation.Nullable;
+import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
@@ -55,6 +56,7 @@
import android.view.animation.Animation;
import android.window.ScreenCapture;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
@@ -72,7 +74,7 @@
class WallpaperController {
private static final String TAG = TAG_WITH_CLASS_NAME ? "WallpaperController" : TAG_WM;
private WindowManagerService mService;
- private final DisplayContent mDisplayContent;
+ private DisplayContent mDisplayContent;
private final ArrayList<WallpaperWindowToken> mWallpaperTokens = new ArrayList<>();
@@ -120,9 +122,19 @@
private boolean mShouldOffsetWallpaperCenter;
+ final boolean mEnableSeparateLockScreenEngine;
+
private final ToBooleanFunction<WindowState> mFindWallpaperTargetFunction = w -> {
if ((w.mAttrs.type == TYPE_WALLPAPER)) {
if (mFindResults.topWallpaper == null || mFindResults.resetTopWallpaper) {
+ WallpaperWindowToken token = w.mToken.asWallpaperToken();
+ if (token == null) {
+ Slog.w(TAG, "Window " + w + " has wallpaper type but not wallpaper token");
+ return false;
+ }
+ if (!token.canShowWhenLocked() && mDisplayContent.isKeyguardLocked()) {
+ return false;
+ }
mFindResults.setTopWallpaper(w);
mFindResults.resetTopWallpaper = false;
}
@@ -249,11 +261,14 @@
WallpaperController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
mDisplayContent = displayContent;
- mMaxWallpaperScale = service.mContext.getResources()
- .getFloat(com.android.internal.R.dimen.config_wallpaperMaxScale);
- mShouldOffsetWallpaperCenter = service.mContext.getResources()
- .getBoolean(
+ Resources resources = service.mContext.getResources();
+ mMaxWallpaperScale =
+ resources.getFloat(com.android.internal.R.dimen.config_wallpaperMaxScale);
+ mShouldOffsetWallpaperCenter =
+ resources.getBoolean(
com.android.internal.R.bool.config_offsetWallpaperToCenterOfLargestDisplay);
+ mEnableSeparateLockScreenEngine =
+ resources.getBoolean(R.bool.config_independentLockscreenLiveWallpaper);
}
void resetLargestDisplay(Display display) {
@@ -753,10 +768,10 @@
result.setWallpaperTarget(wallpaperTarget);
}
- private void updateWallpaperTokens(boolean visible) {
+ private void updateWallpaperTokens(boolean visibility, boolean locked) {
for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
- token.updateWallpaperWindows(visible);
+ token.updateWallpaperWindows(visibility && (!locked || token.canShowWhenLocked()));
}
}
@@ -794,7 +809,13 @@
}
}
- updateWallpaperTokens(visible);
+ // Keep both wallpapers visible unless the keyguard is locked (then hide private wp)
+ updateWallpaperTokens(visible, mDisplayContent.isKeyguardLocked());
+
+ if (DEBUG_WALLPAPER) {
+ Slog.v(TAG, "adjustWallpaperWindows: wallpaper visibility " + visible
+ + ", lock visibility " + mDisplayContent.isKeyguardLocked());
+ }
if (visible && mLastFrozen != mFindResults.isWallpaperTargetForLetterbox) {
mLastFrozen = mFindResults.isWallpaperTargetForLetterbox;
@@ -896,7 +917,6 @@
mWallpaperTokens.remove(token);
}
-
@VisibleForTesting
boolean canScreenshotWallpaper() {
return canScreenshotWallpaper(getTopVisibleWallpaper());
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 8708f73..17ab551 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -76,14 +76,18 @@
return;
}
mShowWhenLocked = showWhenLocked;
+ if (mDisplayContent.mWallpaperController.mEnableSeparateLockScreenEngine) {
+ // Move the window token to the front (private) or back (showWhenLocked). This is
+ // possible
+ // because the DisplayArea underneath TaskDisplayArea only contains TYPE_WALLPAPER
+ // windows.
+ final int position = showWhenLocked ? POSITION_BOTTOM : POSITION_TOP;
- // Move the window token to the front (private) or back (showWhenLocked). This is possible
- // because the DisplayArea underneath TaskDisplayArea only contains TYPE_WALLPAPER windows.
- final int position = showWhenLocked ? POSITION_BOTTOM : POSITION_TOP;
-
- // Note: Moving all the way to the front or back breaks ordering based on addition times.
- // We should never have more than one non-animating token of each type.
- getParent().positionChildAt(position, this /* child */, false /*includingParents */);
+ // Note: Moving all the way to the front or back breaks ordering based on addition
+ // times.
+ // We should never have more than one non-animating token of each type.
+ getParent().positionChildAt(position, this /* child */, false /*includingParents */);
+ }
}
boolean canShowWhenLocked() {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 495d7ce4..b9cb59a 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -317,7 +317,7 @@
}
transition = mTransitionController.createTransition(type);
}
- if (!transition.isCollecting() && !transition.isForcePlaying()) {
+ if (!transition.isCollecting()) {
Slog.e(TAG, "Trying to start a transition that isn't collecting. This probably"
+ " means Shell took too long to respond to a request. WM State may be"
+ " incorrect now, please file a bug");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 87e87b9..09f7fb6 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2342,9 +2342,11 @@
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
- mTempConfiguration.setTo(getConfiguration());
+ // Get from super to avoid using the updated global config from the override method.
+ final Configuration selfConfiguration = super.getConfiguration();
+ mTempConfiguration.setTo(selfConfiguration);
super.onConfigurationChanged(newParentConfig);
- final int diff = getConfiguration().diff(mTempConfiguration);
+ final int diff = selfConfiguration.diff(mTempConfiguration);
if (diff != 0) {
mLastConfigReportedToClient = false;
}
diff --git a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
index 6bf18c2..c8ec7c2 100644
--- a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
@@ -119,7 +119,6 @@
private void respondToClientWithResponseAndFinish() {
Log.i(TAG, "respondToClientWithResponseAndFinish");
if (isSessionCancelled()) {
- // TODO: Differentiate btw cancelled and false
mChosenProviderMetric.setChosenProviderStatus(
MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_SUCCESS);
logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
@@ -144,7 +143,6 @@
private void respondToClientWithErrorAndFinish(String errorType, String errorMsg) {
Log.i(TAG, "respondToClientWithErrorAndFinish");
if (isSessionCancelled()) {
- // TODO: Differentiate btw cancelled and false
logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
finishSession(/*propagateCancellation=*/true);
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index 656e44c..0c1133c 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -143,7 +143,6 @@
return;
}
if (isSessionCancelled()) {
- // TODO: Differentiate btw cancelled and false
logApiCall(ApiName.CREATE_CREDENTIAL, /* apiStatus */
ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
finishSession(/*propagateCancellation=*/true);
@@ -168,7 +167,6 @@
return;
}
if (isSessionCancelled()) {
- // TODO: Differentiate btw cancelled and false
logApiCall(ApiName.CREATE_CREDENTIAL, /* apiStatus */
ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
finishSession(/*propagateCancellation=*/true);
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index ce26c88..13f4b54 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -119,7 +119,6 @@
return;
}
if (isSessionCancelled()) {
- // TODO: Differentiate btw cancelled and false
logApiCall(ApiName.GET_CREDENTIAL, /* apiStatus */
ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
finishSession(/*propagateCancellation=*/true);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e9c23a0..400ee1d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3212,8 +3212,12 @@
private void sendChangedNotification(int userHandle) {
Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ Bundle options = new BroadcastOptions()
+ .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
+ .setDeferUntilActive(true)
+ .toBundle();
mInjector.binderWithCleanCallingIdentity(() ->
- mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle)));
+ mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle), null, options));
}
private void loadSettingsLocked(DevicePolicyData policy, int userHandle) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 850b5b6..edfe95e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2739,6 +2739,14 @@
}
t.traceEnd();
+ t.traceBegin("RegisterLogMteState");
+ try {
+ LogMteState.register(context);
+ } catch (Throwable e) {
+ reportWtf("RegisterLogMteState", e);
+ }
+ t.traceEnd();
+
// Emit any pending system_server WTFs
synchronized (SystemService.class) {
if (sPendingWtfs != null) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index 99da415..8a5d3a6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -558,7 +558,7 @@
// To maximize test coverage, dump current state; we're not worried
// about the actual output, just that we don't crash
- queue.getActive().setDeliveryState(0, BroadcastRecord.DELIVERY_SCHEDULED);
+ queue.getActive().setDeliveryState(0, BroadcastRecord.DELIVERY_SCHEDULED, "Test-driven");
queue.dumpLocked(SystemClock.uptimeMillis(),
new IndentingPrintWriter(new PrintWriter(new ByteArrayOutputStream())));
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 2967c5c..339ccd8 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -130,7 +130,6 @@
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
@@ -283,7 +282,7 @@
return blockedActivities;
}
- private Intent createRestrictedActivityBlockedIntent(List displayCategories,
+ private Intent createRestrictedActivityBlockedIntent(Set<String> displayCategories,
String targetDisplayCategory) {
when(mDisplayManagerInternalMock.createVirtualDisplay(any(), any(), any(), any(),
eq(NONBLOCKED_APP_PACKAGE_NAME))).thenReturn(DISPLAY_ID_1);
@@ -1634,7 +1633,7 @@
@Test
public void nonRestrictedActivityOnRestrictedVirtualDisplay_startBlockedAlertActivity() {
- Intent blockedAppIntent = createRestrictedActivityBlockedIntent(List.of("abc"),
+ Intent blockedAppIntent = createRestrictedActivityBlockedIntent(Set.of("abc"),
/* targetDisplayCategory= */ null);
verify(mContext).startActivityAsUser(argThat(intent ->
intent.filterEquals(blockedAppIntent)), any(), any());
@@ -1642,7 +1641,7 @@
@Test
public void restrictedActivityOnRestrictedVirtualDisplay_doesNotStartBlockedAlertActivity() {
- Intent blockedAppIntent = createRestrictedActivityBlockedIntent(List.of("abc"), "abc");
+ Intent blockedAppIntent = createRestrictedActivityBlockedIntent(Set.of("abc"), "abc");
verify(mContext, never()).startActivityAsUser(argThat(intent ->
intent.filterEquals(blockedAppIntent)), any(), any());
}
@@ -1650,14 +1649,14 @@
@Test
public void restrictedActivityOnNonRestrictedVirtualDisplay_startBlockedAlertActivity() {
Intent blockedAppIntent = createRestrictedActivityBlockedIntent(
- /* displayCategories= */ List.of(), "abc");
+ /* displayCategories= */ Set.of(), "abc");
verify(mContext).startActivityAsUser(argThat(intent ->
intent.filterEquals(blockedAppIntent)), any(), any());
}
@Test
public void restrictedActivityNonMatchingRestrictedVirtualDisplay_startBlockedAlertActivity() {
- Intent blockedAppIntent = createRestrictedActivityBlockedIntent(List.of("abc"), "def");
+ Intent blockedAppIntent = createRestrictedActivityBlockedIntent(Set.of("abc"), "def");
verify(mContext).startActivityAsUser(argThat(intent ->
intent.filterEquals(blockedAppIntent)), any(), any());
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
index d9e4da7..2bfa44e 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
@@ -89,7 +89,7 @@
/* activityBlockedCallback= */ null,
/* secureWindowCallback= */ null,
/* intentListenerCallback= */ null,
- /* displayCategories= */ new ArrayList<>(),
+ /* displayCategories= */ new ArraySet<>(),
/* showTasksInHostDeviceRecents= */ true);
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 1f25da7..aaabb28 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -162,9 +162,9 @@
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
-import org.junit.Ignore;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.internal.util.collections.Sets;
@@ -514,7 +514,9 @@
verify(mContext.spiedContext).sendBroadcastAsUser(
MockUtils.checkIntentAction(
DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
- MockUtils.checkUserHandle(CALLER_USER_HANDLE));
+ MockUtils.checkUserHandle(CALLER_USER_HANDLE),
+ eq(null),
+ any(Bundle.class));
verify(mContext.spiedContext).sendBroadcastAsUser(
MockUtils.checkIntentAction(
DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
@@ -793,7 +795,9 @@
verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
MockUtils.checkIntentAction(
DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
- MockUtils.checkUserHandle(CALLER_USER_HANDLE));
+ MockUtils.checkUserHandle(CALLER_USER_HANDLE),
+ eq(null),
+ any(Bundle.class));
// Remove. No permissions, but same user, so it'll work.
mContext.callerPermissions.clear();
@@ -820,7 +824,9 @@
verify(mContext.spiedContext, times(2)).sendBroadcastAsUser(
MockUtils.checkIntentAction(
DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
- MockUtils.checkUserHandle(CALLER_USER_HANDLE));
+ MockUtils.checkUserHandle(CALLER_USER_HANDLE),
+ eq(null),
+ any(Bundle.class));
// TODO Check other internal calls.
}
@@ -846,7 +852,9 @@
verify(mContext.spiedContext, times(2)).sendBroadcastAsUser(
MockUtils.checkIntentAction(
DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
- MockUtils.checkUserHandle(CALLER_USER_HANDLE));
+ MockUtils.checkUserHandle(CALLER_USER_HANDLE),
+ eq(null),
+ any(Bundle.class));
// Remove. No permissions, but same user, so it'll work.
mContext.callerPermissions.clear();
@@ -874,7 +882,9 @@
verify(mContext.spiedContext, times(3)).sendBroadcastAsUser(
MockUtils.checkIntentAction(
DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
- MockUtils.checkUserHandle(CALLER_USER_HANDLE));
+ MockUtils.checkUserHandle(CALLER_USER_HANDLE),
+ eq(null),
+ any(Bundle.class));
}
/**
@@ -2425,7 +2435,9 @@
verify(mContext.spiedContext, times(2)).sendBroadcastAsUser(
MockUtils.checkIntentAction(
DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
- MockUtils.checkUserHandle(CALLER_USER_HANDLE));
+ MockUtils.checkUserHandle(CALLER_USER_HANDLE),
+ eq(null),
+ any(Bundle.class));
verify(mContext.spiedContext).sendBroadcastAsUser(
MockUtils.checkIntentAction(
DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED),
@@ -5886,7 +5898,9 @@
verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
MockUtils.checkIntentAction(
DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
- MockUtils.checkUserHandle(userHandle));
+ MockUtils.checkUserHandle(userHandle),
+ eq(null),
+ any(Bundle.class));
final Intent intent = new Intent(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED);
intent.setComponent(admin1);
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 7971fd7..de1c219 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -967,7 +967,7 @@
final VirtualDisplayConfig.Builder builder2 = new VirtualDisplayConfig.Builder(
VIRTUAL_DISPLAY_NAME, width, height, dpi)
.setUniqueId(uniqueId2)
- .setWindowManagerMirroring(true);
+ .setWindowManagerMirroringEnabled(true);
final int secondDisplayId = binderService.createVirtualDisplay(builder2.build(),
mMockAppToken2 /* callback */, null /* projection */,
PACKAGE_NAME);
diff --git a/services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt
index b2bfd2b..b660926 100644
--- a/services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt
+++ b/services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt
@@ -256,12 +256,16 @@
@Test
fun testNewUi_getKeyboardLayoutsForInputDevice() {
NewSettingsApiFlag(true).use {
- val keyboardLayouts =
- keyboardLayoutManager.getKeyboardLayoutsForInputDevice(keyboardDevice.identifier)
- assertEquals(
- "New UI: getKeyboardLayoutsForInputDevice API should always return empty array",
- 0,
- keyboardLayouts.size
+ val keyboardLayouts = keyboardLayoutManager.keyboardLayouts
+ assertNotEquals(
+ "New UI: getKeyboardLayoutsForInputDevice API should not return empty array",
+ 0,
+ keyboardLayouts.size
+ )
+ assertTrue(
+ "New UI: getKeyboardLayoutsForInputDevice API should provide English(US) " +
+ "layout",
+ hasLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
)
}
}
diff --git a/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java
new file mode 100644
index 0000000..1e73a45
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.media.AudioManager;
+import android.media.AudioRoutesInfo;
+import android.media.IAudioRoutesObserver;
+import android.media.MediaRoute2Info;
+import android.os.RemoteException;
+
+import com.android.internal.R;
+import com.android.server.audio.AudioService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(JUnit4.class)
+public class AudioPoliciesDeviceRouteControllerTest {
+
+ private static final String ROUTE_NAME_DEFAULT = "default";
+ private static final String ROUTE_NAME_DOCK = "dock";
+ private static final String ROUTE_NAME_HEADPHONES = "headphones";
+
+ private static final int VOLUME_SAMPLE_1 = 25;
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private AudioManager mAudioManager;
+ @Mock
+ private AudioService mAudioService;
+ @Mock
+ private DeviceRouteController.OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
+
+ @Captor
+ private ArgumentCaptor<IAudioRoutesObserver.Stub> mAudioRoutesObserverCaptor;
+
+ private AudioPoliciesDeviceRouteController mController;
+
+ private IAudioRoutesObserver.Stub mAudioRoutesObserver;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mResources.getText(anyInt())).thenReturn(ROUTE_NAME_DEFAULT);
+
+ // Setting built-in speaker as default speaker.
+ AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
+ audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_SPEAKER;
+ when(mAudioService.startWatchingRoutes(mAudioRoutesObserverCaptor.capture()))
+ .thenReturn(audioRoutesInfo);
+
+ mController = new AudioPoliciesDeviceRouteController(
+ mContext, mAudioManager, mAudioService, mOnDeviceRouteChangedListener);
+
+ mAudioRoutesObserver = mAudioRoutesObserverCaptor.getValue();
+ }
+
+ @Test
+ public void getDeviceRoute_noSelectedRoutes_returnsDefaultDevice() {
+ MediaRoute2Info route2Info = mController.getDeviceRoute();
+
+ assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DEFAULT);
+ assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
+ }
+
+ @Test
+ public void getDeviceRoute_audioRouteHasChanged_returnsRouteFromAudioService() {
+ when(mResources.getText(R.string.default_audio_route_name_headphones))
+ .thenReturn(ROUTE_NAME_HEADPHONES);
+
+ AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
+ audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
+ callAudioRoutesObserver(audioRoutesInfo);
+
+ MediaRoute2Info route2Info = mController.getDeviceRoute();
+ assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES);
+ assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
+ }
+
+ @Test
+ public void getDeviceRoute_selectDevice_returnsSelectedRoute() {
+ when(mResources.getText(R.string.default_audio_route_name_dock_speakers))
+ .thenReturn(ROUTE_NAME_DOCK);
+
+ mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
+
+ MediaRoute2Info route2Info = mController.getDeviceRoute();
+ assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DOCK);
+ assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK);
+ }
+
+ @Test
+ public void getDeviceRoute_hasSelectedAndAudioServiceRoutes_returnsSelectedRoute() {
+ when(mResources.getText(R.string.default_audio_route_name_headphones))
+ .thenReturn(ROUTE_NAME_HEADPHONES);
+ when(mResources.getText(R.string.default_audio_route_name_dock_speakers))
+ .thenReturn(ROUTE_NAME_DOCK);
+
+ AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
+ audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
+ callAudioRoutesObserver(audioRoutesInfo);
+
+ mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
+
+ MediaRoute2Info route2Info = mController.getDeviceRoute();
+ assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DOCK);
+ assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK);
+ }
+
+ @Test
+ public void getDeviceRoute_unselectRoute_returnsAudioServiceRoute() {
+ when(mResources.getText(R.string.default_audio_route_name_headphones))
+ .thenReturn(ROUTE_NAME_HEADPHONES);
+ when(mResources.getText(R.string.default_audio_route_name_dock_speakers))
+ .thenReturn(ROUTE_NAME_DOCK);
+
+ mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
+
+ AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
+ audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
+ callAudioRoutesObserver(audioRoutesInfo);
+
+ mController.selectRoute(null);
+
+ MediaRoute2Info route2Info = mController.getDeviceRoute();
+ assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES);
+ assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
+ }
+
+ @Test
+ public void getDeviceRoute_selectRouteFails_returnsAudioServiceRoute() {
+ when(mResources.getText(R.string.default_audio_route_name_headphones))
+ .thenReturn(ROUTE_NAME_HEADPHONES);
+
+ AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
+ audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
+ callAudioRoutesObserver(audioRoutesInfo);
+
+ mController.selectRoute(MediaRoute2Info.TYPE_BLUETOOTH_A2DP);
+
+ MediaRoute2Info route2Info = mController.getDeviceRoute();
+ assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES);
+ assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
+ }
+
+ @Test
+ public void selectRoute_selectWiredRoute_returnsTrue() {
+ assertThat(mController.selectRoute(MediaRoute2Info.TYPE_HDMI)).isTrue();
+ }
+
+ @Test
+ public void selectRoute_selectBluetoothRoute_returnsFalse() {
+ assertThat(mController.selectRoute(MediaRoute2Info.TYPE_BLUETOOTH_A2DP)).isFalse();
+ }
+
+ @Test
+ public void selectRoute_unselectRoute_returnsTrue() {
+ assertThat(mController.selectRoute(null)).isTrue();
+ }
+
+ @Test
+ public void updateVolume_noSelectedRoute_deviceRouteVolumeChanged() {
+ when(mResources.getText(R.string.default_audio_route_name_headphones))
+ .thenReturn(ROUTE_NAME_HEADPHONES);
+
+ AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
+ audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
+ callAudioRoutesObserver(audioRoutesInfo);
+
+ mController.updateVolume(VOLUME_SAMPLE_1);
+
+ MediaRoute2Info route2Info = mController.getDeviceRoute();
+ assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
+ assertThat(route2Info.getVolume()).isEqualTo(VOLUME_SAMPLE_1);
+ }
+
+ @Test
+ public void updateVolume_connectSelectedRouteLater_selectedRouteVolumeChanged() {
+ when(mResources.getText(R.string.default_audio_route_name_headphones))
+ .thenReturn(ROUTE_NAME_HEADPHONES);
+ when(mResources.getText(R.string.default_audio_route_name_dock_speakers))
+ .thenReturn(ROUTE_NAME_DOCK);
+
+ AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
+ audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
+ callAudioRoutesObserver(audioRoutesInfo);
+
+ mController.updateVolume(VOLUME_SAMPLE_1);
+
+ mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
+
+ MediaRoute2Info route2Info = mController.getDeviceRoute();
+ assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK);
+ assertThat(route2Info.getVolume()).isEqualTo(VOLUME_SAMPLE_1);
+ }
+
+ /**
+ * Simulates {@link IAudioRoutesObserver.Stub#dispatchAudioRoutesChanged(AudioRoutesInfo)}
+ * from {@link AudioService}. This happens when there is a wired route change,
+ * like a wired headset being connected.
+ *
+ * @param audioRoutesInfo updated state of connected wired device
+ */
+ private void callAudioRoutesObserver(AudioRoutesInfo audioRoutesInfo) {
+ try {
+ // this is a captured observer implementation
+ // from WiredRoutesController's AudioService#startWatchingRoutes call
+ mAudioRoutesObserver.dispatchAudioRoutesChanged(audioRoutesInfo);
+ } catch (RemoteException exception) {
+ // Should not happen since the object is mocked.
+ assertWithMessage("An unexpected RemoteException happened.").fail();
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/LegacyDeviceRouteControllerTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
rename to services/tests/servicestests/src/com/android/server/media/LegacyDeviceRouteControllerTest.java
index 24ed42c..24e4851 100644
--- a/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/LegacyDeviceRouteControllerTest.java
@@ -50,7 +50,7 @@
import java.util.Collection;
@RunWith(Enclosed.class)
-public class DeviceRouteControllerTest {
+public class LegacyDeviceRouteControllerTest {
private static final String DEFAULT_ROUTE_NAME = "default_route";
private static final String DEFAULT_HEADPHONES_NAME = "headphone";
@@ -97,7 +97,7 @@
// Default route should be initialized even when AudioService returns null.
when(mAudioService.startWatchingRoutes(any())).thenReturn(null);
- DeviceRouteController deviceRouteController = new DeviceRouteController(
+ LegacyDeviceRouteController deviceRouteController = new LegacyDeviceRouteController(
mContext,
mAudioManager,
mAudioService,
@@ -122,7 +122,7 @@
AudioRoutesInfo fakeBluetoothAudioRoute = createFakeBluetoothAudioRoute();
when(mAudioService.startWatchingRoutes(any())).thenReturn(fakeBluetoothAudioRoute);
- DeviceRouteController deviceRouteController = new DeviceRouteController(
+ LegacyDeviceRouteController deviceRouteController = new LegacyDeviceRouteController(
mContext,
mAudioManager,
mAudioService,
@@ -236,7 +236,7 @@
when(mResources.getText(mExpectedRouteNameResource))
.thenReturn(mExpectedRouteNameValue);
- DeviceRouteController deviceRouteController = new DeviceRouteController(
+ LegacyDeviceRouteController deviceRouteController = new LegacyDeviceRouteController(
mContext,
mAudioManager,
mAudioService,
@@ -269,7 +269,7 @@
@Captor
private ArgumentCaptor<IAudioRoutesObserver.Stub> mAudioRoutesObserverCaptor;
- private DeviceRouteController mDeviceRouteController;
+ private LegacyDeviceRouteController mDeviceRouteController;
private IAudioRoutesObserver.Stub mAudioRoutesObserver;
@Before
@@ -287,7 +287,7 @@
when(mAudioService.startWatchingRoutes(mAudioRoutesObserverCaptor.capture()))
.thenReturn(audioRoutesInfo);
- mDeviceRouteController = new DeviceRouteController(
+ mDeviceRouteController = new LegacyDeviceRouteController(
mContext,
mAudioManager,
mAudioService,
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java
index c6a7fbc..ee4b839 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java
@@ -572,41 +572,14 @@
mBatteryStatsImpl.noteBluetoothScanStoppedFromSourceLocked(ws, true, 9000, 9000);
mBatteryStatsImpl.noteBluetoothScanResultsFromSourceLocked(ws, 42, 9000, 9000);
-
-
- final Parcel uidTrafficParcel1 = Parcel.obtain();
- final Parcel uidTrafficParcel2 = Parcel.obtain();
-
- uidTrafficParcel1.writeInt(10042);
- uidTrafficParcel1.writeLong(3000);
- uidTrafficParcel1.writeLong(4000);
- uidTrafficParcel1.setDataPosition(0);
- uidTrafficParcel2.writeInt(10043);
- uidTrafficParcel2.writeLong(5000);
- uidTrafficParcel2.writeLong(8000);
- uidTrafficParcel2.setDataPosition(0);
-
- List<UidTraffic> uidTrafficList = ImmutableList.of(
- UidTraffic.CREATOR.createFromParcel(uidTrafficParcel1),
- UidTraffic.CREATOR.createFromParcel(uidTrafficParcel2));
-
- final Parcel btActivityEnergyInfoParcel = Parcel.obtain();
- btActivityEnergyInfoParcel.writeLong(1000);
- btActivityEnergyInfoParcel.writeInt(
- BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE);
- btActivityEnergyInfoParcel.writeLong(9000);
- btActivityEnergyInfoParcel.writeLong(8000);
- btActivityEnergyInfoParcel.writeLong(12000);
- btActivityEnergyInfoParcel.writeLong(0);
- btActivityEnergyInfoParcel.writeTypedList(uidTrafficList);
- btActivityEnergyInfoParcel.setDataPosition(0);
-
- BluetoothActivityEnergyInfo info = BluetoothActivityEnergyInfo.CREATOR
- .createFromParcel(btActivityEnergyInfoParcel);
-
- uidTrafficParcel1.recycle();
- uidTrafficParcel2.recycle();
- btActivityEnergyInfoParcel.recycle();
+ BluetoothActivityEnergyInfo info = createBluetoothActivityEnergyInfo(
+ /* timestamp= */ 1000,
+ /* controllerTxTimeMs= */ 9000,
+ /* controllerRxTimeMs= */ 8000,
+ /* controllerIdleTimeMs= */ 12000,
+ /* controllerEnergyUsed= */ 0,
+ createUidTraffic(/* appUid= */ 10042, /* rxBytes= */ 3000, /* txBytes= */ 4000),
+ createUidTraffic(/* appUid= */ 10043, /* rxBytes= */ 5000, /* txBytes= */ 8000));
mBatteryStatsImpl.updateBluetoothStateLocked(info, -1, 1000, 1000);
@@ -622,4 +595,105 @@
assertThat(uidStats.rxTimeMs).isEqualTo(7375); // Some scan time is treated as RX
assertThat(uidStats.txTimeMs).isEqualTo(7666); // Some scan time is treated as TX
}
+
+ /** A regression test for b/266128651 */
+ @Test
+ public void testGetNetworkActivityBytes_multipleUpdates() {
+ when(mPowerProfile.getAveragePower(
+ PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE)).thenReturn(3.0);
+ mBatteryStatsImpl.setOnBatteryInternal(true);
+ mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+
+ BluetoothActivityEnergyInfo info1 = createBluetoothActivityEnergyInfo(
+ /* timestamp= */ 10000,
+ /* controllerTxTimeMs= */ 9000,
+ /* controllerRxTimeMs= */ 8000,
+ /* controllerIdleTimeMs= */ 2000,
+ /* controllerEnergyUsed= */ 0,
+ createUidTraffic(/* appUid= */ 10042, /* rxBytes= */ 3000, /* txBytes= */ 4000),
+ createUidTraffic(/* appUid= */ 10043, /* rxBytes= */ 5000, /* txBytes= */ 8000));
+
+ mBatteryStatsImpl.updateBluetoothStateLocked(info1, -1, 1000, 1000);
+
+ long totalRx1 = mBatteryStatsImpl.getNetworkActivityBytes(
+ BatteryStats.NETWORK_BT_RX_DATA, BatteryStats.STATS_SINCE_CHARGED);
+ long totalTx1 = mBatteryStatsImpl.getNetworkActivityBytes(
+ BatteryStats.NETWORK_BT_TX_DATA, BatteryStats.STATS_SINCE_CHARGED);
+
+ assertThat(totalRx1).isEqualTo(8000); // 3000 + 5000
+ assertThat(totalTx1).isEqualTo(12000); // 4000 + 8000
+
+ BluetoothActivityEnergyInfo info2 = createBluetoothActivityEnergyInfo(
+ /* timestamp= */ 20000,
+ /* controllerTxTimeMs= */ 19000,
+ /* controllerRxTimeMs= */ 18000,
+ /* controllerIdleTimeMs= */ 3000,
+ /* controllerEnergyUsed= */ 0,
+ createUidTraffic(/* appUid= */ 10043, /* rxBytes= */ 6000, /* txBytes= */ 9500),
+ createUidTraffic(/* appUid= */ 10044, /* rxBytes= */ 7000, /* txBytes= */ 9000));
+
+ mBatteryStatsImpl.updateBluetoothStateLocked(info2, -1, 2000, 2000);
+
+ long totalRx2 = mBatteryStatsImpl.getNetworkActivityBytes(
+ BatteryStats.NETWORK_BT_RX_DATA, BatteryStats.STATS_SINCE_CHARGED);
+ long totalTx2 = mBatteryStatsImpl.getNetworkActivityBytes(
+ BatteryStats.NETWORK_BT_TX_DATA, BatteryStats.STATS_SINCE_CHARGED);
+
+ assertThat(totalRx2).isEqualTo(16000); // 3000 + 6000 (updated) + 7000 (new)
+ assertThat(totalTx2).isEqualTo(22500); // 4000 + 9500 (updated) + 9000 (new)
+
+ BluetoothActivityEnergyInfo info3 = createBluetoothActivityEnergyInfo(
+ /* timestamp= */ 30000,
+ /* controllerTxTimeMs= */ 20000,
+ /* controllerRxTimeMs= */ 20000,
+ /* controllerIdleTimeMs= */ 4000,
+ /* controllerEnergyUsed= */ 0,
+ createUidTraffic(/* appUid= */ 10043, /* rxBytes= */ 7000, /* txBytes= */ 9900),
+ createUidTraffic(/* appUid= */ 10044, /* rxBytes= */ 8000, /* txBytes= */ 10000));
+
+ mBatteryStatsImpl.updateBluetoothStateLocked(info3, -1, 2000, 2000);
+
+ long totalRx3 = mBatteryStatsImpl.getNetworkActivityBytes(
+ BatteryStats.NETWORK_BT_RX_DATA, BatteryStats.STATS_SINCE_CHARGED);
+ long totalTx3 = mBatteryStatsImpl.getNetworkActivityBytes(
+ BatteryStats.NETWORK_BT_TX_DATA, BatteryStats.STATS_SINCE_CHARGED);
+
+ assertThat(totalRx3).isEqualTo(18000); // 3000 + 7000 (updated) + 8000 (updated)
+ assertThat(totalTx3).isEqualTo(23900); // 4000 + 9900 (updated) + 10000 (updated)
+ }
+
+ private UidTraffic createUidTraffic(int appUid, long rxBytes, long txBytes) {
+ final Parcel parcel = Parcel.obtain();
+ parcel.writeInt(appUid); // mAppUid
+ parcel.writeLong(rxBytes); // mRxBytes
+ parcel.writeLong(txBytes); // mTxBytes
+ parcel.setDataPosition(0);
+ UidTraffic uidTraffic = UidTraffic.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+ return uidTraffic;
+ }
+
+ private BluetoothActivityEnergyInfo createBluetoothActivityEnergyInfo(
+ long timestamp,
+ long controllerTxTimeMs,
+ long controllerRxTimeMs,
+ long controllerIdleTimeMs,
+ long controllerEnergyUsed,
+ UidTraffic... uidTraffic) {
+ Parcel parcel = Parcel.obtain();
+ parcel.writeLong(timestamp); // mTimestamp
+ parcel.writeInt(
+ BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE); // mBluetoothStackState
+ parcel.writeLong(controllerTxTimeMs); // mControllerTxTimeMs;
+ parcel.writeLong(controllerRxTimeMs); // mControllerRxTimeMs;
+ parcel.writeLong(controllerIdleTimeMs); // mControllerIdleTimeMs;
+ parcel.writeLong(controllerEnergyUsed); // mControllerEnergyUsed;
+ parcel.writeTypedList(ImmutableList.copyOf(uidTraffic)); // mUidTraffic
+ parcel.setDataPosition(0);
+
+ BluetoothActivityEnergyInfo info =
+ BluetoothActivityEnergyInfo.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+ return info;
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 6f37e60..ce07621 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -24,6 +24,8 @@
import static com.android.server.notification.ManagedServices.APPROVAL_BY_COMPONENT;
import static com.android.server.notification.ManagedServices.APPROVAL_BY_PACKAGE;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -1201,28 +1203,11 @@
mIpm, approvalLevel);
loadXml(service);
- List<String> allowedPackagesForUser0 = new ArrayList<>();
- allowedPackagesForUser0.add("this.is.a.package.name");
- allowedPackagesForUser0.add("another.package");
- allowedPackagesForUser0.add("secondary");
-
- List<String> actual = service.getAllowedPackages(0);
- assertEquals(3, actual.size());
- for (String pkg : allowedPackagesForUser0) {
- assertTrue(actual.contains(pkg));
- }
-
- List<String> allowedPackagesForUser10 = new ArrayList<>();
- allowedPackagesForUser10.add("this.is.another.package");
- allowedPackagesForUser10.add("package");
- allowedPackagesForUser10.add("this.is.another.package");
- allowedPackagesForUser10.add("component");
-
- actual = service.getAllowedPackages(10);
- assertEquals(4, actual.size());
- for (String pkg : allowedPackagesForUser10) {
- assertTrue(actual.contains(pkg));
- }
+ assertThat(service.getAllowedPackages(0)).containsExactly("this.is.a.package.name",
+ "another.package", "secondary");
+ assertThat(service.getAllowedPackages(10)).containsExactly("this.is.another.package",
+ "package", "this.is.another.package", "component");
+ assertThat(service.getAllowedPackages(999)).isEmpty();
}
}
@@ -1263,31 +1248,6 @@
}
@Test
- public void testGetAllowedPackages() throws Exception {
- ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
- mIpm, APPROVAL_BY_COMPONENT);
- loadXml(service);
- service.mApprovalLevel = APPROVAL_BY_PACKAGE;
- loadXml(service);
-
- List<String> allowedPackages = new ArrayList<>();
- allowedPackages.add("this.is.a.package.name");
- allowedPackages.add("another.package");
- allowedPackages.add("secondary");
- allowedPackages.add("this.is.another.package");
- allowedPackages.add("package");
- allowedPackages.add("component");
- allowedPackages.add("bananas!");
- allowedPackages.add("non.user.set.package");
-
- Set<String> actual = service.getAllowedPackages();
- assertEquals(allowedPackages.size(), actual.size());
- for (String pkg : allowedPackages) {
- assertTrue(actual.contains(pkg));
- }
- }
-
- @Test
public void testOnUserRemoved() throws Exception {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
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 f08d0f5..354420f 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -29,6 +29,7 @@
import static android.app.Notification.FLAG_NO_CLEAR;
import static android.app.Notification.FLAG_ONGOING_EVENT;
import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
+import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
@@ -50,7 +51,6 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
-
import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.app.PendingIntent.FLAG_MUTABLE;
import static android.app.PendingIntent.FLAG_ONE_SHOT;
@@ -237,6 +237,7 @@
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
+import com.google.android.collect.Lists;
import com.google.common.collect.ImmutableList;
import org.junit.After;
@@ -245,10 +246,13 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
+import org.mockito.ArgumentMatchers;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.io.BufferedInputStream;
@@ -440,6 +444,7 @@
mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager);
mContext.addMockSystemService(NotificationManager.class, mMockNm);
+ doNothing().when(mContext).sendBroadcastAsUser(any(), any());
doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
setDpmAppOppsExemptFromDismissal(false);
@@ -7828,6 +7833,75 @@
}
@Test
+ public void onZenModeChanged_sendsBroadcasts() throws Exception {
+ when(mAmi.getCurrentUserId()).thenReturn(100);
+ when(mUmInternal.getProfileIds(eq(100), anyBoolean())).thenReturn(new int[]{100, 101, 102});
+ when(mConditionProviders.getAllowedPackages(anyInt())).then(new Answer<List<String>>() {
+ @Override
+ public List<String> answer(InvocationOnMock invocation) {
+ int userId = invocation.getArgument(0);
+ switch (userId) {
+ case 100:
+ return Lists.newArrayList("a", "b", "c");
+ case 101:
+ return Lists.newArrayList();
+ case 102:
+ return Lists.newArrayList("b");
+ default:
+ throw new IllegalArgumentException(
+ "Why would you ask for packages of userId " + userId + "?");
+ }
+ }
+ });
+
+ mService.getBinderService().setZenMode(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS, null,
+ "testing!");
+ waitForIdle();
+
+ InOrder inOrder = inOrder(mContext);
+ // Verify broadcasts for registered receivers
+ inOrder.verify(mContext).sendBroadcastAsUser(eqIntent(
+ new Intent(ACTION_INTERRUPTION_FILTER_CHANGED).setFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY)), eq(UserHandle.of(100)), eq(null));
+ inOrder.verify(mContext).sendBroadcastAsUser(eqIntent(
+ new Intent(ACTION_INTERRUPTION_FILTER_CHANGED).setFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY)), eq(UserHandle.of(101)), eq(null));
+ inOrder.verify(mContext).sendBroadcastAsUser(eqIntent(
+ new Intent(ACTION_INTERRUPTION_FILTER_CHANGED).setFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY)), eq(UserHandle.of(102)), eq(null));
+
+ // Verify broadcast for packages that manage DND.
+ inOrder.verify(mContext).sendBroadcastAsUser(eqIntent(new Intent(
+ ACTION_INTERRUPTION_FILTER_CHANGED).setPackage("a").setFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)), eq(UserHandle.of(100)));
+ inOrder.verify(mContext).sendBroadcastAsUser(eqIntent(new Intent(
+ ACTION_INTERRUPTION_FILTER_CHANGED).setPackage("b").setFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)), eq(UserHandle.of(100)));
+ inOrder.verify(mContext).sendBroadcastAsUser(eqIntent(new Intent(
+ ACTION_INTERRUPTION_FILTER_CHANGED).setPackage("c").setFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)), eq(UserHandle.of(100)));
+ inOrder.verify(mContext).sendBroadcastAsUser(eqIntent(new Intent(
+ ACTION_INTERRUPTION_FILTER_CHANGED).setPackage("b").setFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)), eq(UserHandle.of(102)));
+ }
+
+ private static Intent eqIntent(Intent wanted) {
+ return ArgumentMatchers.argThat(
+ new ArgumentMatcher<Intent>() {
+ @Override
+ public boolean matches(Intent argument) {
+ return wanted.filterEquals(argument)
+ && wanted.getFlags() == argument.getFlags();
+ }
+
+ @Override
+ public String toString() {
+ return wanted.toString();
+ }
+ });
+ }
+
+ @Test
public void testAreNotificationsEnabledForPackage() throws Exception {
mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
mUid);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index aaeae23..3379beb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1620,7 +1620,6 @@
// If the rotated activity requests to show IME, the IME window should use the
// transformation from activity to lay out in the same orientation.
- mDisplayContent.setImeLayeringTarget(mAppWindow);
LocalServices.getService(WindowManagerInternal.class).onToggleImeRequested(true /* show */,
app.token, app.token, mDisplayContent.mDisplayId);
assertTrue(asyncRotationController.isTargetToken(mImeWindow.mToken));
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
index 573b3b69..d2708ad 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
@@ -42,6 +42,7 @@
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.Until;
import org.junit.After;
@@ -74,7 +75,7 @@
// This is for AOSP System UI for phones. When testing customized System UI, please modify here.
private static final BySelector REPLY_SEND_BUTTON_SELECTOR =
- By.res("com.android.systemui", "remote_input_send");
+ By.res("com.android.systemui", "remote_input_send").enabled(true);
@Rule
public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule();
@@ -119,7 +120,15 @@
mUiDevice.pressKeyCode(KeyEvent.KEYCODE_A);
mUiDevice.pressKeyCode(KeyEvent.KEYCODE_B);
mUiDevice.pressKeyCode(KeyEvent.KEYCODE_C);
- mUiDevice.wait(Until.findObject(REPLY_SEND_BUTTON_SELECTOR.enabled(true)), TIMEOUT).click();
+ UiObject2 sendButton = mUiDevice.wait(
+ Until.findObject(REPLY_SEND_BUTTON_SELECTOR), TIMEOUT);
+ if (sendButton == null) {
+ // If the screen is too small, sendButton may be hidden by IME.
+ // Dismiss IME and try again.
+ mUiDevice.pressBack();
+ sendButton = mUiDevice.wait(Until.findObject(REPLY_SEND_BUTTON_SELECTOR), TIMEOUT);
+ }
+ sendButton.click();
// Verify that IME is gone.
assertThat(mUiDevice.wait(Until.gone(By.pkg(getImePackage(mContext))), TIMEOUT)).isTrue();
}