Merge "Add cpp20 to validatekeymaps" into udc-dev
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobService.java b/apex/jobscheduler/framework/java/android/app/job/JobService.java
index f48e078..3b5f11b 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobService.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobService.java
@@ -265,7 +265,8 @@
* @see JobInfo.Builder#setRequiredNetworkType(int)
*/
public void onNetworkChanged(@NonNull JobParameters params) {
- Log.w(TAG, "onNetworkChanged() not implemented. Must override in a subclass.");
+ Log.w(TAG, "onNetworkChanged() not implemented in " + getClass().getName()
+ + ". Must override in a subclass.");
}
/**
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 2d9a99c..9bbc4a6 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -538,6 +538,7 @@
public class DevicePolicyManager {
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void acknowledgeNewUserDisclaimer();
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void calculateHasIncompatibleAccounts();
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void clearOrganizationId();
method @RequiresPermission(android.Manifest.permission.CLEAR_FREEZE_PERIOD) public void clearSystemUpdatePolicyFreezePeriodRecord();
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs();
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 95e446d..021f932 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -45,6 +45,7 @@
import android.os.WorkSource;
import android.util.ArraySet;
import android.util.Pair;
+import android.util.StatsEvent;
import com.android.internal.os.TimeoutRecord;
@@ -1217,4 +1218,10 @@
*/
public abstract void notifyMediaProjectionEvent(int uid, @NonNull IBinder projectionToken,
@MediaProjectionTokenEvent int event);
+
+ /**
+ * @return The stats event for the cached apps high watermark since last pull.
+ */
+ @NonNull
+ public abstract StatsEvent getCachedAppsHighWatermarkStats(int atomTag, boolean resetAfterPull);
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 3312294..9e59ee4 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2547,7 +2547,7 @@
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
new AppOpInfo.Builder(OP_TURN_SCREEN_ON, OPSTR_TURN_SCREEN_ON, "TURN_SCREEN_ON")
.setPermission(Manifest.permission.TURN_SCREEN_ON)
- .setDefaultMode(AppOpsManager.MODE_ERRORED).build(),
+ .setDefaultMode(AppOpsManager.MODE_DEFAULT).build(),
new AppOpInfo.Builder(OP_GET_ACCOUNTS, OPSTR_GET_ACCOUNTS, "GET_ACCOUNTS")
.setPermission(Manifest.permission.GET_ACCOUNTS)
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 63da0a2..d375760 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2916,6 +2916,14 @@
}
}
+ if (isStyle(CallStyle.class) & extras != null) {
+ Person callPerson = extras.getParcelable(EXTRA_CALL_PERSON);
+ if (callPerson != null) {
+ visitor.accept(callPerson.getIconUri());
+ }
+ visitIconUri(visitor, extras.getParcelable(EXTRA_VERIFICATION_ICON));
+ }
+
if (mBubbleMetadata != null) {
visitIconUri(visitor, mBubbleMetadata.getIcon());
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4d3338b..a8a2ad1 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16825,6 +16825,23 @@
}
/**
+ * Recalculate the incompatible accounts cache.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ public void calculateHasIncompatibleAccounts() {
+ if (mService != null) {
+ try {
+ mService.calculateHasIncompatibleAccounts();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* @return {@code true} if bypassing the device policy management role qualification is allowed
* with the current state of the device.
*
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 9b0b18a..9795cab 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -608,4 +608,6 @@
boolean isDeviceFinanced(String callerPackageName);
String getFinancedDeviceKioskRoleHolder(String callerPackageName);
+
+ void calculateHasIncompatibleAccounts();
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 154068e..307f306 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -16,6 +16,7 @@
package android.content;
+import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY;
import static android.content.ContentProvider.maybeAddUserId;
import android.Manifest;
@@ -12348,7 +12349,9 @@
null, new String[] { getType() },
new ClipData.Item(text, htmlText, null, stream));
setClipData(clipData);
- addFlags(FLAG_GRANT_READ_URI_PERMISSION);
+ if (stream != null) {
+ addFlags(FLAG_GRANT_READ_URI_PERMISSION);
+ }
return true;
}
} catch (ClassCastException e) {
@@ -12387,7 +12390,9 @@
}
setClipData(clipData);
- addFlags(FLAG_GRANT_READ_URI_PERMISSION);
+ if (streams != null) {
+ addFlags(FLAG_GRANT_READ_URI_PERMISSION);
+ }
return true;
}
} catch (ClassCastException e) {
@@ -12463,4 +12468,19 @@
public boolean isDocument() {
return (mFlags & FLAG_ACTIVITY_NEW_DOCUMENT) == FLAG_ACTIVITY_NEW_DOCUMENT;
}
+
+ /** @hide */
+ public boolean isSandboxActivity(@NonNull Context context) {
+ if (mAction != null && mAction.equals(ACTION_START_SANDBOXED_ACTIVITY)) {
+ return true;
+ }
+ final String sandboxPackageName = context.getPackageManager().getSdkSandboxPackageName();
+ if (mPackage != null && mPackage.equals(sandboxPackageName)) {
+ return true;
+ }
+ if (mComponent != null && mComponent.getPackageName().equals(sandboxPackageName)) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 048289f..960d10a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2628,6 +2628,15 @@
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
+ // STOPSHIP: hack for the pre-release SDK
+ if (platformSdkCodenames.length == 0
+ && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+ targetCode)) {
+ Slog.w(TAG, "Package requires development platform " + targetCode
+ + ", returning current version " + Build.VERSION.SDK_INT);
+ return Build.VERSION.SDK_INT;
+ }
+
// Otherwise, we're looking at an incompatible pre-release SDK.
if (platformSdkCodenames.length > 0) {
outError[0] = "Requires development platform " + targetCode
@@ -2699,6 +2708,15 @@
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
+ // STOPSHIP: hack for the pre-release SDK
+ if (platformSdkCodenames.length == 0
+ && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+ minCode)) {
+ Slog.w(TAG, "Package requires min development platform " + minCode
+ + ", returning current version " + Build.VERSION.SDK_INT);
+ return Build.VERSION.SDK_INT;
+ }
+
// Otherwise, we're looking at an incompatible pre-release SDK.
if (platformSdkCodenames.length > 0) {
outError[0] = "Requires development platform " + minCode
diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
index 3e1c5bb..8cc4cdb 100644
--- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
@@ -316,6 +316,15 @@
return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
}
+ // STOPSHIP: hack for the pre-release SDK
+ if (platformSdkCodenames.length == 0
+ && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+ minCode)) {
+ Slog.w(TAG, "Parsed package requires min development platform " + minCode
+ + ", returning current version " + Build.VERSION.SDK_INT);
+ return input.success(Build.VERSION.SDK_INT);
+ }
+
// Otherwise, we're looking at an incompatible pre-release SDK.
if (platformSdkCodenames.length > 0) {
return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
@@ -368,19 +377,27 @@
return input.success(targetVers);
}
+ // If it's a pre-release SDK and the codename matches this platform, it
+ // definitely targets this SDK.
+ if (matchTargetCode(platformSdkCodenames, targetCode)) {
+ return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
+ }
+
+ // STOPSHIP: hack for the pre-release SDK
+ if (platformSdkCodenames.length == 0
+ && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+ targetCode)) {
+ Slog.w(TAG, "Parsed package requires development platform " + targetCode
+ + ", returning current version " + Build.VERSION.SDK_INT);
+ return input.success(Build.VERSION.SDK_INT);
+ }
+
try {
if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) {
return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
}
} catch (IllegalArgumentException e) {
- // isAtMost() throws it when encountering an older SDK codename
- return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, e.getMessage());
- }
-
- // If it's a pre-release SDK and the codename matches this platform, it
- // definitely targets this SDK.
- if (matchTargetCode(platformSdkCodenames, targetCode)) {
- return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
+ return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, "Bad package SDK");
}
// Otherwise, we're looking at an incompatible pre-release SDK.
diff --git a/core/java/android/content/res/FontScaleConverterFactory.java b/core/java/android/content/res/FontScaleConverterFactory.java
index 6b09c30..5eb6526 100644
--- a/core/java/android/content/res/FontScaleConverterFactory.java
+++ b/core/java/android/content/res/FontScaleConverterFactory.java
@@ -34,6 +34,8 @@
@VisibleForTesting
static final SparseArray<FontScaleConverter> LOOKUP_TABLES = new SparseArray<>();
+ private static float sMinScaleBeforeCurvesApplied = 1.05f;
+
static {
// These were generated by frameworks/base/tools/fonts/font-scaling-array-generator.js and
// manually tweaked for optimum readability.
@@ -82,11 +84,30 @@
new float[] { 16f, 20f, 24f, 26f, 30f, 34f, 36f, 38f, 100})
);
+ sMinScaleBeforeCurvesApplied = getScaleFromKey(LOOKUP_TABLES.keyAt(0)) - 0.02f;
+ if (sMinScaleBeforeCurvesApplied <= 1.0f) {
+ throw new IllegalStateException(
+ "You should only apply non-linear scaling to font scales > 1"
+ );
+ }
}
private FontScaleConverterFactory() {}
/**
+ * Returns true if non-linear font scaling curves would be in effect for the given scale, false
+ * if the scaling would follow a linear curve or for no scaling.
+ *
+ * <p>Example usage:
+ * <code>isNonLinearFontScalingActive(getResources().getConfiguration().fontScale)</code>
+ *
+ * @hide
+ */
+ public static boolean isNonLinearFontScalingActive(float fontScale) {
+ return fontScale >= sMinScaleBeforeCurvesApplied;
+ }
+
+ /**
* Finds a matching FontScaleConverter for the given fontScale factor.
*
* @param fontScale the scale factor, usually from {@link Configuration#fontScale}.
@@ -97,10 +118,7 @@
*/
@Nullable
public static FontScaleConverter forScale(float fontScale) {
- if (fontScale <= 1) {
- // We don't need non-linear curves for shrinking text or for 100%.
- // Also, fontScale==0 should not have a curve either.
- // And ignore negative font scales; that's just silly.
+ if (!isNonLinearFontScalingActive(fontScale)) {
return null;
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 72a3f6c..b453304 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -53,12 +53,16 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import libcore.util.EmptyArray;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.Executor;
+import java.util.function.Predicate;
/**
@@ -86,9 +90,6 @@
@GuardedBy("mLock")
private final WeakDisplayCache mDisplayCache = new WeakDisplayCache();
- @GuardedBy("mLock")
- private final ArrayList<Display> mTempDisplays = new ArrayList<Display>();
-
/**
* Broadcast receiver that indicates when the Wifi display status changes.
* <p>
@@ -627,9 +628,7 @@
* @return The display object, or null if there is no valid display with the given id.
*/
public Display getDisplay(int displayId) {
- synchronized (mLock) {
- return getOrCreateDisplayLocked(displayId, false /*assumeValid*/);
- }
+ return getOrCreateDisplay(displayId, false /*assumeValid*/);
}
/**
@@ -661,75 +660,67 @@
boolean includeDisabled = (category != null
&& category.equals(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED));
final int[] displayIds = mGlobal.getDisplayIds(includeDisabled);
- synchronized (mLock) {
- try {
- if (DISPLAY_CATEGORY_PRESENTATION.equals(category)) {
- addDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI,
- Display.FLAG_PRESENTATION);
- addDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_EXTERNAL,
- Display.FLAG_PRESENTATION);
- addDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY,
- Display.FLAG_PRESENTATION);
- addDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_VIRTUAL,
- Display.FLAG_PRESENTATION);
- addDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_INTERNAL,
- Display.FLAG_PRESENTATION);
- } else if (DISPLAY_CATEGORY_REAR.equals(category)) {
- addDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_INTERNAL,
- Display.FLAG_REAR);
- } else if (category == null
- || DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED.equals(category)) {
- addAllDisplaysLocked(mTempDisplays, displayIds);
- }
- return mTempDisplays.toArray(new Display[mTempDisplays.size()]);
- } finally {
- mTempDisplays.clear();
- }
+ if (DISPLAY_CATEGORY_PRESENTATION.equals(category)) {
+ return getDisplays(displayIds, DisplayManager::isPresentationDisplay);
+ } else if (DISPLAY_CATEGORY_REAR.equals(category)) {
+ return getDisplays(displayIds, DisplayManager::isRearDisplay);
+ } else if (category == null || DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED.equals(category)) {
+ return getDisplays(displayIds, Objects::nonNull);
}
+ return (Display[]) EmptyArray.OBJECT;
}
- @GuardedBy("mLock")
- private void addAllDisplaysLocked(ArrayList<Display> displays, int[] displayIds) {
- for (int i = 0; i < displayIds.length; i++) {
- Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/);
- if (display != null) {
- displays.add(display);
- }
- }
- }
-
- @GuardedBy("mLock")
- private void addDisplaysLocked(
- ArrayList<Display> displays, int[] displayIds, int matchType, int flagMask) {
+ private Display[] getDisplays(int[] displayIds, Predicate<Display> predicate) {
+ ArrayList<Display> tmpDisplays = new ArrayList<>();
for (int displayId : displayIds) {
- if (displayId == DEFAULT_DISPLAY) {
- continue;
+ Display display = getOrCreateDisplay(displayId, /*assumeValid=*/true);
+ if (predicate.test(display)) {
+ tmpDisplays.add(display);
}
+ }
+ return tmpDisplays.toArray(new Display[tmpDisplays.size()]);
+ }
- Display display = getOrCreateDisplayLocked(displayId, /* assumeValid= */ true);
- if (display != null
- && (display.getFlags() & flagMask) == flagMask
- && display.getType() == matchType) {
- displays.add(display);
- }
+ private static boolean isPresentationDisplay(@Nullable Display display) {
+ if (display == null || (display.getDisplayId() == DEFAULT_DISPLAY)
+ || (display.getFlags() & Display.FLAG_PRESENTATION) == 0) {
+ return false;
+ }
+ switch (display.getType()) {
+ case Display.TYPE_INTERNAL:
+ case Display.TYPE_EXTERNAL:
+ case Display.TYPE_WIFI:
+ case Display.TYPE_OVERLAY:
+ case Display.TYPE_VIRTUAL:
+ return true;
+ default:
+ return false;
}
}
- @GuardedBy("mLock")
- private Display getOrCreateDisplayLocked(int displayId, boolean assumeValid) {
- Display display = mDisplayCache.get(displayId);
- if (display == null) {
- // TODO: We cannot currently provide any override configurations for metrics on displays
- // other than the display the context is associated with.
- final Resources resources = mContext.getDisplayId() == displayId
- ? mContext.getResources() : null;
+ private static boolean isRearDisplay(@Nullable Display display) {
+ return display != null && display.getDisplayId() != DEFAULT_DISPLAY
+ && display.getType() == Display.TYPE_INTERNAL
+ && (display.getFlags() & Display.FLAG_REAR) != 0;
+ }
- display = mGlobal.getCompatibleDisplay(displayId, resources);
- if (display != null) {
- mDisplayCache.put(display);
+ private Display getOrCreateDisplay(int displayId, boolean assumeValid) {
+ Display display;
+ synchronized (mLock) {
+ display = mDisplayCache.get(displayId);
+ if (display == null) {
+ // TODO: We cannot currently provide any override configurations for metrics on
+ // displays other than the display the context is associated with.
+ final Resources resources = mContext.getDisplayId() == displayId
+ ? mContext.getResources() : null;
+
+ display = mGlobal.getCompatibleDisplay(displayId, resources);
+ if (display != null) {
+ mDisplayCache.put(display);
+ }
+ } else if (!assumeValid && !display.isValid()) {
+ display = null;
}
- } else if (!assumeValid && !display.isValid()) {
- display = null;
}
return display;
}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 55b20e1..e0af913 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -342,6 +342,14 @@
return;
}
+ if (getEnrolledFaces(userId).size()
+ >= mContext.getResources().getInteger(R.integer.config_faceMaxTemplatesPerUser)) {
+ callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
+ getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */));
+ return;
+ }
+
if (mService != null) {
try {
mEnrollmentCallback = callback;
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 2fec02f..a0cceae 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -39,7 +39,6 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Vibrator;
-import android.sysprop.InputProperties;
import android.util.Log;
import android.view.Display;
import android.view.InputDevice;
@@ -1038,9 +1037,7 @@
*/
public boolean isStylusPointerIconEnabled() {
if (mIsStylusPointerIconEnabled == null) {
- mIsStylusPointerIconEnabled = mContext.getResources()
- .getBoolean(com.android.internal.R.bool.config_enableStylusPointerIcon)
- || InputProperties.force_enable_stylus_pointer_icon().orElse(false);
+ mIsStylusPointerIconEnabled = InputSettings.isStylusPointerIconEnabled(mContext);
}
return mIsStylusPointerIconEnabled;
}
diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java
index 5462171..c0877d3 100644
--- a/core/java/android/hardware/input/InputManagerGlobal.java
+++ b/core/java/android/hardware/input/InputManagerGlobal.java
@@ -1252,7 +1252,7 @@
/**
* @see InputManager#requestPointerCapture(IBinder, boolean)
*/
- void requestPointerCapture(IBinder windowToken, boolean enable) {
+ public void requestPointerCapture(IBinder windowToken, boolean enable) {
try {
mIm.requestPointerCapture(windowToken, enable);
} catch (RemoteException ex) {
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index cdf9ea5..6cd32ff 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.os.UserHandle;
import android.provider.Settings;
+import android.sysprop.InputProperties;
/**
* InputSettings encapsulates reading and writing settings related to input
@@ -316,4 +317,15 @@
Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE, enabled ? 1 : 0,
UserHandle.USER_CURRENT);
}
+
+ /**
+ * Whether a pointer icon will be shown over the location of a
+ * stylus pointer.
+ * @hide
+ */
+ public static boolean isStylusPointerIconEnabled(@NonNull Context context) {
+ return context.getResources()
+ .getBoolean(com.android.internal.R.bool.config_enableStylusPointerIcon)
+ || InputProperties.force_enable_stylus_pointer_icon().orElse(false);
+ }
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 77bbeb5..9d3d70d 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -112,6 +112,7 @@
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
/**
@@ -431,6 +432,7 @@
Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED,
reportDraw ? 1 : 0,
mergedConfiguration);
+ mIWallpaperEngine.mPendingResizeCount.incrementAndGet();
mCaller.sendMessage(msg);
}
@@ -1051,6 +1053,10 @@
out.print(prefix); out.print("mZoom="); out.println(mZoom);
out.print(prefix); out.print("mPreviewSurfacePosition=");
out.println(mPreviewSurfacePosition);
+ final int pendingCount = mIWallpaperEngine.mPendingResizeCount.get();
+ if (pendingCount != 0) {
+ out.print(prefix); out.print("mPendingResizeCount="); out.println(pendingCount);
+ }
synchronized (mLock) {
out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset);
out.print(" mPendingXOffset="); out.println(mPendingXOffset);
@@ -1113,10 +1119,6 @@
}
}
- private void updateConfiguration(MergedConfiguration mergedConfiguration) {
- mMergedConfiguration.setTo(mergedConfiguration);
- }
-
void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {
if (mDestroyed) {
Log.w(TAG, "Ignoring updateSurface due to destroyed");
@@ -1165,7 +1167,7 @@
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
final Configuration config = mMergedConfiguration.getMergedConfiguration();
- final Rect maxBounds = config.windowConfiguration.getMaxBounds();
+ final Rect maxBounds = new Rect(config.windowConfiguration.getMaxBounds());
if (myWidth == ViewGroup.LayoutParams.MATCH_PARENT
&& myHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
mLayout.width = myWidth;
@@ -1221,6 +1223,17 @@
final int relayoutResult = mSession.relayout(mWindow, mLayout, mWidth, mHeight,
View.VISIBLE, 0, 0, 0, mWinFrames, mMergedConfiguration,
mSurfaceControl, mInsetsState, mTempControls, mSyncSeqIdBundle);
+ final Rect outMaxBounds = mMergedConfiguration.getMergedConfiguration()
+ .windowConfiguration.getMaxBounds();
+ if (!outMaxBounds.equals(maxBounds)) {
+ Log.i(TAG, "Retry updateSurface because bounds changed from relayout: "
+ + maxBounds + " -> " + outMaxBounds);
+ mSurfaceHolder.mSurfaceLock.unlock();
+ mDrawingAllowed = false;
+ mCaller.sendMessage(mCaller.obtainMessageI(MSG_WINDOW_RESIZED,
+ redrawNeeded ? 1 : 0));
+ return;
+ }
final int transformHint = SurfaceControl.rotationToBufferTransform(
(mDisplay.getInstallOrientation() + mDisplay.getRotation()) % 4);
@@ -1488,6 +1501,8 @@
mWallpaperDimAmount = mDefaultDimAmount;
mPreviousWallpaperDimAmount = mWallpaperDimAmount;
mDisplayState = mDisplay.getCommittedState();
+ mMergedConfiguration.setOverrideConfiguration(
+ mDisplayContext.getResources().getConfiguration());
if (DEBUG) Log.v(TAG, "onCreate(): " + this);
Trace.beginSection("WPMS.Engine.onCreate");
@@ -2324,6 +2339,8 @@
final IBinder mWindowToken;
final int mWindowType;
final boolean mIsPreview;
+ final AtomicInteger mPendingResizeCount = new AtomicInteger();
+ boolean mReportDraw;
boolean mShownReported;
int mReqWidth;
int mReqHeight;
@@ -2579,11 +2596,7 @@
mEngine.doCommand(cmd);
} break;
case MSG_WINDOW_RESIZED: {
- final boolean reportDraw = message.arg1 != 0;
- mEngine.updateConfiguration(((MergedConfiguration) message.obj));
- mEngine.updateSurface(true, false, reportDraw);
- mEngine.doOffsetsChanged(true);
- mEngine.scaleAndCropScreenshot();
+ handleResized((MergedConfiguration) message.obj, message.arg1 != 0);
} break;
case MSG_WINDOW_MOVED: {
// Do nothing. What does it mean for a Wallpaper to move?
@@ -2631,6 +2644,40 @@
Log.w(TAG, "Unknown message type " + message.what);
}
}
+
+ /**
+ * In general this performs relayout for IWindow#resized. If there are several pending
+ * (in the message queue) MSG_WINDOW_RESIZED from server side, only the last one will be
+ * handled (ignore intermediate states). Note that this procedure cannot be skipped if the
+ * configuration is not changed because this is also used to dispatch insets changes.
+ */
+ private void handleResized(MergedConfiguration config, boolean reportDraw) {
+ // The config can be null when retrying for a changed config from relayout, otherwise
+ // it is from IWindow#resized which always sends non-null config.
+ final int pendingCount = config != null ? mPendingResizeCount.decrementAndGet() : -1;
+ if (reportDraw) {
+ mReportDraw = true;
+ }
+ if (pendingCount > 0) {
+ if (DEBUG) {
+ Log.d(TAG, "Skip outdated resize, bounds="
+ + config.getMergedConfiguration().windowConfiguration.getMaxBounds()
+ + " pendingCount=" + pendingCount);
+ }
+ return;
+ }
+ if (config != null) {
+ if (DEBUG) {
+ Log.d(TAG, "Update config from resized, bounds="
+ + config.getMergedConfiguration().windowConfiguration.getMaxBounds());
+ }
+ mEngine.mMergedConfiguration.setTo(config);
+ }
+ mEngine.updateSurface(true /* forceRelayout */, false /* forceReport */, mReportDraw);
+ mReportDraw = false;
+ mEngine.doOffsetsChanged(true);
+ mEngine.scaleAndCropScreenshot();
+ }
}
/**
diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java
index b93e338..330a9fc 100644
--- a/core/java/android/util/TypedValue.java
+++ b/core/java/android/util/TypedValue.java
@@ -385,10 +385,22 @@
*
* @return The complex unit type.
*/
- public int getComplexUnit()
- {
- return COMPLEX_UNIT_MASK & (data>>TypedValue.COMPLEX_UNIT_SHIFT);
- }
+ public int getComplexUnit() {
+ return getUnitFromComplexDimension(data);
+ }
+
+ /**
+ * Return the complex unit type for the given complex dimension. For example, a dimen type
+ * with value 12sp will return {@link #COMPLEX_UNIT_SP}. Use with values created with {@link
+ * #createComplexDimension(int, int)} etc.
+ *
+ * @return The complex unit type.
+ *
+ * @hide
+ */
+ public static int getUnitFromComplexDimension(int complexDimension) {
+ return COMPLEX_UNIT_MASK & (complexDimension >> TypedValue.COMPLEX_UNIT_SHIFT);
+ }
/**
* Converts an unpacked complex data value holding a dimension to its final floating point pixel
diff --git a/core/java/android/view/InputMonitor.java b/core/java/android/view/InputMonitor.java
index 8801fe0..4996f5a 100644
--- a/core/java/android/view/InputMonitor.java
+++ b/core/java/android/view/InputMonitor.java
@@ -43,7 +43,8 @@
private final InputChannel mInputChannel;
@NonNull
private final IInputMonitorHost mHost;
-
+ @NonNull
+ private final SurfaceControl mSurface;
/**
* Takes all of the current pointer events streams that are currently being sent to this
@@ -70,6 +71,7 @@
*/
public void dispose() {
mInputChannel.dispose();
+ mSurface.release();
try {
mHost.dispose();
} catch (RemoteException e) {
@@ -95,13 +97,17 @@
@DataClass.Generated.Member
public InputMonitor(
@NonNull InputChannel inputChannel,
- @NonNull IInputMonitorHost host) {
+ @NonNull IInputMonitorHost host,
+ @NonNull SurfaceControl surface) {
this.mInputChannel = inputChannel;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mInputChannel);
this.mHost = host;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mHost);
+ this.mSurface = surface;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mSurface);
// onConstructed(); // You can define this method to get a callback
}
@@ -116,6 +122,11 @@
return mHost;
}
+ @DataClass.Generated.Member
+ public @NonNull SurfaceControl getSurface() {
+ return mSurface;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -124,7 +135,8 @@
return "InputMonitor { " +
"inputChannel = " + mInputChannel + ", " +
- "host = " + mHost +
+ "host = " + mHost + ", " +
+ "surface = " + mSurface +
" }";
}
@@ -136,6 +148,7 @@
dest.writeTypedObject(mInputChannel, flags);
dest.writeStrongInterface(mHost);
+ dest.writeTypedObject(mSurface, flags);
}
@Override
@@ -151,6 +164,7 @@
InputChannel inputChannel = (InputChannel) in.readTypedObject(InputChannel.CREATOR);
IInputMonitorHost host = IInputMonitorHost.Stub.asInterface(in.readStrongBinder());
+ SurfaceControl surface = (SurfaceControl) in.readTypedObject(SurfaceControl.CREATOR);
this.mInputChannel = inputChannel;
com.android.internal.util.AnnotationValidations.validate(
@@ -158,6 +172,9 @@
this.mHost = host;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mHost);
+ this.mSurface = surface;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mSurface);
// onConstructed(); // You can define this method to get a callback
}
@@ -177,10 +194,10 @@
};
@DataClass.Generated(
- time = 1637697281750L,
+ time = 1679692514588L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/InputMonitor.java",
- inputSignatures = "private static final java.lang.String TAG\nprivate static final boolean DEBUG\nprivate final @android.annotation.NonNull android.view.InputChannel mInputChannel\nprivate final @android.annotation.NonNull android.view.IInputMonitorHost mHost\npublic void pilferPointers()\npublic void dispose()\nclass InputMonitor extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true)")
+ inputSignatures = "private static final java.lang.String TAG\nprivate static final boolean DEBUG\nprivate final @android.annotation.NonNull android.view.InputChannel mInputChannel\nprivate final @android.annotation.NonNull android.view.IInputMonitorHost mHost\nprivate final @android.annotation.NonNull android.view.SurfaceControl mSurface\npublic void pilferPointers()\npublic void dispose()\nclass InputMonitor extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index bd6224b..d987217 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -410,6 +410,13 @@
}
/**
+ * @hide
+ */
+ public @NonNull AttachedSurfaceControl getRootSurfaceControl() {
+ return mViewRoot;
+ }
+
+ /**
* Set the root view of the SurfaceControlViewHost. This view will render in to
* the SurfaceControl, and receive input based on the SurfaceControls positioning on
* screen. It will be laid as if it were in a window of the passed in width and height.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 86e7fb0..2b29e78 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -133,7 +133,9 @@
import android.graphics.drawable.GradientDrawable;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
-import android.hardware.input.InputManager;
+import android.hardware.display.DisplayManagerGlobal;
+import android.hardware.input.InputManagerGlobal;
+import android.hardware.input.InputSettings;
import android.media.AudioManager;
import android.os.Binder;
import android.os.Build;
@@ -443,9 +445,7 @@
@UnsupportedAppUsage
final IWindowSession mWindowSession;
@NonNull Display mDisplay;
- final DisplayManager mDisplayManager;
final String mBasePackageName;
- final InputManager mInputManager;
final int[] mTmpLocation = new int[2];
@@ -550,6 +550,9 @@
// Whether to draw this surface as DISPLAY_DECORATION.
boolean mDisplayDecorationCached = false;
+ // Is the stylus pointer icon enabled
+ private final boolean mIsStylusPointerIconEnabled;
+
/**
* Update the Choreographer's FrameInfo object with the timing information for the current
* ViewRootImpl instance. Erase the data in the current ViewFrameInfo to prepare for the next
@@ -994,14 +997,14 @@
mFallbackEventHandler = new PhoneFallbackEventHandler(context);
// TODO(b/222696368): remove getSfInstance usage and use vsyncId for transactions
mChoreographer = Choreographer.getInstance();
- mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
- mInputManager = context.getSystemService(InputManager.class);
mInsetsController = new InsetsController(new ViewRootInsetsControllerHost(this));
mHandwritingInitiator = new HandwritingInitiator(
mViewConfiguration,
mContext.getSystemService(InputMethodManager.class));
mViewBoundsSandboxingEnabled = getViewBoundsSandboxingEnabled();
+ mIsStylusPointerIconEnabled =
+ InputSettings.isStylusPointerIconEnabled(mContext);
String processorOverrideName = context.getResources().getString(
R.string.config_inputEventCompatProcessorOverrideClassName);
@@ -1488,7 +1491,14 @@
mAccessibilityInteractionConnectionManager, mHandler);
mAccessibilityManager.addHighTextContrastStateChangeListener(
mHighContrastTextManager, mHandler);
- mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
+ DisplayManagerGlobal
+ .getInstance()
+ .registerDisplayListener(
+ mDisplayListener,
+ mHandler,
+ DisplayManager.EVENT_FLAG_DISPLAY_ADDED
+ | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
}
/**
@@ -1499,7 +1509,9 @@
mAccessibilityInteractionConnectionManager);
mAccessibilityManager.removeHighTextContrastStateChangeListener(
mHighContrastTextManager);
- mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ DisplayManagerGlobal
+ .getInstance()
+ .unregisterDisplayListener(mDisplayListener);
}
private void setTag() {
@@ -5382,7 +5394,9 @@
Log.e(mTag, "No input channel to request Pointer Capture.");
return;
}
- mInputManager.requestPointerCapture(inputToken, enabled);
+ InputManagerGlobal
+ .getInstance()
+ .requestPointerCapture(inputToken, enabled);
}
private void handlePointerCaptureChanged(boolean hasCapture) {
@@ -6947,7 +6961,7 @@
}
final boolean needsStylusPointerIcon = event.isStylusPointer()
&& event.isHoverEvent()
- && mInputManager.isStylusPointerIconEnabled();
+ && mIsStylusPointerIconEnabled;
if (needsStylusPointerIcon || event.isFromSource(InputDevice.SOURCE_MOUSE)) {
if (event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER
|| event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) {
@@ -7018,8 +7032,7 @@
}
PointerIcon pointerIcon = null;
-
- if (event.isStylusPointer() && mInputManager.isStylusPointerIconEnabled()) {
+ if (event.isStylusPointer() && mIsStylusPointerIconEnabled) {
pointerIcon = mHandwritingInitiator.onResolvePointerIcon(mContext, event);
}
@@ -7034,14 +7047,18 @@
mPointerIconType = pointerType;
mCustomPointerIcon = null;
if (mPointerIconType != PointerIcon.TYPE_CUSTOM) {
- mInputManager.setPointerIconType(pointerType);
+ InputManagerGlobal
+ .getInstance()
+ .setPointerIconType(pointerType);
return true;
}
}
if (mPointerIconType == PointerIcon.TYPE_CUSTOM &&
!pointerIcon.equals(mCustomPointerIcon)) {
mCustomPointerIcon = pointerIcon;
- mInputManager.setCustomPointerIcon(mCustomPointerIcon);
+ InputManagerGlobal
+ .getInstance()
+ .setCustomPointerIcon(mCustomPointerIcon);
}
return true;
}
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index a5e7086..b65c1a1 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -845,11 +845,7 @@
// Calling overScrollBy will call onOverScrolled, which
// calls onScrollChanged if applicable.
- if (overScrollBy(0, deltaY, 0, mScrollY, 0, range, 0, mOverscrollDistance, true)
- && !hasNestedScrollingParent()) {
- // Break our velocity if we hit a scroll barrier.
- mVelocityTracker.clear();
- }
+ overScrollBy(0, deltaY, 0, mScrollY, 0, range, 0, mOverscrollDistance, true);
final int scrolledDeltaY = mScrollY - oldY;
final int unconsumedY = deltaY - scrolledDeltaY;
@@ -894,6 +890,7 @@
mActivePointerId = INVALID_POINTER;
endDrag();
+ velocityTracker.clear();
}
break;
case MotionEvent.ACTION_CANCEL:
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 67c9f8c..3fbb505 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -62,6 +62,7 @@
import android.content.res.ColorStateList;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.content.res.FontScaleConverterFactory;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
@@ -867,6 +868,14 @@
@UnsupportedAppUsage
private float mSpacingAdd = 0.0f;
+ /**
+ * Remembers what line height was set to originally, before we broke it down into raw pixels.
+ *
+ * <p>This is stored as a complex dimension with both value and unit packed into one field!
+ * {@see TypedValue}
+ */
+ private int mLineHeightComplexDimen;
+
private int mBreakStrategy;
private int mHyphenationFrequency;
private int mJustificationMode;
@@ -1233,7 +1242,8 @@
defStyleAttr, defStyleRes);
int firstBaselineToTopHeight = -1;
int lastBaselineToBottomHeight = -1;
- int lineHeight = -1;
+ float lineHeight = -1f;
+ int lineHeightUnit = -1;
readTextAppearance(context, a, attributes, true /* styleArray */);
@@ -1583,7 +1593,13 @@
break;
case com.android.internal.R.styleable.TextView_lineHeight:
- lineHeight = a.getDimensionPixelSize(attr, -1);
+ TypedValue peekValue = a.peekValue(attr);
+ if (peekValue != null && peekValue.type == TypedValue.TYPE_DIMENSION) {
+ lineHeightUnit = peekValue.getComplexUnit();
+ lineHeight = TypedValue.complexToFloat(peekValue.data);
+ } else {
+ lineHeight = a.getDimensionPixelSize(attr, -1);
+ }
break;
}
}
@@ -1936,7 +1952,11 @@
setLastBaselineToBottomHeight(lastBaselineToBottomHeight);
}
if (lineHeight >= 0) {
- setLineHeight(lineHeight);
+ if (lineHeightUnit == -1) {
+ setLineHeightPx(lineHeight);
+ } else {
+ setLineHeight(lineHeightUnit, lineHeight);
+ }
}
}
@@ -4629,6 +4649,7 @@
if (size != mTextPaint.getTextSize()) {
mTextPaint.setTextSize(size);
+ maybeRecalculateLineHeight();
if (shouldRequestLayout && mLayout != null) {
// Do not auto-size right after setting the text size.
mNeedsAutoSizeText = false;
@@ -6214,6 +6235,9 @@
if (lineHeight != fontHeight) {
// Set lineSpacingExtra by the difference of lineSpacing with lineHeight
setLineSpacing(lineHeight - fontHeight, 1f);
+
+ mLineHeightComplexDimen =
+ TypedValue.createComplexDimension(lineHeight, TypedValue.COMPLEX_UNIT_PX);
}
}
@@ -6236,8 +6260,54 @@
@TypedValue.ComplexDimensionUnit int unit,
@FloatRange(from = 0) float lineHeight
) {
- setLineHeightPx(
- TypedValue.applyDimension(unit, lineHeight, getDisplayMetricsOrSystem()));
+ var metrics = getDisplayMetricsOrSystem();
+ // We can avoid the recalculation if we know non-linear font scaling isn't being used
+ // (an optimization for the majority case).
+ // We also don't try to do the recalculation unless both textSize and lineHeight are in SP.
+ if (!FontScaleConverterFactory.isNonLinearFontScalingActive(
+ getResources().getConfiguration().fontScale)
+ || unit != TypedValue.COMPLEX_UNIT_SP
+ || mTextSizeUnit != TypedValue.COMPLEX_UNIT_SP
+ ) {
+ setLineHeightPx(TypedValue.applyDimension(unit, lineHeight, metrics));
+
+ // Do this last so it overwrites what setLineHeightPx() sets it to.
+ mLineHeightComplexDimen = TypedValue.createComplexDimension(lineHeight, unit);
+ return;
+ }
+
+ // Recalculate a proportional line height when non-linear font scaling is in effect.
+ // Otherwise, a desired 2x line height at font scale 1.0 will not be 2x at font scale 2.0,
+ // due to non-linear font scaling compressing higher SP sizes. See b/273326061 for details.
+ // We know they are using SP units for both the text size and the line height
+ // at this point, so determine the ratio between them. This is the *intended* line spacing
+ // multiplier if font scale == 1.0. We can then determine what the pixel value for the line
+ // height would be if we preserved proportions.
+ var textSizePx = getTextSize();
+ var textSizeSp = TypedValue.convertPixelsToDimension(
+ TypedValue.COMPLEX_UNIT_SP,
+ textSizePx,
+ metrics
+ );
+ var ratio = lineHeight / textSizeSp;
+ setLineHeightPx(textSizePx * ratio);
+
+ // Do this last so it overwrites what setLineHeightPx() sets it to.
+ mLineHeightComplexDimen = TypedValue.createComplexDimension(lineHeight, unit);
+ }
+
+ private void maybeRecalculateLineHeight() {
+ if (mLineHeightComplexDimen == 0) {
+ return;
+ }
+ int unit = TypedValue.getUnitFromComplexDimension(mLineHeightComplexDimen);
+ if (unit != TypedValue.COMPLEX_UNIT_SP) {
+ // The lineHeight was never supplied in SP, so we didn't do any fancy recalculations
+ // in setLineHeight(). We don't need to recalculate.
+ return;
+ }
+
+ setLineHeight(unit, TypedValue.complexToFloat(mLineHeightComplexDimen));
}
/**
diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java
index e0ee683..e44f436 100644
--- a/core/java/android/window/BackNavigationInfo.java
+++ b/core/java/android/window/BackNavigationInfo.java
@@ -94,26 +94,29 @@
@Nullable
private final IOnBackInvokedCallback mOnBackInvokedCallback;
private final boolean mPrepareRemoteAnimation;
+ private final boolean mAnimationCallback;
@Nullable
private final CustomAnimationInfo mCustomAnimationInfo;
/**
* Create a new {@link BackNavigationInfo} instance.
*
- * @param type The {@link BackTargetType} of the destination (what will be
- * @param onBackNavigationDone The callback to be called once the client is done with the
- * back preview.
- * @param onBackInvokedCallback The back callback registered by the current top level window.
+ * @param type The {@link BackTargetType} of the destination (what will be
+ * @param onBackNavigationDone The callback to be called once the client is done with the
+ * back preview.
+ * @param onBackInvokedCallback The back callback registered by the current top level window.
*/
private BackNavigationInfo(@BackTargetType int type,
@Nullable RemoteCallback onBackNavigationDone,
@Nullable IOnBackInvokedCallback onBackInvokedCallback,
boolean isPrepareRemoteAnimation,
+ boolean isAnimationCallback,
@Nullable CustomAnimationInfo customAnimationInfo) {
mType = type;
mOnBackNavigationDone = onBackNavigationDone;
mOnBackInvokedCallback = onBackInvokedCallback;
mPrepareRemoteAnimation = isPrepareRemoteAnimation;
+ mAnimationCallback = isAnimationCallback;
mCustomAnimationInfo = customAnimationInfo;
}
@@ -122,6 +125,7 @@
mOnBackNavigationDone = in.readTypedObject(RemoteCallback.CREATOR);
mOnBackInvokedCallback = IOnBackInvokedCallback.Stub.asInterface(in.readStrongBinder());
mPrepareRemoteAnimation = in.readBoolean();
+ mAnimationCallback = in.readBoolean();
mCustomAnimationInfo = in.readTypedObject(CustomAnimationInfo.CREATOR);
}
@@ -132,6 +136,7 @@
dest.writeTypedObject(mOnBackNavigationDone, flags);
dest.writeStrongInterface(mOnBackInvokedCallback);
dest.writeBoolean(mPrepareRemoteAnimation);
+ dest.writeBoolean(mAnimationCallback);
dest.writeTypedObject(mCustomAnimationInfo, flags);
}
@@ -159,7 +164,7 @@
}
/**
- * Return true if the core is preparing a back gesture nimation.
+ * Return true if the core is preparing a back gesture animation.
* @hide
*/
public boolean isPrepareRemoteAnimation() {
@@ -167,6 +172,14 @@
}
/**
+ * Return true if the callback is {@link OnBackAnimationCallback}.
+ * @hide
+ */
+ public boolean isAnimationCallback() {
+ return mAnimationCallback;
+ }
+
+ /**
* Callback to be called when the back preview is finished in order to notify the server that
* it can clean up the resources created for the animation.
* @hide
@@ -214,6 +227,8 @@
+ "mType=" + typeToString(mType) + " (" + mType + ")"
+ ", mOnBackNavigationDone=" + mOnBackNavigationDone
+ ", mOnBackInvokedCallback=" + mOnBackInvokedCallback
+ + ", mPrepareRemoteAnimation=" + mPrepareRemoteAnimation
+ + ", mAnimationCallback=" + mAnimationCallback
+ ", mCustomizeAnimationInfo=" + mCustomAnimationInfo
+ '}';
}
@@ -343,6 +358,7 @@
private IOnBackInvokedCallback mOnBackInvokedCallback = null;
private boolean mPrepareRemoteAnimation;
private CustomAnimationInfo mCustomAnimationInfo;
+ private boolean mAnimationCallback = false;
/**
* @see BackNavigationInfo#getType()
@@ -387,6 +403,7 @@
mCustomAnimationInfo.mWindowAnimations = windowAnimations;
return this;
}
+
/**
* Set resources ids for customize activity animation.
*/
@@ -402,12 +419,21 @@
}
/**
+ * @param isAnimationCallback whether the callback is {@link OnBackAnimationCallback}
+ */
+ public Builder setAnimationCallback(boolean isAnimationCallback) {
+ mAnimationCallback = isAnimationCallback;
+ return this;
+ }
+
+ /**
* Builds and returns an instance of {@link BackNavigationInfo}
*/
public BackNavigationInfo build() {
return new BackNavigationInfo(mType, mOnBackNavigationDone,
mOnBackInvokedCallback,
mPrepareRemoteAnimation,
+ mAnimationCallback,
mCustomAnimationInfo);
}
}
diff --git a/core/java/android/window/OnBackInvokedCallbackInfo.java b/core/java/android/window/OnBackInvokedCallbackInfo.java
index 6480da3..bb5fe96 100644
--- a/core/java/android/window/OnBackInvokedCallbackInfo.java
+++ b/core/java/android/window/OnBackInvokedCallbackInfo.java
@@ -28,15 +28,20 @@
@NonNull
private final IOnBackInvokedCallback mCallback;
private @OnBackInvokedDispatcher.Priority int mPriority;
+ private final boolean mIsAnimationCallback;
- public OnBackInvokedCallbackInfo(@NonNull IOnBackInvokedCallback callback, int priority) {
+ public OnBackInvokedCallbackInfo(@NonNull IOnBackInvokedCallback callback,
+ int priority,
+ boolean isAnimationCallback) {
mCallback = callback;
mPriority = priority;
+ mIsAnimationCallback = isAnimationCallback;
}
private OnBackInvokedCallbackInfo(@NonNull Parcel in) {
mCallback = IOnBackInvokedCallback.Stub.asInterface(in.readStrongBinder());
mPriority = in.readInt();
+ mIsAnimationCallback = in.readBoolean();
}
@Override
@@ -48,6 +53,7 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeStrongInterface(mCallback);
dest.writeInt(mPriority);
+ dest.writeBoolean(mIsAnimationCallback);
}
public static final Creator<OnBackInvokedCallbackInfo> CREATOR =
@@ -77,9 +83,16 @@
return mPriority;
}
+ public boolean isAnimationCallback() {
+ return mIsAnimationCallback;
+ }
+
@Override
public String toString() {
return "OnBackInvokedCallbackInfo{"
- + "mCallback=" + mCallback + ", mPriority=" + mPriority + '}';
+ + "mCallback=" + mCallback
+ + ", mPriority=" + mPriority
+ + ", mIsAnimationCallback=" + mIsAnimationCallback
+ + '}';
}
}
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 8066f50..51382a4 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -193,7 +193,10 @@
? ((ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback)
callback).getIOnBackInvokedCallback()
: new OnBackInvokedCallbackWrapper(callback);
- callbackInfo = new OnBackInvokedCallbackInfo(iCallback, priority);
+ callbackInfo = new OnBackInvokedCallbackInfo(
+ iCallback,
+ priority,
+ callback instanceof OnBackAnimationCallback);
}
mWindowSession.setOnBackInvokedCallbackInfo(mWindow, callbackInfo);
} catch (RemoteException e) {
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index 2b08a55..853fe2f 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -77,6 +77,10 @@
/** Gating the removal of sorting-notifications-by-interruptiveness. */
public static final Flag NO_SORT_BY_INTERRUPTIVENESS =
devFlag("persist.sysui.notification.no_sort_by_interruptiveness");
+
+ /** Gating the logging of DND state change events. */
+ public static final Flag LOG_DND_STATE_EVENTS =
+ devFlag("persist.sysui.notification.log_dnd_state_events");
}
//// == End of flags. Everything below this line is the implementation. == ////
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index 1b4afd6..bb8bdf5 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -255,6 +255,12 @@
return "HIDE_SOFT_INPUT_IMM_DEPRECATION";
case SoftInputShowHideReason.HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR:
return "HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR";
+ case SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS:
+ return "SHOW_IME_SCREENSHOT_FROM_IMMS";
+ case SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS:
+ return "REMOVE_IME_SCREENSHOT_FROM_IMMS";
+ case SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE:
+ return "HIDE_WHEN_INPUT_TARGET_INVISIBLE";
default:
return "Unknown=" + reason;
}
diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
index ec9184b..6e9cd44 100644
--- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
+++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
@@ -65,7 +65,10 @@
SoftInputShowHideReason.HIDE_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT,
SoftInputShowHideReason.HIDE_SOFT_INPUT_EXTRACT_INPUT_CHANGED,
SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION,
- SoftInputShowHideReason.HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR
+ SoftInputShowHideReason.HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR,
+ SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS,
+ SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS,
+ SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE,
})
public @interface SoftInputShowHideReason {
/** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */
@@ -259,4 +262,20 @@
*/
int HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR =
ImeProtoEnums.REASON_HIDE_WINDOW_GAINED_FOCUS_WITHOUT_EDITOR;
+
+ /**
+ * Shows ime screenshot by {@link com.android.server.inputmethod.InputMethodManagerService}.
+ */
+ int SHOW_IME_SCREENSHOT_FROM_IMMS = ImeProtoEnums.REASON_SHOW_IME_SCREENSHOT_FROM_IMMS;
+
+ /**
+ * Removes ime screenshot by {@link com.android.server.inputmethod.InputMethodManagerService}.
+ */
+ int REMOVE_IME_SCREENSHOT_FROM_IMMS = ImeProtoEnums.REASON_REMOVE_IME_SCREENSHOT_FROM_IMMS;
+
+ /**
+ * Hide soft input when the input target being removed or being obscured by an non-IME
+ * focusable overlay window.
+ */
+ int HIDE_WHEN_INPUT_TARGET_INVISIBLE = ImeProtoEnums.REASON_HIDE_WHEN_INPUT_TARGET_INVISIBLE;
}
diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java
index 134a917..efaedd1 100644
--- a/core/java/com/android/internal/policy/DecorContext.java
+++ b/core/java/com/android/internal/policy/DecorContext.java
@@ -82,13 +82,6 @@
}
return mContentCaptureManager;
}
- // TODO(b/154191411): Try to revisit this issue in S.
- // We use application to get DisplayManager here because ViewRootImpl holds reference of
- // DisplayManager and implicitly holds reference of mContext, which makes activity cannot
- // be GC'd even after destroyed if mContext is an activity object.
- if (Context.DISPLAY_SERVICE.equals(name)) {
- return super.getSystemService(name);
- }
// LayoutInflater and WallpaperManagerService should also be obtained from visual context
// instead of base context.
return (context != null) ? context.getSystemService(name) : super.getSystemService(name);
diff --git a/core/java/com/android/internal/util/QuickSelect.java b/core/java/com/android/internal/util/QuickSelect.java
new file mode 100644
index 0000000..17739c9
--- /dev/null
+++ b/core/java/com/android/internal/util/QuickSelect.java
@@ -0,0 +1,256 @@
+/*
+ * 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.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * An implementation of the quick selection algorithm as described in
+ * http://en.wikipedia.org/wiki/Quickselect.
+ *
+ * @hide
+ */
+public final class QuickSelect {
+ private static <T> int selectImpl(@NonNull List<T> list, int left, int right, int k,
+ @NonNull Comparator<? super T> comparator) {
+ while (true) {
+ if (left == right) {
+ return left;
+ }
+ final int pivotIndex = partition(list, left, right, (left + right) >> 1, comparator);
+ if (k == pivotIndex) {
+ return k;
+ } else if (k < pivotIndex) {
+ right = pivotIndex - 1;
+ } else {
+ left = pivotIndex + 1;
+ }
+ }
+ }
+
+ private static int selectImpl(@NonNull int[] array, int left, int right, int k) {
+ while (true) {
+ if (left == right) {
+ return left;
+ }
+ final int pivotIndex = partition(array, left, right, (left + right) >> 1);
+ if (k == pivotIndex) {
+ return k;
+ } else if (k < pivotIndex) {
+ right = pivotIndex - 1;
+ } else {
+ left = pivotIndex + 1;
+ }
+ }
+ }
+
+ private static int selectImpl(@NonNull long[] array, int left, int right, int k) {
+ while (true) {
+ if (left == right) {
+ return left;
+ }
+ final int pivotIndex = partition(array, left, right, (left + right) >> 1);
+ if (k == pivotIndex) {
+ return k;
+ } else if (k < pivotIndex) {
+ right = pivotIndex - 1;
+ } else {
+ left = pivotIndex + 1;
+ }
+ }
+ }
+
+ private static <T> int selectImpl(@NonNull T[] array, int left, int right, int k,
+ @NonNull Comparator<? super T> comparator) {
+ while (true) {
+ if (left == right) {
+ return left;
+ }
+ final int pivotIndex = partition(array, left, right, (left + right) >> 1, comparator);
+ if (k == pivotIndex) {
+ return k;
+ } else if (k < pivotIndex) {
+ right = pivotIndex - 1;
+ } else {
+ left = pivotIndex + 1;
+ }
+ }
+ }
+
+ private static <T> int partition(@NonNull List<T> list, int left, int right, int pivotIndex,
+ @NonNull Comparator<? super T> comparator) {
+ final T pivotValue = list.get(pivotIndex);
+ swap(list, right, pivotIndex);
+ int storeIndex = left;
+ for (int i = left; i < right; i++) {
+ if (comparator.compare(list.get(i), pivotValue) < 0) {
+ swap(list, storeIndex, i);
+ storeIndex++;
+ }
+ }
+ swap(list, right, storeIndex);
+ return storeIndex;
+ }
+
+ private static int partition(@NonNull int[] array, int left, int right, int pivotIndex) {
+ final int pivotValue = array[pivotIndex];
+ swap(array, right, pivotIndex);
+ int storeIndex = left;
+ for (int i = left; i < right; i++) {
+ if (array[i] < pivotValue) {
+ swap(array, storeIndex, i);
+ storeIndex++;
+ }
+ }
+ swap(array, right, storeIndex);
+ return storeIndex;
+ }
+
+ private static int partition(@NonNull long[] array, int left, int right, int pivotIndex) {
+ final long pivotValue = array[pivotIndex];
+ swap(array, right, pivotIndex);
+ int storeIndex = left;
+ for (int i = left; i < right; i++) {
+ if (array[i] < pivotValue) {
+ swap(array, storeIndex, i);
+ storeIndex++;
+ }
+ }
+ swap(array, right, storeIndex);
+ return storeIndex;
+ }
+
+ private static <T> int partition(@NonNull T[] array, int left, int right, int pivotIndex,
+ @NonNull Comparator<? super T> comparator) {
+ final T pivotValue = array[pivotIndex];
+ swap(array, right, pivotIndex);
+ int storeIndex = left;
+ for (int i = left; i < right; i++) {
+ if (comparator.compare(array[i], pivotValue) < 0) {
+ swap(array, storeIndex, i);
+ storeIndex++;
+ }
+ }
+ swap(array, right, storeIndex);
+ return storeIndex;
+ }
+
+ private static <T> void swap(@NonNull List<T> list, int left, int right) {
+ final T tmp = list.get(left);
+ list.set(left, list.get(right));
+ list.set(right, tmp);
+ }
+
+ private static void swap(@NonNull int[] array, int left, int right) {
+ final int tmp = array[left];
+ array[left] = array[right];
+ array[right] = tmp;
+ }
+
+ private static void swap(@NonNull long[] array, int left, int right) {
+ final long tmp = array[left];
+ array[left] = array[right];
+ array[right] = tmp;
+ }
+
+ private static <T> void swap(@NonNull T[] array, int left, int right) {
+ final T tmp = array[left];
+ array[left] = array[right];
+ array[right] = tmp;
+ }
+
+ /**
+ * Return the kth(0-based) smallest element from the given unsorted list.
+ *
+ * @param list The input list, it <b>will</b> be modified by the algorithm here.
+ * @param start The start offset of the list, inclusive.
+ * @param length The length of the sub list to be searched in.
+ * @param k The 0-based index.
+ * @param comparator The comparator which knows how to compare the elements in the list.
+ * @return The kth smallest element from the given list,
+ * or IllegalArgumentException will be thrown if not found.
+ */
+ @Nullable
+ public static <T> T select(@NonNull List<T> list, int start, int length, int k,
+ @NonNull Comparator<? super T> comparator) {
+ if (list == null || start < 0 || length <= 0 || list.size() < start + length
+ || k < 0 || length <= k) {
+ throw new IllegalArgumentException();
+ }
+ return list.get(selectImpl(list, start, start + length - 1, k + start, comparator));
+ }
+
+ /**
+ * Return the kth(0-based) smallest element from the given unsorted array.
+ *
+ * @param array The input array, it <b>will</b> be modified by the algorithm here.
+ * @param start The start offset of the array, inclusive.
+ * @param length The length of the sub array to be searched in.
+ * @param k The 0-based index to search for.
+ * @return The kth smallest element from the given array,
+ * or IllegalArgumentException will be thrown if not found.
+ */
+ public static int select(@NonNull int[] array, int start, int length, int k) {
+ if (array == null || start < 0 || length <= 0 || array.length < start + length
+ || k < 0 || length <= k) {
+ throw new IllegalArgumentException();
+ }
+ return array[selectImpl(array, start, start + length - 1, k + start)];
+ }
+
+ /**
+ * Return the kth(0-based) smallest element from the given unsorted array.
+ *
+ * @param array The input array, it <b>will</b> be modified by the algorithm here.
+ * @param start The start offset of the array, inclusive.
+ * @param length The length of the sub array to be searched in.
+ * @param k The 0-based index to search for.
+ * @return The kth smallest element from the given array,
+ * or IllegalArgumentException will be thrown if not found.
+ */
+ public static long select(@NonNull long[] array, int start, int length, int k) {
+ if (array == null || start < 0 || length <= 0 || array.length < start + length
+ || k < 0 || length <= k) {
+ throw new IllegalArgumentException();
+ }
+ return array[selectImpl(array, start, start + length - 1, k + start)];
+ }
+
+ /**
+ * Return the kth(0-based) smallest element from the given unsorted array.
+ *
+ * @param array The input array, it <b>will</b> be modified by the algorithm here.
+ * @param start The start offset of the array, inclusive.
+ * @param length The length of the sub array to be searched in.
+ * @param k The 0-based index to search for.
+ * @param comparator The comparator which knows how to compare the elements in the list.
+ * @return The kth smallest element from the given array,
+ * or IllegalArgumentException will be thrown if not found.
+ */
+ public static <T> T select(@NonNull T[] array, int start, int length, int k,
+ @NonNull Comparator<? super T> comparator) {
+ if (array == null || start < 0 || length <= 0 || array.length < start + length
+ || k < 0 || length <= k) {
+ throw new IllegalArgumentException();
+ }
+ return array[selectImpl(array, start, start + length - 1, k + start, comparator)];
+ }
+}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 6bec6bc..42d6896 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -334,7 +334,7 @@
"libtimeinstate",
"server_configurable_flags",
"libimage_io",
- "libjpegrecoverymap",
+ "libultrahdr",
],
export_shared_lib_headers: [
// our headers include libnativewindow's public headers
@@ -393,7 +393,7 @@
"libimage_io",
"libjpegdecoder",
"libjpegencoder",
- "libjpegrecoverymap",
+ "libultrahdr",
],
},
host_linux: {
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index bb3089b..325ebbe 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -458,6 +458,7 @@
optional float global_scale = 44;
repeated .android.graphics.RectProto keep_clear_areas = 45;
repeated .android.graphics.RectProto unrestricted_keep_clear_areas = 46;
+ repeated .android.view.InsetsSourceProto mergedLocalInsetsSources = 47;
}
message IdentifierProto {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 05b38a5..c928a82 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1036,9 +1036,9 @@
android:protectionLevel="dangerous" />
<!-- @SystemApi @hide Allows an application to communicate over satellite.
- Only granted if the application is a system app. -->
+ Only granted if the application is a system app or privileged app. -->
<permission android:name="android.permission.SATELLITE_COMMUNICATION"
- android:protectionLevel="internal|role" />
+ android:protectionLevel="role|signature|privileged" />
<!-- ====================================================================== -->
<!-- Permissions for accessing external storage -->
@@ -1466,6 +1466,9 @@
<!-- Allows an application to initiate a phone call without going through
the Dialer user interface for the user to confirm the call.
+ <p>
+ <em>Note: An app holding this permission can also call carrier MMI codes to change settings
+ such as call forwarding or call waiting preferences.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.CALL_PHONE"
@@ -2539,7 +2542,7 @@
<permission android:name="android.permission.TURN_SCREEN_ON"
android:label="@string/permlab_turnScreenOn"
android:description="@string/permdesc_turnScreenOn"
- android:protectionLevel="normal|appop" />
+ android:protectionLevel="signature|privileged|appop" />
<!-- ==================================================== -->
<!-- Permissions related to changing audio settings -->
@@ -2917,6 +2920,14 @@
<permission android:name="android.permission.BIND_SATELLITE_SERVICE"
android:protectionLevel="signature|privileged|vendorPrivileged" />
+ <!-- Must be required by a SatelliteGatewayService to ensure that only the
+ system can bind to it.
+ <p>Protection level: signature
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_SATELLITE_GATEWAY_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- Must be required by a telephony data service to ensure that only the
system can bind to it.
<p>Protection level: signature
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index fab7609..a57a051 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -125,6 +125,10 @@
<string name="config_satellite_service_package" translatable="false"></string>
<java-symbol type="string" name="config_satellite_service_package" />
+ <!-- Telephony satellite gateway service package name to bind to by default. -->
+ <string name="config_satellite_gateway_service_package" translatable="false"></string>
+ <java-symbol type="string" name="config_satellite_gateway_service_package" />
+
<!-- Telephony pointing UI package name to be launched. -->
<string name="config_pointing_ui_package" translatable="false"></string>
<java-symbol type="string" name="config_pointing_ui_package" />
@@ -142,10 +146,6 @@
<bool name="config_enhanced_iwlan_handover_check">true</bool>
<java-symbol type="bool" name="config_enhanced_iwlan_handover_check" />
- <!-- Whether using the new SubscriptionManagerService or the old SubscriptionController -->
- <bool name="config_using_subscription_manager_service">true</bool>
- <java-symbol type="bool" name="config_using_subscription_manager_service" />
-
<!-- Whether asynchronously update the subscription database or not. Async mode increases
the performance, but sync mode reduces the chance of database/cache out-of-sync. -->
<bool name="config_subscription_database_async_update">true</bool>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ccd0dcc..c6462f1 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1452,7 +1452,8 @@
without your intervention. This may result in unexpected charges or calls.
Note that this doesn\'t allow the app to call emergency numbers.
Malicious apps may cost you money by making calls without your
- confirmation.</string>
+ confirmation, or dial carrier codes which cause incoming calls to be
+ automatically forwarded to another number.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_accessImsCallService">access IMS call service</string>
@@ -5896,9 +5897,9 @@
<string name="resolver_cant_access_personal_apps_explanation">This content can\u2019t be opened with personal apps</string>
<!-- Error message. This text lets the user know that they need to turn on work apps in order to share or open content. There's also a button a user can tap to turn on the apps. [CHAR LIMIT=NONE] -->
- <string name="resolver_turn_on_work_apps">Work profile is paused</string>
- <!-- Button text. This button turns on a user's work profile so they can access their work apps and data. [CHAR LIMIT=NONE] -->
- <string name="resolver_switch_on_work">Tap to turn on</string>
+ <string name="resolver_turn_on_work_apps">Work apps are paused</string>
+ <!-- Button text. This button unpauses a user's work apps and data. [CHAR LIMIT=NONE] -->
+ <string name="resolver_switch_on_work">Unpause</string>
<!-- Error message. This text lets the user know that their current work apps don't support the specific content. [CHAR LIMIT=NONE] -->
<string name="resolver_no_work_apps_available">No work apps</string>
@@ -5907,9 +5908,9 @@
<string name="resolver_no_personal_apps_available">No personal apps</string>
<!-- Dialog title. User must choose between opening content in a cross-profile app or same-profile browser. [CHAR LIMIT=NONE] -->
- <string name="miniresolver_open_in_personal">Open <xliff:g id="app" example="YouTube">%s</xliff:g> in your personal profile?</string>
+ <string name="miniresolver_open_in_personal">Open personal <xliff:g id="app" example="YouTube">%s</xliff:g></string>
<!-- Dialog title. User must choose between opening content in a cross-profile app or same-profile browser. [CHAR LIMIT=NONE] -->
- <string name="miniresolver_open_in_work">Open <xliff:g id="app" example="YouTube">%s</xliff:g> in your work profile?</string>
+ <string name="miniresolver_open_in_work">Open work <xliff:g id="app" example="YouTube">%s</xliff:g></string>
<!-- Button option. Open the link in the personal browser. [CHAR LIMIT=NONE] -->
<string name="miniresolver_use_personal_browser">Use personal browser</string>
<!-- Button option. Open the link in the work browser. [CHAR LIMIT=NONE] -->
diff --git a/core/tests/coretests/res/drawable-nodpi/test_too_big.png b/core/tests/coretests/res/drawable-nodpi/test_too_big.png
new file mode 100644
index 0000000..3754072
--- /dev/null
+++ b/core/tests/coretests/res/drawable-nodpi/test_too_big.png
Binary files differ
diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
index a53d57f..a102b3e 100644
--- a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
+++ b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
@@ -1127,6 +1127,31 @@
mActivityRule.runOnUiThread(() -> {});
}
+ @Test
+ public void restartValueAnimator() throws Throwable {
+ CountDownLatch latch = new CountDownLatch(1);
+ ValueAnimator.AnimatorUpdateListener listener = new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ if (((float) animation.getAnimatedValue()) != A1_START_VALUE) {
+ latch.countDown();
+ }
+ }
+ };
+ a1.addUpdateListener(listener);
+
+ mActivityRule.runOnUiThread(() -> {
+ a1.start();
+ });
+
+ // wait for a change in the value
+ assertTrue(latch.await(2, TimeUnit.SECONDS));
+
+ mActivityRule.runOnUiThread(() -> {
+ a1.start();
+ assertEquals(A1_START_VALUE, a1.getAnimatedValue());
+ });
+ }
class MyUpdateListener implements ValueAnimator.AnimatorUpdateListener {
boolean wasRunning = false;
long firstRunningFrameTime = -1;
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
index a0d8dcf..ba6c8fa 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
@@ -122,6 +122,22 @@
}
}
+ @SmallTest
+ fun testIsNonLinearFontScalingActive() {
+ assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1f)).isFalse()
+ assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(0f)).isFalse()
+ assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(-1f)).isFalse()
+ assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(0.85f)).isFalse()
+ assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1.02f)).isFalse()
+ assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1.10f)).isFalse()
+ assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1.15f)).isTrue()
+ assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1.1499999f))
+ .isTrue()
+ assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(1.5f)).isTrue()
+ assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(2f)).isTrue()
+ assertThat(FontScaleConverterFactory.isNonLinearFontScalingActive(3f)).isTrue()
+ }
+
@LargeTest
@Test
fun allFeasibleScalesAndConversionsDoNotCrash() {
diff --git a/core/tests/coretests/src/android/graphics/drawable/IconTest.java b/core/tests/coretests/src/android/graphics/drawable/IconTest.java
index 75390a2..5d92296 100644
--- a/core/tests/coretests/src/android/graphics/drawable/IconTest.java
+++ b/core/tests/coretests/src/android/graphics/drawable/IconTest.java
@@ -20,6 +20,7 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.RecordingCanvas;
import android.graphics.Region;
import android.os.Handler;
import android.os.HandlerThread;
@@ -371,6 +372,90 @@
}
}
+ private int getMaxWidth(int origWidth, int origHeight, int maxNumPixels) {
+ float aspRatio = (float) origWidth / (float) origHeight;
+ int newHeight = (int) Math.sqrt(maxNumPixels / aspRatio);
+ return (int) (newHeight * aspRatio);
+ }
+
+ private int getMaxHeight(int origWidth, int origHeight, int maxNumPixels) {
+ float aspRatio = (float) origWidth / (float) origHeight;
+ return (int) Math.sqrt(maxNumPixels / aspRatio);
+ }
+
+ @SmallTest
+ public void testScaleDownMaxSizeWithBitmap() throws Exception {
+ final int bmpWidth = 13_000;
+ final int bmpHeight = 10_000;
+ final int bmpBpp = 4;
+ final int maxNumPixels = RecordingCanvas.MAX_BITMAP_SIZE / bmpBpp;
+ final int maxWidth = getMaxWidth(bmpWidth, bmpHeight, maxNumPixels);
+ final int maxHeight = getMaxHeight(bmpWidth, bmpHeight, maxNumPixels);
+
+ final Bitmap bm = Bitmap.createBitmap(bmpWidth, bmpHeight, Bitmap.Config.ARGB_8888);
+ final Icon ic = Icon.createWithBitmap(bm);
+ final Drawable drawable = ic.loadDrawable(mContext);
+
+ assertThat(drawable.getIntrinsicWidth()).isEqualTo(maxWidth);
+ assertThat(drawable.getIntrinsicHeight()).isEqualTo(maxHeight);
+ }
+
+ @SmallTest
+ public void testScaleDownMaxSizeWithAdaptiveBitmap() throws Exception {
+ final int bmpWidth = 20_000;
+ final int bmpHeight = 10_000;
+ final int bmpBpp = 4;
+ final int maxNumPixels = RecordingCanvas.MAX_BITMAP_SIZE / bmpBpp;
+ final int maxWidth = getMaxWidth(bmpWidth, bmpHeight, maxNumPixels);
+ final int maxHeight = getMaxHeight(bmpWidth, bmpHeight, maxNumPixels);
+
+ final Bitmap bm = Bitmap.createBitmap(bmpWidth, bmpHeight, Bitmap.Config.ARGB_8888);
+ final Icon ic = Icon.createWithAdaptiveBitmap(bm);
+ final AdaptiveIconDrawable adaptiveDrawable = (AdaptiveIconDrawable) ic.loadDrawable(
+ mContext);
+ final Drawable drawable = adaptiveDrawable.getForeground();
+
+ assertThat(drawable.getIntrinsicWidth()).isEqualTo(maxWidth);
+ assertThat(drawable.getIntrinsicHeight()).isEqualTo(maxHeight);
+ }
+
+ @SmallTest
+ public void testScaleDownMaxSizeWithResource() throws Exception {
+ final Icon ic = Icon.createWithResource(getContext(), R.drawable.test_too_big);
+ final BitmapDrawable drawable = (BitmapDrawable) ic.loadDrawable(mContext);
+
+ assertThat(drawable.getBitmap().getByteCount()).isAtMost(RecordingCanvas.MAX_BITMAP_SIZE);
+ }
+
+ @SmallTest
+ public void testScaleDownMaxSizeWithFile() throws Exception {
+ final Bitmap bit1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.test_too_big))
+ .getBitmap();
+ final File dir = getContext().getExternalFilesDir(null);
+ final File file1 = new File(dir, "file1-too-big.png");
+ bit1.compress(Bitmap.CompressFormat.PNG, 100,
+ new FileOutputStream(file1));
+
+ final Icon ic = Icon.createWithFilePath(file1.toString());
+ final BitmapDrawable drawable = (BitmapDrawable) ic.loadDrawable(mContext);
+
+ assertThat(drawable.getBitmap().getByteCount()).isAtMost(RecordingCanvas.MAX_BITMAP_SIZE);
+ }
+
+ @SmallTest
+ public void testScaleDownMaxSizeWithData() throws Exception {
+ final int bmpBpp = 4;
+ final Bitmap originalBits = ((BitmapDrawable) getContext().getDrawable(
+ R.drawable.test_too_big)).getBitmap();
+ final ByteArrayOutputStream ostream = new ByteArrayOutputStream(
+ originalBits.getWidth() * originalBits.getHeight() * bmpBpp);
+ originalBits.compress(Bitmap.CompressFormat.PNG, 100, ostream);
+ final byte[] pngdata = ostream.toByteArray();
+ final Icon ic = Icon.createWithData(pngdata, 0, pngdata.length);
+ final BitmapDrawable drawable = (BitmapDrawable) ic.loadDrawable(mContext);
+
+ assertThat(drawable.getBitmap().getByteCount()).isAtMost(RecordingCanvas.MAX_BITMAP_SIZE);
+ }
// ======== utils ========
diff --git a/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java b/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
index 9a202ae..1075d44 100644
--- a/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
@@ -21,8 +21,11 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -38,6 +41,8 @@
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
+import com.android.internal.R;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -50,6 +55,7 @@
import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
@Presubmit
@@ -70,6 +76,8 @@
private IFaceService mService;
@Mock
private FaceManager.AuthenticationCallback mAuthCallback;
+ @Mock
+ private FaceManager.EnrollmentCallback mEnrollmentCallback;
@Captor
private ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> mCaptor;
@@ -107,9 +115,7 @@
@Test
public void getSensorPropertiesInternal_noBinderCalls() throws RemoteException {
- verify(mService).addAuthenticatorsRegisteredCallback(mCaptor.capture());
-
- mCaptor.getValue().onAllAuthenticatorsRegistered(mProps);
+ initializeProperties();
List<FaceSensorPropertiesInternal> actual = mFaceManager.getSensorPropertiesInternal();
assertThat(actual).containsExactlyElementsIn(mProps);
@@ -148,4 +154,33 @@
verify(mAuthCallback).onAuthenticationError(eq(FACE_ERROR_HW_UNAVAILABLE), any());
}
+
+ @Test
+ public void enrollment_errorWhenFaceEnrollmentExists() throws RemoteException {
+ when(mResources.getInteger(R.integer.config_faceMaxTemplatesPerUser)).thenReturn(1);
+ when(mService.getEnrolledFaces(anyInt(), anyInt(), anyString()))
+ .thenReturn(Collections.emptyList())
+ .thenReturn(Collections.singletonList(new Face("Face" /* name */, 0 /* faceId */,
+ 0 /* deviceId */)));
+
+ initializeProperties();
+ mFaceManager.enroll(USER_ID, new byte[]{},
+ new CancellationSignal(), mEnrollmentCallback, null /* disabledFeatures */);
+
+ verify(mService).enroll(eq(USER_ID), any(), any(), any(), anyString(), any(), any(),
+ anyBoolean());
+
+ mFaceManager.enroll(USER_ID, new byte[]{},
+ new CancellationSignal(), mEnrollmentCallback, null /* disabledFeatures */);
+
+ verify(mService, atMost(1 /* maxNumberOfInvocations */)).enroll(eq(USER_ID), any(), any(),
+ any(), anyString(), any(), any(), anyBoolean());
+ verify(mEnrollmentCallback).onEnrollmentError(eq(FACE_ERROR_HW_UNAVAILABLE), anyString());
+ }
+
+ private void initializeProperties() throws RemoteException {
+ verify(mService).addAuthenticatorsRegisteredCallback(mCaptor.capture());
+
+ mCaptor.getValue().onAllAuthenticatorsRegistered(mProps);
+ }
}
diff --git a/core/tests/utiltests/src/com/android/internal/util/QuickSelectTest.java b/core/tests/utiltests/src/com/android/internal/util/QuickSelectTest.java
new file mode 100644
index 0000000..1b9d2ef
--- /dev/null
+++ b/core/tests/utiltests/src/com/android/internal/util/QuickSelectTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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.util;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for {@link QuickSelect}.
+ */
+public final class QuickSelectTest extends TestCase {
+
+ public void testQuickSelect() throws Exception {
+ test((List<Integer>) null, 0, null);
+ test(Arrays.asList(), -1, null);
+ test(Arrays.asList(), 0, null);
+ test(Arrays.asList(), 1, null);
+ test(Arrays.asList(1), -1, 1, 0, null);
+ test(Arrays.asList(1), 1, -1, 0, null);
+ test(Arrays.asList(1), 0, 1, -1, null);
+ test(Arrays.asList(1), 1, 1, 0, null);
+ test(Arrays.asList(1), 0, 1);
+ test(Arrays.asList(1), 1, null);
+ test(Arrays.asList(1, 2, 3, 4, 5), 0, 1);
+ test(Arrays.asList(1, 2, 3, 4, 5), 1, 2);
+ test(Arrays.asList(1, 2, 3, 4, 5), 2, 3);
+ test(Arrays.asList(1, 2, 3, 4, 5), 3, 4);
+ test(Arrays.asList(1, 2, 3, 4, 5), 4, 5);
+ test(Arrays.asList(1, 2, 3, 4, 5), 5, null);
+ test(Arrays.asList(7, 10, 4, 3, 20, 15, 8, 9), 2, 7);
+ test(Arrays.asList(7, 10, 4, 3, 20, 15, 8, 9), 4, 9);
+ test(Arrays.asList(7, 10, 4, 3, 20, 15, 8, 9), 7, 20);
+ test(Arrays.asList(7, 10, 4, 3, 20, 15, 8, 9), 8, null);
+ test(Arrays.asList(7, 10, 4, 3, 20, 15, 8, 9), 1, 3, 0, 3);
+ test(Arrays.asList(7, 10, 4, 3, 20, 15, 8, 9), 1, 3, 1, 4);
+ test(Arrays.asList(7, 10, 4, 3, 20, 15, 8, 9), 1, 3, 2, 10);
+ test(Arrays.asList(7, 10, 4, 3, 20, 15, 8, 9), 1, 3, 3, null);
+
+ test((int[]) null, 0, null);
+ test(new int[0], -1, null);
+ test(new int[0], 0, null);
+ test(new int[0], 1, null);
+ test(new int[] {1}, -1, 1, 0, null);
+ test(new int[] {1}, 1, -1, 0, null);
+ test(new int[] {1}, 1, 0, -1, null);
+ test(new int[] {1}, 1, 1, 0, null);
+ test(new int[] {1}, 0, 1);
+ test(new int[] {1}, 1, null);
+ test(new int[] {1, 2, 3, 4, 5}, 0, 1);
+ test(new int[] {1, 2, 3, 4, 5}, 1, 2);
+ test(new int[] {1, 2, 3, 4, 5}, 2, 3);
+ test(new int[] {1, 2, 3, 4, 5}, 3, 4);
+ test(new int[] {1, 2, 3, 4, 5}, 4, 5);
+ test(new int[] {1, 2, 3, 4, 5}, 5, null);
+ test(new int[] {7, 10, 4, 3, 20, 15, 8, 9}, 2, 7);
+ test(new int[] {7, 10, 4, 3, 20, 15, 8, 9}, 4, 9);
+ test(new int[] {7, 10, 4, 3, 20, 15, 8, 9}, 7, 20);
+ test(new int[] {7, 10, 4, 3, 20, 15, 8, 9}, 8, null);
+ test(new int[] {7, 10, 4, 3, 20, 15, 8, 9}, 1, 3, 0, 3);
+ test(new int[] {7, 10, 4, 3, 20, 15, 8, 9}, 1, 3, 1, 4);
+ test(new int[] {7, 10, 4, 3, 20, 15, 8, 9}, 1, 3, 2, 10);
+ test(new int[] {7, 10, 4, 3, 20, 15, 8, 9}, 1, 3, 3, null);
+
+ test((long[]) null, 0, null);
+ test(new long[0], -1, null);
+ test(new long[0], 0, null);
+ test(new long[0], 1, null);
+ test(new long[] {1}, -1, 1, 0, null);
+ test(new long[] {1}, 1, -1, 0, null);
+ test(new long[] {1}, 1, 0, -1, null);
+ test(new long[] {1}, 1, 1, 0, null);
+ test(new long[] {1}, 0, 1L);
+ test(new long[] {1}, 1, null);
+ test(new long[] {1, 2, 3, 4, 5}, 0, 1L);
+ test(new long[] {1, 2, 3, 4, 5}, 1, 2L);
+ test(new long[] {1, 2, 3, 4, 5}, 2, 3L);
+ test(new long[] {1, 2, 3, 4, 5}, 3, 4L);
+ test(new long[] {1, 2, 3, 4, 5}, 4, 5L);
+ test(new long[] {1, 2, 3, 4, 5}, 5, null);
+ test(new long[] {7, 10, 4, 3, 20, 15, 8, 9}, 2, 7L);
+ test(new long[] {7, 10, 4, 3, 20, 15, 8, 9}, 4, 9L);
+ test(new long[] {7, 10, 4, 3, 20, 15, 8, 9}, 7, 20L);
+ test(new long[] {7, 10, 4, 3, 20, 15, 8, 9}, 8, null);
+ test(new long[] {7, 10, 4, 3, 20, 15, 8, 9}, 1, 3, 0, 3L);
+ test(new long[] {7, 10, 4, 3, 20, 15, 8, 9}, 1, 3, 1, 4L);
+ test(new long[] {7, 10, 4, 3, 20, 15, 8, 9}, 1, 3, 2, 10L);
+ test(new long[] {7, 10, 4, 3, 20, 15, 8, 9}, 1, 3, 3, null);
+ }
+
+ private void test(List<Integer> input, int k, Integer expected) throws Exception {
+ test(input, 0, input == null ? 0 : input.size(), k, expected);
+ }
+
+ private void test(List<Integer> input, int start, int length, int k, Integer expected)
+ throws Exception {
+ try {
+ final Integer result = QuickSelect.select(input, start, length, k, Integer::compare);
+ assertEquals(expected, result);
+ } catch (IllegalArgumentException e) {
+ if (expected != null) {
+ throw new Exception(e);
+ }
+ }
+ }
+
+ private void test(int[] input, int k, Integer expected) throws Exception {
+ test(input, 0, input == null ? 0 : input.length, k, expected);
+ }
+
+ private void test(int[] input, int start, int length, int k, Integer expected)
+ throws Exception {
+ try {
+ final int result = QuickSelect.select(input, start, length, k);
+ assertEquals((int) expected, result);
+ } catch (IllegalArgumentException e) {
+ if (expected != null) {
+ throw new Exception(e);
+ }
+ }
+ }
+
+ private void test(long[] input, int k, Long expected) throws Exception {
+ test(input, 0, input == null ? 0 : input.length, k, expected);
+ }
+
+ private void test(long[] input, int start, int length, int k, Long expected) throws Exception {
+ try {
+ final long result = QuickSelect.select(input, start, length, k);
+ assertEquals((long) expected, result);
+ } catch (IllegalArgumentException e) {
+ if (expected != null) {
+ throw new Exception(e);
+ }
+ }
+ }
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 0faf62e..40cb7f2 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -122,6 +122,7 @@
<permission name="android.permission.BIND_CARRIER_SERVICES"/>
<permission name="android.permission.BIND_CELL_BROADCAST_SERVICE"/>
<permission name="android.permission.BIND_IMS_SERVICE"/>
+ <permission name="android.permission.BIND_SATELLITE_GATEWAY_SERVICE"/>
<permission name="android.permission.BIND_SATELLITE_SERVICE"/>
<permission name="android.permission.BIND_TELEPHONY_DATA_SERVICE"/>
<permission name="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE"/>
@@ -515,6 +516,10 @@
<permission name="android.permission.READ_RESTRICTED_STATS"/>
<!-- Permission required for CTS test -->
<permission name="android.permission.LOG_FOREGROUND_RESOURCE_USE"/>
+ <!-- Permission required for CTS test - CtsVoiceInteractionTestCases -->
+ <permission name="android.permission.SOUND_TRIGGER_RUN_IN_BATTERY_SAVER"/>
+ <!-- Permission required for CTS test - SatelliteManagerTest -->
+ <permission name="android.permission.SATELLITE_COMMUNICATION"/>
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index a76d74e..708feeb 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -35,6 +35,7 @@
import android.graphics.BitmapFactory;
import android.graphics.BlendMode;
import android.graphics.PorterDuff;
+import android.graphics.RecordingCanvas;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
@@ -70,6 +71,7 @@
public final class Icon implements Parcelable {
private static final String TAG = "Icon";
+ private static final boolean DEBUG = false;
/**
* An icon that was created using {@link Icon#createWithBitmap(Bitmap)}.
@@ -361,15 +363,52 @@
}
/**
+ * Resizes image if size too large for Canvas to draw
+ * @param bitmap Bitmap to be resized if size > {@link RecordingCanvas.MAX_BITMAP_SIZE}
+ * @return resized bitmap
+ */
+ private Bitmap fixMaxBitmapSize(Bitmap bitmap) {
+ if (bitmap != null && bitmap.getByteCount() > RecordingCanvas.MAX_BITMAP_SIZE) {
+ int bytesPerPixel = bitmap.getRowBytes() / bitmap.getWidth();
+ int maxNumPixels = RecordingCanvas.MAX_BITMAP_SIZE / bytesPerPixel;
+ float aspRatio = (float) bitmap.getWidth() / (float) bitmap.getHeight();
+ int newHeight = (int) Math.sqrt(maxNumPixels / aspRatio);
+ int newWidth = (int) (newHeight * aspRatio);
+
+ if (DEBUG) {
+ Log.d(TAG,
+ "Image size too large: " + bitmap.getByteCount() + ". Resizing bitmap to: "
+ + newWidth + " " + newHeight);
+ }
+
+ return scaleDownIfNecessary(bitmap, newWidth, newHeight);
+ }
+ return bitmap;
+ }
+
+ /**
+ * Resizes BitmapDrawable if size too large for Canvas to draw
+ * @param drawable Drawable to be resized if size > {@link RecordingCanvas.MAX_BITMAP_SIZE}
+ * @return resized Drawable
+ */
+ private Drawable fixMaxBitmapSize(Resources res, Drawable drawable) {
+ if (drawable instanceof BitmapDrawable) {
+ Bitmap scaledBmp = fixMaxBitmapSize(((BitmapDrawable) drawable).getBitmap());
+ return new BitmapDrawable(res, scaledBmp);
+ }
+ return drawable;
+ }
+
+ /**
* Do the heavy lifting of loading the drawable, but stop short of applying any tint.
*/
private Drawable loadDrawableInner(Context context) {
switch (mType) {
case TYPE_BITMAP:
- return new BitmapDrawable(context.getResources(), getBitmap());
+ return new BitmapDrawable(context.getResources(), fixMaxBitmapSize(getBitmap()));
case TYPE_ADAPTIVE_BITMAP:
return new AdaptiveIconDrawable(null,
- new BitmapDrawable(context.getResources(), getBitmap()));
+ new BitmapDrawable(context.getResources(), fixMaxBitmapSize(getBitmap())));
case TYPE_RESOURCE:
if (getResources() == null) {
// figure out where to load resources from
@@ -400,7 +439,8 @@
}
}
try {
- return getResources().getDrawable(getResId(), context.getTheme());
+ return fixMaxBitmapSize(getResources(),
+ getResources().getDrawable(getResId(), context.getTheme()));
} catch (RuntimeException e) {
Log.e(TAG, String.format("Unable to load resource 0x%08x from pkg=%s",
getResId(),
@@ -409,21 +449,21 @@
}
break;
case TYPE_DATA:
- return new BitmapDrawable(context.getResources(),
- BitmapFactory.decodeByteArray(getDataBytes(), getDataOffset(), getDataLength())
- );
+ return new BitmapDrawable(context.getResources(), fixMaxBitmapSize(
+ BitmapFactory.decodeByteArray(getDataBytes(), getDataOffset(),
+ getDataLength())));
case TYPE_URI:
InputStream is = getUriInputStream(context);
if (is != null) {
return new BitmapDrawable(context.getResources(),
- BitmapFactory.decodeStream(is));
+ fixMaxBitmapSize(BitmapFactory.decodeStream(is)));
}
break;
case TYPE_URI_ADAPTIVE_BITMAP:
is = getUriInputStream(context);
if (is != null) {
return new AdaptiveIconDrawable(null, new BitmapDrawable(context.getResources(),
- BitmapFactory.decodeStream(is)));
+ fixMaxBitmapSize(BitmapFactory.decodeStream(is))));
}
break;
}
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
index dcce4698..ab64f9e 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
@@ -67,7 +67,7 @@
<!-- Temporarily extending the background to show an edu text hint for opening the menu -->
<FrameLayout
- android:id="@+id/tv_pip_menu_edu_text_drawer_placeholder"
+ android:id="@+id/tv_pip_menu_edu_text_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/tv_pip"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 79e0a48..d3f3958 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -64,7 +64,11 @@
public class Bubble implements BubbleViewProvider {
private static final String TAG = "Bubble";
- public static final String KEY_APP_BUBBLE = "key_app_bubble";
+ /** A string suffix used in app bubbles' {@link #mKey}. */
+ private static final String KEY_APP_BUBBLE = "key_app_bubble";
+
+ /** Whether the bubble is an app bubble. */
+ private final boolean mIsAppBubble;
private final String mKey;
@Nullable
@@ -181,7 +185,7 @@
private PendingIntent mDeleteIntent;
/**
- * Used only for a special bubble in the stack that has the key {@link #KEY_APP_BUBBLE}.
+ * Used only for a special bubble in the stack that has {@link #mIsAppBubble} set to true.
* There can only be one of these bubbles in the stack and this intent will be populated for
* that bubble.
*/
@@ -216,24 +220,54 @@
mMainExecutor = mainExecutor;
mTaskId = taskId;
mBubbleMetadataFlagListener = listener;
+ mIsAppBubble = false;
}
- public Bubble(Intent intent,
+ private Bubble(
+ Intent intent,
UserHandle user,
@Nullable Icon icon,
+ boolean isAppBubble,
+ String key,
Executor mainExecutor) {
- mKey = KEY_APP_BUBBLE;
mGroupKey = null;
mLocusId = null;
mFlags = 0;
mUser = user;
mIcon = icon;
+ mIsAppBubble = isAppBubble;
+ mKey = key;
mShowBubbleUpdateDot = false;
mMainExecutor = mainExecutor;
mTaskId = INVALID_TASK_ID;
mAppIntent = intent;
mDesiredHeight = Integer.MAX_VALUE;
mPackageName = intent.getPackage();
+
+ }
+
+ /** Creates an app bubble. */
+ public static Bubble createAppBubble(
+ Intent intent,
+ UserHandle user,
+ @Nullable Icon icon,
+ Executor mainExecutor) {
+ return new Bubble(intent,
+ user,
+ icon,
+ /* isAppBubble= */ true,
+ /* key= */ getAppBubbleKeyForApp(intent.getPackage(), user),
+ mainExecutor);
+ }
+
+ /**
+ * Returns the key for an app bubble from an app with package name, {@code packageName} on an
+ * Android user, {@code user}.
+ */
+ public static String getAppBubbleKeyForApp(String packageName, UserHandle user) {
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(user);
+ return KEY_APP_BUBBLE + ":" + user.getIdentifier() + ":" + packageName;
}
@VisibleForTesting(visibility = PRIVATE)
@@ -241,6 +275,7 @@
final Bubbles.BubbleMetadataFlagListener listener,
final Bubbles.PendingIntentCanceledListener intentCancelListener,
Executor mainExecutor) {
+ mIsAppBubble = false;
mKey = entry.getKey();
mGroupKey = entry.getGroupKey();
mLocusId = entry.getLocusId();
@@ -815,7 +850,7 @@
}
boolean isAppBubble() {
- return KEY_APP_BUBBLE.equals(mKey);
+ return mIsAppBubble;
}
Intent getSettingsIntent(final Context context) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index c407b06..21f02b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -24,7 +24,6 @@
import static android.view.View.VISIBLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static com.android.wm.shell.bubbles.Bubble.KEY_APP_BUBBLE;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_CONTROLLER;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_GESTURE;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
@@ -1193,14 +1192,15 @@
return;
}
+ String appBubbleKey = Bubble.getAppBubbleKeyForApp(intent.getPackage(), user);
PackageManager packageManager = getPackageManagerForUser(mContext, user.getIdentifier());
- if (!isResizableActivity(intent, packageManager, KEY_APP_BUBBLE)) return;
+ if (!isResizableActivity(intent, packageManager, appBubbleKey)) return;
- Bubble existingAppBubble = mBubbleData.getBubbleInStackWithKey(KEY_APP_BUBBLE);
+ Bubble existingAppBubble = mBubbleData.getBubbleInStackWithKey(appBubbleKey);
if (existingAppBubble != null) {
BubbleViewProvider selectedBubble = mBubbleData.getSelectedBubble();
if (isStackExpanded()) {
- if (selectedBubble != null && KEY_APP_BUBBLE.equals(selectedBubble.getKey())) {
+ if (selectedBubble != null && appBubbleKey.equals(selectedBubble.getKey())) {
// App bubble is expanded, lets collapse
collapseStack();
} else {
@@ -1214,7 +1214,7 @@
}
} else {
// App bubble does not exist, lets add and expand it
- Bubble b = new Bubble(intent, user, icon, mMainExecutor);
+ Bubble b = Bubble.createAppBubble(intent, user, icon, mMainExecutor);
b.setShouldAutoExpand(true);
inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false);
}
@@ -1247,8 +1247,8 @@
}
/** Sets the app bubble's taskId which is cached for SysUI. */
- public void setAppBubbleTaskId(int taskId) {
- mImpl.mCachedState.setAppBubbleTaskId(taskId);
+ public void setAppBubbleTaskId(String key, int taskId) {
+ mImpl.mCachedState.setAppBubbleTaskId(key, taskId);
}
/**
@@ -2045,7 +2045,8 @@
private HashSet<String> mSuppressedBubbleKeys = new HashSet<>();
private HashMap<String, String> mSuppressedGroupToNotifKeys = new HashMap<>();
private HashMap<String, Bubble> mShortcutIdToBubble = new HashMap<>();
- private int mAppBubbleTaskId = INVALID_TASK_ID;
+
+ private HashMap<String, Integer> mAppBubbleTaskIds = new HashMap();
private ArrayList<Bubble> mTmpBubbles = new ArrayList<>();
@@ -2077,20 +2078,20 @@
mSuppressedBubbleKeys.clear();
mShortcutIdToBubble.clear();
- mAppBubbleTaskId = INVALID_TASK_ID;
+ mAppBubbleTaskIds.clear();
for (Bubble b : mTmpBubbles) {
mShortcutIdToBubble.put(b.getShortcutId(), b);
updateBubbleSuppressedState(b);
- if (KEY_APP_BUBBLE.equals(b.getKey())) {
- mAppBubbleTaskId = b.getTaskId();
+ if (b.isAppBubble()) {
+ mAppBubbleTaskIds.put(b.getKey(), b.getTaskId());
}
}
}
/** Sets the app bubble's taskId which is cached for SysUI. */
- synchronized void setAppBubbleTaskId(int taskId) {
- mAppBubbleTaskId = taskId;
+ synchronized void setAppBubbleTaskId(String key, int taskId) {
+ mAppBubbleTaskIds.put(key, taskId);
}
/**
@@ -2143,7 +2144,7 @@
pw.println(" suppressing: " + key);
}
- pw.print("mAppBubbleTaskId: " + mAppBubbleTaskId);
+ pw.print("mAppBubbleTaskIds: " + mAppBubbleTaskIds.values());
}
}
@@ -2205,7 +2206,7 @@
@Override
public boolean isAppBubbleTaskId(int taskId) {
- return mCachedState.mAppBubbleTaskId == taskId;
+ return mCachedState.mAppBubbleTaskIds.values().contains(taskId);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 92b969b..cc8f50e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -17,7 +17,6 @@
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
-import static com.android.wm.shell.bubbles.Bubble.KEY_APP_BUBBLE;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_DATA;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
@@ -780,7 +779,7 @@
|| !(reason == Bubbles.DISMISS_AGED
|| reason == Bubbles.DISMISS_USER_GESTURE
|| reason == Bubbles.DISMISS_RELOAD_FROM_DISK)
- || KEY_APP_BUBBLE.equals(bubble.getKey())) {
+ || bubble.isAppBubble()) {
return;
}
if (DEBUG_BUBBLE_DATA) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 684a23a..6c482c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -287,9 +287,9 @@
// The taskId is saved to use for removeTask, preventing appearance in recent tasks.
mTaskId = taskId;
- if (Bubble.KEY_APP_BUBBLE.equals(getBubbleKey())) {
+ if (mBubble != null && mBubble.isAppBubble()) {
// Let the controller know sooner what the taskId is.
- mController.setAppBubbleTaskId(mTaskId);
+ mController.setAppBubbleTaskId(mBubble.getKey(), mTaskId);
}
// With the task org, the taskAppeared callback will only happen once the task has
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index 4970fa0..56616cb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -165,6 +165,10 @@
t.remove(mGapBackgroundLeash);
mGapBackgroundLeash = null;
}
+ if (mScreenshot != null) {
+ t.remove(mScreenshot);
+ mScreenshot = null;
+ }
mHostLeash = null;
mIcon = null;
mResizingIconView = null;
@@ -324,6 +328,8 @@
if (!mShown && mIsResizing && !mOldBounds.equals(mResizingBounds)) {
if (mScreenshotAnimator != null && mScreenshotAnimator.isRunning()) {
mScreenshotAnimator.cancel();
+ } else if (mScreenshot != null) {
+ t.remove(mScreenshot);
}
mTempRect.set(mOldBounds);
@@ -340,6 +346,8 @@
if (!mShown && mIsResizing && !mOldBounds.equals(mResizingBounds)) {
if (mScreenshotAnimator != null && mScreenshotAnimator.isRunning()) {
mScreenshotAnimator.cancel();
+ } else if (mScreenshot != null) {
+ t.remove(mScreenshot);
}
mScreenshot = screenshot;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index d04ce15..b6216b3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -146,13 +146,17 @@
t.apply();
// execute the runnable if non-null after WCT is applied to finish resizing pip
- if (mPipFinishResizeWCTRunnable != null) {
- mPipFinishResizeWCTRunnable.run();
- mPipFinishResizeWCTRunnable = null;
- }
+ maybePerformFinishResizeCallback();
}
};
+ private void maybePerformFinishResizeCallback() {
+ if (mPipFinishResizeWCTRunnable != null) {
+ mPipFinishResizeWCTRunnable.run();
+ mPipFinishResizeWCTRunnable = null;
+ }
+ }
+
// These callbacks are called on the update thread
private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
new PipAnimationController.PipAnimationCallback() {
@@ -619,11 +623,11 @@
* Removes PiP immediately.
*/
public void removePip() {
- if (!mPipTransitionState.isInPip() || mToken == null) {
+ if (!mPipTransitionState.isInPip() || mToken == null || mLeash == null) {
ProtoLog.wtf(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: Not allowed to removePip in current state"
- + " mState=%d mToken=%s", TAG, mPipTransitionState.getTransitionState(),
- mToken);
+ + " mState=%d mToken=%s mLeash=%s", TAG,
+ mPipTransitionState.getTransitionState(), mToken, mLeash);
return;
}
@@ -1007,6 +1011,7 @@
return;
}
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ mPipTransitionController.onFixedRotationFinished();
clearWaitForFixedRotation();
return;
}
@@ -1527,6 +1532,9 @@
if (snapshotSurface != null) {
mSyncTransactionQueue.queue(wct);
mSyncTransactionQueue.runInSync(t -> {
+ // reset the pinch gesture
+ maybePerformFinishResizeCallback();
+
// Scale the snapshot from its pre-resize bounds to the post-resize bounds.
mSurfaceTransactionHelper.scale(t, snapshotSurface, preResizeBounds,
snapshotDest);
@@ -1606,6 +1614,10 @@
if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
mSplitScreenOptional.ifPresent(splitScreenController ->
splitScreenController.enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct));
+ } else if (direction == TRANSITION_DIRECTION_LEAVE_PIP) {
+ // when leaving PiP we can call the callback without sync
+ maybePerformFinishResizeCallback();
+ mTaskOrganizer.applyTransaction(wct);
} else {
mTaskOrganizer.applySyncTransaction(wct, mPipFinishResizeWCTCallback);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 5755a10..99cb6f7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -409,9 +409,31 @@
@Override
public void onFixedRotationStarted() {
+ fadeEnteredPipIfNeed(false /* show */);
+ }
+
+ @Override
+ public void onFixedRotationFinished() {
+ fadeEnteredPipIfNeed(true /* show */);
+ }
+
+ private void fadeEnteredPipIfNeed(boolean show) {
// The transition with this fixed rotation may be handled by other handler before reaching
// PipTransition, so we cannot do this in #startAnimation.
- if (mPipTransitionState.getTransitionState() == ENTERED_PIP && !mHasFadeOut) {
+ if (!mPipTransitionState.hasEnteredPip()) {
+ return;
+ }
+ if (show && mHasFadeOut) {
+ // If there is a pending transition, then let startAnimation handle it. And if it is
+ // handled, mHasFadeOut will be set to false and this runnable will be no-op. Otherwise
+ // make sure the PiP will reshow, e.g. swipe-up with fixed rotation (fade-out) but
+ // return to the current app (only finish the recent transition).
+ mTransitions.runOnIdle(() -> {
+ if (mHasFadeOut && mPipTransitionState.hasEnteredPip()) {
+ fadeExistingPip(true /* show */);
+ }
+ });
+ } else if (!show && !mHasFadeOut) {
// Fade out the existing PiP to avoid jump cut during seamless rotation.
fadeExistingPip(false /* show */);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index ff7ab8b..949d6f5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -123,6 +123,10 @@
public void onFixedRotationStarted() {
}
+ /** Called when the fixed rotation finished. */
+ public void onFixedRotationFinished() {
+ }
+
public PipTransitionController(
@NonNull ShellInit shellInit,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java
index 222307f..5f6b3fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipAction.java
@@ -31,6 +31,12 @@
abstract class TvPipAction {
+ /**
+ * Extras key for adding a boolean to the {@link Notification.Action} to differentiate custom
+ * from system actions, most importantly to identify custom close actions.
+ **/
+ public static final String EXTRA_IS_PIP_CUSTOM_ACTION = "EXTRA_IS_PIP_CUSTOM_ACTION";
+
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"ACTION_"}, value = {
ACTION_FULLSCREEN,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java
index bca27a5..977aad4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipCustomAction.java
@@ -86,7 +86,7 @@
Bundle extras = new Bundle();
extras.putCharSequence(Notification.EXTRA_PICTURE_CONTENT_DESCRIPTION,
mRemoteAction.getContentDescription());
- extras.putBoolean(Notification.EXTRA_CONTAINS_CUSTOM_VIEW, true);
+ extras.putBoolean(TvPipAction.EXTRA_IS_PIP_CUSTOM_ACTION, true);
builder.addExtras(extras);
builder.setSemanticAction(isCloseAction()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java
index 6eef225..f86f987 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuEduTextDrawer.java
@@ -23,6 +23,7 @@
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE;
+import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.drawable.Drawable;
@@ -115,6 +116,10 @@
scheduleLifecycleEvents();
}
+ int getEduTextDrawerHeight() {
+ return getVisibility() == GONE ? 0 : getHeight();
+ }
+
private void scheduleLifecycleEvents() {
final int startScrollDelay = mContext.getResources().getInteger(
R.integer.pip_edu_text_start_scroll_delay);
@@ -226,20 +231,41 @@
.start();
// Start animation to close the drawer by animating its height to 0
- final ValueAnimator heightAnimation = ValueAnimator.ofInt(getHeight(), 0);
- heightAnimation.setDuration(eduTextSlideExitAnimationDuration);
- heightAnimation.setInterpolator(TvPipInterpolators.BROWSE);
- heightAnimation.addUpdateListener(animator -> {
+ final ValueAnimator heightAnimator = ValueAnimator.ofInt(getHeight(), 0);
+ heightAnimator.setDuration(eduTextSlideExitAnimationDuration);
+ heightAnimator.setInterpolator(TvPipInterpolators.BROWSE);
+ heightAnimator.addUpdateListener(animator -> {
final ViewGroup.LayoutParams params = getLayoutParams();
params.height = (int) animator.getAnimatedValue();
setLayoutParams(params);
- if (params.height == 0) {
- setVisibility(GONE);
+ });
+ heightAnimator.addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(@NonNull Animator animator) {
+ }
+
+ @Override
+ public void onAnimationEnd(@NonNull Animator animator) {
+ onCloseEduTextAnimationEnd();
+ }
+
+ @Override
+ public void onAnimationCancel(@NonNull Animator animator) {
+ onCloseEduTextAnimationEnd();
+ }
+
+ @Override
+ public void onAnimationRepeat(@NonNull Animator animator) {
}
});
- heightAnimation.start();
+ heightAnimator.start();
- mListener.onCloseEduText();
+ mListener.onCloseEduTextAnimationStart();
+ }
+
+ public void onCloseEduTextAnimationEnd() {
+ setVisibility(GONE);
+ mListener.onCloseEduTextAnimationEnd();
}
/**
@@ -270,11 +296,8 @@
* A listener for edu text drawer event states.
*/
interface Listener {
- /**
- * The edu text closing impacts the size of the Picture-in-Picture window and influences
- * how it is positioned on the screen.
- */
- void onCloseEduText();
+ void onCloseEduTextAnimationStart();
+ void onCloseEduTextAnimationEnd();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index 6eb719b..d076418 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -57,7 +57,8 @@
* A View that represents Pip Menu on TV. It's responsible for displaying the Pip menu actions from
* the TvPipActionsProvider as well as the buttons for manually moving the PiP.
*/
-public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.Listener {
+public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.Listener,
+ TvPipMenuEduTextDrawer.Listener {
private static final String TAG = "TvPipMenuView";
private final TvPipMenuView.Listener mListener;
@@ -76,6 +77,7 @@
private final View mDimLayer;
private final TvPipMenuEduTextDrawer mEduTextDrawer;
+ private final ViewGroup mEduTextContainer;
private final int mPipMenuOuterSpace;
private final int mPipMenuBorderWidth;
@@ -139,9 +141,9 @@
mPipMenuBorderWidth = context.getResources()
.getDimensionPixelSize(R.dimen.pip_menu_border_width);
- mEduTextDrawer = new TvPipMenuEduTextDrawer(mContext, mainHandler, mListener);
- ((FrameLayout) findViewById(R.id.tv_pip_menu_edu_text_drawer_placeholder))
- .addView(mEduTextDrawer);
+ mEduTextDrawer = new TvPipMenuEduTextDrawer(mContext, mainHandler, this);
+ mEduTextContainer = (ViewGroup) findViewById(R.id.tv_pip_menu_edu_text_container);
+ mEduTextContainer.addView(mEduTextDrawer);
}
void onPipTransitionToTargetBoundsStarted(Rect targetBounds) {
@@ -235,11 +237,13 @@
* pip menu when it gains focus.
*/
private void updatePipFrameBounds() {
- final ViewGroup.LayoutParams pipFrameParams = mPipFrameView.getLayoutParams();
- if (pipFrameParams != null) {
- pipFrameParams.width = mCurrentPipBounds.width() + 2 * mPipMenuBorderWidth;
- pipFrameParams.height = mCurrentPipBounds.height() + 2 * mPipMenuBorderWidth;
- mPipFrameView.setLayoutParams(pipFrameParams);
+ if (mPipFrameView.getVisibility() == VISIBLE) {
+ final ViewGroup.LayoutParams pipFrameParams = mPipFrameView.getLayoutParams();
+ if (pipFrameParams != null) {
+ pipFrameParams.width = mCurrentPipBounds.width() + 2 * mPipMenuBorderWidth;
+ pipFrameParams.height = mCurrentPipBounds.height() + 2 * mPipMenuBorderWidth;
+ mPipFrameView.setLayoutParams(pipFrameParams);
+ }
}
final ViewGroup.LayoutParams pipViewParams = mPipView.getLayoutParams();
@@ -262,7 +266,7 @@
Rect getPipMenuContainerBounds(Rect pipBounds) {
final Rect menuUiBounds = new Rect(pipBounds);
menuUiBounds.inset(-mPipMenuOuterSpace, -mPipMenuOuterSpace);
- menuUiBounds.bottom += mEduTextDrawer.getHeight();
+ menuUiBounds.bottom += mEduTextDrawer.getEduTextDrawerHeight();
return menuUiBounds;
}
@@ -406,6 +410,17 @@
}
@Override
+ public void onCloseEduTextAnimationStart() {
+ mListener.onCloseEduText();
+ }
+
+ @Override
+ public void onCloseEduTextAnimationEnd() {
+ mPipFrameView.setVisibility(GONE);
+ mEduTextContainer.setVisibility(GONE);
+ }
+
+ @Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() == ACTION_UP) {
@@ -551,7 +566,7 @@
}
}
- interface Listener extends TvPipMenuEduTextDrawer.Listener {
+ interface Listener {
void onBackPress();
@@ -573,5 +588,11 @@
* has lost focus.
*/
void onPipWindowFocusChanged(boolean focused);
+
+ /**
+ * The edu text closing impacts the size of the Picture-in-Picture window and influences
+ * how it is positioned on the screen.
+ */
+ void onCloseEduText();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index c9b3a1a..c4a0e9c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -32,6 +32,8 @@
Consts.TAG_WM_SHELL),
WM_SHELL_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM_SHELL),
+ WM_SHELL_RECENTS_TRANSITION(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
+ Consts.TAG_WM_SHELL),
WM_SHELL_DRAG_AND_DROP(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM_SHELL),
WM_SHELL_STARTING_WINDOW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 44d7e6d..b554872 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -47,7 +47,9 @@
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.util.TransitionUtil;
@@ -96,6 +98,8 @@
void startRecentsTransition(PendingIntent intent, Intent fillIn, Bundle options,
IApplicationThread appThread, IRecentsAnimationRunner listener) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsTransitionHandler.startRecentsTransition");
// only care about latest one.
mAnimApp = appThread;
WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -116,7 +120,7 @@
mixer.setRecentsTransition(transition);
}
if (transition == null) {
- controller.cancel();
+ controller.cancel("startRecentsTransition");
return;
}
controller.setTransition(transition);
@@ -127,6 +131,7 @@
public WindowContainerTransaction handleRequest(IBinder transition,
TransitionRequestInfo request) {
// do not directly handle requests. Only entry point should be via startRecentsTransition
+ Slog.e(TAG, "RecentsTransitionHandler.handleRequest: Unexpected transition request");
return null;
}
@@ -143,11 +148,17 @@
SurfaceControl.Transaction finishTransaction,
Transitions.TransitionFinishCallback finishCallback) {
final int controllerIdx = findController(transition);
- if (controllerIdx < 0) return false;
+ if (controllerIdx < 0) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsTransitionHandler.startAnimation: no controller found");
+ return false;
+ }
final RecentsController controller = mControllers.get(controllerIdx);
Transitions.setRunningRemoteTransitionDelegate(mAnimApp);
mAnimApp = null;
if (!controller.start(info, startTransaction, finishTransaction, finishCallback)) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsTransitionHandler.startAnimation: failed to start animation");
return false;
}
return true;
@@ -168,7 +179,7 @@
SurfaceControl.Transaction finishTransaction) {
final int idx = findController(transition);
if (idx < 0) return;
- mControllers.get(idx).cancel();
+ mControllers.get(idx).cancel("onTransitionConsumed");
}
/** There is only one of these and it gets reset on finish. */
@@ -213,39 +224,45 @@
RecentsController(IRecentsAnimationRunner listener) {
mListener = listener;
- mDeathHandler = () -> mExecutor.execute(() -> {
- if (mListener == null) return;
- if (mFinishCB != null) {
- finish(mWillFinishToHome, false /* leaveHint */);
- }
- });
+ mDeathHandler = () -> {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsController.DeathRecipient: binder died");
+ finish(mWillFinishToHome, false /* leaveHint */);
+ };
try {
mListener.asBinder().linkToDeath(mDeathHandler, 0 /* flags */);
} catch (RemoteException e) {
+ Slog.e(TAG, "RecentsController: failed to link to death", e);
mListener = null;
}
}
void setTransition(IBinder transition) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsController.setTransition: id=%s", transition);
mTransition = transition;
}
- void cancel() {
+ void cancel(String reason) {
// restoring (to-home = false) involves submitting more WM changes, so by default, use
// toHome = true when canceling.
- cancel(true /* toHome */);
+ cancel(true /* toHome */, reason);
}
- void cancel(boolean toHome) {
+ void cancel(boolean toHome, String reason) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsController.cancel: toHome=%b reason=%s", toHome, reason);
if (mListener != null) {
try {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsController.cancel: calling onAnimationCanceled");
mListener.onAnimationCanceled(null, null);
} catch (RemoteException e) {
Slog.e(TAG, "Error canceling recents animation", e);
}
}
if (mFinishCB != null) {
- finish(toHome, false /* userLeave */);
+ finishInner(toHome, false /* userLeave */);
} else {
cleanUp();
}
@@ -272,6 +289,8 @@
}
}
try {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsController.cancel: calling onAnimationCanceled with snapshots");
mListener.onAnimationCanceled(taskIds, snapshots);
} catch (RemoteException e) {
Slog.e(TAG, "Error canceling recents animation", e);
@@ -281,6 +300,7 @@
}
void cleanUp() {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "RecentsController.cleanup");
if (mListener != null && mDeathHandler != null) {
mListener.asBinder().unlinkToDeath(mDeathHandler, 0 /* flags */);
mDeathHandler = null;
@@ -304,6 +324,7 @@
boolean start(TransitionInfo info, SurfaceControl.Transaction t,
SurfaceControl.Transaction finishT, Transitions.TransitionFinishCallback finishCB) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "RecentsController.start");
if (mListener == null || mTransition == null) {
cleanUp();
return false;
@@ -363,6 +384,8 @@
info.getChanges().size() - i, info, t, mLeashMap);
apps.add(target);
if (TransitionUtil.isClosingType(change.getMode())) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ " adding pausing taskId=%d", taskInfo.taskId);
// raise closing (pausing) task to "above" layer so it isn't covered
t.setLayer(target.leash, info.getChanges().size() * 3 - i);
mPausingTasks.add(new TaskState(change, target.leash));
@@ -377,19 +400,23 @@
} else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
// do nothing
} else if (TransitionUtil.isOpeningType(change.getMode())) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ " adding opening taskId=%d", taskInfo.taskId);
mOpeningTasks.add(new TaskState(change, target.leash));
}
}
}
t.apply();
try {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsController.start: calling onAnimationStart");
mListener.onAnimationStart(this,
apps.toArray(new RemoteAnimationTarget[apps.size()]),
wallpapers.toArray(new RemoteAnimationTarget[wallpapers.size()]),
new Rect(0, 0, 0, 0), new Rect());
} catch (RemoteException e) {
Slog.e(TAG, "Error starting recents animation", e);
- cancel();
+ cancel("onAnimationStart() failed");
}
return true;
}
@@ -398,14 +425,19 @@
void merge(TransitionInfo info, SurfaceControl.Transaction t,
Transitions.TransitionFinishCallback finishCallback) {
if (mFinishCB == null) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsController.merge: skip, no finish callback");
// This was no-op'd (likely a repeated start) and we've already sent finish.
return;
}
if (info.getType() == TRANSIT_SLEEP) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsController.merge: transit_sleep");
// A sleep event means we need to stop animations immediately, so cancel here.
- cancel();
+ cancel("transit_sleep");
return;
}
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "RecentsController.merge");
ArrayList<TransitionInfo.Change> openingTasks = null;
ArrayList<TransitionInfo.Change> closingTasks = null;
mOpeningSeparateHome = false;
@@ -422,7 +454,7 @@
&& taskInfo.configuration.windowConfiguration.isAlwaysOnTop()) {
// Tasks that are always on top (e.g. bubbles), will handle their own transition
// as they are on top of everything else. So cancel the merge here.
- cancel();
+ cancel("task #" + taskInfo.taskId + " is always_on_top");
return;
}
hasTaskChange = hasTaskChange || taskInfo != null;
@@ -453,7 +485,7 @@
// Finish recents animation if the display is changed, so the default
// transition handler can play the animation such as rotation effect.
if (change.hasFlags(TransitionInfo.FLAG_IS_DISPLAY)) {
- cancel(mWillFinishToHome);
+ cancel(mWillFinishToHome, "display change");
return;
}
// Don't consider order-only changes as changing apps.
@@ -497,7 +529,10 @@
+ " something unexpected: " + change.getTaskInfo().taskId);
continue;
}
- mPausingTasks.add(mOpeningTasks.remove(openingIdx));
+ final TaskState openingTask = mOpeningTasks.remove(openingIdx);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ " pausing opening taskId=%d", openingTask.mTaskInfo.taskId);
+ mPausingTasks.add(openingTask);
didMergeThings = true;
}
}
@@ -514,7 +549,10 @@
// Something is showing/opening a previously-pausing app.
appearedTargets[i] = TransitionUtil.newTarget(
change, layer, mPausingTasks.get(pausingIdx).mLeash);
- mOpeningTasks.add(mPausingTasks.remove(pausingIdx));
+ final TaskState pausingTask = mPausingTasks.remove(pausingIdx);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ " opening pausing taskId=%d", pausingTask.mTaskInfo.taskId);
+ mOpeningTasks.add(pausingTask);
// Setup hides opening tasks initially, so make it visible again (since we
// are already showing it).
t.show(change.getLeash());
@@ -527,6 +565,8 @@
final int rootIdx = TransitionUtil.rootIndexFor(change, mInfo);
t.reparent(appearedTargets[i].leash, mInfo.getRoot(rootIdx).getLeash());
t.setLayer(appearedTargets[i].leash, layer);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ " opening new taskId=%d", appearedTargets[i].taskId);
mOpeningTasks.add(new TaskState(change, appearedTargets[i].leash));
}
}
@@ -544,7 +584,7 @@
+ foundRecentsClosing);
if (foundRecentsClosing) {
mWillFinishToHome = false;
- cancel(false /* toHome */);
+ cancel(false /* toHome */, "didn't merge");
}
return;
}
@@ -552,13 +592,15 @@
t.apply();
// not using the incoming anim-only surfaces
info.releaseAnimSurfaces();
- finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
if (appearedTargets == null) return;
try {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsController.merge: calling onTasksAppeared");
mListener.onTasksAppeared(appearedTargets);
} catch (RemoteException e) {
Slog.e(TAG, "Error sending appeared tasks to recents animation", e);
}
+ finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
}
/** For now, just set-up a jump-cut to the new activity. */
@@ -577,6 +619,8 @@
@Override
public TaskSnapshot screenshotTask(int taskId) {
try {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsController.screenshotTask: taskId=%d", taskId);
return ActivityTaskManager.getService().takeTaskSnapshot(taskId);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to screenshot task", e);
@@ -587,12 +631,19 @@
@Override
public void setInputConsumerEnabled(boolean enabled) {
mExecutor.execute(() -> {
- if (mFinishCB == null || !enabled) return;
+ if (mFinishCB == null || !enabled) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsController.setInputConsumerEnabled: skip, cb?=%b enabled?=%b",
+ mFinishCB != null, enabled);
+ return;
+ }
// transient launches don't receive focus automatically. Since we are taking over
// the gesture now, take focus explicitly.
// This also moves recents back to top if the user gestured before a switch
// animation finished.
try {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsController.setInputConsumerEnabled: set focus to recents");
ActivityTaskManager.getService().setFocusedTask(mRecentsTaskId);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to set focused task", e);
@@ -607,6 +658,8 @@
@Override
public void setFinishTaskTransaction(int taskId,
PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsController.setFinishTaskTransaction: taskId=%d", taskId);
mExecutor.execute(() -> {
if (mFinishCB == null) return;
mPipTransaction = finishTransaction;
@@ -624,6 +677,9 @@
Slog.e(TAG, "Duplicate call to finish");
return;
}
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsController.finishInner: toHome=%b userLeaveHint=%b willFinishToHome=%b",
+ toHome, sendUserLeaveHint, mWillFinishToHome);
final Transitions.TransitionFinishCallback finishCB = mFinishCB;
mFinishCB = null;
@@ -635,6 +691,7 @@
else wct.restoreTransientOrder(mRecentsTask);
}
if (!toHome && !mWillFinishToHome && mPausingTasks != null && mState == STATE_NORMAL) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " returning to app");
// The gesture is returning to the pausing-task(s) rather than continuing with
// recents, so end the transition by moving the app back to the top (and also
// re-showing it's task).
@@ -647,6 +704,7 @@
wct.restoreTransientOrder(mRecentsTask);
}
} else if (toHome && mOpeningSeparateHome && mPausingTasks != null) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " 3p launching home");
// Special situation where 3p launcher was changed during recents (this happens
// during tapltests...). Here we get both "return to home" AND "home opening".
// This is basically going home, but we have to restore the recents and home order.
@@ -665,6 +723,7 @@
wct.restoreTransientOrder(mRecentsTask);
}
} else {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " normal finish");
// The general case: committing to recents, going home, or switching tasks.
for (int i = 0; i < mOpeningTasks.size(); ++i) {
t.show(mOpeningTasks.get(i).mTaskSurface);
@@ -721,6 +780,8 @@
*/
@Override
public void detachNavigationBarFromApp(boolean moveHomeToTop) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsController.detachNavigationBarFromApp");
mExecutor.execute(() -> {
if (mTransition == null) return;
try {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 8e8faca..e8a6a15 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -127,7 +127,7 @@
if (decoration == null) {
createWindowDecoration(taskInfo, taskSurface, startT, finishT);
} else {
- decoration.relayout(taskInfo, startT, finishT);
+ decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */);
}
}
@@ -139,7 +139,7 @@
final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (decoration == null) return;
- decoration.relayout(taskInfo, startT, finishT);
+ decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */);
}
@Override
@@ -192,7 +192,8 @@
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
windowDecoration.setDragPositioningCallback(taskPositioner);
windowDecoration.setDragDetector(touchEventListener.mDragDetector);
- windowDecoration.relayout(taskInfo, startT, finishT);
+ windowDecoration.relayout(taskInfo, startT, finishT,
+ false /* applyStartTransactionOnDraw */);
setupCaptionColor(taskInfo, windowDecoration);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index dfde7e6..116af70 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -90,15 +90,15 @@
@Override
void relayout(RunningTaskInfo taskInfo) {
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- relayout(taskInfo, t, t);
- mSyncQueue.runInSync(transaction -> {
- transaction.merge(t);
- t.close();
- });
+ // Use |applyStartTransactionOnDraw| so that the transaction (that applies task crop) is
+ // synced with the buffer transaction (that draws the View). Both will be shown on screen
+ // at the same, whereas applying them independently causes flickering. See b/270202228.
+ relayout(taskInfo, t, t, true /* applyStartTransactionOnDraw */);
}
void relayout(RunningTaskInfo taskInfo,
- SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) {
+ SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
+ boolean applyStartTransactionOnDraw) {
final int shadowRadiusID = taskInfo.isFocused
? R.dimen.freeform_decor_shadow_focused_thickness
: R.dimen.freeform_decor_shadow_unfocused_thickness;
@@ -115,6 +115,7 @@
mRelayoutParams.mLayoutResId = R.layout.caption_window_decor;
mRelayoutParams.mCaptionHeightId = R.dimen.freeform_decor_caption_height;
mRelayoutParams.mShadowRadiusId = shadowRadiusID;
+ mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
// After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index e137bc4..49a5eac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -247,7 +247,7 @@
if (decoration == null) {
createWindowDecoration(taskInfo, taskSurface, startT, finishT);
} else {
- decoration.relayout(taskInfo, startT, finishT);
+ decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */);
}
}
@@ -259,7 +259,7 @@
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (decoration == null) return;
- decoration.relayout(taskInfo, startT, finishT);
+ decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */);
}
@Override
@@ -781,7 +781,8 @@
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
windowDecoration.setDragPositioningCallback(taskPositioner);
windowDecoration.setDragDetector(touchEventListener.mDragDetector);
- windowDecoration.relayout(taskInfo, startT, finishT);
+ windowDecoration.relayout(taskInfo, startT, finishT,
+ false /* applyStartTransactionOnDraw */);
incrementEventReceiverTasks(taskInfo.displayId);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 67e99d7..af3fb0e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -173,15 +173,15 @@
@Override
void relayout(ActivityManager.RunningTaskInfo taskInfo) {
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- relayout(taskInfo, t, t);
- mSyncQueue.runInSync(transaction -> {
- transaction.merge(t);
- t.close();
- });
+ // Use |applyStartTransactionOnDraw| so that the transaction (that applies task crop) is
+ // synced with the buffer transaction (that draws the View). Both will be shown on screen
+ // at the same, whereas applying them independently causes flickering. See b/270202228.
+ relayout(taskInfo, t, t, true /* applyStartTransactionOnDraw */);
}
void relayout(ActivityManager.RunningTaskInfo taskInfo,
- SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) {
+ SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
+ boolean applyStartTransactionOnDraw) {
final int shadowRadiusID = taskInfo.isFocused
? R.dimen.freeform_decor_shadow_focused_thickness
: R.dimen.freeform_decor_shadow_unfocused_thickness;
@@ -216,6 +216,7 @@
mRelayoutParams.mLayoutResId = windowDecorLayoutId;
mRelayoutParams.mCaptionHeightId = R.dimen.freeform_decor_caption_height;
mRelayoutParams.mShadowRadiusId = shadowRadiusID;
+ mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
// After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 8b35694..9a1b4ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -238,30 +238,6 @@
startT.setWindowCrop(mCaptionContainerSurface, captionWidth, captionHeight)
.show(mCaptionContainerSurface);
- if (mCaptionWindowManager == null) {
- // Put caption under a container surface because ViewRootImpl sets the destination frame
- // of windowless window layers and BLASTBufferQueue#update() doesn't support offset.
- mCaptionWindowManager = new WindowlessWindowManager(
- mTaskInfo.getConfiguration(), mCaptionContainerSurface,
- null /* hostInputToken */);
- }
-
- // Caption view
- mCaptionWindowManager.setConfiguration(taskConfig);
- final WindowManager.LayoutParams lp =
- new WindowManager.LayoutParams(captionWidth, captionHeight,
- WindowManager.LayoutParams.TYPE_APPLICATION,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
- lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
- lp.setTrustedOverlay();
- if (mViewHost == null) {
- mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay,
- mCaptionWindowManager);
- mViewHost.setView(outResult.mRootView, lp);
- } else {
- mViewHost.relayout(lp);
- }
-
if (ViewRootImpl.CAPTION_ON_SHELL) {
outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused);
@@ -287,6 +263,36 @@
.show(mTaskSurface);
finishT.setPosition(mTaskSurface, taskPosition.x, taskPosition.y)
.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
+
+ if (mCaptionWindowManager == null) {
+ // Put caption under a container surface because ViewRootImpl sets the destination frame
+ // of windowless window layers and BLASTBufferQueue#update() doesn't support offset.
+ mCaptionWindowManager = new WindowlessWindowManager(
+ mTaskInfo.getConfiguration(), mCaptionContainerSurface,
+ null /* hostInputToken */);
+ }
+
+ // Caption view
+ mCaptionWindowManager.setConfiguration(taskConfig);
+ final WindowManager.LayoutParams lp =
+ new WindowManager.LayoutParams(captionWidth, captionHeight,
+ WindowManager.LayoutParams.TYPE_APPLICATION,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
+ lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
+ lp.setTrustedOverlay();
+ if (mViewHost == null) {
+ mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay,
+ mCaptionWindowManager);
+ if (params.mApplyStartTransactionOnDraw) {
+ mViewHost.getRootSurfaceControl().applyTransactionOnDraw(startT);
+ }
+ mViewHost.setView(outResult.mRootView, lp);
+ } else {
+ if (params.mApplyStartTransactionOnDraw) {
+ mViewHost.getRootSurfaceControl().applyTransactionOnDraw(startT);
+ }
+ mViewHost.relayout(lp);
+ }
}
/**
@@ -411,6 +417,8 @@
int mCaptionX;
int mCaptionY;
+ boolean mApplyStartTransactionOnDraw;
+
void reset() {
mLayoutResId = Resources.ID_NULL;
mCaptionHeightId = Resources.ID_NULL;
@@ -419,6 +427,8 @@
mCaptionX = 0;
mCaptionY = 0;
+
+ mApplyStartTransactionOnDraw = false;
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 919bf06..4a55429 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -16,8 +16,6 @@
package com.android.wm.shell.bubbles;
-import static com.android.wm.shell.bubbles.Bubble.KEY_APP_BUBBLE;
-
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -185,7 +183,10 @@
Intent appBubbleIntent = new Intent(mContext, BubblesTestActivity.class);
appBubbleIntent.setPackage(mContext.getPackageName());
- mAppBubble = new Bubble(appBubbleIntent, new UserHandle(1), mock(Icon.class),
+ mAppBubble = Bubble.createAppBubble(
+ appBubbleIntent,
+ new UserHandle(1),
+ mock(Icon.class),
mMainExecutor);
mPositioner = new TestableBubblePositioner(mContext,
@@ -1101,14 +1102,15 @@
@Test
public void test_removeAppBubble_skipsOverflow() {
+ String appBubbleKey = mAppBubble.getKey();
mBubbleData.notificationEntryUpdated(mAppBubble, true /* suppressFlyout*/,
false /* showInShade */);
- assertThat(mBubbleData.getBubbleInStackWithKey(KEY_APP_BUBBLE)).isEqualTo(mAppBubble);
+ assertThat(mBubbleData.getBubbleInStackWithKey(appBubbleKey)).isEqualTo(mAppBubble);
- mBubbleData.dismissBubbleWithKey(KEY_APP_BUBBLE, Bubbles.DISMISS_USER_GESTURE);
+ mBubbleData.dismissBubbleWithKey(appBubbleKey, Bubbles.DISMISS_USER_GESTURE);
- assertThat(mBubbleData.getOverflowBubbleWithKey(KEY_APP_BUBBLE)).isNull();
- assertThat(mBubbleData.getBubbleInStackWithKey(KEY_APP_BUBBLE)).isNull();
+ assertThat(mBubbleData.getOverflowBubbleWithKey(appBubbleKey)).isNull();
+ assertThat(mBubbleData.getBubbleInStackWithKey(appBubbleKey)).isNull();
}
private void verifyUpdateReceived() {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index c1e53a9..fc4bfd97 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -33,6 +33,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.content.Context;
@@ -42,6 +43,7 @@
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.util.DisplayMetrics;
+import android.view.AttachedSurfaceControl;
import android.view.Display;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
@@ -97,6 +99,8 @@
@Mock
private SurfaceControlViewHost mMockSurfaceControlViewHost;
@Mock
+ private AttachedSurfaceControl mMockRootSurfaceControl;
+ @Mock
private TestView mMockView;
@Mock
private WindowContainerTransaction mMockWindowContainerTransaction;
@@ -129,6 +133,8 @@
doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory)
.create(any(), any(), any());
+ when(mMockSurfaceControlViewHost.getRootSurfaceControl())
+ .thenReturn(mMockRootSurfaceControl);
}
@Test
@@ -461,6 +467,43 @@
verify(mMockSurfaceControlStartT).show(captionContainerSurface);
}
+ @Test
+ public void testRelayout_applyTransactionInSyncWithDraw() {
+ final Display defaultDisplay = mock(Display.class);
+ doReturn(defaultDisplay).when(mMockDisplayController)
+ .getDisplay(Display.DEFAULT_DISPLAY);
+
+ final SurfaceControl decorContainerSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder decorContainerSurfaceBuilder =
+ createMockSurfaceControlBuilder(decorContainerSurface);
+ mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+ final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder captionContainerSurfaceBuilder =
+ createMockSurfaceControlBuilder(captionContainerSurface);
+ mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
+
+ final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+ mMockSurfaceControlTransactions.add(t);
+ final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+ new ActivityManager.TaskDescription.Builder()
+ .setBackgroundColor(Color.YELLOW);
+ final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setTaskDescriptionBuilder(taskDescriptionBuilder)
+ .setBounds(TASK_BOUNDS)
+ .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
+ .setVisible(true)
+ .build();
+ taskInfo.isFocused = true;
+ taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
+ final SurfaceControl taskSurface = mock(SurfaceControl.class);
+ final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+
+ windowDecor.relayout(taskInfo, true /* applyStartTransactionOnDraw */);
+
+ verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT);
+ }
+
private TestWindowDecoration createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) {
return new TestWindowDecoration(InstrumentationRegistry.getInstrumentation().getContext(),
@@ -516,6 +559,12 @@
@Override
void relayout(ActivityManager.RunningTaskInfo taskInfo) {
+ relayout(taskInfo, false /* applyStartTransactionOnDraw */);
+ }
+
+ void relayout(ActivityManager.RunningTaskInfo taskInfo,
+ boolean applyStartTransactionOnDraw) {
+ mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT,
mMockWindowContainerTransaction, mMockView, mRelayoutResult);
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 70c36a5..34af1f9 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -399,7 +399,7 @@
"libharfbuzz_ng",
"libimage_io",
"libjpeg",
- "libjpegrecoverymap",
+ "libultrahdr",
"liblog",
"libminikin",
"libz",
diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp
index 8874ef1..69418b0 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.cpp
+++ b/libs/hwui/jni/YuvToJpegEncoder.cpp
@@ -298,39 +298,39 @@
}
///////////////////////////////////////////////////////////////////////////////
-using namespace android::jpegrecoverymap;
+using namespace android::ultrahdr;
-jpegr_color_gamut P010Yuv420ToJpegREncoder::findColorGamut(JNIEnv* env, int aDataSpace) {
+ultrahdr_color_gamut P010Yuv420ToJpegREncoder::findColorGamut(JNIEnv* env, int aDataSpace) {
switch (aDataSpace & ADataSpace::STANDARD_MASK) {
case ADataSpace::STANDARD_BT709:
- return jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
+ return ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
case ADataSpace::STANDARD_DCI_P3:
- return jpegr_color_gamut::JPEGR_COLORGAMUT_P3;
+ return ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_P3;
case ADataSpace::STANDARD_BT2020:
- return jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+ return ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
default:
jclass IllegalArgumentException = env->FindClass("java/lang/IllegalArgumentException");
env->ThrowNew(IllegalArgumentException,
"The requested color gamut is not supported by JPEG/R.");
}
- return jpegr_color_gamut::JPEGR_COLORGAMUT_UNSPECIFIED;
+ return ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
}
-jpegr_transfer_function P010Yuv420ToJpegREncoder::findHdrTransferFunction(JNIEnv* env,
+ultrahdr_transfer_function P010Yuv420ToJpegREncoder::findHdrTransferFunction(JNIEnv* env,
int aDataSpace) {
switch (aDataSpace & ADataSpace::TRANSFER_MASK) {
case ADataSpace::TRANSFER_ST2084:
- return jpegr_transfer_function::JPEGR_TF_PQ;
+ return ultrahdr_transfer_function::ULTRAHDR_TF_PQ;
case ADataSpace::TRANSFER_HLG:
- return jpegr_transfer_function::JPEGR_TF_HLG;
+ return ultrahdr_transfer_function::ULTRAHDR_TF_HLG;
default:
jclass IllegalArgumentException = env->FindClass("java/lang/IllegalArgumentException");
env->ThrowNew(IllegalArgumentException,
"The requested HDR transfer function is not supported by JPEG/R.");
}
- return jpegr_transfer_function::JPEGR_TF_UNSPECIFIED;
+ return ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED;
}
bool P010Yuv420ToJpegREncoder::encode(JNIEnv* env,
@@ -344,13 +344,13 @@
return false;
}
- jpegr_color_gamut hdrColorGamut = findColorGamut(env, hdrColorSpace);
- jpegr_color_gamut sdrColorGamut = findColorGamut(env, sdrColorSpace);
- jpegr_transfer_function hdrTransferFunction = findHdrTransferFunction(env, hdrColorSpace);
+ ultrahdr_color_gamut hdrColorGamut = findColorGamut(env, hdrColorSpace);
+ ultrahdr_color_gamut sdrColorGamut = findColorGamut(env, sdrColorSpace);
+ ultrahdr_transfer_function hdrTransferFunction = findHdrTransferFunction(env, hdrColorSpace);
- if (hdrColorGamut == jpegr_color_gamut::JPEGR_COLORGAMUT_UNSPECIFIED
- || sdrColorGamut == jpegr_color_gamut::JPEGR_COLORGAMUT_UNSPECIFIED
- || hdrTransferFunction == jpegr_transfer_function::JPEGR_TF_UNSPECIFIED) {
+ if (hdrColorGamut == ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED
+ || sdrColorGamut == ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED
+ || hdrTransferFunction == ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED) {
return false;
}
diff --git a/libs/hwui/jni/YuvToJpegEncoder.h b/libs/hwui/jni/YuvToJpegEncoder.h
index d22a26c..8ef7805 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.h
+++ b/libs/hwui/jni/YuvToJpegEncoder.h
@@ -2,7 +2,7 @@
#define _ANDROID_GRAPHICS_YUV_TO_JPEG_ENCODER_H_
#include <android/data_space.h>
-#include <jpegrecoverymap/jpegr.h>
+#include <ultrahdr/jpegr.h>
extern "C" {
#include "jpeglib.h"
@@ -103,7 +103,7 @@
* @param aDataSpace data space defined in data_space.h.
* @return color gamut for JPEG/R.
*/
- static android::jpegrecoverymap::jpegr_color_gamut findColorGamut(JNIEnv* env, int aDataSpace);
+ static android::ultrahdr::ultrahdr_color_gamut findColorGamut(JNIEnv* env, int aDataSpace);
/** Map data space (defined in DataSpace.java and data_space.h) to the transfer function
* used in JPEG/R
@@ -112,7 +112,7 @@
* @param aDataSpace data space defined in data_space.h.
* @return color gamut for JPEG/R.
*/
- static android::jpegrecoverymap::jpegr_transfer_function findHdrTransferFunction(
+ static android::ultrahdr::ultrahdr_transfer_function findHdrTransferFunction(
JNIEnv* env, int aDataSpace);
};
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 7e1bbe3..29e8716 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -293,12 +293,16 @@
* Set the component name of the manifest-declared {@link android.content.BroadcastReceiver}
* class that should receive media buttons. This allows restarting playback after the session
* has been stopped. If your app is started in this way an {@link Intent#ACTION_MEDIA_BUTTON}
- * intent will be sent to the broadcast receiver.
- * <p>
- * Note: The given {@link android.content.BroadcastReceiver} should belong to the same package
- * as the context that was given when creating {@link MediaSession}.
+ * intent will be sent to the broadcast receiver. On apps targeting Android U and above, this
+ * will throw an {@link IllegalArgumentException} if the provided {@link ComponentName} does not
+ * resolve to an existing {@link android.content.BroadcastReceiver broadcast receiver}.
+ *
+ * <p>Note: The given {@link android.content.BroadcastReceiver} should belong to the same
+ * package as the context that was given when creating {@link MediaSession}.
*
* @param broadcastReceiver the component name of the BroadcastReceiver class
+ * @throws IllegalArgumentException if {@code broadcastReceiver} does not exist on apps
+ * targeting Android U and above
*/
public void setMediaButtonBroadcastReceiver(@Nullable ComponentName broadcastReceiver) {
try {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 7581b5c..8b9c8b9 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -40,9 +40,7 @@
import com.android.credentialmanager.createflow.CreateCredentialScreen
import com.android.credentialmanager.createflow.hasContentToDisplay
import com.android.credentialmanager.getflow.GetCredentialScreen
-import com.android.credentialmanager.getflow.GetGenericCredentialScreen
import com.android.credentialmanager.getflow.hasContentToDisplay
-import com.android.credentialmanager.getflow.isFallbackScreen
import com.android.credentialmanager.ui.theme.PlatformTheme
@ExperimentalMaterialApi
@@ -161,19 +159,11 @@
providerActivityLauncher = launcher
)
} else if (getCredentialUiState != null && hasContentToDisplay(getCredentialUiState)) {
- if (isFallbackScreen(getCredentialUiState)) {
- GetGenericCredentialScreen(
- viewModel = viewModel,
- getCredentialUiState = getCredentialUiState,
- providerActivityLauncher = launcher
- )
- } else {
- GetCredentialScreen(
- viewModel = viewModel,
- getCredentialUiState = getCredentialUiState,
- providerActivityLauncher = launcher
- )
- }
+ GetCredentialScreen(
+ viewModel = viewModel,
+ getCredentialUiState = getCredentialUiState,
+ providerActivityLauncher = launcher
+ )
} else {
Log.d(Constants.LOG_TAG, "UI wasn't able to render neither get nor create flow")
reportInstantiationErrorAndFinishActivity(credManRepo)
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index f08bbf4..57035d4 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -50,9 +50,7 @@
import androidx.credentials.CreateCredentialRequest
import androidx.credentials.CreateCustomCredentialRequest
import androidx.credentials.CreatePasswordRequest
-import androidx.credentials.CredentialOption
import androidx.credentials.CreatePublicKeyCredentialRequest
-import androidx.credentials.GetPublicKeyCredentialOption
import androidx.credentials.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
import androidx.credentials.provider.Action
import androidx.credentials.provider.AuthenticationAction
@@ -194,20 +192,8 @@
originName: String?,
): com.android.credentialmanager.getflow.RequestDisplayInfo? {
val getCredentialRequest = requestInfo?.getCredentialRequest ?: return null
- val preferImmediatelyAvailableCredentials = getCredentialRequest.credentialOptions.any {
- val credentialOptionJetpack = CredentialOption.createFrom(
- it.type,
- it.credentialRetrievalData,
- it.credentialRetrievalData,
- it.isSystemProviderRequired,
- it.allowedProviders,
- )
- if (credentialOptionJetpack is GetPublicKeyCredentialOption) {
- credentialOptionJetpack.preferImmediatelyAvailableCredentials
- } else {
- false
- }
- }
+ val preferImmediatelyAvailableCredentials = getCredentialRequest.data.getBoolean(
+ "androidx.credentials.BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS")
val preferUiBrandingComponentName =
getCredentialRequest.data.getParcelable(
"androidx.credentials.BUNDLE_KEY_PREFER_UI_BRANDING_COMPONENT_NAME",
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index 516c1a3..74933c9 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -200,18 +200,31 @@
authenticationEntryList.isEmpty()) || (sortedUserNameToCredentialEntryList.isEmpty() &&
authenticationEntryList.size == 1)
item {
- HeadlineText(
- text = stringResource(
- if (hasSingleEntry) {
- if (sortedUserNameToCredentialEntryList.firstOrNull()
- ?.sortedCredentialEntryList?.first()?.credentialType
- == CredentialType.PASSKEY
- ) R.string.get_dialog_title_use_passkey_for
- else R.string.get_dialog_title_use_sign_in_for
- } else R.string.get_dialog_title_choose_sign_in_for,
- requestDisplayInfo.appName
- ),
- )
+ if (requestDisplayInfo.preferIdentityDocUi) {
+ HeadlineText(
+ text = stringResource(
+ if (hasSingleEntry) {
+ R.string.get_dialog_title_use_info_on
+ } else {
+ R.string.get_dialog_title_choose_option_for
+ },
+ requestDisplayInfo.appName
+ ),
+ )
+ } else {
+ HeadlineText(
+ text = stringResource(
+ if (hasSingleEntry) {
+ if (sortedUserNameToCredentialEntryList.firstOrNull()
+ ?.sortedCredentialEntryList?.first()?.credentialType
+ == CredentialType.PASSKEY
+ ) R.string.get_dialog_title_use_passkey_for
+ else R.string.get_dialog_title_use_sign_in_for
+ } else R.string.get_dialog_title_choose_sign_in_for,
+ requestDisplayInfo.appName
+ ),
+ )
+ }
}
item { Divider(thickness = 24.dp, color = Color.Transparent) }
item {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetGenericCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetGenericCredentialComponents.kt
deleted file mode 100644
index 57fefbe..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetGenericCredentialComponents.kt
+++ /dev/null
@@ -1,174 +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.credentialmanager.getflow
-
-import androidx.activity.compose.ManagedActivityResultLauncher
-import androidx.activity.result.ActivityResult
-import androidx.activity.result.IntentSenderRequest
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.material3.Divider
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.asImageBitmap
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.dp
-import androidx.core.graphics.drawable.toBitmap
-import com.android.compose.rememberSystemUiController
-import com.android.credentialmanager.CredentialSelectorViewModel
-import com.android.credentialmanager.R
-import com.android.credentialmanager.common.BaseEntry
-import com.android.credentialmanager.common.ProviderActivityState
-import com.android.credentialmanager.common.ui.ConfirmButton
-import com.android.credentialmanager.common.ui.CredentialContainerCard
-import com.android.credentialmanager.common.ui.CtaButtonRow
-import com.android.credentialmanager.common.ui.HeadlineIcon
-import com.android.credentialmanager.common.ui.HeadlineText
-import com.android.credentialmanager.common.ui.LargeLabelTextOnSurfaceVariant
-import com.android.credentialmanager.common.ui.ModalBottomSheet
-import com.android.credentialmanager.common.ui.SheetContainerCard
-import com.android.credentialmanager.common.ui.setBottomSheetSystemBarsColor
-import com.android.credentialmanager.logging.GetCredentialEvent
-import com.android.internal.logging.UiEventLogger
-
-
-@Composable
-fun GetGenericCredentialScreen(
- viewModel: CredentialSelectorViewModel,
- getCredentialUiState: GetCredentialUiState,
- providerActivityLauncher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
-) {
- val sysUiController = rememberSystemUiController()
- setBottomSheetSystemBarsColor(sysUiController)
- ModalBottomSheet(
- sheetContent = {
- when (viewModel.uiState.providerActivityState) {
- ProviderActivityState.NOT_APPLICABLE -> {
- PrimarySelectionCardGeneric(
- requestDisplayInfo = getCredentialUiState.requestDisplayInfo,
- providerDisplayInfo = getCredentialUiState.providerDisplayInfo,
- providerInfoList = getCredentialUiState.providerInfoList,
- onEntrySelected = viewModel::getFlowOnEntrySelected,
- onConfirm = viewModel::getFlowOnConfirmEntrySelected,
- onLog = { viewModel.logUiEvent(it) },
- )
- viewModel.uiMetrics.log(GetCredentialEvent
- .CREDMAN_GET_CRED_SCREEN_PRIMARY_SELECTION)
- }
- ProviderActivityState.READY_TO_LAUNCH -> {
- // Launch only once per providerActivityState change so that the provider
- // UI will not be accidentally launched twice.
- LaunchedEffect(viewModel.uiState.providerActivityState) {
- viewModel.launchProviderUi(providerActivityLauncher)
- }
- viewModel.uiMetrics.log(GetCredentialEvent
- .CREDMAN_GET_CRED_PROVIDER_ACTIVITY_READY_TO_LAUNCH)
- }
- ProviderActivityState.PENDING -> {
- // Hide our content when the provider activity is active.
- viewModel.uiMetrics.log(GetCredentialEvent
- .CREDMAN_GET_CRED_PROVIDER_ACTIVITY_PENDING)
- }
- }
- },
- onDismiss = viewModel::onUserCancel,
- )
-}
-
-@Composable
-fun PrimarySelectionCardGeneric(
- requestDisplayInfo: RequestDisplayInfo,
- providerDisplayInfo: ProviderDisplayInfo,
- providerInfoList: List<ProviderInfo>,
- onEntrySelected: (BaseEntry) -> Unit,
- onConfirm: () -> Unit,
- onLog: @Composable (UiEventLogger.UiEventEnum) -> Unit,
-) {
- val sortedUserNameToCredentialEntryList =
- providerDisplayInfo.sortedUserNameToCredentialEntryList
- val totalEntriesCount = sortedUserNameToCredentialEntryList
- .flatMap { it.sortedCredentialEntryList }.size
- SheetContainerCard {
- // When only one provider (not counting the remote-only provider) exists, display that
- // provider's icon + name up top.
- if (providerInfoList.size <= 2) { // It's only possible to be the single provider case
- // if we are started with no more than 2 providers.
- val nonRemoteProviderList = providerInfoList.filter(
- { it.credentialEntryList.isNotEmpty() || it.authenticationEntryList.isNotEmpty() }
- )
- if (nonRemoteProviderList.size == 1) {
- val providerInfo = nonRemoteProviderList.firstOrNull() // First should always work
- // but just to be safe.
- if (providerInfo != null) {
- item {
- HeadlineIcon(
- bitmap = providerInfo.icon.toBitmap().asImageBitmap(),
- tint = Color.Unspecified,
- )
- }
- item { Divider(thickness = 4.dp, color = Color.Transparent) }
- item { LargeLabelTextOnSurfaceVariant(text = providerInfo.displayName) }
- item { Divider(thickness = 16.dp, color = Color.Transparent) }
- }
- }
- }
-
- item {
- HeadlineText(
- text = stringResource(
- if (totalEntriesCount == 1) {
- R.string.get_dialog_title_use_info_on
- } else {
- R.string.get_dialog_title_choose_option_for
- },
- requestDisplayInfo.appName
- ),
- )
- }
- item { Divider(thickness = 24.dp, color = Color.Transparent) }
- item {
- CredentialContainerCard {
- Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
- sortedUserNameToCredentialEntryList.forEach {
- // TODO(b/275375861): fallback UI merges entries by account names.
- // Need a strategy to be able to show all entries.
- CredentialEntryRow(
- credentialEntryInfo = it.sortedCredentialEntryList.first(),
- onEntrySelected = onEntrySelected,
- enforceOneLine = true,
- )
- }
- }
- }
- }
- item { Divider(thickness = 24.dp, color = Color.Transparent) }
- item {
- if (totalEntriesCount == 1) {
- CtaButtonRow(
- rightButton = {
- ConfirmButton(
- stringResource(R.string.get_dialog_button_label_continue),
- onClick = onConfirm
- )
- }
- )
- }
- }
- }
- onLog(GetCredentialEvent.CREDMAN_GET_CRED_PRIMARY_SELECTION_CARD)
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index a4a163b..716f474 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -41,10 +41,6 @@
!state.requestDisplayInfo.preferImmediatelyAvailableCredentials)
}
-internal fun isFallbackScreen(state: GetCredentialUiState): Boolean {
- return state.requestDisplayInfo.preferIdentityDocUi
-}
-
internal fun findAutoSelectEntry(providerDisplayInfo: ProviderDisplayInfo): CredentialEntryInfo? {
if (providerDisplayInfo.authenticationEntryList.isNotEmpty()) {
return null
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 78d93bd..751fbaa 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -634,6 +634,9 @@
<uses-permission android:name="android.permission.MANAGE_HOTWORD_DETECTION" />
<uses-permission android:name="android.permission.BIND_HOTWORD_DETECTION_SERVICE" />
+ <!-- Permission required for CTS test - CtsVoiceInteractionTestCases -->
+ <uses-permission android:name="android.permission.SOUND_TRIGGER_RUN_IN_BATTERY_SAVER"/>
+
<uses-permission android:name="android.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE" />
<!-- Permission required for CTS test - KeyguardLockedStateApiTest -->
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
index 3eb7fd8..23f16f2 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
@@ -18,6 +18,7 @@
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.fonts.Font
+import android.graphics.fonts.FontVariationAxis
import android.graphics.text.PositionedGlyphs
import android.text.Layout
import android.text.TextPaint
@@ -211,8 +212,15 @@
run.baseX[i] = MathUtils.lerp(run.baseX[i], run.targetX[i], progress)
run.baseY[i] = MathUtils.lerp(run.baseY[i], run.targetY[i], progress)
}
- run.fontRuns.forEach {
- it.baseFont = fontInterpolator.lerp(it.baseFont, it.targetFont, progress)
+ run.fontRuns.forEach { fontRun ->
+ fontRun.baseFont =
+ fontInterpolator.lerp(fontRun.baseFont, fontRun.targetFont, progress)
+ val tmpFontVariationsArray = mutableListOf<FontVariationAxis>()
+ fontRun.baseFont.axes.forEach {
+ tmpFontVariationsArray.add(FontVariationAxis(it.tag, it.styleValue))
+ }
+ basePaint.fontVariationSettings =
+ FontVariationAxis.toFontVariationSettings(tmpFontVariationsArray)
}
}
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt
index 1786d13..91c0a5b 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt
@@ -19,17 +19,11 @@
val maxHeight: Float = 0f,
val pixelDensity: Float = 1f,
var color: Int = Color.WHITE,
- val opacity: Int = RIPPLE_DEFAULT_ALPHA,
- val sparkleStrength: Float = RIPPLE_SPARKLE_STRENGTH,
+ val opacity: Int = RippleShader.RIPPLE_DEFAULT_ALPHA,
+ val sparkleStrength: Float = RippleShader.RIPPLE_SPARKLE_STRENGTH,
// Null means it uses default fade parameter values.
val baseRingFadeParams: RippleShader.FadeParams? = null,
val sparkleRingFadeParams: RippleShader.FadeParams? = null,
val centerFillFadeParams: RippleShader.FadeParams? = null,
val shouldDistort: Boolean = true
-) {
- companion object {
- const val RIPPLE_SPARKLE_STRENGTH: Float = 0.3f
- const val RIPPLE_DEFAULT_COLOR: Int = 0xffffffff.toInt()
- const val RIPPLE_DEFAULT_ALPHA: Int = 115 // full opacity is 255.
- }
-}
+)
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt
index b5b6037..7e56f4b 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt
@@ -60,6 +60,10 @@
const val DEFAULT_CENTER_FILL_FADE_OUT_START = 0f
const val DEFAULT_CENTER_FILL_FADE_OUT_END = 0.6f
+ const val RIPPLE_SPARKLE_STRENGTH: Float = 0.3f
+ const val RIPPLE_DEFAULT_COLOR: Int = 0xffffffff.toInt()
+ const val RIPPLE_DEFAULT_ALPHA: Int = 115 // full opacity is 255.
+
private const val SHADER_UNIFORMS =
"""
uniform vec2 in_center;
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
index ef5ad43..b899127 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
@@ -72,9 +72,9 @@
this.rippleShape = rippleShape
rippleShader = RippleShader(rippleShape)
- rippleShader.color = RippleAnimationConfig.RIPPLE_DEFAULT_COLOR
+ rippleShader.color = RippleShader.RIPPLE_DEFAULT_COLOR
rippleShader.rawProgress = 0f
- rippleShader.sparkleStrength = RippleAnimationConfig.RIPPLE_SPARKLE_STRENGTH
+ rippleShader.sparkleStrength = RippleShader.RIPPLE_SPARKLE_STRENGTH
rippleShader.pixelDensity = resources.displayMetrics.density
ripplePaint.shader = rippleShader
@@ -209,7 +209,7 @@
*
* The alpha value of the color will be applied to the ripple. The alpha range is [0-255].
*/
- fun setColor(color: Int, alpha: Int = RippleAnimationConfig.RIPPLE_DEFAULT_ALPHA) {
+ fun setColor(color: Int, alpha: Int = RippleShader.RIPPLE_DEFAULT_ALPHA) {
rippleShader.color = ColorUtils.setAlphaComponent(color, alpha)
}
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetectorTest.kt
index a5f832a..ff150c8c 100644
--- a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetectorTest.kt
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetectorTest.kt
@@ -38,7 +38,7 @@
}
@Test
- fun `No violations`() {
+ fun noViolations() {
lint()
.files(
*LEGITIMATE_FILES,
@@ -51,7 +51,7 @@
}
@Test
- fun `Violation - domain depends on ui`() {
+ fun violation_domainDependsOnUi() {
lint()
.files(
*LEGITIMATE_FILES,
@@ -86,7 +86,7 @@
}
@Test
- fun `Violation - ui depends on data`() {
+ fun violation_uiDependsOnData() {
lint()
.files(
*LEGITIMATE_FILES,
@@ -121,7 +121,7 @@
}
@Test
- fun `Violation - shared depends on all other layers`() {
+ fun violation_sharedDependsOnAllOtherLayers() {
lint()
.files(
*LEGITIMATE_FILES,
@@ -166,7 +166,7 @@
}
@Test
- fun `Violation - data depends on domain`() {
+ fun violation_dataDependsOnDomain() {
lint()
.files(
*LEGITIMATE_FILES,
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index eaf3229..34adcc7 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -32,6 +32,12 @@
import com.android.systemui.plugins.PluginLifecycleManager
import com.android.systemui.plugins.PluginListener
import com.android.systemui.plugins.PluginManager
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+import com.android.systemui.plugins.log.LogMessage
+import com.android.systemui.plugins.log.LogMessageImpl
+import com.android.systemui.plugins.log.MessageInitializer
+import com.android.systemui.plugins.log.MessagePrinter
import com.android.systemui.util.Assert
import java.io.PrintWriter
import java.util.concurrent.ConcurrentHashMap
@@ -40,7 +46,6 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
-private const val DEBUG = true
private val KEY_TIMESTAMP = "appliedTimestamp"
private fun <TKey, TVal> ConcurrentHashMap<TKey, TVal>.concurrentGetOrPut(
@@ -55,6 +60,32 @@
return result ?: value
}
+private val TMP_MESSAGE: LogMessage by lazy { LogMessageImpl.Factory.create() }
+
+private inline fun LogBuffer?.tryLog(
+ tag: String,
+ level: LogLevel,
+ messageInitializer: MessageInitializer,
+ noinline messagePrinter: MessagePrinter,
+ ex: Throwable? = null,
+) {
+ if (this != null) {
+ // Wrap messagePrinter to convert it from crossinline to noinline
+ this.log(tag, level, messageInitializer, messagePrinter, ex)
+ } else {
+ messageInitializer(TMP_MESSAGE)
+ val msg = messagePrinter(TMP_MESSAGE)
+ when (level) {
+ LogLevel.VERBOSE -> Log.v(tag, msg, ex)
+ LogLevel.DEBUG -> Log.d(tag, msg, ex)
+ LogLevel.INFO -> Log.i(tag, msg, ex)
+ LogLevel.WARNING -> Log.w(tag, msg, ex)
+ LogLevel.ERROR -> Log.e(tag, msg, ex)
+ LogLevel.WTF -> Log.wtf(tag, msg, ex)
+ }
+ }
+}
+
/** ClockRegistry aggregates providers and plugins */
open class ClockRegistry(
val context: Context,
@@ -66,8 +97,9 @@
val handleAllUsers: Boolean,
defaultClockProvider: ClockProvider,
val fallbackClockId: ClockId = DEFAULT_CLOCK_ID,
+ val logBuffer: LogBuffer? = null,
val keepAllLoaded: Boolean,
- val subTag: String,
+ subTag: String,
) {
private val TAG = "${ClockRegistry::class.simpleName} ($subTag)"
interface ClockChangeListener {
@@ -113,9 +145,11 @@
}
if (manager != info.manager) {
- Log.e(
+ logBuffer.tryLog(
TAG,
- "Clock Id conflict on load: $id is registered to another provider"
+ LogLevel.ERROR,
+ { str1 = id },
+ { "Clock Id conflict on load: $str1 is double registered" }
)
continue
}
@@ -138,9 +172,11 @@
val id = clock.clockId
val info = availableClocks[id]
if (info?.manager != manager) {
- Log.e(
+ logBuffer.tryLog(
TAG,
- "Clock Id conflict on unload: $id is registered to another provider"
+ LogLevel.ERROR,
+ { str1 = id },
+ { "Clock Id conflict on unload: $str1 is double registered" }
)
continue
}
@@ -211,7 +247,7 @@
ClockSettings.deserialize(json)
} catch (ex: Exception) {
- Log.e(TAG, "Failed to parse clock settings", ex)
+ logBuffer.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to parse clock settings" }, ex)
null
}
settings = result
@@ -240,7 +276,7 @@
)
}
} catch (ex: Exception) {
- Log.e(TAG, "Failed to set clock settings", ex)
+ logBuffer.tryLog(TAG, LogLevel.ERROR, {}, { "Failed to set clock settings" }, ex)
}
settings = value
}
@@ -400,46 +436,55 @@
}
private fun onConnected(clockId: ClockId) {
- if (DEBUG) {
- Log.i(TAG, "Connected $clockId")
- }
-
+ logBuffer.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Connected $str1" })
if (currentClockId == clockId) {
- if (DEBUG) {
- Log.i(TAG, "Current clock ($clockId) was connected")
- }
+ logBuffer.tryLog(
+ TAG,
+ LogLevel.INFO,
+ { str1 = clockId },
+ { "Current clock ($str1) was connected" }
+ )
}
}
private fun onLoaded(clockId: ClockId) {
- if (DEBUG) {
- Log.i(TAG, "Loaded $clockId")
- }
+ logBuffer.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Loaded $str1" })
if (currentClockId == clockId) {
- Log.i(TAG, "Current clock ($clockId) was loaded")
+ logBuffer.tryLog(
+ TAG,
+ LogLevel.INFO,
+ { str1 = clockId },
+ { "Current clock ($str1) was loaded" }
+ )
triggerOnCurrentClockChanged()
}
}
private fun onUnloaded(clockId: ClockId) {
- if (DEBUG) {
- Log.i(TAG, "Unloaded $clockId")
- }
+ logBuffer.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Unloaded $str1" })
if (currentClockId == clockId) {
- Log.w(TAG, "Current clock ($clockId) was unloaded")
+ logBuffer.tryLog(
+ TAG,
+ LogLevel.WARNING,
+ { str1 = clockId },
+ { "Current clock ($str1) was unloaded" }
+ )
triggerOnCurrentClockChanged()
}
}
private fun onDisconnected(clockId: ClockId) {
- if (DEBUG) {
- Log.i(TAG, "Disconnected $clockId")
- }
+ logBuffer.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Disconnected $str1" })
if (currentClockId == clockId) {
- Log.w(TAG, "Current clock ($clockId) was disconnected")
+ logBuffer.tryLog(
+ TAG,
+ LogLevel.WARNING,
+ { str1 = clockId },
+ { "Current clock ($str1) was disconnected" }
+ )
}
}
@@ -466,12 +511,27 @@
if (isEnabled && clockId.isNotEmpty()) {
val clock = createClock(clockId)
if (clock != null) {
- if (DEBUG) {
- Log.i(TAG, "Rendering clock $clockId")
- }
+ logBuffer.tryLog(
+ TAG,
+ LogLevel.INFO,
+ { str1 = clockId },
+ { "Rendering clock $str1" }
+ )
return clock
+ } else if (availableClocks.containsKey(clockId)) {
+ logBuffer.tryLog(
+ TAG,
+ LogLevel.WARNING,
+ { str1 = clockId },
+ { "Clock $str1 not loaded; using default" }
+ )
} else {
- Log.e(TAG, "Clock $clockId not found; using default")
+ logBuffer.tryLog(
+ TAG,
+ LogLevel.ERROR,
+ { str1 = clockId },
+ { "Clock $str1 not found; using default" }
+ )
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 3ec3b5c..6ca7f12 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -62,10 +62,7 @@
private val defaultLineSpacing = resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale)
override val events: DefaultClockEvents
- override lateinit var animations: DefaultClockAnimations
- private set
-
- override val config = ClockConfig(hasCustomPositionUpdatedAnimation = true)
+ override val config = ClockConfig()
init {
val parent = FrameLayout(ctx)
@@ -84,13 +81,13 @@
clocks = listOf(smallClock.view, largeClock.view)
events = DefaultClockEvents()
- animations = DefaultClockAnimations(0f, 0f)
events.onLocaleChanged(Locale.getDefault())
}
override fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) {
largeClock.recomputePadding(null)
- animations = DefaultClockAnimations(dozeFraction, foldFraction)
+ largeClock.animations = LargeClockAnimations(largeClock.view, dozeFraction, foldFraction)
+ smallClock.animations = DefaultClockAnimations(smallClock.view, dozeFraction, foldFraction)
events.onColorPaletteChanged(resources)
events.onTimeZoneChanged(TimeZone.getDefault())
smallClock.events.onTimeTick()
@@ -115,6 +112,9 @@
view.logBuffer = value
}
+ override var animations: DefaultClockAnimations = DefaultClockAnimations(view, 0f, 0f)
+ internal set
+
init {
if (seedColor != null) {
currentColor = seedColor!!
@@ -170,6 +170,12 @@
view: AnimatableClockView,
seedColor: Int?,
) : DefaultClockFaceController(view, seedColor) {
+ override val config = ClockFaceConfig(hasCustomPositionUpdatedAnimation = true)
+
+ init {
+ animations = LargeClockAnimations(view, 0f, 0f)
+ }
+
override fun recomputePadding(targetRegion: Rect?) {
// We center the view within the targetRegion instead of within the parent
// view by computing the difference and adding that to the padding.
@@ -220,7 +226,8 @@
}
}
- inner class DefaultClockAnimations(
+ open inner class DefaultClockAnimations(
+ val view: AnimatableClockView,
dozeFraction: Float,
foldFraction: Float,
) : ClockAnimations {
@@ -229,34 +236,40 @@
init {
if (foldState.isActive) {
- clocks.forEach { it.animateFoldAppear(false) }
+ view.animateFoldAppear(false)
} else {
- clocks.forEach { it.animateDoze(dozeState.isActive, false) }
+ view.animateDoze(dozeState.isActive, false)
}
}
override fun enter() {
if (!dozeState.isActive) {
- clocks.forEach { it.animateAppearOnLockscreen() }
+ view.animateAppearOnLockscreen()
}
}
- override fun charge() = clocks.forEach { it.animateCharge { dozeState.isActive } }
+ override fun charge() = view.animateCharge { dozeState.isActive }
override fun fold(fraction: Float) {
val (hasChanged, hasJumped) = foldState.update(fraction)
if (hasChanged) {
- clocks.forEach { it.animateFoldAppear(!hasJumped) }
+ view.animateFoldAppear(!hasJumped)
}
}
override fun doze(fraction: Float) {
val (hasChanged, hasJumped) = dozeState.update(fraction)
if (hasChanged) {
- clocks.forEach { it.animateDoze(dozeState.isActive, !hasJumped) }
+ view.animateDoze(dozeState.isActive, !hasJumped)
}
}
+ }
+ inner class LargeClockAnimations(
+ view: AnimatableClockView,
+ dozeFraction: Float,
+ foldFraction: Float,
+ ) : DefaultClockAnimations(view, dozeFraction, foldFraction) {
override fun onPositionUpdated(fromRect: Rect, toRect: Rect, fraction: Float) {
largeClock.moveForSplitShade(fromRect, toRect, fraction)
}
diff --git a/packages/SystemUI/docs/dagger.md b/packages/SystemUI/docs/dagger.md
index 9b4c21e..4a6240b 100644
--- a/packages/SystemUI/docs/dagger.md
+++ b/packages/SystemUI/docs/dagger.md
@@ -108,20 +108,13 @@
### Using injection with Fragments
-Fragments are created as part of the FragmentManager, so they need to be
-setup so the manager knows how to create them. To do that, add a method
-to com.android.systemui.fragments.FragmentService$FragmentCreator that
-returns your fragment class. That is all that is required, once the method
-exists, FragmentService will automatically pick it up and use injection
-whenever your fragment needs to be created.
+Fragments are created as part of the FragmentManager, so injectable Fragments need to be registered
+so the manager knows how to create them. This is done via
+[FragmentService#addFragmentInstantiationProvider](../src/com/android/systemui/fragments/FragmentService.java).
+Pass it the class of your fragment and a `Provider` for your fragment at some time before your
+Fragment is accessed.
-```java
-public interface FragmentCreator {
- NavigationBarFragment createNavigationBar();
-}
-```
-
-If you need to create your fragment (i.e. for the add or replace transaction),
+When you need to create your fragment (i.e. for the add or replace transaction),
then the FragmentHostManager can do this for you.
```java
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
index 05630e7..8ef2d80 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -69,9 +69,6 @@
/** Events that clocks may need to respond to */
val events: ClockEvents
- /** Triggers for various animations */
- val animations: ClockAnimations
-
/** Initializes various rendering parameters. If never called, provides reasonable defaults. */
fun initialize(
resources: Resources,
@@ -79,8 +76,10 @@
foldFraction: Float,
) {
events.onColorPaletteChanged(resources)
- animations.doze(dozeFraction)
- animations.fold(foldFraction)
+ smallClock.animations.doze(dozeFraction)
+ largeClock.animations.doze(dozeFraction)
+ smallClock.animations.fold(foldFraction)
+ largeClock.animations.fold(foldFraction)
smallClock.events.onTimeTick()
largeClock.events.onTimeTick()
}
@@ -100,6 +99,9 @@
/** Events specific to this clock face */
val events: ClockFaceEvents
+ /** Triggers for various animations */
+ val animations: ClockAnimations
+
/** Some clocks may log debug information */
var logBuffer: LogBuffer?
}
@@ -192,13 +194,6 @@
/** Render configuration for the full clock. Modifies the way systemUI behaves with this clock. */
data class ClockConfig(
- /**
- * Whether this clock has a custom position update animation. If true, the keyguard will call
- * `onPositionUpdated` to notify the clock of a position update animation. If false, a default
- * animation will be used (e.g. a simple translation).
- */
- val hasCustomPositionUpdatedAnimation: Boolean = false,
-
/** Transition to AOD should move smartspace like large clock instead of small clock */
val useAlternateSmartspaceAODTransition: Boolean = false,
@@ -213,6 +208,13 @@
/** Call to check whether the clock consumes weather data */
val hasCustomWeatherDataDisplay: Boolean = false,
+
+ /**
+ * Whether this clock has a custom position update animation. If true, the keyguard will call
+ * `onPositionUpdated` to notify the clock of a position update animation. If false, a default
+ * animation will be used (e.g. a simple translation).
+ */
+ val hasCustomPositionUpdatedAnimation: Boolean = false,
)
/** Structure for keeping clock-specific settings */
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 10bb00c..a8ed843 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -1,156 +1,13 @@
-# Preserve line number information for debugging stack traces.
--keepattributes SourceFile,LineNumberTable
+-include proguard_common.flags
-# Preserve relationship information that can impact simple class naming.
--keepattributes EnclosingMethod,InnerClasses
-
--keep class com.android.systemui.recents.OverviewProxyRecentsImpl
--keep class com.android.systemui.statusbar.car.CarStatusBar
--keep class com.android.systemui.statusbar.phone.CentralSurfaces
-keep class com.android.systemui.statusbar.tv.TvStatusBar
--keep class ** extends com.android.systemui.SystemUIInitializer {
- *;
-}
--keep class * extends com.android.systemui.CoreStartable
--keep class * implements com.android.systemui.CoreStartable$Injector
-
-# Needed for builds to properly initialize KeyFrames from xml scene
--keepclassmembers class * extends androidx.constraintlayout.motion.widget.Key {
- public <init>();
-}
-
-# Needed to ensure callback field references are kept in their respective
-# owning classes when the downstream callback registrars only store weak refs.
-# TODO(b/264686688): Handle these cases with more targeted annotations.
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
- private com.android.keyguard.KeyguardUpdateMonitorCallback *;
- private com.android.systemui.privacy.PrivacyConfig$Callback *;
- private com.android.systemui.privacy.PrivacyItemController$Callback *;
- private com.android.systemui.settings.UserTracker$Callback *;
- private com.android.systemui.statusbar.phone.StatusBarWindowCallback *;
- private com.android.systemui.util.service.Observer$Callback *;
- private com.android.systemui.util.service.ObservableServiceConnection$Callback *;
-}
-# Note that these rules are temporary companions to the above rules, required
-# for cases like Kotlin where fields with anonymous types use the anonymous type
-# rather than the supertype.
--if class * extends com.android.keyguard.KeyguardUpdateMonitorCallback
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
- <1> *;
-}
--if class * extends com.android.systemui.privacy.PrivacyConfig$Callback
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
- <1> *;
-}
--if class * extends com.android.systemui.privacy.PrivacyItemController$Callback
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
- <1> *;
-}
--if class * extends com.android.systemui.settings.UserTracker$Callback
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
- <1> *;
-}
--if class * extends com.android.systemui.statusbar.phone.StatusBarWindowCallback
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
- <1> *;
-}
--if class * extends com.android.systemui.util.service.Observer$Callback
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
- <1> *;
-}
--if class * extends com.android.systemui.util.service.ObservableServiceConnection$Callback
--keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
- <1> *;
-}
-
--keepclasseswithmembers class * {
- public <init>(android.content.Context, android.util.AttributeSet);
-}
-
--keep class ** extends androidx.preference.PreferenceFragment
--keep class com.android.systemui.tuner.*
-
-# The plugins subpackage acts as a shared library that might be referenced in
-# dynamically-loaded plugin APKs.
--keep class com.android.systemui.plugins.** {
- *;
-}
--keep class com.android.systemui.fragments.FragmentService$FragmentCreator {
- *;
-}
--keep class androidx.core.app.CoreComponentFactory
-
--keep public class * extends com.android.systemui.CoreStartable {
- public <init>(android.content.Context);
-}
-
-# Keep the wm shell lib
--keep class com.android.wm.shell.*
-# Keep the protolog group methods that are called by the generated code
--keepclassmembers class com.android.wm.shell.protolog.ShellProtoLogGroup {
+-keep class com.android.systemui.SystemUIInitializerImpl {
*;
}
--keep,allowoptimization,allowaccessmodification class com.android.systemui.dagger.GlobalRootComponent { !synthetic *; }
--keep,allowoptimization,allowaccessmodification class com.android.systemui.dagger.GlobalRootComponent$SysUIComponentImpl { !synthetic *; }
--keep,allowoptimization,allowaccessmodification class com.android.systemui.dagger.Dagger** { !synthetic *; }
--keep,allowoptimization,allowaccessmodification class com.android.systemui.tv.Dagger** { !synthetic *; }
-
-# Prevent optimization or access modification of any referenced code that may
-# conflict with code in the bootclasspath.
-# TODO(b/222468116): Resolve such collisions in the build system.
--keepnames class android.**.nano.** { *; }
--keepnames class com.android.**.nano.** { *; }
--keepnames class com.android.internal.protolog.** { *; }
--keepnames class android.hardware.common.** { *; }
-
-# Allows proguard to make private and protected methods and fields public as
-# part of optimization. This lets proguard inline trivial getter/setter methods.
--allowaccessmodification
-
-# Removes runtime checks added through Kotlin to JVM code genereration to
-# avoid linear growth as more Kotlin code is converted / added to the codebase.
-# These checks are generally applied to Java platform types (values returned
-# from Java code that don't have nullness annotations), but we remove them to
-# avoid code size increases.
-#
-# See also https://kotlinlang.org/docs/reference/java-interop.html
-#
-# TODO(b/199941987): Consider standardizing these rules in a central place as
-# Kotlin gains adoption with other platform targets.
--assumenosideeffects class kotlin.jvm.internal.Intrinsics {
- # Remove check for method parameters being null
- static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
-
- # When a Java platform type is returned and passed to Kotlin NonNull method,
- # remove the null check
- static void checkExpressionValueIsNotNull(java.lang.Object, java.lang.String);
- static void checkNotNullExpressionValue(java.lang.Object, java.lang.String);
-
- # Remove check that final value returned from method is null, if passing
- # back Java platform type.
- static void checkReturnedValueIsNotNull(java.lang.Object, java.lang.String, java.lang.String);
- static void checkReturnedValueIsNotNull(java.lang.Object, java.lang.String);
-
- # Null check for accessing a field from a parent class written in Java.
- static void checkFieldIsNotNull(java.lang.Object, java.lang.String, java.lang.String);
- static void checkFieldIsNotNull(java.lang.Object, java.lang.String);
-
- # Removes code generated from !! operator which converts Nullable type to
- # NonNull type. These would throw an NPE immediate after on access.
- static void checkNotNull(java.lang.Object, java.lang.String);
- static void checkNotNullParameter(java.lang.Object, java.lang.String);
-
- # Removes lateinit var check being used before being set. Check is applied
- # on every field access without this.
- static void throwUninitializedPropertyAccessException(java.lang.String);
+-keep class com.android.systemui.tv.TvSystemUIInitializer {
+ *;
}
-# Strip verbose logs.
--assumenosideeffects class android.util.Log {
- static *** v(...);
- static *** isLoggable(...);
-}
--assumenosideeffects class android.util.Slog {
- static *** v(...);
-}
--maximumremovedandroidloglevel 2
+
+-keep,allowoptimization,allowaccessmodification class com.android.systemui.dagger.DaggerReferenceGlobalRootComponent** { !synthetic *; }
+-keep,allowoptimization,allowaccessmodification class com.android.systemui.tv.DaggerTvGlobalRootComponent** { !synthetic *; }
\ No newline at end of file
diff --git a/packages/SystemUI/proguard_common.flags b/packages/SystemUI/proguard_common.flags
new file mode 100644
index 0000000..1d008cf
--- /dev/null
+++ b/packages/SystemUI/proguard_common.flags
@@ -0,0 +1,141 @@
+# Preserve line number information for debugging stack traces.
+-keepattributes SourceFile,LineNumberTable
+
+-keep class com.android.systemui.VendorServices
+
+# the `#inject` methods are accessed via reflection to work on ContentProviders
+-keepclassmembers class * extends com.android.systemui.dagger.SysUIComponent { void inject(***); }
+
+# Needed for builds to properly initialize KeyFrames from xml scene
+-keepclassmembers class * extends androidx.constraintlayout.motion.widget.Key {
+ public <init>();
+}
+
+# Needed to ensure callback field references are kept in their respective
+# owning classes when the downstream callback registrars only store weak refs.
+# TODO(b/264686688): Handle these cases with more targeted annotations.
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+ private com.android.keyguard.KeyguardUpdateMonitorCallback *;
+ private com.android.systemui.privacy.PrivacyConfig$Callback *;
+ private com.android.systemui.privacy.PrivacyItemController$Callback *;
+ private com.android.systemui.settings.UserTracker$Callback *;
+ private com.android.systemui.statusbar.phone.StatusBarWindowCallback *;
+ private com.android.systemui.util.service.Observer$Callback *;
+ private com.android.systemui.util.service.ObservableServiceConnection$Callback *;
+}
+# Note that these rules are temporary companions to the above rules, required
+# for cases like Kotlin where fields with anonymous types use the anonymous type
+# rather than the supertype.
+-if class * extends com.android.keyguard.KeyguardUpdateMonitorCallback
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+ <1> *;
+}
+-if class * extends com.android.systemui.privacy.PrivacyConfig$Callback
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+ <1> *;
+}
+-if class * extends com.android.systemui.privacy.PrivacyItemController$Callback
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+ <1> *;
+}
+-if class * extends com.android.systemui.settings.UserTracker$Callback
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+ <1> *;
+}
+-if class * extends com.android.systemui.statusbar.phone.StatusBarWindowCallback
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+ <1> *;
+}
+-if class * extends com.android.systemui.util.service.Observer$Callback
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+ <1> *;
+}
+-if class * extends com.android.systemui.util.service.ObservableServiceConnection$Callback
+-keepclassmembers,allowaccessmodification class com.android.systemui.**, com.android.keyguard.** {
+ <1> *;
+}
+
+-keepclasseswithmembers class * {
+ public <init>(android.content.Context, android.util.AttributeSet);
+}
+
+-keep class ** extends androidx.preference.PreferenceFragment
+-keep class com.android.systemui.tuner.*
+
+# The plugins subpackage acts as a shared library that might be referenced in
+# dynamically-loaded plugin APKs.
+-keep class com.android.systemui.plugins.** {
+ *;
+}
+-keep class com.android.systemui.fragments.FragmentService$FragmentCreator {
+ *;
+}
+-keep class androidx.core.app.CoreComponentFactory
+
+# Keep the wm shell lib
+-keep class com.android.wm.shell.*
+# Keep the protolog group methods that are called by the generated code
+-keepclassmembers class com.android.wm.shell.protolog.ShellProtoLogGroup {
+ *;
+}
+
+# Prevent optimization or access modification of any referenced code that may
+# conflict with code in the bootclasspath.
+# TODO(b/222468116): Resolve such collisions in the build system.
+-keepnames class android.**.nano.** { *; }
+-keepnames class com.android.**.nano.** { *; }
+-keepnames class com.android.internal.protolog.** { *; }
+-keepnames class android.hardware.common.** { *; }
+
+# Allows proguard to make private and protected methods and fields public as
+# part of optimization. This lets proguard inline trivial getter/setter methods.
+-allowaccessmodification
+
+# Removes runtime checks added through Kotlin to JVM code genereration to
+# avoid linear growth as more Kotlin code is converted / added to the codebase.
+# These checks are generally applied to Java platform types (values returned
+# from Java code that don't have nullness annotations), but we remove them to
+# avoid code size increases.
+#
+# See also https://kotlinlang.org/docs/reference/java-interop.html
+#
+# TODO(b/199941987): Consider standardizing these rules in a central place as
+# Kotlin gains adoption with other platform targets.
+-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
+ # Remove check for method parameters being null
+ static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
+
+ # When a Java platform type is returned and passed to Kotlin NonNull method,
+ # remove the null check
+ static void checkExpressionValueIsNotNull(java.lang.Object, java.lang.String);
+ static void checkNotNullExpressionValue(java.lang.Object, java.lang.String);
+
+ # Remove check that final value returned from method is null, if passing
+ # back Java platform type.
+ static void checkReturnedValueIsNotNull(java.lang.Object, java.lang.String, java.lang.String);
+ static void checkReturnedValueIsNotNull(java.lang.Object, java.lang.String);
+
+ # Null check for accessing a field from a parent class written in Java.
+ static void checkFieldIsNotNull(java.lang.Object, java.lang.String, java.lang.String);
+ static void checkFieldIsNotNull(java.lang.Object, java.lang.String);
+
+ # Removes code generated from !! operator which converts Nullable type to
+ # NonNull type. These would throw an NPE immediate after on access.
+ static void checkNotNull(java.lang.Object, java.lang.String);
+ static void checkNotNullParameter(java.lang.Object, java.lang.String);
+
+ # Removes lateinit var check being used before being set. Check is applied
+ # on every field access without this.
+ static void throwUninitializedPropertyAccessException(java.lang.String);
+}
+
+
+# Strip verbose logs.
+-assumenosideeffects class android.util.Log {
+ static *** v(...);
+ static *** isLoggable(...);
+}
+-assumenosideeffects class android.util.Slog {
+ static *** v(...);
+}
+-maximumremovedandroidloglevel 2
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 26502f1..bbac7b0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2467,7 +2467,7 @@
<!-- Controls management controls screen default title [CHAR LIMIT=30] -->
<string name="controls_favorite_default_title">Controls</string>
<!-- Controls management controls screen subtitle [CHAR LIMIT=NONE] -->
- <string name="controls_favorite_subtitle">Choose controls to access from Quick Settings</string>
+ <string name="controls_favorite_subtitle">Choose device controls to access quickly</string>
<!-- Controls management editing screen, user direction for rearranging controls [CHAR LIMIT=NONE] -->
<string name="controls_favorite_rearrange">Hold & drag to rearrange controls</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt
new file mode 100644
index 0000000..8f8bff8
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/ConditionExtensions.kt
@@ -0,0 +1,23 @@
+package com.android.systemui.shared.condition
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+
+/** Converts a boolean flow to a [Condition] object which can be used with a [Monitor] */
+@JvmOverloads
+fun Flow<Boolean>.toCondition(scope: CoroutineScope, initialValue: Boolean? = null): Condition {
+ return object : Condition(initialValue, false) {
+ var job: Job? = null
+
+ override fun start() {
+ job = scope.launch { collect { updateCondition(it) } }
+ }
+
+ override fun stop() {
+ job?.cancel()
+ job = null
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 7262a73..8b87e2a 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -99,8 +99,10 @@
value.initialize(resources, dozeAmount, 0f)
if (regionSamplingEnabled) {
- clock?.smallClock?.view?.addOnLayoutChangeListener(mLayoutChangedListener)
- clock?.largeClock?.view?.addOnLayoutChangeListener(mLayoutChangedListener)
+ clock?.run {
+ smallClock.view.addOnLayoutChangeListener(mLayoutChangedListener)
+ largeClock.view.addOnLayoutChangeListener(mLayoutChangedListener)
+ }
} else {
updateColors()
}
@@ -175,15 +177,17 @@
private fun updateColors() {
val wallpaperManager = WallpaperManager.getInstance(context)
if (regionSamplingEnabled && !wallpaperManager.lockScreenWallpaperExists()) {
- if (regionSampler != null) {
- if (regionSampler?.sampledView == clock?.smallClock?.view) {
- smallClockIsDark = regionSampler!!.currentRegionDarkness().isDark
- clock?.smallClock?.events?.onRegionDarknessChanged(smallClockIsDark)
- return
- } else if (regionSampler?.sampledView == clock?.largeClock?.view) {
- largeClockIsDark = regionSampler!!.currentRegionDarkness().isDark
- clock?.largeClock?.events?.onRegionDarknessChanged(largeClockIsDark)
- return
+ regionSampler?.let { regionSampler ->
+ clock?.let { clock ->
+ if (regionSampler.sampledView == clock.smallClock.view) {
+ smallClockIsDark = regionSampler.currentRegionDarkness().isDark
+ clock.smallClock.events.onRegionDarknessChanged(smallClockIsDark)
+ return@updateColors
+ } else if (regionSampler.sampledView == clock.largeClock.view) {
+ largeClockIsDark = regionSampler.currentRegionDarkness().isDark
+ clock.largeClock.events.onRegionDarknessChanged(largeClockIsDark)
+ return@updateColors
+ }
}
}
}
@@ -193,8 +197,10 @@
smallClockIsDark = isLightTheme.data == 0
largeClockIsDark = isLightTheme.data == 0
- clock?.smallClock?.events?.onRegionDarknessChanged(smallClockIsDark)
- clock?.largeClock?.events?.onRegionDarknessChanged(largeClockIsDark)
+ clock?.run {
+ smallClock.events.onRegionDarknessChanged(smallClockIsDark)
+ largeClock.events.onRegionDarknessChanged(largeClockIsDark)
+ }
}
private fun updateRegionSampler(sampledRegion: View) {
@@ -240,7 +246,7 @@
private val configListener =
object : ConfigurationController.ConfigurationListener {
override fun onThemeChanged() {
- clock?.events?.onColorPaletteChanged(resources)
+ clock?.run { events.onColorPaletteChanged(resources) }
updateColors()
}
@@ -253,7 +259,10 @@
object : BatteryStateChangeCallback {
override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
if (isKeyguardVisible && !isCharging && charging) {
- clock?.animations?.charge()
+ clock?.run {
+ smallClock.animations.charge()
+ largeClock.animations.charge()
+ }
}
isCharging = charging
}
@@ -262,7 +271,7 @@
private val localeBroadcastReceiver =
object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
- clock?.events?.onLocaleChanged(Locale.getDefault())
+ clock?.run { events.onLocaleChanged(Locale.getDefault()) }
}
}
@@ -272,7 +281,10 @@
isKeyguardVisible = visible
if (!featureFlags.isEnabled(DOZING_MIGRATION_1)) {
if (!isKeyguardVisible) {
- clock?.animations?.doze(if (isDozing) 1f else 0f)
+ clock?.run {
+ smallClock.animations.doze(if (isDozing) 1f else 0f)
+ largeClock.animations.doze(if (isDozing) 1f else 0f)
+ }
}
}
@@ -281,19 +293,19 @@
}
override fun onTimeFormatChanged(timeFormat: String?) {
- clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+ clock?.run { events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) }
}
override fun onTimeZoneChanged(timeZone: TimeZone) {
- clock?.events?.onTimeZoneChanged(timeZone)
+ clock?.run { events.onTimeZoneChanged(timeZone) }
}
override fun onUserSwitchComplete(userId: Int) {
- clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+ clock?.run { events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) }
}
override fun onWeatherDataChanged(data: WeatherData) {
- clock?.events?.onWeatherDataChanged(data)
+ clock?.run { events.onWeatherDataChanged(data) }
}
}
@@ -349,34 +361,33 @@
smallTimeListener = null
largeTimeListener = null
- clock?.smallClock?.let {
- smallTimeListener = TimeListener(it, mainExecutor)
- smallTimeListener?.update(shouldTimeListenerRun)
- }
- clock?.largeClock?.let {
- largeTimeListener = TimeListener(it, mainExecutor)
- largeTimeListener?.update(shouldTimeListenerRun)
+ clock?.let {
+ smallTimeListener = TimeListener(it.smallClock, mainExecutor).apply {
+ update(shouldTimeListenerRun)
+ }
+ largeTimeListener = TimeListener(it.largeClock, mainExecutor).apply {
+ update(shouldTimeListenerRun)
+ }
}
}
private fun updateFontSizes() {
- clock
- ?.smallClock
- ?.events
- ?.onFontSettingChanged(
+ clock?.run {
+ smallClock.events.onFontSettingChanged(
resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
)
- clock
- ?.largeClock
- ?.events
- ?.onFontSettingChanged(
+ largeClock.events.onFontSettingChanged(
resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
)
+ }
}
private fun handleDoze(doze: Float) {
dozeAmount = doze
- clock?.animations?.doze(dozeAmount)
+ clock?.run {
+ smallClock.animations.doze(dozeAmount)
+ largeClock.animations.doze(dozeAmount)
+ }
smallTimeListener?.update(doze < DOZE_TICKRATE_THRESHOLD)
largeTimeListener?.update(doze < DOZE_TICKRATE_THRESHOLD)
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 5ba0ad6..a6c782d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -26,8 +26,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import kotlin.Unit;
-
/**
* Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
*/
@@ -38,6 +36,8 @@
private static final long CLOCK_OUT_MILLIS = 150;
private static final long CLOCK_IN_MILLIS = 200;
+ public static final long CLOCK_IN_START_DELAY_MILLIS = CLOCK_OUT_MILLIS / 2;
+ private static final long STATUS_AREA_START_DELAY_MILLIS = 50;
private static final long STATUS_AREA_MOVE_MILLIS = 350;
@IntDef({LARGE, SMALL})
@@ -173,7 +173,7 @@
msg.setBool1(useLargeClock);
msg.setBool2(animate);
msg.setBool3(mChildrenAreLaidOut);
- return Unit.INSTANCE;
+ return kotlin.Unit.INSTANCE;
}, (msg) -> "updateClockViews"
+ "; useLargeClock=" + msg.getBool1()
+ "; animate=" + msg.getBool2()
@@ -235,7 +235,7 @@
mClockInAnim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
mClockInAnim.playTogether(ObjectAnimator.ofFloat(in, View.ALPHA, 1f),
ObjectAnimator.ofFloat(in, View.TRANSLATION_Y, direction * mClockSwitchYAmount, 0));
- mClockInAnim.setStartDelay(CLOCK_OUT_MILLIS / 2);
+ mClockInAnim.setStartDelay(CLOCK_IN_START_DELAY_MILLIS);
mClockInAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
mClockInAnim = null;
@@ -247,6 +247,7 @@
mStatusAreaAnim = ObjectAnimator.ofFloat(mStatusArea, View.TRANSLATION_Y,
statusAreaYTranslation);
+ mStatusAreaAnim.setStartDelay(useLargeClock ? STATUS_AREA_START_DELAY_MILLIS : 0L);
mStatusAreaAnim.setDuration(STATUS_AREA_MOVE_MILLIS);
mStatusAreaAnim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
mStatusAreaAnim.addListener(new AnimatorListenerAdapter() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index ad333b7..a34c9fa 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -53,11 +53,11 @@
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.util.ViewController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.settings.SecureSettings;
import java.io.PrintWriter;
import java.util.Locale;
-import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -98,7 +98,7 @@
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private boolean mOnlyClock = false;
- private final Executor mUiExecutor;
+ private final DelayableExecutor mUiExecutor;
private boolean mCanShowDoubleLineClock = true;
private final ContentObserver mDoubleLineClockObserver = new ContentObserver(null) {
@Override
@@ -133,7 +133,7 @@
LockscreenSmartspaceController smartspaceController,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
SecureSettings secureSettings,
- @Main Executor uiExecutor,
+ @Main DelayableExecutor uiExecutor,
DumpManager dumpManager,
ClockEventController clockEventController,
@KeyguardClockLog LogBuffer logBuffer) {
@@ -344,7 +344,8 @@
ClockController clock = getClock();
boolean appeared = mView.switchToClock(clockSize, animate);
if (clock != null && animate && appeared && clockSize == LARGE) {
- clock.getAnimations().enter();
+ mUiExecutor.executeDelayed(() -> clock.getLargeClock().getAnimations().enter(),
+ KeyguardClockSwitch.CLOCK_IN_START_DELAY_MILLIS);
}
}
@@ -354,7 +355,8 @@
public void animateFoldToAod(float foldFraction) {
ClockController clock = getClock();
if (clock != null) {
- clock.getAnimations().fold(foldFraction);
+ clock.getSmallClock().getAnimations().fold(foldFraction);
+ clock.getLargeClock().getAnimations().fold(foldFraction);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
index 5b0e290..461d390 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
@@ -41,10 +41,10 @@
var listeningForFaceAssistant: Boolean = false,
var occludingAppRequestingFaceAuth: Boolean = false,
var postureAllowsListening: Boolean = false,
- var primaryUser: Boolean = false,
var secureCameraLaunched: Boolean = false,
var supportsDetect: Boolean = false,
var switchingUser: Boolean = false,
+ var systemUser: Boolean = false,
var udfpsFingerDown: Boolean = false,
var userNotTrustedOrDetectionIsNeeded: Boolean = false,
) : KeyguardListenModel() {
@@ -69,11 +69,11 @@
keyguardGoingAway.toString(),
listeningForFaceAssistant.toString(),
occludingAppRequestingFaceAuth.toString(),
- primaryUser.toString(),
postureAllowsListening.toString(),
secureCameraLaunched.toString(),
supportsDetect.toString(),
switchingUser.toString(),
+ systemUser.toString(),
alternateBouncerShowing.toString(),
udfpsFingerDown.toString(),
userNotTrustedOrDetectionIsNeeded.toString(),
@@ -109,12 +109,11 @@
keyguardGoingAway = model.keyguardGoingAway
listeningForFaceAssistant = model.listeningForFaceAssistant
occludingAppRequestingFaceAuth = model.occludingAppRequestingFaceAuth
- primaryUser = model.primaryUser
postureAllowsListening = model.postureAllowsListening
secureCameraLaunched = model.secureCameraLaunched
supportsDetect = model.supportsDetect
switchingUser = model.switchingUser
- switchingUser = model.switchingUser
+ systemUser = model.systemUser
udfpsFingerDown = model.udfpsFingerDown
userNotTrustedOrDetectionIsNeeded = model.userNotTrustedOrDetectionIsNeeded
}
@@ -153,11 +152,11 @@
"keyguardGoingAway",
"listeningForFaceAssistant",
"occludingAppRequestingFaceAuth",
- "primaryUser",
"postureAllowsListening",
"secureCameraLaunched",
"supportsDetect",
"switchingUser",
+ "systemUser",
"udfpsBouncerShowing",
"udfpsFingerDown",
"userNotTrustedOrDetectionIsNeeded",
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
index b8c0ccb..f2685c5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
@@ -41,11 +41,11 @@
var keyguardIsVisible: Boolean = false,
var keyguardOccluded: Boolean = false,
var occludingAppRequestingFp: Boolean = false,
- var primaryUser: Boolean = false,
var shouldListenSfpsState: Boolean = false,
var shouldListenForFingerprintAssistant: Boolean = false,
var strongerAuthRequired: Boolean = false,
var switchingUser: Boolean = false,
+ var systemUser: Boolean = false,
var udfps: Boolean = false,
var userDoesNotHaveTrust: Boolean = false,
) : KeyguardListenModel() {
@@ -72,11 +72,11 @@
keyguardIsVisible.toString(),
keyguardOccluded.toString(),
occludingAppRequestingFp.toString(),
- primaryUser.toString(),
shouldListenSfpsState.toString(),
shouldListenForFingerprintAssistant.toString(),
strongerAuthRequired.toString(),
switchingUser.toString(),
+ systemUser.toString(),
udfps.toString(),
userDoesNotHaveTrust.toString(),
)
@@ -112,11 +112,11 @@
keyguardIsVisible = model.keyguardIsVisible
keyguardOccluded = model.keyguardOccluded
occludingAppRequestingFp = model.occludingAppRequestingFp
- primaryUser = model.primaryUser
shouldListenSfpsState = model.shouldListenSfpsState
shouldListenForFingerprintAssistant = model.shouldListenForFingerprintAssistant
strongerAuthRequired = model.strongerAuthRequired
switchingUser = model.switchingUser
+ systemUser = model.systemUser
udfps = model.udfps
userDoesNotHaveTrust = model.userDoesNotHaveTrust
}
@@ -158,11 +158,11 @@
"keyguardIsVisible",
"keyguardOccluded",
"occludingAppRequestingFp",
- "primaryUser",
"shouldListenSidFingerprintState",
"shouldListenForFingerprintAssistant",
"strongAuthRequired",
"switchingUser",
+ "systemUser",
"underDisplayFingerprint",
"userDoesNotHaveTrust",
)
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 76e051e..693268d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -178,6 +178,7 @@
@Override
public void onUserInput() {
+ mKeyguardFaceAuthInteractor.onPrimaryBouncerUserInput();
mUpdateMonitor.cancelFaceAuth();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 0cdef4d..edfcb8d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -349,7 +349,7 @@
ClockController clock = mKeyguardClockSwitchController.getClock();
boolean customClockAnimation = clock != null
- && clock.getConfig().getHasCustomPositionUpdatedAnimation();
+ && clock.getLargeClock().getConfig().getHasCustomPositionUpdatedAnimation();
if (mFeatureFlags.isEnabled(Flags.STEP_CLOCK_ANIMATION) && customClockAnimation) {
// Find the clock, so we can exclude it from this transition.
@@ -436,7 +436,8 @@
return;
}
- clock.getAnimations().onPositionUpdated(from, to, animation.getAnimatedFraction());
+ clock.getLargeClock().getAnimations()
+ .onPositionUpdated(from, to, animation.getAnimatedFraction());
});
return anim;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index c48aaf4..10c08bc 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -297,7 +297,7 @@
private final Context mContext;
private final UserTracker mUserTracker;
private final KeyguardUpdateMonitorLogger mLogger;
- private final boolean mIsPrimaryUser;
+ private final boolean mIsSystemUser;
private final AuthController mAuthController;
private final UiEventLogger mUiEventLogger;
private final Set<Integer> mFaceAcquiredInfoIgnoreList;
@@ -1771,10 +1771,6 @@
MSG_TIMEZONE_UPDATE, intent.getStringExtra(Intent.EXTRA_TIMEZONE));
mHandler.sendMessage(msg);
} else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
- // Clear incompatible charger state when device is unplugged.
- if (!BatteryStatus.isPluggedIn(intent)) {
- mIncompatibleCharger = false;
- }
final Message msg = mHandler.obtainMessage(
MSG_BATTERY_UPDATE, new BatteryStatus(intent, mIncompatibleCharger));
mHandler.sendMessage(msg);
@@ -2526,7 +2522,7 @@
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, FACE_AUTH_UPDATED_ON_KEYGUARD_INIT);
TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
- mIsPrimaryUser = mUserManager.isPrimaryUser();
+ mIsSystemUser = mUserManager.isSystemUser();
int user = mUserTracker.getUserId();
mUserIsUnlocked.put(user, mUserManager.isUserUnlocked(user));
mLogoutEnabled = mDevicePolicyManager.isLogoutEnabled();
@@ -2972,7 +2968,7 @@
|| (mKeyguardOccluded && userDoesNotHaveTrust && mKeyguardShowing
&& (mOccludingAppRequestingFp || isUdfps || mAlternateBouncerShowing));
- // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
+ // Only listen if this KeyguardUpdateMonitor belongs to the system user. There is an
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user);
final boolean userCanSkipBouncer = getUserCanSkipBouncer(user);
@@ -2981,7 +2977,7 @@
!mSwitchingUser
&& !fingerprintDisabledForUser
&& (!mKeyguardGoingAway || !mDeviceInteractive)
- && mIsPrimaryUser
+ && mIsSystemUser
&& biometricEnabledForUser
&& !isUserInLockdown(user);
final boolean strongerAuthRequired = !isUnlockingWithFingerprintAllowed();
@@ -3025,11 +3021,11 @@
isKeyguardVisible(),
mKeyguardOccluded,
mOccludingAppRequestingFp,
- mIsPrimaryUser,
shouldListenSideFpsState,
shouldListenForFingerprintAssistant,
strongerAuthRequired,
mSwitchingUser,
+ mIsSystemUser,
isUdfps,
userDoesNotHaveTrust));
@@ -3074,7 +3070,7 @@
final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant();
final boolean isUdfpsFingerDown = mAuthController.isUdfpsFingerDown();
final boolean isPostureAllowedForFaceAuth = doesPostureAllowFaceAuth(mPostureState);
- // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
+ // Only listen if this KeyguardUpdateMonitor belongs to the system user. There is an
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
final boolean shouldListen =
(mPrimaryBouncerFullyShown
@@ -3086,7 +3082,7 @@
|| mAlternateBouncerShowing)
&& !mSwitchingUser && !faceDisabledForUser && userNotTrustedOrDetectionIsNeeded
&& !mKeyguardGoingAway && biometricEnabledForUser
- && faceAuthAllowedOrDetectionIsNeeded && mIsPrimaryUser
+ && faceAuthAllowedOrDetectionIsNeeded && mIsSystemUser
&& (!mSecureCameraLaunched || mAlternateBouncerShowing)
&& faceAndFpNotAuthenticated
&& !mGoingToSleep
@@ -3112,10 +3108,10 @@
shouldListenForFaceAssistant,
mOccludingAppRequestingFace,
isPostureAllowedForFaceAuth,
- mIsPrimaryUser,
mSecureCameraLaunched,
supportsDetect,
mSwitchingUser,
+ mIsSystemUser,
isUdfpsFingerDown,
userNotTrustedOrDetectionIsNeeded));
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index cde8ff7..6740375 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -27,7 +27,9 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.log.dagger.KeyguardClockLog;
import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.plugins.log.LogBuffer;
import com.android.systemui.shared.clocks.ClockRegistry;
import com.android.systemui.shared.clocks.DefaultClockProvider;
@@ -51,7 +53,8 @@
@Background CoroutineDispatcher bgDispatcher,
FeatureFlags featureFlags,
@Main Resources resources,
- LayoutInflater layoutInflater) {
+ LayoutInflater layoutInflater,
+ @KeyguardClockLog LogBuffer logBuffer) {
ClockRegistry registry = new ClockRegistry(
context,
pluginManager,
@@ -62,6 +65,7 @@
/* handleAllUsers= */ true,
new DefaultClockProvider(context, layoutInflater, resources),
context.getString(R.string.lockscreen_clock_id_fallback),
+ logBuffer,
/* keepAllLoaded = */ false,
/* subTag = */ "System");
registry.registerListeners();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java
index b6ee4cb..3349fe5 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.res.Configuration;
import android.util.Range;
+import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -68,14 +69,18 @@
@NonNull Callback settingsControllerCallback,
SecureSettings secureSettings,
WindowMagnificationSettings windowMagnificationSettings) {
- mContext = context;
+ mContext = context.createWindowContext(
+ context.getDisplay(),
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ null);
+ mContext.setTheme(com.android.systemui.R.style.Theme_SystemUI);
mDisplayId = mContext.getDisplayId();
- mConfiguration = new Configuration(context.getResources().getConfiguration());
+ mConfiguration = new Configuration(mContext.getResources().getConfiguration());
mSettingsControllerCallback = settingsControllerCallback;
if (windowMagnificationSettings != null) {
mWindowMagnificationSettings = windowMagnificationSettings;
} else {
- mWindowMagnificationSettings = new WindowMagnificationSettings(context,
+ mWindowMagnificationSettings = new WindowMagnificationSettings(mContext,
mWindowMagnificationSettingsCallback,
sfVsyncFrameProvider, secureSettings);
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
index 71c5f24..6e8275f 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
@@ -559,7 +559,7 @@
final LayoutParams params = new LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT,
- LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
+ LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSPARENT);
params.gravity = Gravity.TOP | Gravity.START;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index f435b22..aeebb01 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -880,7 +880,7 @@
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
windowFlags,
PixelFormat.TRANSLUCENT);
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index 98a3e4b..11ef749 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -34,7 +34,6 @@
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.surfaceeffects.ripple.RippleAnimationConfig;
import com.android.systemui.surfaceeffects.ripple.RippleShader;
import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape;
import com.android.systemui.surfaceeffects.ripple.RippleView;
@@ -177,7 +176,7 @@
mRippleView.setBlur(6.5f, 2.5f);
} else {
mRippleView.setDuration(CIRCLE_RIPPLE_ANIMATION_DURATION);
- mRippleView.setColor(color, RippleAnimationConfig.RIPPLE_DEFAULT_ALPHA);
+ mRippleView.setColor(color, RippleShader.RIPPLE_DEFAULT_ALPHA);
}
OnAttachStateChangeListener listener = new OnAttachStateChangeListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index 5230159..0aeab10 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -32,7 +32,6 @@
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TAP_OUTSIDE;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TIMED_OUT;
-import static com.android.systemui.flags.Flags.CLIPBOARD_REMOTE_BEHAVIOR;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -277,7 +276,7 @@
} else if (!mIsMinimized) {
setExpandedView();
}
- if (mFeatureFlags.isEnabled(CLIPBOARD_REMOTE_BEHAVIOR) && mClipboardModel.isRemote()) {
+ if (mClipboardModel.isRemote()) {
mTimeoutHandler.cancelTimeout();
mOnUiUpdate = null;
} else {
@@ -291,8 +290,7 @@
mView.setMinimized(false);
switch (model.getType()) {
case TEXT:
- if ((mFeatureFlags.isEnabled(CLIPBOARD_REMOTE_BEHAVIOR) && model.isRemote())
- || DeviceConfig.getBoolean(
+ if (model.isRemote() || DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_SHOW_ACTIONS, false)) {
if (model.getTextLinks() != null) {
classifyText(model);
@@ -326,11 +324,7 @@
mView.showDefaultTextPreview();
break;
}
- if (mFeatureFlags.isEnabled(CLIPBOARD_REMOTE_BEHAVIOR)) {
- if (!model.isRemote()) {
- maybeShowRemoteCopy(model.getClipData());
- }
- } else {
+ if (!model.isRemote()) {
maybeShowRemoteCopy(model.getClipData());
}
if (model.getType() != ClipboardModel.Type.OTHER) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 044dd6a..75f70ae 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -48,7 +48,6 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.FlagsModule;
-import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyboard.KeyboardModule;
import com.android.systemui.keyguard.data.BouncerViewModule;
import com.android.systemui.log.dagger.LogModule;
@@ -64,6 +63,7 @@
import com.android.systemui.qrcodescanner.dagger.QRCodeScannerModule;
import com.android.systemui.qs.FgsManagerController;
import com.android.systemui.qs.FgsManagerControllerImpl;
+import com.android.systemui.qs.QSFragmentStartableModule;
import com.android.systemui.qs.footer.dagger.FooterActionsModule;
import com.android.systemui.recents.Recents;
import com.android.systemui.screenrecord.ScreenRecordModule;
@@ -169,6 +169,7 @@
PolicyModule.class,
PrivacyModule.class,
QRCodeScannerModule.class,
+ QSFragmentStartableModule.class,
ScreenshotModule.class,
SensorModule.class,
SecurityRepositoryModule.class,
@@ -198,8 +199,7 @@
DozeComponent.class,
ExpandableNotificationRowComponent.class,
KeyguardBouncerComponent.class,
- NotificationShelfComponent.class,
- FragmentService.FragmentCreator.class
+ NotificationShelfComponent.class
})
public abstract class SystemUIModule {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 8783ec3..29c63bb 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -72,7 +72,7 @@
@JvmField
val SIMPLIFIED_APPEAR_FRACTION =
- unreleasedFlag(259395680, "simplified_appear_fraction", teamfood = true)
+ releasedFlag(259395680, "simplified_appear_fraction")
// TODO(b/257315550): Tracking Bug
val NO_HUN_FOR_OLD_WHEN = releasedFlag(118, "no_hun_for_old_when")
@@ -383,7 +383,7 @@
val MEDIA_RETAIN_RECOMMENDATIONS = releasedFlag(916, "media_retain_recommendations")
// TODO(b/270437894): Tracking Bug
- val MEDIA_REMOTE_RESUME = unreleasedFlag(917, "media_remote_resume", teamfood = true)
+ val MEDIA_REMOTE_RESUME = releasedFlag(917, "media_remote_resume")
// 1000 - dock
val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag(1000, "simulate_dock_through_charging")
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
index 6a27ee7..81a5206 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -39,16 +39,17 @@
import com.android.systemui.plugins.Plugin;
import com.android.systemui.util.leak.LeakDetector;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.HashMap;
-
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import javax.inject.Provider;
+
public class FragmentHostManager {
private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -322,25 +323,17 @@
return instantiateWithInjections(context, className, arguments);
}
- private Fragment instantiateWithInjections(
- Context context, String className, Bundle args) {
- FragmentService.FragmentInstantiationInfo fragmentInstantiationInfo =
+ private Fragment instantiateWithInjections(Context context, String className, Bundle args) {
+ Provider<? extends Fragment> fragmentProvider =
mManager.getInjectionMap().get(className);
- if (fragmentInstantiationInfo != null) {
- try {
- Fragment f = (Fragment) fragmentInstantiationInfo
- .mMethod
- .invoke(fragmentInstantiationInfo.mDaggerComponent);
- // Setup the args, taken from Fragment#instantiate.
- if (args != null) {
- args.setClassLoader(f.getClass().getClassLoader());
- f.setArguments(args);
- }
- return f;
- } catch (IllegalAccessException | InvocationTargetException e) {
- throw new Fragment.InstantiationException("Unable to instantiate " + className,
- e);
+ if (fragmentProvider != null) {
+ Fragment f = fragmentProvider.get();
+ // Setup the args, taken from Fragment#instantiate.
+ if (args != null) {
+ args.setClassLoader(f.getClass().getClassLoader());
+ f.setArguments(args);
}
+ return f;
}
return Fragment.instantiate(context, className, args);
}
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
index d302b13a..a75c056 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
@@ -24,16 +24,12 @@
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.qs.QSFragment;
import com.android.systemui.statusbar.policy.ConfigurationController;
import java.io.PrintWriter;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
import javax.inject.Inject;
-
-import dagger.Subcomponent;
+import javax.inject.Provider;
/**
* Holds a map of root views to FragmentHostStates and generates them as needed.
@@ -49,9 +45,9 @@
* A map with the means to create fragments via Dagger injection.
*
* key: the fragment class name.
- * value: see {@link FragmentInstantiationInfo}.
+ * value: A {@link Provider} for the Fragment
*/
- private final ArrayMap<String, FragmentInstantiationInfo> mInjectionMap = new ArrayMap<>();
+ private final ArrayMap<String, Provider<? extends Fragment>> mInjectionMap = new ArrayMap<>();
private final Handler mHandler = new Handler();
private final FragmentHostManager.Factory mFragmentHostManagerFactory;
@@ -67,38 +63,31 @@
@Inject
public FragmentService(
- FragmentCreator.Factory fragmentCreatorFactory,
FragmentHostManager.Factory fragmentHostManagerFactory,
ConfigurationController configurationController,
DumpManager dumpManager) {
mFragmentHostManagerFactory = fragmentHostManagerFactory;
- addFragmentInstantiationProvider(fragmentCreatorFactory.build());
configurationController.addCallback(mConfigurationListener);
- dumpManager.registerDumpable(getClass().getSimpleName(), this);
+ dumpManager.registerNormalDumpable(this);
}
- ArrayMap<String, FragmentInstantiationInfo> getInjectionMap() {
+ ArrayMap<String, Provider<? extends Fragment>> getInjectionMap() {
return mInjectionMap;
}
/**
* Adds a new Dagger component object that provides method(s) to create fragments via injection.
*/
- public void addFragmentInstantiationProvider(Object daggerComponent) {
- for (Method method : daggerComponent.getClass().getDeclaredMethods()) {
- if (Fragment.class.isAssignableFrom(method.getReturnType())
- && (method.getModifiers() & Modifier.PUBLIC) != 0) {
- String fragmentName = method.getReturnType().getName();
- if (mInjectionMap.containsKey(fragmentName)) {
- Log.w(TAG, "Fragment " + fragmentName + " is already provided by different"
- + " Dagger component; Not adding method");
- continue;
- }
- mInjectionMap.put(
- fragmentName, new FragmentInstantiationInfo(method, daggerComponent));
- }
+ public void addFragmentInstantiationProvider(
+ Class<?> fragmentCls, Provider<? extends Fragment> provider) {
+ String fragmentName = fragmentCls.getName();
+ if (mInjectionMap.containsKey(fragmentName)) {
+ Log.w(TAG, "Fragment " + fragmentName + " is already provided by different"
+ + " Dagger component; Not adding method");
+ return;
}
+ mInjectionMap.put(fragmentName, provider);
}
public FragmentHostManager getFragmentHostManager(View view) {
@@ -132,22 +121,6 @@
}
}
- /**
- * The subcomponent of dagger that holds all fragments that need injection.
- */
- @Subcomponent
- public interface FragmentCreator {
- /** Factory for creating a FragmentCreator. */
- @Subcomponent.Factory
- interface Factory {
- FragmentCreator build();
- }
- /**
- * Inject a QSFragment.
- */
- QSFragment createQSFragment();
- }
-
private class FragmentHostState {
private final View mView;
@@ -170,16 +143,4 @@
mFragmentHostManager.onConfigurationChanged(newConfig);
}
}
-
- /** An object containing the information needed to instantiate a fragment. */
- static class FragmentInstantiationInfo {
- /** The method that returns a newly-created fragment of the given class. */
- final Method mMethod;
- /** The Dagger component that the method should be invoked on. */
- final Object mDaggerComponent;
- FragmentInstantiationInfo(Method method, Object daggerComponent) {
- this.mMethod = method;
- this.mDaggerComponent = daggerComponent;
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
index 5f6098b..5f2178df 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -140,8 +140,10 @@
private var authCancellationSignal: CancellationSignal? = null
private var detectCancellationSignal: CancellationSignal? = null
private var faceAcquiredInfoIgnoreList: Set<Int>
+ private var retryCount = 0
private var cancelNotReceivedHandlerJob: Job? = null
+ private var halErrorRetryJob: Job? = null
private val _authenticationStatus: MutableStateFlow<AuthenticationStatus?> =
MutableStateFlow(null)
@@ -223,11 +225,20 @@
}
private fun observeFaceAuthResettingConditions() {
- // Clear auth status when keyguard is going away or when the user is switching.
- merge(keyguardRepository.isKeyguardGoingAway, userRepository.userSwitchingInProgress)
- .onEach { goingAwayOrUserSwitchingInProgress ->
- if (goingAwayOrUserSwitchingInProgress) {
+ // Clear auth status when keyguard is going away or when the user is switching or device
+ // starts going to sleep.
+ merge(
+ keyguardRepository.wakefulness.map {
+ WakefulnessModel.isSleepingOrStartingToSleep(it)
+ },
+ keyguardRepository.isKeyguardGoingAway,
+ userRepository.userSwitchingInProgress
+ )
+ .onEach { anyOfThemIsTrue ->
+ if (anyOfThemIsTrue) {
_isAuthenticated.value = false
+ retryCount = 0
+ halErrorRetryJob?.cancel()
}
}
.launchIn(applicationScope)
@@ -244,8 +255,8 @@
"nonStrongBiometricIsNotAllowed",
faceDetectLog
),
- // We don't want to run face detect if it's not possible to authenticate with FP
- // from the bouncer. UDFPS is the only fp sensor type that won't support this.
+ // We don't want to run face detect if fingerprint can be used to unlock the device
+ // but it's not possible to authenticate with FP from the bouncer (UDFPS)
logAndObserve(
and(isUdfps(), deviceEntryFingerprintAuthRepository.isRunning).isFalse(),
"udfpsAuthIsNotPossibleAnymore",
@@ -302,7 +313,7 @@
logAndObserve(
combine(
keyguardInteractor.isSecureCameraActive,
- alternateBouncerInteractor.isVisible,
+ alternateBouncerInteractor.isVisible
) { a, b ->
!a || b
},
@@ -330,12 +341,12 @@
logAndObserve(isLockedOut.isFalse(), "isNotInLockOutState", faceAuthLog),
logAndObserve(
deviceEntryFingerprintAuthRepository.isLockedOut.isFalse(),
- "fpLockedOut",
+ "fpIsNotLockedOut",
faceAuthLog
),
logAndObserve(
trustRepository.isCurrentUserTrusted.isFalse(),
- "currentUserTrusted",
+ "currentUserIsNotTrusted",
faceAuthLog
),
logAndObserve(
@@ -343,11 +354,6 @@
"nonStrongBiometricIsAllowed",
faceAuthLog
),
- logAndObserve(
- userRepository.selectedUserInfo.map { it.isPrimary },
- "userIsPrimaryUser",
- faceAuthLog
- ),
)
.reduce(::and)
.distinctUntilChanged()
@@ -385,14 +391,11 @@
_authenticationStatus.value = errorStatus
_isAuthenticated.value = false
if (errorStatus.isCancellationError()) {
- cancelNotReceivedHandlerJob?.cancel()
- applicationScope.launch {
- faceAuthLogger.launchingQueuedFaceAuthRequest(
- faceAuthRequestedWhileCancellation
- )
- faceAuthRequestedWhileCancellation?.let { authenticate(it) }
- faceAuthRequestedWhileCancellation = null
- }
+ handleFaceCancellationError()
+ }
+ if (errorStatus.isHardwareError()) {
+ faceAuthLogger.hardwareError(errorStatus)
+ handleFaceHardwareError()
}
faceAuthLogger.authenticationError(
errorCode,
@@ -418,6 +421,35 @@
}
}
+ private fun handleFaceCancellationError() {
+ cancelNotReceivedHandlerJob?.cancel()
+ applicationScope.launch {
+ faceAuthRequestedWhileCancellation?.let {
+ faceAuthLogger.launchingQueuedFaceAuthRequest(it)
+ authenticate(it)
+ }
+ faceAuthRequestedWhileCancellation = null
+ }
+ }
+
+ private fun handleFaceHardwareError() {
+ if (retryCount < HAL_ERROR_RETRY_MAX) {
+ retryCount++
+ halErrorRetryJob?.cancel()
+ halErrorRetryJob =
+ applicationScope.launch {
+ delay(HAL_ERROR_RETRY_TIMEOUT)
+ if (retryCount < HAL_ERROR_RETRY_MAX) {
+ faceAuthLogger.attemptingRetryAfterHardwareError(retryCount)
+ authenticate(
+ FaceAuthUiEvent.FACE_AUTH_TRIGGERED_RETRY_AFTER_HW_UNAVAILABLE,
+ fallbackToDetection = false
+ )
+ }
+ }
+ }
+ }
+
private fun onFaceAuthRequestCompleted() {
cancellationInProgress = false
_isAuthRunning.value = false
@@ -558,6 +590,12 @@
* cancelled.
*/
const val DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000L
+
+ /** Number of allowed retries whenever there is a face hardware error */
+ const val HAL_ERROR_RETRY_MAX = 20
+
+ /** Timeout before retries whenever there is a HAL error. */
+ const val HAL_ERROR_RETRY_TIMEOUT = 500L // ms
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
index 06ae11fe8..74ef7a5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
@@ -59,6 +59,7 @@
fun onQsExpansionStared()
fun onNotificationPanelClicked()
fun onSwipeUpOnBouncer()
+ fun onPrimaryBouncerUserInput()
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
index cad40aa..5005b6c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
@@ -59,4 +59,5 @@
override fun onNotificationPanelClicked() {}
override fun onSwipeUpOnBouncer() {}
+ override fun onPrimaryBouncerUserInput() {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
index 20ebb71..6b515da 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
@@ -151,6 +151,10 @@
return featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)
}
+ override fun onPrimaryBouncerUserInput() {
+ repository.cancel()
+ }
+
/** Provide the status of face authentication */
override val authenticationStatus = repository.authenticationStatus
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
index eded9c1..c8bd958 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
@@ -50,6 +50,11 @@
* was cancelled before it completed.
*/
fun isCancellationError() = msgId == FaceManager.FACE_ERROR_CANCELED
+
+ /** Method that checks if [msgId] is a hardware error. */
+ fun isHardwareError() =
+ msgId == FaceManager.FACE_ERROR_HW_UNAVAILABLE ||
+ msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS
}
/** Face detection success message. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
index 5770f3e..ddce516 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
@@ -47,6 +47,7 @@
duration = TO_LOCKSCREEN_DURATION,
onStep = { value -> -translatePx + value * translatePx },
interpolator = EMPHASIZED_DECELERATE,
+ onCancel = { 0f },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
index 7f6e4a9..efd3ad6 100644
--- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
@@ -4,6 +4,7 @@
import android.hardware.face.FaceSensorPropertiesInternal
import com.android.keyguard.FaceAuthUiEvent
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.log.dagger.FaceAuthLog
import com.android.systemui.plugins.log.LogBuffer
@@ -239,4 +240,25 @@
{ "Requesting face auth for trigger: $str1" }
)
}
+
+ fun hardwareError(errorStatus: ErrorAuthenticationStatus) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = "${errorStatus.msg}"
+ int1 = errorStatus.msgId
+ },
+ { "Received face hardware error: $str1 , code: $int1" }
+ )
+ }
+
+ fun attemptingRetryAfterHardwareError(retryCount: Int) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { int1 = retryCount },
+ { "Attempting face auth again because of HW error: retry attempt $int1" }
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
index 1c8bfd1..8b74263 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
@@ -147,6 +147,7 @@
val expandedBottomActionIds =
setOf(
+ R.id.media_progress_bar,
R.id.actionPrev,
R.id.actionNext,
R.id.action0,
@@ -155,7 +156,22 @@
R.id.action3,
R.id.action4,
R.id.media_scrubbing_elapsed_time,
- R.id.media_scrubbing_total_time
+ R.id.media_scrubbing_total_time,
+ )
+
+ val detailIds =
+ setOf(
+ R.id.header_title,
+ R.id.header_artist,
+ R.id.media_explicit_indicator,
+ R.id.actionPlayPause,
+ )
+
+ val backgroundIds =
+ setOf(
+ R.id.album_art,
+ R.id.turbulence_noise_view,
+ R.id.touch_ripple_view,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt
index 2509f21..35f5a8c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt
@@ -20,12 +20,14 @@
import android.media.session.MediaController
import android.media.session.PlaybackState
import android.os.SystemClock
+import android.os.Trace
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import android.widget.SeekBar
import androidx.annotation.AnyThread
+import androidx.annotation.VisibleForTesting
import androidx.annotation.WorkerThread
import androidx.core.view.GestureDetectorCompat
import androidx.lifecycle.LiveData
@@ -36,10 +38,13 @@
import com.android.systemui.statusbar.NotificationMediaManager
import com.android.systemui.util.concurrency.RepeatableExecutor
import javax.inject.Inject
+import kotlin.math.abs
private const val POSITION_UPDATE_INTERVAL_MILLIS = 100L
private const val MIN_FLING_VELOCITY_SCALE_FACTOR = 10
+private const val TRACE_POSITION_NAME = "SeekBarPollingPosition"
+
private fun PlaybackState.isInMotion(): Boolean {
return this.state == PlaybackState.STATE_PLAYING ||
this.state == PlaybackState.STATE_FAST_FORWARDING ||
@@ -295,14 +300,20 @@
@WorkerThread
private fun checkIfPollingNeeded() {
val needed = listening && !scrubbing && playbackState?.isInMotion() ?: false
+ val traceCookie = controller?.sessionToken.hashCode()
if (needed) {
if (cancel == null) {
- cancel =
+ Trace.beginAsyncSection(TRACE_POSITION_NAME, traceCookie)
+ val cancelPolling =
bgExecutor.executeRepeatedly(
this::checkPlaybackPosition,
0L,
POSITION_UPDATE_INTERVAL_MILLIS
)
+ cancel = Runnable {
+ cancelPolling.run()
+ Trace.endAsyncSection(TRACE_POSITION_NAME, traceCookie)
+ }
}
} else {
cancel?.run()
@@ -316,6 +327,10 @@
return SeekBarChangeListener(this, falsingManager)
}
+ /** first and last motion events of seekbar grab. */
+ @VisibleForTesting var firstMotionEvent: MotionEvent? = null
+ @VisibleForTesting var lastMotionEvent: MotionEvent? = null
+
/** Attach touch handlers to the seek bar view. */
fun attachTouchHandlers(bar: SeekBar) {
bar.setOnSeekBarChangeListener(seekBarListener)
@@ -342,6 +357,23 @@
}
}
+ /**
+ * This method specifies if user made a bad seekbar grab or not. If the vertical distance from
+ * first touch on seekbar is more than the horizontal distance, this means that the seekbar grab
+ * is more vertical and should be rejected. Seekbar accepts horizontal grabs only.
+ *
+ * Single tap has the same first and last motion event, it is counted as a valid grab.
+ *
+ * @return whether the touch on seekbar is valid.
+ */
+ private fun isValidSeekbarGrab(): Boolean {
+ if (firstMotionEvent == null || lastMotionEvent == null) {
+ return true
+ }
+ return abs(firstMotionEvent!!.x - lastMotionEvent!!.x) >=
+ abs(firstMotionEvent!!.y - lastMotionEvent!!.y)
+ }
+
/** Listener interface to be notified when the user starts or stops scrubbing. */
interface ScrubbingChangeListener {
fun onScrubbingChanged(scrubbing: Boolean)
@@ -367,7 +399,7 @@
}
override fun onStopTrackingTouch(bar: SeekBar) {
- if (falsingManager.isFalseTouch(MEDIA_SEEKBAR)) {
+ if (!viewModel.isValidSeekbarGrab() || falsingManager.isFalseTouch(MEDIA_SEEKBAR)) {
viewModel.onSeekFalse()
}
viewModel.onSeek(bar.progress.toLong())
@@ -415,6 +447,8 @@
return false
}
detector.onTouchEvent(event)
+ // Store the last motion event done on seekbar.
+ viewModel.lastMotionEvent = event.copy()
return !shouldGoToSeekBar
}
@@ -459,6 +493,8 @@
if (shouldGoToSeekBar) {
bar.parent?.requestDisallowInterceptTouchEvent(true)
}
+ // Store the first motion event done on seekbar.
+ viewModel.firstMotionEvent = event.copy()
return shouldGoToSeekBar
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt
index 70f2dee..0b33904 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt
@@ -180,5 +180,7 @@
R.id.media_cover2_container,
R.id.media_cover3_container
)
+
+ val backgroundId = R.id.sizing_view
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
index cd51d92..4bca778 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
@@ -61,37 +61,6 @@
companion object {
@JvmField val GUTS_ANIMATION_DURATION = 500L
- val controlIds =
- setOf(
- R.id.media_progress_bar,
- R.id.actionNext,
- R.id.actionPrev,
- R.id.action0,
- R.id.action1,
- R.id.action2,
- R.id.action3,
- R.id.action4,
- R.id.media_scrubbing_elapsed_time,
- R.id.media_scrubbing_total_time
- )
-
- val detailIds =
- setOf(
- R.id.header_title,
- R.id.header_artist,
- R.id.media_explicit_indicator,
- R.id.actionPlayPause,
- )
-
- val backgroundIds =
- setOf(
- R.id.album_art,
- R.id.turbulence_noise_view,
- R.id.touch_ripple_view,
- )
-
- // Sizing view id for recommendation card view.
- val recSizingViewId = R.id.sizing_view
}
/** A listener when the current dimensions of the player change */
@@ -182,15 +151,14 @@
lastOrientation = newOrientation
// Update the height of media controls for the expanded layout. it is needed
// for large screen devices.
- if (type == TYPE.PLAYER) {
- backgroundIds.forEach { id ->
- expandedLayout.getConstraint(id).layout.mHeight =
- context.resources.getDimensionPixelSize(
- R.dimen.qs_media_session_height_expanded
- )
+ val backgroundIds =
+ if (type == TYPE.PLAYER) {
+ MediaViewHolder.backgroundIds
+ } else {
+ setOf(RecommendationViewHolder.backgroundId)
}
- } else {
- expandedLayout.getConstraint(recSizingViewId).layout.mHeight =
+ backgroundIds.forEach { id ->
+ expandedLayout.getConstraint(id).layout.mHeight =
context.resources.getDimensionPixelSize(
R.dimen.qs_media_session_height_expanded
)
@@ -338,19 +306,19 @@
squishedViewState.height = squishedHeight
// We are not overriding the squishedViewStates height but only the children to avoid
// them remeasuring the whole view. Instead it just remains as the original size
- backgroundIds.forEach { id ->
+ MediaViewHolder.backgroundIds.forEach { id ->
squishedViewState.widgetStates.get(id)?.let { state -> state.height = squishedHeight }
}
// media player
calculateWidgetGroupAlphaForSquishiness(
- controlIds,
+ MediaViewHolder.expandedBottomActionIds,
squishedViewState.measureHeight.toFloat(),
squishedViewState,
squishFraction
)
calculateWidgetGroupAlphaForSquishiness(
- detailIds,
+ MediaViewHolder.detailIds,
squishedViewState.measureHeight.toFloat(),
squishedViewState,
squishFraction
@@ -660,7 +628,7 @@
result.height = result.measureHeight
result.width = result.measureWidth
// Make sure all background views are also resized such that their size is correct
- backgroundIds.forEach { id ->
+ MediaViewHolder.backgroundIds.forEach { id ->
result.widgetStates.get(id)?.let { state ->
state.height = result.height
state.width = result.width
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index 8f70376..aab898e 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -85,12 +85,12 @@
fun onBubbleExpandChanged(isExpanding: Boolean, key: String?) {
if (!isEnabled) return
- if (key != Bubble.KEY_APP_BUBBLE) return
+ val info = infoReference.getAndSet(null) ?: return
- val info = infoReference.getAndSet(null)
+ if (key != Bubble.getAppBubbleKeyForApp(info.packageName, info.user)) return
// Safe guard mechanism, this callback should only be called for app bubbles.
- if (info?.launchMode != NoteTaskLaunchMode.AppBubble) return
+ if (info.launchMode != NoteTaskLaunchMode.AppBubble) return
if (isExpanding) {
logDebug { "onBubbleExpandChanged - expanding: $info" }
@@ -173,7 +173,7 @@
return
}
- val info = resolver.resolveInfo(entryPoint, isKeyguardLocked)
+ val info = resolver.resolveInfo(entryPoint, isKeyguardLocked, user)
if (info == null) {
logDebug { "Default notes app isn't set" }
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
index 2b9f0af..a758347 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt
@@ -15,10 +15,13 @@
*/
package com.android.systemui.notetask
+import android.os.UserHandle
+
/** Contextual information required to launch a Note Task by [NoteTaskController]. */
data class NoteTaskInfo(
val packageName: String,
val uid: Int,
+ val user: UserHandle,
val entryPoint: NoteTaskEntryPoint? = null,
val isKeyguardLocked: Boolean = false,
) {
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
index 616f9b5..89a8526 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt
@@ -25,7 +25,6 @@
import android.os.UserHandle
import android.util.Log
import com.android.systemui.notetask.NoteTaskRoleManagerExt.getDefaultRoleHolderAsUser
-import com.android.systemui.settings.UserTracker
import javax.inject.Inject
class NoteTaskInfoResolver
@@ -33,15 +32,13 @@
constructor(
private val roleManager: RoleManager,
private val packageManager: PackageManager,
- private val userTracker: UserTracker,
) {
fun resolveInfo(
entryPoint: NoteTaskEntryPoint? = null,
isKeyguardLocked: Boolean = false,
+ user: UserHandle,
): NoteTaskInfo? {
- val user = userTracker.userHandle
-
val packageName = roleManager.getDefaultRoleHolderAsUser(ROLE_NOTES, user)
if (packageName.isNullOrEmpty()) return null
@@ -49,6 +46,7 @@
return NoteTaskInfo(
packageName = packageName,
uid = packageManager.getUidOf(packageName, user),
+ user = user,
entryPoint = entryPoint,
isKeyguardLocked = isKeyguardLocked,
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt
new file mode 100644
index 0000000..253560b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.fragments.FragmentService
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import javax.inject.Inject
+import javax.inject.Provider
+
+@SysUISingleton
+class QSFragmentStartable
+@Inject
+constructor(
+ private val fragmentService: FragmentService,
+ private val qsFragmentProvider: Provider<QSFragment>
+) : CoreStartable {
+ override fun start() {
+ fragmentService.addFragmentInstantiationProvider(QSFragment::class.java, qsFragmentProvider)
+ }
+}
+
+@Module
+interface QSFragmentStartableModule {
+ @Binds
+ @IntoMap
+ @ClassKey(QSFragmentStartable::class)
+ fun bindsQSFragmentStartable(startable: QSFragmentStartable): CoreStartable
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 72ae16e..1714f48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -71,6 +71,8 @@
import com.android.systemui.util.Utils;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -80,8 +82,6 @@
import java.util.Optional;
import java.util.Set;
-import dagger.Lazy;
-
/**
* Handles tasks and state related to media notifications. For example, there is a 'current' media
* notification, which this class keeps track of.
@@ -161,12 +161,49 @@
Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
}
mMediaArtworkProcessor.clearCache();
- mMediaMetadata = metadata;
+ mMediaMetadata = cleanMetadata(metadata);
dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
}
};
/**
+ * If this build is not configured for lockscreen artwork, clear artwork references from the
+ * metadata to avoid excess memory usage. Otherwise, return as is.
+ * @param data Original metadata
+ * @return a copy without artwork data, or original
+ */
+ private MediaMetadata cleanMetadata(MediaMetadata data) {
+ if (SHOW_LOCKSCREEN_MEDIA_ARTWORK) {
+ return data;
+ }
+ if (data == null) {
+ return null;
+ }
+ if (DEBUG_MEDIA) {
+ String[] artKeys = new String[] {
+ MediaMetadata.METADATA_KEY_ART,
+ MediaMetadata.METADATA_KEY_ALBUM_ART,
+ MediaMetadata.METADATA_KEY_DISPLAY_ICON,
+ MediaMetadata.METADATA_KEY_ALBUM_ART_URI,
+ MediaMetadata.METADATA_KEY_ART_URI,
+ MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI
+ };
+ Log.v(TAG, "DEBUG_MEDIA: removing artwork from metadata");
+ for (String key: artKeys) {
+ Log.v(TAG, " " + key + ": " + data.containsKey(key));
+ }
+ }
+ return new MediaMetadata.Builder(data)
+ .putBitmap(MediaMetadata.METADATA_KEY_ART, null)
+ .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, null)
+ .putBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON, null)
+ .putString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI, null)
+ .putString(MediaMetadata.METADATA_KEY_ART_URI, null)
+ .putString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI, null)
+ .build();
+ }
+
+ /**
* Injected constructor. See {@link CentralSurfacesModule}.
*/
public NotificationMediaManager(
@@ -313,6 +350,7 @@
return mMediaNotificationKey;
}
+ @VisibleForTesting
public MediaMetadata getMediaMetadata() {
return mMediaMetadata;
}
@@ -350,7 +388,7 @@
* update this manager's internal state.
* @return whether the current MediaMetadata changed (and needs to be announced to listeners).
*/
- boolean findPlayingMediaNotification(
+ private boolean findPlayingMediaNotification(
@NonNull Collection<NotificationEntry> allNotifications) {
boolean metaDataChanged = false;
// Promote the media notification with a controller in 'playing' state, if any.
@@ -383,7 +421,7 @@
clearCurrentMediaNotificationSession();
mMediaController = controller;
mMediaController.registerCallback(mMediaListener);
- mMediaMetadata = mMediaController.getMetadata();
+ mMediaMetadata = cleanMetadata(mMediaController.getMetadata());
if (DEBUG_MEDIA) {
Log.v(TAG, "DEBUG_MEDIA: insert listener, found new controller: "
+ mMediaController + ", receive metadata: " + mMediaMetadata);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index f579d30..b15d241 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -75,7 +75,7 @@
boolean DEBUG_WAKEUP_DELAY = Compile.IS_DEBUG;
// additional instrumentation for testing purposes; intended to be left on during development
boolean CHATTY = DEBUG;
- boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = true;
+ boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = false;
String ACTION_FAKE_ARTWORK = "fake_artwork";
int FADE_KEYGUARD_START_DELAY = 100;
int FADE_KEYGUARD_DURATION = 300;
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 fab334c..b3953a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -232,6 +232,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
+import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
@@ -1632,7 +1633,9 @@
}
}
mCentralSurfacesComponent = mCentralSurfacesComponentFactory.create();
- mFragmentService.addFragmentInstantiationProvider(mCentralSurfacesComponent);
+ mFragmentService.addFragmentInstantiationProvider(
+ CollapsedStatusBarFragment.class,
+ mCentralSurfacesComponent::createCollapsedStatusBarFragment);
mNotificationShadeWindowView = mCentralSurfacesComponent.getNotificationShadeWindowView();
mNotificationShadeWindowViewController = mCentralSurfacesComponent
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index a9920ec7..8f4b320 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -101,7 +101,8 @@
whenever(smallClockController.events).thenReturn(smallClockEvents)
whenever(largeClockController.events).thenReturn(largeClockEvents)
whenever(clock.events).thenReturn(events)
- whenever(clock.animations).thenReturn(animations)
+ whenever(smallClockController.animations).thenReturn(animations)
+ whenever(largeClockController.animations).thenReturn(animations)
whenever(smallClockController.config)
.thenReturn(ClockFaceConfig(tickRate = ClockTickRate.PER_MINUTE))
whenever(largeClockController.config)
@@ -184,7 +185,7 @@
keyguardCaptor.value.onKeyguardVisibilityChanged(true)
batteryCaptor.value.onBatteryLevelChanged(10, false, true)
- verify(animations).charge()
+ verify(animations, times(2)).charge()
}
@Test
@@ -198,7 +199,7 @@
batteryCaptor.value.onBatteryLevelChanged(10, false, true)
batteryCaptor.value.onBatteryLevelChanged(10, false, true)
- verify(animations, times(1)).charge()
+ verify(animations, times(2)).charge()
}
@Test
@@ -246,7 +247,7 @@
verify(animations, never()).doze(0f)
captor.value.onKeyguardVisibilityChanged(false)
- verify(animations, times(1)).doze(0f)
+ verify(animations, times(2)).doze(0f)
}
@Test
@@ -284,7 +285,7 @@
yield()
- verify(animations).doze(0.4f)
+ verify(animations, times(2)).doze(0.4f)
job.cancel()
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index fc906de..95db0c0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -184,13 +184,14 @@
when(mClockController.getEvents()).thenReturn(mClockEvents);
when(mSmallClockController.getEvents()).thenReturn(mClockFaceEvents);
when(mLargeClockController.getEvents()).thenReturn(mClockFaceEvents);
- when(mClockController.getAnimations()).thenReturn(mClockAnimations);
+ when(mLargeClockController.getAnimations()).thenReturn(mClockAnimations);
+ when(mSmallClockController.getAnimations()).thenReturn(mClockAnimations);
when(mClockRegistry.createCurrentClock()).thenReturn(mClockController);
when(mClockEventController.getClock()).thenReturn(mClockController);
when(mSmallClockController.getConfig())
- .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false));
+ .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false));
when(mLargeClockController.getConfig())
- .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false));
+ .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false));
mSliceView = new View(getContext());
when(mView.findViewById(R.id.keyguard_slice_view)).thenReturn(mSliceView);
@@ -384,9 +385,9 @@
assertEquals(View.VISIBLE, mFakeDateView.getVisibility());
when(mSmallClockController.getConfig())
- .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true));
+ .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false));
when(mLargeClockController.getConfig())
- .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true));
+ .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false));
verify(mClockRegistry).registerClockChangeListener(listenerArgumentCaptor.capture());
listenerArgumentCaptor.getValue().onCurrentClockChanged();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 2c1d2ad..a2c6329 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -130,7 +130,7 @@
public void updatePosition_primaryClockAnimation() {
ClockController mockClock = mock(ClockController.class);
when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock);
- when(mockClock.getConfig()).thenReturn(new ClockConfig(false, false, true));
+ when(mockClock.getConfig()).thenReturn(new ClockConfig(false, true));
mController.updatePosition(10, 15, 20f, true);
@@ -145,7 +145,7 @@
public void updatePosition_alternateClockAnimation() {
ClockController mockClock = mock(ClockController.class);
when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock);
- when(mockClock.getConfig()).thenReturn(new ClockConfig(false, true, true));
+ when(mockClock.getConfig()).thenReturn(new ClockConfig(true, true));
mController.updatePosition(10, 15, 20f, true);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 3eb9590..2962c14 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -155,6 +155,7 @@
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
import org.mockito.internal.util.reflection.FieldSetter;
+import org.mockito.quality.Strictness;
import java.util.ArrayList;
import java.util.Arrays;
@@ -290,7 +291,7 @@
when(mSessionTracker.getSessionId(SESSION_KEYGUARD)).thenReturn(mKeyguardInstanceId);
when(mUserManager.isUserUnlocked(anyInt())).thenReturn(true);
- when(mUserManager.isPrimaryUser()).thenReturn(true);
+ currentUserIsSystem();
when(mStrongAuthTracker.getStub()).thenReturn(mock(IStrongAuthTracker.Stub.class));
when(mStrongAuthTracker
.isUnlockingWithBiometricAllowed(anyBoolean() /* isClass3Biometric */))
@@ -303,6 +304,7 @@
mMockitoSession = ExtendedMockito.mockitoSession()
.spyStatic(SubscriptionManager.class)
+ .strictness(Strictness.WARN)
.startMocking();
ExtendedMockito.doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
.when(SubscriptionManager::getDefaultSubscriptionId);
@@ -958,7 +960,7 @@
public void requestFaceAuth_whenFaceAuthWasStarted_returnsTrue() throws RemoteException {
// This satisfies all the preconditions to run face auth.
keyguardNotGoingAway();
- currentUserIsPrimary();
+ currentUserIsSystem();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
biometricsEnabledForCurrentUser();
@@ -1465,7 +1467,7 @@
// Preconditions for sfps auth to run
keyguardNotGoingAway();
- currentUserIsPrimary();
+ currentUserIsSystem();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
biometricsEnabledForCurrentUser();
@@ -1501,7 +1503,7 @@
// GIVEN Preconditions for sfps auth to run
keyguardNotGoingAway();
- currentUserIsPrimary();
+ currentUserIsSystem();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
biometricsEnabledForCurrentUser();
@@ -1530,7 +1532,7 @@
// GIVEN Preconditions for sfps auth to run
keyguardNotGoingAway();
- currentUserIsPrimary();
+ currentUserIsSystem();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
biometricsEnabledForCurrentUser();
@@ -1682,7 +1684,7 @@
// Face auth should run when the following is true.
keyguardNotGoingAway();
occludingAppRequestsFaceAuth();
- currentUserIsPrimary();
+ currentUserIsSystem();
primaryAuthNotRequiredByStrongAuthTracker();
biometricsEnabledForCurrentUser();
currentUserDoesNotHaveTrust();
@@ -1703,7 +1705,7 @@
// Face auth should run when the following is true.
bouncerFullyVisibleAndNotGoingToSleep();
keyguardNotGoingAway();
- currentUserIsPrimary();
+ currentUserIsSystem();
primaryAuthNotRequiredByStrongAuthTracker();
biometricsEnabledForCurrentUser();
currentUserDoesNotHaveTrust();
@@ -1726,7 +1728,7 @@
// Face auth should run when the following is true.
bouncerFullyVisibleAndNotGoingToSleep();
keyguardNotGoingAway();
- currentUserIsPrimary();
+ currentUserIsSystem();
primaryAuthNotRequiredByStrongAuthTracker();
biometricsEnabledForCurrentUser();
currentUserDoesNotHaveTrust();
@@ -1747,7 +1749,7 @@
public void testShouldListenForFace_whenUserIsNotPrimary_returnsFalse() throws RemoteException {
cleanupKeyguardUpdateMonitor();
// This disables face auth
- when(mUserManager.isPrimaryUser()).thenReturn(false);
+ when(mUserManager.isSystemUser()).thenReturn(false);
mKeyguardUpdateMonitor =
new TestableKeyguardUpdateMonitor(mContext);
@@ -1771,7 +1773,7 @@
// Face auth should run when the following is true.
keyguardNotGoingAway();
bouncerFullyVisibleAndNotGoingToSleep();
- currentUserIsPrimary();
+ currentUserIsSystem();
biometricsEnabledForCurrentUser();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
@@ -1789,7 +1791,7 @@
throws RemoteException {
keyguardNotGoingAway();
bouncerFullyVisibleAndNotGoingToSleep();
- currentUserIsPrimary();
+ currentUserIsSystem();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
biometricsEnabledForCurrentUser();
@@ -1811,7 +1813,7 @@
// Face auth should run when the following is true.
keyguardNotGoingAway();
bouncerFullyVisibleAndNotGoingToSleep();
- currentUserIsPrimary();
+ currentUserIsSystem();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
biometricsEnabledForCurrentUser();
@@ -1831,7 +1833,7 @@
throws RemoteException {
keyguardNotGoingAway();
bouncerFullyVisibleAndNotGoingToSleep();
- currentUserIsPrimary();
+ currentUserIsSystem();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
biometricsEnabledForCurrentUser();
@@ -1852,7 +1854,7 @@
// Face auth should run when the following is true.
keyguardNotGoingAway();
bouncerFullyVisibleAndNotGoingToSleep();
- currentUserIsPrimary();
+ currentUserIsSystem();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
biometricsEnabledForCurrentUser();
@@ -1874,7 +1876,7 @@
throws RemoteException {
// Face auth should run when the following is true.
keyguardNotGoingAway();
- currentUserIsPrimary();
+ currentUserIsSystem();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
biometricsEnabledForCurrentUser();
@@ -1894,7 +1896,7 @@
throws RemoteException {
// Face auth should run when the following is true.
keyguardNotGoingAway();
- currentUserIsPrimary();
+ currentUserIsSystem();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
biometricsEnabledForCurrentUser();
@@ -1913,7 +1915,7 @@
public void testShouldListenForFace_whenKeyguardIsAwake_returnsTrue() throws RemoteException {
// Preconditions for face auth to run
keyguardNotGoingAway();
- currentUserIsPrimary();
+ currentUserIsSystem();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
biometricsEnabledForCurrentUser();
@@ -1938,7 +1940,7 @@
public void testShouldListenForFace_whenUdfpsFingerDown_returnsTrue() throws RemoteException {
// Preconditions for face auth to run
keyguardNotGoingAway();
- currentUserIsPrimary();
+ currentUserIsSystem();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
biometricsEnabledForCurrentUser();
@@ -1957,7 +1959,7 @@
throws RemoteException {
// Preconditions for face auth to run
keyguardNotGoingAway();
- currentUserIsPrimary();
+ currentUserIsSystem();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
biometricsEnabledForCurrentUser();
@@ -1975,7 +1977,7 @@
throws RemoteException {
// Preconditions for face auth to run
keyguardNotGoingAway();
- currentUserIsPrimary();
+ currentUserIsSystem();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
biometricsEnabledForCurrentUser();
@@ -2000,7 +2002,7 @@
throws RemoteException {
// Preconditions for face auth to run
keyguardNotGoingAway();
- currentUserIsPrimary();
+ currentUserIsSystem();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
biometricsEnabledForCurrentUser();
@@ -2322,7 +2324,7 @@
throws RemoteException {
keyguardNotGoingAway();
bouncerFullyVisibleAndNotGoingToSleep();
- currentUserIsPrimary();
+ currentUserIsSystem();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
biometricsEnabledForCurrentUser();
@@ -2453,7 +2455,7 @@
mKeyguardUpdateMonitor.mConfigFaceAuthSupportedPosture = DEVICE_POSTURE_CLOSED;
keyguardNotGoingAway();
bouncerFullyVisibleAndNotGoingToSleep();
- currentUserIsPrimary();
+ currentUserIsSystem();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
biometricsEnabledForCurrentUser();
@@ -2477,7 +2479,7 @@
mKeyguardUpdateMonitor.mConfigFaceAuthSupportedPosture = DEVICE_POSTURE_UNKNOWN;
keyguardNotGoingAway();
bouncerFullyVisibleAndNotGoingToSleep();
- currentUserIsPrimary();
+ currentUserIsSystem();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
biometricsEnabledForCurrentUser();
@@ -2528,19 +2530,6 @@
}
@Test
- public void testBatteryChangedIntent_unplugDevice_resetIncompatibleCharger() {
- mKeyguardUpdateMonitor.mIncompatibleCharger = true;
- Intent batteryChangedIntent =
- getBatteryIntent().putExtra(BatteryManager.EXTRA_PLUGGED, -1);
-
- mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(mContext, batteryChangedIntent);
-
- BatteryStatus status = verifyRefreshBatteryInfo();
- assertThat(status.incompatibleCharger.get()).isFalse();
- assertThat(mKeyguardUpdateMonitor.mIncompatibleCharger).isFalse();
- }
-
- @Test
public void unfoldWakeup_requestActiveUnlock_forceDismissKeyguard()
throws RemoteException {
// GIVEN shouldTriggerActiveUnlock
@@ -2888,8 +2877,8 @@
new FaceManager.AuthenticationResult(null, null, mCurrentUserId, false));
}
- private void currentUserIsPrimary() {
- when(mUserManager.isPrimaryUser()).thenReturn(true);
+ private void currentUserIsSystem() {
+ when(mUserManager.isSystemUser()).thenReturn(true);
}
private void biometricsNotDisabledThroughDevicePolicyManager() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
index 80c3e5e..937a7a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
@@ -172,64 +172,64 @@
}
@Test
- fun `canCameraGestureBeLaunched - status bar state is keyguard - returns true`() {
+ fun canCameraGestureBeLaunched_statusBarStateIsKeyguard_returnsTrue() {
assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isTrue()
}
@Test
- fun `canCameraGestureBeLaunched - state is shade-locked - returns true`() {
+ fun canCameraGestureBeLaunched_stateIsShadeLocked_returnsTrue() {
assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE_LOCKED)).isTrue()
}
@Test
- fun `canCameraGestureBeLaunched - state is keyguard - camera activity on top - returns true`() {
+ fun canCameraGestureBeLaunched_stateIsKeyguard_cameraActivityOnTop_returnsTrue() {
prepare(isCameraActivityRunningOnTop = true)
assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isTrue()
}
@Test
- fun `canCameraGestureBeLaunched - state is shade-locked - camera activity on top - true`() {
+ fun canCameraGestureBeLaunched_stateIsShadeLocked_cameraActivityOnTop_true() {
prepare(isCameraActivityRunningOnTop = true)
assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE_LOCKED)).isTrue()
}
@Test
- fun `canCameraGestureBeLaunched - not allowed by admin - returns false`() {
+ fun canCameraGestureBeLaunched_notAllowedByAdmin_returnsFalse() {
prepare(isCameraAllowedByAdmin = false)
assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isFalse()
}
@Test
- fun `canCameraGestureBeLaunched - intent does not resolve to any app - returns false`() {
+ fun canCameraGestureBeLaunched_intentDoesNotResolveToAnyApp_returnsFalse() {
prepare(installedCameraAppCount = 0)
assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isFalse()
}
@Test
- fun `canCameraGestureBeLaunched - state is shade - no running tasks - returns true`() {
+ fun canCameraGestureBeLaunched_stateIsShade_noRunningTasks_returnsTrue() {
prepare(isCameraActivityRunningOnTop = false, isTaskListEmpty = true)
assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE)).isTrue()
}
@Test
- fun `canCameraGestureBeLaunched - state is shade - camera activity on top - returns false`() {
+ fun canCameraGestureBeLaunched_stateIsShade_cameraActivityOnTop_returnsFalse() {
prepare(isCameraActivityRunningOnTop = true)
assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE)).isFalse()
}
@Test
- fun `canCameraGestureBeLaunched - state is shade - camera activity not on top - true`() {
+ fun canCameraGestureBeLaunched_stateIsShade_cameraActivityNotOnTop_true() {
assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE)).isTrue()
}
@Test
- fun `launchCamera - only one camera app installed - using secure screen lock option`() {
+ fun launchCamera_onlyOneCameraAppInstalled_usingSecureScreenLockOption() {
val source = 1337
underTest.launchCamera(source)
@@ -238,7 +238,7 @@
}
@Test
- fun `launchCamera - only one camera app installed - using non-secure screen lock option`() {
+ fun launchCamera_onlyOneCameraAppInstalled_usingNonSecureScreenLockOption() {
prepare(isUsingSecureScreenLockOption = false)
val source = 1337
@@ -248,7 +248,7 @@
}
@Test
- fun `launchCamera - multiple camera apps installed - using secure screen lock option`() {
+ fun launchCamera_multipleCameraAppsInstalled_usingSecureScreenLockOption() {
prepare(installedCameraAppCount = 2)
val source = 1337
@@ -262,7 +262,7 @@
}
@Test
- fun `launchCamera - multiple camera apps installed - using non-secure screen lock option`() {
+ fun launchCamera_multipleCameraAppsInstalled_usingNonSecureScreenLockOption() {
prepare(
isUsingSecureScreenLockOption = false,
installedCameraAppCount = 2,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
index 8600b7c..fe5fa1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -25,7 +25,6 @@
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHOWN_EXPANDED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHOWN_MINIMIZED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED;
-import static com.android.systemui.flags.Flags.CLIPBOARD_REMOTE_BEHAVIOR;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -121,7 +120,6 @@
mSampleClipData = new ClipData("Test", new String[]{"text/plain"},
new ClipData.Item("Test Item"));
- mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, false);
mOverlayController = new ClipboardOverlayController(
mContext,
@@ -234,7 +232,6 @@
@Test
public void test_remoteCopy_withFlagOn() {
- mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, true);
when(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(true);
mOverlayController.setClipData(mSampleClipData, "");
@@ -243,17 +240,7 @@
}
@Test
- public void test_remoteCopy_withFlagOff() {
- when(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(true);
-
- mOverlayController.setClipData(mSampleClipData, "");
-
- verify(mTimeoutHandler).resetTimeout();
- }
-
- @Test
public void test_nonRemoteCopy() {
- mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, true);
when(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(false);
mOverlayController.setClipData(mSampleClipData, "");
@@ -279,7 +266,6 @@
public void test_logOnClipboardActionsShown() {
ClipData.Item item = mSampleClipData.getItemAt(0);
item.setTextLinks(Mockito.mock(TextLinks.class));
- mFeatureFlags.set(CLIPBOARD_REMOTE_BEHAVIOR, true);
when(mClipboardUtils.isRemoteCopy(any(Context.class), any(ClipData.class), anyString()))
.thenReturn(true);
when(mClipboardUtils.getAction(any(TextLinks.class), anyString()))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
index fe352fd..1b2fc93d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
@@ -72,7 +72,7 @@
}
@Test
- fun `long-press`() = runTest {
+ fun longPress() = runTest {
val downX = 123
val downY = 456
dispatchTouchEvents(
@@ -91,7 +91,7 @@
}
@Test
- fun `long-press but feature not enabled`() = runTest {
+ fun longPressButFeatureNotEnabled() = runTest {
underTest.isLongPressHandlingEnabled = false
dispatchTouchEvents(
Down(
@@ -106,7 +106,7 @@
}
@Test
- fun `long-press but view not attached`() = runTest {
+ fun longPressButViewNotAttached() = runTest {
isAttachedToWindow = false
dispatchTouchEvents(
Down(
@@ -121,7 +121,7 @@
}
@Test
- fun `dragged too far to be considered a long-press`() = runTest {
+ fun draggedTooFarToBeConsideredAlongPress() = runTest {
dispatchTouchEvents(
Down(
x = 123,
@@ -138,7 +138,7 @@
}
@Test
- fun `held down too briefly to be considered a long-press`() = runTest {
+ fun heldDownTooBrieflyToBeConsideredAlongPress() = runTest {
dispatchTouchEvents(
Down(
x = 123,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt
index 87c66b5..75eec72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt
@@ -74,7 +74,7 @@
}
@Test
- fun `demo command flow - returns args`() =
+ fun demoCommandFlow_returnsArgs() =
testScope.runTest {
var latest: Bundle? = null
val flow = underTest.demoFlowForCommand(TEST_COMMAND)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt
index a2dc1eb..4ba1bc6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt
@@ -5,7 +5,6 @@
import android.test.suitebuilder.annotation.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.qs.QSFragment
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -13,9 +12,7 @@
@SmallTest
class FragmentServiceTest : SysuiTestCase() {
- private val fragmentCreator = TestFragmentCreator()
- private val fragmenetHostManagerFactory: FragmentHostManager.Factory = mock()
- private val fragmentCreatorFactory = FragmentService.FragmentCreator.Factory { fragmentCreator }
+ private val fragmentHostManagerFactory: FragmentHostManager.Factory = mock()
private lateinit var fragmentService: FragmentService
@@ -25,65 +22,29 @@
Looper.prepare()
}
- fragmentService =
- FragmentService(
- fragmentCreatorFactory,
- fragmenetHostManagerFactory,
- mock(),
- DumpManager()
- )
- }
-
- @Test
- fun constructor_addsFragmentCreatorMethodsToMap() {
- val map = fragmentService.injectionMap
- assertThat(map).hasSize(2)
- assertThat(map.keys).contains(QSFragment::class.java.name)
- assertThat(map.keys).contains(TestFragmentInCreator::class.java.name)
+ fragmentService = FragmentService(fragmentHostManagerFactory, mock(), DumpManager())
}
@Test
fun addFragmentInstantiationProvider_objectHasNoFragmentMethods_nothingAdded() {
- fragmentService.addFragmentInstantiationProvider(Object())
+ fragmentService.addFragmentInstantiationProvider(TestFragment::class.java) {
+ TestFragment()
+ }
- assertThat(fragmentService.injectionMap).hasSize(2)
- }
-
- @Test
- fun addFragmentInstantiationProvider_objectHasFragmentMethods_methodsAdded() {
- fragmentService.addFragmentInstantiationProvider(
- @Suppress("unused")
- object : Any() {
- fun createTestFragment2() = TestFragment2()
- fun createTestFragment3() = TestFragment3()
- }
- )
-
- val map = fragmentService.injectionMap
- assertThat(map).hasSize(4)
- assertThat(map.keys).contains(TestFragment2::class.java.name)
- assertThat(map.keys).contains(TestFragment3::class.java.name)
+ assertThat(fragmentService.injectionMap).hasSize(1)
}
@Test
fun addFragmentInstantiationProvider_objectFragmentMethodsAlreadyProvided_nothingAdded() {
- fragmentService.addFragmentInstantiationProvider(
- @Suppress("unused")
- object : Any() {
- fun createTestFragment() = TestFragmentInCreator()
- }
- )
+ fragmentService.addFragmentInstantiationProvider(TestFragment::class.java) {
+ TestFragment()
+ }
+ fragmentService.addFragmentInstantiationProvider(TestFragment::class.java) {
+ TestFragment()
+ }
- assertThat(fragmentService.injectionMap).hasSize(2)
+ assertThat(fragmentService.injectionMap).hasSize(1)
}
- class TestFragmentCreator : FragmentService.FragmentCreator {
- override fun createQSFragment(): QSFragment = mock()
- @Suppress("unused")
- fun createTestFragment(): TestFragmentInCreator = TestFragmentInCreator()
- }
-
- class TestFragmentInCreator : Fragment()
- class TestFragment2 : Fragment()
- class TestFragment3 : Fragment()
+ class TestFragment : Fragment()
}
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 1044131..4daecd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -211,7 +211,7 @@
}
@Test
- fun `onAttachInfo - reportsContext`() {
+ fun onAttachInfo_reportsContext() {
val callback: SystemUIAppComponentFactoryBase.ContextAvailableCallback = mock()
underTest.setContextAvailableCallback(callback)
@@ -254,7 +254,7 @@
}
@Test
- fun `insert and query selection`() =
+ fun insertAndQuerySelection() =
testScope.runTest {
val slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
val affordanceId = AFFORDANCE_2
@@ -278,7 +278,7 @@
}
@Test
- fun `query slots`() =
+ fun querySlotsProvidesTwoSlots() =
testScope.runTest {
assertThat(querySlots())
.isEqualTo(
@@ -296,7 +296,7 @@
}
@Test
- fun `query affordances`() =
+ fun queryAffordancesProvidesTwoAffordances() =
testScope.runTest {
assertThat(queryAffordances())
.isEqualTo(
@@ -316,7 +316,7 @@
}
@Test
- fun `delete and query selection`() =
+ fun deleteAndQuerySelection() =
testScope.runTest {
insertSelection(
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
@@ -351,7 +351,7 @@
}
@Test
- fun `delete all selections in a slot`() =
+ fun deleteAllSelectionsInAslot() =
testScope.runTest {
insertSelection(
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
index 5bb8367..e20d3af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
@@ -73,7 +73,7 @@
}
@Test
- fun `affordance triggered -- camera launch called`() {
+ fun affordanceTriggered_cameraLaunchCalled() {
// When
val result = underTest.onTriggered(null)
@@ -84,7 +84,7 @@
}
@Test
- fun `getPickerScreenState - default when launchable`() =
+ fun getPickerScreenState_defaultWhenLaunchable() =
testScope.runTest {
setLaunchable(true)
@@ -93,7 +93,7 @@
}
@Test
- fun `getPickerScreenState - unavailable when camera app not installed`() =
+ fun getPickerScreenState_unavailableWhenCameraAppNotInstalled() =
testScope.runTest {
setLaunchable(isCameraAppInstalled = false)
@@ -102,7 +102,7 @@
}
@Test
- fun `getPickerScreenState - unavailable when camera disabled by admin`() =
+ fun getPickerScreenState_unavailableWhenCameraDisabledByAdmin() =
testScope.runTest {
setLaunchable(isCameraDisabledByDeviceAdmin = true)
@@ -111,7 +111,7 @@
}
@Test
- fun `getPickerScreenState - unavailable when secure camera disabled by admin`() =
+ fun getPickerScreenState_unavailableWhenSecureCameraDisabledByAdmin() =
testScope.runTest {
setLaunchable(isSecureCameraDisabledByDeviceAdmin = true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
index 64839e2..c326a86 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
@@ -97,7 +97,7 @@
}
@Test
- fun `dnd not available - picker state hidden`() =
+ fun dndNotAvailable_pickerStateHidden() =
testScope.runTest {
// given
whenever(zenModeController.isZenAvailable).thenReturn(false)
@@ -113,7 +113,7 @@
}
@Test
- fun `dnd available - picker state visible`() =
+ fun dndAvailable_pickerStateVisible() =
testScope.runTest {
// given
whenever(zenModeController.isZenAvailable).thenReturn(true)
@@ -132,7 +132,7 @@
}
@Test
- fun `onTriggered - dnd mode is not ZEN_MODE_OFF - set to ZEN_MODE_OFF`() =
+ fun onTriggered_dndModeIsNotZEN_MODE_OFF_setToZEN_MODE_OFF() =
testScope.runTest {
// given
whenever(zenModeController.isZenAvailable).thenReturn(true)
@@ -157,7 +157,7 @@
}
@Test
- fun `onTriggered - dnd mode is ZEN_MODE_OFF - setting FOREVER - set zen without condition`() =
+ fun onTriggered_dndModeIsZEN_MODE_OFF_settingFOREVER_setZenWithoutCondition() =
testScope.runTest {
// given
whenever(zenModeController.isZenAvailable).thenReturn(true)
@@ -182,7 +182,7 @@
}
@Test
- fun `onTriggered - dnd ZEN_MODE_OFF - setting not FOREVER or PROMPT - zen with condition`() =
+ fun onTriggered_dndZEN_MODE_OFF_settingNotFOREVERorPROMPT_zenWithCondition() =
testScope.runTest {
// given
whenever(zenModeController.isZenAvailable).thenReturn(true)
@@ -207,7 +207,7 @@
}
@Test
- fun `onTriggered - dnd mode is ZEN_MODE_OFF - setting is PROMPT - show dialog`() =
+ fun onTriggered_dndModeIsZEN_MODE_OFF_settingIsPROMPT_showDialog() =
testScope.runTest {
// given
val expandable: Expandable = mock()
@@ -230,7 +230,7 @@
}
@Test
- fun `lockScreenState - dndAvailable starts as true - change to false - State is Hidden`() =
+ fun lockScreenState_dndAvailableStartsAsTrue_changeToFalse_StateIsHidden() =
testScope.runTest {
// given
whenever(zenModeController.isZenAvailable).thenReturn(true)
@@ -249,7 +249,7 @@
}
@Test
- fun `lockScreenState - dndMode starts as ZEN_MODE_OFF - change to not OFF - State Visible`() =
+ fun lockScreenState_dndModeStartsAsZEN_MODE_OFF_changeToNotOFF_StateVisible() =
testScope.runTest {
// given
whenever(zenModeController.isZenAvailable).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
index 31391ee..292d067 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt
@@ -60,7 +60,7 @@
}
@Test
- fun `flashlight is off -- triggered -- icon is on and active`() = runTest {
+ fun flashlightIsOff_triggered_iconIsOnAndActive() = runTest {
// given
flashlightController.isEnabled = false
flashlightController.isAvailable = true
@@ -83,7 +83,7 @@
}
@Test
- fun `flashlight is on -- triggered -- icon is off and inactive`() = runTest {
+ fun flashlightIsOn_triggered_iconIsOffAndInactive() = runTest {
// given
flashlightController.isEnabled = true
flashlightController.isAvailable = true
@@ -106,7 +106,7 @@
}
@Test
- fun `flashlight is on -- receives error -- icon is off and inactive`() = runTest {
+ fun flashlightIsOn_receivesError_iconIsOffAndInactive() = runTest {
// given
flashlightController.isEnabled = true
flashlightController.isAvailable = false
@@ -129,7 +129,7 @@
}
@Test
- fun `flashlight availability now off -- hidden`() = runTest {
+ fun flashlightAvailabilityNowOff_hidden() = runTest {
// given
flashlightController.isEnabled = true
flashlightController.isAvailable = false
@@ -146,7 +146,7 @@
}
@Test
- fun `flashlight availability now on -- flashlight on -- inactive and icon off`() = runTest {
+ fun flashlightAvailabilityNowOn_flashlightOn_inactiveAndIconOff() = runTest {
// given
flashlightController.isEnabled = true
flashlightController.isAvailable = false
@@ -168,7 +168,7 @@
}
@Test
- fun `flashlight availability now on -- flashlight off -- inactive and icon off`() = runTest {
+ fun flashlightAvailabilityNowOn_flashlightOff_inactiveAndIconOff() = runTest {
// given
flashlightController.isEnabled = false
flashlightController.isAvailable = false
@@ -190,7 +190,7 @@
}
@Test
- fun `flashlight available -- picker state default`() = runTest {
+ fun flashlightAvailable_pickerStateDefault() = runTest {
// given
flashlightController.isAvailable = true
@@ -202,7 +202,7 @@
}
@Test
- fun `flashlight not available -- picker state unavailable`() = runTest {
+ fun flashlightNotAvailable_pickerStateUnavailable() = runTest {
// given
flashlightController.isAvailable = false
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
index 2c1c04c..26f0cdb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
@@ -61,7 +61,7 @@
}
@Test
- fun `state - when cannot show while locked - returns Hidden`() = runBlockingTest {
+ fun state_whenCannotShowWhileLocked_returnsHidden() = runBlockingTest {
whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(false))
whenever(component.isEnabled()).thenReturn(true)
whenever(component.getTileImageId()).thenReturn(R.drawable.controls_icon)
@@ -81,7 +81,7 @@
}
@Test
- fun `state - when listing controller is missing - returns Hidden`() = runBlockingTest {
+ fun state_whenListingControllerIsMissing_returnsHidden() = runBlockingTest {
whenever(component.isEnabled()).thenReturn(true)
whenever(component.getTileImageId()).thenReturn(R.drawable.controls_icon)
whenever(component.getTileTitleId()).thenReturn(R.string.quick_controls_title)
@@ -100,7 +100,7 @@
}
@Test
- fun `onQuickAffordanceTriggered - canShowWhileLockedSetting is true`() = runBlockingTest {
+ fun onQuickAffordanceTriggered_canShowWhileLockedSettingIsTrue() = runBlockingTest {
whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(true))
val onClickedResult = underTest.onTriggered(expandable)
@@ -110,7 +110,7 @@
}
@Test
- fun `onQuickAffordanceTriggered - canShowWhileLockedSetting is false`() = runBlockingTest {
+ fun onQuickAffordanceTriggered_canShowWhileLockedSettingIsFalse() = runBlockingTest {
whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(false))
val onClickedResult = underTest.onTriggered(expandable)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
index 3bae7f7..9a18ba8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
@@ -106,7 +106,7 @@
}
@Test
- fun `Setting a setting selects the affordance`() =
+ fun settingAsettingSelectsTheAffordance() =
testScope.runTest {
val job = underTest.startSyncing()
@@ -129,7 +129,7 @@
}
@Test
- fun `Clearing a setting selects the affordance`() =
+ fun clearingAsettingSelectsTheAffordance() =
testScope.runTest {
val job = underTest.startSyncing()
@@ -156,7 +156,7 @@
}
@Test
- fun `Selecting an affordance sets its setting`() =
+ fun selectingAnAffordanceSetsItsSetting() =
testScope.runTest {
val job = underTest.startSyncing()
@@ -172,7 +172,7 @@
}
@Test
- fun `Unselecting an affordance clears its setting`() =
+ fun unselectingAnAffordanceClearsItsSetting() =
testScope.runTest {
val job = underTest.startSyncing()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
index 1259b47..6989f44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
@@ -164,7 +164,7 @@
}
@Test
- fun `remembers selections by user`() = runTest {
+ fun remembersSelectionsByUser() = runTest {
overrideResource(
R.array.config_keyguardQuickAffordanceDefaults,
arrayOf<String>(),
@@ -246,7 +246,7 @@
}
@Test
- fun `selections respects defaults`() = runTest {
+ fun selectionsRespectsDefaults() = runTest {
val slotId1 = "slot1"
val slotId2 = "slot2"
val affordanceId1 = "affordance1"
@@ -277,7 +277,7 @@
}
@Test
- fun `selections ignores defaults after selecting an affordance`() = runTest {
+ fun selectionsIgnoresDefaultsAfterSelectingAnAffordance() = runTest {
val slotId1 = "slot1"
val slotId2 = "slot2"
val affordanceId1 = "affordance1"
@@ -309,7 +309,7 @@
}
@Test
- fun `selections ignores defaults after clearing a slot`() = runTest {
+ fun selectionsIgnoresDefaultsAfterClearingAslot() = runTest {
val slotId1 = "slot1"
val slotId2 = "slot2"
val affordanceId1 = "affordance1"
@@ -341,7 +341,7 @@
}
@Test
- fun `responds to backup and restore by reloading the selections from disk`() = runTest {
+ fun respondsToBackupAndRestoreByReloadingTheSelectionsFromDisk() = runTest {
overrideResource(R.array.config_keyguardQuickAffordanceDefaults, arrayOf<String>())
val affordanceIdsBySlotId = mutableListOf<Map<String, List<String>>>()
val job =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
index c08ef42..a1c9f87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
@@ -112,7 +112,7 @@
}
@Test
- fun `selections - primary user process`() =
+ fun selections_primaryUserProcess() =
testScope.runTest {
val values = mutableListOf<Map<String, List<String>>>()
val job = launch { underTest.selections.toList(values) }
@@ -163,7 +163,7 @@
}
@Test
- fun `selections - secondary user process - always empty`() =
+ fun selections_secondaryUserProcess_alwaysEmpty() =
testScope.runTest {
whenever(userHandle.isSystem).thenReturn(false)
val values = mutableListOf<Map<String, List<String>>>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
index 925c06f..c38827a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
@@ -85,7 +85,7 @@
}
@Test
- fun `picker state - volume fixed - not available`() = testScope.runTest {
+ fun pickerState_volumeFixed_notAvailable() = testScope.runTest {
//given
whenever(audioManager.isVolumeFixed).thenReturn(true)
@@ -97,7 +97,7 @@
}
@Test
- fun `picker state - volume not fixed - available`() = testScope.runTest {
+ fun pickerState_volumeNotFixed_available() = testScope.runTest {
//given
whenever(audioManager.isVolumeFixed).thenReturn(false)
@@ -109,7 +109,7 @@
}
@Test
- fun `triggered - state was previously NORMAL - currently SILENT - move to previous state`() = testScope.runTest {
+ fun triggered_stateWasPreviouslyNORMAL_currentlySILENT_moveToPreviousState() = testScope.runTest {
//given
val ringerModeCapture = argumentCaptor<Int>()
whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
@@ -127,7 +127,7 @@
}
@Test
- fun `triggered - state is not SILENT - move to SILENT ringer`() = testScope.runTest {
+ fun triggered_stateIsNotSILENT_moveToSILENTringer() = testScope.runTest {
//given
val ringerModeCapture = argumentCaptor<Int>()
whenever(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt
index facc747..f243d7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt
@@ -101,7 +101,7 @@
}
@Test
- fun `feature flag is OFF - do nothing with keyguardQuickAffordanceRepository`() = testScope.runTest {
+ fun featureFlagIsOFF_doNothingWithKeyguardQuickAffordanceRepository() = testScope.runTest {
//given
whenever(featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)).thenReturn(false)
@@ -114,7 +114,7 @@
}
@Test
- fun `feature flag is ON - call to keyguardQuickAffordanceRepository`() = testScope.runTest {
+ fun featureFlagIsON_callToKeyguardQuickAffordanceRepository() = testScope.runTest {
//given
val ringerModeInternal = mock<MutableLiveData<Int>>()
whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
@@ -129,7 +129,7 @@
}
@Test
- fun `ringer mode is changed to SILENT - do not save to shared preferences`() = testScope.runTest {
+ fun ringerModeIsChangedToSILENT_doNotSaveToSharedPreferences() = testScope.runTest {
//given
val ringerModeInternal = mock<MutableLiveData<Int>>()
val observerCaptor = argumentCaptor<Observer<Int>>()
@@ -147,7 +147,7 @@
}
@Test
- fun `ringerModeInternal changes to something not SILENT - is set in sharedpreferences`() = testScope.runTest {
+ fun ringerModeInternalChangesToSomethingNotSILENT_isSetInSharedpreferences() = testScope.runTest {
//given
val newRingerMode = 99
val observerCaptor = argumentCaptor<Observer<Int>>()
@@ -172,7 +172,7 @@
}
@Test
- fun `MUTE is in selections - observe ringerModeInternal`() = testScope.runTest {
+ fun MUTEisInSelections_observeRingerModeInternal() = testScope.runTest {
//given
val ringerModeInternal = mock<MutableLiveData<Int>>()
whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal)
@@ -187,7 +187,7 @@
}
@Test
- fun `MUTE is in selections 2x - observe ringerModeInternal`() = testScope.runTest {
+ fun MUTEisInSelections2x_observeRingerModeInternal() = testScope.runTest {
//given
val config: KeyguardQuickAffordanceConfig = mock()
whenever(config.key).thenReturn(BuiltInKeyguardQuickAffordanceKeys.MUTE)
@@ -206,7 +206,7 @@
}
@Test
- fun `MUTE is not in selections - stop observing ringerModeInternal`() = testScope.runTest {
+ fun MUTEisNotInSelections_stopObservingRingerModeInternal() = testScope.runTest {
//given
val config: KeyguardQuickAffordanceConfig = mock()
whenever(config.key).thenReturn("notmutequickaffordance")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
index 1adf808..faf18d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
@@ -55,7 +55,7 @@
}
@Test
- fun `affordance - sets up registration and delivers initial model`() = runBlockingTest {
+ fun affordance_setsUpRegistrationAndDeliversInitialModel() = runBlockingTest {
whenever(controller.isEnabledForLockScreenButton).thenReturn(true)
var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
@@ -75,7 +75,7 @@
}
@Test
- fun `affordance - scanner activity changed - delivers model with updated intent`() =
+ fun affordance_scannerActivityChanged_deliversModelWithUpdatedIntent() =
runBlockingTest {
whenever(controller.isEnabledForLockScreenButton).thenReturn(true)
var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
@@ -93,7 +93,7 @@
}
@Test
- fun `affordance - scanner preference changed - delivers visible model`() = runBlockingTest {
+ fun affordance_scannerPreferenceChanged_deliversVisibleModel() = runBlockingTest {
var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>()
@@ -109,7 +109,7 @@
}
@Test
- fun `affordance - scanner preference changed - delivers none`() = runBlockingTest {
+ fun affordance_scannerPreferenceChanged_deliversNone() = runBlockingTest {
var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>()
@@ -136,7 +136,7 @@
}
@Test
- fun `getPickerScreenState - enabled if configured on device - can open camera`() = runTest {
+ fun getPickerScreenState_enabledIfConfiguredOnDevice_canOpenCamera() = runTest {
whenever(controller.isAvailableOnDevice).thenReturn(true)
whenever(controller.isAbleToOpenCameraApp).thenReturn(true)
@@ -145,7 +145,7 @@
}
@Test
- fun `getPickerScreenState - disabled if configured on device - cannot open camera`() = runTest {
+ fun getPickerScreenState_disabledIfConfiguredOnDevice_cannotOpenCamera() = runTest {
whenever(controller.isAvailableOnDevice).thenReturn(true)
whenever(controller.isAbleToOpenCameraApp).thenReturn(false)
@@ -154,7 +154,7 @@
}
@Test
- fun `getPickerScreenState - unavailable if not configured on device`() = runTest {
+ fun getPickerScreenState_unavailableIfNotConfiguredOnDevice() = runTest {
whenever(controller.isAvailableOnDevice).thenReturn(false)
whenever(controller.isAbleToOpenCameraApp).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index 752963f..952882d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -69,7 +69,7 @@
}
@Test
- fun `affordance - keyguard showing - has wallet card - visible model`() = runBlockingTest {
+ fun affordance_keyguardShowing_hasWalletCard_visibleModel() = runBlockingTest {
setUpState()
var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
@@ -90,7 +90,7 @@
}
@Test
- fun `affordance - wallet not enabled - model is none`() = runBlockingTest {
+ fun affordance_walletNotEnabled_modelIsNone() = runBlockingTest {
setUpState(isWalletEnabled = false)
var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
@@ -102,7 +102,7 @@
}
@Test
- fun `affordance - query not successful - model is none`() = runBlockingTest {
+ fun affordance_queryNotSuccessful_modelIsNone() = runBlockingTest {
setUpState(isWalletQuerySuccessful = false)
var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
@@ -114,7 +114,7 @@
}
@Test
- fun `affordance - no selected card - model is none`() = runBlockingTest {
+ fun affordance_noSelectedCard_modelIsNone() = runBlockingTest {
setUpState(hasSelectedCard = false)
var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
@@ -143,7 +143,7 @@
}
@Test
- fun `getPickerScreenState - default`() = runTest {
+ fun getPickerScreenState_default() = runTest {
setUpState()
assertThat(underTest.getPickerScreenState())
@@ -151,7 +151,7 @@
}
@Test
- fun `getPickerScreenState - unavailable`() = runTest {
+ fun getPickerScreenState_unavailable() = runTest {
setUpState(
isWalletServiceAvailable = false,
)
@@ -161,7 +161,7 @@
}
@Test
- fun `getPickerScreenState - disabled when the feature is not enabled`() = runTest {
+ fun getPickerScreenState_disabledWhenTheFeatureIsNotEnabled() = runTest {
setUpState(
isWalletEnabled = false,
)
@@ -171,7 +171,7 @@
}
@Test
- fun `getPickerScreenState - disabled when there is no card`() = runTest {
+ fun getPickerScreenState_disabledWhenThereIsNoCard() = runTest {
setUpState(
hasSelectedCard = false,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
index f1b9c5f..a9b9c90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
@@ -73,7 +73,7 @@
}
@Test
- fun `lockScreenState - visible when launchable`() =
+ fun lockScreenState_visibleWhenLaunchable() =
testScope.runTest {
setLaunchable()
@@ -84,7 +84,7 @@
}
@Test
- fun `lockScreenState - hidden when app not installed on device`() =
+ fun lockScreenState_hiddenWhenAppNotInstalledOnDevice() =
testScope.runTest {
setLaunchable(isVideoCameraAppInstalled = false)
@@ -95,7 +95,7 @@
}
@Test
- fun `lockScreenState - hidden when camera disabled by admin`() =
+ fun lockScreenState_hiddenWhenCameraDisabledByAdmin() =
testScope.runTest {
setLaunchable(isCameraDisabledByAdmin = true)
@@ -106,7 +106,7 @@
}
@Test
- fun `getPickerScreenState - default when launchable`() =
+ fun getPickerScreenState_defaultWhenLaunchable() =
testScope.runTest {
setLaunchable()
@@ -115,7 +115,7 @@
}
@Test
- fun `getPickerScreenState - unavailable when app not installed on device`() =
+ fun getPickerScreenState_unavailableWhenAppNotInstalledOnDevice() =
testScope.runTest {
setLaunchable(isVideoCameraAppInstalled = false)
@@ -124,7 +124,7 @@
}
@Test
- fun `getPickerScreenState - unavailable when camera disabled by admin`() =
+ fun getPickerScreenState_unavailableWhenCameraDisabledByAdmin() =
testScope.runTest {
setLaunchable(isCameraDisabledByAdmin = true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index fc75d47..fa40fc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -21,6 +21,7 @@
import android.content.pm.UserInfo
import android.content.pm.UserInfo.FLAG_PRIMARY
import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_CANCELED
+import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE
import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT
import android.hardware.biometrics.ComponentInfoInternal
import android.hardware.face.FaceAuthenticateOptions
@@ -71,7 +72,6 @@
import java.io.PrintWriter
import java.io.StringWriter
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.launch
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.TestScope
@@ -541,14 +541,6 @@
}
@Test
- fun authenticateDoesNotRunWhenCurrentUserIsNotPrimary() =
- testScope.runTest {
- testGatingCheckForFaceAuth {
- launch { fakeUserRepository.setSelectedUserInfo(secondaryUser) }
- }
- }
-
- @Test
fun authenticateDoesNotRunWhenSecureCameraIsActive() =
testScope.runTest {
testGatingCheckForFaceAuth {
@@ -652,6 +644,58 @@
}
@Test
+ fun isAuthenticatedIsResetToFalseWhenDeviceStartsGoingToSleep() =
+ testScope.runTest {
+ initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
+
+ triggerFaceAuth(false)
+
+ authenticationCallback.value.onAuthenticationSucceeded(
+ mock(FaceManager.AuthenticationResult::class.java)
+ )
+
+ assertThat(authenticated()).isTrue()
+
+ keyguardRepository.setWakefulnessModel(
+ WakefulnessModel(
+ WakefulnessState.STARTING_TO_SLEEP,
+ isWakingUpOrAwake = false,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.POWER_BUTTON
+ )
+ )
+
+ assertThat(authenticated()).isFalse()
+ }
+
+ @Test
+ fun isAuthenticatedIsResetToFalseWhenDeviceGoesToSleep() =
+ testScope.runTest {
+ initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
+
+ triggerFaceAuth(false)
+
+ authenticationCallback.value.onAuthenticationSucceeded(
+ mock(FaceManager.AuthenticationResult::class.java)
+ )
+
+ assertThat(authenticated()).isTrue()
+
+ keyguardRepository.setWakefulnessModel(
+ WakefulnessModel(
+ WakefulnessState.ASLEEP,
+ isWakingUpOrAwake = false,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.POWER_BUTTON
+ )
+ )
+
+ assertThat(authenticated()).isFalse()
+ }
+
+ @Test
fun isAuthenticatedIsResetToFalseWhenUserIsSwitching() =
testScope.runTest {
initCollectors()
@@ -824,6 +868,26 @@
verify(faceManager).scheduleWatchdog()
}
+ @Test
+ fun retryFaceIfThereIsAHardwareError() =
+ testScope.runTest {
+ initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
+
+ triggerFaceAuth(fallbackToDetect = false)
+ clearInvocations(faceManager)
+
+ authenticationCallback.value.onAuthenticationError(
+ FACE_ERROR_HW_UNAVAILABLE,
+ "HW unavailable"
+ )
+
+ advanceTimeBy(DeviceEntryFaceAuthRepositoryImpl.HAL_ERROR_RETRY_TIMEOUT)
+ runCurrent()
+
+ faceAuthenticateIsCalled()
+ }
+
private suspend fun TestScope.testGatingCheckForFaceAuth(gatingCheckModifier: () -> Unit) {
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index a668af3..12b8261 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
@@ -258,7 +258,7 @@
}
@Test
- fun `selections for secondary user`() =
+ fun selectionsForSecondaryUser() =
testScope.runTest {
userTracker.set(
userInfos =
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 3fd97da..b53a434 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
@@ -281,7 +281,7 @@
}
@Test
- fun `isDozing - starts with correct initial value for isDozing`() =
+ fun isDozing_startsWithCorrectInitialValueForIsDozing() =
testScope.runTest {
var latest: Boolean? = null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
index d9d4013..d0bfaa9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
@@ -70,7 +70,7 @@
}
@Test
- fun `startTransition runs animator to completion`() =
+ fun startTransitionRunsAnimatorToCompletion() =
TestScope().runTest {
val steps = mutableListOf<TransitionStep>()
val job = underTest.transition(AOD, LOCKSCREEN).onEach { steps.add(it) }.launchIn(this)
@@ -86,7 +86,7 @@
}
@Test
- fun `starting second transition will cancel the first transition`() =
+ fun startingSecondTransitionWillCancelTheFirstTransition() =
TestScope().runTest {
val steps = mutableListOf<TransitionStep>()
val job = underTest.transition(AOD, LOCKSCREEN).onEach { steps.add(it) }.launchIn(this)
@@ -114,7 +114,7 @@
}
@Test
- fun `Null animator enables manual control with updateTransition`() =
+ fun nullAnimatorEnablesManualControlWithUpdateTransition() =
TestScope().runTest {
val steps = mutableListOf<TransitionStep>()
val job = underTest.transition(AOD, LOCKSCREEN).onEach { steps.add(it) }.launchIn(this)
@@ -146,13 +146,13 @@
}
@Test
- fun `Attempt to manually update transition with invalid UUID throws exception`() {
+ fun attemptTomanuallyUpdateTransitionWithInvalidUUIDthrowsException() {
underTest.updateTransition(UUID.randomUUID(), 0f, TransitionState.RUNNING)
assertThat(wtfHandler.failed).isTrue()
}
@Test
- fun `Attempt to manually update transition after FINISHED state throws exception`() {
+ fun attemptToManuallyUpdateTransitionAfterFINISHEDstateThrowsException() {
val uuid =
underTest.startTransition(
TransitionInfo(
@@ -171,7 +171,7 @@
}
@Test
- fun `Attempt to manually update transition after CANCELED state throws exception`() {
+ fun attemptToManuallyUpdateTransitionAfterCANCELEDstateThrowsException() {
val uuid =
underTest.startTransition(
TransitionInfo(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
index f9493d1..9daf3f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
@@ -48,7 +48,7 @@
}
@Test
- fun `nextRevealEffect - effect switches between default and biometric with no dupes`() =
+ fun nextRevealEffect_effectSwitchesBetweenDefaultAndBiometricWithNoDupes() =
runTest {
val values = mutableListOf<LightRevealEffect>()
val job = launch { underTest.revealEffect.collect { values.add(it) } }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
index 3d1d2f4..5da1a84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -279,6 +279,23 @@
}
@Test
+ fun faceAuthIsCancelledWhenUserInputOnPrimaryBouncer() =
+ testScope.runTest {
+ underTest.start()
+
+ underTest.onSwipeUpOnBouncer()
+
+ runCurrent()
+ assertThat(faceAuthRepository.isAuthRunning.value).isTrue()
+
+ underTest.onPrimaryBouncerUserInput()
+
+ runCurrent()
+
+ assertThat(faceAuthRepository.isAuthRunning.value).isFalse()
+ }
+
+ @Test
fun faceAuthIsRequestedWhenSwipeUpOnBouncer() =
testScope.runTest {
underTest.start()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
index 77bb12c..8a0cf4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
@@ -92,7 +92,7 @@
}
@Test
- fun `isEnabled - always false when quick settings are visible`() =
+ fun isEnabled_alwaysFalseWhenQuickSettingsAreVisible() =
testScope.runTest {
val isEnabled = collectLastValue(underTest.isLongPressHandlingEnabled)
KeyguardState.values().forEach { keyguardState ->
@@ -163,7 +163,7 @@
}
@Test
- fun `long pressed - close dialogs broadcast received - popup dismissed`() =
+ fun longPressed_closeDialogsBroadcastReceived_popupDismissed() =
testScope.runTest {
val isMenuVisible by collectLastValue(underTest.isMenuVisible)
runCurrent()
@@ -211,7 +211,7 @@
}
@Test
- fun `logs when menu is shown`() =
+ fun logsWhenMenuIsShown() =
testScope.runTest {
collectLastValue(underTest.isMenuVisible)
runCurrent()
@@ -223,7 +223,7 @@
}
@Test
- fun `logs when menu is clicked`() =
+ fun logsWhenMenuIsClicked() =
testScope.runTest {
collectLastValue(underTest.isMenuVisible)
runCurrent()
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 503e002..96fff64 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
@@ -195,7 +195,7 @@
}
@Test
- fun `quickAffordance - bottom start affordance is visible`() =
+ fun quickAffordance_bottomStartAffordanceIsVisible() =
testScope.runTest {
val configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS
homeControls.setState(
@@ -221,7 +221,7 @@
}
@Test
- fun `quickAffordance - bottom end affordance is visible`() =
+ fun quickAffordance_bottomEndAffordanceIsVisible() =
testScope.runTest {
val configKey = BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
quickAccessWallet.setState(
@@ -246,7 +246,7 @@
}
@Test
- fun `quickAffordance - hidden when all features are disabled by device policy`() =
+ fun quickAffordance_hiddenWhenAllFeaturesAreDisabledByDevicePolicy() =
testScope.runTest {
whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
@@ -265,7 +265,7 @@
}
@Test
- fun `quickAffordance - hidden when shortcuts feature is disabled by device policy`() =
+ fun quickAffordance_hiddenWhenShortcutsFeatureIsDisabledByDevicePolicy() =
testScope.runTest {
whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL)
@@ -284,7 +284,7 @@
}
@Test
- fun `quickAffordance - hidden when quick settings is visible`() =
+ fun quickAffordance_hiddenWhenQuickSettingsIsVisible() =
testScope.runTest {
repository.setQuickSettingsVisible(true)
quickAccessWallet.setState(
@@ -302,7 +302,7 @@
}
@Test
- fun `quickAffordance - bottom start affordance hidden while dozing`() =
+ fun quickAffordance_bottomStartAffordanceHiddenWhileDozing() =
testScope.runTest {
repository.setDozing(true)
homeControls.setState(
@@ -319,7 +319,7 @@
}
@Test
- fun `quickAffordance - bottom start affordance hidden when lockscreen is not showing`() =
+ fun quickAffordance_bottomStartAffordanceHiddenWhenLockscreenIsNotShowing() =
testScope.runTest {
repository.setKeyguardShowing(false)
homeControls.setState(
@@ -336,7 +336,7 @@
}
@Test
- fun `quickAffordanceAlwaysVisible - even when lock screen not showing and dozing`() =
+ fun quickAffordanceAlwaysVisible_evenWhenLockScreenNotShowingAndDozing() =
testScope.runTest {
repository.setKeyguardShowing(false)
repository.setDozing(true)
@@ -511,7 +511,7 @@
}
@Test
- fun `unselect - one`() =
+ fun unselect_one() =
testScope.runTest {
featureFlags.set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true)
homeControls.setState(
@@ -588,7 +588,7 @@
}
@Test
- fun `unselect - all`() =
+ fun unselect_all() =
testScope.runTest {
featureFlags.set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true)
homeControls.setState(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index 276b3e3..503687d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -52,7 +52,7 @@
}
@Test
- fun `transition collectors receives only appropriate events`() = runTest {
+ fun transitionCollectorsReceivesOnlyAppropriateEvents() = runTest {
val lockscreenToAodSteps by collectValues(underTest.lockscreenToAodTransition)
val aodToLockscreenSteps by collectValues(underTest.aodToLockscreenTransition)
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 e2d0ec3..fe65236 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
@@ -180,7 +180,7 @@
}
@Test
- fun `DREAMING to LOCKSCREEN`() =
+ fun DREAMINGtoLOCKSCREEN() =
testScope.runTest {
// GIVEN a device is dreaming
keyguardRepository.setDreamingWithOverlay(true)
@@ -215,7 +215,7 @@
}
@Test
- fun `LOCKSCREEN to PRIMARY_BOUNCER via bouncer showing call`() =
+ fun LOCKSCREENtoPRIMARY_BOUNCERviaBouncerShowingCall() =
testScope.runTest {
// GIVEN a device that has at least woken up
keyguardRepository.setWakefulnessModel(startingToWake())
@@ -242,7 +242,7 @@
}
@Test
- fun `OCCLUDED to DOZING`() =
+ fun OCCLUDEDtoDOZING() =
testScope.runTest {
// GIVEN a device with AOD not available
keyguardRepository.setAodAvailable(false)
@@ -269,7 +269,7 @@
}
@Test
- fun `OCCLUDED to AOD`() =
+ fun OCCLUDEDtoAOD() =
testScope.runTest {
// GIVEN a device with AOD available
keyguardRepository.setAodAvailable(true)
@@ -296,7 +296,7 @@
}
@Test
- fun `LOCKSCREEN to DREAMING`() =
+ fun LOCKSCREENtoDREAMING() =
testScope.runTest {
// GIVEN a device that is not dreaming or dozing
keyguardRepository.setDreamingWithOverlay(false)
@@ -327,7 +327,7 @@
}
@Test
- fun `LOCKSCREEN to DOZING`() =
+ fun LOCKSCREENtoDOZING() =
testScope.runTest {
// GIVEN a device with AOD not available
keyguardRepository.setAodAvailable(false)
@@ -354,7 +354,7 @@
}
@Test
- fun `LOCKSCREEN to AOD`() =
+ fun LOCKSCREENtoAOD() =
testScope.runTest {
// GIVEN a device with AOD available
keyguardRepository.setAodAvailable(true)
@@ -381,7 +381,7 @@
}
@Test
- fun `DOZING to LOCKSCREEN`() =
+ fun DOZINGtoLOCKSCREEN() =
testScope.runTest {
// GIVEN a prior transition has run to DOZING
runTransition(KeyguardState.LOCKSCREEN, KeyguardState.DOZING)
@@ -404,7 +404,7 @@
}
@Test
- fun `DOZING to LOCKSCREEN cannot be interruped by DREAMING`() =
+ fun DOZINGtoLOCKSCREENcannotBeInterrupedByDREAMING() =
testScope.runTest {
// GIVEN a prior transition has started to LOCKSCREEN
transitionRepository.sendTransitionStep(
@@ -430,7 +430,7 @@
}
@Test
- fun `DOZING to GONE`() =
+ fun DOZINGtoGONE() =
testScope.runTest {
// GIVEN a prior transition has run to DOZING
runTransition(KeyguardState.LOCKSCREEN, KeyguardState.DOZING)
@@ -453,7 +453,7 @@
}
@Test
- fun `GONE to DOZING`() =
+ fun GONEtoDOZING() =
testScope.runTest {
// GIVEN a device with AOD not available
keyguardRepository.setAodAvailable(false)
@@ -480,7 +480,7 @@
}
@Test
- fun `GONE to AOD`() =
+ fun GONEtoAOD() =
testScope.runTest {
// GIVEN a device with AOD available
keyguardRepository.setAodAvailable(true)
@@ -507,7 +507,7 @@
}
@Test
- fun `GONE to LOCKSREEN`() =
+ fun GONEtoLOCKSREEN() =
testScope.runTest {
// GIVEN a prior transition has run to GONE
runTransition(KeyguardState.LOCKSCREEN, KeyguardState.GONE)
@@ -530,7 +530,7 @@
}
@Test
- fun `GONE to DREAMING`() =
+ fun GONEtoDREAMING() =
testScope.runTest {
// GIVEN a device that is not dreaming or dozing
keyguardRepository.setDreamingWithOverlay(false)
@@ -561,7 +561,7 @@
}
@Test
- fun `ALTERNATE_BOUNCER to PRIMARY_BOUNCER`() =
+ fun ALTERNATE_BOUNCERtoPRIMARY_BOUNCER() =
testScope.runTest {
// GIVEN a prior transition has run to ALTERNATE_BOUNCER
runTransition(KeyguardState.LOCKSCREEN, KeyguardState.ALTERNATE_BOUNCER)
@@ -584,7 +584,7 @@
}
@Test
- fun `ALTERNATE_BOUNCER to AOD`() =
+ fun ALTERNATE_BOUNCERtoAOD() =
testScope.runTest {
// GIVEN a prior transition has run to ALTERNATE_BOUNCER
bouncerRepository.setAlternateVisible(true)
@@ -613,7 +613,7 @@
}
@Test
- fun `ALTERNATE_BOUNCER to DOZING`() =
+ fun ALTERNATE_BOUNCERtoDOZING() =
testScope.runTest {
// GIVEN a prior transition has run to ALTERNATE_BOUNCER
bouncerRepository.setAlternateVisible(true)
@@ -643,7 +643,7 @@
}
@Test
- fun `ALTERNATE_BOUNCER to LOCKSCREEN`() =
+ fun ALTERNATE_BOUNCERtoLOCKSCREEN() =
testScope.runTest {
// GIVEN a prior transition has run to ALTERNATE_BOUNCER
bouncerRepository.setAlternateVisible(true)
@@ -671,7 +671,7 @@
}
@Test
- fun `PRIMARY_BOUNCER to AOD`() =
+ fun PRIMARY_BOUNCERtoAOD() =
testScope.runTest {
// GIVEN a prior transition has run to PRIMARY_BOUNCER
bouncerRepository.setPrimaryShow(true)
@@ -699,7 +699,7 @@
}
@Test
- fun `PRIMARY_BOUNCER to DOZING`() =
+ fun PRIMARY_BOUNCERtoDOZING() =
testScope.runTest {
// GIVEN a prior transition has run to PRIMARY_BOUNCER
bouncerRepository.setPrimaryShow(true)
@@ -727,7 +727,7 @@
}
@Test
- fun `PRIMARY_BOUNCER to LOCKSCREEN`() =
+ fun PRIMARY_BOUNCERtoLOCKSCREEN() =
testScope.runTest {
// GIVEN a prior transition has run to PRIMARY_BOUNCER
bouncerRepository.setPrimaryShow(true)
@@ -754,7 +754,7 @@
}
@Test
- fun `OCCLUDED to GONE`() =
+ fun OCCLUDEDtoGONE() =
testScope.runTest {
// GIVEN a device on lockscreen
keyguardRepository.setKeyguardShowing(true)
@@ -785,7 +785,7 @@
}
@Test
- fun `OCCLUDED to LOCKSCREEN`() =
+ fun OCCLUDEDtoLOCKSCREEN() =
testScope.runTest {
// GIVEN a device on lockscreen
keyguardRepository.setKeyguardShowing(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
index 6236616..359854b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -69,7 +69,7 @@
}
@Test
- fun `lightRevealEffect - does not change during keyguard transition`() =
+ fun lightRevealEffect_doesNotChangeDuringKeyguardTransition() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<LightRevealEffect>()
val job = underTest.lightRevealEffect.onEach(values::add).launchIn(this)
@@ -103,7 +103,7 @@
}
@Test
- fun `revealAmount - inverted when appropriate`() =
+ fun revealAmount_invertedWhenAppropriate() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<Float>()
val job = underTest.revealAmount.onEach(values::add).launchIn(this)
@@ -132,7 +132,7 @@
}
@Test
- fun `revealAmount - ignores transitions that do not affect reveal amount`() =
+ fun revealAmount_ignoresTransitionsThatDoNotAffectRevealAmount() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<Float>()
val job = underTest.revealAmount.onEach(values::add).launchIn(this)
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 224eec1..2361c59 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
@@ -251,7 +251,7 @@
}
@Test
- fun `startButton - present - visible model - starts activity on click`() =
+ fun startButton_present_visibleModel_startsActivityOnClick() =
testScope.runTest {
repository.setKeyguardShowing(true)
val latest = collectLastValue(underTest.startButton)
@@ -280,7 +280,7 @@
}
@Test
- fun `startButton - hidden when device policy disables all keyguard features`() =
+ fun startButton_hiddenWhenDevicePolicyDisablesAllKeyguardFeatures() =
testScope.runTest {
whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
@@ -315,7 +315,7 @@
}
@Test
- fun `startButton - in preview mode - visible even when keyguard not showing`() =
+ fun startButton_inPreviewMode_visibleEvenWhenKeyguardNotShowing() =
testScope.runTest {
underTest.enablePreviewMode(
initiallySelectedSlotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
@@ -359,7 +359,7 @@
}
@Test
- fun `endButton - in higlighted preview mode - dimmed when other is selected`() =
+ fun endButton_inHiglightedPreviewMode_dimmedWhenOtherIsSelected() =
testScope.runTest {
underTest.enablePreviewMode(
initiallySelectedSlotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
@@ -416,7 +416,7 @@
}
@Test
- fun `endButton - present - visible model - do nothing on click`() =
+ fun endButton_present_visibleModel_doNothingOnClick() =
testScope.runTest {
repository.setKeyguardShowing(true)
val latest = collectLastValue(underTest.endButton)
@@ -445,7 +445,7 @@
}
@Test
- fun `startButton - not present - model is hidden`() =
+ fun startButton_notPresent_modelIsHidden() =
testScope.runTest {
val latest = collectLastValue(underTest.startButton)
@@ -524,7 +524,7 @@
}
@Test
- fun `alpha - in preview mode - does not change`() =
+ fun alpha_inPreviewMode_doesNotChange() =
testScope.runTest {
underTest.enablePreviewMode(
initiallySelectedSlotId = null,
@@ -629,7 +629,7 @@
}
@Test
- fun `isClickable - true when alpha at threshold`() =
+ fun isClickable_trueWhenAlphaAtThreshold() =
testScope.runTest {
repository.setKeyguardShowing(true)
repository.setBottomAreaAlpha(
@@ -661,7 +661,7 @@
}
@Test
- fun `isClickable - true when alpha above threshold`() =
+ fun isClickable_trueWhenAlphaAboveThreshold() =
testScope.runTest {
repository.setKeyguardShowing(true)
val latest = collectLastValue(underTest.startButton)
@@ -692,7 +692,7 @@
}
@Test
- fun `isClickable - false when alpha below threshold`() =
+ fun isClickable_falseWhenAlphaBelowThreshold() =
testScope.runTest {
repository.setKeyguardShowing(true)
val latest = collectLastValue(underTest.startButton)
@@ -723,7 +723,7 @@
}
@Test
- fun `isClickable - false when alpha at zero`() =
+ fun isClickable_falseWhenAlphaAtZero() =
testScope.runTest {
repository.setKeyguardShowing(true)
val latest = collectLastValue(underTest.startButton)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
index 0c4e845..efa5f0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
@@ -92,6 +92,21 @@
job.cancel()
}
+ @Test
+ fun lockscreenTranslationYResettedAfterJobCancelled() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Float>()
+
+ val pixels = 100
+ val job =
+ underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
+ repository.sendTransitionStep(step(0.5f, TransitionState.CANCELED))
+
+ assertThat(values.last()).isEqualTo(0f)
+
+ job.cancel()
+ }
+
private fun step(
value: Float,
state: TransitionState = TransitionState.RUNNING
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt
index ea11f01..afab250 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt
@@ -88,7 +88,7 @@
}
@Test(expected = IllegalStateException::class)
- fun `repeatWhenAttached - enforces main thread`() =
+ fun repeatWhenAttached_enforcesMainThread() =
testScope.runTest {
Assert.setTestThread(null)
@@ -96,7 +96,7 @@
}
@Test(expected = IllegalStateException::class)
- fun `repeatWhenAttached - dispose enforces main thread`() =
+ fun repeatWhenAttached_disposeEnforcesMainThread() =
testScope.runTest {
val disposableHandle = repeatWhenAttached()
Assert.setTestThread(null)
@@ -105,7 +105,7 @@
}
@Test
- fun `repeatWhenAttached - view starts detached - runs block when attached`() =
+ fun repeatWhenAttached_viewStartsDetached_runsBlockWhenAttached() =
testScope.runTest {
whenever(view.isAttachedToWindow).thenReturn(false)
repeatWhenAttached()
@@ -120,7 +120,7 @@
}
@Test
- fun `repeatWhenAttached - view already attached - immediately runs block`() =
+ fun repeatWhenAttached_viewAlreadyAttached_immediatelyRunsBlock() =
testScope.runTest {
whenever(view.isAttachedToWindow).thenReturn(true)
@@ -132,7 +132,7 @@
}
@Test
- fun `repeatWhenAttached - starts visible without focus - STARTED`() =
+ fun repeatWhenAttached_startsVisibleWithoutFocus_STARTED() =
testScope.runTest {
whenever(view.isAttachedToWindow).thenReturn(true)
whenever(view.windowVisibility).thenReturn(View.VISIBLE)
@@ -145,7 +145,7 @@
}
@Test
- fun `repeatWhenAttached - starts with focus but invisible - CREATED`() =
+ fun repeatWhenAttached_startsWithFocusButInvisible_CREATED() =
testScope.runTest {
whenever(view.isAttachedToWindow).thenReturn(true)
whenever(view.hasWindowFocus()).thenReturn(true)
@@ -158,7 +158,7 @@
}
@Test
- fun `repeatWhenAttached - starts visible and with focus - RESUMED`() =
+ fun repeatWhenAttached_startsVisibleAndWithFocus_RESUMED() =
testScope.runTest {
whenever(view.isAttachedToWindow).thenReturn(true)
whenever(view.windowVisibility).thenReturn(View.VISIBLE)
@@ -172,7 +172,7 @@
}
@Test
- fun `repeatWhenAttached - becomes visible without focus - STARTED`() =
+ fun repeatWhenAttached_becomesVisibleWithoutFocus_STARTED() =
testScope.runTest {
whenever(view.isAttachedToWindow).thenReturn(true)
repeatWhenAttached()
@@ -188,7 +188,7 @@
}
@Test
- fun `repeatWhenAttached - gains focus but invisible - CREATED`() =
+ fun repeatWhenAttached_gainsFocusButInvisible_CREATED() =
testScope.runTest {
whenever(view.isAttachedToWindow).thenReturn(true)
repeatWhenAttached()
@@ -204,7 +204,7 @@
}
@Test
- fun `repeatWhenAttached - becomes visible and gains focus - RESUMED`() =
+ fun repeatWhenAttached_becomesVisibleAndGainsFocus_RESUMED() =
testScope.runTest {
whenever(view.isAttachedToWindow).thenReturn(true)
repeatWhenAttached()
@@ -224,7 +224,7 @@
}
@Test
- fun `repeatWhenAttached - view gets detached - destroys the lifecycle`() =
+ fun repeatWhenAttached_viewGetsDetached_destroysTheLifecycle() =
testScope.runTest {
whenever(view.isAttachedToWindow).thenReturn(true)
repeatWhenAttached()
@@ -238,7 +238,7 @@
}
@Test
- fun `repeatWhenAttached - view gets reattached - recreates a lifecycle`() =
+ fun repeatWhenAttached_viewGetsReattached_recreatesAlifecycle() =
testScope.runTest {
whenever(view.isAttachedToWindow).thenReturn(true)
repeatWhenAttached()
@@ -255,7 +255,7 @@
}
@Test
- fun `repeatWhenAttached - dispose attached`() =
+ fun repeatWhenAttached_disposeAttached() =
testScope.runTest {
whenever(view.isAttachedToWindow).thenReturn(true)
val handle = repeatWhenAttached()
@@ -269,7 +269,7 @@
}
@Test
- fun `repeatWhenAttached - dispose never attached`() =
+ fun repeatWhenAttached_disposeNeverAttached() =
testScope.runTest {
whenever(view.isAttachedToWindow).thenReturn(false)
val handle = repeatWhenAttached()
@@ -281,7 +281,7 @@
}
@Test
- fun `repeatWhenAttached - dispose previously attached now detached`() =
+ fun repeatWhenAttached_disposePreviouslyAttachedNowDetached() =
testScope.runTest {
whenever(view.isAttachedToWindow).thenReturn(true)
val handle = repeatWhenAttached()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
index 411b1bd..af83a56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
@@ -31,7 +31,7 @@
private val underTest = TableLogBufferFactory(dumpManager, systemClock)
@Test
- fun `create - always creates new instance`() {
+ fun create_alwaysCreatesNewInstance() {
val b1 = underTest.create(NAME_1, SIZE)
val b1_copy = underTest.create(NAME_1, SIZE)
val b2 = underTest.create(NAME_2, SIZE)
@@ -43,7 +43,7 @@
}
@Test
- fun `getOrCreate - reuses instance`() {
+ fun getOrCreate_reusesInstance() {
val b1 = underTest.getOrCreate(NAME_1, SIZE)
val b1_copy = underTest.getOrCreate(NAME_1, SIZE)
val b2 = underTest.getOrCreate(NAME_2, SIZE)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarViewModelTest.kt
index 56c91bc..e3c8b05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarViewModelTest.kt
@@ -22,6 +22,7 @@
import android.media.session.PlaybackState
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import android.view.MotionEvent
import android.widget.SeekBar
import androidx.arch.core.executor.ArchTaskExecutor
import androidx.arch.core.executor.TaskExecutor
@@ -466,21 +467,63 @@
whenever(mockController.getTransportControls()).thenReturn(mockTransport)
whenever(falsingManager.isFalseTouch(Classifier.MEDIA_SEEKBAR)).thenReturn(true)
whenever(falsingManager.isFalseTap(anyInt())).thenReturn(true)
- viewModel.updateController(mockController)
- val pos = 169
- viewModel.attachTouchHandlers(mockBar)
+ viewModel.updateController(mockController)
+ val pos = 40
+ val bar = SeekBar(context).apply { progress = pos }
with(viewModel.seekBarListener) {
- onStartTrackingTouch(mockBar)
- onProgressChanged(mockBar, pos, true)
- onStopTrackingTouch(mockBar)
+ onStartTrackingTouch(bar)
+ onStopTrackingTouch(bar)
}
+ fakeExecutor.runAllReady()
// THEN transport controls should not be used
verify(mockTransport, never()).seekTo(pos.toLong())
}
@Test
+ fun onSeekbarGrabInvalidTouch() {
+ whenever(mockController.getTransportControls()).thenReturn(mockTransport)
+ viewModel.firstMotionEvent =
+ MotionEvent.obtain(12L, 13L, MotionEvent.ACTION_DOWN, 76F, 0F, 0)
+ viewModel.lastMotionEvent = MotionEvent.obtain(12L, 14L, MotionEvent.ACTION_UP, 78F, 4F, 0)
+ val pos = 78
+
+ viewModel.updateController(mockController)
+ // WHEN user ends drag
+ val bar = SeekBar(context).apply { progress = pos }
+ with(viewModel.seekBarListener) {
+ onStartTrackingTouch(bar)
+ onStopTrackingTouch(bar)
+ }
+ fakeExecutor.runAllReady()
+
+ // THEN transport controls should not be used
+ verify(mockTransport, never()).seekTo(pos.toLong())
+ }
+
+ @Test
+ fun onSeekbarGrabValidTouch() {
+ whenever(mockController.transportControls).thenReturn(mockTransport)
+ viewModel.firstMotionEvent =
+ MotionEvent.obtain(12L, 13L, MotionEvent.ACTION_DOWN, 36F, 0F, 0)
+ viewModel.lastMotionEvent = MotionEvent.obtain(12L, 14L, MotionEvent.ACTION_UP, 40F, 1F, 0)
+ val pos = 40
+
+ viewModel.updateController(mockController)
+ // WHEN user ends drag
+ val bar = SeekBar(context).apply { progress = pos }
+ with(viewModel.seekBarListener) {
+ onStartTrackingTouch(bar)
+ onStopTrackingTouch(bar)
+ }
+ fakeExecutor.runAllReady()
+
+ // THEN transport controls should be used
+ verify(mockTransport).seekTo(pos.toLong())
+ }
+
+ @Test
fun queuePollTaskWhenPlaying() {
// GIVEN that the track is playing
val state =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
index 4565762..c9956f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
@@ -24,6 +24,8 @@
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.media.controls.models.player.MediaViewHolder
+import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.util.animation.MeasurementInput
import com.android.systemui.util.animation.TransitionLayout
@@ -55,13 +57,12 @@
@Mock private lateinit var mockCopiedState: TransitionViewState
@Mock private lateinit var detailWidgetState: WidgetState
@Mock private lateinit var controlWidgetState: WidgetState
- @Mock private lateinit var bgWidgetState: WidgetState
@Mock private lateinit var mediaTitleWidgetState: WidgetState
@Mock private lateinit var mediaSubTitleWidgetState: WidgetState
@Mock private lateinit var mediaContainerWidgetState: WidgetState
@Mock private lateinit var mediaFlags: MediaFlags
- val delta = 0.1F
+ private val delta = 0.1F
private lateinit var mediaViewController: MediaViewController
@@ -84,13 +85,13 @@
mediaViewController.attach(player, MediaViewController.TYPE.PLAYER)
// Change the height to see the effect of orientation change.
- MediaViewController.backgroundIds.forEach { id ->
+ MediaViewHolder.backgroundIds.forEach { id ->
mediaViewController.expandedLayout.getConstraint(id).layout.mHeight = 10
}
newConfig.orientation = ORIENTATION_LANDSCAPE
configurationController.onConfigurationChanged(newConfig)
- MediaViewController.backgroundIds.forEach { id ->
+ MediaViewHolder.backgroundIds.forEach { id ->
assertTrue(
mediaViewController.expandedLayout.getConstraint(id).layout.mHeight ==
context.resources.getDimensionPixelSize(
@@ -107,7 +108,7 @@
mediaViewController.attach(recommendation, MediaViewController.TYPE.RECOMMENDATION)
// Change the height to see the effect of orientation change.
mediaViewController.expandedLayout
- .getConstraint(MediaViewController.recSizingViewId)
+ .getConstraint(RecommendationViewHolder.backgroundId)
.layout
.mHeight = 10
newConfig.orientation = ORIENTATION_LANDSCAPE
@@ -115,7 +116,7 @@
assertTrue(
mediaViewController.expandedLayout
- .getConstraint(MediaViewController.recSizingViewId)
+ .getConstraint(RecommendationViewHolder.backgroundId)
.layout
.mHeight ==
context.resources.getDimensionPixelSize(R.dimen.qs_media_session_height_expanded)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index a03bc1e..7dc622b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -98,7 +98,7 @@
whenever(context.getString(R.string.note_task_button_label))
.thenReturn(NOTE_TASK_SHORT_LABEL)
whenever(context.packageManager).thenReturn(packageManager)
- whenever(resolver.resolveInfo(any(), any())).thenReturn(NOTE_TASK_INFO)
+ whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(NOTE_TASK_INFO)
whenever(userManager.isUserUnlocked).thenReturn(true)
whenever(
devicePolicyManager.getKeyguardDisabledFeatures(
@@ -142,7 +142,7 @@
.apply { infoReference.set(expectedInfo) }
.onBubbleExpandChanged(
isExpanding = true,
- key = Bubble.KEY_APP_BUBBLE,
+ key = Bubble.getAppBubbleKeyForApp(expectedInfo.packageName, expectedInfo.user),
)
verify(eventLogger).logNoteTaskOpened(expectedInfo)
@@ -157,7 +157,7 @@
.apply { infoReference.set(expectedInfo) }
.onBubbleExpandChanged(
isExpanding = false,
- key = Bubble.KEY_APP_BUBBLE,
+ key = Bubble.getAppBubbleKeyForApp(expectedInfo.packageName, expectedInfo.user),
)
verify(eventLogger).logNoteTaskClosed(expectedInfo)
@@ -172,7 +172,7 @@
.apply { infoReference.set(expectedInfo) }
.onBubbleExpandChanged(
isExpanding = true,
- key = Bubble.KEY_APP_BUBBLE,
+ key = Bubble.getAppBubbleKeyForApp(expectedInfo.packageName, expectedInfo.user),
)
verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger)
@@ -186,7 +186,7 @@
.apply { infoReference.set(expectedInfo) }
.onBubbleExpandChanged(
isExpanding = false,
- key = Bubble.KEY_APP_BUBBLE,
+ key = Bubble.getAppBubbleKeyForApp(expectedInfo.packageName, expectedInfo.user),
)
verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger)
@@ -208,7 +208,7 @@
createNoteTaskController(isEnabled = false)
.onBubbleExpandChanged(
isExpanding = true,
- key = Bubble.KEY_APP_BUBBLE,
+ key = Bubble.getAppBubbleKeyForApp(NOTE_TASK_INFO.packageName, NOTE_TASK_INFO.user),
)
verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger)
@@ -224,7 +224,7 @@
isKeyguardLocked = true,
)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
- whenever(resolver.resolveInfo(any(), any())).thenReturn(expectedInfo)
+ whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
createNoteTaskController()
.showNoteTask(
@@ -256,9 +256,10 @@
NOTE_TASK_INFO.copy(
entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
isKeyguardLocked = true,
+ user = user10,
)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
- whenever(resolver.resolveInfo(any(), any())).thenReturn(expectedInfo)
+ whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
createNoteTaskController()
.showNoteTaskAsUser(
@@ -292,7 +293,7 @@
isKeyguardLocked = true,
)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
- whenever(resolver.resolveInfo(any(), any())).thenReturn(expectedInfo)
+ whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
whenever(activityManager.getRunningTasks(anyInt()))
.thenReturn(listOf(NOTE_RUNNING_TASK_INFO))
@@ -318,7 +319,7 @@
entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
isKeyguardLocked = false,
)
- whenever(resolver.resolveInfo(any(), any())).thenReturn(expectedInfo)
+ whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
createNoteTaskController()
@@ -344,7 +345,7 @@
@Test
fun showNoteTask_intentResolverReturnsNull_shouldShowToast() {
- whenever(resolver.resolveInfo(any(), any())).thenReturn(null)
+ whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(null)
val noteTaskController = spy(createNoteTaskController())
doNothing().whenever(noteTaskController).showNoDefaultNotesAppToast()
@@ -384,7 +385,7 @@
isKeyguardLocked = true,
)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
- whenever(resolver.resolveInfo(any(), any())).thenReturn(expectedInfo)
+ whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
createNoteTaskController()
.showNoteTask(
@@ -717,6 +718,7 @@
NoteTaskInfo(
packageName = NOTE_TASK_PACKAGE_NAME,
uid = NOTE_TASK_UID,
+ user = UserHandle.of(0),
)
private val NOTE_RUNNING_TASK_INFO =
ActivityManager.RunningTaskInfo().apply {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt
index a4df346..b4f5528 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.notetask
+import android.os.UserHandle
import android.test.suitebuilder.annotation.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.internal.logging.UiEventLogger
@@ -44,7 +45,7 @@
NoteTaskEventLogger(uiEventLogger)
private fun createNoteTaskInfo(): NoteTaskInfo =
- NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID)
+ NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID, UserHandle.of(0))
@Before
fun setUp() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt
index 0c945df..e09c804 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt
@@ -22,8 +22,6 @@
import android.test.suitebuilder.annotation.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.systemui.SysuiTestCase
-import com.android.systemui.settings.FakeUserTracker
-import com.android.systemui.settings.UserTracker
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -46,14 +44,13 @@
@Mock lateinit var packageManager: PackageManager
@Mock lateinit var roleManager: RoleManager
- private val userTracker: UserTracker = FakeUserTracker()
private lateinit var underTest: NoteTaskInfoResolver
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- underTest = NoteTaskInfoResolver(roleManager, packageManager, userTracker)
+ underTest = NoteTaskInfoResolver(roleManager, packageManager)
}
@Test
@@ -72,11 +69,12 @@
)
.thenReturn(ApplicationInfo().apply { this.uid = uid })
- val actual = underTest.resolveInfo()
+ val actual = underTest.resolveInfo(user = context.user)
requireNotNull(actual) { "Note task info must not be null" }
assertThat(actual.packageName).isEqualTo(packageName)
assertThat(actual.uid).isEqualTo(uid)
+ assertThat(actual.user).isEqualTo(context.user)
}
@Test
@@ -94,11 +92,12 @@
)
.thenThrow(PackageManager.NameNotFoundException(packageName))
- val actual = underTest.resolveInfo()
+ val actual = underTest.resolveInfo(user = context.user)
requireNotNull(actual) { "Note task info must not be null" }
assertThat(actual.packageName).isEqualTo(packageName)
assertThat(actual.uid).isEqualTo(0)
+ assertThat(actual.user).isEqualTo(context.user)
}
@Test
@@ -107,7 +106,7 @@
emptyList<String>()
}
- val actual = underTest.resolveInfo()
+ val actual = underTest.resolveInfo(user = context.user)
assertThat(actual).isNull()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
index 91cd6ae..3435450 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.notetask
+import android.os.UserHandle
import android.test.suitebuilder.annotation.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.systemui.SysuiTestCase
@@ -28,7 +29,7 @@
internal class NoteTaskInfoTest : SysuiTestCase() {
private fun createNoteTaskInfo(): NoteTaskInfo =
- NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID)
+ NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID, UserHandle.of(0))
@Test
fun launchMode_keyguardLocked_launchModeActivity() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt
index 249a91b..bb3b3f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt
@@ -66,7 +66,7 @@
}
@Test
- fun `isInteractive - registers for broadcasts`() =
+ fun isInteractive_registersForBroadcasts() =
runBlocking(IMMEDIATE) {
val job = underTest.isInteractive.onEach {}.launchIn(this)
@@ -78,7 +78,7 @@
}
@Test
- fun `isInteractive - unregisters from broadcasts`() =
+ fun isInteractive_unregistersFromBroadcasts() =
runBlocking(IMMEDIATE) {
val job = underTest.isInteractive.onEach {}.launchIn(this)
verifyRegistered()
@@ -89,7 +89,7 @@
}
@Test
- fun `isInteractive - emits initial true value if screen was on`() =
+ fun isInteractive_emitsInitialTrueValueIfScreenWasOn() =
runBlocking(IMMEDIATE) {
isInteractive = true
var value: Boolean? = null
@@ -102,7 +102,7 @@
}
@Test
- fun `isInteractive - emits initial false value if screen was off`() =
+ fun isInteractive_emitsInitialFalseValueIfScreenWasOff() =
runBlocking(IMMEDIATE) {
isInteractive = false
var value: Boolean? = null
@@ -115,7 +115,7 @@
}
@Test
- fun `isInteractive - emits true when the screen turns on`() =
+ fun isInteractive_emitsTrueWhenTheScreenTurnsOn() =
runBlocking(IMMEDIATE) {
var value: Boolean? = null
val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
@@ -129,7 +129,7 @@
}
@Test
- fun `isInteractive - emits false when the screen turns off`() =
+ fun isInteractive_emitsFalseWhenTheScreenTurnsOff() =
runBlocking(IMMEDIATE) {
var value: Boolean? = null
val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
@@ -143,7 +143,7 @@
}
@Test
- fun `isInteractive - emits correctly over time`() =
+ fun isInteractive_emitsCorrectlyOverTime() =
runBlocking(IMMEDIATE) {
val values = mutableListOf<Boolean>()
val job = underTest.isInteractive.onEach(values::add).launchIn(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
index bf6a37e..31d4512 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
@@ -47,7 +47,7 @@
}
@Test
- fun `isInteractive - screen turns off`() =
+ fun isInteractive_screenTurnsOff() =
runBlocking(IMMEDIATE) {
repository.setInteractive(true)
var value: Boolean? = null
@@ -60,7 +60,7 @@
}
@Test
- fun `isInteractive - becomes interactive`() =
+ fun isInteractive_becomesInteractive() =
runBlocking(IMMEDIATE) {
repository.setInteractive(false)
var value: Boolean? = null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
index 8cb5d31..355c4b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -153,7 +153,7 @@
}
@Test
- fun `WakefulnessLifecycle - dispatchFinishedWakingUp sets SysUI flag to AWAKE`() {
+ fun wakefulnessLifecycle_dispatchFinishedWakingUpSetsSysUIflagToAWAKE() {
// WakefulnessLifecycle is initialized to AWAKE initially, and won't emit a noop.
wakefulnessLifecycle.dispatchFinishedGoingToSleep()
clearInvocations(overviewProxy)
@@ -167,7 +167,7 @@
}
@Test
- fun `WakefulnessLifecycle - dispatchStartedWakingUp sets SysUI flag to WAKING`() {
+ fun wakefulnessLifecycle_dispatchStartedWakingUpSetsSysUIflagToWAKING() {
wakefulnessLifecycle.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN)
verify(overviewProxy)
@@ -177,7 +177,7 @@
}
@Test
- fun `WakefulnessLifecycle - dispatchFinishedGoingToSleep sets SysUI flag to ASLEEP`() {
+ fun wakefulnessLifecycle_dispatchFinishedGoingToSleepSetsSysUIflagToASLEEP() {
wakefulnessLifecycle.dispatchFinishedGoingToSleep()
verify(overviewProxy)
@@ -187,7 +187,7 @@
}
@Test
- fun `WakefulnessLifecycle - dispatchStartedGoingToSleep sets SysUI flag to GOING_TO_SLEEP`() {
+ fun wakefulnessLifecycle_dispatchStartedGoingToSleepSetsSysUIflagToGOING_TO_SLEEP() {
wakefulnessLifecycle.dispatchStartedGoingToSleep(
PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
index 57b6b2b..beb981d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplReceiveTest.kt
@@ -73,7 +73,7 @@
}
@Test
- fun `calls callback and updates profiles when an intent received`() {
+ fun callsCallbackAndUpdatesProfilesWhenAnIntentReceived() {
tracker.initialize(0)
tracker.addCallback(callback, executor)
val profileID = tracker.userId + 10
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
index b547318..d421aca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QsBatteryModeControllerTest.kt
@@ -54,7 +54,7 @@
}
@Test
- fun `returns MODE_ON for qqs with center cutout`() {
+ fun returnsMODE_ONforQqsWithCenterCutout() {
assertThat(
controller.getBatteryMode(CENTER_TOP_CUTOUT, QQS_START_FRAME.prevFrameToFraction())
)
@@ -62,13 +62,13 @@
}
@Test
- fun `returns MODE_ESTIMATE for qs with center cutout`() {
+ fun returnsMODE_ESTIMATEforQsWithCenterCutout() {
assertThat(controller.getBatteryMode(CENTER_TOP_CUTOUT, QS_END_FRAME.nextFrameToFraction()))
.isEqualTo(BatteryMeterView.MODE_ESTIMATE)
}
@Test
- fun `returns MODE_ON for qqs with corner cutout`() {
+ fun returnsMODE_ONforQqsWithCornerCutout() {
whenever(insetsProvider.currentRotationHasCornerCutout()).thenReturn(true)
assertThat(
@@ -78,7 +78,7 @@
}
@Test
- fun `returns MODE_ESTIMATE for qs with corner cutout`() {
+ fun returnsMODE_ESTIMATEforQsWithCornerCutout() {
whenever(insetsProvider.currentRotationHasCornerCutout()).thenReturn(true)
assertThat(controller.getBatteryMode(CENTER_TOP_CUTOUT, QS_END_FRAME.nextFrameToFraction()))
@@ -86,7 +86,7 @@
}
@Test
- fun `returns null in-between`() {
+ fun returnsNullInBetween() {
assertThat(
controller.getBatteryMode(CENTER_TOP_CUTOUT, QQS_START_FRAME.nextFrameToFraction())
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
index 76f7401..9fe75ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
@@ -366,7 +366,7 @@
}
@Test
- fun `battery mode controller called when qsExpandedFraction changes`() {
+ fun batteryModeControllerCalledWhenQsExpandedFractionChanges() {
whenever(qsBatteryModeController.getBatteryMode(Mockito.same(null), eq(0f)))
.thenReturn(BatteryMeterView.MODE_ON)
whenever(qsBatteryModeController.getBatteryMode(Mockito.same(null), eq(1f)))
@@ -825,7 +825,7 @@
}
@Test
- fun `carrier left padding is set when clock layout changes`() {
+ fun carrierLeftPaddingIsSetWhenClockLayoutChanges() {
val width = 200
whenever(clock.width).thenReturn(width)
whenever(clock.scaleX).thenReturn(2.57f) // 2.57 comes from qs_header.xml
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
index 7fa27f3..3f3faf7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt
@@ -201,7 +201,7 @@
@Test
fun test_aodClock_always_whiteColor() {
val clock = provider.createClock(DEFAULT_CLOCK_ID)
- clock.animations.doze(0.9f) // set AOD mode to active
+ clock.smallClock.animations.doze(0.9f) // set AOD mode to active
clock.smallClock.events.onRegionDarknessChanged(true)
verify((clock.smallClock.view as AnimatableClockView), never()).animateAppearOnLockscreen()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
new file mode 100644
index 0000000..2b4a7fb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
@@ -0,0 +1,135 @@
+package com.android.systemui.shared.condition
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ConditionExtensionsTest : SysuiTestCase() {
+ private lateinit var testScope: TestScope
+
+ @Before
+ fun setUp() {
+ testScope = TestScope(StandardTestDispatcher())
+ }
+
+ @Test
+ fun flowInitiallyTrue() =
+ testScope.runTest {
+ val flow = flowOf(true)
+ val condition = flow.toCondition(this)
+
+ runCurrent()
+ assertThat(condition.isConditionSet).isFalse()
+
+ condition.start()
+ runCurrent()
+ assertThat(condition.isConditionSet).isTrue()
+ assertThat(condition.isConditionMet).isTrue()
+ }
+
+ @Test
+ fun flowInitiallyFalse() =
+ testScope.runTest {
+ val flow = flowOf(false)
+ val condition = flow.toCondition(this)
+
+ runCurrent()
+ assertThat(condition.isConditionSet).isFalse()
+
+ condition.start()
+ runCurrent()
+ assertThat(condition.isConditionSet).isTrue()
+ assertThat(condition.isConditionMet).isFalse()
+ }
+
+ @Test
+ fun emptyFlowWithNoInitialValue() =
+ testScope.runTest {
+ val flow = emptyFlow<Boolean>()
+ val condition = flow.toCondition(this)
+ condition.start()
+
+ runCurrent()
+ assertThat(condition.isConditionSet).isFalse()
+ assertThat(condition.isConditionMet).isFalse()
+ }
+
+ @Test
+ fun emptyFlowWithInitialValueOfTrue() =
+ testScope.runTest {
+ val flow = emptyFlow<Boolean>()
+ val condition = flow.toCondition(scope = this, initialValue = true)
+ condition.start()
+
+ runCurrent()
+ assertThat(condition.isConditionSet).isTrue()
+ assertThat(condition.isConditionMet).isTrue()
+ }
+
+ @Test
+ fun emptyFlowWithInitialValueOfFalse() =
+ testScope.runTest {
+ val flow = emptyFlow<Boolean>()
+ val condition = flow.toCondition(scope = this, initialValue = false)
+ condition.start()
+
+ runCurrent()
+ assertThat(condition.isConditionSet).isTrue()
+ assertThat(condition.isConditionMet).isFalse()
+ }
+
+ @Test
+ fun conditionUpdatesWhenFlowEmitsNewValue() =
+ testScope.runTest {
+ val flow = MutableStateFlow(false)
+ val condition = flow.toCondition(this)
+ condition.start()
+
+ runCurrent()
+ assertThat(condition.isConditionSet).isTrue()
+ assertThat(condition.isConditionMet).isFalse()
+
+ flow.value = true
+ runCurrent()
+ assertThat(condition.isConditionMet).isTrue()
+
+ flow.value = false
+ runCurrent()
+ assertThat(condition.isConditionMet).isFalse()
+
+ condition.stop()
+ }
+
+ @Test
+ fun stoppingConditionUnsubscribesFromFlow() =
+ testScope.runTest {
+ val flow = MutableSharedFlow<Boolean>()
+ val condition = flow.toCondition(this)
+ runCurrent()
+ assertThat(flow.subscriptionCount.value).isEqualTo(0)
+
+ condition.start()
+ runCurrent()
+ assertThat(flow.subscriptionCount.value).isEqualTo(1)
+
+ condition.stop()
+ runCurrent()
+ assertThat(flow.subscriptionCount.value).isEqualTo(0)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
index 9d6ea85..b997f64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
@@ -16,11 +16,34 @@
package com.android.systemui.statusbar
+import android.app.Notification
+import android.app.WallpaperManager
+import android.graphics.Bitmap
+import android.media.MediaMetadata
+import android.media.session.MediaSession
+import android.media.session.PlaybackState
import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.colorextraction.SysuiColorExtractor
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.notification.collection.NotifCollection
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.whenever
-import org.junit.After
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import dagger.Lazy
+import java.util.Optional
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -32,39 +55,121 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-/**
- * Temporary test for the lock screen live wallpaper project.
- *
- * TODO(b/273443374): remove this test
- */
@RunWith(AndroidTestingRunner::class)
@SmallTest
+@RunWithLooper
class NotificationMediaManagerTest : SysuiTestCase() {
- @Mock private lateinit var notificationMediaManager: NotificationMediaManager
+ @Mock private lateinit var centralSurfaces: CentralSurfaces
+ @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+ @Mock private lateinit var visibilityProvider: NotificationVisibilityProvider
+ @Mock private lateinit var mediaArtworkProcessor: MediaArtworkProcessor
+ @Mock private lateinit var keyguardBypassController: KeyguardBypassController
+ @Mock private lateinit var notifPipeline: NotifPipeline
+ @Mock private lateinit var notifCollection: NotifCollection
+ @Mock private lateinit var mediaDataManager: MediaDataManager
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var colorExtractor: SysuiColorExtractor
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var wallpaperManager: WallpaperManager
+ @Mock private lateinit var notificationEntry: NotificationEntry
+
+ lateinit var manager: NotificationMediaManager
+ val clock = FakeSystemClock()
+ val mainExecutor: DelayableExecutor = FakeExecutor(clock)
+
+ @Mock private lateinit var mockManager: NotificationMediaManager
@Mock private lateinit var mockBackDropView: BackDropView
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- doCallRealMethod()
- .whenever(notificationMediaManager)
- .updateMediaMetaData(anyBoolean(), anyBoolean())
- doReturn(mockBackDropView).whenever(notificationMediaManager).backDropView
+ doCallRealMethod().whenever(mockManager).updateMediaMetaData(anyBoolean(), anyBoolean())
+ doReturn(mockBackDropView).whenever(mockManager).backDropView
+
+ manager =
+ NotificationMediaManager(
+ context,
+ Lazy { Optional.of(centralSurfaces) },
+ Lazy { notificationShadeWindowController },
+ visibilityProvider,
+ mediaArtworkProcessor,
+ keyguardBypassController,
+ notifPipeline,
+ notifCollection,
+ mainExecutor,
+ mediaDataManager,
+ statusBarStateController,
+ colorExtractor,
+ keyguardStateController,
+ dumpManager,
+ wallpaperManager,
+ )
}
- @After fun tearDown() {}
-
- /** Check that updateMediaMetaData is a no-op with mIsLockscreenLiveWallpaperEnabled = true */
+ /**
+ * Check that updateMediaMetaData is a no-op with mIsLockscreenLiveWallpaperEnabled = true
+ * Temporary test for the lock screen live wallpaper project.
+ *
+ * TODO(b/273443374): remove this test
+ */
@Test
fun testUpdateMediaMetaDataDisabled() {
- notificationMediaManager.mIsLockscreenLiveWallpaperEnabled = true
+ mockManager.mIsLockscreenLiveWallpaperEnabled = true
for (metaDataChanged in listOf(true, false)) {
for (allowEnterAnimation in listOf(true, false)) {
- notificationMediaManager.updateMediaMetaData(metaDataChanged, allowEnterAnimation)
- verify(notificationMediaManager, never()).mediaMetadata
+ mockManager.updateMediaMetaData(metaDataChanged, allowEnterAnimation)
+ verify(mockManager, never()).mediaMetadata
}
}
}
+
+ @Test
+ fun testMetadataUpdated_doesNotRetainArtwork() {
+ val artBmp = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
+ val artUri = "content://example"
+ val inputMetadata =
+ MediaMetadata.Builder()
+ .putBitmap(MediaMetadata.METADATA_KEY_ART, artBmp)
+ .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, artBmp)
+ .putBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON, artBmp)
+ .putString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI, artUri)
+ .putString(MediaMetadata.METADATA_KEY_ART_URI, artUri)
+ .putString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI, artUri)
+ .build()
+
+ // Create a playing media notification
+ val state = PlaybackState.Builder().setState(PlaybackState.STATE_PLAYING, 0L, 1f).build()
+ val session = MediaSession(context, "NotificationMediaManagerTest")
+ session.setMetadata(inputMetadata)
+ session.setPlaybackState(state)
+ val sbn =
+ SbnBuilder().run {
+ modifyNotification(context).also {
+ it.setSmallIcon(android.R.drawable.ic_media_play)
+ it.setStyle(
+ Notification.MediaStyle().apply { setMediaSession(session.sessionToken) }
+ )
+ }
+ build()
+ }
+ whenever(notificationEntry.sbn).thenReturn(sbn)
+ val collection = ArrayList<NotificationEntry>()
+ collection.add(notificationEntry)
+ whenever(notifPipeline.allNotifs).thenReturn(collection)
+
+ // Trigger update in NotificationMediaManager
+ manager.findAndUpdateMediaNotifications()
+
+ // Verify that there is no artwork data retained
+ val metadata = manager.mediaMetadata
+ assertThat(metadata.getBitmap(MediaMetadata.METADATA_KEY_ART)).isNull()
+ assertThat(metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)).isNull()
+ assertThat(metadata.getBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON)).isNull()
+ assertThat(metadata.getString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI)).isNull()
+ assertThat(metadata.getString(MediaMetadata.METADATA_KEY_ART_URI)).isNull()
+ assertThat(metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI)).isNull()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt
index 63cb30c..95b132d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt
@@ -39,7 +39,7 @@
}
@Test
- fun `process new config - reflected by isUsingDefault`() {
+ fun processNewConfig_reflectedByIsUsingDefault() {
// Starts out using the defaults
assertThat(underTest.isUsingDefault).isTrue()
@@ -50,7 +50,7 @@
}
@Test
- fun `process new config - updates all flows`() {
+ fun processNewConfig_updatesAllFlows() {
assertThat(underTest.shouldInflateSignalStrength.value).isFalse()
assertThat(underTest.showOperatorNameInStatusBar.value).isFalse()
@@ -66,7 +66,7 @@
}
@Test
- fun `process new config - defaults to false for config overrides`() {
+ fun processNewConfig_defaultsToFalseForConfigOverrides() {
// This case is only apparent when:
// 1. The default is true
// 2. The override config has no value for a given key
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
index dfef62e..6e3af26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
@@ -96,7 +96,7 @@
}
@Test
- fun `carrier config stream produces int-bundle pairs`() =
+ fun carrierConfigStreamProducesIntBundlePairs() =
testScope.runTest {
var latest: Pair<Int, PersistableBundle>? = null
val job = underTest.carrierConfigStream.onEach { latest = it }.launchIn(this)
@@ -111,7 +111,7 @@
}
@Test
- fun `carrier config stream ignores invalid subscriptions`() =
+ fun carrierConfigStreamIgnoresInvalidSubscriptions() =
testScope.runTest {
var latest: Pair<Int, PersistableBundle>? = null
val job = underTest.carrierConfigStream.onEach { latest = it }.launchIn(this)
@@ -124,19 +124,19 @@
}
@Test
- fun `getOrCreateConfig - uses default config if no override`() {
+ fun getOrCreateConfig_usesDefaultConfigIfNoOverride() {
val config = underTest.getOrCreateConfigForSubId(123)
assertThat(config.isUsingDefault).isTrue()
}
@Test
- fun `getOrCreateConfig - uses override if exists`() {
+ fun getOrCreateConfig_usesOverrideIfExists() {
val config = underTest.getOrCreateConfigForSubId(SUB_ID_1)
assertThat(config.isUsingDefault).isFalse()
}
@Test
- fun `config - updates while config stream is collected`() =
+ fun config_updatesWhileConfigStreamIsCollected() =
testScope.runTest {
CONFIG_1.putBoolean(CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
index 1fdcf7f..3ec9690 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
@@ -156,7 +156,7 @@
}
@Test
- fun `active repo matches demo mode setting`() =
+ fun activeRepoMatchesDemoModeSetting() =
runBlocking(IMMEDIATE) {
whenever(demoModeController.isInDemoMode).thenReturn(false)
@@ -177,7 +177,7 @@
}
@Test
- fun `subscription list updates when demo mode changes`() =
+ fun subscriptionListUpdatesWhenDemoModeChanges() =
runBlocking(IMMEDIATE) {
whenever(demoModeController.isInDemoMode).thenReturn(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
index 47f8cd3..1251dfa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
@@ -105,7 +105,7 @@
}
@Test
- fun `network event - create new subscription`() =
+ fun networkEvent_createNewSubscription() =
testScope.runTest {
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -121,7 +121,7 @@
}
@Test
- fun `wifi carrier merged event - create new subscription`() =
+ fun wifiCarrierMergedEvent_createNewSubscription() =
testScope.runTest {
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -137,7 +137,7 @@
}
@Test
- fun `network event - reuses subscription when same Id`() =
+ fun networkEvent_reusesSubscriptionWhenSameId() =
testScope.runTest {
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -159,7 +159,7 @@
}
@Test
- fun `wifi carrier merged event - reuses subscription when same Id`() =
+ fun wifiCarrierMergedEvent_reusesSubscriptionWhenSameId() =
testScope.runTest {
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -181,7 +181,7 @@
}
@Test
- fun `multiple subscriptions`() =
+ fun multipleSubscriptions() =
testScope.runTest {
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -195,7 +195,7 @@
}
@Test
- fun `mobile subscription and carrier merged subscription`() =
+ fun mobileSubscriptionAndCarrierMergedSubscription() =
testScope.runTest {
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -209,7 +209,7 @@
}
@Test
- fun `multiple mobile subscriptions and carrier merged subscription`() =
+ fun multipleMobileSubscriptionsAndCarrierMergedSubscription() =
testScope.runTest {
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -224,7 +224,7 @@
}
@Test
- fun `mobile disabled event - disables connection - subId specified - single conn`() =
+ fun mobileDisabledEvent_disablesConnection_subIdSpecified_singleConn() =
testScope.runTest {
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -239,7 +239,7 @@
}
@Test
- fun `mobile disabled event - disables connection - subId not specified - single conn`() =
+ fun mobileDisabledEvent_disablesConnection_subIdNotSpecified_singleConn() =
testScope.runTest {
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -254,7 +254,7 @@
}
@Test
- fun `mobile disabled event - disables connection - subId specified - multiple conn`() =
+ fun mobileDisabledEvent_disablesConnection_subIdSpecified_multipleConn() =
testScope.runTest {
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -270,7 +270,7 @@
}
@Test
- fun `mobile disabled event - subId not specified - multiple conn - ignores command`() =
+ fun mobileDisabledEvent_subIdNotSpecified_multipleConn_ignoresCommand() =
testScope.runTest {
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -286,7 +286,7 @@
}
@Test
- fun `wifi network updates to disabled - carrier merged connection removed`() =
+ fun wifiNetworkUpdatesToDisabled_carrierMergedConnectionRemoved() =
testScope.runTest {
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -303,7 +303,7 @@
}
@Test
- fun `wifi network updates to active - carrier merged connection removed`() =
+ fun wifiNetworkUpdatesToActive_carrierMergedConnectionRemoved() =
testScope.runTest {
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -326,7 +326,7 @@
}
@Test
- fun `mobile sub updates to carrier merged - only one connection`() =
+ fun mobileSubUpdatesToCarrierMerged_onlyOneConnection() =
testScope.runTest {
var latestSubsList: List<SubscriptionModel>? = null
var connections: List<DemoMobileConnectionRepository>? = null
@@ -352,7 +352,7 @@
}
@Test
- fun `mobile sub updates to carrier merged then back - has old mobile data`() =
+ fun mobileSubUpdatesToCarrierMergedThenBack_hasOldMobileData() =
testScope.runTest {
var latestSubsList: List<SubscriptionModel>? = null
var connections: List<DemoMobileConnectionRepository>? = null
@@ -393,7 +393,7 @@
/** Regression test for b/261706421 */
@Test
- fun `multiple connections - remove all - does not throw`() =
+ fun multipleConnections_removeAll_doesNotThrow() =
testScope.runTest {
var latest: List<SubscriptionModel>? = null
val job = underTest.subscriptions.onEach { latest = it }.launchIn(this)
@@ -411,7 +411,7 @@
}
@Test
- fun `demo connection - single subscription`() =
+ fun demoConnection_singleSubscription() =
testScope.runTest {
var currentEvent: FakeNetworkEventModel = validMobileEvent(subId = 1)
var connections: List<DemoMobileConnectionRepository>? = null
@@ -440,7 +440,7 @@
}
@Test
- fun `demo connection - two connections - update second - no affect on first`() =
+ fun demoConnection_twoConnections_updateSecond_noAffectOnFirst() =
testScope.runTest {
var currentEvent1 = validMobileEvent(subId = 1)
var connection1: DemoMobileConnectionRepository? = null
@@ -487,7 +487,7 @@
}
@Test
- fun `demo connection - two connections - update carrier merged - no affect on first`() =
+ fun demoConnection_twoConnections_updateCarrierMerged_noAffectOnFirst() =
testScope.runTest {
var currentEvent1 = validMobileEvent(subId = 1)
var connection1: DemoMobileConnectionRepository? = null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
index 423c476..9f77744 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
@@ -292,7 +292,7 @@
}
@Test
- fun `factory - reuses log buffers for same connection`() =
+ fun factory_reusesLogBuffersForSameConnection() =
testScope.runTest {
val realLoggerFactory = TableLogBufferFactory(mock(), FakeSystemClock())
@@ -327,7 +327,7 @@
}
@Test
- fun `factory - reuses log buffers for same sub ID even if carrier merged`() =
+ fun factory_reusesLogBuffersForSameSubIDevenIfCarrierMerged() =
testScope.runTest {
val realLoggerFactory = TableLogBufferFactory(mock(), FakeSystemClock())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index 8d7f0f6..c276865 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -353,7 +353,7 @@
}
@Test
- fun `isInService - uses repository value`() =
+ fun isInService_usesRepositoryValue() =
testScope.runTest {
var latest: Boolean? = null
val job = underTest.isInService.onEach { latest = it }.launchIn(this)
@@ -370,7 +370,7 @@
}
@Test
- fun `roaming - is gsm - uses connection model`() =
+ fun roaming_isGsm_usesConnectionModel() =
testScope.runTest {
var latest: Boolean? = null
val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
@@ -389,7 +389,7 @@
}
@Test
- fun `roaming - is cdma - uses cdma roaming bit`() =
+ fun roaming_isCdma_usesCdmaRoamingBit() =
testScope.runTest {
var latest: Boolean? = null
val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
@@ -410,7 +410,7 @@
}
@Test
- fun `roaming - false while carrierNetworkChangeActive`() =
+ fun roaming_falseWhileCarrierNetworkChangeActive() =
testScope.runTest {
var latest: Boolean? = null
val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
@@ -431,7 +431,7 @@
}
@Test
- fun `network name - uses operatorAlphaShot when non null and repo is default`() =
+ fun networkName_usesOperatorAlphaShotWhenNonNullAndRepoIsDefault() =
testScope.runTest {
var latest: NetworkNameModel? = null
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index dc68386..c84c9c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -520,7 +520,7 @@
// is private and can only be tested by looking at [isDefaultConnectionFailed].
@Test
- fun `data switch - in same group - validated matches previous value - expires after 2s`() =
+ fun dataSwitch_inSameGroup_validatedMatchesPreviousValue_expiresAfter2s() =
testScope.runTest {
var latestConnectionFailed: Boolean? = null
val job =
@@ -548,7 +548,7 @@
}
@Test
- fun `data switch - in same group - not validated - immediately marked as failed`() =
+ fun dataSwitch_inSameGroup_notValidated_immediatelyMarkedAsFailed() =
testScope.runTest {
var latestConnectionFailed: Boolean? = null
val job =
@@ -567,7 +567,7 @@
}
@Test
- fun `data switch - lose validation - then switch happens - clears forced bit`() =
+ fun dataSwitch_loseValidation_thenSwitchHappens_clearsForcedBit() =
testScope.runTest {
var latestConnectionFailed: Boolean? = null
val job =
@@ -602,7 +602,7 @@
}
@Test
- fun `data switch - while already forcing validation - resets clock`() =
+ fun dataSwitch_whileAlreadyForcingValidation_resetsClock() =
testScope.runTest {
var latestConnectionFailed: Boolean? = null
val job =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
index e99be86..d5fb577 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
@@ -92,7 +92,7 @@
}
@Test
- fun `location based view models receive same icon id when common impl updates`() =
+ fun locationBasedViewModelsReceiveSameIconIdWhenCommonImplUpdates() =
testScope.runTest {
var latestHome: SignalIconModel? = null
val homeJob = homeIcon.icon.onEach { latestHome = it }.launchIn(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index 297cb9d..2b7bc78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -249,7 +249,7 @@
}
@Test
- fun `icon - uses empty state - when not in service`() =
+ fun icon_usesEmptyState_whenNotInService() =
testScope.runTest {
var latest: SignalIconModel? = null
val job = underTest.icon.onEach { latest = it }.launchIn(this)
@@ -480,7 +480,7 @@
}
@Test
- fun `network type - alwaysShow - shown when not default`() =
+ fun networkType_alwaysShow_shownWhenNotDefault() =
testScope.runTest {
interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
interactor.mobileIsDefault.value = false
@@ -500,7 +500,7 @@
}
@Test
- fun `network type - not shown when not default`() =
+ fun networkType_notShownWhenNotDefault() =
testScope.runTest {
interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
interactor.isDataConnected.value = true
@@ -531,7 +531,7 @@
}
@Test
- fun `data activity - null when config is off`() =
+ fun dataActivity_nullWhenConfigIsOff() =
testScope.runTest {
// Create a new view model here so the constants are properly read
whenever(constants.shouldShowActivityConfig).thenReturn(false)
@@ -563,7 +563,7 @@
}
@Test
- fun `data activity - config on - test indicators`() =
+ fun dataActivity_configOn_testIndicators() =
testScope.runTest {
// Create a new view model here so the constants are properly read
whenever(constants.shouldShowActivityConfig).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
index f8e1aa9..f0458fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
@@ -111,7 +111,7 @@
}
@Test
- fun `caching - mobile icon view model is reused for same sub id`() =
+ fun caching_mobileIconViewModelIsReusedForSameSubId() =
testScope.runTest {
val model1 = underTest.viewModelForSub(1, StatusBarLocation.HOME)
val model2 = underTest.viewModelForSub(1, StatusBarLocation.QS)
@@ -120,7 +120,7 @@
}
@Test
- fun `caching - invalid view models are removed from cache when sub disappears`() =
+ fun caching_invalidViewModelsAreRemovedFromCacheWhenSubDisappears() =
testScope.runTest {
// Retrieve models to trigger caching
val model1 = underTest.viewModelForSub(1, StatusBarLocation.HOME)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
index 70d2d2b..30b95ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
@@ -105,7 +105,7 @@
}
@Test
- fun `switcher active repo - updates when demo mode changes`() =
+ fun switcherActiveRepo_updatesWhenDemoModeChanges() =
testScope.runTest {
assertThat(underTest.activeRepo.value).isSameInstanceAs(realImpl)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt
index 0a3da0b..67727ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt
@@ -87,7 +87,7 @@
}
@Test
- fun `Adds self to controller in constructor`() {
+ fun addsSelfToControllerInConstructor() {
val captor = kotlinArgumentCaptor<WeakReference<BaseUserSwitcherAdapter>>()
verify(controller).addAdapter(captor.capture())
@@ -100,7 +100,7 @@
}
@Test
- fun `count - ignores restricted users when device is locked`() {
+ fun count_ignoresRestrictedUsersWhenDeviceIsLocked() {
whenever(controller.isKeyguardShowing).thenReturn(true)
users =
ArrayList(
@@ -131,7 +131,7 @@
}
@Test
- fun `count - does not ignore restricted users when device is not locked`() {
+ fun count_doesNotIgnoreRestrictedUsersWhenDeviceIsNotLocked() {
whenever(controller.isKeyguardShowing).thenReturn(false)
users =
ArrayList(
@@ -185,7 +185,7 @@
}
@Test
- fun `getName - non guest - returns real name`() {
+ fun getName_nonGuest_returnsRealName() {
val userRecord =
createUserRecord(
id = 1,
@@ -196,7 +196,7 @@
}
@Test
- fun `getName - guest and selected - returns exit guest action name`() {
+ fun getName_guestAndSelected_returnsExitGuestActionName() {
val expected = "Exit guest"
context.orCreateTestableResources.addOverride(
com.android.settingslib.R.string.guest_exit_quick_settings_button,
@@ -215,7 +215,7 @@
}
@Test
- fun `getName - guest and not selected - returns enter guest action name`() {
+ fun getName_guestAndNotSelected_returnsEnterGuestActionName() {
val expected = "Guest"
context.orCreateTestableResources.addOverride(
com.android.internal.R.string.guest_name,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/UnfoldRemoteFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/UnfoldRemoteFilterTest.kt
index f14009aa..70eadce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/UnfoldRemoteFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/UnfoldRemoteFilterTest.kt
@@ -39,16 +39,36 @@
}
@Test
- fun onTransitionProgress_withInterval_propagated() {
- runOnMainThreadWithInterval(
- { progressProvider.onTransitionStarted() },
- { progressProvider.onTransitionProgress(0.5f) }
- )
+ fun onTransitionProgress_firstProgressEvent_propagatedImmediately() {
+ progressProvider.onTransitionStarted()
+ progressProvider.onTransitionProgress(0.5f)
listener.assertLastProgress(0.5f)
}
@Test
+ fun onTransitionProgress_secondProgressEvent_isNotPropagatedImmediately() =
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ progressProvider.onTransitionStarted()
+ progressProvider.onTransitionProgress(0.5f)
+ progressProvider.onTransitionProgress(0.8f)
+
+ // 0.8f should be set only later, after the animation
+ listener.assertLastProgress(0.5f)
+ }
+
+ @Test
+ fun onTransitionProgress_severalProgressEventsWithInterval_propagated() {
+ runOnMainThreadWithInterval(
+ { progressProvider.onTransitionStarted() },
+ { progressProvider.onTransitionProgress(0.5f) },
+ { progressProvider.onTransitionProgress(0.8f) }
+ )
+
+ listener.assertLastProgress(0.8f)
+ }
+
+ @Test
fun onTransitionEnded_propagated() {
runOnMainThreadWithInterval(
{ progressProvider.onTransitionStarted() },
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index e2f3cf7..079fbcd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -164,7 +164,7 @@
}
@Test
- fun `refreshUsers - sorts by creation time - guest user last`() = runSelfCancelingTest {
+ fun refreshUsers_sortsByCreationTime_guestUserLast() = runSelfCancelingTest {
underTest = create(this)
val unsortedUsers =
setUpUsers(
@@ -205,7 +205,7 @@
return userInfos
}
@Test
- fun `userTrackerCallback - updates selectedUserInfo`() = runSelfCancelingTest {
+ fun userTrackerCallback_updatesSelectedUserInfo() = runSelfCancelingTest {
underTest = create(this)
var selectedUserInfo: UserInfo? = null
underTest.selectedUserInfo.onEach { selectedUserInfo = it }.launchIn(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
index 0c119fd..948670f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
@@ -97,13 +97,13 @@
}
@Test
- fun `registers broadcast receivers`() {
+ fun registersBroadcastReceivers() {
verify(resumeSessionReceiver).register()
verify(resetOrExitSessionReceiver).register()
}
@Test
- fun `onDeviceBootCompleted - allowed to add - create guest`() =
+ fun onDeviceBootCompleted_allowedToAdd_createGuest() =
runBlocking(IMMEDIATE) {
setAllowedToAdd()
@@ -114,7 +114,7 @@
}
@Test
- fun `onDeviceBootCompleted - await provisioning - and create guest`() =
+ fun onDeviceBootCompleted_awaitProvisioning_andCreateGuest() =
runBlocking(IMMEDIATE) {
setAllowedToAdd(isAllowed = false)
underTest.onDeviceBootCompleted()
@@ -145,7 +145,7 @@
}
@Test
- fun `createAndSwitchTo - fails to create - does not switch to`() =
+ fun createAndSwitchTo_failsToCreate_doesNotSwitchTo() =
runBlocking(IMMEDIATE) {
whenever(manager.createGuest(any())).thenReturn(null)
@@ -162,7 +162,7 @@
}
@Test
- fun `exit - returns to target user`() =
+ fun exit_returnsToTargetUser() =
runBlocking(IMMEDIATE) {
repository.setSelectedUserInfo(GUEST_USER_INFO)
@@ -182,7 +182,7 @@
}
@Test
- fun `exit - returns to last non-guest`() =
+ fun exit_returnsToLastNonGuest() =
runBlocking(IMMEDIATE) {
val expectedUserId = NON_GUEST_USER_INFO.id
whenever(manager.getUserInfo(expectedUserId)).thenReturn(NON_GUEST_USER_INFO)
@@ -204,7 +204,7 @@
}
@Test
- fun `exit - last non-guest was removed - returns to main user`() =
+ fun exit_lastNonGuestWasRemoved_returnsToMainUser() =
runBlocking(IMMEDIATE) {
val removedUserId = 310
val mainUserId = 10
@@ -227,7 +227,7 @@
}
@Test
- fun `exit - guest was ephemeral - it is removed`() =
+ fun exit_guestWasEphemeral_itIsRemoved() =
runBlocking(IMMEDIATE) {
whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true)
repository.setUserInfos(listOf(NON_GUEST_USER_INFO, EPHEMERAL_GUEST_USER_INFO))
@@ -250,7 +250,7 @@
}
@Test
- fun `exit - force remove guest - it is removed`() =
+ fun exit_forceRemoveGuest_itIsRemoved() =
runBlocking(IMMEDIATE) {
whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true)
repository.setSelectedUserInfo(GUEST_USER_INFO)
@@ -272,7 +272,7 @@
}
@Test
- fun `exit - selected different from guest user - do nothing`() =
+ fun exit_selectedDifferentFromGuestUser_doNothing() =
runBlocking(IMMEDIATE) {
repository.setSelectedUserInfo(NON_GUEST_USER_INFO)
@@ -289,7 +289,7 @@
}
@Test
- fun `exit - selected is actually not a guest user - do nothing`() =
+ fun exit_selectedIsActuallyNotAguestUser_doNothing() =
runBlocking(IMMEDIATE) {
repository.setSelectedUserInfo(NON_GUEST_USER_INFO)
@@ -306,7 +306,7 @@
}
@Test
- fun `remove - returns to target user`() =
+ fun remove_returnsToTargetUser() =
runBlocking(IMMEDIATE) {
whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true)
repository.setSelectedUserInfo(GUEST_USER_INFO)
@@ -327,7 +327,7 @@
}
@Test
- fun `remove - selected different from guest user - do nothing`() =
+ fun remove_selectedDifferentFromGuestUser_doNothing() =
runBlocking(IMMEDIATE) {
whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true)
repository.setSelectedUserInfo(NON_GUEST_USER_INFO)
@@ -344,7 +344,7 @@
}
@Test
- fun `remove - selected is actually not a guest user - do nothing`() =
+ fun remove_selectedIsActuallyNotAguestUser_doNothing() =
runBlocking(IMMEDIATE) {
whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true)
repository.setSelectedUserInfo(NON_GUEST_USER_INFO)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt
index 593ce1f..b30f77a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt
@@ -45,7 +45,7 @@
}
@Test
- fun `pause - prevents the next refresh from happening`() =
+ fun pause_preventsTheNextRefreshFromHappening() =
runBlocking(IMMEDIATE) {
underTest =
RefreshUsersScheduler(
@@ -60,7 +60,7 @@
}
@Test
- fun `unpauseAndRefresh - forces the refresh even when paused`() =
+ fun unpauseAndRefresh_forcesTheRefreshEvenWhenPaused() =
runBlocking(IMMEDIATE) {
underTest =
RefreshUsersScheduler(
@@ -76,7 +76,7 @@
}
@Test
- fun `refreshIfNotPaused - refreshes when not paused`() =
+ fun refreshIfNotPaused_refreshesWhenNotPaused() =
runBlocking(IMMEDIATE) {
underTest =
RefreshUsersScheduler(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index adba538..d252d53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -182,7 +182,7 @@
}
@Test
- fun `testKeyguardUpdateMonitor_onKeyguardGoingAway`() =
+ fun testKeyguardUpdateMonitor_onKeyguardGoingAway() =
testScope.runTest {
val argumentCaptor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
verify(keyguardUpdateMonitor).registerCallback(argumentCaptor.capture())
@@ -194,7 +194,7 @@
}
@Test
- fun `onRecordSelected - user`() =
+ fun onRecordSelected_user() =
testScope.runTest {
val userInfos = createUserInfos(count = 3, includeGuest = false)
userRepository.setUserInfos(userInfos)
@@ -211,7 +211,7 @@
}
@Test
- fun `onRecordSelected - switch to guest user`() =
+ fun onRecordSelected_switchToGuestUser() =
testScope.runTest {
val userInfos = createUserInfos(count = 3, includeGuest = true)
userRepository.setUserInfos(userInfos)
@@ -227,7 +227,7 @@
}
@Test
- fun `onRecordSelected - switch to restricted user`() =
+ fun onRecordSelected_switchToRestrictedUser() =
testScope.runTest {
var userInfos = createUserInfos(count = 2, includeGuest = false).toMutableList()
userInfos.add(
@@ -252,7 +252,7 @@
}
@Test
- fun `onRecordSelected - enter guest mode`() =
+ fun onRecordSelected_enterGuestMode() =
testScope.runTest {
val userInfos = createUserInfos(count = 3, includeGuest = false)
userRepository.setUserInfos(userInfos)
@@ -272,7 +272,7 @@
}
@Test
- fun `onRecordSelected - action`() =
+ fun onRecordSelected_action() =
testScope.runTest {
val userInfos = createUserInfos(count = 3, includeGuest = true)
userRepository.setUserInfos(userInfos)
@@ -288,7 +288,7 @@
}
@Test
- fun `users - switcher enabled`() =
+ fun users_switcherEnabled() =
testScope.runTest {
val userInfos = createUserInfos(count = 3, includeGuest = true)
userRepository.setUserInfos(userInfos)
@@ -301,7 +301,7 @@
}
@Test
- fun `users - switches to second user`() =
+ fun users_switchesToSecondUser() =
testScope.runTest {
val userInfos = createUserInfos(count = 2, includeGuest = false)
userRepository.setUserInfos(userInfos)
@@ -315,7 +315,7 @@
}
@Test
- fun `users - switcher not enabled`() =
+ fun users_switcherNotEnabled() =
testScope.runTest {
val userInfos = createUserInfos(count = 2, includeGuest = false)
userRepository.setUserInfos(userInfos)
@@ -342,7 +342,7 @@
}
@Test
- fun `actions - device unlocked`() =
+ fun actions_deviceUnlocked() =
testScope.runTest {
val userInfos = createUserInfos(count = 2, includeGuest = false)
@@ -366,7 +366,7 @@
}
@Test
- fun `actions - device unlocked - full screen`() =
+ fun actions_deviceUnlocked_fullScreen() =
testScope.runTest {
featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
val userInfos = createUserInfos(count = 2, includeGuest = false)
@@ -389,7 +389,7 @@
}
@Test
- fun `actions - device unlocked user not primary - empty list`() =
+ fun actions_deviceUnlockedUserNotPrimary_emptyList() =
testScope.runTest {
val userInfos = createUserInfos(count = 2, includeGuest = false)
userRepository.setUserInfos(userInfos)
@@ -402,7 +402,7 @@
}
@Test
- fun `actions - device unlocked user is guest - empty list`() =
+ fun actions_deviceUnlockedUserIsGuest_emptyList() =
testScope.runTest {
val userInfos = createUserInfos(count = 2, includeGuest = true)
assertThat(userInfos[1].isGuest).isTrue()
@@ -416,7 +416,7 @@
}
@Test
- fun `actions - device locked add from lockscreen set - full list`() =
+ fun actions_deviceLockedAddFromLockscreenSet_fullList() =
testScope.runTest {
val userInfos = createUserInfos(count = 2, includeGuest = false)
userRepository.setUserInfos(userInfos)
@@ -442,7 +442,7 @@
}
@Test
- fun `actions - device locked add from lockscreen set - full list - full screen`() =
+ fun actions_deviceLockedAddFromLockscreenSet_fullList_fullScreen() =
testScope.runTest {
featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
val userInfos = createUserInfos(count = 2, includeGuest = false)
@@ -469,7 +469,7 @@
}
@Test
- fun `actions - device locked - only manage user is shown`() =
+ fun actions_deviceLocked_onlymanageUserIsShown() =
testScope.runTest {
val userInfos = createUserInfos(count = 2, includeGuest = false)
userRepository.setUserInfos(userInfos)
@@ -482,7 +482,7 @@
}
@Test
- fun `executeAction - add user - dialog shown`() =
+ fun executeAction_addUser_dialogShown() =
testScope.runTest {
val userInfos = createUserInfos(count = 2, includeGuest = false)
userRepository.setUserInfos(userInfos)
@@ -509,7 +509,7 @@
}
@Test
- fun `executeAction - add supervised user - dismisses dialog and starts activity`() =
+ fun executeAction_addSupervisedUser_dismissesDialogAndStartsActivity() =
testScope.runTest {
underTest.executeAction(UserActionModel.ADD_SUPERVISED_USER)
@@ -523,7 +523,7 @@
}
@Test
- fun `executeAction - navigate to manage users`() =
+ fun executeAction_navigateToManageUsers() =
testScope.runTest {
underTest.executeAction(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT)
@@ -533,7 +533,7 @@
}
@Test
- fun `executeAction - guest mode`() =
+ fun executeAction_guestMode() =
testScope.runTest {
val userInfos = createUserInfos(count = 2, includeGuest = false)
userRepository.setUserInfos(userInfos)
@@ -571,7 +571,7 @@
}
@Test
- fun `selectUser - already selected guest re-selected - exit guest dialog`() =
+ fun selectUser_alreadySelectedGuestReSelected_exitGuestDialog() =
testScope.runTest {
val userInfos = createUserInfos(count = 2, includeGuest = true)
val guestUserInfo = userInfos[1]
@@ -592,7 +592,7 @@
}
@Test
- fun `selectUser - currently guest non-guest selected - exit guest dialog`() =
+ fun selectUser_currentlyGuestNonGuestSelected_exitGuestDialog() =
testScope.runTest {
val userInfos = createUserInfos(count = 2, includeGuest = true)
val guestUserInfo = userInfos[1]
@@ -610,7 +610,7 @@
}
@Test
- fun `selectUser - not currently guest - switches users`() =
+ fun selectUser_notCurrentlyGuest_switchesUsers() =
testScope.runTest {
val userInfos = createUserInfos(count = 2, includeGuest = false)
userRepository.setUserInfos(userInfos)
@@ -626,7 +626,7 @@
}
@Test
- fun `Telephony call state changes - refreshes users`() =
+ fun telephonyCallStateChanges_refreshesUsers() =
testScope.runTest {
runCurrent()
@@ -639,7 +639,7 @@
}
@Test
- fun `User switched broadcast`() =
+ fun userSwitchedBroadcast() =
testScope.runTest {
val userInfos = createUserInfos(count = 2, includeGuest = false)
userRepository.setUserInfos(userInfos)
@@ -670,7 +670,7 @@
}
@Test
- fun `User info changed broadcast`() =
+ fun userInfoChangedBroadcast() =
testScope.runTest {
val userInfos = createUserInfos(count = 2, includeGuest = false)
userRepository.setUserInfos(userInfos)
@@ -690,7 +690,7 @@
}
@Test
- fun `System user unlocked broadcast - refresh users`() =
+ fun systemUserUnlockedBroadcast_refreshUsers() =
testScope.runTest {
val userInfos = createUserInfos(count = 2, includeGuest = false)
userRepository.setUserInfos(userInfos)
@@ -710,7 +710,7 @@
}
@Test
- fun `Non-system user unlocked broadcast - do not refresh users`() =
+ fun nonSystemUserUnlockedBroadcast_doNotRefreshUsers() =
testScope.runTest {
val userInfos = createUserInfos(count = 2, includeGuest = false)
userRepository.setUserInfos(userInfos)
@@ -799,7 +799,7 @@
}
@Test
- fun `users - secondary user - guest user can be switched to`() =
+ fun users_secondaryUser_guestUserCanBeSwitchedTo() =
testScope.runTest {
val userInfos = createUserInfos(count = 3, includeGuest = true)
userRepository.setUserInfos(userInfos)
@@ -812,7 +812,7 @@
}
@Test
- fun `users - secondary user - no guest action`() =
+ fun users_secondaryUser_noGuestAction() =
testScope.runTest {
val userInfos = createUserInfos(count = 3, includeGuest = true)
userRepository.setUserInfos(userInfos)
@@ -824,7 +824,7 @@
}
@Test
- fun `users - secondary user - no guest user record`() =
+ fun users_secondaryUser_noGuestUserRecord() =
testScope.runTest {
val userInfos = createUserInfos(count = 3, includeGuest = true)
userRepository.setUserInfos(userInfos)
@@ -835,7 +835,7 @@
}
@Test
- fun `show user switcher - full screen disabled - shows dialog switcher`() =
+ fun showUserSwitcher_fullScreenDisabled_showsDialogSwitcher() =
testScope.runTest {
val expandable = mock<Expandable>()
underTest.showUserSwitcher(expandable)
@@ -851,7 +851,7 @@
}
@Test
- fun `show user switcher - full screen enabled - launches full screen dialog`() =
+ fun showUserSwitcher_fullScreenEnabled_launchesFullScreenDialog() =
testScope.runTest {
featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
@@ -869,7 +869,7 @@
}
@Test
- fun `users - secondary user - managed profile is not included`() =
+ fun users_secondaryUser_managedProfileIsNotIncluded() =
testScope.runTest {
val userInfos = createUserInfos(count = 3, includeGuest = false).toMutableList()
userInfos.add(
@@ -889,7 +889,7 @@
}
@Test
- fun `current user is not primary and user switcher is disabled`() =
+ fun currentUserIsNotPrimaryAndUserSwitcherIsDisabled() =
testScope.runTest {
val userInfos = createUserInfos(count = 2, includeGuest = false)
userRepository.setUserInfos(userInfos)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index 9b74c1f..fd8c6c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -137,7 +137,7 @@
}
@Test
- fun `config is false - chip is disabled`() {
+ fun configIsFalse_chipIsDisabled() {
// the enabled bit is set at SystemUI startup, so recreate the view model here
userRepository.isStatusBarUserChipEnabled = false
underTest = viewModel()
@@ -146,7 +146,7 @@
}
@Test
- fun `config is true - chip is enabled`() {
+ fun configIsTrue_chipIsEnabled() {
// the enabled bit is set at SystemUI startup, so recreate the view model here
userRepository.isStatusBarUserChipEnabled = true
underTest = viewModel()
@@ -155,7 +155,7 @@
}
@Test
- fun `should show chip criteria - single user`() =
+ fun shouldShowChipCriteria_singleUser() =
testScope.runTest {
userRepository.setUserInfos(listOf(USER_0))
userRepository.setSelectedUserInfo(USER_0)
@@ -172,7 +172,7 @@
}
@Test
- fun `should show chip criteria - multiple users`() =
+ fun shouldShowChipCriteria_multipleUsers() =
testScope.runTest {
setMultipleUsers()
@@ -186,7 +186,7 @@
}
@Test
- fun `user chip name - shows selected user info`() =
+ fun userChipName_showsSelectedUserInfo() =
testScope.runTest {
setMultipleUsers()
@@ -206,7 +206,7 @@
}
@Test
- fun `user chip avatar - shows selected user info`() =
+ fun userChipAvatar_showsSelectedUserInfo() =
testScope.runTest {
setMultipleUsers()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index a342dad..9155084 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -232,7 +232,7 @@
}
@Test
- fun `maximumUserColumns - few users`() =
+ fun maximumUserColumns_fewUsers() =
testScope.runTest {
setUsers(count = 2)
val values = mutableListOf<Int>()
@@ -244,7 +244,7 @@
}
@Test
- fun `maximumUserColumns - many users`() =
+ fun maximumUserColumns_manyUsers() =
testScope.runTest {
setUsers(count = 5)
val values = mutableListOf<Int>()
@@ -255,7 +255,7 @@
}
@Test
- fun `isOpenMenuButtonVisible - has actions - true`() =
+ fun isOpenMenuButtonVisible_hasActions_true() =
testScope.runTest {
setUsers(2)
@@ -267,7 +267,7 @@
}
@Test
- fun `isOpenMenuButtonVisible - no actions - false`() =
+ fun isOpenMenuButtonVisible_noActions_false() =
testScope.runTest {
val userInfos = setUsers(2)
userRepository.setSelectedUserInfo(userInfos[1])
@@ -298,7 +298,7 @@
}
@Test
- fun `menu actions`() =
+ fun menuActions() =
testScope.runTest {
setUsers(2)
val actions = mutableListOf<List<UserActionViewModel>>()
@@ -318,7 +318,7 @@
}
@Test
- fun `isFinishRequested - finishes when cancel button is clicked`() =
+ fun isFinishRequested_finishesWhenCancelButtonIsClicked() =
testScope.runTest {
setUsers(count = 2)
val isFinishRequested = mutableListOf<Boolean>()
@@ -338,7 +338,7 @@
}
@Test
- fun `guest selected -- name is exit guest`() =
+ fun guestSelected_nameIsExitGuest() =
testScope.runTest {
val userInfos =
listOf(
@@ -386,7 +386,7 @@
}
@Test
- fun `guest not selected -- name is guest`() =
+ fun guestNotSelected_nameIsGuest() =
testScope.runTest {
val userInfos =
listOf(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt
index 9bd3a79..3901d72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt
@@ -93,7 +93,7 @@
}
@Test
- fun `state - has wallet cards- callbacks called`() = runTest {
+ fun state_hasWalletCardsCallbacksCalled() = runTest {
setUpWalletClient(listOf(CARD_1, CARD_2, PAYMENT_CARD))
val controller = createWalletContextualSuggestionsController(backgroundScope)
var latest1 = emptyList<WalletCard>()
@@ -115,7 +115,7 @@
}
@Test
- fun `state - no wallet cards - set suggestion cards`() = runTest {
+ fun state_noWalletCards_setSuggestionCards() = runTest {
setUpWalletClient(emptyList())
val controller = createWalletContextualSuggestionsController(backgroundScope)
val latest =
@@ -132,7 +132,7 @@
}
@Test
- fun `state - has wallet cards - set and update suggestion cards`() = runTest {
+ fun state_hasWalletCards_setAndUpdateSuggestionCards() = runTest {
setUpWalletClient(listOf(CARD_1, CARD_2, PAYMENT_CARD))
val controller = createWalletContextualSuggestionsController(backgroundScope)
val latest =
@@ -151,7 +151,7 @@
}
@Test
- fun `state - wallet cards error`() = runTest {
+ fun state_walletCardsError() = runTest {
setUpWalletClient(shouldFail = true)
val controller = createWalletContextualSuggestionsController(backgroundScope)
val latest =
@@ -167,7 +167,7 @@
}
@Test
- fun `state - has wallet cards - received contextual cards - feature disabled`() = runTest {
+ fun state_hasWalletCards_receivedContextualCards_featureDisabled() = runTest {
whenever(featureFlags.isEnabled(eq(Flags.ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS)))
.thenReturn(false)
setUpWalletClient(listOf(CARD_1, CARD_2, PAYMENT_CARD))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 9a99538..bc3a5b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -24,7 +24,6 @@
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.wm.shell.bubbles.Bubble.KEY_APP_BUBBLE;
import static com.google.common.truth.Truth.assertThat;
@@ -1734,13 +1733,13 @@
@Test
public void testShowOrHideAppBubble_addsAndExpand() {
assertThat(mBubbleController.isStackExpanded()).isFalse();
- assertThat(mBubbleData.getBubbleInStackWithKey(KEY_APP_BUBBLE)).isNull();
mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
verify(mBubbleController).inflateAndAdd(any(Bubble.class), /* suppressFlyout= */ eq(true),
/* showInShade= */ eq(false));
- assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
+ assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(
+ Bubble.getAppBubbleKeyForApp(mContext.getPackageName(), mUser0));
assertThat(mBubbleController.isStackExpanded()).isTrue();
}
@@ -1754,7 +1753,8 @@
// Calling this while collapsed will expand the app bubble
mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
- assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
+ assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(
+ Bubble.getAppBubbleKeyForApp(mContext.getPackageName(), mUser0));
assertThat(mBubbleController.isStackExpanded()).isTrue();
assertThat(mBubbleData.getBubbles().size()).isEqualTo(2);
}
@@ -1762,13 +1762,15 @@
@Test
public void testShowOrHideAppBubble_collapseIfSelected() {
mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
- assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
+ assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(
+ Bubble.getAppBubbleKeyForApp(mContext.getPackageName(), mUser0));
assertThat(mBubbleController.isStackExpanded()).isTrue();
// Calling this while the app bubble is expanded should collapse the stack
mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
- assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
+ assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(
+ Bubble.getAppBubbleKeyForApp(mContext.getPackageName(), mUser0));
assertThat(mBubbleController.isStackExpanded()).isFalse();
assertThat(mBubbleData.getBubbles().size()).isEqualTo(1);
assertThat(mBubbleData.getBubbles().get(0).getUser()).isEqualTo(mUser0);
@@ -1777,8 +1779,9 @@
@Test
public void testShowOrHideAppBubbleWithNonPrimaryUser_bubbleCollapsedWithExpectedUser() {
UserHandle user10 = createUserHandle(/* userId = */ 10);
+ String appBubbleKey = Bubble.getAppBubbleKeyForApp(mContext.getPackageName(), user10);
mBubbleController.showOrHideAppBubble(mAppBubbleIntent, user10, mAppBubbleIcon);
- assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
+ assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(appBubbleKey);
assertThat(mBubbleController.isStackExpanded()).isTrue();
assertThat(mBubbleData.getBubbles().size()).isEqualTo(1);
assertThat(mBubbleData.getBubbles().get(0).getUser()).isEqualTo(user10);
@@ -1786,13 +1789,28 @@
// Calling this while the app bubble is expanded should collapse the stack
mBubbleController.showOrHideAppBubble(mAppBubbleIntent, user10, mAppBubbleIcon);
- assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
+ assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(appBubbleKey);
assertThat(mBubbleController.isStackExpanded()).isFalse();
assertThat(mBubbleData.getBubbles().size()).isEqualTo(1);
assertThat(mBubbleData.getBubbles().get(0).getUser()).isEqualTo(user10);
}
@Test
+ public void testShowOrHideAppBubbleOnUser10AndThenUser0_user0BubbleExpanded() {
+ UserHandle user10 = createUserHandle(/* userId = */ 10);
+ mBubbleController.showOrHideAppBubble(mAppBubbleIntent, user10, mAppBubbleIcon);
+
+ String appBubbleUser0Key = Bubble.getAppBubbleKeyForApp(mContext.getPackageName(), mUser0);
+ mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
+
+ assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(appBubbleUser0Key);
+ assertThat(mBubbleController.isStackExpanded()).isTrue();
+ assertThat(mBubbleData.getBubbles()).hasSize(2);
+ assertThat(mBubbleData.getBubbles().get(0).getUser()).isEqualTo(mUser0);
+ assertThat(mBubbleData.getBubbles().get(1).getUser()).isEqualTo(user10);
+ }
+
+ @Test
public void testShowOrHideAppBubble_selectIfNotSelected() {
mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
mBubbleController.updateBubble(mBubbleEntry);
@@ -1801,7 +1819,8 @@
assertThat(mBubbleController.isStackExpanded()).isTrue();
mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
- assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(KEY_APP_BUBBLE);
+ assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(
+ Bubble.getAppBubbleKeyForApp(mContext.getPackageName(), mUser0));
assertThat(mBubbleController.isStackExpanded()).isTrue();
assertThat(mBubbleData.getBubbles().size()).isEqualTo(2);
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldRemoteFilter.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldRemoteFilter.kt
index 3041888..843cc3b 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldRemoteFilter.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldRemoteFilter.kt
@@ -34,6 +34,7 @@
}
private var inProgress = false
+ private var receivedProgressEvent = false
private var processedProgress: Float = 1.0f
set(newProgress) {
@@ -54,7 +55,16 @@
override fun onTransitionProgress(progress: Float) {
logCounter({ "$TAG#plain_remote_progress" }, progress)
if (inProgress) {
- springAnimation.animateToFinalPosition(progress)
+ if (receivedProgressEvent) {
+ // We have received at least one progress event, animate from the previous
+ // progress to the current
+ springAnimation.animateToFinalPosition(progress)
+ } else {
+ // This is the first progress event after starting the animation, send it
+ // straightaway and set the spring value without animating it
+ processedProgress = progress
+ receivedProgressEvent = true
+ }
} else {
Log.e(TAG, "Progress received while not in progress.")
}
@@ -62,6 +72,7 @@
override fun onTransitionFinished() {
inProgress = false
+ receivedProgressEvent = false
listener.onTransitionFinished()
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 22e742b..c1c47f5 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -231,6 +231,12 @@
*/
public void transitionMagnificationModeLocked(int displayId, int targetMode,
@NonNull TransitionCallBack transitionCallBack) {
+ // check if target mode is already activated
+ if (isActivated(displayId, targetMode)) {
+ transitionCallBack.onResult(displayId, true);
+ return;
+ }
+
final PointF currentCenter = getCurrentMagnificationCenterLocked(displayId, targetMode);
final DisableMagnificationCallback animationCallback =
getDisableMagnificationEndRunnableLocked(displayId);
@@ -322,13 +328,16 @@
: config.getScale();
try {
setTransitionState(displayId, targetMode);
+ final MagnificationAnimationCallback magnificationAnimationCallback = animate
+ ? success -> mAms.changeMagnificationMode(displayId, targetMode)
+ : null;
// Activate or deactivate target mode depending on config activated value
if (targetMode == MAGNIFICATION_MODE_WINDOW) {
screenMagnificationController.reset(displayId, false);
if (targetActivated) {
windowMagnificationMgr.enableWindowMagnification(displayId,
targetScale, magnificationCenter.x, magnificationCenter.y,
- animate ? STUB_ANIMATION_CALLBACK : null, id);
+ magnificationAnimationCallback, id);
} else {
windowMagnificationMgr.disableWindowMagnification(displayId, false);
}
@@ -339,8 +348,8 @@
screenMagnificationController.register(displayId);
}
screenMagnificationController.setScaleAndCenter(displayId, targetScale,
- magnificationCenter.x, magnificationCenter.y, animate,
- id);
+ magnificationCenter.x, magnificationCenter.y,
+ magnificationAnimationCallback, id);
} else {
if (screenMagnificationController.isRegistered(displayId)) {
screenMagnificationController.reset(displayId, false);
@@ -348,6 +357,9 @@
}
}
} finally {
+ if (!animate) {
+ mAms.changeMagnificationMode(displayId, targetMode);
+ }
// Reset transition state after enabling target mode.
setTransitionState(displayId, null);
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 2a964b8..3bd4547 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -56,6 +56,10 @@
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;
import static com.android.server.autofill.Helper.toArray;
+import static com.android.server.autofill.PresentationStatsEventLogger.AUTHENTICATION_RESULT_FAILURE;
+import static com.android.server.autofill.PresentationStatsEventLogger.AUTHENTICATION_RESULT_SUCCESS;
+import static com.android.server.autofill.PresentationStatsEventLogger.AUTHENTICATION_TYPE_DATASET_AUTHENTICATION;
+import static com.android.server.autofill.PresentationStatsEventLogger.AUTHENTICATION_TYPE_FULL_AUTHENTICATION;
import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_NO_FOCUS;
import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_REQUEST_FAILED;
import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_REQUEST_TIMEOUT;
@@ -75,6 +79,12 @@
import static com.android.server.autofill.SaveEventLogger.SAVE_UI_SHOWN_REASON_REQUIRED_ID_CHANGE;
import static com.android.server.autofill.SaveEventLogger.SAVE_UI_SHOWN_REASON_TRIGGER_ID_SET;
import static com.android.server.autofill.SaveEventLogger.SAVE_UI_SHOWN_REASON_UNKNOWN;
+import static com.android.server.autofill.SessionCommittedEventLogger.CommitReason;
+import static com.android.server.autofill.SessionCommittedEventLogger.COMMIT_REASON_ACTIVITY_FINISHED;
+import static com.android.server.autofill.SessionCommittedEventLogger.COMMIT_REASON_VIEW_CHANGED;
+import static com.android.server.autofill.SessionCommittedEventLogger.COMMIT_REASON_VIEW_CLICKED;
+import static com.android.server.autofill.SessionCommittedEventLogger.COMMIT_REASON_VIEW_COMMITTED;
+import static com.android.server.autofill.SessionCommittedEventLogger.COMMIT_REASON_SESSION_DESTROYED;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
@@ -364,6 +374,11 @@
private final long mStartTime;
/**
+ * Count of FillRequests in the session.
+ */
+ private int mRequestCount;
+
+ /**
* Starting timestamp of latency logger.
* This is set when Session created or when the view is reset.
*/
@@ -1132,6 +1147,7 @@
int flags) {
final FillResponse existingResponse = viewState.getResponse();
mFillRequestEventLogger.startLogForNewRequest();
+ mRequestCount++;
mFillRequestEventLogger.maybeSetAppPackageUid(uid);
mFillRequestEventLogger.maybeSetFlags(mFlags);
if(mPreviouslyFillDialogPotentiallyStarted) {
@@ -1330,8 +1346,6 @@
this.userId = userId;
this.taskId = taskId;
this.uid = uid;
- mStartTime = SystemClock.elapsedRealtime();
- mLatencyBaseTime = mStartTime;
mService = service;
mLock = lock;
mUi = ui;
@@ -1347,11 +1361,17 @@
mComponentName = componentName;
mCompatMode = compatMode;
mSessionState = STATE_ACTIVE;
+ // Initiate all loggers & counters.
+ mStartTime = SystemClock.elapsedRealtime();
+ mLatencyBaseTime = mStartTime;
+ mRequestCount = 0;
mPresentationStatsEventLogger = PresentationStatsEventLogger.forSessionId(sessionId);
mFillRequestEventLogger = FillRequestEventLogger.forSessionId(sessionId);
mFillResponseEventLogger = FillResponseEventLogger.forSessionId(sessionId);
mSessionCommittedEventLogger = SessionCommittedEventLogger.forSessionId(sessionId);
+ mSessionCommittedEventLogger.maybeSetComponentPackageUid(uid);
mSaveEventLogger = SaveEventLogger.forSessionId(sessionId);
+
synchronized (mLock) {
mSessionFlags = new SessionFlags();
mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly;
@@ -1971,6 +1991,7 @@
// Start a new FillRequest logger for client suggestion fallback.
mFillRequestEventLogger.startLogForNewRequest();
+ mRequestCount++;
mFillRequestEventLogger.maybeSetAppPackageUid(uid);
mFillRequestEventLogger.maybeSetFlags(
flags & ~FLAG_ENABLED_CLIENT_SUGGESTIONS);
@@ -2187,6 +2208,8 @@
}
final Intent fillInIntent;
synchronized (mLock) {
+ mPresentationStatsEventLogger.maybeSetAuthenticationType(
+ AUTHENTICATION_TYPE_FULL_AUTHENTICATION);
if (mDestroyed) {
Slog.w(TAG, "Call to Session#authenticate() rejected - session: "
+ id + " destroyed");
@@ -2231,7 +2254,6 @@
if (mDestroyed) {
Slog.w(TAG, "Call to Session#save() rejected - session: "
+ id + " destroyed");
- mSaveEventLogger.logAndEndEvent();
return;
}
}
@@ -2251,7 +2273,6 @@
if (mDestroyed) {
Slog.w(TAG, "Call to Session#cancelSave() rejected - session: "
+ id + " destroyed");
- mSaveEventLogger.logAndEndEvent();
return;
}
}
@@ -2438,18 +2459,26 @@
final int requestId = AutofillManager.getRequestIdFromAuthenticationId(authenticationId);
if (requestId == AUGMENTED_AUTOFILL_REQUEST_ID) {
setAuthenticationResultForAugmentedAutofillLocked(data, authenticationId);
+ // Augmented autofill is not logged.
+ mPresentationStatsEventLogger.logAndEndEvent();
return;
}
if (mResponses == null) {
// Typically happens when app explicitly called cancel() while the service was showing
// the auth UI.
Slog.w(TAG, "setAuthenticationResultLocked(" + authenticationId + "): no responses");
+ mPresentationStatsEventLogger.maybeSetAuthenticationResult(
+ AUTHENTICATION_RESULT_FAILURE);
+ mPresentationStatsEventLogger.logAndEndEvent();
removeFromService();
return;
}
final FillResponse authenticatedResponse = mResponses.get(requestId);
if (authenticatedResponse == null || data == null) {
Slog.w(TAG, "no authenticated response");
+ mPresentationStatsEventLogger.maybeSetAuthenticationResult(
+ AUTHENTICATION_RESULT_FAILURE);
+ mPresentationStatsEventLogger.logAndEndEvent();
removeFromService();
return;
}
@@ -2461,6 +2490,9 @@
final Dataset dataset = authenticatedResponse.getDatasets().get(datasetIdx);
if (dataset == null) {
Slog.w(TAG, "no dataset with index " + datasetIdx + " on fill response");
+ mPresentationStatsEventLogger.maybeSetAuthenticationResult(
+ AUTHENTICATION_RESULT_FAILURE);
+ mPresentationStatsEventLogger.logAndEndEvent();
removeFromService();
return;
}
@@ -2477,11 +2509,15 @@
}
if (result instanceof FillResponse) {
logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_AUTHENTICATED);
+ mPresentationStatsEventLogger.maybeSetAuthenticationResult(
+ AUTHENTICATION_RESULT_SUCCESS);
replaceResponseLocked(authenticatedResponse, (FillResponse) result, newClientState);
} else if (result instanceof Dataset) {
if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) {
logAuthenticationStatusLocked(requestId,
MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED);
+ mPresentationStatsEventLogger.maybeSetAuthenticationResult(
+ AUTHENTICATION_RESULT_SUCCESS);
if (newClientState != null) {
if (sDebug) Slog.d(TAG, "Updating client state from auth dataset");
mClientState = newClientState;
@@ -2497,6 +2533,8 @@
+ authenticationId);
logAuthenticationStatusLocked(requestId,
MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION);
+ mPresentationStatsEventLogger.maybeSetAuthenticationResult(
+ AUTHENTICATION_RESULT_FAILURE);
}
} else {
if (result != null) {
@@ -2504,6 +2542,8 @@
}
logAuthenticationStatusLocked(requestId,
MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION);
+ mPresentationStatsEventLogger.maybeSetAuthenticationResult(
+ AUTHENTICATION_RESULT_FAILURE);
processNullResponseLocked(requestId, 0);
}
}
@@ -4790,6 +4830,7 @@
}
// Log FillRequest for Augmented Autofill.
mFillRequestEventLogger.startLogForNewRequest();
+ mRequestCount++;
mFillRequestEventLogger.maybeSetAppPackageUid(uid);
mFillRequestEventLogger.maybeSetFlags(mFlags);
mFillRequestEventLogger.maybeSetRequestId(AUGMENTED_AUTOFILL_REQUEST_ID);
@@ -5036,6 +5077,7 @@
if (dataset.getAuthentication() == null) {
if (generateEvent) {
mService.logDatasetSelected(dataset.getId(), id, mClientState, uiType);
+ mPresentationStatsEventLogger.maybeSetSelectedDatasetId(datasetIndex);
}
if (mCurrentViewId != null) {
mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
@@ -5046,6 +5088,8 @@
// ...or handle authentication.
mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState, uiType);
+ mPresentationStatsEventLogger.maybeSetAuthenticationType(
+ AUTHENTICATION_TYPE_DATASET_AUTHENTICATION);
setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, false);
final Intent fillInIntent = createAuthFillInIntentLocked(requestId, mClientState);
if (fillInIntent == null) {
@@ -5639,6 +5683,17 @@
*/
@GuardedBy("mLock")
RemoteFillService destroyLocked() {
+ // Log unlogged events.
+ mSessionCommittedEventLogger.maybeSetCommitReason(COMMIT_REASON_SESSION_DESTROYED);
+ mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
+ mSessionCommittedEventLogger.maybeSetSessionDurationMillis(
+ SystemClock.elapsedRealtime() - mStartTime);
+ mSessionCommittedEventLogger.logAndEndEvent();
+ mPresentationStatsEventLogger.logAndEndEvent();
+ mSaveEventLogger.logAndEndEvent();
+ mFillResponseEventLogger.logAndEndEvent();
+ mFillRequestEventLogger.logAndEndEvent();
+
if (mDestroyed) {
return null;
}
diff --git a/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java b/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java
index 92d72ac..541ec80 100644
--- a/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java
@@ -18,6 +18,7 @@
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SESSION_COMMITTED;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_ACTIVITY_FINISHED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_SESSION_DESTROYED;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_UNKNOWN;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_VIEW_CHANGED;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_VIEW_CLICKED;
@@ -53,7 +54,8 @@
COMMIT_REASON_ACTIVITY_FINISHED,
COMMIT_REASON_VIEW_COMMITTED,
COMMIT_REASON_VIEW_CLICKED,
- COMMIT_REASON_VIEW_CHANGED
+ COMMIT_REASON_VIEW_CHANGED,
+ COMMIT_REASON_SESSION_DESTROYED
})
@Retention(RetentionPolicy.SOURCE)
public @interface CommitReason {
@@ -69,6 +71,8 @@
AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_VIEW_CLICKED;
public static final int COMMIT_REASON_VIEW_CHANGED =
AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_VIEW_CHANGED;
+ public static final int COMMIT_REASON_SESSION_DESTROYED =
+ AUTOFILL_SESSION_COMMITTED__COMMIT_REASON__COMMIT_REASON_SESSION_DESTROYED;
private final int mSessionId;
private Optional<SessionCommittedEventInternal> mEventInternal;
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index b042c30..ff72476 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -397,7 +397,7 @@
setUpPipes();
mAgent = mBackupManagerService.bindToAgentSynchronous(mTargetApp,
FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain)
- ? ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL
+ ? ApplicationThreadConstants.BACKUP_MODE_RESTORE
: ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL,
mBackupEligibilityRules.getBackupDestination());
mAgentPackage = pkg;
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 1656b6f..77990af 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -677,7 +677,7 @@
// Good to go! Set up and bind the agent...
mAgent = backupManagerService.bindToAgentSynchronous(
mCurrentPackage.applicationInfo,
- ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL,
+ ApplicationThreadConstants.BACKUP_MODE_RESTORE,
mBackupEligibilityRules.getBackupDestination());
if (mAgent == null) {
Slog.w(TAG, "Can't find backup agent for " + packageName);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 51d349f..9f9642c 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -5912,22 +5912,24 @@
}
private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) {
- // the managing app can always modify accounts
- if (isProfileOwner(callingUid)) {
- return true;
- }
- DevicePolicyManager dpm = (DevicePolicyManager) mContext
- .getSystemService(Context.DEVICE_POLICY_SERVICE);
- String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
- if (typesArray == null) {
- return true;
- }
- for (String forbiddenType : typesArray) {
- if (forbiddenType.equals(accountType)) {
- return false;
+ return Binder.withCleanCallingIdentity(() -> {
+ // the managing app can always modify accounts
+ if (isProfileOwner(callingUid)) {
+ return true;
}
- }
- return true;
+ DevicePolicyManager dpm = (DevicePolicyManager) mContext
+ .getSystemService(Context.DEVICE_POLICY_SERVICE);
+ String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
+ if (typesArray == null) {
+ return true;
+ }
+ for (String forbiddenType : typesArray) {
+ if (forbiddenType.equals(accountType)) {
+ return false;
+ }
+ }
+ return true;
+ });
}
private boolean isProfileOwner(int uid) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 6adccf6..df3c95b 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -829,7 +829,8 @@
// Service.startForeground()), at that point we will consult the BFSL check and the timeout
// and make the necessary decisions.
setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, r, userId,
- backgroundStartPrivileges, false /* isBindService */);
+ backgroundStartPrivileges, false /* isBindService */,
+ !fgRequired /* isStartService */);
if (!mAm.mUserController.exists(r.userId)) {
Slog.w(TAG, "Trying to start service with non-existent user! " + r.userId);
@@ -2119,7 +2120,11 @@
}
}
- if (r.isForeground && isOldTypeShortFgs) {
+ final boolean enableFgsWhileInUseFix = mAm.mConstants.mEnableFgsWhileInUseFix;
+ final boolean fgsTypeChangingFromShortFgs = r.isForeground && isOldTypeShortFgs;
+
+ if (fgsTypeChangingFromShortFgs) {
+
// If we get here, that means startForeground(SHORT_SERVICE) is called again
// on a SHORT_SERVICE FGS.
@@ -2128,7 +2133,7 @@
setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
r.appInfo.uid, r.intent.getIntent(), r, r.userId,
BackgroundStartPrivileges.NONE,
- false /* isBindService */);
+ false /* isBindService */, false /* isStartService */);
if (r.mAllowStartForeground == REASON_DENIED) {
Slog.w(TAG_SERVICE, "FGS type change to/from SHORT_SERVICE: "
+ " BFSL DENIED.");
@@ -2171,8 +2176,19 @@
// "if (r.mAllowStartForeground == REASON_DENIED...)" block below.
}
}
+ }
- } else if (r.mStartForegroundCount == 0) {
+ // Re-evaluate mAllowWhileInUsePermissionInFgs and mAllowStartForeground
+ // (i.e. while-in-use and BFSL flags) if needed.
+ //
+ // Consider the below if-else section to be in the else of the above
+ // `if (fgsTypeChangingFromShortFgs)`.
+ // Using an else would increase the indent further, so we don't use it here
+ // and instead just add !fgsTypeChangingFromShortFgs to all if's.
+ //
+ // The first if's are for the original while-in-use logic.
+ if (!fgsTypeChangingFromShortFgs && !enableFgsWhileInUseFix
+ && r.mStartForegroundCount == 0) {
/*
If the service was started with startService(), not
startForegroundService(), and if startForeground() isn't called within
@@ -2193,7 +2209,7 @@
setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
r.appInfo.uid, r.intent.getIntent(), r, r.userId,
BackgroundStartPrivileges.NONE,
- false /* isBindService */);
+ false /* isBindService */, false /* isStartService */);
final String temp = "startForegroundDelayMs:" + delayMs;
if (r.mInfoAllowStartForeground != null) {
r.mInfoAllowStartForeground += "; " + temp;
@@ -2203,9 +2219,10 @@
r.mLoggedInfoAllowStartForeground = false;
}
}
- } else if (r.mStartForegroundCount >= 1) {
+ } else if (!fgsTypeChangingFromShortFgs && !enableFgsWhileInUseFix
+ && r.mStartForegroundCount >= 1) {
// We get here if startForeground() is called multiple times
- // on the same sarvice after it's created, regardless of whether
+ // on the same service after it's created, regardless of whether
// stopForeground() has been called or not.
// The second or later time startForeground() is called after service is
@@ -2213,7 +2230,71 @@
setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
r.appInfo.uid, r.intent.getIntent(), r, r.userId,
BackgroundStartPrivileges.NONE,
- false /* isBindService */);
+ false /* isBindService */, false /* isStartService */);
+ } else if (!fgsTypeChangingFromShortFgs && enableFgsWhileInUseFix) {
+ // The new while-in-use logic.
+ //
+ // When startForeground() is called, we _always_ call
+ // setFgsRestrictionLocked() to set the restrictions according to the
+ // current state of the app.
+ // (So if the app is now in TOP, for example, the service will now always
+ // get while-in-use permissions.)
+ //
+ // Note, setFgsRestrictionLocked() will never disallow
+ // mAllowWhileInUsePermissionInFgs nor mAllowStartForeground
+ // (i.e. while-in-use and BFSL flags) once they're set to "allowed".
+ //
+ // HOWEVER, if these flags were set to "allowed" in Context.startService()
+ // (as opposed to startForegroundService()), when the service wasn't yet
+ // a foreground service, then we may not always
+ // want to trust them -- for example, if the service has been running as a
+ // BG service or a bound service for a long time when the app is not longer
+ // in the foreground, then we shouldn't grant while-in-user nor BFSL.
+ // So in that case, we need to reset it first.
+
+ final long delayMs =
+ (r.mLastUntrustedSetFgsRestrictionAllowedTime == 0) ? 0
+ : (SystemClock.elapsedRealtime()
+ - r.mLastUntrustedSetFgsRestrictionAllowedTime);
+ final boolean resetNeeded =
+ !r.isForeground
+ && delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs;
+ if (resetNeeded) {
+ resetFgsRestrictionLocked(r);
+ }
+ setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(),
+ r.appInfo.uid, r.intent.getIntent(), r, r.userId,
+ BackgroundStartPrivileges.NONE,
+ false /* isBindService */, false /* isStartService */);
+
+ final String temp = "startForegroundDelayMs:" + delayMs
+ + "; started: " + r.startRequested
+ + "; num_bindings: " + r.getConnections().size()
+ + "; wasForeground: " + r.isForeground
+ + "; resetNeeded:" + resetNeeded;
+ if (r.mInfoAllowStartForeground != null) {
+ r.mInfoAllowStartForeground += "; " + temp;
+ } else {
+ r.mInfoAllowStartForeground = temp;
+ }
+ r.mLoggedInfoAllowStartForeground = false;
+ }
+
+ // If the service has any bindings and it's not yet a FGS
+ // we compare the new and old while-in-use logics.
+ // (If it's not the first startForeground() call, we already reset the
+ // while-in-use and BFSL flags, so the logic change wouldn't matter.)
+ if (enableFgsWhileInUseFix
+ && !r.isForeground
+ && (r.getConnections().size() > 0)
+ && (r.mDebugWhileInUseReasonInBindService
+ != r.mDebugWhileInUseReasonInStartForeground)) {
+ Slog.wtf(TAG, "FGS while-in-use changed (b/276963716): old="
+ + reasonCodeToString(r.mDebugWhileInUseReasonInBindService)
+ + " new="
+ + reasonCodeToString(r.mDebugWhileInUseReasonInStartForeground)
+ + " "
+ + r.shortInstanceName);
}
// If the foreground service is not started from TOP process, do not allow it to
@@ -2321,6 +2402,11 @@
active.mNumActive++;
}
r.isForeground = true;
+
+ // Once the service becomes a foreground service,
+ // the FGS restriction information always becomes "trustable".
+ r.mLastUntrustedSetFgsRestrictionAllowedTime = 0;
+
// The logging of FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER event could
// be deferred, make a copy of mAllowStartForeground and
// mAllowWhileInUsePermissionInFgs.
@@ -3663,8 +3749,25 @@
return 0;
}
}
- setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, userId,
- BackgroundStartPrivileges.NONE, true /* isBindService */);
+ if (!mAm.mConstants.mEnableFgsWhileInUseFix) {
+ // Old while-in-use logic.
+ setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, userId,
+ BackgroundStartPrivileges.NONE, true /* isBindService */,
+ false /* isStartService */);
+ } else {
+ // New logic will not call setFgsRestrictionLocked() here, but we still
+ // keep track of the allow reason from the old logic here, so we can compare to
+ // the new logic.
+ // Once we're confident enough in the new logic, we should remove it.
+ if (s.mDebugWhileInUseReasonInBindService == REASON_DENIED) {
+ s.mDebugWhileInUseReasonInBindService =
+ shouldAllowFgsWhileInUsePermissionLocked(
+ callingPackage, callingPid, callingUid, s.app,
+ BackgroundStartPrivileges.NONE,
+ true /* isBindService */,
+ false /* DO NOT enableFgsWhileInUseFix; use the old logic */);
+ }
+ }
if (s.app != null) {
ProcessServiceRecord servicePsr = s.app.mServices;
@@ -7357,45 +7460,76 @@
* @param callingUid caller app's uid.
* @param intent intent to start/bind service.
* @param r the service to start.
+ * @param isStartService True if it's called from Context.startService().
+ * False if it's called from Context.startForegroundService() or
+ * Service.startService().
* @return true if allow, false otherwise.
*/
private void setFgsRestrictionLocked(String callingPackage,
int callingPid, int callingUid, Intent intent, ServiceRecord r, int userId,
- BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService) {
- r.mLastSetFgsRestrictionTime = SystemClock.elapsedRealtime();
+ BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService,
+ boolean isStartService) {
+ final long now = SystemClock.elapsedRealtime();
+
// Check DeviceConfig flag.
if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {
r.mAllowWhileInUsePermissionInFgs = true;
}
final @ReasonCode int allowWhileInUse;
+
+ // Either (or both) mAllowWhileInUsePermissionInFgs or mAllowStartForeground is
+ // newly allowed?
+ boolean newlyAllowed = false;
if (!r.mAllowWhileInUsePermissionInFgs
|| (r.mAllowStartForeground == REASON_DENIED)) {
allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
callingPackage, callingPid, callingUid, r.app, backgroundStartPrivileges,
isBindService);
+ // We store them to compare the old and new while-in-use logics to each other.
+ // (They're not used for any other purposes.)
+ if (isBindService) {
+ r.mDebugWhileInUseReasonInBindService = allowWhileInUse;
+ } else {
+ r.mDebugWhileInUseReasonInStartForeground = allowWhileInUse;
+ }
if (!r.mAllowWhileInUsePermissionInFgs) {
r.mAllowWhileInUsePermissionInFgs = (allowWhileInUse != REASON_DENIED);
+ newlyAllowed |= r.mAllowWhileInUsePermissionInFgs;
}
if (r.mAllowStartForeground == REASON_DENIED) {
r.mAllowStartForeground = shouldAllowFgsStartForegroundWithBindingCheckLocked(
allowWhileInUse, callingPackage, callingPid, callingUid, intent, r,
backgroundStartPrivileges, isBindService);
+ newlyAllowed |= r.mAllowStartForeground != REASON_DENIED;
}
} else {
allowWhileInUse = REASON_UNKNOWN;
}
r.mAllowWhileInUsePermissionInFgsReason = allowWhileInUse;
+
+ if (isStartService && !r.isForeground && newlyAllowed) {
+ // If it's called by Context.startService() (not by startForegroundService()),
+ // and we're setting "allowed", then we can't fully trust it yet -- we'll need to reset
+ // the restrictions if startForeground() is called after the grace period.
+ r.mLastUntrustedSetFgsRestrictionAllowedTime = now;
+ }
}
void resetFgsRestrictionLocked(ServiceRecord r) {
r.mAllowWhileInUsePermissionInFgs = false;
r.mAllowWhileInUsePermissionInFgsReason = REASON_DENIED;
+ r.mDebugWhileInUseReasonInStartForeground = REASON_DENIED;
+ // We don't reset mWhileInUseReasonInBindService here, because if we do this, we would
+ // lose it in the "reevaluation" case in startForeground(), where we call
+ // resetFgsRestrictionLocked().
+ // Not resetting this is fine because it's only used in the first Service.startForeground()
+ // case, and there's no situations where we call resetFgsRestrictionLocked() before that.
r.mAllowStartForeground = REASON_DENIED;
r.mInfoAllowStartForeground = null;
r.mInfoTempFgsAllowListReason = null;
r.mLoggedInfoAllowStartForeground = false;
- r.mLastSetFgsRestrictionTime = 0;
+ r.mLastUntrustedSetFgsRestrictionAllowedTime = 0;
r.updateAllowUiJobScheduling(r.mAllowWhileInUsePermissionInFgs);
}
@@ -7430,14 +7564,29 @@
private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
int callingPid, int callingUid, @Nullable ProcessRecord targetProcess,
BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService) {
+ return shouldAllowFgsWhileInUsePermissionLocked(callingPackage,
+ callingPid, callingUid, targetProcess, backgroundStartPrivileges, isBindService,
+ /* enableFgsWhileInUseFix =*/ mAm.mConstants.mEnableFgsWhileInUseFix);
+ }
+
+ private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
+ int callingPid, int callingUid, @Nullable ProcessRecord targetProcess,
+ BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService,
+ boolean enableFgsWhileInUseFix) {
int ret = REASON_DENIED;
- final int uidState = mAm.getUidStateLocked(callingUid);
- if (ret == REASON_DENIED) {
- // Allow FGS while-in-use if the caller's process state is PROCESS_STATE_PERSISTENT,
- // PROCESS_STATE_PERSISTENT_UI or PROCESS_STATE_TOP.
- if (uidState <= PROCESS_STATE_TOP) {
- ret = getReasonCodeFromProcState(uidState);
+ // Define some local variables for better readability...
+ final boolean useOldLogic = !enableFgsWhileInUseFix;
+ final boolean forStartForeground = !isBindService;
+
+ if (useOldLogic || forStartForeground) {
+ final int uidState = mAm.getUidStateLocked(callingUid);
+ if (ret == REASON_DENIED) {
+ // Allow FGS while-in-use if the caller's process state is PROCESS_STATE_PERSISTENT,
+ // PROCESS_STATE_PERSISTENT_UI or PROCESS_STATE_TOP.
+ if (uidState <= PROCESS_STATE_TOP) {
+ ret = getReasonCodeFromProcState(uidState);
+ }
}
}
@@ -7480,6 +7629,10 @@
}
}
+ if (enableFgsWhileInUseFix && ret == REASON_DENIED) {
+ ret = shouldAllowFgsWhileInUsePermissionByBindingsLocked(callingUid);
+ }
+
if (ret == REASON_DENIED) {
// Allow FGS while-in-use if the WindowManager allows background activity start.
// This is mainly to get the 10 seconds grace period if any activity in the caller has
@@ -7558,6 +7711,59 @@
}
/**
+ * Check all bindings into the calling UID, and see if:
+ * - It's bound by a TOP app
+ * - or, bound by a persistent process with BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS.
+ */
+ private @ReasonCode int shouldAllowFgsWhileInUsePermissionByBindingsLocked(int callingUid) {
+ final ArraySet<Integer> checkedClientUids = new ArraySet<>();
+ final Integer result = mAm.mProcessList.searchEachLruProcessesLOSP(
+ false, pr -> {
+ if (pr.uid != callingUid) {
+ return null;
+ }
+ final ProcessServiceRecord psr = pr.mServices;
+ final int serviceCount = psr.mServices.size();
+ for (int svc = 0; svc < serviceCount; svc++) {
+ final ArrayMap<IBinder, ArrayList<ConnectionRecord>> conns =
+ psr.mServices.valueAt(svc).getConnections();
+ final int size = conns.size();
+ for (int conni = 0; conni < size; conni++) {
+ final ArrayList<ConnectionRecord> crs = conns.valueAt(conni);
+ for (int con = 0; con < crs.size(); con++) {
+ final ConnectionRecord cr = crs.get(con);
+ final ProcessRecord clientPr = cr.binding.client;
+ final int clientUid = clientPr.uid;
+
+ // An UID can bind to itself, do not check on itself again.
+ // Also skip already checked clientUid.
+ if (clientUid == callingUid
+ || checkedClientUids.contains(clientUid)) {
+ continue;
+ }
+
+ // Binding found, check the client procstate and the flag.
+ final int clientUidState = mAm.getUidStateLocked(callingUid);
+ final boolean boundByTop = clientUidState == PROCESS_STATE_TOP;
+ final boolean boundByPersistentWithBal =
+ clientUidState < PROCESS_STATE_TOP
+ && cr.hasFlag(
+ Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS);
+ if (boundByTop || boundByPersistentWithBal) {
+ return getReasonCodeFromProcState(clientUidState);
+ }
+
+ // Don't check the same UID.
+ checkedClientUids.add(clientUid);
+ }
+ }
+ }
+ return null;
+ });
+ return result == null ? REASON_DENIED : result;
+ }
+
+ /**
* The uid is not allowed to start FGS, but the uid has a service that is bound
* by a clientUid, if the clientUid can start FGS, then the clientUid can propagate its
* BG-FGS-start capability down to the callingUid.
@@ -8142,7 +8348,8 @@
r.mFgsEnterTime = SystemClock.uptimeMillis();
r.foregroundServiceType = options.mForegroundServiceTypes;
setFgsRestrictionLocked(callingPackage, callingPid, callingUid, intent, r, userId,
- BackgroundStartPrivileges.NONE, false);
+ BackgroundStartPrivileges.NONE, false /* isBindService */,
+ false /* isStartService */);
final ProcessServiceRecord psr = callerApp.mServices;
final boolean newService = psr.startService(r);
// updateOomAdj.
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 44e198b..3841b6a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -1058,6 +1058,13 @@
/** @see #KEY_USE_MODERN_TRIM */
public boolean USE_MODERN_TRIM = DEFAULT_USE_MODERN_TRIM;
+ private static final String KEY_ENABLE_FGS_WHILE_IN_USE_FIX =
+ "key_enable_fgs_while_in_use_fix";
+
+ private static final boolean DEFAULT_ENABLE_FGS_WHILE_IN_USE_FIX = true;
+
+ public volatile boolean mEnableFgsWhileInUseFix = DEFAULT_ENABLE_FGS_WHILE_IN_USE_FIX;
+
private final OnPropertiesChangedListener mOnDeviceConfigChangedListener =
new OnPropertiesChangedListener() {
@Override
@@ -1226,6 +1233,9 @@
case KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION:
updateEnableWaitForFinishAttachApplication();
break;
+ case KEY_ENABLE_FGS_WHILE_IN_USE_FIX:
+ updateEnableFgsWhileInUseFix();
+ break;
case KEY_MAX_PREVIOUS_TIME:
updateMaxPreviousTime();
break;
@@ -1995,6 +2005,12 @@
DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION);
}
+ private void updateEnableFgsWhileInUseFix() {
+ mEnableFgsWhileInUseFix = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_ENABLE_FGS_WHILE_IN_USE_FIX,
+ DEFAULT_ENABLE_FGS_WHILE_IN_USE_FIX);
+ }
private void updateUseTieredCachedAdj() {
USE_TIERED_CACHED_ADJ = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -2195,6 +2211,9 @@
pw.print(" "); pw.print(KEY_SYSTEM_EXEMPT_POWER_RESTRICTIONS_ENABLED);
pw.print("="); pw.println(mFlagSystemExemptPowerRestrictionsEnabled);
+ pw.print(" "); pw.print(KEY_ENABLE_FGS_WHILE_IN_USE_FIX);
+ pw.print("="); pw.println(mEnableFgsWhileInUseFix);
+
pw.print(" "); pw.print(KEY_SHORT_FGS_TIMEOUT_DURATION);
pw.print("="); pw.println(mShortFgsTimeoutDuration);
pw.print(" "); pw.print(KEY_SHORT_FGS_PROC_STATE_EXTRA_WAIT_DURATION);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 97d34b8..1f80aec 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -382,6 +382,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.util.StatsEvent;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.util.proto.ProtoUtils;
@@ -1595,6 +1596,7 @@
static final int SERVICE_SHORT_FGS_TIMEOUT_MSG = 76;
static final int SERVICE_SHORT_FGS_PROCSTATE_TIMEOUT_MSG = 77;
static final int SERVICE_SHORT_FGS_ANR_TIMEOUT_MSG = 78;
+ static final int UPDATE_CACHED_APP_HIGH_WATERMARK = 79;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1938,6 +1940,9 @@
case SERVICE_SHORT_FGS_ANR_TIMEOUT_MSG: {
mServices.onShortFgsAnrTimeout((ServiceRecord) msg.obj);
} break;
+ case UPDATE_CACHED_APP_HIGH_WATERMARK: {
+ mAppProfiler.mCachedAppsWatermarkData.updateCachedAppsSnapshot((long) msg.obj);
+ } break;
}
}
}
@@ -4598,8 +4603,7 @@
boolean isRestrictedBackupMode = false;
if (backupTarget != null && backupTarget.appInfo.packageName.equals(processName)) {
isRestrictedBackupMode = backupTarget.appInfo.uid >= FIRST_APPLICATION_UID
- && ((backupTarget.backupMode == BackupRecord.RESTORE)
- || (backupTarget.backupMode == BackupRecord.RESTORE_FULL)
+ && ((backupTarget.backupMode == BackupRecord.RESTORE_FULL)
|| (backupTarget.backupMode == BackupRecord.BACKUP_FULL));
}
@@ -7311,7 +7315,14 @@
// Send broadcast to shell to trigger bugreport using Bugreport API
// Always start the shell process on the current user to ensure that
// the foreground user can see all bugreport notifications.
- mContext.sendBroadcastAsUser(triggerShellBugreport, getCurrentUser().getUserHandle());
+ // In case of BUGREPORT_MODE_REMOTE send the broadcast to SYSTEM user as the device
+ // owner apps are running on the SYSTEM user.
+ if (bugreportType == BugreportParams.BUGREPORT_MODE_REMOTE) {
+ mContext.sendBroadcastAsUser(triggerShellBugreport, UserHandle.SYSTEM);
+ } else {
+ mContext.sendBroadcastAsUser(triggerShellBugreport,
+ getCurrentUser().getUserHandle());
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -13382,7 +13393,8 @@
BackupRecord r = new BackupRecord(app, backupMode, targetUserId, backupDestination);
ComponentName hostingName =
- (backupMode == ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL)
+ (backupMode == ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL
+ || backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE)
? new ComponentName(app.packageName, app.backupAgentName)
: new ComponentName("android", "FullBackupAgent");
@@ -18598,6 +18610,13 @@
@MediaProjectionTokenEvent int event) {
ActivityManagerService.this.notifyMediaProjectionEvent(uid, projectionToken, event);
}
+
+ @Override
+ @NonNull
+ public StatsEvent getCachedAppsHighWatermarkStats(int atomTag, boolean resetAfterPull) {
+ return mAppProfiler.mCachedAppsWatermarkData.getCachedAppsHighWatermarkStats(
+ atomTag, resetAfterPull);
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, TimeoutRecord timeoutRecord) {
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 25ac956..f29a2e1 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -82,17 +82,21 @@
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.StatsEvent;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.ProcessMap;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.BinderInternal;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.MemInfoReader;
+import com.android.internal.util.QuickSelect;
import com.android.server.am.LowMemDetector.MemFactor;
import com.android.server.power.stats.BatteryStatsImpl;
import com.android.server.utils.PriorityDump;
@@ -323,6 +327,8 @@
private final ActivityManagerService mService;
private final Handler mBgHandler;
+ final CachedAppsWatermarkData mCachedAppsWatermarkData = new CachedAppsWatermarkData();
+
/**
* The lock to guard some of the profiling data here and {@link ProcessProfileRecord}.
*
@@ -391,6 +397,193 @@
}
}
+ /**
+ * A simple data class holding the information about the cached apps high watermark.
+ *
+ * Keep it sync with the frameworks/proto_logging/stats/atoms.proto
+ */
+ class CachedAppsWatermarkData {
+ /** The high water mark of the number of cached apps. */
+ @GuardedBy("mProcLock")
+ int mCachedAppHighWatermark;
+
+ /**
+ * The uptime (in seconds) at the high watermark.
+ * Note this is going to be pull metrics, so we'll need the timestamp here.
+ */
+ @GuardedBy("mProcLock")
+ int mUptimeInSeconds;
+
+ /** The number of binder proxy at that high water mark. */
+ @GuardedBy("mProcLock")
+ int mBinderProxySnapshot;
+
+ /** Free physical memory (in kb) on device. */
+ @GuardedBy("mProcLock")
+ int mFreeInKb;
+
+ /** Cched physical memory (in kb) on device. */
+ @GuardedBy("mProcLock")
+ int mCachedInKb;
+
+ /** zram (in kb) on device. */
+ @GuardedBy("mProcLock")
+ int mZramInKb;
+
+ /** Kernel memory (in kb) on device. */
+ @GuardedBy("mProcLock")
+ int mKernelInKb;
+
+ /** The number of apps in frozen state. */
+ @GuardedBy("mProcLock")
+ int mNumOfFrozenApps;
+
+ /** The longest frozen time (now - last_frozen) in current frozen apps. */
+ @GuardedBy("mProcLock")
+ int mLongestFrozenTimeInSeconds;
+
+ /** The shortest frozen time (now - last_frozen) in current frozen apps. */
+ @GuardedBy("mProcLock")
+ int mShortestFrozenTimeInSeconds;
+
+ /** The mean frozen time (now - last_frozen) in current frozen apps. */
+ @GuardedBy("mProcLock")
+ int mMeanFrozenTimeInSeconds;
+
+ /** The average frozen time (now - last_frozen) in current frozen apps. */
+ @GuardedBy("mProcLock")
+ int mAverageFrozenTimeInSeconds;
+
+ /**
+ * This is an array holding the frozen app durations temporarily
+ * while updating the cached app high watermark.
+ */
+ @GuardedBy("mProcLock")
+ private long[] mCachedAppFrozenDurations;
+
+ /**
+ * The earliest frozen timestamp within the frozen apps.
+ */
+ @GuardedBy("mProcLock")
+ private long mEarliestFrozenTimestamp;
+
+ /**
+ * The most recent frozen timestamp within the frozen apps.
+ */
+ @GuardedBy("mProcLock")
+ private long mLatestFrozenTimestamp;
+
+ /**
+ * The sum of total frozen durations of all frozen apps.
+ */
+ @GuardedBy("mProcLock")
+ private long mTotalFrozenDurations;
+
+ @GuardedBy("mProcLock")
+ void updateCachedAppsHighWatermarkIfNecessaryLocked(int numOfCachedApps, long now) {
+ if (numOfCachedApps > mCachedAppHighWatermark) {
+ mCachedAppHighWatermark = numOfCachedApps;
+ mUptimeInSeconds = (int) (now / 1000);
+
+ // The rest of the updates are pretty costly, do it in a separated handler.
+ mService.mHandler.removeMessages(
+ ActivityManagerService.UPDATE_CACHED_APP_HIGH_WATERMARK);
+ mService.mHandler.obtainMessage(
+ ActivityManagerService.UPDATE_CACHED_APP_HIGH_WATERMARK, Long.valueOf(now))
+ .sendToTarget();
+ }
+ }
+
+ void updateCachedAppsSnapshot(long now) {
+ synchronized (mProcLock) {
+ mEarliestFrozenTimestamp = now;
+ mLatestFrozenTimestamp = 0L;
+ mTotalFrozenDurations = 0L;
+ mNumOfFrozenApps = 0;
+ if (mCachedAppFrozenDurations == null
+ || mCachedAppFrozenDurations.length < mCachedAppHighWatermark) {
+ mCachedAppFrozenDurations = new long[Math.max(
+ mCachedAppHighWatermark, mService.mConstants.CUR_MAX_CACHED_PROCESSES)];
+ }
+ mService.mProcessList.forEachLruProcessesLOSP(true, app -> {
+ if (app.mOptRecord.isFrozen()) {
+ final long freezeTime = app.mOptRecord.getFreezeUnfreezeTime();
+ if (freezeTime < mEarliestFrozenTimestamp) {
+ mEarliestFrozenTimestamp = freezeTime;
+ }
+ if (freezeTime > mLatestFrozenTimestamp) {
+ mLatestFrozenTimestamp = freezeTime;
+ }
+ final long duration = now - freezeTime;
+ mTotalFrozenDurations += duration;
+ mCachedAppFrozenDurations[mNumOfFrozenApps++] = duration;
+ }
+ });
+ if (mNumOfFrozenApps > 0) {
+ mLongestFrozenTimeInSeconds = (int) ((now - mEarliestFrozenTimestamp) / 1000);
+ mShortestFrozenTimeInSeconds = (int) ((now - mLatestFrozenTimestamp) / 1000);
+ mAverageFrozenTimeInSeconds =
+ (int) ((mTotalFrozenDurations / mNumOfFrozenApps) / 1000);
+ mMeanFrozenTimeInSeconds = (int) (QuickSelect.select(mCachedAppFrozenDurations,
+ 0, mNumOfFrozenApps, mNumOfFrozenApps / 2) / 1000);
+ }
+
+ mBinderProxySnapshot = 0;
+ final SparseIntArray counts = BinderInternal.nGetBinderProxyPerUidCounts();
+ if (counts != null) {
+ for (int i = 0, size = counts.size(); i < size; i++) {
+ final int uid = counts.keyAt(i);
+ final UidRecord uidRec = mService.mProcessList.getUidRecordLOSP(uid);
+ if (uidRec != null) {
+ mBinderProxySnapshot += counts.valueAt(i);
+ }
+ }
+ }
+
+ final MemInfoReader memInfo = new MemInfoReader();
+ memInfo.readMemInfo();
+ mFreeInKb = (int) memInfo.getFreeSizeKb();
+ mCachedInKb = (int) memInfo.getCachedSizeKb();
+ mZramInKb = (int) memInfo.getZramTotalSizeKb();
+ mKernelInKb = (int) memInfo.getKernelUsedSizeKb();
+ }
+ }
+
+ @NonNull
+ StatsEvent getCachedAppsHighWatermarkStats(int atomTag, boolean resetAfterPull) {
+ synchronized (mProcLock) {
+ final StatsEvent event = FrameworkStatsLog.buildStatsEvent(atomTag,
+ mCachedAppHighWatermark,
+ mUptimeInSeconds,
+ mBinderProxySnapshot,
+ mFreeInKb,
+ mCachedInKb,
+ mZramInKb,
+ mKernelInKb,
+ mNumOfFrozenApps,
+ mLongestFrozenTimeInSeconds,
+ mShortestFrozenTimeInSeconds,
+ mMeanFrozenTimeInSeconds,
+ mAverageFrozenTimeInSeconds);
+ if (resetAfterPull) {
+ mCachedAppHighWatermark = 0;
+ mUptimeInSeconds = 0;
+ mBinderProxySnapshot = 0;
+ mFreeInKb = 0;
+ mCachedInKb = 0;
+ mZramInKb = 0;
+ mKernelInKb = 0;
+ mNumOfFrozenApps = 0;
+ mLongestFrozenTimeInSeconds = 0;
+ mShortestFrozenTimeInSeconds = 0;
+ mMeanFrozenTimeInSeconds = 0;
+ mAverageFrozenTimeInSeconds = 0;
+ }
+ return event;
+ }
+ }
+ }
+
private class BgHandler extends Handler {
static final int COLLECT_PSS_BG_MSG = 1;
static final int DEFER_PSS_MSG = 2;
@@ -954,7 +1147,7 @@
}
@GuardedBy({"mService", "mProcLock"})
- boolean updateLowMemStateLSP(int numCached, int numEmpty, int numTrimming) {
+ boolean updateLowMemStateLSP(int numCached, int numEmpty, int numTrimming, long now) {
int memFactor;
if (mLowMemDetector != null && mLowMemDetector.isAvailable()) {
memFactor = mLowMemDetector.getMemFactor();
@@ -1040,11 +1233,10 @@
mLastNumProcesses = mService.mProcessList.getLruSizeLOSP();
boolean allChanged;
int trackerMemFactor;
- final long now;
synchronized (mService.mProcessStats.mLock) {
- now = SystemClock.uptimeMillis();
allChanged = mService.mProcessStats.setMemFactorLocked(memFactor,
- mService.mAtmInternal == null || !mService.mAtmInternal.isSleeping(), now);
+ mService.mAtmInternal == null || !mService.mAtmInternal.isSleeping(),
+ SystemClock.uptimeMillis() /* re-acquire the time within the lock */);
trackerMemFactor = mService.mProcessStats.getMemFactorLocked();
}
if (memFactor != ADJ_MEM_FACTOR_NORMAL) {
@@ -1124,6 +1316,8 @@
profile.setTrimMemoryLevel(0);
});
}
+ mCachedAppsWatermarkData.updateCachedAppsHighWatermarkIfNecessaryLocked(
+ numCached + numEmpty, now);
return allChanged;
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index c3519d2..1e5f187 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1401,7 +1401,7 @@
mLastFreeSwapPercent = freeSwapPercent;
- return mService.mAppProfiler.updateLowMemStateLSP(numCached, numEmpty, numTrimming);
+ return mService.mAppProfiler.updateLowMemStateLSP(numCached, numEmpty, numTrimming, now);
}
@GuardedBy({"mService", "mProcLock"})
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 312f98a..d7b22a8 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -108,6 +108,7 @@
import android.os.UserHandle;
import android.os.storage.StorageManagerInternal;
import android.system.Os;
+import android.system.OsConstants;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -2331,9 +2332,15 @@
if (!regularZygote) {
// webview and app zygote don't have the permission to create the nodes
- if (Process.createProcessGroup(uid, startResult.pid) < 0) {
- throw new AssertionError("Unable to create process group for " + app.processName
- + " (" + startResult.pid + ")");
+ final int res = Process.createProcessGroup(uid, startResult.pid);
+ if (res < 0) {
+ if (res == -OsConstants.ESRCH) {
+ Slog.e(ActivityManagerService.TAG, "Unable to create process group for "
+ + app.processName + " (" + startResult.pid + ")");
+ } else {
+ throw new AssertionError("Unable to create process group for "
+ + app.processName + " (" + startResult.pid + ")");
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index a875860..78aafeb 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -176,6 +176,13 @@
boolean mAllowWhileInUsePermissionInFgs;
@PowerExemptionManager.ReasonCode
int mAllowWhileInUsePermissionInFgsReason;
+
+ // Integer version of mAllowWhileInUsePermissionInFgs that we keep track to compare
+ // the old and new logics.
+ // TODO: Remove them once we're confident in the new logic.
+ int mDebugWhileInUseReasonInStartForeground = REASON_DENIED;
+ int mDebugWhileInUseReasonInBindService = REASON_DENIED;
+
// A copy of mAllowWhileInUsePermissionInFgs's value when the service is entering FGS state.
boolean mAllowWhileInUsePermissionInFgsAtEntering;
/** Allow scheduling user-initiated jobs from the background. */
@@ -216,8 +223,13 @@
// created. (i.e. due to "bound" or "start".) It never decreases, even when stopForeground()
// is called.
int mStartForegroundCount;
- // Last time mAllowWhileInUsePermissionInFgs or mAllowStartForeground is set.
- long mLastSetFgsRestrictionTime;
+
+ // Last time mAllowWhileInUsePermissionInFgs or mAllowStartForeground was set to "allowed"
+ // from "disallowed" when the service was _not_ already a foreground service.
+ // this means they're set in startService(). (not startForegroundService)
+ // In startForeground(), if this timestamp is too old, we can't trust these flags, so
+ // we need to reset them.
+ long mLastUntrustedSetFgsRestrictionAllowedTime;
// This is a service record of a FGS delegate (not a service record of a real service)
boolean mIsFgsDelegate;
@@ -609,10 +621,14 @@
pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByStart=");
pw.println(mBackgroundStartPrivilegesByStartMerged);
}
- pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs=");
- pw.println(mAllowWhileInUsePermissionInFgs);
pw.print(prefix); pw.print("mAllowWhileInUsePermissionInFgsReason=");
pw.println(mAllowWhileInUsePermissionInFgsReason);
+
+ pw.print(prefix); pw.print("debugWhileInUseReasonInStartForeground=");
+ pw.println(mDebugWhileInUseReasonInStartForeground);
+ pw.print(prefix); pw.print("debugWhileInUseReasonInBindService=");
+ pw.println(mDebugWhileInUseReasonInBindService);
+
pw.print(prefix); pw.print("allowUiJobScheduling="); pw.println(mAllowUiJobScheduling);
pw.print(prefix); pw.print("recentCallingPackage=");
pw.println(mRecentCallingPackage);
@@ -624,6 +640,10 @@
pw.println(mStartForegroundCount);
pw.print(prefix); pw.print("infoAllowStartForeground=");
pw.println(mInfoAllowStartForeground);
+
+ pw.print(prefix); pw.print("lastUntrustedSetFgsRestrictionAllowedTime=");
+ TimeUtils.formatDuration(mLastUntrustedSetFgsRestrictionAllowedTime, now, pw);
+
if (delayed) {
pw.print(prefix); pw.print("delayed="); pw.println(delayed);
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index a3163e0..02c4770 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2284,8 +2284,8 @@
synchronized (VolumeStreamState.class) {
mStreamStates[AudioSystem.STREAM_DTMF]
.setAllIndexes(mStreamStates[dtmfStreamAlias], caller);
- mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].mVolumeIndexSettingName =
- System.VOLUME_SETTINGS_INT[a11yStreamAlias];
+ mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].setSettingName(
+ System.VOLUME_SETTINGS_INT[a11yStreamAlias]);
mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].setAllIndexes(
mStreamStates[a11yStreamAlias], caller);
}
@@ -7700,7 +7700,7 @@
private int mPublicStreamType = AudioSystem.STREAM_MUSIC;
private AudioAttributes mAudioAttributes = AudioProductStrategy.getDefaultAttributes();
private boolean mIsMuted = false;
- private final String mSettingName;
+ private String mSettingName;
// No API in AudioSystem to get a device from strategy or from attributes.
// Need a valid public stream type to use current API getDeviceForStream
@@ -8029,15 +8029,19 @@
}
private void persistVolumeGroup(int device) {
- if (mUseFixedVolume) {
+ // No need to persist the index if the volume group is backed up
+ // by a public stream type as this is redundant
+ if (mUseFixedVolume || mHasValidStreamType) {
return;
}
if (DEBUG_VOL) {
Log.v(TAG, "persistVolumeGroup: storing index " + getIndex(device) + " for group "
+ mAudioVolumeGroup.name()
+ ", device " + AudioSystem.getOutputDeviceName(device)
- + " and User=" + getCurrentUserId());
+ + " and User=" + getCurrentUserId()
+ + " mSettingName: " + mSettingName);
}
+
boolean success = mSettings.putSystemIntForUser(mContentResolver,
getSettingNameForDevice(device),
getIndex(device),
@@ -8100,6 +8104,14 @@
return mSettingName + "_" + AudioSystem.getOutputDeviceName(device);
}
+ void setSettingName(String settingName) {
+ mSettingName = settingName;
+ }
+
+ String getSettingName() {
+ return mSettingName;
+ }
+
private void dump(PrintWriter pw) {
pw.println("- VOLUME GROUP " + mAudioVolumeGroup.name() + ":");
pw.print(" Muted: ");
@@ -8242,6 +8254,9 @@
*/
public void setVolumeGroupState(VolumeGroupState volumeGroupState) {
mVolumeGroupState = volumeGroupState;
+ if (mVolumeGroupState != null) {
+ mVolumeGroupState.setSettingName(mVolumeIndexSettingName);
+ }
}
/**
* Update the minimum index that can be used without MODIFY_AUDIO_SETTINGS permission
@@ -8315,6 +8330,17 @@
return (mVolumeIndexSettingName != null && !mVolumeIndexSettingName.isEmpty());
}
+ void setSettingName(String settingName) {
+ mVolumeIndexSettingName = settingName;
+ if (mVolumeGroupState != null) {
+ mVolumeGroupState.setSettingName(mVolumeIndexSettingName);
+ }
+ }
+
+ String getSettingName() {
+ return mVolumeIndexSettingName;
+ }
+
public void readSettings() {
synchronized (mSettingsLock) {
synchronized (VolumeStreamState.class) {
@@ -8989,7 +9015,7 @@
if (streamState.hasValidSettingsName()) {
mSettings.putSystemIntForUser(mContentResolver,
streamState.getSettingNameForDevice(device),
- (streamState.getIndex(device) + 5)/ 10,
+ (streamState.getIndex(device) + 5) / 10,
UserHandle.USER_CURRENT);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthResultCoordinator.java b/services/core/java/com/android/server/biometrics/sensors/AuthResultCoordinator.java
index a48a9d1..cd2a26f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthResultCoordinator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthResultCoordinator.java
@@ -37,13 +37,17 @@
*/
static final int AUTHENTICATOR_DEFAULT = 0;
/**
- * Indicated this authenticator has received a lockout.
+ * Indicated this authenticator has received a permanent lockout.
*/
- static final int AUTHENTICATOR_LOCKED = 1 << 0;
+ static final int AUTHENTICATOR_PERMANENT_LOCKED = 1 << 0;
+ /**
+ * Indicates this authenticator has received a timed unlock.
+ */
+ static final int AUTHENTICATOR_TIMED_LOCKED = 1 << 1;
/**
* Indicates this authenticator has received a successful unlock.
*/
- static final int AUTHENTICATOR_UNLOCKED = 1 << 1;
+ static final int AUTHENTICATOR_UNLOCKED = 1 << 2;
private static final String TAG = "AuthResultCoordinator";
private final Map<Integer, Integer> mAuthenticatorState;
@@ -85,7 +89,14 @@
* Adds a lock out of a given strength to the current operation list.
*/
void lockedOutFor(@Authenticators.Types int strength) {
- updateState(strength, (old) -> AUTHENTICATOR_LOCKED | old);
+ updateState(strength, (old) -> AUTHENTICATOR_PERMANENT_LOCKED | old);
+ }
+
+ /**
+ * Adds a timed lock out of a given strength to the current operation list.
+ */
+ void lockOutTimed(@Authenticators.Types int strength) {
+ updateState(strength, (old) -> AUTHENTICATOR_TIMED_LOCKED | old);
}
/**
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java b/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java
index 1aee5d4..2653ce7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java
@@ -16,21 +16,19 @@
package com.android.server.biometrics.sensors;
-import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_LOCKED;
+import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_PERMANENT_LOCKED;
+import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_TIMED_LOCKED;
import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_UNLOCKED;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.os.SystemClock;
-import android.util.Pair;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import java.time.Clock;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
-import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -45,9 +43,7 @@
private final Set<Integer> mAuthOperations;
private final MultiBiometricLockoutState mMultiBiometricLockoutState;
- private final List<Pair<Integer, Long>> mTimedLockouts;
private final RingBuffer mRingBuffer;
- private final Clock mClock;
private int mUserId;
private boolean mIsAuthenticating;
@@ -63,8 +59,6 @@
mAuthResultCoordinator = new AuthResultCoordinator();
mMultiBiometricLockoutState = new MultiBiometricLockoutState(clock);
mRingBuffer = new RingBuffer(100);
- mTimedLockouts = new ArrayList<>();
- mClock = clock;
}
/**
@@ -74,8 +68,6 @@
mAuthOperations.clear();
mUserId = userId;
mIsAuthenticating = true;
- mAuthOperations.clear();
- mTimedLockouts.clear();
mAuthResultCoordinator = new AuthResultCoordinator();
mRingBuffer.addApiCall("internal : onAuthSessionStarted(" + userId + ")");
}
@@ -85,39 +77,30 @@
*
* This can happen two ways.
* 1. Manually calling this API
- * 2. If authStartedFor() was called, and all authentication attempts finish.
+ * 2. If authStartedFor() was called, and any authentication attempts finish.
*/
void endAuthSession() {
- if (mIsAuthenticating) {
- final long currentTime = mClock.millis();
- for (Pair<Integer, Long> timedLockouts : mTimedLockouts) {
- mMultiBiometricLockoutState.increaseLockoutTime(mUserId, timedLockouts.first,
- timedLockouts.second + currentTime);
+ // User unlocks can also unlock timed lockout Authenticator.Types
+ final Map<Integer, Integer> result = mAuthResultCoordinator.getResult();
+ for (int authenticator : Arrays.asList(Authenticators.BIOMETRIC_CONVENIENCE,
+ Authenticators.BIOMETRIC_WEAK, Authenticators.BIOMETRIC_STRONG)) {
+ final Integer value = result.get(authenticator);
+ if ((value & AUTHENTICATOR_UNLOCKED) == AUTHENTICATOR_UNLOCKED) {
+ mMultiBiometricLockoutState.clearPermanentLockOut(mUserId, authenticator);
+ mMultiBiometricLockoutState.clearTimedLockout(mUserId, authenticator);
+ } else if ((value & AUTHENTICATOR_PERMANENT_LOCKED) == AUTHENTICATOR_PERMANENT_LOCKED) {
+ mMultiBiometricLockoutState.setPermanentLockOut(mUserId, authenticator);
+ } else if ((value & AUTHENTICATOR_TIMED_LOCKED) == AUTHENTICATOR_TIMED_LOCKED) {
+ mMultiBiometricLockoutState.setTimedLockout(mUserId, authenticator);
}
- // User unlocks can also unlock timed lockout Authenticator.Types
- final Map<Integer, Integer> result = mAuthResultCoordinator.getResult();
- for (int authenticator : Arrays.asList(Authenticators.BIOMETRIC_CONVENIENCE,
- Authenticators.BIOMETRIC_WEAK, Authenticators.BIOMETRIC_STRONG)) {
- final Integer value = result.get(authenticator);
- if ((value & AUTHENTICATOR_UNLOCKED) == AUTHENTICATOR_UNLOCKED) {
- mMultiBiometricLockoutState.setAuthenticatorTo(mUserId, authenticator,
- true /* canAuthenticate */);
- mMultiBiometricLockoutState.clearLockoutTime(mUserId, authenticator);
- } else if ((value & AUTHENTICATOR_LOCKED) == AUTHENTICATOR_LOCKED) {
- mMultiBiometricLockoutState.setAuthenticatorTo(mUserId, authenticator,
- false /* canAuthenticate */);
- }
-
- }
-
- mRingBuffer.addApiCall("internal : onAuthSessionEnded(" + mUserId + ")");
- clearSession();
}
+
+ mRingBuffer.addApiCall("internal : onAuthSessionEnded(" + mUserId + ")");
+ clearSession();
}
private void clearSession() {
mIsAuthenticating = false;
- mTimedLockouts.clear();
mAuthOperations.clear();
}
@@ -171,7 +154,7 @@
+ ", sensorId=" + sensorId + "time=" + time + ", requestId=" + requestId
+ ")";
mRingBuffer.addApiCall(lockedOutStr);
- mTimedLockouts.add(new Pair<>(biometricStrength, time));
+ mAuthResultCoordinator.lockOutTimed(biometricStrength);
attemptToFinish(userId, sensorId, lockedOutStr);
}
@@ -202,9 +185,8 @@
// Lockouts cannot be reset by non-strong biometrics
return;
}
- mMultiBiometricLockoutState.setAuthenticatorTo(userId, biometricStrength,
- true /*canAuthenticate */);
- mMultiBiometricLockoutState.clearLockoutTime(userId, biometricStrength);
+ mMultiBiometricLockoutState.clearPermanentLockOut(userId, biometricStrength);
+ mMultiBiometricLockoutState.clearTimedLockout(userId, biometricStrength);
}
private void attemptToFinish(int userId, int sensorId, String description) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java b/services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java
index c24a989..45933fe 100644
--- a/services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java
+++ b/services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java
@@ -50,10 +50,11 @@
private Map<Integer, AuthenticatorState> createUnlockedMap() {
Map<Integer, AuthenticatorState> lockOutMap = new HashMap<>();
lockOutMap.put(BIOMETRIC_STRONG,
- new AuthenticatorState(BIOMETRIC_STRONG, false, 0, mClock));
- lockOutMap.put(BIOMETRIC_WEAK, new AuthenticatorState(BIOMETRIC_WEAK, false, 0, mClock));
+ new AuthenticatorState(BIOMETRIC_STRONG, false, false));
+ lockOutMap.put(BIOMETRIC_WEAK,
+ new AuthenticatorState(BIOMETRIC_WEAK, false, false));
lockOutMap.put(BIOMETRIC_CONVENIENCE,
- new AuthenticatorState(BIOMETRIC_CONVENIENCE, false, 0, mClock));
+ new AuthenticatorState(BIOMETRIC_CONVENIENCE, false, false));
return lockOutMap;
}
@@ -64,54 +65,71 @@
return mCanUserAuthenticate.get(userId);
}
- void setAuthenticatorTo(int userId, @Authenticators.Types int strength, boolean canAuth) {
+ void setPermanentLockOut(int userId, @Authenticators.Types int strength) {
final Map<Integer, AuthenticatorState> authMap = getAuthMapForUser(userId);
switch (strength) {
case Authenticators.BIOMETRIC_STRONG:
- authMap.get(BIOMETRIC_STRONG).mPermanentlyLockedOut = !canAuth;
+ authMap.get(BIOMETRIC_STRONG).mPermanentlyLockedOut = true;
// fall through
case Authenticators.BIOMETRIC_WEAK:
- authMap.get(BIOMETRIC_WEAK).mPermanentlyLockedOut = !canAuth;
+ authMap.get(BIOMETRIC_WEAK).mPermanentlyLockedOut = true;
// fall through
case Authenticators.BIOMETRIC_CONVENIENCE:
- authMap.get(BIOMETRIC_CONVENIENCE).mPermanentlyLockedOut = !canAuth;
+ authMap.get(BIOMETRIC_CONVENIENCE).mPermanentlyLockedOut = true;
return;
default:
Slog.e(TAG, "increaseLockoutTime called for invalid strength : " + strength);
}
}
- void increaseLockoutTime(int userId, @Authenticators.Types int strength, long duration) {
+ void clearPermanentLockOut(int userId, @Authenticators.Types int strength) {
final Map<Integer, AuthenticatorState> authMap = getAuthMapForUser(userId);
switch (strength) {
case Authenticators.BIOMETRIC_STRONG:
- authMap.get(BIOMETRIC_STRONG).increaseLockoutTo(duration);
+ authMap.get(BIOMETRIC_STRONG).mPermanentlyLockedOut = false;
// fall through
case Authenticators.BIOMETRIC_WEAK:
- authMap.get(BIOMETRIC_WEAK).increaseLockoutTo(duration);
+ authMap.get(BIOMETRIC_WEAK).mPermanentlyLockedOut = false;
// fall through
case Authenticators.BIOMETRIC_CONVENIENCE:
- authMap.get(BIOMETRIC_CONVENIENCE).increaseLockoutTo(duration);
+ authMap.get(BIOMETRIC_CONVENIENCE).mPermanentlyLockedOut = false;
return;
default:
Slog.e(TAG, "increaseLockoutTime called for invalid strength : " + strength);
}
}
- void clearLockoutTime(int userId, @Authenticators.Types int strength) {
+ void setTimedLockout(int userId, @Authenticators.Types int strength) {
final Map<Integer, AuthenticatorState> authMap = getAuthMapForUser(userId);
switch (strength) {
case Authenticators.BIOMETRIC_STRONG:
- authMap.get(BIOMETRIC_STRONG).setTimedLockout(0);
+ authMap.get(BIOMETRIC_STRONG).mTimedLockout = true;
// fall through
case Authenticators.BIOMETRIC_WEAK:
- authMap.get(BIOMETRIC_WEAK).setTimedLockout(0);
+ authMap.get(BIOMETRIC_WEAK).mTimedLockout = true;
// fall through
case Authenticators.BIOMETRIC_CONVENIENCE:
- authMap.get(BIOMETRIC_CONVENIENCE).setTimedLockout(0);
+ authMap.get(BIOMETRIC_CONVENIENCE).mTimedLockout = true;
return;
default:
- Slog.e(TAG, "clearLockoutTime called for invalid strength : " + strength);
+ Slog.e(TAG, "increaseLockoutTime called for invalid strength : " + strength);
+ }
+ }
+
+ void clearTimedLockout(int userId, @Authenticators.Types int strength) {
+ final Map<Integer, AuthenticatorState> authMap = getAuthMapForUser(userId);
+ switch (strength) {
+ case Authenticators.BIOMETRIC_STRONG:
+ authMap.get(BIOMETRIC_STRONG).mTimedLockout = false;
+ // fall through
+ case Authenticators.BIOMETRIC_WEAK:
+ authMap.get(BIOMETRIC_WEAK).mTimedLockout = false;
+ // fall through
+ case Authenticators.BIOMETRIC_CONVENIENCE:
+ authMap.get(BIOMETRIC_CONVENIENCE).mTimedLockout = false;
+ return;
+ default:
+ Slog.e(TAG, "increaseLockoutTime called for invalid strength : " + strength);
}
}
@@ -132,7 +150,7 @@
final AuthenticatorState state = authMap.get(strength);
if (state.mPermanentlyLockedOut) {
return LockoutTracker.LOCKOUT_PERMANENT;
- } else if (state.isTimedLockout()) {
+ } else if (state.mTimedLockout) {
return LockoutTracker.LOCKOUT_TIMED;
} else {
return LockoutTracker.LOCKOUT_NONE;
@@ -158,43 +176,21 @@
private static class AuthenticatorState {
private Integer mAuthenticatorType;
private boolean mPermanentlyLockedOut;
- private long mTimedLockout;
- private Clock mClock;
+ private boolean mTimedLockout;
AuthenticatorState(Integer authenticatorId, boolean permanentlyLockedOut,
- long timedLockout, Clock clock) {
+ boolean timedLockout) {
mAuthenticatorType = authenticatorId;
mPermanentlyLockedOut = permanentlyLockedOut;
mTimedLockout = timedLockout;
- mClock = clock;
- }
-
- boolean canAuthenticate() {
- return !mPermanentlyLockedOut && !isTimedLockout();
- }
-
- boolean isTimedLockout() {
- return mClock.millis() - mTimedLockout < 0;
- }
-
- void setTimedLockout(long duration) {
- mTimedLockout = duration;
- }
-
- /**
- * Either increases the lockout to duration, or leaves it as it, whichever is longer.
- */
- void increaseLockoutTo(long duration) {
- mTimedLockout = Math.max(mTimedLockout, duration);
}
String toString(long currentTime) {
- final String duration =
- mTimedLockout - currentTime > 0 ? (mTimedLockout - currentTime) + "ms" : "none";
+ final String timedLockout = mTimedLockout ? "true" : "false";
final String permanentLockout = mPermanentlyLockedOut ? "true" : "false";
- return String.format("(%s, permanentLockout=%s, timedLockoutRemaining=%s)",
+ return String.format("(%s, permanentLockout=%s, timedLockout=%s)",
BiometricManager.authenticatorToStr(mAuthenticatorType), permanentLockout,
- duration);
+ timedLockout);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index 759c52a..1a12fcd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -28,6 +28,7 @@
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -88,10 +89,9 @@
void onLockoutCleared() {
resetLocalLockoutStateToNone(getSensorId(), getTargetUserId(), mLockoutCache,
- mLockoutResetDispatcher);
+ mLockoutResetDispatcher, getBiometricContext().getAuthSessionCoordinator(),
+ mBiometricStrength, getRequestId());
mCallback.onClientFinished(this, true /* success */);
- getBiometricContext().getAuthSessionCoordinator()
- .resetLockoutFor(getTargetUserId(), mBiometricStrength, getRequestId());
}
public boolean interruptsPrecedingClients() {
@@ -108,7 +108,10 @@
*/
static void resetLocalLockoutStateToNone(int sensorId, int userId,
@NonNull LockoutCache lockoutTracker,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull AuthSessionCoordinator authSessionCoordinator,
+ @Authenticators.Types int biometricStrength, long requestId) {
+ authSessionCoordinator.resetLockoutFor(userId, biometricStrength, requestId);
lockoutTracker.setLockoutModeForUser(userId, LockoutTracker.LOCKOUT_NONE);
lockoutResetDispatcher.notifyLockoutResetCallbacks(sensorId);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index 0d30ddd..468bf55 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -54,6 +54,7 @@
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -127,6 +128,9 @@
private final LockoutCache mLockoutCache;
@NonNull
private final LockoutResetDispatcher mLockoutResetDispatcher;
+
+ @NonNull
+ private AuthSessionCoordinator mAuthSessionCoordinator;
@NonNull
private final Callback mCallback;
@@ -134,6 +138,7 @@
@NonNull UserAwareBiometricScheduler scheduler, int sensorId, int userId,
@NonNull LockoutCache lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull AuthSessionCoordinator authSessionCoordinator,
@NonNull Callback callback) {
mContext = context;
mHandler = handler;
@@ -143,6 +148,7 @@
mUserId = userId;
mLockoutCache = lockoutTracker;
mLockoutResetDispatcher = lockoutResetDispatcher;
+ mAuthSessionCoordinator = authSessionCoordinator;
mCallback = callback;
}
@@ -346,8 +352,12 @@
final BaseClientMonitor client = mScheduler.getCurrentClient();
if (!(client instanceof FaceResetLockoutClient)) {
Slog.d(mTag, "onLockoutCleared outside of resetLockout by HAL");
+ // Given that onLockoutCleared() can happen at any time, and is not necessarily
+ // coming from a specific client, set this to -1 to indicate it wasn't for a
+ // specific request.
FaceResetLockoutClient.resetLocalLockoutStateToNone(mSensorId, mUserId,
- mLockoutCache, mLockoutResetDispatcher);
+ mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
+ Utils.getCurrentStrength(mSensorId), -1 /* requestId */);
} else {
Slog.d(mTag, "onLockoutCleared after resetLockout");
final FaceResetLockoutClient resetLockoutClient =
@@ -514,7 +524,8 @@
final HalSessionCallback resultController = new HalSessionCallback(mContext,
mHandler, mTag, mScheduler, sensorId, newUserId, mLockoutCache,
- lockoutResetDispatcher, () -> {
+ lockoutResetDispatcher,
+ biometricContext.getAuthSessionCoordinator(), () -> {
Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
mCurrentSession = null;
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
index 0b2421b..7a62034 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -28,6 +28,7 @@
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ErrorConsumer;
import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -92,10 +93,9 @@
void onLockoutCleared() {
resetLocalLockoutStateToNone(getSensorId(), getTargetUserId(), mLockoutCache,
- mLockoutResetDispatcher);
+ mLockoutResetDispatcher, getBiometricContext().getAuthSessionCoordinator(),
+ mBiometricStrength, getRequestId());
mCallback.onClientFinished(this, true /* success */);
- getBiometricContext().getAuthSessionCoordinator()
- .resetLockoutFor(getTargetUserId(), mBiometricStrength, getRequestId());
}
/**
@@ -108,9 +108,12 @@
*/
static void resetLocalLockoutStateToNone(int sensorId, int userId,
@NonNull LockoutCache lockoutTracker,
- @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull AuthSessionCoordinator authSessionCoordinator,
+ @Authenticators.Types int biometricStrength, long requestId) {
lockoutTracker.setLockoutModeForUser(userId, LockoutTracker.LOCKOUT_NONE);
lockoutResetDispatcher.notifyLockoutResetCallbacks(sensorId);
+ authSessionCoordinator.resetLockoutFor(userId, biometricStrength, requestId);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index 1dcf4e9..22ca816 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -52,6 +52,7 @@
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -131,12 +132,15 @@
@NonNull
private final LockoutResetDispatcher mLockoutResetDispatcher;
@NonNull
+ private AuthSessionCoordinator mAuthSessionCoordinator;
+ @NonNull
private final Callback mCallback;
HalSessionCallback(@NonNull Context context, @NonNull Handler handler, @NonNull String tag,
@NonNull UserAwareBiometricScheduler scheduler, int sensorId, int userId,
@NonNull LockoutCache lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull AuthSessionCoordinator authSessionCoordinator,
@NonNull Callback callback) {
mContext = context;
mHandler = handler;
@@ -146,6 +150,7 @@
mUserId = userId;
mLockoutCache = lockoutTracker;
mLockoutResetDispatcher = lockoutResetDispatcher;
+ mAuthSessionCoordinator = authSessionCoordinator;
mCallback = callback;
}
@@ -327,8 +332,12 @@
final BaseClientMonitor client = mScheduler.getCurrentClient();
if (!(client instanceof FingerprintResetLockoutClient)) {
Slog.d(mTag, "onLockoutCleared outside of resetLockout by HAL");
+ // Given that onLockoutCleared() can happen at any time, and is not necessarily
+ // coming from a specific client, set this to -1 to indicate it wasn't for a
+ // specific request.
FingerprintResetLockoutClient.resetLocalLockoutStateToNone(mSensorId, mUserId,
- mLockoutCache, mLockoutResetDispatcher);
+ mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
+ Utils.getCurrentStrength(mSensorId), -1 /* requestId */);
} else {
Slog.d(mTag, "onLockoutCleared after resetLockout");
final FingerprintResetLockoutClient resetLockoutClient =
@@ -470,7 +479,8 @@
final HalSessionCallback resultController = new HalSessionCallback(mContext,
mHandler, mTag, mScheduler, sensorId, newUserId, mLockoutCache,
- lockoutResetDispatcher, () -> {
+ lockoutResetDispatcher,
+ biometricContext.getAuthSessionCoordinator(), () -> {
Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
mCurrentSession = null;
});
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 1f4c7e6..1dc2725 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -152,6 +152,7 @@
import com.android.internal.net.VpnProfile;
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.BinderUtils;
+import com.android.net.module.util.LinkPropertiesUtils;
import com.android.net.module.util.NetdUtils;
import com.android.net.module.util.NetworkStackConstants;
import com.android.server.DeviceIdleInternal;
@@ -230,7 +231,35 @@
* <p>If retries have exceeded the length of this array, the last entry in the array will be
* used as a repeating interval.
*/
- private static final long[] IKEV2_VPN_RETRY_DELAYS_SEC = {1L, 2L, 5L, 30L, 60L, 300L, 900L};
+ private static final long[] IKEV2_VPN_RETRY_DELAYS_MS =
+ {1_000L, 2_000L, 5_000L, 30_000L, 60_000L, 300_000L, 900_000L};
+
+ /**
+ * A constant to pass to {@link IkeV2VpnRunner#scheduleStartIkeSession(long)} to mean the
+ * delay should be computed automatically with backoff.
+ */
+ private static final long RETRY_DELAY_AUTO_BACKOFF = -1;
+
+ /**
+ * How long to wait before trying to migrate the IKE connection when NetworkCapabilities or
+ * LinkProperties change in a way that may require migration.
+ *
+ * This delay is useful to avoid multiple migration tries (e.g. when a network changes
+ * both its NC and LP at the same time, e.g. when it first connects) and to minimize the
+ * cases where an old list of addresses is detected for the network.
+ *
+ * In practice, the IKE library reads the LinkProperties of the passed network with
+ * the synchronous {@link ConnectivityManager#getLinkProperties(Network)}, which means in
+ * most cases the race would resolve correctly, but this delay increases the chance that
+ * it correctly is.
+ * Further, using the synchronous method in the IKE library is actually dangerous because
+ * it is racy (it races with {@code IkeNetworkCallbackBase#onLost} and it should be fixed
+ * by using callbacks instead. When that happens, the race within IKE is fixed but the
+ * race between that callback and the one in IkeV2VpnRunner becomes a much bigger problem,
+ * and this delay will be necessary to ensure the correct link address list is used.
+ */
+ private static final long IKE_DELAY_ON_NC_LP_CHANGE_MS = 300;
+
/**
* Largest profile size allowable for Platform VPNs.
*
@@ -619,14 +648,14 @@
/**
* Retrieves the next retry delay
*
- * <p>If retries have exceeded the IKEV2_VPN_RETRY_DELAYS_SEC, the last entry in
+ * <p>If retries have exceeded the size of IKEV2_VPN_RETRY_DELAYS_MS, the last entry in
* the array will be used as a repeating interval.
*/
- public long getNextRetryDelaySeconds(int retryCount) {
- if (retryCount >= IKEV2_VPN_RETRY_DELAYS_SEC.length) {
- return IKEV2_VPN_RETRY_DELAYS_SEC[IKEV2_VPN_RETRY_DELAYS_SEC.length - 1];
+ public long getNextRetryDelayMs(int retryCount) {
+ if (retryCount >= IKEV2_VPN_RETRY_DELAYS_MS.length) {
+ return IKEV2_VPN_RETRY_DELAYS_MS[IKEV2_VPN_RETRY_DELAYS_MS.length - 1];
} else {
- return IKEV2_VPN_RETRY_DELAYS_SEC[retryCount];
+ return IKEV2_VPN_RETRY_DELAYS_MS[retryCount];
}
}
@@ -679,6 +708,14 @@
boolean isIpv4) {
return MtuUtils.getMtu(childProposals, maxMtu, underlyingMtu, isIpv4);
}
+
+ /** Verify the binder calling UID is the one passed in arguments */
+ public void verifyCallingUidAndPackage(Context context, String packageName, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ if (getAppUid(context, packageName, userId) != callingUid) {
+ throw new SecurityException(packageName + " does not belong to uid " + callingUid);
+ }
+ }
}
@VisibleForTesting
@@ -726,7 +763,7 @@
mUserManager = mContext.getSystemService(UserManager.class);
mPackage = VpnConfig.LEGACY_VPN;
- mOwnerUID = getAppUid(mPackage, mUserId);
+ mOwnerUID = getAppUid(mContext, mPackage, mUserId);
mIsPackageTargetingAtLeastQ = doesPackageTargetAtLeastQ(mPackage);
try {
@@ -823,7 +860,7 @@
}
/**
- * Chooses whether to force all connections to go though VPN.
+ * Chooses whether to force all connections to go through VPN.
*
* Used to enable/disable legacy VPN lockdown.
*
@@ -831,7 +868,7 @@
* {@link #setAlwaysOnPackage(String, boolean, List<String>)}; previous settings from calling
* that function will be replaced and saved with the always-on state.
*
- * @param lockdown whether to prevent all traffic outside of a VPN.
+ * @param lockdown whether to prevent all traffic outside of the VPN.
*/
public synchronized void setLockdown(boolean lockdown) {
enforceControlPermissionOrInternalCaller();
@@ -1108,6 +1145,7 @@
mAlwaysOn = false;
}
+ final boolean oldLockdownState = mLockdown;
mLockdown = (mAlwaysOn && lockdown);
mLockdownAllowlist = (mLockdown && lockdownAllowlist != null)
? Collections.unmodifiableList(new ArrayList<>(lockdownAllowlist))
@@ -1118,6 +1156,13 @@
if (isCurrentPreparedPackage(packageName)) {
updateAlwaysOnNotification(mNetworkInfo.getDetailedState());
setVpnForcedLocked(mLockdown);
+
+ // Lockdown forces the VPN to be non-bypassable (see #agentConnect) because it makes
+ // no sense for a VPN to be bypassable when connected but not when not connected.
+ // As such, changes in lockdown need to restart the agent.
+ if (mNetworkAgent != null && oldLockdownState != mLockdown) {
+ startNewNetworkAgent(mNetworkAgent, "Lockdown mode changed");
+ }
} else {
// Prepare this app. The notification will update as a side-effect of updateState().
// It also calls setVpnForcedLocked().
@@ -1355,7 +1400,8 @@
// We can't just check that packageName matches mPackage, because if the app was uninstalled
// and reinstalled it will no longer be prepared. Similarly if there is a shared UID, the
// calling package may not be the same as the prepared package. Check both UID and package.
- return getAppUid(packageName, mUserId) == mOwnerUID && mPackage.equals(packageName);
+ return getAppUid(mContext, packageName, mUserId) == mOwnerUID
+ && mPackage.equals(packageName);
}
/** Prepare the VPN for the given package. Does not perform permission checks. */
@@ -1396,7 +1442,7 @@
Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
mPackage = newPackage;
- mOwnerUID = getAppUid(newPackage, mUserId);
+ mOwnerUID = getAppUid(mContext, newPackage, mUserId);
mIsPackageTargetingAtLeastQ = doesPackageTargetAtLeastQ(newPackage);
try {
mNms.allowProtect(mOwnerUID);
@@ -1417,7 +1463,7 @@
// Check if the caller is authorized.
enforceControlPermissionOrInternalCaller();
- final int uid = getAppUid(packageName, mUserId);
+ final int uid = getAppUid(mContext, packageName, mUserId);
if (uid == -1 || VpnConfig.LEGACY_VPN.equals(packageName)) {
// Authorization for nonexistent packages (or fake ones) can't be updated.
return false;
@@ -1497,11 +1543,11 @@
|| isVpnServicePreConsented(context, packageName);
}
- private int getAppUid(final String app, final int userId) {
+ private static int getAppUid(final Context context, final String app, final int userId) {
if (VpnConfig.LEGACY_VPN.equals(app)) {
return Process.myUid();
}
- PackageManager pm = mContext.getPackageManager();
+ PackageManager pm = context.getPackageManager();
final long token = Binder.clearCallingIdentity();
try {
return pm.getPackageUidAsUser(app, userId);
@@ -1630,6 +1676,10 @@
*/
private boolean updateLinkPropertiesInPlaceIfPossible(NetworkAgent agent, VpnConfig oldConfig) {
// NetworkAgentConfig cannot be updated without registering a new NetworkAgent.
+ // Strictly speaking, bypassability is affected by lockdown and therefore it's possible
+ // it doesn't actually change even if mConfig.allowBypass changed. It might be theoretically
+ // possible to do handover in this case, but this is far from obvious to VPN authors and
+ // it's simpler if the rule is just "can't update in place if you change allow bypass".
if (oldConfig.allowBypass != mConfig.allowBypass) {
Log.i(TAG, "Handover not possible due to changes to allowBypass");
return false;
@@ -1671,10 +1721,11 @@
mLegacyState = LegacyVpnInfo.STATE_CONNECTING;
updateState(DetailedState.CONNECTING, "agentConnect");
+ final boolean bypassable = mConfig.allowBypass && !mLockdown;
final NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig.Builder()
.setLegacyType(ConnectivityManager.TYPE_VPN)
.setLegacyTypeName("VPN")
- .setBypassableVpn(mConfig.allowBypass && !mLockdown)
+ .setBypassableVpn(bypassable)
.setVpnRequiresValidation(mConfig.requiresInternetValidation)
.setLocalRoutesExcludedForVpn(mConfig.excludeLocalRoutes)
.build();
@@ -1688,7 +1739,7 @@
capsBuilder.setTransportInfo(new VpnTransportInfo(
getActiveVpnType(),
mConfig.session,
- mConfig.allowBypass,
+ bypassable,
expensive));
// Only apps targeting Q and above can explicitly declare themselves as metered.
@@ -1719,6 +1770,10 @@
Binder.restoreCallingIdentity(token);
}
updateState(DetailedState.CONNECTED, "agentConnect");
+ if (isIkev2VpnRunner()) {
+ final IkeSessionWrapper session = ((IkeV2VpnRunner) mVpnRunner).mSession;
+ if (null != session) session.setUnderpinnedNetwork(mNetworkAgent.getNetwork());
+ }
}
private static boolean areLongLivedTcpConnectionsExpensive(@NonNull VpnRunner runner) {
@@ -1913,7 +1968,7 @@
private SortedSet<Integer> getAppsUids(List<String> packageNames, int userId) {
SortedSet<Integer> uids = new TreeSet<>();
for (String app : packageNames) {
- int uid = getAppUid(app, userId);
+ int uid = getAppUid(mContext, app, userId);
if (uid != -1) uids.add(uid);
// TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
// ConnectivityServiceTest.
@@ -3232,7 +3287,6 @@
prepareStatusIntent();
}
agentConnect(this::onValidationStatus);
- mSession.setUnderpinnedNetwork(mNetworkAgent.getNetwork());
return; // Link properties are already sent.
} else {
// Underlying networks also set in agentConnect()
@@ -3349,7 +3403,6 @@
if (!removedAddrs.isEmpty()) {
startNewNetworkAgent(
mNetworkAgent, "MTU too low for IPv6; restarting network agent");
- mSession.setUnderpinnedNetwork(mNetworkAgent.getNetwork());
for (LinkAddress removed : removedAddrs) {
mTunnelIface.removeAddress(
@@ -3628,7 +3681,7 @@
final VpnTransportInfo info = new VpnTransportInfo(
getActiveVpnType(),
mConfig.session,
- mConfig.allowBypass,
+ mConfig.allowBypass && !mLockdown,
areLongLivedTcpConnectionsExpensive(keepaliveDelaySec));
final boolean ncUpdateRequired = !info.equals(mNetworkCapabilities.getTransportInfo());
if (ncUpdateRequired) {
@@ -3718,13 +3771,20 @@
}
}
- private void scheduleRetryNewIkeSession() {
+ /**
+ * Schedule starting an IKE session.
+ * @param delayMs the delay after which to try starting the session. This should be
+ * RETRY_DELAY_AUTO_BACKOFF for automatic retries with backoff.
+ */
+ private void scheduleStartIkeSession(final long delayMs) {
if (mScheduledHandleRetryIkeSessionFuture != null) {
Log.d(TAG, "There is a pending retrying task, skip the new retrying task");
return;
}
- final long retryDelay = mDeps.getNextRetryDelaySeconds(mRetryCount++);
- Log.d(TAG, "Retry new IKE session after " + retryDelay + " seconds.");
+ final long retryDelayMs = RETRY_DELAY_AUTO_BACKOFF != delayMs
+ ? delayMs
+ : mDeps.getNextRetryDelayMs(mRetryCount++);
+ Log.d(TAG, "Retry new IKE session after " + retryDelayMs + " milliseconds.");
// If the default network is lost during the retry delay, the mActiveNetwork will be
// null, and the new IKE session won't be established until there is a new default
// network bringing up.
@@ -3735,7 +3795,7 @@
// Reset mScheduledHandleRetryIkeSessionFuture since it's already run on
// executor thread.
mScheduledHandleRetryIkeSessionFuture = null;
- }, retryDelay, TimeUnit.SECONDS);
+ }, retryDelayMs, TimeUnit.MILLISECONDS);
}
/** Called when the NetworkCapabilities of underlying network is changed */
@@ -3747,15 +3807,23 @@
if (oldNc == null || !nc.getSubscriptionIds().equals(oldNc.getSubscriptionIds())) {
// A new default network is available, or the subscription has changed.
// Try to migrate the session, or failing that, start a new one.
- startOrMigrateIkeSession(mActiveNetwork);
+ scheduleStartIkeSession(IKE_DELAY_ON_NC_LP_CHANGE_MS);
}
}
/** Called when the LinkProperties of underlying network is changed */
public void onDefaultNetworkLinkPropertiesChanged(@NonNull LinkProperties lp) {
- mEventChanges.log("[UnderlyingNW] Lp changed from "
- + mUnderlyingLinkProperties + " to " + lp);
+ final LinkProperties oldLp = mUnderlyingLinkProperties;
+ mEventChanges.log("[UnderlyingNW] Lp changed from " + oldLp + " to " + lp);
mUnderlyingLinkProperties = lp;
+ if (oldLp == null || !LinkPropertiesUtils.isIdenticalAllLinkAddresses(oldLp, lp)) {
+ // If some of the link addresses changed, the IKE session may need to be migrated
+ // or restarted, for example if the available IP families have changed or if the
+ // source address used has gone away. See IkeConnectionController#onNetworkSetByUser
+ // and IkeConnectionController#selectAndSetRemoteAddress for where this ends up
+ // re-evaluating the session.
+ scheduleStartIkeSession(IKE_DELAY_ON_NC_LP_CHANGE_MS);
+ }
}
class VpnConnectivityDiagnosticsCallback
@@ -4033,7 +4101,7 @@
markFailedAndDisconnect(exception);
return;
} else {
- scheduleRetryNewIkeSession();
+ scheduleStartIkeSession(RETRY_DELAY_AUTO_BACKOFF);
}
// Close all obsolete state, but keep VPN alive incase a usable network comes up.
@@ -4470,10 +4538,7 @@
}
private void verifyCallingUidAndPackage(String packageName) {
- final int callingUid = Binder.getCallingUid();
- if (getAppUid(packageName, mUserId) != callingUid) {
- throw new SecurityException(packageName + " does not belong to uid " + callingUid);
- }
+ mDeps.verifyCallingUidAndPackage(mContext, packageName, mUserId);
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java b/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java
index ab261ac..f4c84e7 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java
@@ -37,8 +37,11 @@
import android.util.SparseArray;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import java.util.Locale;
+
/**
* Manages the user-visible device state notifications.
*/
@@ -56,15 +59,14 @@
private final NotificationManager mNotificationManager;
private final PackageManager mPackageManager;
- // Stores the notification title and content indexed with the device state identifier.
- private final SparseArray<NotificationInfo> mNotificationInfos;
-
// The callback when a device state is requested to be canceled.
private final Runnable mCancelStateRunnable;
+ private final NotificationInfoProvider mNotificationInfoProvider;
+
DeviceStateNotificationController(@NonNull Context context, @NonNull Handler handler,
@NonNull Runnable cancelStateRunnable) {
- this(context, handler, cancelStateRunnable, getNotificationInfos(context),
+ this(context, handler, cancelStateRunnable, new NotificationInfoProvider(context),
context.getPackageManager(), context.getSystemService(NotificationManager.class));
}
@@ -72,13 +74,13 @@
DeviceStateNotificationController(
@NonNull Context context, @NonNull Handler handler,
@NonNull Runnable cancelStateRunnable,
- @NonNull SparseArray<NotificationInfo> notificationInfos,
+ @NonNull NotificationInfoProvider notificationInfoProvider,
@NonNull PackageManager packageManager,
@NonNull NotificationManager notificationManager) {
mContext = context;
mHandler = handler;
mCancelStateRunnable = cancelStateRunnable;
- mNotificationInfos = notificationInfos;
+ mNotificationInfoProvider = notificationInfoProvider;
mPackageManager = packageManager;
mNotificationManager = notificationManager;
mContext.registerReceiver(
@@ -97,7 +99,7 @@
* @param requestingAppUid the uid of the requesting app used to retrieve the app name.
*/
void showStateActiveNotificationIfNeeded(int state, int requestingAppUid) {
- NotificationInfo info = mNotificationInfos.get(state);
+ NotificationInfo info = getNotificationInfos().get(state);
if (info == null || !info.hasActiveNotification()) {
return;
}
@@ -127,7 +129,7 @@
* @param state the identifier of the device state being canceled.
*/
void showThermalCriticalNotificationIfNeeded(int state) {
- NotificationInfo info = mNotificationInfos.get(state);
+ NotificationInfo info = getNotificationInfos().get(state);
if (info == null || !info.hasThermalCriticalNotification()) {
return;
}
@@ -148,7 +150,7 @@
* @param state the identifier of the device state being canceled.
*/
void showPowerSaveNotificationIfNeeded(int state) {
- NotificationInfo info = mNotificationInfos.get(state);
+ NotificationInfo info = getNotificationInfos().get(state);
if (info == null || !info.hasPowerSaveModeNotification()) {
return;
}
@@ -170,7 +172,7 @@
* @param state the device state identifier.
*/
void cancelNotification(int state) {
- if (!mNotificationInfos.contains(state)) {
+ if (getNotificationInfos().get(state) == null) {
return;
}
mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
@@ -221,69 +223,121 @@
mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
}
- /**
- * Loads the resources for the notifications. The device state identifiers and strings are
- * stored in arrays. All the string arrays must have the same length and same order as the
- * identifier array.
- */
- private static SparseArray<NotificationInfo> getNotificationInfos(Context context) {
- final SparseArray<NotificationInfo> notificationInfos = new SparseArray<>();
+ private SparseArray<NotificationInfo> getNotificationInfos() {
+ Locale locale = mContext.getResources().getConfiguration().getLocales().get(0);
+ return mNotificationInfoProvider.getNotificationInfos(locale);
+ }
- final int[] stateIdentifiers =
- context.getResources().getIntArray(
- R.array.device_state_notification_state_identifiers);
- final String[] names =
- context.getResources().getStringArray(R.array.device_state_notification_names);
- final String[] activeNotificationTitles =
- context.getResources().getStringArray(
- R.array.device_state_notification_active_titles);
- final String[] activeNotificationContents =
- context.getResources().getStringArray(
- R.array.device_state_notification_active_contents);
- final String[] thermalCriticalNotificationTitles =
- context.getResources().getStringArray(
- R.array.device_state_notification_thermal_titles);
- final String[] thermalCriticalNotificationContents =
- context.getResources().getStringArray(
- R.array.device_state_notification_thermal_contents);
- final String[] powerSaveModeNotificationTitles =
- context.getResources().getStringArray(
- R.array.device_state_notification_power_save_titles);
- final String[] powerSaveModeNotificationContents =
- context.getResources().getStringArray(
- R.array.device_state_notification_power_save_contents);
+ @VisibleForTesting
+ public static class NotificationInfoProvider {
+ @NonNull
+ private final Context mContext;
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ @Nullable
+ private SparseArray<NotificationInfo> mCachedNotificationInfos;
- if (stateIdentifiers.length != names.length
- || stateIdentifiers.length != activeNotificationTitles.length
- || stateIdentifiers.length != activeNotificationContents.length
- || stateIdentifiers.length != thermalCriticalNotificationTitles.length
- || stateIdentifiers.length != thermalCriticalNotificationContents.length
- || stateIdentifiers.length != powerSaveModeNotificationTitles.length
- || stateIdentifiers.length != powerSaveModeNotificationContents.length
- ) {
- throw new IllegalStateException(
- "The length of state identifiers and notification texts must match!");
+ @GuardedBy("mLock")
+ @Nullable
+ @VisibleForTesting
+ Locale mCachedLocale;
+
+ NotificationInfoProvider(@NonNull Context context) {
+ mContext = context;
}
- for (int i = 0; i < stateIdentifiers.length; i++) {
- int identifier = stateIdentifiers[i];
- if (identifier == DeviceStateManager.INVALID_DEVICE_STATE) {
- continue;
+ /**
+ * Loads the resources for the notifications. The device state identifiers and strings are
+ * stored in arrays. All the string arrays must have the same length and same order as the
+ * identifier array.
+ */
+ @NonNull
+ public SparseArray<NotificationInfo> getNotificationInfos(@NonNull Locale locale) {
+ synchronized (mLock) {
+ if (!locale.equals(mCachedLocale)) {
+ refreshNotificationInfos(locale);
+ }
+ return mCachedNotificationInfos;
+ }
+ }
+
+
+ @VisibleForTesting
+ Locale getCachedLocale() {
+ synchronized (mLock) {
+ return mCachedLocale;
+ }
+ }
+
+ @VisibleForTesting
+ public void refreshNotificationInfos(Locale locale) {
+ synchronized (mLock) {
+ mCachedLocale = locale;
+ mCachedNotificationInfos = loadNotificationInfos();
+ }
+ }
+
+ @VisibleForTesting
+ public SparseArray<NotificationInfo> loadNotificationInfos() {
+ final SparseArray<NotificationInfo> notificationInfos = new SparseArray<>();
+
+ final int[] stateIdentifiers =
+ mContext.getResources().getIntArray(
+ R.array.device_state_notification_state_identifiers);
+ final String[] names =
+ mContext.getResources().getStringArray(R.array.device_state_notification_names);
+ final String[] activeNotificationTitles =
+ mContext.getResources().getStringArray(
+ R.array.device_state_notification_active_titles);
+ final String[] activeNotificationContents =
+ mContext.getResources().getStringArray(
+ R.array.device_state_notification_active_contents);
+ final String[] thermalCriticalNotificationTitles =
+ mContext.getResources().getStringArray(
+ R.array.device_state_notification_thermal_titles);
+ final String[] thermalCriticalNotificationContents =
+ mContext.getResources().getStringArray(
+ R.array.device_state_notification_thermal_contents);
+ final String[] powerSaveModeNotificationTitles =
+ mContext.getResources().getStringArray(
+ R.array.device_state_notification_power_save_titles);
+ final String[] powerSaveModeNotificationContents =
+ mContext.getResources().getStringArray(
+ R.array.device_state_notification_power_save_contents);
+
+ if (stateIdentifiers.length != names.length
+ || stateIdentifiers.length != activeNotificationTitles.length
+ || stateIdentifiers.length != activeNotificationContents.length
+ || stateIdentifiers.length != thermalCriticalNotificationTitles.length
+ || stateIdentifiers.length != thermalCriticalNotificationContents.length
+ || stateIdentifiers.length != powerSaveModeNotificationTitles.length
+ || stateIdentifiers.length != powerSaveModeNotificationContents.length
+ ) {
+ throw new IllegalStateException(
+ "The length of state identifiers and notification texts must match!");
}
- notificationInfos.put(
- identifier,
- new NotificationInfo(
- names[i], activeNotificationTitles[i], activeNotificationContents[i],
- thermalCriticalNotificationTitles[i],
- thermalCriticalNotificationContents[i],
- powerSaveModeNotificationTitles[i],
- powerSaveModeNotificationContents[i])
- );
- }
+ for (int i = 0; i < stateIdentifiers.length; i++) {
+ int identifier = stateIdentifiers[i];
+ if (identifier == DeviceStateManager.INVALID_DEVICE_STATE) {
+ continue;
+ }
- return notificationInfos;
+ notificationInfos.put(
+ identifier,
+ new NotificationInfo(
+ names[i],
+ activeNotificationTitles[i],
+ activeNotificationContents[i],
+ thermalCriticalNotificationTitles[i],
+ thermalCriticalNotificationContents[i],
+ powerSaveModeNotificationTitles[i],
+ powerSaveModeNotificationContents[i])
+ );
+ }
+ return notificationInfos;
+ }
}
/**
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 85b4034..5d92c7f 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -119,7 +119,6 @@
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.IntArray;
@@ -298,11 +297,10 @@
mDisplayWindowPolicyControllers = new SparseArray<>();
/**
- * Map of every internal primary display device {@link HighBrightnessModeMetadata}s indexed by
- * {@link DisplayDevice#mUniqueId}.
+ * Provides {@link HighBrightnessModeMetadata}s for {@link DisplayDevice}s.
*/
- public final ArrayMap<String, HighBrightnessModeMetadata> mHighBrightnessModeMetadataMap =
- new ArrayMap<>();
+ private final HighBrightnessModeMetadataMapper mHighBrightnessModeMetadataMapper =
+ new HighBrightnessModeMetadataMapper();
// List of all currently registered display adapters.
private final ArrayList<DisplayAdapter> mDisplayAdapters = new ArrayList<DisplayAdapter>();
@@ -1823,19 +1821,14 @@
DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
- final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
- if (device == null) {
- Slog.wtf(TAG, "Display Device is null in DisplayManagerService for display: "
- + display.getDisplayIdLocked());
- return;
- }
-
final int leadDisplayId = display.getLeadDisplayIdLocked();
updateDisplayPowerControllerLeaderLocked(dpc, leadDisplayId);
- final String uniqueId = device.getUniqueId();
- HighBrightnessModeMetadata hbmMetadata = mHighBrightnessModeMetadataMap.get(uniqueId);
- dpc.onDisplayChanged(hbmMetadata, leadDisplayId);
+ HighBrightnessModeMetadata hbmMetadata =
+ mHighBrightnessModeMetadataMapper.getHighBrightnessModeMetadataLocked(display);
+ if (hbmMetadata != null) {
+ dpc.onDisplayChanged(hbmMetadata, leadDisplayId);
+ }
}
}
@@ -1922,19 +1915,14 @@
final int displayId = display.getDisplayIdLocked();
final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
- final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
- if (device == null) {
- Slog.wtf(TAG, "Display Device is null in DisplayManagerService for display: "
- + display.getDisplayIdLocked());
- return;
- }
-
final int leadDisplayId = display.getLeadDisplayIdLocked();
updateDisplayPowerControllerLeaderLocked(dpc, leadDisplayId);
- final String uniqueId = device.getUniqueId();
- HighBrightnessModeMetadata hbmMetadata = mHighBrightnessModeMetadataMap.get(uniqueId);
- dpc.onDisplayChanged(hbmMetadata, leadDisplayId);
+ HighBrightnessModeMetadata hbmMetadata =
+ mHighBrightnessModeMetadataMapper.getHighBrightnessModeMetadataLocked(display);
+ if (hbmMetadata != null) {
+ dpc.onDisplayChanged(hbmMetadata, leadDisplayId);
+ }
}
}
@@ -3073,26 +3061,6 @@
mLogicalDisplayMapper.forEachLocked(this::addDisplayPowerControllerLocked);
}
- private HighBrightnessModeMetadata getHighBrightnessModeMetadata(LogicalDisplay display) {
- final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
- if (device == null) {
- Slog.wtf(TAG, "Display Device is null in DisplayPowerController for display: "
- + display.getDisplayIdLocked());
- return null;
- }
-
- final String uniqueId = device.getUniqueId();
-
- if (mHighBrightnessModeMetadataMap.containsKey(uniqueId)) {
- return mHighBrightnessModeMetadataMap.get(uniqueId);
- }
-
- // HBM Time info not present. Create a new one for this physical display.
- HighBrightnessModeMetadata hbmInfo = new HighBrightnessModeMetadata();
- mHighBrightnessModeMetadataMap.put(uniqueId, hbmInfo);
- return hbmInfo;
- }
-
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
private void addDisplayPowerControllerLocked(LogicalDisplay display) {
if (mPowerHandler == null) {
@@ -3113,7 +3081,13 @@
// We also need to pass a mapping of the HighBrightnessModeTimeInfoMap to
// displayPowerController, so the hbm info can be correctly associated
// with the corresponding displaydevice.
- HighBrightnessModeMetadata hbmMetadata = getHighBrightnessModeMetadata(display);
+ HighBrightnessModeMetadata hbmMetadata =
+ mHighBrightnessModeMetadataMapper.getHighBrightnessModeMetadataLocked(display);
+ if (hbmMetadata == null) {
+ Slog.wtf(TAG, "High Brightness Mode Metadata is null in DisplayManagerService for "
+ + "display: " + display.getDisplayIdLocked());
+ return;
+ }
if (DeviceConfig.getBoolean("display_manager",
"use_newly_structured_display_power_controller", true)) {
displayPowerController = new DisplayPowerController2(
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 5e3990a..1acc208 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -514,6 +514,7 @@
private boolean mIsEnabled;
private boolean mIsInTransition;
+ private boolean mIsDisplayInternal;
// The id of the thermal brightness throttling policy that should be used.
private String mThermalBrightnessThrottlingDataId;
@@ -553,6 +554,8 @@
mDisplayStatsId = mUniqueDisplayId.hashCode();
mIsEnabled = logicalDisplay.isEnabledLocked();
mIsInTransition = logicalDisplay.isInTransitionLocked();
+ mIsDisplayInternal = logicalDisplay.getPrimaryDisplayDeviceLocked()
+ .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL;
mHandler = new DisplayControllerHandler(handler.getLooper());
mLastBrightnessEvent = new BrightnessEvent(mDisplayId);
mTempBrightnessEvent = new BrightnessEvent(mDisplayId);
@@ -892,6 +895,9 @@
final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
final boolean isEnabled = mLogicalDisplay.isEnabledLocked();
final boolean isInTransition = mLogicalDisplay.isInTransitionLocked();
+ final boolean isDisplayInternal = mLogicalDisplay.getPrimaryDisplayDeviceLocked() != null
+ && mLogicalDisplay.getPrimaryDisplayDeviceLocked()
+ .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL;
final String thermalBrightnessThrottlingDataId =
mLogicalDisplay.getThermalBrightnessThrottlingDataIdLocked();
mHandler.postAtTime(() -> {
@@ -924,7 +930,7 @@
mIsEnabled = isEnabled;
mIsInTransition = isInTransition;
}
-
+ mIsDisplayInternal = isDisplayInternal;
if (changed) {
updatePowerState();
}
@@ -1810,10 +1816,11 @@
// TODO(b/216365040): The decision to prevent HBM for HDR in low power mode should be
// done in HighBrightnessModeController.
if (mHbmController.getHighBrightnessMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
- && (mBrightnessReason.getModifier() & BrightnessReason.MODIFIER_DIMMED) == 0
- && (mBrightnessReason.getModifier() & BrightnessReason.MODIFIER_LOW_POWER)
+ && (mBrightnessReasonTemp.getModifier() & BrightnessReason.MODIFIER_DIMMED) == 0
+ && (mBrightnessReasonTemp.getModifier() & BrightnessReason.MODIFIER_LOW_POWER)
== 0) {
- // We want to scale HDR brightness level with the SDR level
+ // We want to scale HDR brightness level with the SDR level, we also need to restore
+ // SDR brightness immediately when entering dim or low power mode.
animateValue = mHbmController.getHdrBrightnessValue();
}
@@ -3074,9 +3081,7 @@
event.getThermalMax() == PowerManager.BRIGHTNESS_MAX
? -1f : convertToNits(event.getThermalMax());
- if (mLogicalDisplay.getPrimaryDisplayDeviceLocked() != null
- && mLogicalDisplay.getPrimaryDisplayDeviceLocked()
- .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL) {
+ if (mIsDisplayInternal) {
FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED,
convertToNits(event.getInitialBrightness()),
convertToNits(event.getBrightness()),
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 23e606c..b36aede 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -399,6 +399,7 @@
private boolean mIsEnabled;
private boolean mIsInTransition;
+ private boolean mIsDisplayInternal;
// The id of the thermal brightness throttling policy that should be used.
private String mThermalBrightnessThrottlingDataId;
@@ -431,6 +432,8 @@
.getDisplayDeviceConfig();
mIsEnabled = logicalDisplay.isEnabledLocked();
mIsInTransition = logicalDisplay.isInTransitionLocked();
+ mIsDisplayInternal = logicalDisplay.getPrimaryDisplayDeviceLocked()
+ .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL;
mWakelockController = mInjector.getWakelockController(mDisplayId, callbacks);
mDisplayPowerProximityStateController = mInjector.getDisplayPowerProximityStateController(
mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(),
@@ -708,6 +711,9 @@
final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
final boolean isEnabled = mLogicalDisplay.isEnabledLocked();
final boolean isInTransition = mLogicalDisplay.isInTransitionLocked();
+ final boolean isDisplayInternal = mLogicalDisplay.getPrimaryDisplayDeviceLocked() != null
+ && mLogicalDisplay.getPrimaryDisplayDeviceLocked()
+ .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL;
final String thermalBrightnessThrottlingDataId =
mLogicalDisplay.getThermalBrightnessThrottlingDataIdLocked();
@@ -742,6 +748,7 @@
mIsInTransition = isInTransition;
}
+ mIsDisplayInternal = isDisplayInternal;
if (changed) {
updatePowerState();
}
@@ -1449,10 +1456,11 @@
// TODO(b/216365040): The decision to prevent HBM for HDR in low power mode should be
// done in HighBrightnessModeController.
if (mHbmController.getHighBrightnessMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
- && (mBrightnessReason.getModifier() & BrightnessReason.MODIFIER_DIMMED) == 0
- && (mBrightnessReason.getModifier() & BrightnessReason.MODIFIER_LOW_POWER)
+ && (mBrightnessReasonTemp.getModifier() & BrightnessReason.MODIFIER_DIMMED) == 0
+ && (mBrightnessReasonTemp.getModifier() & BrightnessReason.MODIFIER_LOW_POWER)
== 0) {
- // We want to scale HDR brightness level with the SDR level
+ // We want to scale HDR brightness level with the SDR level, we also need to restore
+ // SDR brightness immediately when entering dim or low power mode.
animateValue = mHbmController.getHdrBrightnessValue();
}
@@ -2217,6 +2225,7 @@
pw.println(" mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp);
pw.println(" mColorFadeFadesConfig=" + mColorFadeFadesConfig);
pw.println(" mColorFadeEnabled=" + mColorFadeEnabled);
+ pw.println(" mIsDisplayInternal=" + mIsDisplayInternal);
synchronized (mCachedBrightnessInfo) {
pw.println(" mCachedBrightnessInfo.brightness="
+ mCachedBrightnessInfo.brightness.value);
@@ -2434,9 +2443,7 @@
float appliedThermalCapNits =
event.getThermalMax() == PowerManager.BRIGHTNESS_MAX
? -1f : mDisplayBrightnessController.convertToNits(event.getThermalMax());
- if (mLogicalDisplay.getPrimaryDisplayDeviceLocked() != null
- && mLogicalDisplay.getPrimaryDisplayDeviceLocked()
- .getDisplayDeviceInfoLocked().type == Display.TYPE_INTERNAL) {
+ if (mIsDisplayInternal) {
FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED,
mDisplayBrightnessController.convertToNits(event.getInitialBrightness()),
mDisplayBrightnessController.convertToNits(event.getBrightness()),
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeMetadataMapper.java b/services/core/java/com/android/server/display/HighBrightnessModeMetadataMapper.java
new file mode 100644
index 0000000..76702d3
--- /dev/null
+++ b/services/core/java/com/android/server/display/HighBrightnessModeMetadataMapper.java
@@ -0,0 +1,56 @@
+/*
+ * 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.display;
+
+import android.util.ArrayMap;
+import android.util.Slog;
+
+/**
+ * Provides {@link HighBrightnessModeMetadata}s for {@link DisplayDevice}s. This class should only
+ * be accessed from the display thread.
+ */
+class HighBrightnessModeMetadataMapper {
+
+ private static final String TAG = "HighBrightnessModeMetadataMapper";
+
+ /**
+ * Map of every internal primary display device {@link HighBrightnessModeMetadata}s indexed by
+ * {@link DisplayDevice#mUniqueId}.
+ */
+ private final ArrayMap<String, HighBrightnessModeMetadata> mHighBrightnessModeMetadataMap =
+ new ArrayMap<>();
+
+ HighBrightnessModeMetadata getHighBrightnessModeMetadataLocked(LogicalDisplay display) {
+ final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+ if (device == null) {
+ Slog.wtf(TAG, "Display Device is null in DisplayPowerController for display: "
+ + display.getDisplayIdLocked());
+ return null;
+ }
+
+ final String uniqueId = device.getUniqueId();
+
+ if (mHighBrightnessModeMetadataMap.containsKey(uniqueId)) {
+ return mHighBrightnessModeMetadataMap.get(uniqueId);
+ }
+
+ // HBM Time info not present. Create a new one for this physical display.
+ HighBrightnessModeMetadata hbmInfo = new HighBrightnessModeMetadata();
+ mHighBrightnessModeMetadataMap.put(uniqueId, hbmInfo);
+ return hbmInfo;
+ }
+}
diff --git a/services/core/java/com/android/server/display/WakelockController.java b/services/core/java/com/android/server/display/WakelockController.java
index 6511f4f..1e13974 100644
--- a/services/core/java/com/android/server/display/WakelockController.java
+++ b/services/core/java/com/android/server/display/WakelockController.java
@@ -38,7 +38,9 @@
public static final int WAKE_LOCK_STATE_CHANGED = 4;
public static final int WAKE_LOCK_UNFINISHED_BUSINESS = 5;
- private static final int WAKE_LOCK_MAX = WAKE_LOCK_UNFINISHED_BUSINESS;
+ @VisibleForTesting
+ static final int WAKE_LOCK_MAX = WAKE_LOCK_UNFINISHED_BUSINESS;
+
private static final boolean DEBUG = false;
@IntDef(flag = true, prefix = "WAKE_LOCK_", value = {
@@ -132,7 +134,7 @@
* A utility to release all the wakelock acquired by the system
*/
public void releaseAll() {
- for (int i = WAKE_LOCK_PROXIMITY_POSITIVE; i < WAKE_LOCK_MAX; i++) {
+ for (int i = WAKE_LOCK_PROXIMITY_POSITIVE; i <= WAKE_LOCK_MAX; i++) {
releaseWakelockInternal(i);
}
}
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index 01cae42..ad640b1 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -239,7 +239,7 @@
String[] certs = mContext.getResources().getStringArray(
R.array.config_fontManagerServiceCerts);
- if (mDebugCertFilePath != null && (Build.IS_USERDEBUG || Build.IS_ENG)) {
+ if (mDebugCertFilePath != null && Build.IS_DEBUGGABLE) {
String[] tmp = new String[certs.length + 1];
System.arraycopy(certs, 0, tmp, 0, certs.length);
tmp[certs.length] = mDebugCertFilePath;
@@ -251,8 +251,8 @@
}
/**
- * Add debug certificate to the cert list. This must be called only on userdebug/eng
- * build.
+ * Add debug certificate to the cert list. This must be called only on debuggable build.
+ *
* @param debugCertPath a debug certificate file path
*/
public void addDebugCertificate(@Nullable String debugCertPath) {
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
index c039a83..6d82841d 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
@@ -105,8 +105,8 @@
w.println(" Update font families with the new definitions.");
w.println();
w.println("install-debug-cert [cert file path]");
- w.println(" Install debug certificate file. This command can be used only on userdebug");
- w.println(" or eng device with root user.");
+ w.println(" Install debug certificate file. This command can be used only on");
+ w.println(" debuggable device with root user.");
w.println();
w.println("clear");
w.println(" Remove all installed font files and reset to the initial state.");
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 9eedc4e..f47c4b2 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -682,7 +682,6 @@
@ServiceThreadOnly
private void launchDeviceDiscovery() {
assertRunOnServiceThread();
- clearDeviceInfoList();
DeviceDiscoveryAction action = new DeviceDiscoveryAction(this,
new DeviceDiscoveryCallback() {
@Override
@@ -691,13 +690,6 @@
mService.getHdmiCecNetwork().addCecDevice(info);
}
- // Since we removed all devices when it starts and
- // device discovery action does not poll local devices,
- // we should put device info of local device manually here
- for (HdmiCecLocalDevice device : mService.getAllCecLocalDevices()) {
- mService.getHdmiCecNetwork().addCecDevice(device.getDeviceInfo());
- }
-
mSelectRequestBuffer.process();
resetSelectRequestBuffer();
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 805ff66..75fe63a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1267,6 +1267,7 @@
// It's now safe to flush existing local devices from mCecController since they were
// already moved to 'localDevices'.
clearCecLocalDevices();
+ mHdmiCecNetwork.clearDeviceList();
allocateLogicalAddress(localDevices, initiatedBy);
}
@@ -1303,6 +1304,7 @@
HdmiControlManager.POWER_STATUS_ON, getCecVersion());
localDevice.setDeviceInfo(deviceInfo);
mHdmiCecNetwork.addLocalDevice(deviceType, localDevice);
+ mHdmiCecNetwork.addCecDevice(localDevice.getDeviceInfo());
mCecController.addLogicalAddress(logicalAddress);
allocatedDevices.add(localDevice);
}
diff --git a/services/core/java/com/android/server/infra/OWNERS b/services/core/java/com/android/server/infra/OWNERS
index 0466d8a..4fea05d 100644
--- a/services/core/java/com/android/server/infra/OWNERS
+++ b/services/core/java/com/android/server/infra/OWNERS
@@ -1,3 +1,3 @@
# Bug component: 655446
-include /core/java/android/service/cloudsearch/OWNERS
+srazdan@google.com
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index d0669e7..5f45f91 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -686,13 +686,7 @@
@NonNull
private InputChannel createSpyWindowGestureMonitor(IBinder monitorToken, String name,
- int displayId, int pid, int uid) {
- final SurfaceControl sc = mWindowManagerCallbacks.createSurfaceForGestureMonitor(name,
- displayId);
- if (sc == null) {
- throw new IllegalArgumentException(
- "Could not create gesture monitor surface on display: " + displayId);
- }
+ SurfaceControl sc, int displayId, int pid, int uid) {
final InputChannel channel = createInputChannel(name);
try {
@@ -749,9 +743,18 @@
final long ident = Binder.clearCallingIdentity();
try {
- final InputChannel inputChannel =
- createSpyWindowGestureMonitor(monitorToken, name, displayId, pid, uid);
- return new InputMonitor(inputChannel, new InputMonitorHost(inputChannel.getToken()));
+ final SurfaceControl sc = mWindowManagerCallbacks.createSurfaceForGestureMonitor(name,
+ displayId);
+ if (sc == null) {
+ throw new IllegalArgumentException(
+ "Could not create gesture monitor surface on display: " + displayId);
+ }
+
+ final InputChannel inputChannel = createSpyWindowGestureMonitor(
+ monitorToken, name, sc, displayId, pid, uid);
+ return new InputMonitor(inputChannel,
+ new InputMonitorHost(inputChannel.getToken()),
+ new SurfaceControl(sc, "IMS.monitorGestureInput"));
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
index 0ae1e80..a1b67e1 100644
--- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
@@ -18,14 +18,19 @@
import static android.view.inputmethod.ImeTracker.DEBUG_IME_VISIBILITY;
+import static com.android.internal.inputmethod.SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS;
+import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS;
import static com.android.server.EventLogTags.IMF_HIDE_IME;
import static com.android.server.EventLogTags.IMF_SHOW_IME;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_EXPLICIT;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_NOT_ALWAYS;
+import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_REMOVE_IME_SNAPSHOT;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME_IMPLICIT;
+import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME_SNAPSHOT;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
import android.os.ResultReceiver;
@@ -38,6 +43,7 @@
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.server.LocalServices;
+import com.android.server.wm.ImeTargetVisibilityPolicy;
import com.android.server.wm.WindowManagerInternal;
import java.util.Objects;
@@ -56,10 +62,14 @@
private final WindowManagerInternal mWindowManagerInternal;
+ @NonNull
+ private final ImeTargetVisibilityPolicy mImeTargetVisibilityPolicy;
+
DefaultImeVisibilityApplier(InputMethodManagerService service) {
mService = service;
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
+ mImeTargetVisibilityPolicy = LocalServices.getService(ImeTargetVisibilityPolicy.class);
}
@GuardedBy("ImfLock.class")
@@ -162,8 +172,37 @@
mService.showCurrentInputLocked(windowToken, statsToken,
InputMethodManager.SHOW_IMPLICIT, null, reason);
break;
+ case STATE_SHOW_IME_SNAPSHOT:
+ showImeScreenshot(windowToken, mService.getDisplayIdToShowImeLocked(), null);
+ break;
+ case STATE_REMOVE_IME_SNAPSHOT:
+ removeImeScreenshot(mService.getDisplayIdToShowImeLocked());
+ break;
default:
throw new IllegalArgumentException("Invalid IME visibility state: " + state);
}
}
+
+ @GuardedBy("ImfLock.class")
+ @Override
+ public boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId,
+ @Nullable ImeTracker.Token statsToken) {
+ if (mImeTargetVisibilityPolicy.showImeScreenshot(imeTarget, displayId)) {
+ mService.onShowHideSoftInputRequested(false /* show */, imeTarget,
+ SHOW_IME_SCREENSHOT_FROM_IMMS, statsToken);
+ return true;
+ }
+ return false;
+ }
+
+ @GuardedBy("ImfLock.class")
+ @Override
+ public boolean removeImeScreenshot(int displayId) {
+ if (mImeTargetVisibilityPolicy.removeImeScreenshot(displayId)) {
+ mService.onShowHideSoftInputRequested(false /* show */, mService.mCurFocusedWindow,
+ REMOVE_IME_SCREENSHOT_FROM_IMMS, null);
+ return true;
+ }
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
index f03e985..27f6a89 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
@@ -16,6 +16,7 @@
package com.android.server.inputmethod;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
import android.os.ResultReceiver;
@@ -76,4 +77,27 @@
// TODO: add a method in WindowManagerInternal to call DC#updateImeInputAndControlTarget
// here to end up updating IME layering after IMMS#attachNewInputLocked called.
}
+
+ /**
+ * Shows the IME screenshot and attach it to the given IME target window.
+ *
+ * @param windowToken The token of a window to show the IME screenshot.
+ * @param displayId The unique id to identify the display
+ * @param statsToken A token that tracks the progress of an IME request.
+ * @return {@code true} if success, {@code false} otherwise.
+ */
+ default boolean showImeScreenshot(@NonNull IBinder windowToken, int displayId,
+ @Nullable ImeTracker.Token statsToken) {
+ return false;
+ }
+
+ /**
+ * Removes the IME screenshot on the given display.
+ *
+ * @param displayId The target display of showing IME screenshot.
+ * @return {@code true} if success, {@code false} otherwise.
+ */
+ default boolean removeImeScreenshot(int displayId) {
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
index 61fe654..19d6fa0 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
@@ -29,6 +29,8 @@
import static android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import static com.android.internal.inputmethod.InputMethodDebug.softInputModeToString;
+import static com.android.internal.inputmethod.SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS;
+import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS;
import static com.android.server.inputmethod.InputMethodManagerService.computeImeDisplayIdForTarget;
import android.accessibilityservice.AccessibilityService;
@@ -49,6 +51,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.server.LocalServices;
+import com.android.server.wm.ImeTargetChangeListener;
import com.android.server.wm.WindowManagerInternal;
import java.io.PrintWriter;
@@ -99,6 +102,18 @@
*/
private boolean mInputShown;
+ /**
+ * Set if we called
+ * {@link com.android.server.wm.ImeTargetVisibilityPolicy#showImeScreenshot(IBinder, int)}.
+ */
+ private boolean mRequestedImeScreenshot;
+
+ /** The window token of the current visible IME layering target overlay. */
+ private IBinder mCurVisibleImeLayeringOverlay;
+
+ /** The window token of the current visible IME input target. */
+ private IBinder mCurVisibleImeInputTarget;
+
/** Represent the invalid IME visibility state */
public static final int STATE_INVALID = -1;
@@ -122,6 +137,10 @@
public static final int STATE_HIDE_IME_NOT_ALWAYS = 6;
public static final int STATE_SHOW_IME_IMPLICIT = 7;
+
+ /** State to handle removing an IME preview surface when necessary. */
+ public static final int STATE_REMOVE_IME_SNAPSHOT = 8;
+
@IntDef({
STATE_INVALID,
STATE_HIDE_IME,
@@ -132,6 +151,7 @@
STATE_HIDE_IME_EXPLICIT,
STATE_HIDE_IME_NOT_ALWAYS,
STATE_SHOW_IME_IMPLICIT,
+ STATE_REMOVE_IME_SNAPSHOT,
})
@interface VisibilityState {}
@@ -172,6 +192,24 @@
mWindowManagerInternal = wmService;
mImeDisplayValidator = imeDisplayValidator;
mPolicy = imePolicy;
+ mWindowManagerInternal.setInputMethodTargetChangeListener(new ImeTargetChangeListener() {
+ @Override
+ public void onImeTargetOverlayVisibilityChanged(IBinder overlayWindowToken,
+ boolean visible, boolean removed) {
+ mCurVisibleImeLayeringOverlay = (visible && !removed) ? overlayWindowToken : null;
+ }
+
+ @Override
+ public void onImeInputTargetVisibilityChanged(IBinder imeInputTarget,
+ boolean visibleRequested, boolean removed) {
+ mCurVisibleImeInputTarget = (visibleRequested && !removed) ? imeInputTarget : null;
+ if (mCurVisibleImeInputTarget == null && mCurVisibleImeLayeringOverlay != null) {
+ mService.onApplyImeVisibilityFromComputer(imeInputTarget,
+ new ImeVisibilityResult(STATE_HIDE_IME_EXPLICIT,
+ SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE));
+ }
+ }
+ });
}
/**
@@ -453,6 +491,21 @@
return null;
}
+ @VisibleForTesting
+ ImeVisibilityResult onInteractiveChanged(IBinder windowToken, boolean interactive) {
+ final ImeTargetWindowState state = getWindowStateOrNull(windowToken);
+ if (state != null && state.isRequestedImeVisible() && mInputShown && !interactive) {
+ mRequestedImeScreenshot = true;
+ return new ImeVisibilityResult(STATE_SHOW_IME_SNAPSHOT, SHOW_IME_SCREENSHOT_FROM_IMMS);
+ }
+ if (interactive && mRequestedImeScreenshot) {
+ mRequestedImeScreenshot = false;
+ return new ImeVisibilityResult(STATE_REMOVE_IME_SNAPSHOT,
+ REMOVE_IME_SCREENSHOT_FROM_IMMS);
+ }
+ return null;
+ }
+
IBinder getWindowTokenFrom(IBinder requestImeToken) {
for (IBinder windowToken : mRequestWindowStateMap.keySet()) {
final ImeTargetWindowState state = mRequestWindowStateMap.get(windowToken);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 2433211..c70d555 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4847,6 +4847,14 @@
}
}
+ void onApplyImeVisibilityFromComputer(IBinder windowToken,
+ @NonNull ImeVisibilityResult result) {
+ synchronized (ImfLock.class) {
+ mVisibilityApplier.applyImeVisibility(windowToken, null, result.getState(),
+ result.getReason());
+ }
+ }
+
@GuardedBy("ImfLock.class")
void setEnabledSessionLocked(SessionState session) {
if (mEnabledSession != session) {
@@ -5083,6 +5091,14 @@
return;
}
if (mImePlatformCompatUtils.shouldUseSetInteractiveProtocol(getCurMethodUidLocked())) {
+ // Handle IME visibility when interactive changed before finishing the input to
+ // ensure we preserve the last state as possible.
+ final ImeVisibilityResult imeVisRes = mVisibilityStateComputer.onInteractiveChanged(
+ mCurFocusedWindow, interactive);
+ if (imeVisRes != null) {
+ mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, null,
+ imeVisRes.getState(), imeVisRes.getReason());
+ }
// Eligible IME processes use new "setInteractive" protocol.
mCurClient.mClient.setInteractive(mIsInteractive, mInFullscreenMode);
} else {
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index b82e3a3..c076c05 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
+import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
@@ -26,6 +27,7 @@
import com.android.internal.annotations.GuardedBy;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -108,6 +110,28 @@
&& mComponentName.getClassName().equals(className);
}
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + getDebugString());
+ prefix += " ";
+ if (mProviderInfo == null) {
+ pw.println(prefix + "<provider info not received, yet>");
+ } else if (mProviderInfo.getRoutes().isEmpty()) {
+ pw.println(prefix + "<provider info has no routes>");
+ } else {
+ for (MediaRoute2Info route : mProviderInfo.getRoutes()) {
+ pw.printf("%s%s | %s\n", prefix, route.getId(), route.getName());
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getDebugString();
+ }
+
+ /** Returns a human-readable string describing the instance, for debugging purposes. */
+ protected abstract String getDebugString();
+
public interface Callback {
void onProviderStateChanged(@Nullable MediaRoute2Provider provider);
void onSessionCreated(@NonNull MediaRoute2Provider provider,
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index 90451b1..72b8436 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -44,7 +44,6 @@
import com.android.internal.annotations.GuardedBy;
-import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
@@ -83,10 +82,6 @@
mHandler = new Handler(Looper.myLooper());
}
- public void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + getDebugString());
- }
-
public void setManagerScanning(boolean managerScanning) {
if (mIsManagerScanning != managerScanning) {
mIsManagerScanning = managerScanning;
@@ -488,11 +483,7 @@
}
@Override
- public String toString() {
- return getDebugString();
- }
-
- private String getDebugString() {
+ protected String getDebugString() {
return TextUtils.formatSimple(
"ProviderServiceProxy - package: %s, bound: %b, connection (active:%b, ready:%b)",
mComponentName.getPackageName(),
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 3c97aaf8..2d3b97b 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -1751,6 +1751,7 @@
String indent = prefix + " ";
pw.println(indent + "mRunning=" + mRunning);
+ mSystemProvider.dump(pw, prefix);
mWatcher.dump(pw, prefix);
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 5ea2ca4..464a256 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -16,14 +16,22 @@
package com.android.server.media;
+import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.PendingIntent;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
+import android.content.pm.ResolveInfo;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioSystem;
@@ -42,6 +50,7 @@
import android.media.session.PlaybackState;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.DeadObjectException;
import android.os.Handler;
@@ -52,6 +61,7 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
@@ -73,6 +83,17 @@
*/
// TODO(jaewan): Do not call service method directly -- introduce listener instead.
public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionRecordImpl {
+
+ /**
+ * {@link MediaSession#setMediaButtonBroadcastReceiver(ComponentName)} throws an {@link
+ * IllegalArgumentException} if the provided {@link ComponentName} does not resolve to a valid
+ * {@link android.content.BroadcastReceiver broadcast receiver} for apps targeting Android U and
+ * above. For apps targeting Android T and below, the request will be ignored.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ static final long THROW_FOR_INVALID_BROADCAST_RECEIVER = 270049379L;
+
private static final String TAG = "MediaSessionRecord";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -871,6 +892,22 @@
}
};
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ private static boolean componentNameExists(
+ @NonNull ComponentName componentName, @NonNull Context context, int userId) {
+ Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ mediaButtonIntent.setComponent(componentName);
+
+ UserHandle userHandle = UserHandle.of(userId);
+ PackageManager pm = context.getPackageManager();
+
+ List<ResolveInfo> resolveInfos =
+ pm.queryBroadcastReceiversAsUser(
+ mediaButtonIntent, PackageManager.ResolveInfoFlags.of(0), userHandle);
+ return !resolveInfos.isEmpty();
+ }
+
private final class SessionStub extends ISession.Stub {
@Override
public void destroySession() throws RemoteException {
@@ -955,7 +992,9 @@
}
@Override
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
public void setMediaButtonBroadcastReceiver(ComponentName receiver) throws RemoteException {
+ final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
//mPackageName has been verified in MediaSessionService.enforcePackageName().
@@ -970,6 +1009,20 @@
!= 0) {
return;
}
+
+ if (!componentNameExists(receiver, mContext, mUserId)) {
+ if (CompatChanges.isChangeEnabled(THROW_FOR_INVALID_BROADCAST_RECEIVER, uid)) {
+ throw new IllegalArgumentException("Invalid component name: " + receiver);
+ } else {
+ Log.w(
+ TAG,
+ "setMediaButtonBroadcastReceiver(): "
+ + "Ignoring invalid component name="
+ + receiver);
+ }
+ return;
+ }
+
mMediaButtonReceiverHolder = MediaButtonReceiverHolder.create(mUserId, receiver);
mService.onMediaButtonReceiverChanged(MediaSessionRecord.this);
} finally {
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 5d5c621..6d2d2e4 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -392,6 +392,15 @@
mCallback.onSessionUpdated(this, sessionInfo);
}
+ @Override
+ protected String getDebugString() {
+ return TextUtils.formatSimple(
+ "SystemMR2Provider - package: %s, selected route id: %s, bluetooth impl: %s",
+ mComponentName.getPackageName(),
+ mSelectedRouteId,
+ mBluetoothRouteController.getClass().getSimpleName());
+ }
+
private static class SessionCreationRequest {
final long mRequestId;
final String mRouteId;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ebcbfed..07891f3 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -10796,7 +10796,8 @@
static final String FLAG_SEPARATOR = "\\|";
private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
- ArrayMap<Pair<ComponentName, Integer>, NotificationListenerFilter>
+ @GuardedBy("mRequestedNotificationListeners")
+ private final ArrayMap<Pair<ComponentName, Integer>, NotificationListenerFilter>
mRequestedNotificationListeners = new ArrayMap<>();
private final boolean mIsHeadlessSystemUserMode;
@@ -10914,9 +10915,11 @@
@Override
public void onUserRemoved(int user) {
super.onUserRemoved(user);
- for (int i = mRequestedNotificationListeners.size() - 1; i >= 0; i--) {
- if (mRequestedNotificationListeners.keyAt(i).second == user) {
- mRequestedNotificationListeners.removeAt(i);
+ synchronized (mRequestedNotificationListeners) {
+ for (int i = mRequestedNotificationListeners.size() - 1; i >= 0; i--) {
+ if (mRequestedNotificationListeners.keyAt(i).second == user) {
+ mRequestedNotificationListeners.removeAt(i);
+ }
}
}
}
@@ -10925,31 +10928,34 @@
public void onPackagesChanged(boolean removingPackage, String[] pkgList, int[] uidList) {
super.onPackagesChanged(removingPackage, pkgList, uidList);
- // Since the default behavior is to allow everything, we don't need to explicitly
- // handle package add or update. they will be added to the xml file on next boot or
- // when the user tries to change the settings.
- if (removingPackage) {
- for (int i = 0; i < pkgList.length; i++) {
- String pkg = pkgList[i];
- int userId = UserHandle.getUserId(uidList[i]);
- for (int j = mRequestedNotificationListeners.size() - 1; j >= 0; j--) {
- Pair<ComponentName, Integer> key = mRequestedNotificationListeners.keyAt(j);
- if (key.second == userId && key.first.getPackageName().equals(pkg)) {
- mRequestedNotificationListeners.removeAt(j);
+ synchronized (mRequestedNotificationListeners) {
+ // Since the default behavior is to allow everything, we don't need to explicitly
+ // handle package add or update. they will be added to the xml file on next boot or
+ // when the user tries to change the settings.
+ if (removingPackage) {
+ for (int i = 0; i < pkgList.length; i++) {
+ String pkg = pkgList[i];
+ int userId = UserHandle.getUserId(uidList[i]);
+ for (int j = mRequestedNotificationListeners.size() - 1; j >= 0; j--) {
+ Pair<ComponentName, Integer> key =
+ mRequestedNotificationListeners.keyAt(j);
+ if (key.second == userId && key.first.getPackageName().equals(pkg)) {
+ mRequestedNotificationListeners.removeAt(j);
+ }
}
}
}
- }
- // clean up anything in the disallowed pkgs list
- for (int i = 0; i < pkgList.length; i++) {
- String pkg = pkgList[i];
- int userId = UserHandle.getUserId(uidList[i]);
- for (int j = mRequestedNotificationListeners.size() - 1; j >= 0; j--) {
- NotificationListenerFilter nlf = mRequestedNotificationListeners.valueAt(j);
+ // clean up anything in the disallowed pkgs list
+ for (int i = 0; i < pkgList.length; i++) {
+ String pkg = pkgList[i];
+ for (int j = mRequestedNotificationListeners.size() - 1; j >= 0; j--) {
+ NotificationListenerFilter nlf =
+ mRequestedNotificationListeners.valueAt(j);
- VersionedPackage ai = new VersionedPackage(pkg, uidList[i]);
- nlf.removePackage(ai);
+ VersionedPackage ai = new VersionedPackage(pkg, uidList[i]);
+ nlf.removePackage(ai);
+ }
}
}
}
@@ -10997,7 +11003,9 @@
}
NotificationListenerFilter nlf =
new NotificationListenerFilter(approved, disallowedPkgs);
- mRequestedNotificationListeners.put(Pair.create(cn, userId), nlf);
+ synchronized (mRequestedNotificationListeners) {
+ mRequestedNotificationListeners.put(Pair.create(cn, userId), nlf);
+ }
}
}
}
@@ -11005,72 +11013,81 @@
@Override
protected void writeExtraXmlTags(TypedXmlSerializer out) throws IOException {
out.startTag(null, TAG_REQUESTED_LISTENERS);
- for (Pair<ComponentName, Integer> listener : mRequestedNotificationListeners.keySet()) {
- NotificationListenerFilter nlf = mRequestedNotificationListeners.get(listener);
- out.startTag(null, TAG_REQUESTED_LISTENER);
- XmlUtils.writeStringAttribute(
- out, ATT_COMPONENT, listener.first.flattenToString());
- XmlUtils.writeIntAttribute(out, ATT_USER_ID, listener.second);
+ synchronized (mRequestedNotificationListeners) {
+ for (Pair<ComponentName, Integer> listener :
+ mRequestedNotificationListeners.keySet()) {
+ NotificationListenerFilter nlf = mRequestedNotificationListeners.get(listener);
+ out.startTag(null, TAG_REQUESTED_LISTENER);
+ XmlUtils.writeStringAttribute(
+ out, ATT_COMPONENT, listener.first.flattenToString());
+ XmlUtils.writeIntAttribute(out, ATT_USER_ID, listener.second);
- out.startTag(null, TAG_APPROVED);
- XmlUtils.writeIntAttribute(out, ATT_TYPES, nlf.getTypes());
- out.endTag(null, TAG_APPROVED);
+ out.startTag(null, TAG_APPROVED);
+ XmlUtils.writeIntAttribute(out, ATT_TYPES, nlf.getTypes());
+ out.endTag(null, TAG_APPROVED);
- for (VersionedPackage ai : nlf.getDisallowedPackages()) {
- if (!TextUtils.isEmpty(ai.getPackageName())) {
- out.startTag(null, TAG_DISALLOWED);
- XmlUtils.writeStringAttribute(out, ATT_PKG, ai.getPackageName());
- XmlUtils.writeIntAttribute(out, ATT_UID, ai.getVersionCode());
- out.endTag(null, TAG_DISALLOWED);
+ for (VersionedPackage ai : nlf.getDisallowedPackages()) {
+ if (!TextUtils.isEmpty(ai.getPackageName())) {
+ out.startTag(null, TAG_DISALLOWED);
+ XmlUtils.writeStringAttribute(out, ATT_PKG, ai.getPackageName());
+ XmlUtils.writeIntAttribute(out, ATT_UID, ai.getVersionCode());
+ out.endTag(null, TAG_DISALLOWED);
+ }
}
- }
- out.endTag(null, TAG_REQUESTED_LISTENER);
+ out.endTag(null, TAG_REQUESTED_LISTENER);
+ }
}
out.endTag(null, TAG_REQUESTED_LISTENERS);
}
- protected @Nullable NotificationListenerFilter getNotificationListenerFilter(
+ @Nullable protected NotificationListenerFilter getNotificationListenerFilter(
Pair<ComponentName, Integer> pair) {
- return mRequestedNotificationListeners.get(pair);
+ synchronized (mRequestedNotificationListeners) {
+ return mRequestedNotificationListeners.get(pair);
+ }
}
protected void setNotificationListenerFilter(Pair<ComponentName, Integer> pair,
NotificationListenerFilter nlf) {
- mRequestedNotificationListeners.put(pair, nlf);
+ synchronized (mRequestedNotificationListeners) {
+ mRequestedNotificationListeners.put(pair, nlf);
+ }
}
@Override
protected void ensureFilters(ServiceInfo si, int userId) {
- Pair listener = Pair.create(si.getComponentName(), userId);
- NotificationListenerFilter existingNlf =
- mRequestedNotificationListeners.get(listener);
- if (si.metaData != null) {
- if (existingNlf == null) {
- // no stored filters for this listener; see if they provided a default
- if (si.metaData.containsKey(META_DATA_DEFAULT_FILTER_TYPES)) {
- String typeList =
- si.metaData.get(META_DATA_DEFAULT_FILTER_TYPES).toString();
- if (typeList != null) {
- int types = getTypesFromStringList(typeList);
- NotificationListenerFilter nlf =
- new NotificationListenerFilter(types, new ArraySet<>());
- mRequestedNotificationListeners.put(listener, nlf);
+ Pair<ComponentName, Integer> listener = Pair.create(si.getComponentName(), userId);
+ synchronized (mRequestedNotificationListeners) {
+ NotificationListenerFilter existingNlf =
+ mRequestedNotificationListeners.get(listener);
+ if (si.metaData != null) {
+ if (existingNlf == null) {
+ // no stored filters for this listener; see if they provided a default
+ if (si.metaData.containsKey(META_DATA_DEFAULT_FILTER_TYPES)) {
+ String typeList =
+ si.metaData.get(META_DATA_DEFAULT_FILTER_TYPES).toString();
+ if (typeList != null) {
+ int types = getTypesFromStringList(typeList);
+ NotificationListenerFilter nlf =
+ new NotificationListenerFilter(types, new ArraySet<>());
+ mRequestedNotificationListeners.put(listener, nlf);
+ }
}
}
- }
- // also check the types they never want bridged
- if (si.metaData.containsKey(META_DATA_DISABLED_FILTER_TYPES)) {
- int neverBridge = getTypesFromStringList(si.metaData.get(
- META_DATA_DISABLED_FILTER_TYPES).toString());
- if (neverBridge != 0) {
- NotificationListenerFilter nlf =
- mRequestedNotificationListeners.getOrDefault(
- listener, new NotificationListenerFilter());
- nlf.setTypes(nlf.getTypes() & ~neverBridge);
- mRequestedNotificationListeners.put(listener, nlf);
+ // also check the types they never want bridged
+ if (si.metaData.containsKey(META_DATA_DISABLED_FILTER_TYPES)) {
+ int neverBridge = getTypesFromStringList(si.metaData.get(
+ META_DATA_DISABLED_FILTER_TYPES).toString());
+ if (neverBridge != 0) {
+ NotificationListenerFilter nlf =
+ mRequestedNotificationListeners.getOrDefault(
+ listener, new NotificationListenerFilter());
+ nlf.setTypes(nlf.getTypes() & ~neverBridge);
+ mRequestedNotificationListeners.put(listener, nlf);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index c1171fa..2704f56 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -38,6 +38,7 @@
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.PowerExemptionManager;
@@ -337,7 +338,7 @@
broadcastAllowlist, null /* filterExtrasForReceiver */, null);
// Send to PermissionController for all new users, even if it may not be running for some
// users
- if (isPrivacySafetyLabelChangeNotificationsEnabled()) {
+ if (isPrivacySafetyLabelChangeNotificationsEnabled(mContext)) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
packageName, extras, 0,
mContext.getPackageManager().getPermissionControllerPackageName(),
@@ -389,9 +390,13 @@
}
/** Returns whether the Safety Label Change notification, a privacy feature, is enabled. */
- public static boolean isPrivacySafetyLabelChangeNotificationsEnabled() {
+ public static boolean isPrivacySafetyLabelChangeNotificationsEnabled(Context context) {
+ PackageManager packageManager = context.getPackageManager();
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED, false);
+ SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED, false)
+ && !packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ && !packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+ && !packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH);
}
@NonNull
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 6f7ce80..1aa1fd1 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -4298,6 +4298,11 @@
if (Process.isSdkSandboxUid(uid)) {
uid = getBaseSdkSandboxUid();
}
+ if (Process.isIsolatedUid(uid)
+ && mPermissionManager.getHotwordDetectionServiceProvider() != null
+ && uid == mPermissionManager.getHotwordDetectionServiceProvider().getUid()) {
+ uid = getIsolatedOwner(uid);
+ }
final int callingUserId = UserHandle.getUserId(callingUid);
final int appId = UserHandle.getAppId(uid);
final Object obj = mSettings.getSettingBase(appId);
@@ -4334,6 +4339,11 @@
if (Process.isSdkSandboxUid(uid)) {
uid = getBaseSdkSandboxUid();
}
+ if (Process.isIsolatedUid(uid)
+ && mPermissionManager.getHotwordDetectionServiceProvider() != null
+ && uid == mPermissionManager.getHotwordDetectionServiceProvider().getUid()) {
+ uid = getIsolatedOwner(uid);
+ }
final int appId = UserHandle.getAppId(uid);
final Object obj = mSettings.getSettingBase(appId);
if (obj instanceof SharedUserSetting) {
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 89d2d124..1e0c95c 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -1143,22 +1143,22 @@
// behavior.
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
"MinInstallableTargetSdk__install_block_enabled",
- true)) {
+ false)) {
int minInstallableTargetSdk =
DeviceConfig.getInt(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
"MinInstallableTargetSdk__min_installable_target_sdk",
- PackageManagerService.MIN_INSTALLABLE_TARGET_SDK);
+ 0);
// Determine if enforcement is in strict mode
boolean strictMode = false;
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
"MinInstallableTargetSdk__install_block_strict_mode_enabled",
- true)) {
+ false)) {
if (parsedPackage.getTargetSdkVersion()
< DeviceConfig.getInt(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
"MinInstallableTargetSdk__strict_mode_target_sdk",
- PackageManagerService.MIN_INSTALLABLE_TARGET_SDK)) {
+ 0)) {
strictMode = true;
}
}
@@ -2954,7 +2954,7 @@
}
// Send to PermissionController for all update users, even if it may not be running
// for some users
- if (BroadcastHelper.isPrivacySafetyLabelChangeNotificationsEnabled()) {
+ if (BroadcastHelper.isPrivacySafetyLabelChangeNotificationsEnabled(mContext)) {
mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
extras, 0 /*flags*/,
mPm.mRequiredPermissionControllerPackage, null /*finishedReceiver*/,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ccade60..b5108af 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -560,14 +560,6 @@
// How many required verifiers can be on the system.
private static final int REQUIRED_VERIFIERS_MAX_COUNT = 2;
- /**
- * Specifies the minimum target SDK version an apk must specify in order to be installed
- * on the system. This improves security and privacy by blocking low
- * target sdk apps as malware can target older sdk versions to avoid
- * the enforcement of new API behavior.
- */
- public static final int MIN_INSTALLABLE_TARGET_SDK = Build.VERSION_CODES.M;
-
// Compilation reasons.
// TODO(b/260124949): Clean this up with the legacy dexopt code.
public static final int REASON_FIRST_BOOT = 0;
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index 5015985..7198de2 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -353,11 +353,10 @@
PackageInfoLite pkgLite,
PackageVerificationState verificationState) {
- // TODO: http://b/22976637
- // Apps installed for "all" users use the device owner to verify the app
+ // Apps installed for "all" users use the current user to verify the app
UserHandle verifierUser = getUser();
if (verifierUser == UserHandle.ALL) {
- verifierUser = UserHandle.SYSTEM;
+ verifierUser = UserHandle.of(mPm.mUserManager.getCurrentUserId());
}
final int verifierUserId = verifierUser.getIdentifier();
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 8640377..0ce17de 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -751,6 +751,8 @@
return pullPendingIntentsPerPackage(atomTag, data);
case FrameworkStatsLog.HDR_CAPABILITIES:
return pullHdrCapabilities(atomTag, data);
+ case FrameworkStatsLog.CACHED_APPS_HIGH_WATERMARK:
+ return pullCachedAppsHighWatermark(atomTag, data);
default:
throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
}
@@ -951,6 +953,7 @@
registerPendingIntentsPerPackagePuller();
registerPinnerServiceStats();
registerHdrCapabilitiesPuller();
+ registerCachedAppsHighWatermarkPuller();
}
private void initAndRegisterNetworkStatsPullers() {
@@ -4732,6 +4735,12 @@
return StatsManager.PULL_SUCCESS;
}
+ private int pullCachedAppsHighWatermark(int atomTag, List<StatsEvent> pulledData) {
+ pulledData.add(LocalServices.getService(ActivityManagerInternal.class)
+ .getCachedAppsHighWatermarkStats(atomTag, true));
+ return StatsManager.PULL_SUCCESS;
+ }
+
private boolean hasDolbyVisionIssue(Display display) {
AtomicInteger modesSupportingDolbyVision = new AtomicInteger();
Arrays.stream(display.getSupportedModes())
@@ -4777,6 +4786,16 @@
);
}
+ private void registerCachedAppsHighWatermarkPuller() {
+ final int tagId = FrameworkStatsLog.CACHED_APPS_HIGH_WATERMARK;
+ mStatsManager.setPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ DIRECT_EXECUTOR,
+ mStatsCallbackImpl
+ );
+ }
+
int pullSystemServerPinnerStats(int atomTag, List<StatsEvent> pulledData) {
PinnerService pinnerService = LocalServices.getService(PinnerService.class);
List<PinnedFileStats> pinnedFileStats = pinnerService.dumpDataForStatsd();
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 1ab7f362..9cf0834 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -124,6 +124,7 @@
private static final Set<Integer> SYSTEM_VIBRATION_SCREEN_OFF_USAGE_ALLOWLIST = new HashSet<>(
Arrays.asList(
USAGE_TOUCH,
+ USAGE_ACCESSIBILITY,
USAGE_PHYSICAL_EMULATION,
USAGE_HARDWARE_FEEDBACK));
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index a757d90..f71f3b1 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -397,9 +397,21 @@
/** Returns {@code true} if the incoming activity can belong to this transition. */
boolean canCoalesce(ActivityRecord r) {
- return mLastLaunchedActivity.mDisplayContent == r.mDisplayContent
- && mLastLaunchedActivity.getTask().getBounds().equals(r.getTask().getBounds())
- && mLastLaunchedActivity.getWindowingMode() == r.getWindowingMode();
+ if (mLastLaunchedActivity.mDisplayContent != r.mDisplayContent
+ || mLastLaunchedActivity.getWindowingMode() != r.getWindowingMode()) {
+ return false;
+ }
+ // The current task should be non-null because it is just launched. While the
+ // last task can be cleared when starting activity with FLAG_ACTIVITY_CLEAR_TASK.
+ final Task lastTask = mLastLaunchedActivity.getTask();
+ final Task currentTask = r.getTask();
+ if (lastTask != null && currentTask != null) {
+ if (lastTask == currentTask) {
+ return true;
+ }
+ return lastTask.getBounds().equals(currentTask.getBounds());
+ }
+ return mLastLaunchedActivity.isUid(r.launchedFromUid);
}
/** @return {@code true} if the activity matches a launched activity in this transition. */
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f5cb613..c6a2e0e 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2821,6 +2821,27 @@
}
}
+ @Override
+ void waitForSyncTransactionCommit(ArraySet<WindowContainer> wcAwaitingCommit) {
+ super.waitForSyncTransactionCommit(wcAwaitingCommit);
+ if (mStartingData != null) {
+ mStartingData.mWaitForSyncTransactionCommit = true;
+ }
+ }
+
+ @Override
+ void onSyncTransactionCommitted(SurfaceControl.Transaction t) {
+ super.onSyncTransactionCommitted(t);
+ if (mStartingData == null) {
+ return;
+ }
+ mStartingData.mWaitForSyncTransactionCommit = false;
+ if (mStartingData.mRemoveAfterTransaction) {
+ mStartingData.mRemoveAfterTransaction = false;
+ removeStartingWindowAnimation(mStartingData.mPrepareRemoveAnimation);
+ }
+ }
+
void removeStartingWindowAnimation(boolean prepareAnimation) {
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE;
if (task != null) {
@@ -2843,6 +2864,12 @@
final WindowState startingWindow = mStartingWindow;
final boolean animate;
if (mStartingData != null) {
+ if (mStartingData.mWaitForSyncTransactionCommit
+ || mTransitionController.inCollectingTransition(startingWindow)) {
+ mStartingData.mRemoveAfterTransaction = true;
+ mStartingData.mPrepareRemoveAnimation = prepareAnimation;
+ return;
+ }
animate = prepareAnimation && mStartingData.needRevealAnimation()
&& mStartingWindow.isVisibleByPolicy();
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Schedule remove starting %s startingWindow=%s"
@@ -2863,18 +2890,7 @@
this);
return;
}
-
- if (animate && mTransitionController.inCollectingTransition(startingWindow)) {
- // Defer remove starting window after transition start.
- // The surface of app window could really show after the transition finish.
- startingWindow.mSyncTransaction.addTransactionCommittedListener(Runnable::run, () -> {
- synchronized (mAtmService.mGlobalLock) {
- surface.remove(true);
- }
- });
- } else {
- surface.remove(animate);
- }
+ surface.remove(animate);
}
/**
@@ -5315,6 +5331,13 @@
if (finishing || isState(STOPPED)) {
displayContent.mUnknownAppVisibilityController.appRemovedOrHidden(this);
}
+ // Because starting window was transferred, this activity may be a trampoline which has
+ // been occluded by next activity. If it has added windows, set client visibility
+ // immediately to avoid the client getting RELAYOUT_RES_FIRST_TIME from relayout and
+ // drawing an unnecessary frame.
+ if (startingMoved && !firstWindowDrawn && hasChild()) {
+ setClientVisible(false);
+ }
} else {
if (!appTransition.isTransitionSet()
&& appTransition.isReady()) {
diff --git a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
index 5f56af7..1208b6ef 100644
--- a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
+++ b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
@@ -99,13 +99,15 @@
}
public void forEachConnection(Consumer<T> consumer) {
+ final ArraySet<T> connections;
synchronized (mActivity) {
if (mConnections == null || mConnections.isEmpty()) {
return;
}
- for (int i = mConnections.size() - 1; i >= 0; i--) {
- consumer.accept(mConnections.valueAt(i));
- }
+ connections = new ArraySet<>(mConnections);
+ }
+ for (int i = connections.size() - 1; i >= 0; i--) {
+ consumer.accept(connections.valueAt(i));
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 1944b3f..90af4c6 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -23,7 +23,6 @@
import static android.app.PendingIntent.FLAG_ONE_SHOT;
import static android.app.admin.DevicePolicyManager.EXTRA_RESTRICTION;
import static android.app.admin.DevicePolicyManager.POLICY_SUSPEND_PACKAGES;
-import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.Intent.EXTRA_INTENT;
import static android.content.Intent.EXTRA_PACKAGE_NAME;
@@ -503,8 +502,7 @@
@ActivityInterceptorCallback.OrderedId int orderId,
@NonNull ActivityInterceptorCallback.ActivityInterceptorInfo info) {
if (orderId == MAINLINE_SDK_SANDBOX_ORDER_ID) {
- return info.getIntent() != null && info.getIntent().getAction() != null
- && info.getIntent().getAction().equals(ACTION_START_SANDBOXED_ACTIVITY);
+ return info.getIntent() != null && info.getIntent().isSandboxActivity(mServiceContext);
}
return true;
}
@@ -513,8 +511,7 @@
@ActivityInterceptorCallback.OrderedId int orderId,
@NonNull ActivityInterceptorCallback.ActivityInterceptorInfo info) {
if (orderId == MAINLINE_SDK_SANDBOX_ORDER_ID) {
- return info.getIntent() != null && info.getIntent().getAction() != null
- && info.getIntent().getAction().equals(ACTION_START_SANDBOXED_ACTIVITY);
+ return info.getIntent() != null && info.getIntent().isSandboxActivity(mServiceContext);
}
return true;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c5e75fa..a27f3e4 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2926,8 +2926,7 @@
// If the matching task is already in the adjacent task of the launch target. Adjust to use
// the adjacent task as its launch target. So the existing task will be launched into the
// closer one and won't be reparent redundantly.
- final Task adjacentTargetTask = mTargetRootTask.getAdjacentTaskFragment() != null
- ? mTargetRootTask.getAdjacentTaskFragment().asTask() : null;
+ final Task adjacentTargetTask = mTargetRootTask.getAdjacentTask();
if (adjacentTargetTask != null && intentActivity.isDescendantOf(adjacentTargetTask)) {
mTargetRootTask = adjacentTargetTask;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 401c16f..f93afe8 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -38,7 +38,6 @@
import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
@@ -1244,9 +1243,7 @@
assertPackageMatchesCallingUid(callingPackage);
enforceNotIsolatedCaller("startActivityAsUser");
- boolean isSandboxedActivity = (intent != null && intent.getAction() != null
- && intent.getAction().equals(ACTION_START_SANDBOXED_ACTIVITY));
- if (isSandboxedActivity) {
+ if (intent != null && intent.isSandboxActivity(mContext)) {
SdkSandboxManagerLocal sdkSandboxManagerLocal = LocalManagerRegistry.getManager(
SdkSandboxManagerLocal.class);
sdkSandboxManagerLocal.enforceAllowedToHostSandboxedActivity(
@@ -2011,7 +2008,8 @@
return;
}
- if (r.isState(RESUMED) && r == mRootWindowContainer.getTopResumedActivity()) {
+ if ((touchedActivity == null || r == touchedActivity) && r.isState(RESUMED)
+ && r == mRootWindowContainer.getTopResumedActivity()) {
setLastResumedActivityUncheckLocked(r, "setFocusedTask-alreadyTop");
return;
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 597c8bf..805bff2 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -1030,12 +1030,11 @@
canPromote = false;
}
- // If the current window container is task and it have adjacent task, it means
- // both tasks will open or close app toghther but we want get their opening or
- // closing animation target independently so do not promote.
+ // If the current window container is a task with adjacent task set, the both
+ // adjacent tasks will be opened or closed together. To get their opening or
+ // closing animation target independently, skip promoting their animation targets.
if (current.asTask() != null
- && current.asTask().getAdjacentTaskFragment() != null
- && current.asTask().getAdjacentTaskFragment().asTask() != null) {
+ && current.asTask().getAdjacentTask() != null) {
canPromote = false;
}
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 11d84ff..0c196d7 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -227,6 +227,7 @@
backType = BackNavigationInfo.TYPE_CALLBACK;
}
infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback());
+ infoBuilder.setAnimationCallback(callbackInfo.isAnimationCallback());
mNavigationMonitor.startMonitor(window, navigationObserver);
}
diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
index a83a033..3dc3be9 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
@@ -135,12 +135,6 @@
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register display organizer=%s uid=%d",
organizer.asBinder(), uid);
if (mOrganizersByFeatureIds.get(feature) != null) {
- if (mOrganizersByFeatureIds.get(feature).mOrganizer.asBinder()
- .isBinderAlive()) {
- throw new IllegalStateException(
- "Replacing existing organizer currently unsupported");
- }
-
mOrganizersByFeatureIds.remove(feature).destroy();
Slog.d(TAG, "Replacing dead organizer for feature=" + feature);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c2bc459..bad64d3 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -656,6 +656,14 @@
*/
private InputTarget mLastImeInputTarget;
+
+ /**
+ * Tracks the windowToken of the input method input target and the corresponding
+ * {@link WindowContainerListener} for monitoring changes (e.g. the requested visibility
+ * change).
+ */
+ private @Nullable Pair<IBinder, WindowContainerListener> mImeTargetTokenListenerPair;
+
/**
* This controls the visibility and animation of the input method window.
*/
@@ -4267,7 +4275,38 @@
@VisibleForTesting
void setImeInputTarget(InputTarget target) {
+ if (mImeTargetTokenListenerPair != null) {
+ // Unregister the listener before changing to the new IME input target.
+ final WindowToken oldToken = mTokenMap.get(mImeTargetTokenListenerPair.first);
+ if (oldToken != null) {
+ oldToken.unregisterWindowContainerListener(mImeTargetTokenListenerPair.second);
+ }
+ mImeTargetTokenListenerPair = null;
+ }
mImeInputTarget = target;
+ // Notify listeners about IME input target window visibility by the target change.
+ if (target != null) {
+ // TODO(b/276743705): Let InputTarget register the visibility change of the hierarchy.
+ final WindowState targetWin = target.getWindowState();
+ if (targetWin != null) {
+ mImeTargetTokenListenerPair = new Pair<>(targetWin.mToken.token,
+ new WindowContainerListener() {
+ @Override
+ public void onVisibleRequestedChanged(boolean isVisibleRequested) {
+ // Notify listeners for IME input target window visibility change
+ // requested by the parent container.
+ mWmService.dispatchImeInputTargetVisibilityChanged(
+ targetWin.mClient.asBinder(), isVisibleRequested,
+ targetWin.mActivityRecord != null
+ && targetWin.mActivityRecord.finishing);
+ }
+ });
+ targetWin.mToken.registerWindowContainerListener(
+ mImeTargetTokenListenerPair.second);
+ mWmService.dispatchImeInputTargetVisibilityChanged(targetWin.mClient.asBinder(),
+ targetWin.isVisible() /* visible */, false /* removed */);
+ }
+ }
if (refreshImeSecureFlag(getPendingTransaction())) {
mWmService.requestTraversal();
}
@@ -4433,6 +4472,10 @@
}
private void attachImeScreenshotOnTarget(WindowState imeTarget) {
+ attachImeScreenshotOnTarget(imeTarget, false);
+ }
+
+ private void attachImeScreenshotOnTarget(WindowState imeTarget, boolean hideImeWindow) {
final SurfaceControl.Transaction t = getPendingTransaction();
// Remove the obsoleted IME snapshot first in case the new snapshot happens to
// override the current one before the transition finish and the surface never be
@@ -4441,6 +4484,11 @@
mImeScreenshot = new ImeScreenshot(
mWmService.mSurfaceControlFactory.apply(null), imeTarget);
mImeScreenshot.attachAndShow(t);
+ if (mInputMethodWindow != null && hideImeWindow) {
+ // Hide the IME window when deciding to show IME snapshot on demand.
+ // InsetsController will make IME visible again before animating it.
+ mInputMethodWindow.hide(false, false);
+ }
}
/**
@@ -4458,7 +4506,7 @@
*/
@VisibleForTesting
void showImeScreenshot(WindowState imeTarget) {
- attachImeScreenshotOnTarget(imeTarget);
+ attachImeScreenshotOnTarget(imeTarget, true /* hideImeWindow */);
}
/**
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 339b6ec..747819e9 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.view.Display.TYPE_INTERNAL;
import static android.view.InsetsFrameProvider.SOURCE_ARBITRARY_RECTANGLE;
import static android.view.InsetsFrameProvider.SOURCE_CONTAINER_BOUNDS;
@@ -2208,16 +2207,15 @@
private int updateSystemBarsLw(WindowState win, int disableFlags) {
final TaskDisplayArea defaultTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
- final boolean multiWindowTaskVisible =
+ final boolean adjacentTasksVisible =
defaultTaskDisplayArea.getRootTask(task -> task.isVisible()
- && task.getTopLeafTask().getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW)
+ && task.getTopLeafTask().getAdjacentTask() != null)
!= null;
final boolean freeformRootTaskVisible =
defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_FREEFORM);
- // We need to force showing system bars when the multi-window or freeform root task is
- // visible.
- mForceShowSystemBars = multiWindowTaskVisible || freeformRootTaskVisible;
+ // We need to force showing system bars when adjacent tasks or freeform roots visible.
+ mForceShowSystemBars = adjacentTasksVisible || freeformRootTaskVisible;
// We need to force the consumption of the system bars if they are force shown or if they
// are controlled by a remote insets controller.
mForceConsumeSystemBars = mForceShowSystemBars
@@ -2238,7 +2236,7 @@
int appearance = APPEARANCE_OPAQUE_NAVIGATION_BARS | APPEARANCE_OPAQUE_STATUS_BARS;
appearance = configureStatusBarOpacity(appearance);
- appearance = configureNavBarOpacity(appearance, multiWindowTaskVisible,
+ appearance = configureNavBarOpacity(appearance, adjacentTasksVisible,
freeformRootTaskVisible);
// Show immersive mode confirmation if needed.
diff --git a/services/core/java/com/android/server/wm/ImeTargetChangeListener.java b/services/core/java/com/android/server/wm/ImeTargetChangeListener.java
new file mode 100644
index 0000000..8bc445b
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ImeTargetChangeListener.java
@@ -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.server.wm;
+
+import android.annotation.NonNull;
+import android.os.IBinder;
+
+/**
+ * Callback the IME targeting window visibility change state for
+ * {@link com.android.server.inputmethod.InputMethodManagerService} to manage the IME surface
+ * visibility and z-ordering.
+ */
+public interface ImeTargetChangeListener {
+ /**
+ * Called when a non-IME-focusable overlay window being the IME layering target (e.g. a
+ * window with {@link android.view.WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} and
+ * {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} flags)
+ * has changed its window visibility.
+ *
+ * @param overlayWindowToken the window token of the overlay window.
+ * @param visible the visibility of the overlay window, {@code true} means visible
+ * and {@code false} otherwise.
+ * @param removed Whether the IME target overlay window has being removed.
+ */
+ default void onImeTargetOverlayVisibilityChanged(@NonNull IBinder overlayWindowToken,
+ boolean visible, boolean removed) {
+ }
+
+ /**
+ * Called when the visibility of IME input target window has changed.
+ *
+ * @param imeInputTarget the window token of the IME input target window.
+ * @param visible the new window visibility made by {@param imeInputTarget}. visible is
+ * {@code true} when switching to the new visible IME input target
+ * window and started input, or the same input target relayout to
+ * visible from invisible. In contrast, visible is {@code false} when
+ * closing the input target, or the same input target relayout to
+ * invisible from visible.
+ * @param removed Whether the IME input target window has being removed.
+ */
+ default void onImeInputTargetVisibilityChanged(@NonNull IBinder imeInputTarget, boolean visible,
+ boolean removed) {
+ }
+}
diff --git a/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java b/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java
index 71dd917..1d9f24c 100644
--- a/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java
+++ b/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java
@@ -19,6 +19,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
import android.view.WindowManager;
@@ -36,16 +37,15 @@
* @param displayId A unique id to identify the display.
* @return {@code true} if success, {@code false} otherwise.
*/
- public abstract boolean showImeScreenShot(IBinder imeTarget, int displayId);
+ public abstract boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId);
/**
- * Updates the IME parent for target window.
+ * Removes the IME screenshot on the given display.
*
- * @param imeTarget The target window to update the IME parent.
- * @param displayId A unique id to identify the display.
+ * @param displayId The target display of showing IME screenshot.
* @return {@code true} if success, {@code false} otherwise.
*/
- public abstract boolean updateImeParent(IBinder imeTarget, int displayId);
+ public abstract boolean removeImeScreenshot(int displayId);
/**
* Called when {@link DisplayContent#computeImeParent()} to check if it's valid to keep
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index 300a894..cff86ad 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -41,6 +41,26 @@
/** Whether the starting window is drawn. */
boolean mIsDisplayed;
+ /**
+ * For Shell transition.
+ * There will be a transition happen on attached activity, do not remove starting window during
+ * this period, because the transaction to show app window may not apply before remove starting
+ * window.
+ * Note this isn't equal to transition playing, the period should be
+ * Sync finishNow -> Start transaction apply.
+ */
+ boolean mWaitForSyncTransactionCommit;
+
+ /**
+ * For Shell transition.
+ * This starting window should be removed after applying the start transaction of transition,
+ * which ensures the app window has shown.
+ */
+ boolean mRemoveAfterTransaction;
+
+ /** Whether to prepare the removal animation. */
+ boolean mPrepareRemoveAnimation;
+
protected StartingData(WindowManagerService service, int typeParams) {
mService = service;
mTypeParams = typeParams;
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 2e5ab1a..7572a64 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -221,11 +221,11 @@
if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
mChoreographer.postFrameCallback(this::startAnimations);
}
-
- // Some animations (e.g. move animations) require the initial transform to be
- // applied immediately.
- applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
}
+
+ // Some animations (e.g. move animations) require the initial transform to be
+ // applied immediately.
+ applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 9363eb5..5c33e64 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2362,6 +2362,22 @@
return parentTask == null ? null : parentTask.getCreatedByOrganizerTask();
}
+ /** @return the first adjacent task of this task or its parent. */
+ @Nullable
+ Task getAdjacentTask() {
+ final TaskFragment adjacentTaskFragment = getAdjacentTaskFragment();
+ if (adjacentTaskFragment != null && adjacentTaskFragment.asTask() != null) {
+ return adjacentTaskFragment.asTask();
+ }
+
+ final WindowContainer parent = getParent();
+ if (parent == null || parent.asTask() == null) {
+ return null;
+ }
+
+ return parent.asTask().getAdjacentTask();
+ }
+
// TODO(task-merge): Figure out what's the right thing to do for places that used it.
boolean isRootTask() {
return getRootTask() == this;
@@ -2747,7 +2763,7 @@
Rect outSurfaceInsets) {
// If this task has its adjacent task, it means they should animate together. Use display
// bounds for them could move same as full screen task.
- if (getAdjacentTaskFragment() != null && getAdjacentTaskFragment().asTask() != null) {
+ if (getAdjacentTask() != null) {
super.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets);
return;
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index b0a879e..e80cbb3 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1081,12 +1081,12 @@
if (sourceTask != null && sourceTask == candidateTask) {
// Do nothing when task that is getting opened is same as the source.
} else if (sourceTask != null
- && mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment() != null
+ && mLaunchAdjacentFlagRootTask.getAdjacentTask() != null
&& (sourceTask == mLaunchAdjacentFlagRootTask
|| sourceTask.isDescendantOf(mLaunchAdjacentFlagRootTask))) {
// If the adjacent launch is coming from the same root, launch to
// adjacent root instead.
- return mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment().asTask();
+ return mLaunchAdjacentFlagRootTask.getAdjacentTask();
} else {
return mLaunchAdjacentFlagRootTask;
}
@@ -1095,10 +1095,8 @@
for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) {
if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) {
final Task launchRootTask = mLaunchRootTasks.get(i).task;
- final TaskFragment adjacentTaskFragment = launchRootTask != null
- ? launchRootTask.getAdjacentTaskFragment() : null;
- final Task adjacentRootTask =
- adjacentTaskFragment != null ? adjacentTaskFragment.asTask() : null;
+ final Task adjacentRootTask = launchRootTask != null
+ ? launchRootTask.getAdjacentTask() : null;
if (sourceTask != null && adjacentRootTask != null
&& (sourceTask == adjacentRootTask
|| sourceTask.isDescendantOf(adjacentRootTask))) {
@@ -1116,16 +1114,14 @@
// A pinned task relaunching should be handled by its task organizer. Skip fallback
// launch target of a pinned task from source task.
|| candidateTask.getWindowingMode() != WINDOWING_MODE_PINNED)) {
- Task launchTarget = sourceTask.getCreatedByOrganizerTask();
- if (launchTarget != null && launchTarget.getAdjacentTaskFragment() != null) {
- if (candidateTask != null) {
- final Task candidateRoot = candidateTask.getCreatedByOrganizerTask();
- if (candidateRoot != null && candidateRoot != launchTarget
- && launchTarget == candidateRoot.getAdjacentTaskFragment()) {
- launchTarget = candidateRoot;
- }
+ final Task adjacentTarget = sourceTask.getAdjacentTask();
+ if (adjacentTarget != null) {
+ if (candidateTask != null
+ && (candidateTask == adjacentTarget
+ || candidateTask.isDescendantOf(adjacentTarget))) {
+ return adjacentTarget;
}
- return launchTarget;
+ return sourceTask.getCreatedByOrganizerTask();
}
}
diff --git a/services/core/java/com/android/server/wm/TaskFpsCallbackController.java b/services/core/java/com/android/server/wm/TaskFpsCallbackController.java
index c099628..8c79875 100644
--- a/services/core/java/com/android/server/wm/TaskFpsCallbackController.java
+++ b/services/core/java/com/android/server/wm/TaskFpsCallbackController.java
@@ -26,8 +26,8 @@
final class TaskFpsCallbackController {
private final Context mContext;
- private final HashMap<ITaskFpsCallback, Long> mTaskFpsCallbacks;
- private final HashMap<ITaskFpsCallback, IBinder.DeathRecipient> mDeathRecipients;
+ private final HashMap<IBinder, Long> mTaskFpsCallbacks;
+ private final HashMap<IBinder, IBinder.DeathRecipient> mDeathRecipients;
TaskFpsCallbackController(Context context) {
mContext = context;
@@ -36,32 +36,42 @@
}
void registerListener(int taskId, ITaskFpsCallback callback) {
- if (mTaskFpsCallbacks.containsKey(callback)) {
+ if (callback == null) {
+ return;
+ }
+
+ IBinder binder = callback.asBinder();
+ if (mTaskFpsCallbacks.containsKey(binder)) {
return;
}
final long nativeListener = nativeRegister(callback, taskId);
- mTaskFpsCallbacks.put(callback, nativeListener);
+ mTaskFpsCallbacks.put(binder, nativeListener);
final IBinder.DeathRecipient deathRecipient = () -> unregisterListener(callback);
try {
- callback.asBinder().linkToDeath(deathRecipient, 0);
- mDeathRecipients.put(callback, deathRecipient);
+ binder.linkToDeath(deathRecipient, 0);
+ mDeathRecipients.put(binder, deathRecipient);
} catch (RemoteException e) {
// ignore
}
}
void unregisterListener(ITaskFpsCallback callback) {
- if (!mTaskFpsCallbacks.containsKey(callback)) {
+ if (callback == null) {
return;
}
- callback.asBinder().unlinkToDeath(mDeathRecipients.get(callback), 0);
- mDeathRecipients.remove(callback);
+ IBinder binder = callback.asBinder();
+ if (!mTaskFpsCallbacks.containsKey(binder)) {
+ return;
+ }
- nativeUnregister(mTaskFpsCallbacks.get(callback));
- mTaskFpsCallbacks.remove(callback);
+ binder.unlinkToDeath(mDeathRecipients.get(binder), 0);
+ mDeathRecipients.remove(binder);
+
+ nativeUnregister(mTaskFpsCallbacks.get(binder));
+ mTaskFpsCallbacks.remove(binder);
}
private static native long nativeRegister(ITaskFpsCallback callback, int taskId);
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 184293e..5626aa7 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -681,6 +681,7 @@
final StartingWindowRemovalInfo removalInfo = new StartingWindowRemovalInfo();
removalInfo.taskId = task.mTaskId;
removalInfo.playRevealAnimation = prepareAnimation
+ && task.getDisplayContent() != null
&& task.getDisplayInfo().state == Display.STATE_ON;
final boolean playShiftUpAnimation = !task.inMultiWindowMode();
final ActivityRecord topActivity = task.topActivityContainsStartingWindow();
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 969afe5..4922523 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -848,6 +848,16 @@
}
/**
+ * Sets by the {@link com.android.server.inputmethod.InputMethodManagerService} to monitor
+ * the visibility change of the IME targeted windows.
+ *
+ * @see ImeTargetChangeListener#onImeTargetOverlayVisibilityChanged
+ * @see ImeTargetChangeListener#onImeInputTargetVisibilityChanged
+ */
+ public abstract void setInputMethodTargetChangeListener(
+ @NonNull ImeTargetChangeListener listener);
+
+ /**
* Moves the {@link WindowToken} {@code binder} to the display specified by {@code displayId}.
*/
public abstract void moveWindowTokenToDisplay(IBinder binder, int displayId);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 99d0ea8..62b3c7c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -723,6 +723,9 @@
boolean mHardKeyboardAvailable;
WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener;
+
+ @Nullable ImeTargetChangeListener mImeTargetChangeListener;
+
SettingsObserver mSettingsObserver;
final EmbeddedWindowController mEmbeddedWindowController;
final AnrController mAnrController;
@@ -1807,6 +1810,10 @@
if (imMayMove) {
displayContent.computeImeTarget(true /* updateImeTarget */);
+ if (win.isImeOverlayLayeringTarget()) {
+ dispatchImeTargetOverlayVisibilityChanged(client.asBinder(),
+ win.isVisibleRequestedOrAdding(), false /* removed */);
+ }
}
// Don't do layout here, the window must call
@@ -2328,6 +2335,8 @@
winAnimator.mSurfaceController.setSecure(win.isSecureLocked());
}
+ final boolean wasVisible = win.isVisible();
+
win.mRelayoutCalled = true;
win.mInRelayout = true;
@@ -2336,7 +2345,6 @@
"Relayout %s: oldVis=%d newVis=%d. %s", win, oldVisibility,
viewVisibility, new RuntimeException().fillInStackTrace());
-
win.setDisplayLayoutNeeded();
win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
@@ -2501,6 +2509,18 @@
}
win.mInRelayout = false;
+ final boolean winVisibleChanged = win.isVisible() != wasVisible;
+ if (win.isImeOverlayLayeringTarget() && winVisibleChanged) {
+ dispatchImeTargetOverlayVisibilityChanged(client.asBinder(),
+ win.isVisible(), false /* removed */);
+ }
+ // Notify listeners about IME input target window visibility change.
+ final boolean isImeInputTarget = win.getDisplayContent().getImeInputTarget() == win;
+ if (isImeInputTarget && winVisibleChanged) {
+ dispatchImeInputTargetVisibilityChanged(win.mClient.asBinder(),
+ win.isVisible() /* visible */, false /* removed */);
+ }
+
if (outSyncIdBundle != null) {
final int maybeSyncSeqId;
if (USE_BLAST_SYNC && win.useBLASTSync() && viewVisibility == View.VISIBLE
@@ -3325,6 +3345,30 @@
});
}
+ void dispatchImeTargetOverlayVisibilityChanged(@NonNull IBinder token, boolean visible,
+ boolean removed) {
+ if (mImeTargetChangeListener != null) {
+ if (DEBUG_INPUT_METHOD) {
+ Slog.d(TAG, "onImeTargetOverlayVisibilityChanged, win=" + mWindowMap.get(token)
+ + "visible=" + visible + ", removed=" + removed);
+ }
+ mH.post(() -> mImeTargetChangeListener.onImeTargetOverlayVisibilityChanged(token,
+ visible, removed));
+ }
+ }
+
+ void dispatchImeInputTargetVisibilityChanged(@NonNull IBinder token, boolean visible,
+ boolean removed) {
+ if (mImeTargetChangeListener != null) {
+ if (DEBUG_INPUT_METHOD) {
+ Slog.d(TAG, "onImeInputTargetVisibilityChanged, win=" + mWindowMap.get(token)
+ + "visible=" + visible + ", removed=" + removed);
+ }
+ mH.post(() -> mImeTargetChangeListener.onImeInputTargetVisibilityChanged(token,
+ visible, removed));
+ }
+ }
+
@Override
public void setSwitchingUser(boolean switching) {
if (!checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
@@ -8262,13 +8306,19 @@
}
return null;
}
+
+ @Override
+ public void setInputMethodTargetChangeListener(@NonNull ImeTargetChangeListener listener) {
+ synchronized (mGlobalLock) {
+ mImeTargetChangeListener = listener;
+ }
+ }
}
private final class ImeTargetVisibilityPolicyImpl extends ImeTargetVisibilityPolicy {
- // TODO(b/258048231): Track IME visibility change in bugreport when invocations.
@Override
- public boolean showImeScreenShot(@NonNull IBinder imeTarget, int displayId) {
+ public boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId) {
synchronized (mGlobalLock) {
final WindowState imeTargetWindow = mWindowMap.get(imeTarget);
if (imeTargetWindow == null) {
@@ -8284,24 +8334,18 @@
return true;
}
}
-
- // TODO(b/258048231): Track IME visibility change in bugreport when invocations.
@Override
- public boolean updateImeParent(@NonNull IBinder imeTarget, int displayId) {
+ public boolean removeImeScreenshot(int displayId) {
synchronized (mGlobalLock) {
- final WindowState imeTargetWindow = mWindowMap.get(imeTarget);
- if (imeTargetWindow == null) {
- return false;
- }
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc == null) {
- Slog.w(TAG, "Invalid displayId:" + displayId + ", fail to update ime parent");
+ Slog.w(TAG, "Invalid displayId:" + displayId
+ + ", fail to remove ime screenshot");
return false;
}
-
- dc.updateImeParent();
- return true;
+ dc.removeImeSurfaceImmediately();
}
+ return true;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d1618e9..a299592 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -166,6 +166,7 @@
import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY;
import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
import static com.android.server.wm.WindowStateProto.KEEP_CLEAR_AREAS;
+import static com.android.server.wm.WindowStateProto.MERGED_LOCAL_INSETS_SOURCES;
import static com.android.server.wm.WindowStateProto.PENDING_SEAMLESS_ROTATION;
import static com.android.server.wm.WindowStateProto.REMOVED;
import static com.android.server.wm.WindowStateProto.REMOVE_ON_EXIT;
@@ -353,6 +354,7 @@
// overlay window is hidden because the owning app is suspended
private boolean mHiddenWhileSuspended;
private boolean mAppOpVisibility = true;
+
boolean mPermanentlyHidden; // the window should never be shown again
// This is a non-system overlay window that is currently force hidden.
private boolean mForceHideNonSystemOverlayWindow;
@@ -2349,6 +2351,10 @@
}
super.removeImmediately();
+ if (isImeOverlayLayeringTarget()) {
+ mWmService.dispatchImeTargetOverlayVisibilityChanged(mClient.asBinder(),
+ false /* visible */, true /* removed */);
+ }
final DisplayContent dc = getDisplayContent();
if (isImeLayeringTarget()) {
// Remove the attached IME screenshot surface.
@@ -2359,6 +2365,8 @@
dc.computeImeTarget(true /* updateImeTarget */);
}
if (dc.getImeInputTarget() == this && !inRelaunchingActivity()) {
+ mWmService.dispatchImeInputTargetVisibilityChanged(mClient.asBinder(),
+ false /* visible */, true /* removed */);
dc.updateImeInputAndControlTarget(null);
}
@@ -4027,6 +4035,11 @@
for (Rect r : mUnrestrictedKeepClearAreas) {
r.dumpDebug(proto, UNRESTRICTED_KEEP_CLEAR_AREAS);
}
+ if (mMergedLocalInsetsSources != null) {
+ for (int i = 0; i < mMergedLocalInsetsSources.size(); ++i) {
+ mMergedLocalInsetsSources.valueAt(i).dumpDebug(proto, MERGED_LOCAL_INSETS_SOURCES);
+ }
+ }
proto.end(token);
}
@@ -5493,6 +5506,14 @@
return getDisplayContent().getImeTarget(IME_TARGET_LAYERING) == this;
}
+ /**
+ * Whether the window is non-focusable IME overlay layering target.
+ */
+ boolean isImeOverlayLayeringTarget() {
+ return isImeLayeringTarget()
+ && (mAttrs.flags & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0;
+ }
+
WindowState getImeLayeringTarget() {
final InsetsControlTarget target = getDisplayContent().getImeTarget(IME_TARGET_LAYERING);
return target != null ? target.getWindow() : null;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BundlePolicySerializer.java b/services/devicepolicy/java/com/android/server/devicepolicy/BundlePolicySerializer.java
index ee73f8a..82f9aad 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BundlePolicySerializer.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BundlePolicySerializer.java
@@ -17,50 +17,28 @@
package com.android.server.devicepolicy;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.admin.BundlePolicyValue;
import android.app.admin.PackagePolicyKey;
import android.app.admin.PolicyKey;
import android.os.Bundle;
-import android.os.Environment;
import android.os.Parcelable;
-import android.util.AtomicFile;
-import android.util.Slog;
-import android.util.Xml;
+import android.util.Log;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
-import libcore.io.IoUtils;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Objects;
-// TODO(b/266704763): clean this up and stop creating separate files for each value, the code here
-// is copied from UserManagerService, however it doesn't currently handle setting different
-// restrictions for the same package in different users, it also will not remove the files for
-// outdated restrictions, this will all get fixed when we save it as part of the policies file
-// rather than in its own files.
final class BundlePolicySerializer extends PolicySerializer<Bundle> {
private static final String TAG = "BundlePolicySerializer";
- private static final String ATTR_FILE_NAME = "file-name";
-
- private static final String RESTRICTIONS_FILE_PREFIX = "AppRestrictions_";
- private static final String XML_SUFFIX = ".xml";
-
- private static final String TAG_RESTRICTIONS = "restrictions";
private static final String TAG_ENTRY = "entry";
private static final String TAG_VALUE = "value";
private static final String ATTR_KEY = "key";
@@ -83,62 +61,26 @@
throw new IllegalArgumentException("policyKey is not of type "
+ "PackagePolicyKey");
}
- String packageName = ((PackagePolicyKey) policyKey).getPackageName();
- String fileName = packageToRestrictionsFileName(packageName, value);
- writeApplicationRestrictionsLAr(fileName, value);
- serializer.attribute(/* namespace= */ null, ATTR_FILE_NAME, fileName);
+ writeBundle(value, serializer);
}
- @Nullable
@Override
BundlePolicyValue readFromXml(TypedXmlPullParser parser) {
- String fileName = parser.getAttributeValue(/* namespace= */ null, ATTR_FILE_NAME);
-
- return new BundlePolicyValue(readApplicationRestrictions(fileName));
- }
-
- private static String packageToRestrictionsFileName(String packageName, Bundle restrictions) {
- return RESTRICTIONS_FILE_PREFIX + packageName + Objects.hash(restrictions) + XML_SUFFIX;
- }
-
- @GuardedBy("mAppRestrictionsLock")
- private static Bundle readApplicationRestrictions(String fileName) {
- AtomicFile restrictionsFile =
- new AtomicFile(new File(Environment.getDataSystemDirectory(), fileName));
- return readApplicationRestrictions(restrictionsFile);
- }
-
- @VisibleForTesting
- @GuardedBy("mAppRestrictionsLock")
- static Bundle readApplicationRestrictions(AtomicFile restrictionsFile) {
- final Bundle restrictions = new Bundle();
- final ArrayList<String> values = new ArrayList<>();
- if (!restrictionsFile.getBaseFile().exists()) {
- return restrictions;
- }
-
- FileInputStream fis = null;
+ Bundle bundle = new Bundle();
+ ArrayList<String> values = new ArrayList<>();
try {
- fis = restrictionsFile.openRead();
- final TypedXmlPullParser parser = Xml.resolvePullParser(fis);
- XmlUtils.nextElement(parser);
- if (parser.getEventType() != XmlPullParser.START_TAG) {
- Slog.e(TAG, "Unable to read restrictions file "
- + restrictionsFile.getBaseFile());
- return restrictions;
+ final int outerDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ readBundle(bundle, values, parser);
}
- while (parser.next() != XmlPullParser.END_DOCUMENT) {
- readEntry(restrictions, values, parser);
- }
- } catch (IOException | XmlPullParserException e) {
- Slog.w(TAG, "Error parsing " + restrictionsFile.getBaseFile(), e);
- } finally {
- IoUtils.closeQuietly(fis);
+ } catch (XmlPullParserException | IOException e) {
+ Log.e(TAG, "Error parsing Bundle policy.", e);
+ return null;
}
- return restrictions;
+ return new BundlePolicyValue(bundle);
}
- private static void readEntry(Bundle restrictions, ArrayList<String> values,
+ private static void readBundle(Bundle restrictions, ArrayList<String> values,
TypedXmlPullParser parser) throws XmlPullParserException, IOException {
int type = parser.getEventType();
if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) {
@@ -186,37 +128,11 @@
Bundle childBundle = new Bundle();
int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
- readEntry(childBundle, values, parser);
+ readBundle(childBundle, values, parser);
}
return childBundle;
}
- private static void writeApplicationRestrictionsLAr(String fileName, Bundle restrictions) {
- AtomicFile restrictionsFile = new AtomicFile(
- new File(Environment.getDataSystemDirectory(), fileName));
- writeApplicationRestrictionsLAr(restrictions, restrictionsFile);
- }
-
- static void writeApplicationRestrictionsLAr(Bundle restrictions, AtomicFile restrictionsFile) {
- FileOutputStream fos = null;
- try {
- fos = restrictionsFile.startWrite();
- final TypedXmlSerializer serializer = Xml.resolveSerializer(fos);
- serializer.startDocument(null, true);
- serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
-
- serializer.startTag(null, TAG_RESTRICTIONS);
- writeBundle(restrictions, serializer);
- serializer.endTag(null, TAG_RESTRICTIONS);
-
- serializer.endDocument();
- restrictionsFile.finishWrite(fos);
- } catch (Exception e) {
- restrictionsFile.failWrite(fos);
- Slog.e(TAG, "Error writing application restrictions list", e);
- }
- }
-
private static void writeBundle(Bundle restrictions, TypedXmlSerializer serializer)
throws IOException {
for (String key : restrictions.keySet()) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 702602a..415440b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -632,6 +632,38 @@
}
/**
+ * Returns all the {@code policyKeys} set by any admin that share the same
+ * {@link PolicyKey#getIdentifier()} as the provided {@code policyDefinition}.
+ *
+ * <p>For example, getLocalPolicyKeysSetByAllAdmins(PERMISSION_GRANT) returns all permission
+ * grants set by any admin.
+ *
+ * <p>Note that this will always return at most one item for policies that do not require
+ * additional params (e.g. {@link PolicyDefinition#LOCK_TASK} vs
+ * {@link PolicyDefinition#PERMISSION_GRANT(String, String)}).
+ *
+ */
+ @NonNull
+ <V> Set<PolicyKey> getLocalPolicyKeysSetByAllAdmins(
+ @NonNull PolicyDefinition<V> policyDefinition,
+ int userId) {
+ Objects.requireNonNull(policyDefinition);
+
+ synchronized (mLock) {
+ if (policyDefinition.isGlobalOnlyPolicy() || !mLocalPolicies.contains(userId)) {
+ return Set.of();
+ }
+ Set<PolicyKey> keys = new HashSet<>();
+ for (PolicyKey key : mLocalPolicies.get(userId).keySet()) {
+ if (key.hasSameIdentifierAs(policyDefinition.getPolicyKey())) {
+ keys.add(key);
+ }
+ }
+ return keys;
+ }
+ }
+
+ /**
* Returns all user restriction policies set by the given admin.
*
* <p>Pass in {@link UserHandle#USER_ALL} for {@code userId} to get global restrictions set by
@@ -985,8 +1017,12 @@
int userId = user.id;
// Apply local policies present on parent to newly created child profile.
UserInfo parentInfo = mUserManager.getProfileParent(userId);
- if (parentInfo == null || parentInfo.getUserHandle().getIdentifier() == userId) return;
-
+ if (parentInfo == null || parentInfo.getUserHandle().getIdentifier() == userId) {
+ return;
+ }
+ if (!mLocalPolicies.contains(parentInfo.getUserHandle().getIdentifier())) {
+ return;
+ }
for (Map.Entry<PolicyKey, PolicyState<?>> entry : mLocalPolicies.get(
parentInfo.getUserHandle().getIdentifier()).entrySet()) {
enforcePolicyOnUser(userId, entry.getValue());
@@ -1210,6 +1246,31 @@
synchronized (mLock) {
clear();
new DevicePoliciesReaderWriter().readFromFileLocked();
+ reapplyAllPolicies();
+ }
+ }
+
+ private <V> void reapplyAllPolicies() {
+ for (PolicyKey policy : mGlobalPolicies.keySet()) {
+ PolicyState<?> policyState = mGlobalPolicies.get(policy);
+ // Policy definition and value will always be of the same type
+ PolicyDefinition<V> policyDefinition =
+ (PolicyDefinition<V>) policyState.getPolicyDefinition();
+ PolicyValue<V> policyValue = (PolicyValue<V>) policyState.getCurrentResolvedPolicy();
+ enforcePolicy(policyDefinition, policyValue, UserHandle.USER_ALL);
+ }
+ for (int i = 0; i < mLocalPolicies.size(); i++) {
+ int userId = mLocalPolicies.keyAt(i);
+ for (PolicyKey policy : mLocalPolicies.get(userId).keySet()) {
+ PolicyState<?> policyState = mLocalPolicies.get(userId).get(policy);
+ // Policy definition and value will always be of the same type
+ PolicyDefinition<V> policyDefinition =
+ (PolicyDefinition<V>) policyState.getPolicyDefinition();
+ PolicyValue<V> policyValue =
+ (PolicyValue<V>) policyState.getCurrentResolvedPolicy();
+ enforcePolicy(policyDefinition, policyValue, userId);
+
+ }
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 8557348..7e5d5aa 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -85,6 +85,7 @@
import static android.Manifest.permission.SET_TIME;
import static android.Manifest.permission.SET_TIME_ZONE;
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
+import static android.accounts.AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_DEFAULT;
@@ -280,6 +281,7 @@
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.StatusBarManager;
+import android.app.admin.AccountTypePolicyKey;
import android.app.admin.BooleanPolicyValue;
import android.app.admin.BundlePolicyValue;
import android.app.admin.ComponentNamePolicyValue;
@@ -531,6 +533,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -1139,6 +1142,11 @@
}
}
}
+
+ if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
+ calculateHasIncompatibleAccounts();
+ }
+
if (Intent.ACTION_BOOT_COMPLETED.equals(action)
&& userHandle == mOwners.getDeviceOwnerUserId()) {
mBugreportCollectionManager.checkForPendingBugreportAfterBoot();
@@ -1252,6 +1260,8 @@
} else if (ACTION_MANAGED_PROFILE_AVAILABLE.equals(action)) {
notifyIfManagedSubscriptionsAreUnavailable(
UserHandle.of(userHandle), /* managedProfileAvailable= */ true);
+ } else if (LOGIN_ACCOUNTS_CHANGED_ACTION.equals(action)) {
+ calculateHasIncompatibleAccounts();
}
}
@@ -2104,6 +2114,7 @@
filter.addAction(Intent.ACTION_USER_STOPPED);
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_UNLOCKED);
+ filter.addAction(LOGIN_ACCOUNTS_CHANGED_ACTION);
filter.addAction(ACTION_MANAGED_PROFILE_UNAVAILABLE);
filter.addAction(ACTION_MANAGED_PROFILE_AVAILABLE);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
@@ -2130,7 +2141,7 @@
mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
mDeviceManagementResourcesProvider.load();
- if (isPermissionCheckFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) {
mDevicePolicyEngine.load();
}
@@ -3279,8 +3290,10 @@
policy.validatePasswordOwner();
updateMaximumTimeToLockLocked(userHandle);
- updateLockTaskPackagesLocked(mContext, policy.mLockTaskPackages, userHandle);
- updateLockTaskFeaturesLocked(policy.mLockTaskFeatures, userHandle);
+ if (!isPolicyEngineForFinanceFlagEnabled()) {
+ updateLockTaskPackagesLocked(mContext, policy.mLockTaskPackages, userHandle);
+ updateLockTaskFeaturesLocked(policy.mLockTaskFeatures, userHandle);
+ }
if (policy.mStatusBarDisabled) {
setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle);
}
@@ -3592,7 +3605,7 @@
}
startOwnerService(userId, "start-user");
- if (isPermissionCheckFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) {
mDevicePolicyEngine.handleStartUser(userId);
}
}
@@ -3619,7 +3632,7 @@
void handleUnlockUser(int userId) {
startOwnerService(userId, "unlock-user");
- if (isPermissionCheckFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) {
mDevicePolicyEngine.handleUnlockUser(userId);
}
}
@@ -3631,7 +3644,7 @@
void handleStopUser(int userId) {
updateNetworkPreferenceForUser(userId, List.of(PreferentialNetworkServiceConfig.DEFAULT));
mDeviceAdminServiceController.stopServicesForUser(userId, /* actionForLog= */ "stop-user");
- if (isPermissionCheckFlagEnabled()) {
+ if (isPermissionCheckFlagEnabled() || isPolicyEngineForFinanceFlagEnabled()) {
mDevicePolicyEngine.handleStopUser(userId);
}
}
@@ -9557,6 +9570,7 @@
synchronized (getLockObject()) {
enforceCanSetDeviceOwnerLocked(caller, admin, userId, hasIncompatibleAccountsOrNonAdb);
+
Preconditions.checkArgument(isPackageInstalledForUser(admin.getPackageName(), userId),
"Invalid component " + admin + " for device owner");
final ActiveAdmin activeAdmin = getActiveAdminUncheckedLocked(admin, userId);
@@ -10246,7 +10260,9 @@
policy.mUserProvisioningState = DevicePolicyManager.STATE_USER_UNMANAGED;
policy.mAffiliationIds.clear();
policy.mLockTaskPackages.clear();
- updateLockTaskPackagesLocked(mContext, policy.mLockTaskPackages, userId);
+ if (!isPolicyEngineForFinanceFlagEnabled()) {
+ updateLockTaskPackagesLocked(mContext, policy.mLockTaskPackages, userId);
+ }
policy.mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
saveSettingsLocked(userId);
@@ -11036,8 +11052,7 @@
return false;
}
- if (!isPermissionCheckFlagEnabled()) {
- // TODO: Figure out if something like this needs to be restored for policy engine
+ if (!isPermissionCheckFlagEnabled() && !isPolicyEngineForFinanceFlagEnabled()) {
final ComponentName profileOwner = getProfileOwnerAsUser(userId);
if (profileOwner == null) {
return false;
@@ -11051,17 +11066,6 @@
return true;
}
-
- private void enforceCanCallLockTaskLocked(CallerIdentity caller) {
- Preconditions.checkCallAuthorization(isProfileOwner(caller)
- || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
-
- final int userId = caller.getUserId();
- if (!canUserUseLockTaskLocked(userId)) {
- throw new SecurityException("User " + userId + " is not allowed to use lock task");
- }
- }
-
private void enforceCanQueryLockTaskLocked(ComponentName who, String callerPackageName) {
CallerIdentity caller = getCallerIdentity(who, callerPackageName);
final int userId = caller.getUserId();
@@ -11089,6 +11093,16 @@
return enforcingAdmin;
}
+ private void enforceCanCallLockTaskLocked(CallerIdentity caller) {
+ Preconditions.checkCallAuthorization(isProfileOwner(caller)
+ || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
+
+ final int userId = caller.getUserId();
+ if (!canUserUseLockTaskLocked(userId)) {
+ throw new SecurityException("User " + userId + " is not allowed to use lock task");
+ }
+ }
+
private boolean isSystemUid(CallerIdentity caller) {
return UserHandle.isSameApp(caller.getUid(), Process.SYSTEM_UID);
}
@@ -11625,7 +11639,7 @@
caller.getUserId());
}
setBackwardsCompatibleAppRestrictions(
- packageName, restrictions, caller.getUserHandle());
+ caller, packageName, restrictions, caller.getUserHandle());
} else {
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
&& (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
@@ -11646,17 +11660,28 @@
}
/**
- * Set app restrictions in user manager to keep backwards compatibility for the old
- * getApplicationRestrictions API.
+ * Set app restrictions in user manager for DPC callers only to keep backwards compatibility
+ * for the old getApplicationRestrictions API.
*/
private void setBackwardsCompatibleAppRestrictions(
- String packageName, Bundle restrictions, UserHandle userHandle) {
- Bundle restrictionsToApply = restrictions == null || restrictions.isEmpty()
- ? getAppRestrictionsSetByAnyAdmin(packageName, userHandle)
- : restrictions;
- mInjector.binderWithCleanCallingIdentity(() -> {
- mUserManager.setApplicationRestrictions(packageName, restrictionsToApply, userHandle);
- });
+ CallerIdentity caller, String packageName, Bundle restrictions, UserHandle userHandle) {
+ if ((caller.hasAdminComponent() && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS))) {
+ Bundle restrictionsToApply = restrictions == null || restrictions.isEmpty()
+ ? getAppRestrictionsSetByAnyAdmin(packageName, userHandle)
+ : restrictions;
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ mUserManager.setApplicationRestrictions(packageName, restrictionsToApply,
+ userHandle);
+ });
+ } else {
+ // Notify package of changes via an intent - only sent to explicitly registered
+ // receivers. Sending here because For DPCs, this is being sent in UMS.
+ final Intent changeIntent = new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
+ changeIntent.setPackage(packageName);
+ changeIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mContext.sendBroadcastAsUser(changeIntent, userHandle);
+ }
}
private Bundle getAppRestrictionsSetByAnyAdmin(String packageName, UserHandle userHandle) {
@@ -14020,16 +14045,28 @@
caller = getCallerIdentity(who);
}
synchronized (getLockObject()) {
- final ActiveAdmin ap;
if (isPermissionCheckFlagEnabled()) {
+ int affectedUser = getAffectedUser(parent);
EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
who,
MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
caller.getPackageName(),
- getAffectedUser(parent)
+ affectedUser
);
- ap = enforcingAdmin.getActiveAdmin();
+ if (disabled) {
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType),
+ enforcingAdmin,
+ new BooleanPolicyValue(disabled),
+ affectedUser);
+ } else {
+ mDevicePolicyEngine.removeLocalPolicy(
+ PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType),
+ enforcingAdmin,
+ affectedUser);
+ }
} else {
+ final ActiveAdmin ap;
Objects.requireNonNull(who, "ComponentName is null");
/*
* When called on the parent DPM instance (parent == true), affects active admin
@@ -14046,13 +14083,13 @@
ap = getParentOfAdminIfRequired(
getProfileOwnerOrDeviceOwnerLocked(caller.getUserId()), parent);
}
+ if (disabled) {
+ ap.accountTypesWithManagementDisabled.add(accountType);
+ } else {
+ ap.accountTypesWithManagementDisabled.remove(accountType);
+ }
+ saveSettingsLocked(UserHandle.getCallingUserId());
}
- if (disabled) {
- ap.accountTypesWithManagementDisabled.add(accountType);
- } else {
- ap.accountTypesWithManagementDisabled.remove(accountType);
- }
- saveSettingsLocked(UserHandle.getCallingUserId());
}
}
@@ -14070,41 +14107,64 @@
}
CallerIdentity caller;
Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
+ final ArraySet<String> resultSet = new ArraySet<>();
if (isPermissionCheckFlagEnabled()) {
+ int affectedUser = parent ? getProfileParentId(userId) : userId;
caller = getCallerIdentity(callerPackageName);
if (!hasPermission(MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
- caller.getPackageName(), userId)
+ callerPackageName, affectedUser)
&& !hasFullCrossUsersPermission(caller, userId)) {
throw new SecurityException("Caller does not have permission to call this on user: "
- + userId);
+ + affectedUser);
}
- } else {
- caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId));
- }
+ Set<PolicyKey> keys = mDevicePolicyEngine.getLocalPolicyKeysSetByAllAdmins(
+ PolicyDefinition.GENERIC_ACCOUNT_MANAGEMENT_DISABLED,
+ affectedUser);
- synchronized (getLockObject()) {
- final ArraySet<String> resultSet = new ArraySet<>();
+ for (PolicyKey key : keys) {
+ if (!(key instanceof AccountTypePolicyKey)) {
+ throw new IllegalStateException("PolicyKey for "
+ + "MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT is not of type "
+ + "AccountTypePolicyKey");
+ }
+ AccountTypePolicyKey parsedKey =
+ (AccountTypePolicyKey) key;
+ String accountType = Objects.requireNonNull(parsedKey.getAccountType());
- if (!parent) {
- final DevicePolicyData policy = getUserData(userId);
- for (ActiveAdmin admin : policy.mAdminList) {
- resultSet.addAll(admin.accountTypesWithManagementDisabled);
+ Boolean disabled = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.ACCOUNT_MANAGEMENT_DISABLED(accountType),
+ affectedUser);
+ if (disabled != null && disabled) {
+ resultSet.add(accountType);
}
}
- // Check if there's a profile owner of an org-owned device and the method is called for
- // the parent user of this profile owner.
- final ActiveAdmin orgOwnedAdmin =
- getProfileOwnerOfOrganizationOwnedDeviceLocked(userId);
- final boolean shouldGetParentAccounts = orgOwnedAdmin != null && (parent
- || UserHandle.getUserId(orgOwnedAdmin.getUid()) != userId);
- if (shouldGetParentAccounts) {
- resultSet.addAll(
- orgOwnedAdmin.getParentActiveAdmin().accountTypesWithManagementDisabled);
+ } else {
+ caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId));
+
+ synchronized (getLockObject()) {
+ if (!parent) {
+ final DevicePolicyData policy = getUserData(userId);
+ for (ActiveAdmin admin : policy.mAdminList) {
+ resultSet.addAll(admin.accountTypesWithManagementDisabled);
+ }
+ }
+
+ // Check if there's a profile owner of an org-owned device and the method is called
+ // for the parent user of this profile owner.
+ final ActiveAdmin orgOwnedAdmin =
+ getProfileOwnerOfOrganizationOwnedDeviceLocked(userId);
+ final boolean shouldGetParentAccounts = orgOwnedAdmin != null && (parent
+ || UserHandle.getUserId(orgOwnedAdmin.getUid()) != userId);
+ if (shouldGetParentAccounts) {
+ resultSet.addAll(
+ orgOwnedAdmin.getParentActiveAdmin()
+ .accountTypesWithManagementDisabled);
+ }
}
- return resultSet.toArray(new String[resultSet.size()]);
}
+ return resultSet.toArray(new String[resultSet.size()]);
}
@Override
@@ -14679,7 +14739,7 @@
if (isPolicyEngineForFinanceFlagEnabled()) {
EnforcingAdmin enforcingAdmin;
synchronized (getLockObject()) {
- enforcingAdmin = enforceCanCallLockTaskLocked(who, callerPackageName);
+ enforcingAdmin = enforceCanCallLockTaskLocked(who, caller.getPackageName());
}
if (packages.length == 0) {
mDevicePolicyEngine.removeLocalPolicy(
@@ -14806,8 +14866,7 @@
if (isPolicyEngineForFinanceFlagEnabled()) {
EnforcingAdmin enforcingAdmin;
synchronized (getLockObject()) {
- enforcingAdmin = enforceCanCallLockTaskLocked(who,
- callerPackageName);
+ enforcingAdmin = enforceCanCallLockTaskLocked(who, caller.getPackageName());
enforceCanSetLockTaskFeaturesOnFinancedDevice(caller, flags);
}
LockTaskPolicy currentPolicy = mDevicePolicyEngine.getLocalPolicySetByAdmin(
@@ -14884,6 +14943,7 @@
}
final List<String> lockTaskPackages = getUserData(userId).mLockTaskPackages;
+ // TODO(b/278438525): handle in the policy engine
if (!lockTaskPackages.isEmpty()) {
Slogf.d(LOG_TAG,
"User id " + userId + " not affiliated. Clearing lock task packages");
@@ -18347,6 +18407,26 @@
return isUserAffiliatedWithDeviceLocked(userId);
}
+ private boolean hasIncompatibleAccountsOnAnyUser() {
+ if (mHasIncompatibleAccounts == null) {
+ // Hasn't loaded for the first time yet - assume the worst
+ return true;
+ }
+
+ for (boolean hasIncompatible : mHasIncompatibleAccounts.values()) {
+ if (hasIncompatible) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean hasIncompatibleAccounts(int userId) {
+ return mHasIncompatibleAccounts == null ? true
+ : mHasIncompatibleAccounts.getOrDefault(userId, /* default= */ false);
+ }
+
/**
* Return true if a given user has any accounts that'll prevent installing a device or profile
* owner {@code owner}.
@@ -18384,7 +18464,7 @@
}
}
- boolean compatible = !hasIncompatibleAccounts(am, accounts);
+ boolean compatible = !hasIncompatibleAccounts(userId);
if (compatible) {
Slogf.w(LOG_TAG, "All accounts are compatible");
} else {
@@ -18394,37 +18474,67 @@
});
}
- private boolean hasIncompatibleAccounts(AccountManager am, Account[] accounts) {
- // TODO(b/244284408): Add test
- final String[] feature_allow =
- { DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED };
- final String[] feature_disallow =
- { DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED };
-
- for (Account account : accounts) {
- if (hasAccountFeatures(am, account, feature_disallow)) {
- Slogf.e(LOG_TAG, "%s has %s", account, feature_disallow[0]);
- return true;
- }
- if (!hasAccountFeatures(am, account, feature_allow)) {
- Slogf.e(LOG_TAG, "%s doesn't have %s", account, feature_allow[0]);
- return true;
- }
- }
-
- return false;
+ @Override
+ public void calculateHasIncompatibleAccounts() {
+ new CalculateHasIncompatibleAccountsTask().executeOnExecutor(
+ AsyncTask.THREAD_POOL_EXECUTOR, null);
}
- private boolean hasAccountFeatures(AccountManager am, Account account, String[] features) {
- try {
- // TODO(267156507): Restore without blocking binder thread
- return false;
-// return am.hasFeatures(account, features, null, null).getResult();
- } catch (Exception e) {
- Slogf.w(LOG_TAG, "Failed to get account feature", e);
+ @Nullable
+ private volatile Map<Integer, Boolean> mHasIncompatibleAccounts;
+
+ class CalculateHasIncompatibleAccountsTask extends AsyncTask<
+ Void, Void, Map<Integer, Boolean>> {
+ private static final String[] FEATURE_ALLOW =
+ {DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED};
+ private static final String[] FEATURE_DISALLOW =
+ {DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED};
+
+ @Override
+ protected Map<Integer, Boolean> doInBackground(Void... args) {
+ List<UserInfo> users = mUserManagerInternal.getUsers(/* excludeDying= */ true);
+ Map<Integer, Boolean> results = new HashMap<>();
+ for (UserInfo userInfo : users) {
+ results.put(userInfo.id, userHasIncompatibleAccounts(userInfo.id));
+ }
+
+ return results;
+ }
+
+ private boolean userHasIncompatibleAccounts(int id) {
+ AccountManager am = mContext.createContextAsUser(UserHandle.of(id), /* flags= */ 0)
+ .getSystemService(AccountManager.class);
+ Account[] accounts = am.getAccounts();
+
+ for (Account account : accounts) {
+ if (hasAccountFeatures(am, account, FEATURE_DISALLOW)) {
+ return true;
+ }
+ if (!hasAccountFeatures(am, account, FEATURE_ALLOW)) {
+ return true;
+ }
+ }
+
return false;
}
- }
+
+ @Override
+ protected void onPostExecute(Map<Integer, Boolean> results) {
+ mHasIncompatibleAccounts = Collections.unmodifiableMap(results);
+
+ Slogf.i(LOG_TAG, "Finished calculating hasIncompatibleAccountsTask");
+ }
+
+ private static boolean hasAccountFeatures(AccountManager am, Account account,
+ String[] features) {
+ try {
+ return am.hasFeatures(account, features, null, null).getResult();
+ } catch (Exception e) {
+ Slogf.w(LOG_TAG, "Failed to get account feature", e);
+ return false;
+ }
+ }
+ };
private boolean isAdb(CallerIdentity caller) {
return isShellUid(caller) || isRootUid(caller);
@@ -22256,26 +22366,6 @@
}
}
- private boolean hasIncompatibleAccountsOnAnyUser() {
- long callingIdentity = Binder.clearCallingIdentity();
- try {
- for (UserInfo user : mUserManagerInternal.getUsers(/* excludeDying= */ true)) {
- AccountManager am = mContext.createContextAsUser(
- UserHandle.of(user.id), /* flags= */ 0)
- .getSystemService(AccountManager.class);
- Account[] accounts = am.getAccounts();
-
- if (hasIncompatibleAccounts(am, accounts)) {
- return true;
- }
- }
-
- return false;
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
- }
-
private void setBypassDevicePolicyManagementRoleQualificationStateInternal(
String currentRoleHolder, boolean allowBypass) {
boolean stateChanged = false;
@@ -22516,11 +22606,26 @@
"manage_device_policy_microphone_toggle";
// DPC types
+ private static final int NOT_A_DPC = -1;
private static final int DEFAULT_DEVICE_OWNER = 0;
private static final int FINANCED_DEVICE_OWNER = 1;
private static final int PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE = 2;
private static final int PROFILE_OWNER_ON_USER_0 = 3;
private static final int PROFILE_OWNER = 4;
+ private static final int PROFILE_OWNER_ON_USER = 5;
+ private static final int AFFILIATED_PROFILE_OWNER_ON_USER = 6;
+ // DPC types
+ @IntDef(value = {
+ NOT_A_DPC,
+ DEFAULT_DEVICE_OWNER,
+ FINANCED_DEVICE_OWNER,
+ PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE,
+ PROFILE_OWNER_ON_USER_0,
+ PROFILE_OWNER,
+ PROFILE_OWNER_ON_USER,
+ AFFILIATED_PROFILE_OWNER_ON_USER
+ })
+ private @interface DpcType {}
// Permissions of existing DPC types.
private static final List<String> DEFAULT_DEVICE_OWNER_PERMISSIONS = List.of(
@@ -22674,7 +22779,9 @@
SET_TIME_ZONE
);
-
+ /**
+ * All the additional permissions granted to a Profile Owner on user 0.
+ */
private static final List<String> ADDITIONAL_PROFILE_OWNER_ON_USER_0_PERMISSIONS =
List.of(
MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
@@ -22699,6 +22806,20 @@
);
/**
+ * All the additional permissions granted to a Profile Owner on an unaffiliated user.
+ */
+ private static final List<String> ADDITIONAL_PROFILE_OWNER_ON_USER_PERMISSIONS =
+ List.of(
+ MANAGE_DEVICE_POLICY_LOCK_TASK
+ );
+
+ /**
+ * All the additional permissions granted to a Profile Owner on an affiliated user.
+ */
+ private static final List<String> ADDITIONAL_AFFILIATED_PROFILE_OWNER_ON_USER_PERMISSIONS =
+ List.of();
+
+ /**
* Combination of {@link PROFILE_OWNER_PERMISSIONS} and
* {@link ADDITIONAL_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS}.
*/
@@ -22712,6 +22833,20 @@
private static final List<String> PROFILE_OWNER_ON_USER_0_PERMISSIONS =
new ArrayList();
+ /**
+ * Combination of {@link PROFILE_OWNER_PERMISSIONS} and
+ * {@link ADDITIONAL_AFFILIATED_PROFIL_OWNER_ON_USER_PERMISSIONS}.
+ */
+ private static final List<String> AFFILIATED_PROFILE_OWNER_ON_USER_PERMISSIONS =
+ new ArrayList();
+
+ /**
+ * Combination of {@link PROFILE_OWNER_PERMISSIONS} and
+ * {@link ADDITIONAL_PROFILE_OWNER_ON_USER_PERMISSIONS}.
+ */
+ private static final List<String> PROFILE_OWNER_ON_USER_PERMISSIONS =
+ new ArrayList();
+
private static final HashMap<Integer, List<String>> DPC_PERMISSIONS = new HashMap<>();
{
@@ -22724,6 +22859,16 @@
// some extra permissions.
PROFILE_OWNER_ON_USER_0_PERMISSIONS.addAll(PROFILE_OWNER_PERMISSIONS);
PROFILE_OWNER_ON_USER_0_PERMISSIONS.addAll(ADDITIONAL_PROFILE_OWNER_ON_USER_0_PERMISSIONS);
+ // Profile owners on users have all the permission of a profile owner plus
+ // some extra permissions.
+ PROFILE_OWNER_ON_USER_PERMISSIONS.addAll(PROFILE_OWNER_PERMISSIONS);
+ PROFILE_OWNER_ON_USER_PERMISSIONS.addAll(
+ ADDITIONAL_PROFILE_OWNER_ON_USER_PERMISSIONS);
+ // Profile owners on affiliated users have all the permission of a profile owner on a user
+ // plus some extra permissions.
+ AFFILIATED_PROFILE_OWNER_ON_USER_PERMISSIONS.addAll(PROFILE_OWNER_ON_USER_PERMISSIONS);
+ AFFILIATED_PROFILE_OWNER_ON_USER_PERMISSIONS.addAll(
+ ADDITIONAL_AFFILIATED_PROFILE_OWNER_ON_USER_PERMISSIONS);
DPC_PERMISSIONS.put(DEFAULT_DEVICE_OWNER, DEFAULT_DEVICE_OWNER_PERMISSIONS);
DPC_PERMISSIONS.put(FINANCED_DEVICE_OWNER, FINANCED_DEVICE_OWNER_PERMISSIONS);
@@ -22731,6 +22876,9 @@
PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS);
DPC_PERMISSIONS.put(PROFILE_OWNER_ON_USER_0, PROFILE_OWNER_ON_USER_0_PERMISSIONS);
DPC_PERMISSIONS.put(PROFILE_OWNER, PROFILE_OWNER_PERMISSIONS);
+ DPC_PERMISSIONS.put(PROFILE_OWNER_ON_USER, PROFILE_OWNER_ON_USER_PERMISSIONS);
+ DPC_PERMISSIONS.put(AFFILIATED_PROFILE_OWNER_ON_USER,
+ AFFILIATED_PROFILE_OWNER_ON_USER_PERMISSIONS);
}
//Map of Permission to Delegate Scope.
private static final HashMap<String, String> DELEGATE_SCOPES = new HashMap<>();
@@ -23108,22 +23256,9 @@
if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
return true;
}
- // Check the permissions of DPCs
- if (isDefaultDeviceOwner(caller)) {
- return DPC_PERMISSIONS.get(DEFAULT_DEVICE_OWNER).contains(permission);
- }
- if (isFinancedDeviceOwner(caller)) {
- return DPC_PERMISSIONS.get(FINANCED_DEVICE_OWNER).contains(permission);
- }
- if (isProfileOwnerOfOrganizationOwnedDevice(caller)) {
- return DPC_PERMISSIONS.get(PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE).contains(
- permission);
- }
- if (isProfileOwnerOnUser0(caller)) {
- return DPC_PERMISSIONS.get(PROFILE_OWNER_ON_USER_0).contains(permission);
- }
- if (isProfileOwner(caller)) {
- return DPC_PERMISSIONS.get(PROFILE_OWNER).contains(permission);
+ int dpcType = getDpcType(caller);
+ if (dpcType != NOT_A_DPC) {
+ return DPC_PERMISSIONS.get(dpcType).contains(permission);
}
// Check the permission for the role-holder
if (isCallerDevicePolicyManagementRoleHolder(caller)) {
@@ -23193,6 +23328,35 @@
return calledOnParent ? getProfileParentId(callingUserId) : callingUserId;
}
+ /**
+ * Return the DPC type of the given caller.
+ */
+ private @DpcType int getDpcType(CallerIdentity caller) {
+ // Check the permissions of DPCs
+ if (isDefaultDeviceOwner(caller)) {
+ return DEFAULT_DEVICE_OWNER;
+ }
+ if (isFinancedDeviceOwner(caller)) {
+ return FINANCED_DEVICE_OWNER;
+ }
+ if (isProfileOwner(caller)) {
+ if (isProfileOwnerOfOrganizationOwnedDevice(caller)) {
+ return PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE;
+ }
+ if (isManagedProfile(caller.getUserId())) {
+ return PROFILE_OWNER;
+ }
+ if (isProfileOwnerOnUser0(caller)) {
+ return PROFILE_OWNER_ON_USER_0;
+ }
+ if (isUserAffiliatedWithDevice(caller.getUserId())) {
+ return AFFILIATED_PROFILE_OWNER_ON_USER;
+ }
+ return PROFILE_OWNER_ON_USER;
+ }
+ return NOT_A_DPC;
+ }
+
private boolean isPermissionCheckFlagEnabled() {
return DeviceConfig.getBoolean(
NAMESPACE_DEVICE_POLICY_MANAGER,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 8c2468a..638596b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.AccountTypePolicyKey;
import android.app.admin.BooleanPolicyValue;
import android.app.admin.DevicePolicyIdentifiers;
import android.app.admin.DevicePolicyManager;
@@ -281,6 +282,32 @@
DevicePolicyIdentifiers.APPLICATION_HIDDEN_POLICY, packageName));
}
+ // This is saved in the static map sPolicyDefinitions so that we're able to reconstruct the
+ // actual policy with the correct arguments (i.e. packageName) when reading the policies from
+ // xml.
+ static PolicyDefinition<Boolean> GENERIC_ACCOUNT_MANAGEMENT_DISABLED =
+ new PolicyDefinition<>(
+ new AccountTypePolicyKey(
+ DevicePolicyIdentifiers.ACCOUNT_MANAGEMENT_DISABLED_POLICY),
+ TRUE_MORE_RESTRICTIVE,
+ POLICY_FLAG_LOCAL_ONLY_POLICY,
+ // Nothing is enforced, we just need to store it
+ (Boolean value, Context context, Integer userId, PolicyKey policyKey) -> true,
+ new BooleanPolicySerializer());
+
+ /**
+ * Passing in {@code null} for {@code accountType} will return
+ * {@link #GENERIC_ACCOUNT_MANAGEMENT_DISABLED}.
+ */
+ static PolicyDefinition<Boolean> ACCOUNT_MANAGEMENT_DISABLED(String accountType) {
+ if (accountType == null) {
+ return GENERIC_ACCOUNT_MANAGEMENT_DISABLED;
+ }
+ return GENERIC_ACCOUNT_MANAGEMENT_DISABLED.createPolicyDefinition(
+ new AccountTypePolicyKey(
+ DevicePolicyIdentifiers.ACCOUNT_MANAGEMENT_DISABLED_POLICY, accountType));
+ }
+
private static final Map<String, PolicyDefinition<?>> POLICY_DEFINITIONS = new HashMap<>();
private static Map<String, Integer> USER_RESTRICTION_FLAGS = new HashMap<>();
@@ -304,6 +331,8 @@
KEYGUARD_DISABLED_FEATURES);
POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.APPLICATION_HIDDEN_POLICY,
GENERIC_APPLICATION_HIDDEN);
+ POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.ACCOUNT_MANAGEMENT_DISABLED_POLICY,
+ GENERIC_ACCOUNT_MANAGEMENT_DISABLED);
// User Restriction Policies
USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_MODIFY_ACCOUNTS, /* flags= */ 0);
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 7d4f87d..a6ada4d 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -38,6 +38,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.view.Display;
import android.view.inputmethod.InputMethodManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -144,6 +145,26 @@
}
}
+ @Test
+ public void testShowImeScreenshot() {
+ synchronized (ImfLock.class) {
+ mVisibilityApplier.showImeScreenshot(mWindowToken, Display.DEFAULT_DISPLAY,
+ null /* statsToken */);
+ }
+
+ verify(mMockImeTargetVisibilityPolicy).showImeScreenshot(eq(mWindowToken),
+ eq(Display.DEFAULT_DISPLAY));
+ }
+
+ @Test
+ public void testRemoveImeScreenshot() {
+ synchronized (ImfLock.class) {
+ mVisibilityApplier.removeImeScreenshot(Display.DEFAULT_DISPLAY);
+ }
+
+ verify(mMockImeTargetVisibilityPolicy).removeImeScreenshot(eq(Display.DEFAULT_DISPLAY));
+ }
+
private InputBindResult startInputOrWindowGainedFocus(IBinder windowToken, int softInputMode) {
return mInputMethodManagerService.startInputOrWindowGainedFocus(
StartInputReason.WINDOW_FOCUS_GAIN /* startInputReason */,
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
index 2a256f2..3871e1d 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
@@ -24,7 +24,15 @@
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.internal.inputmethod.SoftInputShowHideReason.HIDE_WHEN_INPUT_TARGET_INVISIBLE;
+import static com.android.internal.inputmethod.SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS;
+import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.ImeTargetWindowState;
+import static com.android.server.inputmethod.ImeVisibilityStateComputer.ImeVisibilityResult;
+import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_EXPLICIT;
+import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_REMOVE_IME_SNAPSHOT;
+import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME_SNAPSHOT;
import static com.android.server.inputmethod.InputMethodManagerService.FALLBACK_DISPLAY_ID;
import static com.android.server.inputmethod.InputMethodManagerService.ImeDisplayValidator;
@@ -37,11 +45,13 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.android.server.wm.ImeTargetChangeListener;
import com.android.server.wm.WindowManagerInternal;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
/**
* Test the behavior of {@link ImeVisibilityStateComputer} and {@link ImeVisibilityApplier} when
@@ -53,6 +63,7 @@
@RunWith(AndroidJUnit4.class)
public class ImeVisibilityStateComputerTest extends InputMethodManagerServiceTestBase {
private ImeVisibilityStateComputer mComputer;
+ private ImeTargetChangeListener mListener;
private int mImeDisplayPolicy = DISPLAY_IME_POLICY_LOCAL;
@Before
@@ -69,7 +80,11 @@
return displayId -> mImeDisplayPolicy;
}
};
+ ArgumentCaptor<ImeTargetChangeListener> captor = ArgumentCaptor.forClass(
+ ImeTargetChangeListener.class);
+ verify(mMockWindowManagerInternal).setInputMethodTargetChangeListener(captor.capture());
mComputer = new ImeVisibilityStateComputer(mInputMethodManagerService, injector);
+ mListener = captor.getValue();
}
@Test
@@ -196,6 +211,53 @@
lastState.isRequestedImeVisible());
}
+ @Test
+ public void testOnInteractiveChanged() {
+ mComputer.getOrCreateWindowState(mWindowToken);
+ // Precondition: ensure IME has shown before hiding request.
+ mComputer.requestImeVisibility(mWindowToken, true);
+ mComputer.setInputShown(true);
+
+ // No need any visibility change When initially shows IME on the device was interactive.
+ ImeVisibilityStateComputer.ImeVisibilityResult result = mComputer.onInteractiveChanged(
+ mWindowToken, true /* interactive */);
+ assertThat(result).isNull();
+
+ // Show the IME screenshot to capture the last IME visible state when the device inactive.
+ result = mComputer.onInteractiveChanged(mWindowToken, false /* interactive */);
+ assertThat(result).isNotNull();
+ assertThat(result.getState()).isEqualTo(STATE_SHOW_IME_SNAPSHOT);
+ assertThat(result.getReason()).isEqualTo(SHOW_IME_SCREENSHOT_FROM_IMMS);
+
+ // Remove the IME screenshot when the device became interactive again.
+ result = mComputer.onInteractiveChanged(mWindowToken, true /* interactive */);
+ assertThat(result).isNotNull();
+ assertThat(result.getState()).isEqualTo(STATE_REMOVE_IME_SNAPSHOT);
+ assertThat(result.getReason()).isEqualTo(REMOVE_IME_SCREENSHOT_FROM_IMMS);
+ }
+
+ @Test
+ public void testOnApplyImeVisibilityFromComputer() {
+ final IBinder testImeTargetOverlay = new Binder();
+ final IBinder testImeInputTarget = new Binder();
+
+ // Simulate a test IME layering target overlay fully occluded the IME input target.
+ mListener.onImeTargetOverlayVisibilityChanged(testImeTargetOverlay, true, false);
+ mListener.onImeInputTargetVisibilityChanged(testImeInputTarget, false, false);
+ final ArgumentCaptor<IBinder> targetCaptor = ArgumentCaptor.forClass(IBinder.class);
+ final ArgumentCaptor<ImeVisibilityResult> resultCaptor = ArgumentCaptor.forClass(
+ ImeVisibilityResult.class);
+ verify(mInputMethodManagerService).onApplyImeVisibilityFromComputer(targetCaptor.capture(),
+ resultCaptor.capture());
+ final IBinder imeInputTarget = targetCaptor.getValue();
+ final ImeVisibilityResult result = resultCaptor.getValue();
+
+ // Verify the computer will callback hiding IME state to IMMS.
+ assertThat(imeInputTarget).isEqualTo(testImeInputTarget);
+ assertThat(result.getState()).isEqualTo(STATE_HIDE_IME_EXPLICIT);
+ assertThat(result.getReason()).isEqualTo(HIDE_WHEN_INPUT_TARGET_INVISIBLE);
+ }
+
private ImeTargetWindowState initImeTargetWindowState(IBinder windowToken) {
final ImeTargetWindowState state = new ImeTargetWindowState(SOFT_INPUT_STATE_UNCHANGED,
0, true, true, true);
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
index 90691a7..c80ecbf 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -62,6 +62,7 @@
import com.android.server.SystemService;
import com.android.server.input.InputManagerInternal;
import com.android.server.pm.UserManagerInternal;
+import com.android.server.wm.ImeTargetVisibilityPolicy;
import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
@@ -113,6 +114,7 @@
@Mock protected IInputMethod mMockInputMethod;
@Mock protected IBinder mMockInputMethodBinder;
@Mock protected IInputManager mMockIInputManager;
+ @Mock protected ImeTargetVisibilityPolicy mMockImeTargetVisibilityPolicy;
protected Context mContext;
protected MockitoSession mMockingSession;
@@ -166,6 +168,8 @@
.when(() -> LocalServices.getService(DisplayManagerInternal.class));
doReturn(mMockUserManagerInternal)
.when(() -> LocalServices.getService(UserManagerInternal.class));
+ doReturn(mMockImeTargetVisibilityPolicy)
+ .when(() -> LocalServices.getService(ImeTargetVisibilityPolicy.class));
doReturn(mMockIInputMethodManager)
.when(() -> ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE));
doReturn(mMockIPlatformCompat)
@@ -218,6 +222,7 @@
false);
mInputMethodManagerService = new InputMethodManagerService(mContext, mServiceThread,
mMockInputMethodBindingController);
+ spyOn(mInputMethodManagerService);
// Start a InputMethodManagerService.Lifecycle to publish and manage the lifecycle of
// InputMethodManagerService, which is closer to the real situation.
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java
index 8c84014..dc92376 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java
@@ -199,10 +199,11 @@
}
public Intent getResult() {
- try {
- return mResult.take();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
+ while (true) {
+ try {
+ return mResult.take();
+ } catch (InterruptedException e) {
+ }
}
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/HighBrightnessModeMetadataMapperTest.java b/services/tests/mockingservicestests/src/com/android/server/display/HighBrightnessModeMetadataMapperTest.java
new file mode 100644
index 0000000..d9fbba5
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/display/HighBrightnessModeMetadataMapperTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.display;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class HighBrightnessModeMetadataMapperTest {
+
+ private HighBrightnessModeMetadataMapper mHighBrightnessModeMetadataMapper;
+
+ @Before
+ public void setUp() {
+ mHighBrightnessModeMetadataMapper = new HighBrightnessModeMetadataMapper();
+ }
+
+ @Test
+ public void testGetHighBrightnessModeMetadata() {
+ // Display device is null
+ final LogicalDisplay display = mock(LogicalDisplay.class);
+ when(display.getPrimaryDisplayDeviceLocked()).thenReturn(null);
+ assertNull(mHighBrightnessModeMetadataMapper.getHighBrightnessModeMetadataLocked(display));
+
+ // No HBM metadata stored for this display yet
+ final DisplayDevice device = mock(DisplayDevice.class);
+ when(display.getPrimaryDisplayDeviceLocked()).thenReturn(device);
+ HighBrightnessModeMetadata hbmMetadata =
+ mHighBrightnessModeMetadataMapper.getHighBrightnessModeMetadataLocked(display);
+ assertTrue(hbmMetadata.getHbmEventQueue().isEmpty());
+ assertTrue(hbmMetadata.getRunningStartTimeMillis() < 0);
+
+ // Modify the metadata
+ long startTimeMillis = 100;
+ long endTimeMillis = 200;
+ long setTime = 300;
+ hbmMetadata.addHbmEvent(new HbmEvent(startTimeMillis, endTimeMillis));
+ hbmMetadata.setRunningStartTimeMillis(setTime);
+ hbmMetadata =
+ mHighBrightnessModeMetadataMapper.getHighBrightnessModeMetadataLocked(display);
+ assertEquals(1, hbmMetadata.getHbmEventQueue().size());
+ assertEquals(startTimeMillis,
+ hbmMetadata.getHbmEventQueue().getFirst().getStartTimeMillis());
+ assertEquals(endTimeMillis, hbmMetadata.getHbmEventQueue().getFirst().getEndTimeMillis());
+ assertEquals(setTime, hbmMetadata.getRunningStartTimeMillis());
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/WakelockControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/WakelockControllerTest.java
index 07a81ff..c23d4b1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/WakelockControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/WakelockControllerTest.java
@@ -244,6 +244,15 @@
verifyZeroInteractions(mDisplayPowerCallbacks);
}
+ @Test
+ public void testReleaseAll() throws Exception {
+ // Use WAKE_LOCK_MAX to verify it has been correctly set and used in releaseAll().
+ verifyWakelockAcquisition(WakelockController.WAKE_LOCK_MAX,
+ () -> mWakelockController.hasUnfinishedBusiness());
+ mWakelockController.releaseAll();
+ assertFalse(mWakelockController.hasUnfinishedBusiness());
+ }
+
private void verifyWakelockAcquisitionAndReaquisition(int wakelockId,
Callable<Boolean> isWakelockAcquiredCallable)
throws Exception {
@@ -284,6 +293,4 @@
assertFalse(mWakelockController.releaseWakelock(wakelockId));
assertFalse(isWakelockAcquiredCallable.call());
}
-
-
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 913d8c1..11e4120 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -35,6 +35,7 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -435,9 +436,9 @@
mMockConnection.invokeCallbacks();
assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
- verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY,
- DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
- animate, TEST_SERVICE_ID);
+ verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY),
+ eq(DEFAULT_SCALE), eq(MAGNIFIED_CENTER_X), eq(MAGNIFIED_CENTER_Y),
+ any(MagnificationAnimationCallback.class), eq(TEST_SERVICE_ID));
}
@Test
@@ -504,6 +505,42 @@
}
@Test
+ public void configTransitionToFullScreenWithAnimation_windowMagnifying_notifyService()
+ throws RemoteException {
+ final boolean animate = true;
+ activateMagnifier(MODE_WINDOW, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
+
+ reset(mService);
+ MagnificationConfig config = (new MagnificationConfig.Builder())
+ .setMode(MODE_FULLSCREEN).build();
+ mMagnificationController.transitionMagnificationConfigMode(TEST_DISPLAY,
+ config, animate, TEST_SERVICE_ID);
+ verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY),
+ /* scale= */ anyFloat(), /* centerX= */ anyFloat(), /* centerY= */ anyFloat(),
+ mCallbackArgumentCaptor.capture(), /* id= */ anyInt());
+ mCallbackArgumentCaptor.getValue().onResult(true);
+ mMockConnection.invokeCallbacks();
+
+ verify(mService).changeMagnificationMode(TEST_DISPLAY, MODE_FULLSCREEN);
+ }
+
+ @Test
+ public void configTransitionToFullScreenWithoutAnimation_windowMagnifying_notifyService()
+ throws RemoteException {
+ final boolean animate = false;
+ activateMagnifier(MODE_WINDOW, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
+
+ reset(mService);
+ MagnificationConfig config = (new MagnificationConfig.Builder())
+ .setMode(MODE_FULLSCREEN).build();
+ mMagnificationController.transitionMagnificationConfigMode(TEST_DISPLAY,
+ config, animate, TEST_SERVICE_ID);
+ mMockConnection.invokeCallbacks();
+
+ verify(mService).changeMagnificationMode(TEST_DISPLAY, MODE_FULLSCREEN);
+ }
+
+ @Test
public void interruptDuringTransitionToWindow_disablingFullScreen_discardPreviousTransition()
throws RemoteException {
activateMagnifier(MODE_FULLSCREEN, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthResultCoordinatorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthResultCoordinatorTest.java
index ebf7fd8..2102b09 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthResultCoordinatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthResultCoordinatorTest.java
@@ -17,7 +17,8 @@
package com.android.server.biometrics.sensors;
import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_DEFAULT;
-import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_LOCKED;
+import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_PERMANENT_LOCKED;
+import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_TIMED_LOCKED;
import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_UNLOCKED;
import static com.google.common.truth.Truth.assertThat;
@@ -76,7 +77,22 @@
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
AUTHENTICATOR_DEFAULT);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
- AUTHENTICATOR_LOCKED);
+ AUTHENTICATOR_PERMANENT_LOCKED);
+ }
+
+ @Test
+ public void testConvenientLockoutTimed() {
+ mAuthResultCoordinator.lockOutTimed(
+ BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE);
+
+ final Map<Integer, Integer> authMap = mAuthResultCoordinator.getResult();
+
+ assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
+ AUTHENTICATOR_DEFAULT);
+ assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
+ AUTHENTICATOR_DEFAULT);
+ assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
+ AUTHENTICATOR_TIMED_LOCKED);
}
@Test
@@ -97,16 +113,31 @@
@Test
public void testWeakLockout() {
mAuthResultCoordinator.lockedOutFor(
- BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE);
+ BiometricManager.Authenticators.BIOMETRIC_WEAK);
Map<Integer, Integer> authMap = mAuthResultCoordinator.getResult();
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
AUTHENTICATOR_DEFAULT);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
- AUTHENTICATOR_DEFAULT);
+ AUTHENTICATOR_PERMANENT_LOCKED);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
- AUTHENTICATOR_LOCKED);
+ AUTHENTICATOR_PERMANENT_LOCKED);
+ }
+
+ @Test
+ public void testWeakLockoutTimed() {
+ mAuthResultCoordinator.lockOutTimed(
+ BiometricManager.Authenticators.BIOMETRIC_WEAK);
+
+ Map<Integer, Integer> authMap = mAuthResultCoordinator.getResult();
+
+ assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
+ AUTHENTICATOR_DEFAULT);
+ assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
+ AUTHENTICATOR_TIMED_LOCKED);
+ assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
+ AUTHENTICATOR_TIMED_LOCKED);
}
@Test
@@ -132,13 +163,27 @@
Map<Integer, Integer> authMap = mAuthResultCoordinator.getResult();
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
- AUTHENTICATOR_LOCKED);
+ AUTHENTICATOR_PERMANENT_LOCKED);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
- AUTHENTICATOR_LOCKED);
+ AUTHENTICATOR_PERMANENT_LOCKED);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
- AUTHENTICATOR_LOCKED);
+ AUTHENTICATOR_PERMANENT_LOCKED);
}
+ @Test
+ public void testStrongLockoutTimed() {
+ mAuthResultCoordinator.lockOutTimed(
+ BiometricManager.Authenticators.BIOMETRIC_STRONG);
+
+ Map<Integer, Integer> authMap = mAuthResultCoordinator.getResult();
+
+ assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
+ AUTHENTICATOR_TIMED_LOCKED);
+ assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
+ AUTHENTICATOR_TIMED_LOCKED);
+ assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
+ AUTHENTICATOR_TIMED_LOCKED);
+ }
@Test
public void testStrongUnlock() {
@@ -167,10 +212,30 @@
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
AUTHENTICATOR_DEFAULT);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
- AUTHENTICATOR_LOCKED);
+ AUTHENTICATOR_PERMANENT_LOCKED);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
- AUTHENTICATOR_LOCKED);
+ AUTHENTICATOR_PERMANENT_LOCKED);
}
+ @Test
+ public void testLockoutAndAuth() {
+ mAuthResultCoordinator.lockedOutFor(
+ BiometricManager.Authenticators.BIOMETRIC_WEAK);
+ mAuthResultCoordinator.authenticatedFor(
+ BiometricManager.Authenticators.BIOMETRIC_STRONG);
+
+ final Map<Integer, Integer> authMap = mAuthResultCoordinator.getResult();
+
+ assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)
+ & AUTHENTICATOR_UNLOCKED).isEqualTo(
+ AUTHENTICATOR_UNLOCKED);
+ assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)
+ & AUTHENTICATOR_UNLOCKED).isEqualTo(
+ AUTHENTICATOR_UNLOCKED);
+ assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)
+ & AUTHENTICATOR_UNLOCKED).isEqualTo(
+ AUTHENTICATOR_UNLOCKED);
+ }
+
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthSessionCoordinatorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthSessionCoordinatorTest.java
index c3b9cb1..f26c7e6 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthSessionCoordinatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthSessionCoordinatorTest.java
@@ -154,7 +154,7 @@
LockoutTracker.LOCKOUT_NONE);
assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
LockoutTracker.LOCKOUT_NONE);
- assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
LockoutTracker.LOCKOUT_NONE);
}
@@ -194,7 +194,7 @@
LockoutTracker.LOCKOUT_NONE);
assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
LockoutTracker.LOCKOUT_NONE);
- assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
+ assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
LockoutTracker.LOCKOUT_NONE);
assertThat(
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/MultiBiometricLockoutStateTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/MultiBiometricLockoutStateTest.java
index 968844e4c..c28de55 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/MultiBiometricLockoutStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/MultiBiometricLockoutStateTest.java
@@ -49,7 +49,7 @@
private Clock mClock;
private static void unlockAllBiometrics(MultiBiometricLockoutState lockoutState, int userId) {
- lockoutState.setAuthenticatorTo(userId, BIOMETRIC_STRONG, true /* canAuthenticate */);
+ lockoutState.clearPermanentLockOut(userId, BIOMETRIC_STRONG);
assertThat(lockoutState.getLockoutState(userId, BIOMETRIC_STRONG)).isEqualTo(
LockoutTracker.LOCKOUT_NONE);
assertThat(lockoutState.getLockoutState(userId, BIOMETRIC_WEAK)).isEqualTo(
@@ -59,7 +59,7 @@
}
private static void lockoutAllBiometrics(MultiBiometricLockoutState lockoutState, int userId) {
- lockoutState.setAuthenticatorTo(userId, BIOMETRIC_STRONG, false /* canAuthenticate */);
+ lockoutState.setPermanentLockOut(userId, BIOMETRIC_STRONG);
assertThat(lockoutState.getLockoutState(userId, BIOMETRIC_STRONG)).isEqualTo(
LockoutTracker.LOCKOUT_PERMANENT);
assertThat(lockoutState.getLockoutState(userId, BIOMETRIC_WEAK)).isEqualTo(
@@ -96,8 +96,7 @@
@Test
public void testConvenienceLockout() {
unlockAllBiometrics();
- mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_CONVENIENCE,
- false /* canAuthenticate */);
+ mLockoutState.setPermanentLockOut(PRIMARY_USER, BIOMETRIC_CONVENIENCE);
assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
LockoutTracker.LOCKOUT_NONE);
assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
@@ -110,7 +109,7 @@
@Test
public void testWeakLockout() {
unlockAllBiometrics();
- mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_WEAK, false /* canAuthenticate */);
+ mLockoutState.setPermanentLockOut(PRIMARY_USER, BIOMETRIC_WEAK);
assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
LockoutTracker.LOCKOUT_NONE);
assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
@@ -123,8 +122,7 @@
@Test
public void testStrongLockout() {
lockoutAllBiometrics();
- mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_STRONG,
- false /* canAuthenticate */);
+ mLockoutState.setPermanentLockOut(PRIMARY_USER, BIOMETRIC_STRONG);
assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
LockoutTracker.LOCKOUT_PERMANENT);
assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
@@ -137,8 +135,7 @@
@Test
public void testConvenienceUnlock() {
lockoutAllBiometrics();
- mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_CONVENIENCE,
- true /* canAuthenticate */);
+ mLockoutState.clearPermanentLockOut(PRIMARY_USER, BIOMETRIC_CONVENIENCE);
assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
LockoutTracker.LOCKOUT_PERMANENT);
assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
@@ -150,7 +147,7 @@
@Test
public void testWeakUnlock() {
lockoutAllBiometrics();
- mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_WEAK, true /* canAuthenticate */);
+ mLockoutState.clearPermanentLockOut(PRIMARY_USER, BIOMETRIC_WEAK);
assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
LockoutTracker.LOCKOUT_PERMANENT);
assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
@@ -162,8 +159,7 @@
@Test
public void testStrongUnlock() {
lockoutAllBiometrics();
- mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_STRONG,
- true /* canAuthenticate */);
+ mLockoutState.clearPermanentLockOut(PRIMARY_USER, BIOMETRIC_STRONG);
assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
LockoutTracker.LOCKOUT_NONE);
assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
@@ -180,7 +176,7 @@
lockoutAllBiometrics(lockoutState, userOne);
lockoutAllBiometrics(lockoutState, userTwo);
- lockoutState.setAuthenticatorTo(userOne, BIOMETRIC_WEAK, true /* canAuthenticate */);
+ lockoutState.clearPermanentLockOut(userOne, BIOMETRIC_WEAK);
assertThat(lockoutState.getLockoutState(userOne, BIOMETRIC_STRONG)).isEqualTo(
LockoutTracker.LOCKOUT_PERMANENT);
assertThat(lockoutState.getLockoutState(userOne, BIOMETRIC_WEAK)).isEqualTo(
@@ -205,8 +201,7 @@
assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
LockoutTracker.LOCKOUT_NONE);
- mLockoutState.increaseLockoutTime(PRIMARY_USER, BIOMETRIC_STRONG,
- System.currentTimeMillis() + 1);
+ mLockoutState.setTimedLockout(PRIMARY_USER, BIOMETRIC_STRONG);
assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
LockoutTracker.LOCKOUT_TIMED);
assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
@@ -225,8 +220,7 @@
assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
LockoutTracker.LOCKOUT_NONE);
- when(mClock.millis()).thenReturn(0L);
- mLockoutState.increaseLockoutTime(PRIMARY_USER, BIOMETRIC_STRONG, 1);
+ mLockoutState.setTimedLockout(PRIMARY_USER, BIOMETRIC_STRONG);
assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
LockoutTracker.LOCKOUT_TIMED);
assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
@@ -235,7 +229,7 @@
mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
LockoutTracker.LOCKOUT_TIMED);
- when(mClock.millis()).thenReturn(2L);
+ mLockoutState.clearTimedLockout(PRIMARY_USER, BIOMETRIC_STRONG);
assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
LockoutTracker.LOCKOUT_NONE);
assertThat(mLockoutState.getLockoutState(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
index 1f29bec..9c9d3f8 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
@@ -19,6 +19,8 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
@@ -109,7 +111,8 @@
mUserSwitchCallback);
mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()),
TAG, mScheduler, SENSOR_ID,
- USER_ID, mLockoutCache, mLockoutResetDispatcher, mHalSessionCallback);
+ USER_ID, mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
+ mHalSessionCallback);
final SensorProps sensor1 = new SensorProps();
sensor1.commonProps = new CommonProps();
@@ -164,5 +167,6 @@
private void verifyNotLocked() {
assertEquals(LockoutTracker.LOCKOUT_NONE, mLockoutCache.getLockoutModeForUser(USER_ID));
verify(mLockoutResetDispatcher).notifyLockoutResetCallbacks(eq(SENSOR_ID));
+ verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
index 7ae4e17..0c13466 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
@@ -18,6 +18,8 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
@@ -97,7 +99,8 @@
mUserSwitchCallback);
mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()),
TAG, mScheduler, SENSOR_ID,
- USER_ID, mLockoutCache, mLockoutResetDispatcher, mHalSessionCallback);
+ USER_ID, mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
+ mHalSessionCallback);
}
@Test
@@ -130,5 +133,6 @@
private void verifyNotLocked() {
assertEquals(LockoutTracker.LOCKOUT_NONE, mLockoutCache.getLockoutModeForUser(USER_ID));
verify(mLockoutResetDispatcher).notifyLockoutResetCallbacks(eq(SENSOR_ID));
+ verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID), anyInt(), anyLong());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java
index e396263..728606f 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java
@@ -17,8 +17,13 @@
package com.android.server.devicestate;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -41,6 +46,8 @@
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
+import java.util.Locale;
+
/**
* Unit tests for {@link DeviceStateNotificationController}.
* <p/>
@@ -77,6 +84,8 @@
Notification.class);
private final NotificationManager mNotificationManager = mock(NotificationManager.class);
+ private DeviceStateNotificationController.NotificationInfoProvider mNotificationInfoProvider;
+
@Before
public void setup() throws Exception {
Context context = InstrumentationRegistry.getInstrumentation().getContext();
@@ -97,6 +106,11 @@
THERMAL_TITLE_2, THERMAL_CONTENT_2,
POWER_SAVE_TITLE_2, POWER_SAVE_CONTENT_2));
+ mNotificationInfoProvider =
+ new DeviceStateNotificationController.NotificationInfoProvider(context);
+ mNotificationInfoProvider = spy(mNotificationInfoProvider);
+ doReturn(notificationInfos).when(mNotificationInfoProvider).loadNotificationInfos();
+
when(packageManager.getNameForUid(VALID_APP_UID)).thenReturn(VALID_APP_NAME);
when(packageManager.getNameForUid(INVALID_APP_UID)).thenReturn(INVALID_APP_NAME);
when(packageManager.getApplicationInfo(eq(VALID_APP_NAME), ArgumentMatchers.any()))
@@ -106,7 +120,7 @@
when(applicationInfo.loadLabel(eq(packageManager))).thenReturn(VALID_APP_LABEL);
mController = new DeviceStateNotificationController(
- context, handler, cancelStateRunnable, notificationInfos,
+ context, handler, cancelStateRunnable, mNotificationInfoProvider,
packageManager, mNotificationManager);
}
@@ -223,4 +237,26 @@
eq(DeviceStateNotificationController.NOTIFICATION_ID),
mNotificationCaptor.capture());
}
+
+ @Test
+ public void test_notificationInfoProvider() {
+ assertNull(mNotificationInfoProvider.getCachedLocale());
+
+ mNotificationInfoProvider.getNotificationInfos(Locale.ENGLISH);
+ verify(mNotificationInfoProvider).refreshNotificationInfos(eq(Locale.ENGLISH));
+ assertEquals(Locale.ENGLISH, mNotificationInfoProvider.getCachedLocale());
+ clearInvocations(mNotificationInfoProvider);
+
+ // If the same locale is used again, the provider uses the cached value, so it won't refresh
+ mNotificationInfoProvider.getNotificationInfos(Locale.ENGLISH);
+ verify(mNotificationInfoProvider, never()).refreshNotificationInfos(eq(Locale.ENGLISH));
+ assertEquals(Locale.ENGLISH, mNotificationInfoProvider.getCachedLocale());
+ clearInvocations(mNotificationInfoProvider);
+
+ // If a different locale is used, the provider refreshes.
+ mNotificationInfoProvider.getNotificationInfos(Locale.ITALY);
+ verify(mNotificationInfoProvider).refreshNotificationInfos(eq(Locale.ITALY));
+ assertEquals(Locale.ITALY, mNotificationInfoProvider.getCachedLocale());
+ clearInvocations(mNotificationInfoProvider);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index aca96ad..aad373f 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -445,14 +445,14 @@
+ " <library \n"
+ " name=\"foo\"\n"
+ " file=\"" + mFooJar + "\"\n"
- + " on-bootclasspath-before=\"Q\"\n"
+ + " on-bootclasspath-before=\"A\"\n"
+ " on-bootclasspath-since=\"W\"\n"
+ " />\n\n"
+ " </permissions>";
parseSharedLibraries(contents);
assertFooIsOnlySharedLibrary();
SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
- assertThat(entry.onBootclasspathBefore).isEqualTo("Q");
+ assertThat(entry.onBootclasspathBefore).isEqualTo("A");
assertThat(entry.onBootclasspathSince).isEqualTo("W");
}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index d50aca9..2efd9fc 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -618,13 +618,19 @@
}
@Test
- public void shouldCancelVibrationOnScreenOff_withUidZero_returnsFalseForTouchAndHardware() {
+ public void shouldCancelVibrationOnScreenOff_withUidZero_returnsFalseForUsagesInAllowlist() {
long vibrateStartTime = 100;
mockGoToSleep(vibrateStartTime + 10, PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN);
+ Set<Integer> expectedAllowedVibrations = new HashSet<>(Arrays.asList(
+ USAGE_TOUCH,
+ USAGE_ACCESSIBILITY,
+ USAGE_PHYSICAL_EMULATION,
+ USAGE_HARDWARE_FEEDBACK
+ ));
+
for (int usage : ALL_USAGES) {
- if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK
- || usage == USAGE_PHYSICAL_EMULATION) {
+ if (expectedAllowedVibrations.contains(usage)) {
assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
createCallerInfo(/* uid= */ 0, "", usage), vibrateStartTime));
} else {
@@ -635,13 +641,19 @@
}
@Test
- public void shouldCancelVibrationOnScreenOff_withSystemUid_returnsFalseForTouchAndHardware() {
+ public void shouldCancelVibrationOnScreenOff_withSystemUid__returnsFalseForUsagesInAllowlist() {
long vibrateStartTime = 100;
mockGoToSleep(vibrateStartTime + 10, PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD);
+ Set<Integer> expectedAllowedVibrations = new HashSet<>(Arrays.asList(
+ USAGE_TOUCH,
+ USAGE_ACCESSIBILITY,
+ USAGE_PHYSICAL_EMULATION,
+ USAGE_HARDWARE_FEEDBACK
+ ));
+
for (int usage : ALL_USAGES) {
- if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK
- || usage == USAGE_PHYSICAL_EMULATION) {
+ if (expectedAllowedVibrations.contains(usage)) {
assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
createCallerInfo(Process.SYSTEM_UID, "", usage), vibrateStartTime));
} else {
@@ -652,13 +664,19 @@
}
@Test
- public void shouldCancelVibrationOnScreenOff_withSysUiPkg_returnsFalseForTouchAndHardware() {
+ public void shouldCancelVibrationOnScreenOff_withSysUiPkg_returnsFalseForUsagesInAllowlist() {
long vibrateStartTime = 100;
mockGoToSleep(vibrateStartTime + 10, PowerManager.GO_TO_SLEEP_REASON_HDMI);
+ Set<Integer> expectedAllowedVibrations = new HashSet<>(Arrays.asList(
+ USAGE_TOUCH,
+ USAGE_ACCESSIBILITY,
+ USAGE_PHYSICAL_EMULATION,
+ USAGE_HARDWARE_FEEDBACK
+ ));
+
for (int usage : ALL_USAGES) {
- if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK
- || usage == USAGE_PHYSICAL_EMULATION) {
+ if (expectedAllowedVibrations.contains(usage)) {
assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
createCallerInfo(UID, SYSUI_PACKAGE_NAME, usage), vibrateStartTime));
} else {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
index 90d1488..4406d83 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
@@ -71,10 +71,10 @@
import android.testing.TestableContext;
import android.util.ArraySet;
import android.util.Pair;
-import com.android.modules.utils.TypedXmlPullParser;
-import com.android.modules.utils.TypedXmlSerializer;
import android.util.Xml;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.UiServiceTestCase;
import com.google.common.collect.ImmutableList;
@@ -92,6 +92,7 @@
import java.io.ByteArrayOutputStream;
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
public class NotificationListenersTest extends UiServiceTestCase {
@@ -626,6 +627,58 @@
.onNotificationChannelGroupModification(anyString(), any(), any(), anyInt());
}
+ @Test
+ public void testNotificationListenerFilter_threadSafety() throws Exception {
+ testThreadSafety(() -> {
+ mListeners.setNotificationListenerFilter(
+ new Pair<>(new ComponentName("pkg1", "cls1"), 0),
+ new NotificationListenerFilter());
+ mListeners.setNotificationListenerFilter(
+ new Pair<>(new ComponentName("pkg2", "cls2"), 10),
+ new NotificationListenerFilter());
+ mListeners.setNotificationListenerFilter(
+ new Pair<>(new ComponentName("pkg3", "cls3"), 11),
+ new NotificationListenerFilter());
+
+ mListeners.onUserRemoved(10);
+ mListeners.onPackagesChanged(true, new String[]{"pkg1", "pkg2"}, new int[]{0, 0});
+ }, 20, 50);
+ }
+
+ /**
+ * Helper method to test the thread safety of some operations.
+ *
+ * <p>Runs the supplied {@code operationToTest}, {@code nRunsPerThread} times,
+ * concurrently using {@code nThreads} threads, and waits for all of them to finish.
+ */
+ private static void testThreadSafety(Runnable operationToTest, int nThreads,
+ int nRunsPerThread) throws InterruptedException {
+ final CountDownLatch startLatch = new CountDownLatch(1);
+ final CountDownLatch doneLatch = new CountDownLatch(nThreads);
+
+ for (int i = 0; i < nThreads; i++) {
+ Runnable threadRunnable = () -> {
+ try {
+ startLatch.await();
+ for (int j = 0; j < nRunsPerThread; j++) {
+ operationToTest.run();
+ }
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } finally {
+ doneLatch.countDown();
+ }
+ };
+ new Thread(threadRunnable, "Test Thread #" + i).start();
+ }
+
+ // Ready set go
+ startLatch.countDown();
+
+ // Wait for all test threads to be done.
+ doneLatch.await();
+ }
+
private ManagedServices.ManagedServiceInfo getParcelingListener(
final NotificationChannelGroup toParcel)
throws RemoteException {
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 9cfdaa7..dd9f3cb 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -5447,6 +5447,29 @@
}
@Test
+ public void testVisitUris_callStyle() {
+ Icon personIcon = Icon.createWithContentUri("content://media/person");
+ Icon verificationIcon = Icon.createWithContentUri("content://media/verification");
+ Person callingPerson = new Person.Builder().setName("Someone")
+ .setIcon(personIcon)
+ .build();
+ PendingIntent hangUpIntent = PendingIntent.getActivity(mContext, 0, new Intent(),
+ PendingIntent.FLAG_IMMUTABLE);
+ Notification n = new Notification.Builder(mContext, "a")
+ .setStyle(Notification.CallStyle.forOngoingCall(callingPerson, hangUpIntent)
+ .setVerificationIcon(verificationIcon))
+ .setContentTitle("Calling...")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .build();
+
+ Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
+ n.visitUris(visitor);
+
+ verify(visitor, times(1)).accept(eq(personIcon.getUri()));
+ verify(visitor, times(1)).accept(eq(verificationIcon.getUri()));
+ }
+
+ @Test
public void testVisitUris_audioContentsString() throws Exception {
final Uri audioContents = Uri.parse("content://com.example/audio");
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 4e001fe..37c4b37 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -28,6 +28,7 @@
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
@@ -501,6 +502,12 @@
onActivityLaunched(mTrampolineActivity);
mActivityMetricsLogger.notifyActivityLaunching(mTopActivity.intent,
mTrampolineActivity /* caller */, mTrampolineActivity.getUid());
+
+ // Simulate a corner case that the trampoline activity is removed by CLEAR_TASK.
+ // The 2 launch events can still be coalesced to one by matching the uid.
+ mTrampolineActivity.takeFromHistory();
+ assertNull(mTrampolineActivity.getTask());
+
notifyActivityLaunched(START_SUCCESS, mTopActivity);
transitToDrawnAndVerifyOnLaunchFinished(mTopActivity);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 8f2b470..0033e3e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2399,7 +2399,10 @@
holder.addConnection(connection);
assertTrue(holder.isActivityVisible());
final int[] count = new int[1];
- final Consumer<Object> c = conn -> count[0]++;
+ final Consumer<Object> c = conn -> {
+ count[0]++;
+ assertFalse(Thread.holdsLock(activity));
+ };
holder.forEachConnection(c);
assertEquals(1, count[0]);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 7d16fb2..4890f3e6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -44,11 +44,13 @@
import android.app.ActivityOptions;
import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManagerInternal;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
@@ -445,12 +447,15 @@
}
@Test
- public void testSandboxServiceInterceptionHappensToSandboxedActivityAction()
- throws InterruptedException {
-
+ public void testSandboxServiceInterceptionHappensToIntentWithSandboxActivityAction() {
ActivityInterceptorCallback spyCallback = Mockito.spy(info -> null);
mActivityInterceptorCallbacks.put(MAINLINE_SDK_SANDBOX_ORDER_ID, spyCallback);
+ PackageManager packageManagerMock = mock(PackageManager.class);
+ String sandboxPackageNameMock = "com.sandbox.mock";
+ when(mContext.getPackageManager()).thenReturn(packageManagerMock);
+ when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock);
+
Intent intent = new Intent().setAction(ACTION_START_SANDBOXED_ACTIVITY);
mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
@@ -459,13 +464,68 @@
}
@Test
- public void testSandboxServiceInterceptionNotCalledForNotSandboxedActivityAction() {
+ public void testSandboxServiceInterceptionHappensToIntentWithSandboxPackage() {
ActivityInterceptorCallback spyCallback = Mockito.spy(info -> null);
mActivityInterceptorCallbacks.put(MAINLINE_SDK_SANDBOX_ORDER_ID, spyCallback);
+ PackageManager packageManagerMock = mock(PackageManager.class);
+ String sandboxPackageNameMock = "com.sandbox.mock";
+ when(mContext.getPackageManager()).thenReturn(packageManagerMock);
+ when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock);
+
+ Intent intent = new Intent().setPackage(sandboxPackageNameMock);
+ mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
+
+ verify(spyCallback, times(1)).onInterceptActivityLaunch(
+ any(ActivityInterceptorCallback.ActivityInterceptorInfo.class));
+ }
+
+ @Test
+ public void testSandboxServiceInterceptionHappensToIntentWithComponentNameWithSandboxPackage() {
+ ActivityInterceptorCallback spyCallback = Mockito.spy(info -> null);
+ mActivityInterceptorCallbacks.put(MAINLINE_SDK_SANDBOX_ORDER_ID, spyCallback);
+
+ PackageManager packageManagerMock = mock(PackageManager.class);
+ String sandboxPackageNameMock = "com.sandbox.mock";
+ when(mContext.getPackageManager()).thenReturn(packageManagerMock);
+ when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock);
+
+ Intent intent = new Intent().setComponent(new ComponentName(sandboxPackageNameMock, ""));
+ mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
+
+ verify(spyCallback, times(1)).onInterceptActivityLaunch(
+ any(ActivityInterceptorCallback.ActivityInterceptorInfo.class));
+ }
+
+ @Test
+ public void testSandboxServiceInterceptionNotCalledWhenIntentNotRelatedToSandbox() {
+ ActivityInterceptorCallback spyCallback = Mockito.spy(info -> null);
+ mActivityInterceptorCallbacks.put(MAINLINE_SDK_SANDBOX_ORDER_ID, spyCallback);
+
+ PackageManager packageManagerMock = mock(PackageManager.class);
+ String sandboxPackageNameMock = "com.sandbox.mock";
+ when(mContext.getPackageManager()).thenReturn(packageManagerMock);
+ when(packageManagerMock.getSdkSandboxPackageName()).thenReturn(sandboxPackageNameMock);
+
+ // Intent: null
+ mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null);
+
+ // Action: null, Package: null, ComponentName: null
Intent intent = new Intent();
mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
+ // Wrong Action
+ intent = new Intent().setAction(Intent.ACTION_VIEW);
+ mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
+
+ // Wrong Package
+ intent = new Intent().setPackage("Random");
+ mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
+
+ // Wrong ComponentName's package
+ intent = new Intent().setComponent(new ComponentName("Random", ""));
+ mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
+
verify(spyCallback, never()).onInterceptActivityLaunch(
any(ActivityInterceptorCallback.ActivityInterceptorInfo.class));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 17ae215..6d7f2c1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -232,11 +232,36 @@
IOnBackInvokedCallback callback = createOnBackInvokedCallback();
window.setOnBackInvokedCallbackInfo(
- new OnBackInvokedCallbackInfo(callback, OnBackInvokedDispatcher.PRIORITY_DEFAULT));
+ new OnBackInvokedCallbackInfo(
+ callback,
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ /* isAnimationCallback = */ false));
BackNavigationInfo backNavigationInfo = startBackNavigation();
assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull();
assertThat(backNavigationInfo.getType()).isEqualTo(BackNavigationInfo.TYPE_CALLBACK);
+ assertThat(backNavigationInfo.isAnimationCallback()).isEqualTo(false);
+ assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback);
+ }
+
+ @Test
+ public void backInfoWithAnimationCallback() {
+ WindowState window = createWindow(null, WindowManager.LayoutParams.TYPE_WALLPAPER,
+ "Wallpaper");
+ addToWindowMap(window, true);
+ makeWindowVisibleAndDrawn(window);
+
+ IOnBackInvokedCallback callback = createOnBackInvokedCallback();
+ window.setOnBackInvokedCallbackInfo(
+ new OnBackInvokedCallbackInfo(
+ callback,
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ /* isAnimationCallback = */ true));
+
+ BackNavigationInfo backNavigationInfo = startBackNavigation();
+ assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull();
+ assertThat(backNavigationInfo.getType()).isEqualTo(BackNavigationInfo.TYPE_CALLBACK);
+ assertThat(backNavigationInfo.isAnimationCallback()).isEqualTo(true);
assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback);
}
@@ -364,7 +389,10 @@
IOnBackInvokedCallback callback = createOnBackInvokedCallback();
window.setOnBackInvokedCallbackInfo(
- new OnBackInvokedCallbackInfo(callback, OnBackInvokedDispatcher.PRIORITY_DEFAULT));
+ new OnBackInvokedCallbackInfo(
+ callback,
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ /* isAnimationCallback = */ false));
BackNavigationInfo backNavigationInfo = startBackNavigation();
assertThat(backNavigationInfo).isNull();
@@ -450,14 +478,20 @@
private IOnBackInvokedCallback withSystemCallback(Task task) {
IOnBackInvokedCallback callback = createOnBackInvokedCallback();
task.getTopMostActivity().getTopChild().setOnBackInvokedCallbackInfo(
- new OnBackInvokedCallbackInfo(callback, OnBackInvokedDispatcher.PRIORITY_SYSTEM));
+ new OnBackInvokedCallbackInfo(
+ callback,
+ OnBackInvokedDispatcher.PRIORITY_SYSTEM,
+ /* isAnimationCallback = */ false));
return callback;
}
private IOnBackInvokedCallback withAppCallback(Task task) {
IOnBackInvokedCallback callback = createOnBackInvokedCallback();
task.getTopMostActivity().getTopChild().setOnBackInvokedCallbackInfo(
- new OnBackInvokedCallbackInfo(callback, OnBackInvokedDispatcher.PRIORITY_DEFAULT));
+ new OnBackInvokedCallbackInfo(
+ callback,
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ /* isAnimationCallback = */ false));
return callback;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
index 2686a24..d2f0385 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
@@ -112,13 +112,6 @@
}
@Test
- public void testRegisterOrganizer_alreadyRegisteredFeature() {
- registerMockOrganizer(FEATURE_VENDOR_FIRST);
- assertThrows(IllegalStateException.class,
- () -> registerMockOrganizer(FEATURE_VENDOR_FIRST));
- }
-
- @Test
public void testRegisterOrganizer_ignoreUntrustedDisplay() throws RemoteException {
doReturn(false).when(mDisplayContent).isTrusted();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 10540dc..1ad04a2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -619,19 +619,6 @@
}
@Test
- public void testRegisterSameFeatureOrganizer_expectThrowsException() {
- final IDisplayAreaOrganizer mockDisplayAreaOrganizer = mock(IDisplayAreaOrganizer.class);
- final IBinder binder = mock(IBinder.class);
- doReturn(true).when(binder).isBinderAlive();
- doReturn(binder).when(mockDisplayAreaOrganizer).asBinder();
- final DisplayAreaOrganizerController controller =
- mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController;
- controller.registerOrganizer(mockDisplayAreaOrganizer, FEATURE_VENDOR_FIRST);
- assertThrows(IllegalStateException.class,
- () -> controller.registerOrganizer(mockDisplayAreaOrganizer, FEATURE_VENDOR_FIRST));
- }
-
- @Test
public void testRegisterUnregisterOrganizer() {
final IDisplayAreaOrganizer mockDisplayAreaOrganizer = mock(IDisplayAreaOrganizer.class);
doReturn(mock(IBinder.class)).when(mockDisplayAreaOrganizer).asBinder();
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index 2065540..a8fc25f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -84,12 +84,14 @@
}
@Test
- public void testControlsForDispatch_multiWindowTaskVisible() {
+ public void testControlsForDispatch_adjacentTasksVisible() {
addStatusBar();
addNavigationBar();
- final WindowState win = createWindow(null, WINDOWING_MODE_MULTI_WINDOW,
- ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
+ final Task task1 = createTask(mDisplayContent);
+ final Task task2 = createTask(mDisplayContent);
+ task1.setAdjacentTaskFragment(task2);
+ final WindowState win = createAppWindow(task1, WINDOWING_MODE_MULTI_WINDOW, "app");
final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
// The app must not control any system bars.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 49d8da1..9d597b1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -586,6 +586,15 @@
// Making the activity0 be the focused activity and ensure the focused app is updated.
activity0.moveFocusableActivityToTop("test");
assertEquals(activity0, mDisplayContent.mFocusedApp);
+
+ // Moving activity1 to top and make both the two activities resumed.
+ activity1.moveFocusableActivityToTop("test");
+ activity0.setState(RESUMED, "test");
+ activity1.setState(RESUMED, "test");
+
+ // Verifies that the focus app can be updated to an Activity in the adjacent TF
+ mAtm.setFocusedTask(task.mTaskId, activity0);
+ assertEquals(activity0, mDisplayContent.mFocusedApp);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 766e74c..460a603 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.InsetsSource.ID_IME;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
@@ -85,17 +86,25 @@
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.InputConfig;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
+import android.util.MergedConfiguration;
import android.view.Gravity;
+import android.view.IWindow;
+import android.view.IWindowSessionCallback;
import android.view.InputWindowHandle;
import android.view.InsetsSource;
+import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.SurfaceControl;
+import android.view.View;
+import android.view.WindowInsets;
import android.view.WindowManager;
+import android.window.ClientWindowFrames;
import android.window.ITaskFragmentOrganizer;
import android.window.TaskFragmentOrganizer;
@@ -1280,4 +1289,118 @@
assertEquals(new ArraySet(Arrays.asList(expectedArea1, expectedArea2)),
new ArraySet(unrestrictedKeepClearAreas));
}
+
+ @Test
+ public void testImeTargetChangeListener_OnImeInputTargetVisibilityChanged() {
+ final TestImeTargetChangeListener listener = new TestImeTargetChangeListener();
+ mWm.mImeTargetChangeListener = listener;
+
+ final WindowState imeTarget = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
+ createActivityRecord(mDisplayContent), "imeTarget");
+
+ imeTarget.mActivityRecord.setVisibleRequested(true);
+ makeWindowVisible(imeTarget);
+ mDisplayContent.setImeInputTarget(imeTarget);
+ waitHandlerIdle(mWm.mH);
+
+ assertThat(listener.mImeTargetToken).isEqualTo(imeTarget.mClient.asBinder());
+ assertThat(listener.mIsRemoved).isFalse();
+ assertThat(listener.mIsVisibleForImeInputTarget).isTrue();
+
+ imeTarget.mActivityRecord.setVisibleRequested(false);
+ waitHandlerIdle(mWm.mH);
+
+ assertThat(listener.mImeTargetToken).isEqualTo(imeTarget.mClient.asBinder());
+ assertThat(listener.mIsRemoved).isFalse();
+ assertThat(listener.mIsVisibleForImeInputTarget).isFalse();
+
+ imeTarget.removeImmediately();
+ assertThat(listener.mImeTargetToken).isEqualTo(imeTarget.mClient.asBinder());
+ assertThat(listener.mIsRemoved).isTrue();
+ assertThat(listener.mIsVisibleForImeInputTarget).isFalse();
+ }
+
+ @SetupWindows(addWindows = {W_INPUT_METHOD})
+ @Test
+ public void testImeTargetChangeListener_OnImeTargetOverlayVisibilityChanged() {
+ final TestImeTargetChangeListener listener = new TestImeTargetChangeListener();
+ mWm.mImeTargetChangeListener = listener;
+
+ // Scenario 1: test addWindow/relayoutWindow to add Ime layering overlay window as visible.
+ final WindowToken windowToken = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
+ mDisplayContent);
+ final IWindow client = new TestIWindow();
+ final Session session = new Session(mWm, new IWindowSessionCallback.Stub() {
+ @Override
+ public void onAnimatorScaleChanged(float v) throws RemoteException {
+
+ }
+ });
+ final ClientWindowFrames outFrames = new ClientWindowFrames();
+ final MergedConfiguration outConfig = new MergedConfiguration();
+ final SurfaceControl outSurfaceControl = new SurfaceControl();
+ final InsetsState outInsetsState = new InsetsState();
+ final InsetsSourceControl.Array outControls = new InsetsSourceControl.Array();
+ final Bundle outBundle = new Bundle();
+ final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+ TYPE_APPLICATION_OVERLAY);
+ params.setTitle("imeLayeringTargetOverlay");
+ params.token = windowToken.token;
+ params.flags = FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM;
+
+ mWm.addWindow(session, client, params, View.VISIBLE, DEFAULT_DISPLAY,
+ 0 /* userUd */, WindowInsets.Type.defaultVisible(), null, new InsetsState(),
+ new InsetsSourceControl.Array(), new Rect(), new float[1]);
+ mWm.relayoutWindow(session, client, params, 100, 200, View.VISIBLE, 0, 0, 0,
+ outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle);
+ waitHandlerIdle(mWm.mH);
+
+ final WindowState imeLayeringTargetOverlay = mDisplayContent.getWindow(
+ w -> w.mClient.asBinder() == client.asBinder());
+ assertThat(imeLayeringTargetOverlay.isVisible()).isTrue();
+ assertThat(listener.mImeTargetToken).isEqualTo(client.asBinder());
+ assertThat(listener.mIsRemoved).isFalse();
+ assertThat(listener.mIsVisibleForImeTargetOverlay).isTrue();
+
+ // Scenario 2: test relayoutWindow to let the Ime layering target overlay window invisible.
+ mWm.relayoutWindow(session, client, params, 100, 200, View.GONE, 0, 0, 0,
+ outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle);
+ waitHandlerIdle(mWm.mH);
+
+ assertThat(imeLayeringTargetOverlay.isVisible()).isFalse();
+ assertThat(listener.mImeTargetToken).isEqualTo(client.asBinder());
+ assertThat(listener.mIsRemoved).isFalse();
+ assertThat(listener.mIsVisibleForImeTargetOverlay).isFalse();
+
+ // Scenario 3: test removeWindow to remove the Ime layering target overlay window.
+ mWm.removeWindow(session, client);
+ waitHandlerIdle(mWm.mH);
+
+ assertThat(listener.mImeTargetToken).isEqualTo(client.asBinder());
+ assertThat(listener.mIsRemoved).isTrue();
+ assertThat(listener.mIsVisibleForImeTargetOverlay).isFalse();
+ }
+
+ private static class TestImeTargetChangeListener implements ImeTargetChangeListener {
+ private IBinder mImeTargetToken;
+ private boolean mIsRemoved;
+ private boolean mIsVisibleForImeTargetOverlay;
+ private boolean mIsVisibleForImeInputTarget;
+
+ @Override
+ public void onImeTargetOverlayVisibilityChanged(IBinder overlayWindowToken, boolean visible,
+ boolean removed) {
+ mImeTargetToken = overlayWindowToken;
+ mIsVisibleForImeTargetOverlay = visible;
+ mIsRemoved = removed;
+ }
+
+ @Override
+ public void onImeInputTargetVisibilityChanged(IBinder imeInputTarget,
+ boolean visibleRequested, boolean removed) {
+ mImeTargetToken = imeInputTarget;
+ mIsVisibleForImeInputTarget = visibleRequested;
+ mIsRemoved = removed;
+ }
+ }
}
diff --git a/telephony/OWNERS b/telephony/OWNERS
index 025869d..3158ad8 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -11,12 +11,6 @@
amruthr@google.com
sasindran@google.com
-# Temporarily reduced the owner during refactoring
-per-file SubscriptionManager.java=set noparent
-per-file SubscriptionManager.java=jackyu@google.com,amruthr@google.com
-per-file SubscriptionInfo.java=set noparent
-per-file SubscriptionInfo.java=jackyu@google.com,amruthr@google.com
-
# Requiring TL ownership for new carrier config keys.
per-file CarrierConfigManager.java=set noparent
per-file CarrierConfigManager.java=amruthr@google.com,tgunn@google.com,rgreenwalt@google.com,satk@google.com
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 905a90c..caafce2 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -331,8 +331,8 @@
* Same as {@link #checkCallingOrSelfReadSubscriberIdentifiers(Context, int, String, String,
* String)} except this allows an additional parameter reportFailure. Caller may not want to
* report a failure when this is an internal/intermediate check, for example,
- * SubscriptionController calls this with an INVALID_SUBID to check if caller has the required
- * permissions to bypass carrier privilege checks.
+ * SubscriptionManagerService calls this with an INVALID_SUBID to check if caller has the
+ * required permissions to bypass carrier privilege checks.
* @param reportFailure Indicates if failure should be reported.
*/
public static boolean checkCallingOrSelfReadSubscriberIdentifiers(Context context, int subId,
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 559faf9..64e4356 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -91,8 +91,7 @@
import java.util.stream.Collectors;
/**
- * SubscriptionManager is the application interface to SubscriptionController
- * and provides information about the current Telephony Subscriptions.
+ * Subscription manager provides the mobile subscription information.
*/
@SystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -119,13 +118,12 @@
public static final int DEFAULT_SUBSCRIPTION_ID = Integer.MAX_VALUE;
/**
- * Indicates the caller wants the default phone id.
- * Used in SubscriptionController and Phone but do we really need it???
+ * Indicates the default phone id.
* @hide
*/
public static final int DEFAULT_PHONE_INDEX = Integer.MAX_VALUE;
- /** Indicates the caller wants the default slot id. NOT used remove? */
+ /** Indicates the default slot index. */
/** @hide */
public static final int DEFAULT_SIM_SLOT_INDEX = Integer.MAX_VALUE;
@@ -141,29 +139,10 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static final Uri CONTENT_URI = SimInfo.CONTENT_URI;
- private static final String CACHE_KEY_DEFAULT_SUB_ID_PROPERTY =
- "cache_key.telephony.get_default_sub_id";
-
- private static final String CACHE_KEY_DEFAULT_DATA_SUB_ID_PROPERTY =
- "cache_key.telephony.get_default_data_sub_id";
-
- private static final String CACHE_KEY_DEFAULT_SMS_SUB_ID_PROPERTY =
- "cache_key.telephony.get_default_sms_sub_id";
-
- private static final String CACHE_KEY_ACTIVE_DATA_SUB_ID_PROPERTY =
- "cache_key.telephony.get_active_data_sub_id";
-
- private static final String CACHE_KEY_SLOT_INDEX_PROPERTY =
- "cache_key.telephony.get_slot_index";
-
/** The IPC cache key shared by all subscription manager service cacheable properties. */
private static final String CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY =
"cache_key.telephony.subscription_manager_service";
- /** The temporarily cache key to indicate whether subscription manager service is enabled. */
- private static final String CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_ENABLED_PROPERTY =
- "cache_key.telephony.subscription_manager_service_enabled";
-
/** @hide */
public static final String GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME = "getSimSpecificSettings";
@@ -273,83 +252,41 @@
}
}
- private static VoidPropertyInvalidatedCache<Integer> sDefaultSubIdCache =
- new VoidPropertyInvalidatedCache<>(ISub::getDefaultSubId,
- CACHE_KEY_DEFAULT_SUB_ID_PROPERTY,
- INVALID_SUBSCRIPTION_ID);
-
private static VoidPropertyInvalidatedCache<Integer> sGetDefaultSubIdCache =
new VoidPropertyInvalidatedCache<>(ISub::getDefaultSubId,
CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
INVALID_SUBSCRIPTION_ID);
- private static VoidPropertyInvalidatedCache<Integer> sDefaultDataSubIdCache =
- new VoidPropertyInvalidatedCache<>(ISub::getDefaultDataSubId,
- CACHE_KEY_DEFAULT_DATA_SUB_ID_PROPERTY,
- INVALID_SUBSCRIPTION_ID);
-
private static VoidPropertyInvalidatedCache<Integer> sGetDefaultDataSubIdCache =
new VoidPropertyInvalidatedCache<>(ISub::getDefaultDataSubId,
CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
INVALID_SUBSCRIPTION_ID);
- private static VoidPropertyInvalidatedCache<Integer> sDefaultSmsSubIdCache =
- new VoidPropertyInvalidatedCache<>(ISub::getDefaultSmsSubId,
- CACHE_KEY_DEFAULT_SMS_SUB_ID_PROPERTY,
- INVALID_SUBSCRIPTION_ID);
-
private static VoidPropertyInvalidatedCache<Integer> sGetDefaultSmsSubIdCache =
new VoidPropertyInvalidatedCache<>(ISub::getDefaultSmsSubId,
CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
INVALID_SUBSCRIPTION_ID);
- private static VoidPropertyInvalidatedCache<Integer> sActiveDataSubIdCache =
- new VoidPropertyInvalidatedCache<>(ISub::getActiveDataSubscriptionId,
- CACHE_KEY_ACTIVE_DATA_SUB_ID_PROPERTY,
- INVALID_SUBSCRIPTION_ID);
-
private static VoidPropertyInvalidatedCache<Integer> sGetActiveDataSubscriptionIdCache =
new VoidPropertyInvalidatedCache<>(ISub::getActiveDataSubscriptionId,
CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
INVALID_SUBSCRIPTION_ID);
- private static IntegerPropertyInvalidatedCache<Integer> sSlotIndexCache =
- new IntegerPropertyInvalidatedCache<>(ISub::getSlotIndex,
- CACHE_KEY_SLOT_INDEX_PROPERTY,
- INVALID_SIM_SLOT_INDEX);
-
private static IntegerPropertyInvalidatedCache<Integer> sGetSlotIndexCache =
new IntegerPropertyInvalidatedCache<>(ISub::getSlotIndex,
CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
INVALID_SIM_SLOT_INDEX);
- private static IntegerPropertyInvalidatedCache<Integer> sSubIdCache =
- new IntegerPropertyInvalidatedCache<>(ISub::getSubId,
- CACHE_KEY_SLOT_INDEX_PROPERTY,
- INVALID_SUBSCRIPTION_ID);
-
private static IntegerPropertyInvalidatedCache<Integer> sGetSubIdCache =
new IntegerPropertyInvalidatedCache<>(ISub::getSubId,
CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
INVALID_SUBSCRIPTION_ID);
- /** Cache depends on getDefaultSubId, so we use the defaultSubId cache key */
- private static IntegerPropertyInvalidatedCache<Integer> sPhoneIdCache =
- new IntegerPropertyInvalidatedCache<>(ISub::getPhoneId,
- CACHE_KEY_DEFAULT_SUB_ID_PROPERTY,
- INVALID_PHONE_INDEX);
-
private static IntegerPropertyInvalidatedCache<Integer> sGetPhoneIdCache =
new IntegerPropertyInvalidatedCache<>(ISub::getPhoneId,
CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
INVALID_PHONE_INDEX);
- //TODO: Removed before U AOSP public release.
- private static VoidPropertyInvalidatedCache<Boolean> sIsSubscriptionManagerServiceEnabled =
- new VoidPropertyInvalidatedCache<>(ISub::isSubscriptionManagerServiceEnabled,
- CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_ENABLED_PROPERTY,
- false);
-
/**
* Generates a content {@link Uri} used to receive updates on simInfo change
* on the given subscriptionId
@@ -1455,17 +1392,6 @@
mContext = context;
}
- /**
- * @return {@code true} if the new subscription manager service is used. This is temporary and
- * will be removed before Android 14 release.
- *
- * @hide
- */
- //TODO: Removed before U AOSP public release.
- public static boolean isSubscriptionManagerServiceEnabled() {
- return sIsSubscriptionManagerServiceEnabled.query(null);
- }
-
private NetworkPolicyManager getNetworkPolicyManager() {
return (NetworkPolicyManager) mContext
.getSystemService(Context.NETWORK_POLICY_SERVICE);
@@ -1520,7 +1446,7 @@
+ " listener=" + listener);
}
// We use the TelephonyRegistry as it runs in the system and thus is always
- // available. Where as SubscriptionController could crash and not be available
+ // available.
TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)
mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
if (telephonyRegistryManager != null) {
@@ -1550,7 +1476,7 @@
+ " listener=" + listener);
}
// We use the TelephonyRegistry as it runs in the system and thus is always
- // available where as SubscriptionController could crash and not be available
+ // available.
TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)
mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
if (telephonyRegistryManager != null) {
@@ -1608,7 +1534,7 @@
}
// We use the TelephonyRegistry as it runs in the system and thus is always
- // available where as SubscriptionController could crash and not be available
+ // available.
TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)
mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
if (telephonyRegistryManager != null) {
@@ -2149,9 +2075,9 @@
Log.e(LOG_TAG, "[removeSubscriptionInfoRecord]- ISub service is null");
return;
}
- int result = iSub.removeSubInfo(uniqueId, subscriptionType);
- if (result < 0) {
- Log.e(LOG_TAG, "Removal of subscription didn't succeed: error = " + result);
+ boolean result = iSub.removeSubInfo(uniqueId, subscriptionType);
+ if (!result) {
+ Log.e(LOG_TAG, "Removal of subscription didn't succeed");
} else {
logd("successfully removed subscription");
}
@@ -2236,8 +2162,7 @@
* subscriptionId doesn't have an associated slot index.
*/
public static int getSlotIndex(int subscriptionId) {
- if (isSubscriptionManagerServiceEnabled()) return sGetSlotIndexCache.query(subscriptionId);
- return sSlotIndexCache.query(subscriptionId);
+ return sGetSlotIndexCache.query(subscriptionId);
}
/**
@@ -2294,15 +2219,13 @@
return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
- if (isSubscriptionManagerServiceEnabled()) return sGetSubIdCache.query(slotIndex);
- return sSubIdCache.query(slotIndex);
+ return sGetSubIdCache.query(slotIndex);
}
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public static int getPhoneId(int subId) {
- if (isSubscriptionManagerServiceEnabled()) return sGetPhoneIdCache.query(subId);
- return sPhoneIdCache.query(subId);
+ return sGetPhoneIdCache.query(subId);
}
private static void logd(String msg) {
@@ -2323,8 +2246,7 @@
* @return the "system" default subscription id.
*/
public static int getDefaultSubscriptionId() {
- if (isSubscriptionManagerServiceEnabled()) return sGetDefaultSubIdCache.query(null);
- return sDefaultSubIdCache.query(null);
+ return sGetDefaultSubIdCache.query(null);
}
/**
@@ -2412,8 +2334,7 @@
* @return the default SMS subscription Id.
*/
public static int getDefaultSmsSubscriptionId() {
- if (isSubscriptionManagerServiceEnabled()) return sGetDefaultSmsSubIdCache.query(null);
- return sDefaultSmsSubIdCache.query(null);
+ return sGetDefaultSmsSubIdCache.query(null);
}
/**
@@ -2447,8 +2368,7 @@
* @return the default data subscription Id.
*/
public static int getDefaultDataSubscriptionId() {
- if (isSubscriptionManagerServiceEnabled()) return sGetDefaultDataSubIdCache.query(null);
- return sDefaultDataSubIdCache.query(null);
+ return sGetDefaultDataSubIdCache.query(null);
}
/**
@@ -3912,10 +3832,7 @@
* @see TelephonyCallback.ActiveDataSubscriptionIdListener
*/
public static int getActiveDataSubscriptionId() {
- if (isSubscriptionManagerServiceEnabled()) {
- return sGetActiveDataSubscriptionIdCache.query(null);
- }
- return sActiveDataSubIdCache.query(null);
+ return sGetActiveDataSubscriptionIdCache.query(null);
}
/**
@@ -3934,61 +3851,16 @@
}
/** @hide */
- public static void invalidateDefaultSubIdCaches() {
- PropertyInvalidatedCache.invalidateCache(CACHE_KEY_DEFAULT_SUB_ID_PROPERTY);
- }
-
- /** @hide */
- public static void invalidateDefaultDataSubIdCaches() {
- PropertyInvalidatedCache.invalidateCache(CACHE_KEY_DEFAULT_DATA_SUB_ID_PROPERTY);
- }
-
- /** @hide */
- public static void invalidateDefaultSmsSubIdCaches() {
- PropertyInvalidatedCache.invalidateCache(CACHE_KEY_DEFAULT_SMS_SUB_ID_PROPERTY);
- }
-
- /** @hide */
- public static void invalidateActiveDataSubIdCaches() {
- if (isSubscriptionManagerServiceEnabled()) {
- PropertyInvalidatedCache.invalidateCache(
- CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY);
- } else {
- PropertyInvalidatedCache.invalidateCache(CACHE_KEY_ACTIVE_DATA_SUB_ID_PROPERTY);
- }
- }
-
- /** @hide */
- public static void invalidateSlotIndexCaches() {
- PropertyInvalidatedCache.invalidateCache(CACHE_KEY_SLOT_INDEX_PROPERTY);
- }
-
- /** @hide */
public static void invalidateSubscriptionManagerServiceCaches() {
PropertyInvalidatedCache.invalidateCache(CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY);
}
- /** @hide */
- //TODO: Removed before U AOSP public release.
- public static void invalidateSubscriptionManagerServiceEnabledCaches() {
- PropertyInvalidatedCache.invalidateCache(
- CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_ENABLED_PROPERTY);
- }
-
/**
* Allows a test process to disable client-side caching operations.
*
* @hide
*/
public static void disableCaching() {
- sDefaultSubIdCache.disableLocal();
- sDefaultDataSubIdCache.disableLocal();
- sActiveDataSubIdCache.disableLocal();
- sDefaultSmsSubIdCache.disableLocal();
- sSlotIndexCache.disableLocal();
- sSubIdCache.disableLocal();
- sPhoneIdCache.disableLocal();
-
sGetDefaultSubIdCache.disableLocal();
sGetDefaultDataSubIdCache.disableLocal();
sGetActiveDataSubscriptionIdCache.disableLocal();
@@ -3996,8 +3868,6 @@
sGetSlotIndexCache.disableLocal();
sGetSubIdCache.disableLocal();
sGetPhoneIdCache.disableLocal();
-
- sIsSubscriptionManagerServiceEnabled.disableLocal();
}
/**
@@ -4005,14 +3875,6 @@
*
* @hide */
public static void clearCaches() {
- sDefaultSubIdCache.clear();
- sDefaultDataSubIdCache.clear();
- sActiveDataSubIdCache.clear();
- sDefaultSmsSubIdCache.clear();
- sSlotIndexCache.clear();
- sSubIdCache.clear();
- sPhoneIdCache.clear();
-
sGetDefaultSubIdCache.clear();
sGetDefaultDataSubIdCache.clear();
sGetActiveDataSubscriptionIdCache.clear();
@@ -4020,8 +3882,6 @@
sGetSlotIndexCache.clear();
sGetSubIdCache.clear();
sGetPhoneIdCache.clear();
-
- sIsSubscriptionManagerServiceEnabled.clear();
}
/**
diff --git a/telephony/java/android/telephony/satellite/stub/ISatelliteGateway.aidl b/telephony/java/android/telephony/satellite/stub/ISatelliteGateway.aidl
new file mode 100644
index 0000000..4f1a136
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/stub/ISatelliteGateway.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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 android.telephony.satellite.stub;
+
+/**
+ * {@hide}
+ */
+oneway interface ISatelliteGateway {
+ // An empty service because Telephony does not need to use any APIs from this service.
+ // Once satellite modem is enabled, Telephony will bind to the ISatelliteGateway service; and
+ // when satellite modem is disabled, Telephony will unbind to the service.
+}
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteGatewayService.java b/telephony/java/android/telephony/satellite/stub/SatelliteGatewayService.java
new file mode 100644
index 0000000..f4514a6
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteGatewayService.java
@@ -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 android.telephony.satellite.stub;
+
+import android.annotation.SdkConstant;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+import com.android.telephony.Rlog;
+
+/**
+ * Main SatelliteGatewayService implementation, which binds via the Telephony SatelliteController.
+ * Services that extend SatelliteGatewayService must register the service in their AndroidManifest
+ * to be detected by the framework. The application must declare that they require the
+ * "android.permission.BIND_SATELLITE_GATEWAY_SERVICE" permission to ensure that nothing else can
+ * bind to their service except the Telephony framework. The SatelliteGatewayService definition in
+ * the manifest must follow the following format:
+ *
+ * ...
+ * <service android:name=".EgSatelliteGatewayService"
+ * android:permission="android.permission.BIND_SATELLITE_GATEWAY_SERVICE" >
+ * ...
+ * <intent-filter>
+ * <action android:name="android.telephony.satellite.SatelliteGatewayService" />
+ * </intent-filter>
+ * </service>
+ * ...
+ *
+ * The telephony framework will then bind to the SatelliteGatewayService defined in the manifest if
+ * it is the default SatelliteGatewayService defined in the device overlay
+ * "config_satellite_gateway_service_package".
+ * @hide
+ */
+public abstract class SatelliteGatewayService extends Service {
+ private static final String TAG = "SatelliteGatewayService";
+
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE =
+ "android.telephony.satellite.SatelliteGatewayService";
+
+ private final IBinder mBinder = new ISatelliteGateway.Stub() {};
+
+ /**
+ * @hide
+ */
+ @Override
+ public final IBinder onBind(Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ Rlog.d(TAG, "SatelliteGatewayService bound");
+ return mBinder;
+ }
+ return null;
+ }
+
+ /**
+ * @return The binder for the ISatelliteGateway.
+ * @hide
+ */
+ public final IBinder getBinder() {
+ return mBinder;
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 6a5380d..21a6b44 100644
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -129,9 +129,9 @@
* @param uniqueId This is the unique identifier for the subscription within the specific
* subscription type.
* @param subscriptionType the type of subscription to be removed
- * @return 0 if success, < 0 on error.
+ * @return true if success, false on error.
*/
- int removeSubInfo(String uniqueId, int subscriptionType);
+ boolean removeSubInfo(String uniqueId, int subscriptionType);
/**
* Set SIM icon tint color by simInfo index
@@ -260,7 +260,7 @@
int[] getActiveSubIdList(boolean visibleOnly);
- int setSubscriptionProperty(int subId, String propKey, String propValue);
+ void setSubscriptionProperty(int subId, String propKey, String propValue);
String getSubscriptionProperty(int subId, String propKey, String callingPackage,
String callingFeatureId);
@@ -353,13 +353,6 @@
*/
List<SubscriptionInfo> getSubscriptionInfoListAssociatedWithUser(in UserHandle userHandle);
- /**
- * @return {@code true} if using SubscriptionManagerService instead of
- * SubscriptionController.
- */
- //TODO: Removed before U AOSP public release.
- boolean isSubscriptionManagerServiceEnabled();
-
/**
* Called during setup wizard restore flow to attempt to restore the backed up sim-specific
* configs to device for all existing SIMs in the subscription database
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index ee9d6c1..282b64d 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2991,6 +2991,15 @@
boolean setSatelliteServicePackageName(in String servicePackageName);
/**
+ * This API can be used by only CTS to update satellite gateway service package name.
+ *
+ * @param servicePackageName The package name of the satellite gateway service.
+ * @return {@code true} if the satellite gateway service is set successfully,
+ * {@code false} otherwise.
+ */
+ boolean setSatelliteGatewayServicePackageName(in String servicePackageName);
+
+ /**
* This API can be used by only CTS to update the timeout duration in milliseconds that
* satellite should stay at listening mode to wait for the next incoming page before disabling
* listening mode.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
index 18e49fe..ae9ca80 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
@@ -102,7 +102,7 @@
@Postsubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
- @Postsubmit
+ @Ignore("Not applicable to this CUJ. App is full screen at the end")
@Test
override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
@@ -127,11 +127,11 @@
@Test
override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
- @Postsubmit
+ @Ignore("Not applicable to this CUJ. App is full screen at the end")
@Test
override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
- @Postsubmit
+ @Ignore("Not applicable to this CUJ. App is full screen at the end")
@Test
override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
@@ -145,7 +145,7 @@
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
super.visibleWindowsShownMoreThanOneConsecutiveEntry()
- @Postsubmit
+ @Ignore("Not applicable to this CUJ. App is full screen at the end")
@Test
override fun navBarWindowIsVisibleAtStartAndEnd() {
super.navBarWindowIsVisibleAtStartAndEnd()
diff --git a/tests/SharedLibraryLoadingTest/AndroidTest.xml b/tests/SharedLibraryLoadingTest/AndroidTest.xml
index 947453d..ad05847 100644
--- a/tests/SharedLibraryLoadingTest/AndroidTest.xml
+++ b/tests/SharedLibraryLoadingTest/AndroidTest.xml
@@ -22,7 +22,6 @@
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="cleanup" value="false" />
<option name="remount-system" value="true" />
<option name="push"
value="SharedLibraryLoadingTests_StandardSharedLibrary.apk->/product/app/SharedLibraryLoadingTests_StandardSharedLibrary.apk" />
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java
index 166fbdd..c6e675a 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/NetworkProviderInfo.java
@@ -84,6 +84,12 @@
public @interface DeviceType {
}
+ /**
+ * Key in extras bundle indicating that the device battery is charging.
+ * @hide
+ */
+ public static final String EXTRA_KEY_IS_BATTERY_CHARGING = "is_battery_charging";
+
@DeviceType
private final int mDeviceType;
private final String mDeviceName;